El protocolo
Esto es lo que debe hablar un feeder, un relay o un cliente para interoperar. La referencia es el crate wire del repositorio de Tally, el único código del repositorio que codifica o decodifica el protocolo, y todos los binarios lo usan. Todos los enteros son big-endian.
Transporte
Cada salto es Noise_IK_25519_ChaChaPoly_BLAKE2s sobre TCP. Cada mensaje Noise se envía precedido de una longitud de dos bytes, con un máximo de 65 535 bytes incluida su etiqueta, y solo transporta registros completos.
La identidad es una única clave Ed25519. La clave X25519 que autentica Noise se deriva de ella (sk_to_curve25519 de libsodium), y el primer mensaje del handshake del iniciador lleva su clave pública Ed25519 de 32 bytes, que el respondedor comprueba contra la clave que demostró el handshake. Una sola clave identifica una antena en todas partes.
El preámbulo
Antes del handshake, el iniciador envía 16 bytes en claro. Son también el prólogo de Noise, así que un byte alterado hace fallar el handshake, y permiten que el propio handshake cambie en el futuro y que un relay mantenga una clave antigua y una nueva mientras la rota.
Versiones
Después, el iniciador envía Hello con las versiones de protocolo mínima y máxima que habla y su rol. El respondedor contesta con Welcome y la versión que ambos hablarán a partir de entonces, o con Goodbye, un motivo y cuánto esperar. Desde el primer byte hasta Welcome pasan como máximo 15 segundos, y cada envío posterior tiene un plazo de 30. Por ahora, la versión de protocolo 1 es la única.
Un relay responde a todas las versiones de protocolo que haya incluido alguna vez un feeder, para siempre. Los feeders instalados no se actualizan, y el resto del protocolo se apoya en esta regla.
Registros
Un registro es un byte de tipo, una longitud de cuerpo de dos bytes y el cuerpo. Estos son los tipos que conoce la versión de protocolo 1:
Reglas para el cambio
Los feeders nunca se actualizan, así que estas son las reglas que permiten que el protocolo cambie sin ellos:
- Un tipo desconocido se ignora, salvo que su bit alto (0x80) lo marque como crítico, lo que cierra la sesión de forma ordenada. Por tanto, los registros nuevos no son críticos salvo que ignorarlos fuera un error.
- Un cuerpo puede llevar bytes después de los campos que conoce un lector, y estos se ignoran. Los campos se añaden al final.
- Un lector conserva un valor de enum que no conoce como el valor Other, nunca como un error.
- Una forma firmada mantiene el mismo prefijo en todas las versiones de formato y termina con su firma sobre todo lo anterior, así que un relay puede verificar, enrutar y deduplicar un formato que, por lo demás, no sabe leer.
- El formato de una observación es el propio byte de tipo de BEAST para 1090 MHz. UAT de 978 MHz usa valores nuevos y no necesita un registro nuevo, y los relays reenvían los formatos que no conocen.
El lote firmado
Un lote contiene alrededor de un segundo de recepciones de un receptor. El formato 1 se organiza así:
Formatos de observación: 1 es Mode A/C (2 bytes), 2 Mode-S corto (7), 3 Mode-S largo (14), D un downlink UAT de 978 MHz y U un uplink UAT de tierra, cada uno escrito como su carácter ASCII.
La firma cubre el texto ASCII tally batch, un byte cero y todos los bytes del lote anteriores a la firma. Los lotes deduplicados propios de un relay firman en su lugar tally relay batch, así que uno nunca puede hacerse pasar por el otro.
La identidad de un lote es el hash BLAKE2s-256 de todos sus bytes, firma incluida. Los relays deduplican con él, y el campo Anterior del siguiente lote lo nombra.
Los primeros 49 bytes (formato, clave, id de arranque y secuencia) mantienen su posición en todos los formatos futuros, y la firma siempre va al final.
Nadie calibra nunca los contadores antes de que lleguen a la MLAT: la MLAT de un relay necesita el contador en bruto para resolver.