AES-GCM-HKDF Streaming AEAD

In diesem Dokument wird die mathematische Funktion formal definiert, die durch AES-GCM-HKDF-Streamingschlüssel dargestellt wird, die im Proto-Format als type.googleapis.com/google.crypto.tink.AesGcmHkdfStreamingKey codiert sind.

Diese Verschlüsselung basiert lose auf HRRV151. Für die Sicherheitsanalyse verweisen wir auf HS202.

Schlüssel und Parameter

Schlüssel werden durch die folgenden Teile beschrieben (alle Größen in diesem Dokument sind in Byte angegeben):

  • \(\mathrm{KeyValue}\), ein Bytestring.
  • \(\mathrm{CiphertextSegmentSize} \in \{1, 2, \ldots, 2^{31}-1\}\).
  • \(\mathrm{DerivedKeySize} \in \{16, 32\}\).
  • \(\mathrm{HkdfHashType} \in \{\mathrm{SHA1}, \mathrm{SHA256}, \mathrm{SHA512}\}\).

Gültige Schlüssel erfüllen zusätzlich die folgenden Eigenschaften:

  • \(\mathrm{len}(\mathrm{KeyValue}) \geq \mathrm{DerivedKeySize}\).
  • \(\mathrm{CiphertextSegmentSize} > \mathrm{DerivedKeySize} + 24\) (wie später erläutert, entspricht dies \(\mathrm{len}(\mathrm{Header}) + 16\) ).

Schlüssel, die keine dieser Eigenschaften erfüllen, werden von Tink abgelehnt, entweder beim Parsen des Schlüssels oder beim Erstellen des entsprechenden Primitives.

Verschlüsselungsfunktion

Um eine Nachricht \(\mathrm{Msg}\) mit zugehörigen Daten\(\mathrm{AssociatedData}\)zu verschlüsseln, erstellen wir einen Header, teilen die Nachricht in Segmente auf, verschlüsseln jedes Segment und verknüpfen die verschlüsselten Segmente.

Kopfzeile erstellen

Wir wählen einen gleichverteilten Zufallsstring \(\mathrm{Salt}\) mit der Länge\(\mathrm{DerivedKeySize}\) und einen gleichverteilten Zufallsstring \(\mathrm{NoncePrefix}\)mit der Länge 7 aus.

Wir legen dann \(\mathrm{Header} := \mathrm{len}(\mathrm{Header}) \| \mathrm{Salt} \| \mathrm{NoncePrefix}\)fest, wobei die Länge des Headers als einzelnes Byte codiert wird. Hinweis: \(\mathrm{len}(\mathrm{Header}) \in \{24, 40\}\)

Als Nächstes verwenden wir HKDF3 mit der Hash-Funktion \(\mathrm{HkdfHashType}\)und den Eingaben \(\mathrm{ikm} := \mathrm{KeyValue}\), \(\mathrm{salt} := \mathrm{Salt}\)und \(\mathrm{info} := \mathrm{AssociatedData}\)mit der Ausgabelänge \(\mathrm{DerivedKeySize}\). Wir nennen das Ergebnis \(\mathrm{DerivedKey}\).

Nachricht aufteilen

Die Nachricht \(\mathrm{Msg}\) wird dann in Teile unterteilt: \(\mathrm{Msg} = M_0 \| M_1 \| \cdots \| M_{n-1}\).

Die Längen werden so gewählt, dass folgende Anforderungen erfüllt werden:

  • \(\mathrm{len}(M_0) \in \{0,\ldots, \mathrm{CiphertextSegmentSize} - \mathrm{len}(\mathrm{Header}) - \mathrm{16}\}\).
  • Wenn \(n>1\), dann \(\mathrm{len}(M_1), \ldots, \mathrm{len}(M_{n-1}) \in \{1,\ldots, \mathrm{CiphertextSegmentSize} - \mathrm{16}\}\).
  • Wenn \(n>1\), dann muss \(\mathrm{len}(M_{0}), \ldots, \mathrm{len}(M_{n-2})\) gemäß den oben genannten Einschränkungen eine maximale Länge haben.

\(n\) darf maximal \(2^{32}\)sein. Andernfalls schlägt die Verschlüsselung fehl.

Blöcke verschlüsseln

