5. Record Protocol (Protocollo di record)
Il protocollo di record TLS prende i messaggi da trasmettere, frammenta i dati in blocchi gestibili, protegge i record e trasmette il risultato. I dati ricevuti vengono verificati, decifrati, riassemblati e poi consegnati ai client di livello superiore.
I record TLS sono tipizzati, il che consente di multiplexare più protocolli di livello superiore sullo stesso livello di record. Questo documento specifica quattro tipi di contenuto: handshake, application_data, alert e change_cipher_spec. Il record change_cipher_spec è usato solo a fini di compatibilità (vedere l'appendice D.4).
Un'implementazione può (MAY) ricevere un record non cifrato di tipo change_cipher_spec costituito dal singolo valore di byte 0x01 in qualsiasi momento dopo che il primo messaggio ClientHello è stato inviato o ricevuto e prima che sia stato ricevuto il messaggio Finished del peer, e deve (MUST) limitarsi a scartarlo senza ulteriori elaborazioni. Si noti che questo record può comparire in un punto dell'handshake in cui l'implementazione si aspetta record protetti, quindi è necessario rilevare questa condizione prima di tentare di deproteggere il record. Le implementazioni che ricevono qualsiasi altro valore change_cipher_spec o che ricevono un record change_cipher_spec protetto devono (MUST) interrompere l'handshake con un avviso "unexpected_message". Se un'implementazione rileva un record change_cipher_spec ricevuto prima del primo messaggio ClientHello o dopo il messaggio Finished del peer, esso deve (MUST) essere trattato come un tipo di record imprevisto (sebbene i server stateless possano non essere in grado di distinguere questi casi da quelli consentiti).
Le implementazioni non devono (MUST NOT) inviare tipi di record non definiti in questo documento, a meno che non siano negoziati da qualche estensione. Se un'implementazione TLS riceve un tipo di record imprevisto, deve (MUST) terminare la connessione con un avviso "unexpected_message". I nuovi valori dei tipi di contenuto dei record sono assegnati da IANA nel registro TLS ContentType come descritto nella sezione 11.
5.1. Record Layer (Livello di record)
Il livello di record frammenta i blocchi di informazioni in record TLSPlaintext che trasportano dati in blocchi di 2^14 byte o meno. I confini dei messaggi sono gestiti in modo diverso a seconda del ContentType sottostante. Qualsiasi tipo di contenuto futuro deve (MUST) specificare regole appropriate. Si noti che queste regole sono più rigorose di quelle applicate in TLS 1.2.
I messaggi di handshake possono (MAY) essere uniti in un singolo record TLSPlaintext oppure frammentati su più record, a condizione che:
-
I messaggi di handshake non devono (MUST NOT) essere interlacciati con altri tipi di record. Vale a dire, se un messaggio di handshake è diviso su due o più record, non deve (MUST NOT) esserci alcun altro record tra di essi.
-
I messaggi di handshake non devono (MUST NOT) attraversare i cambi di chiave. Le implementazioni devono (MUST) verificare che tutti i messaggi immediatamente precedenti un cambio di chiave siano allineati a un confine di record; in caso contrario, devono (MUST) terminare la connessione con un avviso "unexpected_message". Poiché i messaggi ClientHello, EndOfEarlyData, ServerHello, Finished e KeyUpdate possono immediatamente precedere un cambio di chiave, le implementazioni devono (MUST) inviare questi messaggi allineati a un confine di record.
Le implementazioni non devono (MUST NOT) inviare frammenti di lunghezza zero di tipo Handshake, anche se tali frammenti contengono padding.
I messaggi di avviso (sezione 6) non devono (MUST NOT) essere frammentati su più record e più messaggi di avviso non devono (MUST NOT) essere uniti in un singolo record TLSPlaintext. In altre parole, un record di tipo Alert deve (MUST) contenere esattamente un messaggio.
I messaggi Application Data contengono dati opachi per TLS. I messaggi Application Data sono sempre protetti. I frammenti di lunghezza zero di Application Data possono (MAY) essere inviati, poiché sono potenzialmente utili come contromisura all'analisi del traffico. I frammenti di Application Data possono (MAY) essere suddivisi su più record o uniti in un singolo record.
enum {
invalid(0),
change_cipher_spec(20),
alert(21),
handshake(22),
application_data(23),
(255)
} ContentType;
struct {
ContentType type;
ProtocolVersion legacy_record_version;
uint16 length;
opaque fragment[TLSPlaintext.length];
} TLSPlaintext;
type: Il protocollo di livello superiore usato per elaborare il frammento incapsulato.
legacy_record_version: deve (MUST) essere impostato a 0x0303 per tutti i record generati da un'implementazione di TLS 1.3 diversi da un ClientHello iniziale (cioè uno non generato dopo un HelloRetryRequest), dove può (MAY) anche essere 0x0301 per scopi di compatibilità. Questo campo è deprecato e deve (MUST) essere ignorato per qualsiasi scopo. Le versioni precedenti di TLS usavano altri valori in questo campo in alcune circostanze.
length: La lunghezza (in byte) del seguente TLSPlaintext.fragment. La lunghezza non deve (MUST NOT) superare 2^14 byte. Un endpoint che riceve un record che supera questa lunghezza deve (MUST) terminare la connessione con un avviso "record_overflow".
fragment: I dati in trasmissione. Questo valore è trasparente ed è trattato come un blocco indipendente da gestire da parte del protocollo di livello superiore specificato dal campo type.
Questo documento descrive TLS 1.3, che usa la versione 0x0304. Questo valore di versione è storico e deriva dall'uso di 0x0301 per TLS 1.0 e 0x0300 per SSL 3.0. Per massimizzare la compatibilità retroattiva, un record contenente un ClientHello iniziale dovrebbe (SHOULD) avere versione 0x0301 (che riflette TLS 1.0) e un record contenente un secondo ClientHello o un ServerHello deve (MUST) avere versione 0x0303 (che riflette TLS 1.2). Quando si negoziano versioni precedenti di TLS, gli endpoint seguono la procedura e i requisiti forniti nell'appendice D.
Quando la protezione dei record non è ancora stata attivata, le strutture TLSPlaintext vengono scritte direttamente sul filo. Una volta avviata la protezione dei record, i record TLSPlaintext vengono protetti e inviati come descritto nella sezione seguente. Si noti che i record Application Data non devono (MUST NOT) essere scritti sul filo senza protezione (per i dettagli vedere la sezione 2).
5.2. Record Payload Protection (Protezione del payload del record)
Le funzioni di protezione dei record traducono una struttura TLSPlaintext in una struttura TLSCiphertext. Le funzioni di deprotezione invertono il processo. In TLS 1.3, a differenza delle versioni precedenti di TLS, tutti i cifrari sono modellati come "Authenticated Encryption with Associated Data" (AEAD) [RFC5116]. Le funzioni AEAD forniscono un'operazione unificata di cifratura e autenticazione che trasforma il testo in chiaro in testo cifrato autenticato e viceversa. Ogni record cifrato è costituito da un'intestazione in chiaro seguita da un corpo cifrato, che a sua volta contiene un tipo e un padding opzionale.
struct {
opaque content[TLSPlaintext.length];
ContentType type;
uint8 zeros[length_of_padding];
} TLSInnerPlaintext;
struct {
ContentType opaque_type = application_data; /* 23 */
ProtocolVersion legacy_record_version = 0x0303; /* TLS v1.2 */
uint16 length;
opaque encrypted_record[TLSCiphertext.length];
} TLSCiphertext;
content: Il valore TLSPlaintext.fragment, contenente la codifica in byte di un messaggio di handshake o di avviso, oppure i byte grezzi dei dati dell'applicazione da inviare.
type: Il valore TLSPlaintext.type contenente il tipo di contenuto del record.
zeros: Dopo il campo type può (MAY) comparire nel testo in chiaro una sequenza di byte di valore zero di lunghezza arbitraria. Ciò offre ai mittenti l'opportunità di riempire qualsiasi record TLS di una quantità scelta, purché il totale rimanga entro i limiti di dimensione del record. Per maggiori dettagli vedere la sezione 5.4.
opaque_type: Il campo opaque_type esterno di un record TLSCiphertext è sempre impostato al valore 23 (application_data) per compatibilità verso l'esterno con le middlebox abituate ad analizzare le versioni precedenti di TLS. Il tipo di contenuto effettivo del record si trova in TLSInnerPlaintext.type dopo la decifratura.
legacy_record_version: Il campo legacy_record_version è sempre 0x0303. I TLSCiphertext di TLS 1.3 non vengono generati prima che TLS 1.3 sia stato negoziato, quindi non vi sono preoccupazioni di compatibilità storica per cui potrebbero essere ricevuti altri valori. Si noti che il protocollo di handshake, inclusi i messaggi ClientHello e ServerHello, autentica la versione del protocollo, quindi questo valore è ridondante.
length: La lunghezza (in byte) del seguente TLSCiphertext.encrypted_record, che è la somma delle lunghezze del contenuto e del padding, più uno per il tipo di contenuto interno, più qualsiasi espansione aggiunta dall'algoritmo AEAD. La lunghezza non deve (MUST NOT) superare 2^14 + 256 byte. Un endpoint che riceve un record che supera questa lunghezza deve (MUST) terminare la connessione con un avviso "record_overflow".
encrypted_record: La forma cifrata AEAD della struttura TLSInnerPlaintext serializzata.
Gli algoritmi AEAD prendono come input una singola chiave, un nonce, un testo in chiaro e "dati aggiuntivi" da includere nel controllo di autenticazione, come descritto nella sezione 2.1 di [RFC5116]. La chiave è client_write_key o server_write_key, il nonce è derivato dal numero di sequenza e da client_write_iv o server_write_iv (vedere la sezione 5.3), e l'input dei dati aggiuntivi è l'intestazione del record.
cioè,
additional_data = TLSCiphertext.opaque_type ||
TLSCiphertext.legacy_record_version ||
TLSCiphertext.length
L'input in chiaro per l'algoritmo AEAD è la struttura TLSInnerPlaintext codificata. La derivazione delle chiavi di traffico è definita nella sezione 7.3.
L'output AEAD consiste nell'output di testo cifrato dell'operazione di cifratura AEAD. La lunghezza del testo in chiaro è maggiore del corrispondente TLSPlaintext.length a causa dell'inclusione di TLSInnerPlaintext.type e di qualsiasi padding fornito dal mittente. La lunghezza dell'output AEAD sarà generalmente maggiore del testo in chiaro, ma di una quantità che varia con l'algoritmo AEAD. Poiché i cifrari possono incorporare padding, la quantità di overhead può variare con lunghezze diverse del testo in chiaro. Simbolicamente,
AEADEncrypted =
AEAD-Encrypt(write_key, nonce, additional_data, plaintext)
Il campo encrypted_record di TLSCiphertext è impostato a AEADEncrypted.
Per decifrare e verificare, il cifrario prende come input la chiave, il nonce, i dati aggiuntivi e il valore AEADEncrypted. L'output è il testo in chiaro oppure un errore che indica che la decifratura è fallita. Non esiste un controllo di integrità separato. Simbolicamente,
plaintext of encrypted_record =
AEAD-Decrypt(peer_write_key, nonce,
additional_data, AEADEncrypted)
Se la decifratura fallisce, il ricevente deve (MUST) terminare la connessione con un avviso "bad_record_mac".
Un algoritmo AEAD usato in TLS 1.3 non deve (MUST NOT) produrre un'espansione maggiore di 255 ottetti. Un endpoint che riceve dal proprio peer un record con TLSCiphertext.length maggiore di 2^14 + 256 ottetti deve (MUST) terminare la connessione con un avviso "record_overflow". Questo limite deriva dalla lunghezza massima di TLSInnerPlaintext di 2^14 ottetti + 1 ottetto per ContentType + l'espansione massima AEAD di 255 ottetti.
5.3. Per-Record Nonce (Nonce per record)
Un numero di sequenza a 64 bit è mantenuto separatamente per la lettura e la scrittura dei record. Il numero di sequenza appropriato viene incrementato di uno dopo la lettura o la scrittura di ogni record. Ogni numero di sequenza è impostato a zero all'inizio di una connessione e ogni volta che la chiave viene cambiata; il primo record trasmesso sotto una particolare chiave di traffico deve (MUST) usare il numero di sequenza 0.
Poiché la dimensione dei numeri di sequenza è di 64 bit, non dovrebbero andare in wrap-around. Se un'implementazione TLS dovesse aver bisogno di far riavvolgere un numero di sequenza, deve (MUST) rieseguire la derivazione delle chiavi (sezione 4.6.3) oppure terminare la connessione.
Ogni algoritmo AEAD specifica un intervallo di lunghezze possibili per il nonce per record, da N_MIN byte a N_MAX byte di input [RFC5116]. La lunghezza del nonce per record di TLS (iv_length) è impostata al maggiore tra 8 byte e N_MIN per l'algoritmo AEAD (vedere [RFC5116], sezione 4). Un algoritmo AEAD in cui N_MAX è inferiore a 8 byte non deve (MUST NOT) essere usato con TLS. Il nonce per record per la costruzione AEAD è formato come segue:
-
Il numero di sequenza del record a 64 bit è codificato in ordine di byte di rete e riempito a sinistra con zeri fino a iv_length.
-
Il numero di sequenza riempito è sottoposto a XOR con client_write_iv o server_write_iv statico (a seconda del ruolo).
La quantità risultante (di lunghezza iv_length) è usata come nonce per record.
Nota: questa è una costruzione diversa da quella di TLS 1.2, che specificava un nonce parzialmente esplicito.
5.4. Record Padding (Padding del record)
Tutti i record TLS cifrati possono essere riempiti per aumentare la dimensione del TLSCiphertext. Ciò consente al mittente di nascondere la dimensione del traffico a un osservatore.
Quando genera un record TLSCiphertext, un'implementazione può (MAY) scegliere di applicare padding. Un record senza padding è semplicemente un record con lunghezza di padding pari a zero. Il padding è una stringa di byte di valore zero aggiunta al campo ContentType prima della cifratura. Le implementazioni devono (MUST) impostare a zero tutti gli ottetti di padding prima della cifratura.
I record Application Data possono contenere un TLSInnerPlaintext.content di lunghezza zero se il mittente lo desidera. Ciò consente di generare traffico di copertura di dimensioni plausibili in contesti in cui la presenza o l'assenza di attività può essere sensibile. Le implementazioni non devono (MUST NOT) inviare record Handshake e Alert che abbiano un TLSInnerPlaintext.content di lunghezza zero; se viene ricevuto un tale messaggio, l'implementazione ricevente deve (MUST) terminare la connessione con un avviso "unexpected_message".
Il padding inviato è verificato automaticamente dal meccanismo di protezione dei record; dopo la decifratura riuscita di un TLSCiphertext.encrypted_record, l'implementazione ricevente analizza il campo dalla fine verso l'inizio finché non trova un ottetto diverso da zero. Questo ottetto diverso da zero è il tipo di contenuto del messaggio. Questo schema di padding è stato scelto perché consente di riempire qualsiasi record TLS cifrato di una dimensione arbitraria (da zero fino ai limiti di dimensione del record TLS) senza introdurre nuovi tipi di contenuto. Il design impone inoltre che tutti gli ottetti di padding siano zero, il che consente una rapida individuazione degli errori di padding.
Le implementazioni devono (MUST) limitare la scansione al testo in chiaro restituito dalla decifratura AEAD. Se un'implementazione ricevente non trova un ottetto diverso da zero nel testo in chiaro, deve (MUST) terminare la connessione con un avviso "unexpected_message".
La presenza del padding non modifica i limiti complessivi di dimensione dei record: il TLSInnerPlaintext completo codificato non deve (MUST NOT) superare 2^14 + 1 ottetti. Se la lunghezza massima del frammento viene ridotta — come, ad esempio, dall'estensione record_size_limit di [RFC8449] — il limite ridotto si applica al testo in chiaro completo, incluso il tipo di contenuto e il padding.
Selezionare una politica di padding che suggerisca quando e quanto applicarlo è un argomento complesso e va oltre lo scopo di questa specifica. Se il protocollo di livello applicativo sopra TLS ha un proprio padding, può essere preferibile applicare il padding ai record TLS di Application Data all'interno del livello applicativo. Il padding per i record Handshake o Alert cifrati deve comunque essere gestito a livello TLS. Documenti successivi potranno definire algoritmi di selezione del padding oppure definire un meccanismo di richiesta di politica di padding tramite estensioni TLS o altri mezzi.
5.5. Limits on Key Usage (Limiti sull'uso delle chiavi)
Esistono limiti crittografici sulla quantità di testo in chiaro che può essere cifrata in sicurezza con un dato insieme di chiavi. [AEAD-LIMITS] fornisce un'analisi di questi limiti partendo dal presupposto che la primitiva sottostante (AES o ChaCha20) non abbia debolezze. Le implementazioni dovrebbero (SHOULD) eseguire un aggiornamento delle chiavi come descritto nella sezione 4.6.3 prima di raggiungere questi limiti.
Per AES-GCM, è possibile cifrare fino a 2^24.5 record di dimensione piena (circa 24 milioni) su una data connessione mantenendo un margine di sicurezza di circa 2^-57 per la sicurezza Authenticated Encryption (AE). Per ChaCha20/Poly1305, il numero di sequenza dei record andrebbe in wrap-around prima che il limite di sicurezza venga raggiunto.