# 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: } 8. B confirma: DATA_ACK { message_id: 1 } 9. Cierre: A envía CLOSE { reason: "transfer complete" } B cierra conexión ```