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 außerdem 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 gegen eine dieser Eigenschaften verstoßen, werden von Tink entweder beim Parsen des Schlüssels oder beim Erstellen der entsprechenden Primitive abgelehnt.
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 zufälligen String \(\mathrm{Salt}\) der Länge\(\mathrm{DerivedKeySize}\) und einen einheitlichen zufälligen String \(\mathrm{NoncePrefix}\)mit 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 (also die Verkettung von \(C\) und \(T\) in Abschnitt 5.2.1.2 der verlinkten 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}\)abzurufen und jedes Geheimtextsegment einzeln zu entschlüsseln.
APIs können zufälligen Zugriff oder Zugriff auf den Anfang einer Datei zulassen (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 Proto zu, das unter aes_gcm_hkdf_streaming.proto angegeben ist. 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 wurde, kann Tink entweder RAW
oder den vorherigen Wert schreiben.
Um einen Schlüssel zu parsen, kehren 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
-
Hoang, Reyhanitabar, Rogaway und Vizar, 2015 Die authentifizierte Onlineverschlüsselung und ihre Widerstandsfähigkeit gegen die missbräuchliche Verwendung bei Nonce-Wiederverwendung CRYPTO 2015. https://eprint.iacr.org/2015/189 ↩
-
Hoang, Shen, 2020 Security of Streaming Encryption in der Tink Library von Google. https://eprint.iacr.org/2020/1019 ↩
-
RFC 5869. HMAC-basierte Extract-and-Expand Key Derivation Function (HKDF), https://www.rfc-editor.org/rfc/rfc5869 ↩
-
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 ↩