Files
adif-api-reverse-engineering/docs/API_DOCUMENTATION.md
Dasemu 68fac80520 Refactor: reorganización completa del proyecto y documentación consolidada
Esta actualización reorganiza el proyecto de reverse engineering de la API de ADIF con los siguientes cambios:

Estructura del proyecto:
- Movida documentación principal a carpeta docs/
- Consolidados archivos markdown redundantes en CLAUDE.md (contexto completo del proyecto)
- Organización de tests en carpeta tests/ con README explicativo
- APK renombrado de base.apk a adif.apk para mayor claridad

Archivos de código:
- Movidos adif_auth.py y adif_client.py a la raíz (antes en api_testing_scripts/)
- Eliminados scripts de testing obsoletos y scripts de Frida no utilizados
- Nuevos tests detallados: test_endpoints_detailed.py y test_onepaths_with_real_trains.py

Descubrimientos:
- Documentados nuevos hallazgos en docs/NEW_DISCOVERIES.md
- Actualización de onePaths funcionando con commercialNumber real (devuelve 200)
- Extraídos 1587 códigos de estación en station_codes.txt

Configuración:
- Actualizado .gitignore con mejores patrones para Python e IDEs
- Eliminados archivos temporales de depuración y logs
2025-12-05 11:22:13 +01:00

483 lines
13 KiB
Markdown

