6. Le protocole d'enregistrement TLS (The TLS Record Protocol)
Le protocole d'enregistrement TLS (TLS Record Protocol) est un protocole en couches. À chaque couche, les messages peuvent inclure des champs pour la longueur, la description et le contenu. Le protocole d'enregistrement prend les messages à transmettre, fragmente les données en blocs gérables, compresse éventuellement les données, applique un MAC, chiffre, puis transmet le résultat. Les données reçues sont déchiffrées, vérifiées, décompressées, réassemblées, puis livrées aux clients de niveau supérieur.
Quatre protocoles utilisant le protocole d'enregistrement sont décrits dans ce document: le protocole de poignée de main, le protocole d'alerte, le protocole de changement de spécification de chiffrement et le protocole de données d'application. Afin de permettre l'extension du protocole TLS, le protocole d'enregistrement peut prendre en charge des types de contenu d'enregistrement supplémentaires. Les nouvelles valeurs de type de contenu d'enregistrement sont attribuées par l'IANA dans le registre des types de contenu TLS, comme décrit dans la section 12.
Les implémentations NE DOIVENT PAS (MUST NOT) envoyer de types d'enregistrement non définis dans ce document, sauf s'ils sont négociés par une extension. Si une implémentation TLS reçoit un type d'enregistrement inattendu, elle DOIT (MUST) envoyer une alerte unexpected_message.
Tout protocole conçu pour être utilisé sur TLS doit être soigneusement conçu pour faire face à toutes les attaques possibles contre lui. En pratique, cela signifie que le concepteur du protocole doit être conscient des propriétés de sécurité que TLS fournit et ne fournit pas, et ne peut pas s'appuyer en toute sécurité sur ces dernières.
Notez en particulier que le type et la longueur d'un enregistrement ne sont pas protégés par le chiffrement. Si ces informations sont elles-mêmes sensibles, les concepteurs d'applications peuvent souhaiter prendre des mesures (remplissage, trafic de couverture) pour minimiser les fuites d'informations.
6.1. États de connexion (Connection States)
Un état de connexion TLS (TLS connection state) est l'environnement opérationnel du protocole d'enregistrement TLS. Il spécifie un algorithme de compression, un algorithme de chiffrement et un algorithme MAC. De plus, les paramètres de ces algorithmes sont connus: la clé MAC et les clés de chiffrement en bloc pour la connexion dans les deux directions de lecture et d'écriture. Logiquement, il y a toujours quatre états de connexion en cours: les états de lecture et d'écriture actuels, et les états de lecture et d'écriture en attente. Tous les enregistrements sont traités sous les états de lecture et d'écriture actuels. Les paramètres de sécurité pour les états en attente peuvent être définis par les protocoles de poignée de main TLS, et le ChangeCipherSpec peut sélectivement rendre l'un des états en attente actuel, auquel cas l'état actuel approprié est supprimé et remplacé par l'état en attente; l'état en attente est ensuite réinitialisé à un état vide. Il est illégal de rendre actuel un état qui n'a pas été initialisé avec des paramètres de sécurité. L'état actuel initial spécifie toujours qu'aucun chiffrement, compression ou MAC ne sera utilisé.
Les paramètres de sécurité pour les états de lecture et d'écriture d'une connexion TLS sont définis en fournissant les valeurs suivantes:
connection end (extrémité de connexion)
- Si cette entité est considérée comme le "client" ou le "serveur" dans cette connexion.
PRF algorithm (algorithme PRF)
- Un algorithme utilisé pour générer des clés à partir du secret maître (voir sections 5 et 6.3).
bulk encryption algorithm (algorithme de chiffrement en bloc)
- Un algorithme à utiliser pour le chiffrement en bloc. Cette spécification comprend la taille de clé de cet algorithme, qu'il s'agisse d'un chiffrement par bloc, par flux ou AEAD, la taille de bloc du chiffrement (le cas échéant) et les longueurs des vecteurs d'initialisation (ou nonces) explicites et implicites.
MAC algorithm (algorithme MAC)
- Un algorithme à utiliser pour l'authentification des messages. Cette spécification comprend la taille de la valeur renvoyée par l'algorithme MAC.
compression algorithm (algorithme de compression)
- Un algorithme à utiliser pour la compression des données. Cette spécification doit inclure toutes les informations nécessaires à l'algorithme pour effectuer la compression.
master secret (secret maître)
- Un secret de 48 octets partagé entre les deux pairs dans la connexion.
client random (aléatoire client)
- Une valeur de 32 octets fournie par le client.
server random (aléatoire serveur)
- Une valeur de 32 octets fournie par le serveur.
Ces paramètres sont définis dans le langage de présentation comme suit:
enum { server, client } ConnectionEnd;
enum { tls_prf_sha256 } PRFAlgorithm;
enum { null, rc4, 3des, aes }
BulkCipherAlgorithm;
enum { stream, block, aead } CipherType;
enum { null, hmac_md5, hmac_sha1, hmac_sha256,
hmac_sha384, hmac_sha512} MACAlgorithm;
enum { null(0), (255) } CompressionMethod;
/* The algorithms specified in CompressionMethod, PRFAlgorithm,
BulkCipherAlgorithm, and MACAlgorithm may be added to. */
struct {
ConnectionEnd entity;
PRFAlgorithm prf_algorithm;
BulkCipherAlgorithm bulk_cipher_algorithm;
CipherType cipher_type;
uint8 enc_key_length;
uint8 block_length;
uint8 fixed_iv_length;
uint8 record_iv_length;
MACAlgorithm mac_algorithm;
uint8 mac_length;
uint8 mac_key_length;
CompressionMethod compression_algorithm;
opaque master_secret[48];
opaque client_random[32];
opaque server_random[32];
} SecurityParameters;
La couche d'enregistrement utilisera les paramètres de sécurité pour générer les six éléments suivants (certains d'entre eux ne sont pas requis par tous les chiffrements et sont donc vides):
- client write MAC key (clé MAC d'écriture client)
- server write MAC key (clé MAC d'écriture serveur)
- client write encryption key (clé de chiffrement d'écriture client)
- server write encryption key (clé de chiffrement d'écriture serveur)
- client write IV (IV d'écriture client)
- server write IV (IV d'écriture serveur)
Les paramètres d'écriture du client sont utilisés par le serveur lors de la réception et du traitement des enregistrements et vice versa. L'algorithme utilisé pour générer ces éléments à partir des paramètres de sécurité est décrit dans la section 6.3.
Une fois que les paramètres de sécurité ont été définis et que les clés ont été générées, les états de connexion peuvent être instanciés en les rendant états actuels. Ces états actuels DOIVENT (MUST) être mis à jour pour chaque enregistrement traité. Chaque état de connexion comprend les éléments suivants:
compression state (état de compression)
- L'état actuel de l'algorithme de compression.
cipher state (état de chiffrement)
- L'état actuel de l'algorithme de chiffrement. Cela comprendra la clé planifiée pour cette connexion. Pour les chiffrements par flux, cela contiendra également toutes les informations d'état nécessaires pour permettre au flux de continuer à chiffrer ou déchiffrer des données.
MAC key (clé MAC)
- La clé MAC pour cette connexion, générée comme ci-dessus.
sequence number (numéro de séquence)
- Chaque état de connexion contient un numéro de séquence, qui est maintenu séparément pour les états de lecture et d'écriture. Le numéro de séquence DOIT (MUST) être défini à zéro chaque fois qu'un état de connexion devient l'état actif. Les numéros de séquence sont de type uint64 et ne peuvent pas dépasser 2^64-1. Les numéros de séquence ne bouclent pas. Si une implémentation TLS devait boucler un numéro de séquence, elle doit renégocier à la place. Un numéro de séquence est incrémenté après chaque enregistrement: en particulier, le premier enregistrement transmis sous un état de connexion particulier DOIT (MUST) utiliser le numéro de séquence 0.
6.2. Couche d'enregistrement (Record Layer)
La couche d'enregistrement TLS reçoit des données non interprétées des couches supérieures dans des blocs non vides de taille arbitraire.
6.2.1. Fragmentation
La couche d'enregistrement fragmente les blocs d'informations en enregistrements TLSPlaintext portant des données en morceaux de 2^14 octets ou moins. Les limites des messages client ne sont pas préservées dans la couche d'enregistrement (c'est-à-dire que plusieurs messages client du même ContentType PEUVENT (MAY) être fusionnés en un seul enregistrement TLSPlaintext, ou un seul message PEUT (MAY) être fragmenté sur plusieurs enregistrements).
struct {
uint8 major;
uint8 minor;
} ProtocolVersion;
enum {
change_cipher_spec(20), alert(21), handshake(22),
application_data(23), (255)
} ContentType;
struct {
ContentType type;
ProtocolVersion version;
uint16 length;
opaque fragment[TLSPlaintext.length];
} TLSPlaintext;
type
- Le protocole de niveau supérieur utilisé pour traiter le fragment joint.
version
- La version du protocole utilisé. Ce document décrit la version 1.2 de TLS, qui utilise la version 3. La valeur de version 3.3 est historique, dérivant de l'utilisation de 1 pour TLS 1.0. (Voir annexe A.1.) Notez qu'un client prenant en charge plusieurs versions de TLS peut ne pas savoir quelle version sera utilisée avant de recevoir le ServerHello. Voir l'annexe E pour une discussion sur le numéro de version de la couche d'enregistrement que le ClientHello devrait utiliser.
length
- La longueur (en octets) du TLSPlaintext.fragment suivant. La longueur NE DOIT PAS (MUST NOT) dépasser 2^14.
fragment
- Les données d'application. Ces données sont transparentes et traitées comme un bloc indépendant à traiter par le protocole de niveau supérieur spécifié par le champ type.
Les implémentations NE DOIVENT PAS (MUST NOT) envoyer de fragments de longueur nulle de types de contenu Handshake, Alert ou ChangeCipherSpec. Des fragments de longueur nulle de données d'application PEUVENT (MAY) être envoyés car ils peuvent être utiles comme contre-mesure d'analyse de trafic.
Note: Les données de différents types de contenu de la couche d'enregistrement TLS PEUVENT (MAY) être entrelacées. Les données d'application sont généralement de priorité inférieure pour la transmission par rapport aux autres types de contenu. Cependant, les enregistrements NE DOIVENT PAS (MUST NOT) chevaucher les limites des messages ChangeCipherSpec.
6.2.2. Compression et décompression d'enregistrement (Record Compression and Decompression)
Tous les enregistrements sont compressés à l'aide de l'algorithme de compression défini dans l'état de session actuel. Il y a toujours un algorithme de compression actif; cependant, il est initialement défini comme CompressionMethod.null. L'algorithme de compression traduit une structure TLSPlaintext en une structure TLSCompressed. La compression doit être sans perte et ne peut pas augmenter la longueur du contenu de plus de 1024 octets. Si la fonction de décompression rencontre un TLSCompressed.fragment qui se décompresse à une longueur supérieure à 2^14 octets, elle DOIT (MUST) signaler une alerte fatale decompression_failure.
struct {
ContentType type;
ProtocolVersion version;
uint16 length;
opaque fragment[TLSCompressed.length];
} TLSCompressed;
length
- La longueur (en octets) du TLSCompressed.fragment suivant. La longueur NE DOIT PAS (MUST NOT) dépasser 2^14 + 1024.
fragment
- La forme compressée de TLSPlaintext.fragment.
Note: Une opération CompressionMethod.null est une opération d'identité; aucun champ n'est modifié.
Note d'implémentation: Les fonctions de décompression sont responsables de s'assurer que les messages ne peuvent pas causer de débordement de tampon interne.
6.2.3. Protection de la charge utile d'enregistrement (Record Payload Protection)
Les fonctions de chiffrement et MAC traduisent une structure TLSCompressed en un TLSCiphertext. Les fonctions de déchiffrement inversent le processus. Le MAC de l'enregistrement inclut également un numéro de séquence afin que les messages manquants, supplémentaires ou répétés soient détectables.
struct {
ContentType type;
ProtocolVersion version;
uint16 length;
select (SecurityParameters.cipher_type) {
case stream: GenericStreamCipher;
case block: GenericBlockCipher;
case aead: GenericAEADCipher;
} fragment;
} TLSCiphertext;
type
- Le champ type est identique à TLSCompressed.type.
version
- Le champ version est identique à TLSCompressed.version.
length
- La longueur (en octets) du TLSCiphertext.fragment suivant. La longueur NE DOIT PAS (MUST NOT) dépasser 2^14 + 2048.
fragment
- La forme chiffrée de TLSCompressed.fragment, avec le MAC.
6.2.3.1. Chiffrement par flux nul ou standard (Null or Standard Stream Cipher)
Les chiffrements par flux (y compris BulkCipherAlgorithm.null; voir annexe A.6) convertissent les structures TLSCompressed.fragment en structures TLSCiphertext.fragment de flux.
stream-ciphered struct {
opaque content[TLSCompressed.length];
opaque MAC[SecurityParameters.mac_length];
} GenericStreamCipher;
Le MAC est généré comme suit:
MAC(MAC_write_key, seq_num +
TLSCompressed.type +
TLSCompressed.version +
TLSCompressed.length +
TLSCompressed.fragment);
où "+" désigne la concaténation.
seq_num
- Le numéro de séquence pour cet enregistrement.
MAC
- L'algorithme MAC spécifié dans SecurityParameters.mac_algorithm.
Notez que le MAC est calculé avant le chiffrement. Le chiffrement par flux chiffre le bloc entier, y compris le MAC.
6.2.3.2. Chiffrement par bloc CBC (CBC Block Cipher)
Pour les chiffrements par bloc (tels que 3DES ou AES), les fonctions de chiffrement et MAC convertissent les structures TLSCompressed.fragment en structures TLSCiphertext.fragment de bloc.
struct {
opaque IV[SecurityParameters.record_iv_length];
block-ciphered struct {
opaque content[TLSCompressed.length];
opaque MAC[SecurityParameters.mac_length];
uint8 padding[GenericBlockCipher.padding_length];
uint8 padding_length;
};
} GenericBlockCipher;
Le MAC est généré comme décrit dans la section 6.2.3.1.
IV
- Le vecteur d'initialisation (IV) DEVRAIT (SHOULD) être choisi au hasard et DOIT (MUST) être imprévisible. Notez que dans les versions de TLS antérieures à 1.1, il n'y avait pas de champ IV, et le dernier bloc de texte chiffré de l'enregistrement précédent (le "résidu CBC") était utilisé comme IV. Cela a été modifié pour empêcher les attaques décrites dans [CBCATT]. Pour les chiffrements par bloc, la longueur de l'IV est de SecurityParameters.record_iv_length, qui est égale à SecurityParameters.block_length.
padding
- Remplissage ajouté pour forcer la longueur du texte en clair à être un multiple entier de la longueur de bloc du chiffrement par bloc. Le remplissage PEUT (MAY) être de n'importe quelle longueur jusqu'à 255 octets, tant qu'il résulte en ce que TLSCiphertext.length soit un multiple entier de la longueur de bloc. Des longueurs plus longues que nécessaire peuvent être souhaitables pour contrecarrer les attaques contre un protocole qui sont basées sur l'analyse des longueurs des messages échangés. Chaque uint8 dans le vecteur de données de remplissage DOIT (MUST) être rempli avec la valeur de longueur de remplissage. Le récepteur DOIT (MUST) vérifier ce remplissage et DOIT (MUST) retourner une alerte bad_record_mac si le remplissage n'est pas correct. Le récepteur DEVRAIT (SHOULD) examiner le remplissage le moins possible afin de fournir un temps de réponse aussi uniforme que possible, que le remplissage soit correct ou non.
padding_length
- La longueur de remplissage DOIT (MUST) être telle que la taille totale de la structure GenericBlockCipher soit un multiple de la longueur de bloc du chiffrement. Les valeurs légales vont de zéro à 255, inclus. Cette longueur spécifie la longueur du champ de remplissage exclusif du champ padding_length lui-même.
La longueur des données chiffrées (TLSCiphertext.length) est supérieure de un à la somme de SecurityParameters.block_length, TLSCompressed.length, SecurityParameters.mac_length et padding_length.
Exemple: Si la longueur de bloc est de 8 octets, la longueur du contenu (TLSCompressed.length) est de 61 octets et la longueur du MAC est de 20 octets, alors la longueur avant remplissage est de 82 octets (cela n'inclut pas l'IV). Ainsi, la longueur de remplissage modulo 8 doit être égale à 6 afin de rendre la longueur totale un multiple pair de 8 octets (la longueur de bloc). La longueur de remplissage peut être de 6, 14, 22, et ainsi de suite, jusqu'à 254. Si la longueur de remplissage était le minimum nécessaire, 6, le remplissage serait de 6 octets, chacun contenant la valeur 6. Ainsi, les 8 derniers octets du GenericBlockCipher avant le chiffrement par bloc seraient xx 06 06 06 06 06 06 06, où xx est le dernier octet du MAC.
Note: Avec les chiffrements par bloc en mode CBC (Cipher Block Chaining), il est critique que tout le texte en clair de l'enregistrement soit connu avant la transmission de tout texte chiffré. Sinon, il est possible pour l'attaquant de monter l'attaque décrite dans [CBCATT].
Note d'implémentation: Canvel et al. [CBCTIME] ont démontré une attaque temporelle sur le remplissage CBC basée sur le temps requis pour calculer le MAC. Afin de se défendre contre cette attaque, les implémentations DOIVENT (MUST) s'assurer que le temps de traitement des enregistrements est essentiellement le même, que le remplissage soit correct ou non. En général, la meilleure façon de le faire est de calculer le MAC même si le remplissage est incorrect, et seulement ensuite rejeter le paquet. Par exemple, si le remplissage semble être incorrect, l'implémentation pourrait supposer un remplissage de longueur nulle, puis calculer le MAC. Cela laisse un petit canal temporel, car les performances du MAC dépendent dans une certaine mesure de la taille du fragment de données, mais on ne pense pas qu'il soit assez grand pour être exploitable, en raison de la grande taille de bloc des MAC existants et de la petite taille du signal temporel.
6.2.3.3. Chiffrements AEAD (AEAD Ciphers)
Pour les chiffrements AEAD [AEAD] (tels que CCM ou GCM), la fonction AEAD convertit les structures TLSCompressed.fragment en structures TLSCiphertext.fragment AEAD.
struct {
opaque nonce_explicit[SecurityParameters.record_iv_length];
aead-ciphered struct {
opaque content[TLSCompressed.length];
};
} GenericAEADCipher;
Les chiffrements AEAD prennent en entrée une seule clé, un nonce, un texte en clair et des "données supplémentaires" à inclure dans la vérification d'authentification, comme décrit dans la section 2.1 de [AEAD]. La clé est soit client_write_key soit server_write_key. Aucune clé MAC n'est utilisée.
Chaque suite de chiffrement AEAD DOIT (MUST) spécifier comment le nonce fourni à l'opération AEAD est construit, et quelle est la longueur de la partie GenericAEADCipher.nonce_explicit. Dans de nombreux cas, il est approprié d'utiliser la technique de nonce partiellement implicite décrite dans la section 3.2.1 de [AEAD]; record_iv_length étant la longueur de la partie explicite. Dans ce cas, la partie implicite DEVRAIT (SHOULD) être dérivée de key_block comme client_write_iv et server_write_iv (comme décrit dans la section 6.3), et la partie explicite est incluse dans GenericAEADCipher.nonce_explicit.
Le texte en clair est le TLSCompressed.fragment.
Les données authentifiées supplémentaires, que nous désignons par additional_data, sont définies comme suit:
additional_data = seq_num + TLSCompressed.type +
TLSCompressed.version + TLSCompressed.length;
où "+" désigne la concaténation.
Le aead_output se compose du texte chiffré produit par l'opération de chiffrement AEAD. La longueur sera généralement plus grande que TLSCompressed.length, mais d'une quantité qui varie avec le chiffrement AEAD. Étant donné que les chiffrements peuvent incorporer un remplissage, la quantité de surcharge peut varier avec différentes valeurs TLSCompressed.length. Chaque chiffrement AEAD NE DOIT PAS (MUST NOT) produire une expansion supérieure à 1024 octets. Symboliquement,
AEADEncrypted = AEAD-Encrypt(write_key, nonce, plaintext,
additional_data)
Afin de déchiffrer et de vérifier, le chiffrement prend en entrée la clé, le nonce, les "additional_data" et la valeur AEADEncrypted. La sortie est soit le texte en clair, soit une erreur indiquant que le déchiffrement a échoué. Il n'y a pas de vérification d'intégrité séparée. C'est-à-dire:
TLSCompressed.fragment = AEAD-Decrypt(write_key, nonce,
AEADEncrypted,
additional_data)
Si le déchiffrement échoue, une alerte fatale bad_record_mac DOIT (MUST) être générée.
6.3. Calcul de clé (Key Calculation)
Le protocole d'enregistrement nécessite un algorithme pour générer les clés requises par l'état de connexion actuel (voir annexe A.6) à partir des paramètres de sécurité fournis par le protocole de poignée de main.
Le secret maître est étendu en une séquence d'octets sécurisés, qui est ensuite divisée en une clé MAC d'écriture client, une clé MAC d'écriture serveur, une clé de chiffrement d'écriture client et une clé de chiffrement d'écriture serveur. Chacun d'eux est généré à partir de la séquence d'octets dans cet ordre. Les valeurs inutilisées sont vides. Certains chiffrements AEAD peuvent également nécessiter un IV d'écriture client et un IV d'écriture serveur (voir section 6.2.3.3).
Lorsque les clés et les clés MAC sont générées, le secret maître est utilisé comme source d'entropie.
Pour générer le matériel de clé, calculez
key_block = PRF(SecurityParameters.master_secret,
"key expansion",
SecurityParameters.server_random +
SecurityParameters.client_random);
jusqu'à ce qu'une sortie suffisante ait été générée. Ensuite, le key_block est partitionné comme suit:
client_write_MAC_key[SecurityParameters.mac_key_length]
server_write_MAC_key[SecurityParameters.mac_key_length]
client_write_key[SecurityParameters.enc_key_length]
server_write_key[SecurityParameters.enc_key_length]
client_write_IV[SecurityParameters.fixed_iv_length]
server_write_IV[SecurityParameters.fixed_iv_length]
Actuellement, l'exigence de matériel de clé la plus importante est pour AES_256_CBC_SHA256. Elle nécessite 2 x 32 octets de clés et 2 x 32 octets de clés MAC, pour un total de 128 octets de matériel de clé. En revanche, les données aléatoires fournies par le client et le serveur (64 octets au total) sont suffisantes. Des données supplémentaires peuvent également être fournies par la PRF, mais dans le cas de TLS 1.2, elles sont toujours définies sur la chaîne vide.
Les algorithmes de chiffrement exportables (qui ne sont plus pris en charge) nécessitaient un traitement supplémentaire pour dériver des clés plus courtes du key_block. Les chiffrements d'exportation NE DEVRAIENT PAS (SHOULD NOT) être utilisés dans les nouvelles implémentations.