7. Cryptographic Computations (Kryptographische Berechnungen)
Der TLS-Handshake etabliert ein oder mehrere Eingangssecrets, die kombiniert werden, um das tatsächliche funktionierende Schlüsselmaterial zu erstellen, wie unten detailliert beschrieben. Der Schlüsselableitungsprozess inkorporiert sowohl die Eingangssecrets als auch das Handshake-Transkript. Beachten Sie, dass, da das Handshake-Transkript die Zufallswerte aus den Hello-Nachrichten enthält, jeder gegebene Handshake unterschiedliche Traffic-Secrets haben wird, selbst wenn dieselben Eingangssecrets verwendet werden, wie es der Fall ist, wenn derselbe PSK für mehrere Verbindungen verwendet wird.
7.1. Key Schedule (Schlüsselplan)
Der Schlüsselableitungsprozess verwendet die HKDF-Extract- und HKDF-Expand-Funktionen, wie für HKDF [RFC5869] definiert, sowie die unten definierten Funktionen:
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)
Die von Transcript-Hash und HKDF verwendete Hash-Funktion ist der Hash-Algorithmus der Cipher Suite. Hash.length ist ihre Ausgabelänge in Bytes. Messages ist die Verkettung der angegebenen Handshake-Nachrichten, einschließlich des Handshake-Nachrichtentyps und der Längenfelder, aber ohne Record-Layer-Header. Beachten Sie, dass in einigen Fällen ein Context mit Länge Null (angezeigt durch "") an HKDF-Expand-Label übergeben wird. Die in diesem Dokument spezifizierten Labels sind alle ASCII-Strings und enthalten kein abschließendes NUL-Byte.
Hinweis: Bei gängigen Hash-Funktionen erfordert jedes Label, das länger als 12 Zeichen ist, eine zusätzliche Iteration der Hash-Funktion zur Berechnung. Die Labels in dieser Spezifikation wurden alle so gewählt, dass sie innerhalb dieser Grenze passen.
Schlüssel werden aus zwei Eingangssecrets unter Verwendung der HKDF-Extract- und Derive-Secret-Funktionen abgeleitet. Das allgemeine Muster zum Hinzufügen eines neuen Secrets besteht darin, HKDF-Extract zu verwenden, wobei das Salt der aktuelle Secret-Zustand ist und das Input Keying Material (IKM) das hinzuzufügende neue Secret ist. In dieser Version von TLS 1.3 sind die zwei Eingangssecrets:
- PSK (ein extern etablierter Pre-Shared Key oder abgeleitet vom resumption_master_secret-Wert einer vorherigen Verbindung)
- (EC)DHE Shared Secret (Abschnitt 7.4)
Dies erzeugt den vollständigen Schlüsselableitungsplan, der im folgenden Diagramm gezeigt wird. In diesem Diagramm gelten die folgenden Formatierungskonventionen:
- HKDF-Extract wird gezeichnet, als würde es das Salt-Argument von oben und das IKM-Argument von links nehmen, mit seiner Ausgabe nach unten und dem Namen der Ausgabe rechts.
- Das Secret-Argument von Derive-Secret wird durch den eingehenden Pfeil angezeigt. Zum Beispiel ist das Early Secret das Secret zur Generierung des client_early_traffic_secret.
- "0" bezeichnet einen String von Hash.length Bytes, die auf Null gesetzt sind.
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
Das allgemeine Muster hier ist, dass die auf der linken Seite des Diagramms gezeigten Secrets nur rohe Entropie ohne Kontext sind, während die Secrets auf der rechten Seite Handshake-Kontext enthalten und daher zur Ableitung von Arbeitsschlüsseln ohne zusätzlichen Kontext verwendet werden können. Beachten Sie, dass die verschiedenen Aufrufe von Derive-Secret unterschiedliche Messages-Argumente nehmen können, selbst mit demselben Secret. In einem 0-RTT-Austausch wird Derive-Secret mit vier verschiedenen Transkripten aufgerufen; in einem reinen 1-RTT-Austausch wird es mit drei verschiedenen Transkripten aufgerufen.
Wenn ein gegebenes Secret nicht verfügbar ist, wird der 0-Wert verwendet, der aus einem String von Hash.length Bytes besteht, die auf Nullen gesetzt sind. Beachten Sie, dass dies nicht bedeutet, eine Runde zu überspringen, also wenn PSK nicht verwendet wird, wird Early Secret immer noch HKDF-Extract(0, 0) sein. Für die Berechnung des binder_key ist das Label "ext binder" für externe PSKs (die außerhalb von TLS bereitgestellt werden) und "res binder" für Resumption-PSKs (die als Resumption Master Secret eines vorherigen Handshakes bereitgestellt werden). Die verschiedenen Labels verhindern die Substitution eines PSK-Typs durch den anderen.
Es gibt mehrere potenzielle Early Secret-Werte, abhängig davon, welchen PSK der Server letztendlich auswählt. Der Client muss einen für jeden potenziellen PSK berechnen; wenn kein PSK ausgewählt wird, muss er dann das Early Secret berechnen, das dem Null-PSK entspricht.
Sobald alle Werte, die aus einem gegebenen Secret abgeleitet werden sollen, berechnet wurden, SOLLTE (SHOULD) dieses Secret gelöscht werden.
7.2. Updating Traffic Secrets (Aktualisierung der Traffic-Secrets)
Sobald der Handshake abgeschlossen ist, ist es für beide Seiten möglich, ihre sendenden Traffic-Schlüssel mit der in Abschnitt 4.6.3 definierten KeyUpdate-Handshake-Nachricht zu aktualisieren. Die nächste Generation von Traffic-Schlüsseln wird berechnet, indem client_/server_application_traffic_secret_N+1 aus client_/server_application_traffic_secret_N wie in diesem Abschnitt beschrieben generiert wird und dann die Traffic-Schlüssel wie in Abschnitt 7.3 beschrieben neu abgeleitet werden.
Das application_traffic_secret der nächsten Generation wird wie folgt berechnet:
application_traffic_secret_N+1 =
HKDF-Expand-Label(application_traffic_secret_N,
"traffic upd", "", Hash.length)
Sobald client_/server_application_traffic_secret_N+1 und die zugehörigen Traffic-Schlüssel berechnet wurden, SOLLTEN (SHOULD) Implementierungen client_/server_application_traffic_secret_N und die zugehörigen Traffic-Schlüssel löschen.
7.3. Traffic Key Calculation (Traffic-Schlüsselberechnung)
Das Traffic-Schlüsselmaterial wird aus den folgenden Eingabewerten generiert:
- Ein Secret-Wert
- Ein Zweckwert, der den spezifischen generierten Wert angibt
- Die Länge des generierten Schlüssels
Das Traffic-Schlüsselmaterial wird aus einem Eingabe-Traffic-Secret-Wert wie folgt generiert:
[sender]_write_key = HKDF-Expand-Label(Secret, "key", "", key_length)
[sender]_write_iv = HKDF-Expand-Label(Secret, "iv", "", iv_length)
[sender] bezeichnet die sendende Seite. Die folgende Tabelle zeigt den Secret-Wert für jeden Record-Typ.
| Record Type | Secret |
|---|---|
| 0-RTT Application | client_early_traffic_secret |
| Handshake | [sender]_handshake_traffic_secret |
| Application Data | [sender]_application_traffic_secret_N |
Das gesamte Traffic-Schlüsselmaterial wird neu berechnet, wann immer sich das zugrunde liegende Secret ändert (z. B. beim Wechsel von Handshake- zu Anwendungsdatenschlüsseln oder bei einer Schlüsselaktualisierung).
7.4. (EC)DHE Shared Secret Calculation ((EC)DHE Shared Secret-Berechnung)
7.4.1. Finite Field Diffie-Hellman (Finite Field Diffie-Hellman)
Für Finite-Field-Gruppen wird eine konventionelle Diffie-Hellman [DH76] Berechnung durchgeführt. Der ausgehandelte Schlüssel (Z) wird in einen Byte-String konvertiert, indem er im Big-Endian-Format kodiert und mit Nullen bis zur Größe der Primzahl links aufgefüllt wird. Dieser Byte-String wird als Shared Secret im Schlüsselplan wie oben spezifiziert verwendet.
Beachten Sie, dass diese Konstruktion sich von früheren Versionen von TLS unterscheidet, die führende Nullen entfernt haben.
7.4.2. Elliptic Curve Diffie-Hellman (Elliptic Curve Diffie-Hellman)
Für secp256r1, secp384r1 und secp521r1 werden ECDH-Berechnungen (einschließlich Parameter- und Schlüsselgenerierung sowie der Shared Secret-Berechnung) gemäß [IEEE1363] unter Verwendung des ECKAS-DH1-Schemas mit der Identitätsabbildung als Key Derivation Function (KDF) durchgeführt, so dass das Shared Secret die x-Koordinate des ECDH Shared Secret Elliptic Curve-Punkts ist, dargestellt als Oktettstring. Beachten Sie, dass dieser Oktettstring (Z in IEEE 1363-Terminologie), wie er von FE2OSP (der Field Element to Octet String Conversion Primitive) ausgegeben wird, für jedes gegebene Feld eine konstante Länge hat; führende Nullen, die in diesem Oktettstring gefunden werden, DÜRFEN NICHT (MUST NOT) gekürzt werden.
(Beachten Sie, dass diese Verwendung der Identitäts-KDF eine Formalität ist. Das vollständige Bild ist, dass ECDH mit einer nicht-trivialen KDF eingesetzt wird, weil TLS dieses Secret nicht direkt für etwas anderes als für die Berechnung anderer Secrets verwendet.)
Für X25519 und X448 ist die ECDH-Berechnung wie folgt:
-
Der öffentliche Schlüssel, der in die KeyShareEntry.key_exchange-Struktur eingefügt werden soll, ist das Ergebnis der Anwendung der ECDH-Skalarmultiplikationsfunktion auf den geheimen Schlüssel der entsprechenden Länge (in die Skalareingabe) und den Standard-öffentlichen Basispunkt (in die u-Koordinaten-Punkteingabe).
-
Das ECDH Shared Secret ist das Ergebnis der Anwendung der ECDH-Skalarmultiplikationsfunktion auf den geheimen Schlüssel (in die Skalareingabe) und den öffentlichen Schlüssel des Peers (in die u-Koordinaten-Punkteingabe). Die Ausgabe wird roh ohne Verarbeitung verwendet.
Für diese Kurven SOLLTEN (SHOULD) Implementierungen den in [RFC7748] spezifizierten Ansatz verwenden, um das Diffie-Hellman Shared Secret zu berechnen. Implementierungen MÜSSEN (MUST) prüfen, ob das berechnete Diffie-Hellman Shared Secret der All-Zero-Wert ist, und abbrechen, wenn dies der Fall ist, wie in Abschnitt 6 von [RFC7748] beschrieben. Wenn Implementierer eine alternative Implementierung dieser elliptischen Kurven verwenden, SOLLTEN (SHOULD) sie die zusätzlichen Prüfungen durchführen, die in Abschnitt 7 von [RFC7748] spezifiziert sind.
7.5. Exporters (Exporteure)
[RFC5705] definiert Keying Material Exporters für TLS in Bezug auf die TLS Pseudorandom Function (PRF). Dieses Dokument ersetzt die PRF durch HKDF und erfordert daher eine neue Konstruktion. Die Exporter-Schnittstelle bleibt dieselbe.
Der Exporter-Wert wird wie folgt berechnet:
TLS-Exporter(label, context_value, key_length) =
HKDF-Expand-Label(Derive-Secret(Secret, label, ""),
"exporter", Hash(context_value), key_length)
Wobei Secret entweder das early_exporter_master_secret oder das exporter_master_secret ist. Implementierungen MÜSSEN (MUST) das exporter_master_secret verwenden, sofern nicht explizit von der Anwendung spezifiziert. Das early_exporter_master_secret ist für die Verwendung in Einstellungen definiert, in denen ein Exporter für 0-RTT-Daten benötigt wird. Eine separate Schnittstelle für den frühen Exporter wird EMPFOHLEN (RECOMMENDED); dies vermeidet, dass der Exporter-Benutzer versehentlich einen frühen Exporter verwendet, wenn ein regulärer gewünscht wird, oder umgekehrt.
Wenn kein Kontext bereitgestellt wird, ist der context_value null lang. Folglich berechnet das Nicht-Bereitstellen eines Kontexts denselben Wert wie das Bereitstellen eines leeren Kontexts. Dies ist eine Änderung gegenüber früheren Versionen von TLS, bei denen ein leerer Kontext eine andere Ausgabe als ein fehlender Kontext erzeugte. Zum Zeitpunkt der Veröffentlichung dieses Dokuments wird kein zugewiesenes Exporter-Label sowohl mit als auch ohne Kontext verwendet. Zukünftige Spezifikationen DÜRFEN NICHT (MUST NOT) eine Verwendung von Exportern definieren, die sowohl einen leeren Kontext als auch keinen Kontext mit demselben Label zulassen. Neue Verwendungen von Exportern SOLLTEN (SHOULD) in allen Exporter-Berechnungen einen Kontext bereitstellen, obwohl der Wert leer sein könnte.
Anforderungen an das Format von Exporter-Labels sind in Abschnitt 4 von [RFC5705] definiert.