# Adif Elcano API - Ingeniería Reversa
Documentación completa de la API de ADIF (El Cano Móvil) obtenida mediante ingeniería reversa de la aplicación móvil.
**Estado**: ✅ Proyecto completado con éxito
**Última actualización**: 2025-12-05
## 🎯 Resumen de Funcionalidad
| Característica | Estado | Notas |
|----------------|--------|-------|
| Autenticación HMAC-SHA256 | ✅ Implementada | Algoritmo completo y validado |
| Endpoints funcionales | ✅ 4/8 (50%) | departures, arrivals, onepaths, stationsobservations |
| Endpoints bloqueados | ⚠️ 4/8 | 401 Unauthorized por permisos limitados |
| Códigos de estación | ✅ 1587 | Extraídos de `assets/stations_all.json` |
| Claves extraídas | ✅ Completas | ACCESS_KEY y SECRET_KEY de `libapi-keys.so` |
## URLs Base
```
BASE_URL_STATIONS = https://estaciones.api.adif.es
BASE_URL_CIRCULATION = https://circulacion.api.adif.es
BASE_URL_ELCANOWEB = https://elcanoweb.adif.es/api/
BASE_URL_AVISA = https://avisa.adif.es/avisa-ws/api/
```
## Headers de Autenticación
### Para API de Circulaciones y Composiciones
```
Content-Type: application/json;charset=utf-8
User-key: f4ce9fbfa9d721e39b8984805901b5df
```
### Para API de Estaciones
```
Content-Type: application/json;charset=utf-8
User-key: 0d021447a2fd2ac64553674d5a0c1a6f
```
### Para Avisa Login
```
Authorization: Basic YXZpc3RhX2NsaWVudF9hbmRyb2lkOjh5WzZKNyFmSjwhXypmYXE1NyNnOSohNElwa2MjWC1BTg==
```
### Para Suscripciones
```
Authorization: Basic ZGVpbW9zOmRlaW1vc3R0
X-CanalMovil-Authentication: <token>
X-CanalMovil-deviceID: <device_id>
X-CanalMovil-pushID: <push_id>
```
## Tokens
```
REGISTRATION_TOKEN = Bearer b9034774-c6e4-4663-a1a8-74bf7102651b
```
## Endpoints
### Estaciones
#### Obtener todas las estaciones
```
GET /portroyalmanager/secure/stations/allstations/reducedinfo/{token}/
Base: https://estaciones.api.adif.es
Headers: User-key para estaciones
```
#### Obtener detalles de una estación
```
POST /portroyalmanager/secure/stations/onestation/
Base: https://estaciones.api.adif.es
Headers: User-key para estaciones
Body:
{
"stationCode": "string"
}
```
#### Observaciones de estación
```
POST /portroyalmanager/secure/stationsobservations/
Base: https://estaciones.api.adif.es
Headers: User-key para estaciones
Body:
{
"stationCodes": ["string"] // Array de códigos de estación (requerido)
}
Ejemplo:
{
"stationCodes": ["60000", "71801"]
}
```
### Circulaciones (Trenes)
#### Salidas (Departures)
```
POST /portroyalmanager/secure/circulationpaths/departures/traffictype/
Base: https://circulacion.api.adif.es
Headers: User-key para circulaciones
Body:
{
"commercialService": "YES|NOT|BOTH", // Estado del servicio comercial
"commercialStopType": "YES|NOT|BOTH", // Tipo de parada comercial
"destinationStationCode": "string|null", // Código estación destino (opcional)
"originStationCode": "string|null", // Código estación origen (opcional)
"page": {
"pageNumber": number // Número de página
},
"stationCode": "string|null", // Código estación (opcional)
"trafficType": "CERCANIAS|AVLDMD|OTHERS|TRAVELERS|GOODS|ALL" // Tipo de tráfico
}
Ejemplo:
{
"commercialService": "BOTH",
"commercialStopType": "BOTH",
"destinationStationCode": null,
"originStationCode": null,
"page": {
"pageNumber": 0
},
"stationCode": "60000",
"trafficType": "ALL"
}
```
#### Llegadas (Arrivals)
```
POST /portroyalmanager/secure/circulationpaths/arrivals/traffictype/
Base: https://circulacion.api.adif.es
Headers: User-key para circulaciones
Body: Mismo formato que departures (TrafficCirculationPathRequest)
```
#### Entre estaciones
```
POST /portroyalmanager/secure/circulationpaths/betweenstations/traffictype/
Base: https://circulacion.api.adif.es
Headers: User-key para circulaciones
Body: Mismo formato que departures (TrafficCirculationPathRequest)
```
#### Una ruta específica
```
POST /portroyalmanager/secure/circulationpathdetails/onepaths/
Base: https://circulacion.api.adif.es
Headers: User-key para circulaciones
Body:
{
"allControlPoints": boolean|null, // Todos los puntos de control (opcional)
"commercialNumber": "string|null", // Número comercial del tren (opcional)
"destinationStationCode": "string|null", // Código estación destino (opcional)
"launchingDate": number|null, // Fecha de lanzamiento en timestamp (Long) (opcional)
"originStationCode": "string|null" // Código estación origen (opcional)
}
Ejemplo:
{
"allControlPoints": true,
"commercialNumber": "04138",
"destinationStationCode": "60000",
"launchingDate": 1733356800000,
"originStationCode": "71801"
}
```
#### Varias rutas
```
POST /portroyalmanager/secure/circulationpathdetails/severalpaths/
Base: https://circulacion.api.adif.es
Headers: User-key para circulaciones
Body: Mismo formato que onepaths (OneOrSeveralPathsRequest)
```
### Composiciones
#### Composición de tren
```
POST /portroyalmanager/secure/circulationpaths/compositions/path/
Base: https://circulacion.api.adif.es
Headers: User-key para circulaciones
Body: Same as onepaths request
```
### Avisa (Sistema de incidencias)
#### Login
```
POST /avisa-ws/api/token
Base: https://avisa.adif.es
Headers: Basic auth token para Avisa
Query params:
- grant_type: "password"
- username: <username>
- password: <password>
```
#### Registrar cliente
```
POST /avisa-ws/api/v1/client
Base: https://avisa.adif.es
```
#### Estaciones Avisa
```
GET /avisa-ws/api/v1/station
Base: https://avisa.adif.es
```
#### Categorías de estación
```
GET /avisa-ws/api/v1/category
Base: https://avisa.adif.es
```
#### Incidencias
```
GET /avisa-ws/api/v1/incidence
POST /avisa-ws/api/v1/incidence
GET /avisa-ws/api/v1/incidence/{incidenceId}
Base: https://avisa.adif.es
```
### Suscripciones
#### Listar suscripciones
```
GET /api/subscriptions?platform=300
Base: https://elcanoweb.adif.es
Headers: Basic auth + X-CanalMovil headers
```
#### Crear suscripción
```
POST /api/subscriptions
Base: https://elcanoweb.adif.es
Headers: Basic auth + X-CanalMovil headers
```
#### Silenciar suscripción
```
PUT /api/subscriptions/{id}/mute
Base: https://elcanoweb.adif.es
Headers: Basic auth + X-CanalMovil headers
```
## 📊 Estructura de Respuestas
### Respuesta de Departures/Arrivals
```json
{
"commercialPaths": [
{
"commercialPathInfo": {
"timestamp": 1764927847100,
"commercialPathKey": {
"commercialCirculationKey": {
"commercialNumber": "90399",
"launchingDate": 1764889200000
},
"originStationCode": "10620",
"destinationStationCode": "60004"
},
"commercialOriginStationCode": "10620",
"commercialDestinationStationCode": "60004",
"line": null,
"core": null,
"observation": null,
"trafficType": "CERCANIAS",
"opeProComPro": {
"operator": "RF",
"product": "C",
"commercialProduct": " "
},
"compositionData": {
"compositionLenghtType": null,
"compositionFloorType": null,
"accesible": false
},
"announceableStations": ["60004"]
},
"passthroughStep": {
"stopType": "NO_STOP",
"announceable": false,
"stationCode": "10200",
"arrivalPassthroughStepSides": null,
"departurePassthroughStepSides": {
"plannedTime": 1764927902000,
"forecastedOrAuditedDelay": 2175,
"timeType": "FORECASTED",
"plannedPlatform": "2",
"sitraPlatform": null,
"ctcPlatform": null,
"operatorPlatform": null,
"resultantPlatform": "PLANNED",
"preassignedPlatform": null,
"observation": null,
"circulationState": "RUNNING",
"announceState": "NORMAL",
"technicalCirculationKey": {
"technicalNumber": "90399",
"technicalLaunchingDate": 1764889200000
},
"visualEffects": {
"inmediateDeparture": false,
"countDown": false,
"showDelay": true
}
}
}
}
]
}
```
**Campos importantes**:
- `commercialNumber`: Número comercial del tren
- `launchingDate`: Fecha de salida en milisegundos (timestamp)
- `plannedTime`: Hora planificada en milisegundos
- `forecastedOrAuditedDelay`: Retraso en segundos
- `circulationState`: Estado del tren (RUNNING, PENDING_TO_CIRCULATE, etc.)
- `plannedPlatform`: Andén planificado
### Respuesta de OnePaths (Ruta Completa)
```json
{
"commercialPaths": [
{
"commercialPathInfo": { /* Igual que en departures */ },
"passthroughSteps": [ // ← Array con TODAS las paradas
{
"stopType": "COMMERCIAL",
"announceable": false,
"stationCode": "10620",
"arrivalPassthroughStepSides": null,
"departurePassthroughStepSides": {
"plannedTime": 1764918000000,
"forecastedOrAuditedDelay": 430,
"timeType": "AUDITED",
"plannedPlatform": "1",
"circulationState": "RUNNING",
"showDelay": false
}
},
{
"stopType": "NO_STOP",
"stationCode": "C1062",
"arrivalPassthroughStepSides": { /* ... */ },
"departurePassthroughStepSides": { /* ... */ }
}
// ... más paradas
]
}
]
}
```
**Diferencia clave**:
- `departures/arrivals``passthroughStep` (singular, solo la estación consultada)
- `onepaths``passthroughSteps` (plural, array con todas las paradas del recorrido)
### Respuesta de Station Observations
```json
{
"stationObservations": [
{
"stationCode": "10200",
"observation": "Texto de la observación"
}
]
}
```
### Status Codes
| Código | Significado | Causa |
|--------|-------------|-------|
| 200 | ✅ Success | Petición exitosa con datos |
| 204 | ⚠️ No Content | Autenticación correcta pero sin datos disponibles |
| 400 | ❌ Bad Request | Payload incorrecto, campo requerido faltante o formato inválido |
| 401 | ❌ Unauthorized | Sin permisos (claves con perfil limitado) |
**Nota importante sobre 204**: Un status 204 NO es un error. Significa que la autenticación y el payload son correctos, pero no hay datos disponibles para esa consulta específica.
## Tipos de Datos
### TrafficType (Tipos de tráfico)
- `CERCANIAS` - Trenes de cercanías
- `AVLDMD` - Alta Velocidad, Larga y Media Distancia
- `OTHERS` - Otros tipos de tráfico
- `TRAVELERS` - Viajeros
- `GOODS` - Mercancías
- `ALL` - Todos los tipos
### State (Estados para comercialService y comercialStopType)
- `YES` - Sí
- `NOT` - No
- `BOTH` - Ambos
### PageInfoDTO
```json
{
"pageNumber": 0
}
```
## Notas de Seguridad
- La app usa certificate pinning con claves públicas específicas
- Los tokens están hardcodeados en la aplicación
- Las User-keys son diferentes para cada servicio (estaciones vs circulaciones)
- El token de registro `b9034774-c6e4-4663-a1a8-74bf7102651b` está en el código
## 🗺️ Códigos de Estación
**Total**: 1587 estaciones disponibles
**Archivo**: `station_codes.txt` (raíz del proyecto)
**Fuente**: `apk_extracted/assets/stations_all.json`
### Formato
```
código nombre tipos_tráfico
```
### Top Estaciones
```
10200 Madrid Puerta de Atocha AVLDMD
10302 Madrid Chamartín-Clara Campoamor AVLDMD
71801 Barcelona Sants AVLDMD,CERCANIAS
60000 València Nord AVLDMD
11401 Sevilla Santa Justa AVLDMD
50003 Alacant Terminal AVLDMD,CERCANIAS
54007 Córdoba Central AVLDMD
79600 Zaragoza Portillo AVLDMD,CERCANIAS
03216 València J.Sorolla AVLDMD
04040 Zaragoza Delicias AVLDMD,CERCANIAS
```
## Notas de Implementación
Esta documentación se ha obtenido mediante ingeniería reversa del código decompilado de la aplicación Android de ADIF Elcano.
**Herramientas utilizadas**:
- **Ghidra**: Extracción de claves de `libapi-keys.so`
- **JADX**: Decompilación del APK
- **Python 3**: Implementación del cliente
- **Frida**: Análisis dinámico (opcional)
**Clases principales analizadas**:
- `com.adif.elcanomovil.serviceNetworking.circulations.model.request.TrafficCirculationPathRequest`
- `com.adif.elcanomovil.serviceNetworking.circulations.model.request.OneOrSeveralPathsRequest`
- `com.adif.elcanomovil.serviceNetworking.stationObservations.model.StationObservationsRequest`
- `com.adif.elcanomovil.serviceNetworking.circulations.model.request.CirculationPathRequest` (interface)
- `com.adif.elcanomovil.serviceNetworking.circulations.model.request.TrafficType` (enum)
- `com.adif.elcanomovil.serviceNetworking.interceptors.auth.ElcanoAuth` (algoritmo HMAC)
**Archivos clave**:
- `apk_extracted/lib/x86_64/libapi-keys.so` - Claves de autenticación
- `apk_extracted/assets/stations_all.json` - Base de datos de estaciones
- `apk_decompiled/sources/com/adif/elcanomovil/` - Código fuente decompilado
---
**Última actualización**: 2025-12-05
**Estado**: ✅ Proyecto completado con éxito