# 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
