Il protocollo
Ecco cosa deve parlare un feeder, un relay o un client per interoperare. Il riferimento è il crate wire nel repository di Tally, l’unico codice del repository che codifica o decodifica il protocollo, e ogni binario lo usa. Gli interi sono sempre big-endian.
Trasporto
Ogni tratta usa Noise_IK_25519_ChaChaPoly_BLAKE2s su TCP. Ogni messaggio Noise è preceduto da una lunghezza di due byte, è lungo al massimo 65.535 byte tag compreso e trasporta solo record interi.
L’identità è un’unica chiave Ed25519. La chiave X25519 che Noise autentica ne è derivata (sk_to_curve25519 di libsodium), e il primo messaggio di handshake dell’iniziatore contiene la sua chiave pubblica Ed25519 di 32 byte, che il rispondente confronta con la chiave dimostrata dall’handshake. Un’unica chiave identifica un’antenna ovunque.
Il preambolo
Prima dell’handshake l’iniziatore invia 16 byte in chiaro. Sono anche il prologo Noise, quindi un byte alterato fa fallire l’handshake, e permettono all’handshake stesso di cambiare in futuro, e a un relay di tenere una chiave vecchia e una nuova durante una rotazione.
Versioni
Poi l’iniziatore invia Hello con la versione di protocollo più bassa e quella più alta che parla, e il suo ruolo. Il rispondente risponde con Welcome e la versione che entrambi parleranno da quel momento, oppure con Goodbye, un motivo e quanto attendere. Dal primo byte a Welcome passano al massimo 15 secondi, e ogni invio successivo ha una scadenza di 30. Finora la versione di protocollo 1 è l’unica.
Un relay accetta per sempre ogni versione di protocollo che un feeder abbia mai distribuito. I feeder installati non si aggiornano, e tutto il resto del protocollo si regge su questa regola.
Record
Un record è un byte di tipo, una lunghezza del corpo di due byte e il corpo. Questi sono i tipi che conosce la versione 1 del protocollo:
Regole per l’evoluzione
I feeder non si aggiornano mai, quindi queste sono le regole che permettono al protocollo di cambiare senza di loro:
- Un tipo sconosciuto viene saltato, a meno che il suo bit più alto (0x80) lo marchi come critico, nel qual caso la sessione si chiude in modo pulito. Per questo i nuovi record non sono critici, a meno che ignorarli sia sbagliato.
- Un corpo può contenere byte dopo i campi che un lettore conosce, e questi vengono ignorati. I campi nuovi si aggiungono in coda.
- Un lettore conserva un valore di enum che non conosce come il valore Other, mai come errore.
- Una forma firmata mantiene lo stesso prefisso in ogni versione di formato e termina con la sua firma su tutto ciò che la precede, così un relay può verificare, instradare e deduplicare un formato che per il resto non sa leggere.
- Il formato di un’osservazione è il byte di tipo di BEAST stesso per i 1090 MHz. L’UAT a 978 MHz usa nuovi valori e non richiede un nuovo record, e i relay inoltrano i formati che non conoscono.
Il batch firmato
Un batch corrisponde a circa un secondo di ricezioni di un singolo ricevitore. Il formato 1 è strutturato così:
Formati delle osservazioni: 1 è Mode A/C (2 byte), 2 Mode-S corto (7), 3 Mode-S lungo (14), D un downlink UAT a 978 MHz, U un uplink UAT da terra, ciascuno scritto come il proprio carattere ASCII.
La firma copre il testo ASCII tally batch, un byte zero e ogni byte del batch prima della firma. I batch deduplicati di un relay firmano invece tally relay batch, così l’uno non può mai passare per l’altro.
L’identità di un batch è l’hash BLAKE2s-256 di tutti i suoi byte, firma inclusa. I relay deduplicano in base a essa, e il campo Precedente del batch successivo vi fa riferimento.
I primi 49 byte (formato, chiave, ID di avvio e sequenza) mantengono la loro posizione in ogni formato futuro, e la firma viene sempre per ultima.
Nessuno calibra mai i contatori prima della MLAT: la MLAT di un relay ha bisogno del contatore grezzo per calcolare la soluzione.