AES-GCM-HKDF 串流 AEAD

本文件正式定義了 AES-GCM-HKDF 串流金鑰表示的數學函式,以 proto 格式編碼為 type.googleapis.com/google.crypto.tink.AesGcmHkdfStreamingKey

這種加密方式大部分是以 HRRV151 為基礎。進行安全性分析時,我們使用 HS202

鍵與參數

索引鍵分為下列幾個部分 (本文件中的所有大小均以位元組為單位):

  • \(\mathrm{KeyValue}\),位元組字串。
  • \(\mathrm{CiphertextSegmentSize} \in \{1, 2, \ldots, 2^{31}-1\}\).
  • \(\mathrm{DerivedKeySize} \in \{16, 32\}\).
  • \(\mathrm{HkdfHashType} \in \{\mathrm{SHA1}, \mathrm{SHA256}, \mathrm{SHA512}\}\).

有效金鑰還符合下列屬性:

  • \(\mathrm{len}(\mathrm{KeyValue}) \geq \mathrm{DerivedKeySize}\).
  • \(\mathrm{CiphertextSegmentSize} > \mathrm{DerivedKeySize} + 24\) (相當於 \(\mathrm{len}(\mathrm{Header}) + 16\) 稍後說明的情況)。

在剖析金鑰或建立對應的基元的建立時,如果金鑰不符合任何這些屬性,就會遭到 Tink 拒絕。

加密函式

為了使用 \(\mathrm{Msg}\) 關聯資料\(\mathrm{AssociatedData}\)加密訊息,我們會建立標頭,將訊息拆分為多個區段、加密每個區段,然後再將加密區段串連起來。

建立標頭

我們會挑選長度為 \(\mathrm{Salt}\) 長度\(\mathrm{DerivedKeySize}\) 的統一隨機字串,以及長度為 7 的統一隨機字串 \(\mathrm{NoncePrefix}\)。

接著設定 \(\mathrm{Header} := \mathrm{len}(\mathrm{Header}) \| \mathrm{Salt} \| \mathrm{NoncePrefix}\),將標頭長度編碼為單位元組。請注意, \(\mathrm{len}(\mathrm{Header}) \in \{24, 40\}\)。

接著,使用 HKDF3 搭配 \(\mathrm{HkdfHashType}\)、input \(\mathrm{ikm} := \mathrm{KeyValue}\)、 \(\mathrm{salt} := \mathrm{Salt}\)和 \(\mathrm{info} := \mathrm{AssociatedData}\)提供的雜湊函式,輸出長度 \(\mathrm{DerivedKeySize}\)。我們將結果稱為 \(\mathrm{DerivedKey}\)。

拆分訊息

此訊息 \(\mathrm{Msg}\) 將分成多個部分: \(\mathrm{Msg} = M_0 \| M_1 \| \cdots \| M_{n-1}\)。

可選擇的長度如下:

  • \(\mathrm{len}(M_0) \in \{0,\ldots, \mathrm{CiphertextSegmentSize} - \mathrm{len}(\mathrm{Header}) - \mathrm{16}\}\).
  • 若為 \(n>1\),則 \(\mathrm{len}(M_1), \ldots, \mathrm{len}(M_{n-1}) \in \{1,\ldots, \mathrm{CiphertextSegmentSize} - \mathrm{16}\}\)。
  • 如果為 \(n>1\),則 \(\mathrm{len}(M_{0}), \ldots, \mathrm{len}(M_{n-2})\) 必須符合上述限制的長度上限。

\(n\) 最多 \(2^{32}\)。否則加密會失敗。

將區塊加密

為加密區段 \(M_i\),我們會計算 \(\mathrm{IV}_i := \mathrm{NoncePrefix} \| \mathrm{i} \| b\), \(\mathrm{i}\) 在大端序編碼中為 4 個位元組,如果 $i < n-1$ 和 0x01 則使用 0x00 位元組 $b$。

接著我們會使用 AES-GCM4 加密,其中金鑰為\(\mathrm{DerivedKey}\),初始化向量 \(\mathrm{IV}_i\),相關資料為空白字串。 \(C_i\) 是這項加密的結果 (即 \(C\) 和 \(T\) 已連結 AES-GCM 參考資料第 5.2.1.2 節的串連)。 \(M_i\)

串連加密區段

最後,所有區段都會串連為 \(\mathrm{Header} \| C_0 \| \cdots \| C_{n-1}\),也就是最終的密文。

解密

解密會反向解密加密。我們會使用標頭取得 \mathrm{NoncePrefix}$$,並分別解密每個密文區段。

API 能夠 (且通常) 允許隨機存取,或存取檔案開頭而不檢查檔案結尾。這是刻意設計,因為可以從 \(C_i\)解密 \(M_i\) ,而不會解密所有先前和剩餘的密文區塊。

不過,請務必小心,不要讓使用者混淆檔案結尾和解密錯誤:不論是哪一種情況,API 都可能需要傳回錯誤,而忽略差異將導致對手能夠有效截斷檔案。

金鑰的序列化與剖析

為了將金鑰序列化為「Tink Proto」格式,我們會先以明顯的方式將參數對應至透過 aes_gcm_hkdf_streaming.proto 提供的 proto,version 欄位必須設為 0。然後,我們會使用一般的原型序列化程序序列化,然後將產生的字串嵌入 KeyData 原型的欄位值中。我們將 type_url 欄位設為 type.googleapis.com/google.crypto.tink.AesGcmHkdfStreamingKey。接著,我們將 key_material_type 設為 SYMMETRIC,並將其嵌入金鑰組。我們通常會將 output_prefix_type 設為 RAW。例外狀況是,如果剖析金鑰時使用的 output_prefix_type 值不同,Tink 可能會寫入 RAW 或先前的值。

為剖析金鑰,我們會反向執行上述程序 (剖析原型時如往常)。系統會忽略 key_material_type 欄位。您可以忽略 output_prefix_type 的值,或是拒絕 output_prefix_typeRAW 不同的鍵。version 與 0 不同的金鑰必須遭到拒絕。

已知問題

實作上述加密函式不應成為安全的分支,請參閱分支安全相關說明。

參考資料


  1. Hoang、Reyhanitabar、Rogaway、Viizar,2015 年。線上驗證加密及其防止濫用。CRYPTO 2015。 https://eprint.iacr.org/2015/189 

  2. Hoang, Shen,2020 年。Google Tink 程式庫中的串流加密安全性。https://eprint.iacr.org/2020/1019 

  3. RFC 5869。HMAC 型擷取並擴充金鑰衍生函式 (HKDF)。 https://www.rfc-editor.org/rfc/rfc5869 

  4. NIST SP 800-38D。針對封鎖加密模式 (Galois/Counter Mode (GCM)) 和 GMAC 的建議。https://csrc.nist.gov/pubs/sp/800/38/d/final