7. Cryptographic Computations (Calculs Cryptographiques)
La poignée de main TLS établit un ou plusieurs secrets d'entrée qui sont combinés pour créer le matériel de clé de travail réel, comme détaillé ci-dessous. Le processus de dérivation de clé incorpore à la fois les secrets d'entrée et la transcription de la poignée de main. Notez que parce que la transcription de la poignée de main inclut les valeurs aléatoires des messages Hello, toute poignée de main donnée aura des secrets de trafic différents, même si les mêmes secrets d'entrée sont utilisés, comme c'est le cas lorsque le même PSK est utilisé pour plusieurs connexions.
7.1. Key Schedule (Calendrier de Clés)
Le processus de dérivation de clé utilise les fonctions HKDF-Extract et HKDF-Expand telles que définies pour HKDF [RFC5869], ainsi que les fonctions définies ci-dessous :
HKDF-Expand-Label(Secret, Label, Context, Length) =
HKDF-Expand(Secret, HkdfLabel, Length)
Where HkdfLabel is specified as:
struct {
uint16 length = Length;
opaque label<7..255> = "tls13 " + Label;
opaque context<0..255> = Context;
} HkdfLabel;
Derive-Secret(Secret, Label, Messages) =
HKDF-Expand-Label(Secret, Label,
Transcript-Hash(Messages), Hash.length)
La fonction Hash utilisée par Transcript-Hash et HKDF est l'algorithme de hachage de la suite de chiffrement. Hash.length est sa longueur de sortie en octets. Messages est la concaténation des messages de poignée de main indiqués, y compris le type de message de poignée de main et les champs de longueur, mais sans inclure les en-têtes de couche d'enregistrement. Notez que dans certains cas, un Context de longueur nulle (indiqué par "") est passé à HKDF-Expand-Label. Les étiquettes spécifiées dans ce document sont toutes des chaînes ASCII et n'incluent pas d'octet NUL final.
Note : Avec les fonctions de hachage courantes, toute étiquette de plus de 12 caractères nécessite une itération supplémentaire de la fonction de hachage pour le calcul. Les étiquettes de cette spécification ont toutes été choisies pour tenir dans cette limite.
Les clés sont dérivées de deux secrets d'entrée en utilisant les fonctions HKDF-Extract et Derive-Secret. Le modèle général pour ajouter un nouveau secret consiste à utiliser HKDF-Extract avec le Salt étant l'état secret actuel et l'Input Keying Material (IKM) étant le nouveau secret à ajouter. Dans cette version de TLS 1.3, les deux secrets d'entrée sont :
- PSK (une clé pré-partagée établie en externe ou dérivée de la valeur resumption_master_secret d'une connexion précédente)
- Secret partagé (EC)DHE (Section 7.4)
Cela produit le calendrier complet de dérivation de clé montré dans le diagramme ci-dessous. Dans ce diagramme, les conventions de formatage suivantes s'appliquent :
- HKDF-Extract est dessiné comme prenant l'argument Salt du haut et l'argument IKM de la gauche, avec sa sortie en bas et le nom de la sortie à droite.
- L'argument Secret de Derive-Secret est indiqué par la flèche entrante. Par exemple, l'Early Secret est le Secret pour générer le client_early_traffic_secret.
- "0" indique une chaîne d'octets Hash.length définis à zéro.
0
|
v
PSK -> HKDF-Extract = Early Secret
|
+-----> Derive-Secret(., "ext binder" | "res binder", "")
| = binder_key
|
+-----> Derive-Secret(., "c e traffic", ClientHello)
| = client_early_traffic_secret
|
+-----> Derive-Secret(., "e exp master", ClientHello)
| = early_exporter_master_secret
v
Derive-Secret(., "derived", "")
|
v
(EC)DHE -> HKDF-Extract = Handshake Secret
|
+-----> Derive-Secret(., "c hs traffic",
| ClientHello...ServerHello)
| = client_handshake_traffic_secret
|
+-----> Derive-Secret(., "s hs traffic",
| ClientHello...ServerHello)
| = server_handshake_traffic_secret
v
Derive-Secret(., "derived", "")
|
v
0 -> HKDF-Extract = Master Secret
|
+-----> Derive-Secret(., "c ap traffic",
| ClientHello...server Finished)
| = client_application_traffic_secret_0
|
+-----> Derive-Secret(., "s ap traffic",
| ClientHello...server Finished)
| = server_application_traffic_secret_0
|
+-----> Derive-Secret(., "exp master",
| ClientHello...server Finished)
| = exporter_master_secret
|
+-----> Derive-Secret(., "res master",
ClientHello...client Finished)
= resumption_master_secret
Le modèle général ici est que les secrets montrés sur le côté gauche du diagramme sont juste de l'entropie brute sans contexte, tandis que les secrets sur le côté droit incluent le contexte de poignée de main et peuvent donc être utilisés pour dériver des clés de travail sans contexte supplémentaire. Notez que les différents appels à Derive-Secret peuvent prendre différents arguments Messages, même avec le même secret. Dans un échange 0-RTT, Derive-Secret est appelé avec quatre transcriptions distinctes ; dans un échange 1-RTT uniquement, il est appelé avec trois transcriptions distinctes.
Si un secret donné n'est pas disponible, alors la valeur 0 constituée d'une chaîne d'octets Hash.length définis à zéros est utilisée. Notez que cela ne signifie pas sauter un tour, donc si PSK n'est pas utilisé, Early Secret sera toujours HKDF-Extract(0, 0). Pour le calcul du binder_key, l'étiquette est "ext binder" pour les PSK externes (ceux provisionnés en dehors de TLS) et "res binder" pour les PSK de reprise (ceux provisionnés comme secret maître de reprise d'une poignée de main précédente). Les différentes étiquettes empêchent la substitution d'un type de PSK pour l'autre.
Il existe plusieurs valeurs Early Secret potentielles, selon le PSK que le serveur sélectionne finalement. Le client devra en calculer une pour chaque PSK potentiel ; si aucun PSK n'est sélectionné, il devra alors calculer l'Early Secret correspondant au PSK zéro.
Une fois que toutes les valeurs qui doivent être dérivées d'un secret donné ont été calculées, ce secret DEVRAIT (SHOULD) être effacé.
7.2. Updating Traffic Secrets (Mise à Jour des Secrets de Trafic)
Une fois la poignée de main terminée, il est possible pour l'une ou l'autre partie de mettre à jour ses clés de trafic d'envoi en utilisant le message de poignée de main KeyUpdate défini dans la Section 4.6.3. La génération suivante de clés de trafic est calculée en générant client_/server_application_traffic_secret_N+1 à partir de client_/server_application_traffic_secret_N comme décrit dans cette section, puis en redérivant les clés de trafic comme décrit dans la Section 7.3.
Le application_traffic_secret de nouvelle génération est calculé comme suit :
application_traffic_secret_N+1 =
HKDF-Expand-Label(application_traffic_secret_N,
"traffic upd", "", Hash.length)
Une fois que client_/server_application_traffic_secret_N+1 et ses clés de trafic associées ont été calculés, les implémentations DEVRAIENT (SHOULD) supprimer client_/server_application_traffic_secret_N et ses clés de trafic associées.
7.3. Traffic Key Calculation (Calcul des Clés de Trafic)
Le matériel de clé de trafic est généré à partir des valeurs d'entrée suivantes :
- Une valeur secrète
- Une valeur de but indiquant la valeur spécifique générée
- La longueur de la clé générée
Le matériel de clé de trafic est généré à partir d'une valeur secrète de trafic d'entrée en utilisant :
[sender]_write_key = HKDF-Expand-Label(Secret, "key", "", key_length)
[sender]_write_iv = HKDF-Expand-Label(Secret, "iv", "", iv_length)
[sender] désigne le côté émetteur. Le tableau ci-dessous montre la valeur Secret pour chaque type d'enregistrement.
| Record Type | Secret |
|---|---|
| 0-RTT Application | client_early_traffic_secret |
| Handshake | [sender]_handshake_traffic_secret |
| Application Data | [sender]_application_traffic_secret_N |
Tout le matériel de clé de trafic est recalculé chaque fois que le Secret sous-jacent change (par exemple, lors du passage des clés de poignée de main aux clés de données d'application ou lors d'une mise à jour de clé).
7.4. (EC)DHE Shared Secret Calculation (Calcul du Secret Partagé (EC)DHE)
7.4.1. Finite Field Diffie-Hellman (Diffie-Hellman à Corps Fini)
Pour les groupes de corps finis, un calcul conventionnel de Diffie-Hellman [DH76] est effectué. La clé négociée (Z) est convertie en une chaîne d'octets en encodant en format big-endian et en ajoutant des zéros à gauche jusqu'à la taille du nombre premier. Cette chaîne d'octets est utilisée comme secret partagé dans le calendrier de clés comme spécifié ci-dessus.
Notez que cette construction diffère des versions précédentes de TLS qui supprimaient les zéros de tête.
7.4.2. Elliptic Curve Diffie-Hellman (Diffie-Hellman à Courbe Elliptique)
Pour secp256r1, secp384r1 et secp521r1, les calculs ECDH (y compris la génération de paramètres et de clés ainsi que le calcul du secret partagé) sont effectués selon [IEEE1363] en utilisant le schéma ECKAS-DH1 avec la fonction d'identité comme fonction de dérivation de clé (KDF), de sorte que le secret partagé est la coordonnée x du point de courbe elliptique du secret partagé ECDH représenté comme une chaîne d'octets. Notez que cette chaîne d'octets (Z dans la terminologie IEEE 1363) telle que sortie par FE2OSP (la Primitive de Conversion d'Élément de Corps en Chaîne d'Octets) a une longueur constante pour tout champ donné ; les zéros de tête trouvés dans cette chaîne d'octets NE DOIVENT PAS (MUST NOT) être tronqués.
(Notez que cette utilisation de la KDF d'identité est une technicité. L'image complète est qu'ECDH est employé avec une KDF non triviale parce que TLS n'utilise pas directement ce secret pour autre chose que pour calculer d'autres secrets.)
Pour X25519 et X448, le calcul ECDH est le suivant :
-
La clé publique à placer dans la structure KeyShareEntry.key_exchange est le résultat de l'application de la fonction de multiplication scalaire ECDH à la clé secrète de longueur appropriée (dans l'entrée scalaire) et au point de base publique standard (dans l'entrée du point de coordonnée u).
-
Le secret partagé ECDH est le résultat de l'application de la fonction de multiplication scalaire ECDH à la clé secrète (dans l'entrée scalaire) et à la clé publique du pair (dans l'entrée du point de coordonnée u). La sortie est utilisée brute, sans traitement.
Pour ces courbes, les implémentations DEVRAIENT (SHOULD) utiliser l'approche spécifiée dans [RFC7748] pour calculer le secret partagé Diffie-Hellman. Les implémentations DOIVENT (MUST) vérifier si le secret partagé Diffie-Hellman calculé est la valeur tout-zéro et abandonner si c'est le cas, comme décrit dans la Section 6 de [RFC7748]. Si les implémenteurs utilisent une implémentation alternative de ces courbes elliptiques, ils DEVRAIENT (SHOULD) effectuer les vérifications supplémentaires spécifiées dans la Section 7 de [RFC7748].
7.5. Exporters (Exportateurs)
[RFC5705] définit les exportateurs de matériel de clé pour TLS en termes de la fonction pseudoaléatoire (PRF) TLS. Ce document remplace la PRF par HKDF, nécessitant ainsi une nouvelle construction. L'interface d'exportateur reste la même.
La valeur d'exportateur est calculée comme suit :
TLS-Exporter(label, context_value, key_length) =
HKDF-Expand-Label(Derive-Secret(Secret, label, ""),
"exporter", Hash(context_value), key_length)
Où Secret est soit le early_exporter_master_secret, soit le exporter_master_secret. Les implémentations DOIVENT (MUST) utiliser le exporter_master_secret sauf indication explicite de l'application. Le early_exporter_master_secret est défini pour une utilisation dans des contextes où un exportateur est nécessaire pour les données 0-RTT. Une interface séparée pour l'exportateur précoce est RECOMMANDÉE (RECOMMENDED) ; cela évite que l'utilisateur d'exportateur utilise accidentellement un exportateur précoce lorsqu'un exportateur régulier est souhaité ou vice versa.
Si aucun contexte n'est fourni, le context_value est de longueur nulle. Par conséquent, ne pas fournir de contexte calcule la même valeur que fournir un contexte vide. Ceci est un changement par rapport aux versions précédentes de TLS où un contexte vide produisait une sortie différente d'un contexte absent. À la date de publication de ce document, aucune étiquette d'exportateur allouée n'est utilisée à la fois avec et sans contexte. Les spécifications futures NE DOIVENT PAS (MUST NOT) définir une utilisation d'exportateurs qui permette à la fois un contexte vide et aucun contexte avec la même étiquette. Les nouvelles utilisations d'exportateurs DEVRAIENT (SHOULD) fournir un contexte dans tous les calculs d'exportateur, bien que la valeur puisse être vide.
Les exigences pour le format des étiquettes d'exportateur sont définies dans la Section 4 de [RFC5705].