Um das Segment \(M_i\)zu verschlüsseln, berechnen wir \(\mathrm{IV}_i := \mathrm{NoncePrefix} \| \mathrm{i} \| b\), wobei \(\mathrm{i}\)  4 Byte in Big-Endian-Codierung ist und das Byte $b$ 0x00 ist, wenn $i < n-1$, andernfalls 0x01.

Wir verschlüsseln \(M_i\) dann mit AES-GCM4, wobei der Schlüssel\(\mathrm{DerivedKey}\), der Initialisierungsvektor \(\mathrm{IV}_i\)und die zugehörigen Daten der leere String sind. \(C_i\) ist das Ergebnis dieser Verschlüsselung (d.h. die Konkatenierung von \(C\) und \(T\) in Abschnitt 5.2.1.2 der verlinkten AES-GCM-Referenz).

Verschlüsselte Segmente zusammenführen

Schließlich werden alle Segmente zu \(\mathrm{Header} \| C_0 \| \cdots \| C_{n-1}\)zusammengefügt, dem endgültigen Geheimtext.

Entschlüsselung

Bei der Entschlüsselung wird die Verschlüsselung umgekehrt. Wir verwenden die Kopfzeile, um\(\mathrm{NoncePrefix}\)zu erhalten, und entschlüsseln jedes Segment des Geheimtexts einzeln.

APIs können (und tun dies in der Regel) den Zufallszugriff oder den Zugriff auf den Anfang einer Datei zulassen, ohne das Ende der Datei zu prüfen. Das ist beabsichtigt, da es möglich ist, \(M_i\) von \(C_i\)zu entschlüsseln, ohne alle vorherigen und verbleibenden Geheimtextblöcke zu entschlüsseln.

Bei APIs sollte jedoch darauf geachtet werden, dass Nutzer das Ende der Datei nicht mit einem Entschlüsselungsfehler verwechseln: In beiden Fällen muss die API wahrscheinlich einen Fehler zurückgeben. Wenn der Unterschied ignoriert wird, kann ein Angreifer Dateien effektiv kürzen.

Serialisierung und Parsen von Schlüsseln

Um einen Schlüssel im Tink Proto-Format zu serialisieren, ordnen wir die Parameter zuerst auf die offensichtliche Weise dem Proto zu, das unter aes_gcm_hkdf_streaming.proto angegeben ist. Das Feld version muss auf 0 gesetzt sein. Wir serialisieren dies dann mit der normalen Proto-Serialisierung und fügen den resultierenden String in das Wertfeld eines KeyData-Proto ein. Wir setzen das Feld type_url auf type.googleapis.com/google.crypto.tink.AesGcmHkdfStreamingKey. Wir legen dann key_material_type auf SYMMETRIC fest und betten dies in ein Keyset ein. Normalerweise setzen wir output_prefix_type auf RAW. Eine Ausnahme gilt, wenn der Schlüssel mit einem anderen Wert für output_prefix_type geparst wurde. In diesem Fall schreibt Tink entweder RAW oder den vorherigen Wert.

Um einen Schlüssel zu parsen, wenden wir den obigen Vorgang auf umgekehrte Weise an (wie beim Parsen von Protokollen üblich). Das Feld key_material_type wird ignoriert. Der Wert von output_prefix_type kann entweder ignoriert oder Schlüssel mit einem anderen Wert als RAW abgelehnt werden.output_prefix_type Schlüssel mit einem anderen version-Wert als 0 müssen abgelehnt werden.

Bekannte Probleme

Implementierungen der oben genannten Verschlüsselungsfunktion sind nicht für Forks geeignet. Weitere Informationen finden Sie unter Fork Safety.

Verweise


  1. Hoang, Reyhanitabar, Rogaway, Vizar, 2015. Online-authentifizierte Verschlüsselung und die Widerstandsfähigkeit gegen Missbrauch durch Wiederverwendung von Nonces. CRYPTO 2015. https://eprint.iacr.org/2015/189 

  2. Hoang, Shen, 2020. Security of Streaming Encryption in Google's Tink Library. https://eprint.iacr.org/2020/1019 

  3. RFC 5869. HMAC-basierte Schlüsselableitungsfunktion (Extract-and-Expand Key Derivation Function, HKDF). https://www.rfc-editor.org/rfc/rfc5869 

  4. NIST SP 800-38D Empfehlung für Blockverschlüsselungsmodi: Galois/Counter Mode (GCM) und GMAC. https://csrc.nist.gov/pubs/sp/800/38/d/final