zcatp2p/PROTOCOL.md
reugenio 7e5b16ee15 Inicial: biblioteca zcatp2p para comunicación P2P segura
- Especificación completa del protocolo (PROTOCOL.md)
- Referencia de API (API.md)
- Implementación crypto: SHA256, ChaCha20-Poly1305
- Device ID con Base32 y verificación Luhn32
- Framing de mensajes (HELLO, PING, DATA, etc.)
- Discovery local UDP broadcast
- Estructura de conexiones y estados
- Build system para Zig 0.15.2

Pendiente: TLS 1.3, STUN, Global Discovery HTTPS, Relay

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 01:06:30 +01:00

538 lines
20 KiB
Markdown

# Protocolo zcatp2p - Especificación Técnica v1.0
## 1. Visión General
zcatp2p es un protocolo de comunicación P2P para intercambio seguro de mensajes
entre nodos identificados criptográficamente. Diseñado para comunicación directa
entre empresas (facturas, certificados) sin intermediarios.
### 1.1 Principios de Diseño
1. **Seguridad primero**: Todo tráfico cifrado E2E con TLS 1.3
2. **Identificación por certificado**: Device ID = SHA256(certificado)
3. **Descentralizado**: Funciona sin servidor central (LAN)
4. **NAT-friendly**: STUN + relay para conectividad universal
5. **Sin dependencias**: Implementación completa en Zig puro
### 1.2 Componentes del Sistema
```
┌──────────────────────────────────────────────────────────────┐
│ NODO SIMIFACTU │
├──────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
│ │ Discovery │ │ STUN │ │ Relay Client │ │
│ │ (local+ │ │ Client │ │ (fallback) │ │
│ │ global) │ │ │ │ │ │
│ └──────┬──────┘ └──────┬──────┘ └──────────┬──────────┘ │
│ │ │ │ │
│ ┌──────┴────────────────┴────────────────────┴──────────┐ │
│ │ Connection Manager │ │
│ │ - Mantiene conexiones activas │ │
│ │ - Intenta conexión directa primero │ │
│ │ - Fallback a relay si falla │ │
│ └──────────────────────────┬────────────────────────────┘ │
│ │ │
│ ┌──────────────────────────┴────────────────────────────┐ │
│ │ TLS 1.3 Layer │ │
│ │ - Autenticación mutua por certificado │ │
│ │ - Device ID derivado de certificado │ │
│ └──────────────────────────┬────────────────────────────┘ │
│ │ │
│ ┌──────────────────────────┴────────────────────────────┐ │
│ │ Protocol Layer │ │
│ │ - Message framing │ │
│ │ - Compresión LZ4 opcional │ │
│ │ - Request/Response pattern │ │
│ └───────────────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────┘
```
## 2. Identidad de Dispositivo (Device ID)
### 2.1 Generación
El Device ID es un identificador único de 32 bytes derivado del certificado TLS:
```
DeviceID = SHA256(DER_encoded_certificate)
```
### 2.2 Representación en String
Para facilitar el intercambio, el Device ID se representa como string Base32 con
dígitos de verificación Luhn:
```
Formato: XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX
└──7──┘ └──7──┘ └──7──┘ └──7──┘ └──7──┘ └──7──┘ └──7──┘ └──7──┘
Cada grupo de 7 caracteres incluye 1 dígito de verificación Luhn.
Total: 56 caracteres + 7 guiones = 63 caracteres
```
### 2.3 Algoritmo Luhn para Base32
```
Alfabeto Base32: ABCDEFGHIJKLMNOPQRSTUVWXYZ234567
function luhn32(s):
factor = 1
sum = 0
n = 32 // base
for char in reverse(s):
codepoint = base32_to_int(char)
addend = factor * codepoint
factor = (factor == 2) ? 1 : 2
addend = (addend / n) + (addend % n)
sum += addend
remainder = sum % n
check = (n - remainder) % n
return int_to_base32(check)
```
### 2.4 Certificado Auto-firmado
Cada nodo genera un certificado X.509 auto-firmado al inicializarse:
```
- Subject: CN=syncthing (o "zcatp2p" para nuestra implementación)
- Key Algorithm: ECDSA P-256 o Ed25519
- Validity: 20 años
- Serial: Random 128 bits
```
## 3. Protocolo de Transporte
### 3.1 Conexión TLS 1.3
Toda comunicación usa TLS 1.3 con autenticación mutua de certificados:
```
Cliente Servidor
│ │
│──────── ClientHello ────────────────>│
│ + key_share │
│ + supported_versions │
│ │
│<─────── ServerHello ─────────────────│
│ + key_share │
│ + EncryptedExtensions │
│ + CertificateRequest │
│ + Certificate │
│ + CertificateVerify │
│ + Finished │
│ │
│──────── Certificate ────────────────>│
│ + CertificateVerify │
│ + Finished │
│ │
│<═══════ Application Data ═══════════>│
│ │
```
### 3.2 Cipher Suites Soportadas
En orden de preferencia:
1. TLS_CHACHA20_POLY1305_SHA256
2. TLS_AES_256_GCM_SHA384
3. TLS_AES_128_GCM_SHA256
### 3.3 Verificación de Identidad
Después del handshake TLS, ambos extremos:
1. Calculan SHA256 del certificado del peer
2. Verifican que coincide con el Device ID esperado
3. Si no coincide, cierran la conexión
## 4. Protocolo de Mensajes
### 4.1 Formato de Trama
Cada mensaje tiene el siguiente formato:
```
┌────────────────┬────────────────┬─────────────────────────────┐
│ Header (2B) │ Length (4B) │ Payload (variable) │
└────────────────┴────────────────┴─────────────────────────────┘
│ │ │
│ │ └── Datos del mensaje
│ └── Longitud del payload en big-endian
└── Tipo de mensaje (1B) + Flags (1B)
```
### 4.2 Tipos de Mensaje
```
Valor Nombre Descripción
───── ────── ───────────
0x00 HELLO Intercambio inicial de información
0x01 PING Keepalive
0x02 PONG Respuesta a PING
0x03 DATA Datos de aplicación
0x04 DATA_ACK Confirmación de recepción
0x05 CLOSE Cierre de conexión
0x06 ERROR Notificación de error
```
### 4.3 Flags
```
Bit Nombre Descripción
─── ────── ───────────
0 COMPRESSED Payload comprimido con LZ4
1 ENCRYPTED Payload cifrado (adicional a TLS)
2 REQUEST Espera respuesta
3 RESPONSE Es respuesta a REQUEST
4-7 Reserved Para uso futuro
```
### 4.4 Mensaje HELLO
Intercambiado inmediatamente después del handshake TLS:
```
┌─────────────────────────────────────────────────────────────┐
│ HELLO Message │
├─────────────────────────────────────────────────────────────┤
│ device_name_len: u8 │ Longitud del nombre │
│ device_name: [N]u8 │ Nombre del dispositivo │
│ client_name_len: u8 │ Longitud del cliente │
│ client_name: [N]u8 │ "simifactu" o "zcatp2p" │
│ client_version_len: u8 │ Longitud de versión │
│ client_version: [N]u8 │ Ej: "1.0.0" │
│ timestamp: i64 │ Unix timestamp │
│ capabilities: u32 │ Bitmap de capacidades │
└─────────────────────────────────────────────────────────────┘
```
Capacidades (bitmap):
```
Bit Capacidad
─── ─────────
0 COMPRESSION_LZ4
1 ENCRYPTION_CHACHA20
2 RELAY_SUPPORT
3 IPV6_SUPPORT
```
### 4.5 Mensaje DATA
```
┌─────────────────────────────────────────────────────────────┐
│ DATA Message │
├─────────────────────────────────────────────────────────────┤
│ message_id: u32 │ ID único para correlación │
│ content_type_len: u8 │ Longitud del tipo │
│ content_type: [N]u8 │ MIME type │
│ data_len: u32 │ Longitud de los datos │
│ data: [N]u8 │ Datos de aplicación │
└─────────────────────────────────────────────────────────────┘
```
Content types sugeridos para Simifactu:
- `application/x-simifactu-invoice` - Factura
- `application/x-simifactu-certificate` - Certificado
- `application/x-simifactu-verifactu` - Datos Verifactu
### 4.6 Mensaje CLOSE
```
┌─────────────────────────────────────────────────────────────┐
│ CLOSE Message │
├─────────────────────────────────────────────────────────────┤
│ reason_len: u8 │ Longitud del motivo │
│ reason: [N]u8 │ Motivo del cierre │
└─────────────────────────────────────────────────────────────┘
```
## 5. Discovery Protocol
### 5.1 Local Discovery (LAN)
#### 5.1.1 Formato de Anuncio
Enviado via UDP broadcast (IPv4) o multicast (IPv6):
```
Puerto: 21027
IPv4 Broadcast: 255.255.255.255:21027
IPv6 Multicast: [ff12::8384]:21027
┌─────────────────────────────────────────────────────────────┐
│ Local Announce Packet │
├─────────────────────────────────────────────────────────────┤
│ magic: u32 │ 0x2EA7D90B │
│ device_id: [32]u8 │ SHA256 del certificado │
│ instance_id: i64 │ ID único por ejecución │
│ num_addresses: u8 │ Número de direcciones │
│ addresses: [N]Address │ Lista de direcciones │
└─────────────────────────────────────────────────────────────┘
Address:
│ addr_len: u8 │ Longitud de la URL │
│ addr: [N]u8 │ URL (ej: "tcp://192.168.1.5:22000") │
```
#### 5.1.2 Frecuencia de Anuncios
- Intervalo normal: 30 segundos
- Al detectar nuevo dispositivo: inmediatamente
- Caducidad del cache: 90 segundos
### 5.2 Global Discovery (Internet)
#### 5.2.1 Servidor de Discovery
El servidor de discovery es un servicio HTTPS que:
1. Recibe anuncios de dispositivos
2. Responde consultas sobre dispositivos
Para funcionar en Internet, necesitas al menos un servidor de discovery.
Puedes usar servidores públicos de Syncthing o ejecutar uno propio.
#### 5.2.2 Anuncio (POST)
```
POST https://discovery.example.com/v2/
Content-Type: application/json
Authorization: (certificado TLS cliente)
{
"addresses": [
"tcp://203.0.113.45:22000",
"relay://relay.example.com:22067/?id=XXXXX"
]
}
```
El Device ID se extrae del certificado TLS del cliente.
#### 5.2.3 Consulta (GET)
```
GET https://discovery.example.com/v2/?device=XXXXXXX-XXXXXXX-...
Response:
{
"addresses": [
"tcp://203.0.113.45:22000",
"relay://relay.example.com:22067/?id=XXXXX"
]
}
```
#### 5.2.4 Headers de Control
```
Response Headers:
- Reannounce-After: 1800 // Segundos hasta próximo anuncio
- Retry-After: 60 // En caso de error
Status Codes:
- 200: OK
- 400: Bad Request
- 403: Forbidden (sin certificado)
- 404: Device not found
- 429: Too Many Requests
```
## 6. NAT Traversal
### 6.1 Estrategia de Conexión
Orden de intentos para conectar con un peer:
1. **Conexión directa TCP**: Si tenemos IP directa
2. **Conexión directa con STUN**: Usar IP externa descubierta
3. **Hole punching**: Ambos peers intentan simultáneamente
4. **Relay**: Último recurso si todo falla
### 6.2 STUN (Session Traversal Utilities for NAT)
Usamos STUN para descubrir nuestra IP externa y tipo de NAT.
#### 6.2.1 Servidores STUN públicos
```
stun.l.google.com:19302
stun1.l.google.com:19302
stun.syncthing.net:3478
```
#### 6.2.2 Tipos de NAT detectables
```
Tipo Hole-punchable? Descripción
──── ─────────────── ───────────
Full Cone Sí Puerto externo fijo
Restricted Cone Sí Requiere envío previo
Port Restricted Cone Sí + puerto específico
Symmetric No Puerto diferente por destino
```
### 6.3 Relay Protocol
Cuando no es posible conexión directa, usamos un servidor relay.
#### 6.3.1 Conectarse al Relay
```
1. Cliente se conecta al relay via TLS
2. Envía JoinRelayRequest
3. Relay responde con SessionInvitation
4. Cliente A publica su dirección de relay en discovery
5. Cliente B consulta discovery, ve dirección relay
6. Cliente B conecta al relay, envía ConnectRequest(device_id_A)
7. Relay notifica a A, ambos reciben SessionInvitation
8. Relay hace de proxy transparente
```
#### 6.3.2 Mensajes del Protocolo Relay
```
JoinRelayRequest:
│ token_len: u8 │
│ token: [N]u8 │ Token de autenticación (opcional)
ConnectRequest:
│ device_id: [32]u8 │ ID del dispositivo destino
SessionInvitation:
│ from: [32]u8 │ Device ID del peer
│ key: [32]u8 │ Clave de sesión
│ address: [16]u8 │ IP del relay
│ port: u16 │ Puerto
│ is_server: bool │ ¿Somos el "servidor"?
Response:
│ code: i32 │ 0=OK, 1=NotFound, 2=AlreadyConnected
│ message_len: u8 │
│ message: [N]u8 │
```
## 7. Cifrado Adicional (Opcional)
Además del cifrado TLS, se puede añadir cifrado a nivel de aplicación.
### 7.1 ChaCha20-Poly1305
```
Nonce size: 24 bytes (XChaCha20)
Tag size: 16 bytes
Key size: 32 bytes
Encrypted = Nonce || ChaCha20-Poly1305(Key, Nonce, Plaintext)
```
### 7.2 Derivación de Claves
Para cifrado de datos específicos:
```
FileKey = HKDF-SHA256(
IKM = FolderKey || filename,
salt = "zcatp2p",
info = empty
)
```
## 8. Compresión
### 8.1 LZ4
Usamos LZ4 para compresión de mensajes grandes (>128 bytes):
```
Compressed = u32_be(original_size) || LZ4_compress(data)
```
Solo comprimimos si el resultado es al menos 3% más pequeño.
## 9. Keepalive y Timeouts
```
Intervalo de PING: 90 segundos
Timeout de recepción: 300 segundos (5 min)
Timeout de conexión: 30 segundos
Timeout de close: 10 segundos
```
## 10. Puertos por Defecto
```
Puerto Uso
───── ───
22000 Conexiones P2P directas
21027 Local discovery (UDP)
22067 Relay server
22070 Relay status (HTTP)
443 Global discovery (HTTPS)
```
## 11. URLs de Direcciones
```
Formato Ejemplo
─────── ───────
tcp://host:port tcp://192.168.1.5:22000
tcp4://host:port tcp4://192.168.1.5:22000
tcp6://host:port tcp6://[::1]:22000
relay://host:port/?id=XXX relay://relay.example.com:22067/?id=ABCDEFG
```
## 12. Consideraciones de Seguridad
1. **Certificados**: Generar con entropía suficiente
2. **Device ID**: Verificar siempre después del handshake TLS
3. **Replay attacks**: Usar timestamps y nonces únicos
4. **DoS**: Limitar conexiones por IP, rate limiting
5. **Man-in-the-middle**: Verificar Device ID conocidos
6. **Relay**: El relay ve metadatos pero NO el contenido (TLS end-to-end)
## 13. Ejemplo de Flujo Completo
```
Empresa A quiere enviar factura a Empresa B
1. A tiene el Device ID de B (intercambiado previamente, ej: QR)
2. A busca a B:
a. Busca en cache local
b. Envía broadcast LAN
c. Consulta discovery global
3. A obtiene direcciones de B:
["tcp://203.0.113.45:22000", "relay://relay.example.com:22067/?id=B"]
4. A intenta conectar:
a. Intenta TCP directo → falla (NAT)
b. Intenta relay → éxito
5. Handshake TLS:
- A presenta certificado
- B presenta certificado
- Ambos verifican Device IDs
6. Intercambio HELLO:
- A envía sus capacidades
- B responde con las suyas
7. A envía factura:
DATA {
message_id: 1,
content_type: "application/x-simifactu-invoice",
data: <factura_serializada>
}
8. B confirma:
DATA_ACK { message_id: 1 }
9. Cierre:
A envía CLOSE { reason: "transfer complete" }
B cierra conexión
```