AES-GCM-HKDF Streaming AEAD

In diesem Dokument wird formal die mathematische Funktion 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 Sicherheitsanalysen verweisen wir auf HS202.

Schlüssel und Parameter

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

  • \(\mathrm{KeyValue}\)ist 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 darüber hinaus die folgenden Eigenschaften:

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

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

Verschlüsselungsfunktion

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

Header erstellen

Wir wählen einen einheitlichen Zufallsstring \(\mathrm{Salt}\) der Länge\(\mathrm{DerivedKeySize}\) und einen einheitlichen zufälligen String \(\mathrm{NoncePrefix}\)der Länge 7 aus.

Dann wird \(\mathrm{Header} := \mathrm{len}(\mathrm{Header}) \| \mathrm{Salt} \| \mathrm{NoncePrefix}\)festgelegt, 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 von \(\mathrm{HkdfHashType}\)angegebenen Hash-Funktion 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 mehrere Teile aufgeteilt: \(\mathrm{Msg} = M_0 \| M_1 \| \cdots \| M_{n-1}\).

Ihre Längen sind für folgende Kriterien geeignet:

  • \(\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\), muss \(\mathrm{len}(M_{0}), \ldots, \mathrm{len}(M_{n-2})\) gemäß den obigen Einschränkungen die maximale Länge haben.

\(n\) Darf höchstens \(2^{32}\)sein. Andernfalls schlägt die Verschlüsselung fehl.

Blöcke verschlüsseln

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

Anschließend verschlüsseln wir \(M_i\) 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 Verkettung von \(C\) und \(T\) in Abschnitt 5.2.1.2 der verknüpften AES-GCM-Referenz).

Verschlüsselte Segmente verketten

Zum Schluss werden alle Segmente zu \(\mathrm{Header} \| C_0 \| \cdots \| C_{n-1}\)verkettet. Dies ist der endgültige Geheimtext.

Entschlüsselung

Die Entschlüsselung invertiert die Verschlüsselung. Wir verwenden den Header, um \mathrm{NoncePrefix}$$ zu erhalten und jedes Geheimtextsegment einzeln zu entschlüsseln.

APIs können zufälligen Zugriff oder Zugriff auf den Anfang einer Datei ermöglichen (und in der Regel auch) ermöglichen, ohne das Ende der Datei zu prüfen. Das ist beabsichtigt, da es möglich ist, \(M_i\) aus \(C_i\)zu entschlüsseln, ohne alle vorherigen und verbleibenden Geheimtextblöcke zu entschlüsseln.

APIs sollten jedoch so gestaltet sein, dass Nutzer keine Fehler am Ende und bei der Entschlüsselung verwechseln: In beiden Fällen muss die API wahrscheinlich einen Fehler zurückgeben. Wenn Sie den Unterschied ignorieren, können Angreifer Dateien möglicherweise effektiv kürzen.

Serialisierung und Parsen von Schlüsseln

Um einen Schlüssel im Format "Tink Proto" zu serialisieren, ordnen wir die Parameter zuerst auf offensichtliche Weise dem unter aes_gcm_hkdf_streaming.proto angegebenen Proto zu. Das Feld version muss auf 0 festgelegt sein. Anschließend serialisieren wir dies mithilfe der normalen .proto-Serialisierung und betten den resultierenden String in den Feldwert eines KeyData-Protokolls ein. Wir setzen das Feld type_url auf type.googleapis.com/google.crypto.tink.AesGcmHkdfStreamingKey. Dann setzen wir key_material_type auf SYMMETRIC und betten dies in einen Schlüsselsatz ein. Normalerweise setzen wir den output_prefix_type auf RAW. Wenn der Schlüssel jedoch mit einem anderen für output_prefix_type festgelegten Wert geparst wird, kann Tink entweder RAW oder den vorherigen Wert schreiben.

Um einen Schlüssel zu parsen, setzen wir den obigen Vorgang um (wie beim Parsen von Protos üblich). Das Feld key_material_type wird ignoriert. Der Wert von output_prefix_type kann entweder ignoriert werden oder Schlüssel, deren output_prefix_type sich von RAW unterscheidet, können abgelehnt werden. Schlüssel, deren version sich von 0 unterscheidet, müssen abgelehnt werden.

Bekannte Probleme

Implementierungen der oben genannten Verschlüsselungsfunktion sind nicht mehr verzweigungssicher. Weitere Informationen finden Sie unter Fork Safety (Gabelsicherheit).

Verweise


  1. Hoang, Reyhanitabar, Rogaway und Vizar, 2015 Die authentifizierte Onlineverschlüsselung und ihre Widerstandsfähigkeit gegen Missbrauch bei Nonce-Wiederverwendung CRYPTO 2015. https://eprint.iacr.org/2015/189 

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

  3. RFC 5869. HMAC-basierte 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