本頁說明 Tink 的金鑰和基本輸出內容線路格式。這份文件適用於想在 Tink 中新增其他語言的密碼編譯人員,以及想使用線路相容模式的其他高階加密程式庫維護人員。不適合一般觀眾。
金鑰組序列化
Tink 會使用 Google protobuf 將金鑰集序列化。
- 二進位序列化金鑰組是 tink.proto 中定義的序列化 Keyset proto。金鑰的 KeyData 值屬性是相應金鑰類型的序列化 Proto。
- JSON 序列化金鑰組是採用 JSON 格式序列化的金鑰組 Proto。 請注意,KeyData 值仍是 二進位序列化 Proto。
- 加密金鑰組是序列化的 EncryptedKeyset Proto,定義於 tink.proto。其中包含加密的二進位序列化金鑰組,以及一些未加密的 KeysetInfo 中繼資料 (選用)。
Tink 輸出內容前置字串
大多數 Tink 基本體都支援 5 位元組的輸出前置字串,包括:
- 1 位元組版本:
0x01 - 4 個位元組的金鑰提示:這是所用金鑰的金鑰 ID。
部分舊版金鑰也可能支援版本位元組 0x00。
請注意,這個前置字串未通過驗證,因此不適合用於安全用途。Tink 會將這項資訊做為提示,加快解密或驗證速度。
AEAD
一般來說,Tink 會將 AEAD 密文格式設為:
prefix || IV || ciphertext || tag
除非對應的 RFC 另有規定。prefix 可以是空白,也可以是 5 位元組的 Tink 輸出前置字元。
AES-CTR-HMAC
如果是 AES-CTR-HMAC,Tink 會使用相關聯的資料 (AD) 計算 MAC,如下所示:
AD || IV || ciphertext || bitlen(AD)
其中 bitlen(AD) 是 AD 的長度 (以位元為單位),以 64 位元大端序不帶正負號整數表示。這個 HMAC 配置遵循 Mcgrew 的 AES-CBC-HMAC 草案。
確定性 AEAD
Tink 會為 AES-SIV 實作 RFC 5297,將合成初始向量 (SIV) 放在密文開頭。原始項目可能會新增 5 位元組的 Tink 輸出前置字串。
雖然 RFC 5297 支援相關資料清單,但 Tink 只支援一項相關資料,這對應於 RFC 5297 中的單一元素清單。空的關聯資料是含有一個空元素的清單,而不是空清單。
串流 AEAD
請參閱 AES-CTR HMAC 和 AES-GCM-HKDF。
信封式加密
信封式加密會使用 Tink 的 AEAD 基本類型,以資料加密金鑰 DEK 加密資料。加密方式如下:
- 系統會使用指定的金鑰範本 (或金鑰參數) 產生新的
DEK。 DEK會序列化為位元組字串。金鑰類型 proto 的通訊協定緩衝區序列化格式。舉例來說,這是為 AES GCM 金鑰類型 DEK 定義的序列化AesGcmKey通訊協定緩衝區訊息 (位於 aes_gcm.proto 中)。如要瞭解如何序列化通訊協定緩衝區,請參閱通訊協定緩衝區序列化。- 序列化的
DEK會由外部供應商 (例如 GCP) 加密為encrypted DEK。 DEK會將明文和相關聯的資料加密為ciphertext。因此ciphertext的格式與對應DEK的 AEAD 基本體完全相同。
信封式加密的輸出格式如下:
encrypted DEK length || encrypted DEK || ciphertext
encrypted DEK length 為 4 個位元組,以 32 位元大端序整數的形式儲存 encrypted DEK 的長度。
MAC
Tink 會遵循對應的 RFC。原始物件可能會在標記中加入 5 位元組的 Tink 輸出前置字元。
PRF 設定
Tink 會遵循對應的 RFC。請注意,PRF 的金鑰類型與相同演算法的 MAC 金鑰類型不同,因為前者不包含輸出長度。PRF 集合金鑰一律不會新增 Tink 輸出前置字串。這可確保輸出內容確實是 PRF。
混合式加密
Tink 混合式加密的一般線路格式如下:
prefix || encapsulated_key || encrypted_data
prefix 為空值或 5 位元組的 Tink 輸出前置字元。每個鍵類型都包含要剖析的位元組數量,以及如何從 encapsulated_key 剖析這些位元組的資訊。
HPKE (混合公開金鑰加密)
Tink 遵循 RFC 9180 中定義的 HPKE 標準。HPKE 密碼套件包含下列三種基本元素。
- 金鑰封裝機制 (KEM)
- 金鑰衍生函式 (KDF)
- AEAD
HPKE 標準並未在 RFC 9180 第 10 節中定義一般線路格式。Tink 的 HPKE 實作會使用下列 encapsulated_key 和 encrypted_data 值。
encapsulated_key- 傳送者的序列化公開金鑰
- 定義為
enc,請參閱 RFC 9180 第 4.1 節 - 格式取決於使用的特定 HPKE KEM
encrypted_data- 密文和標記 (即
ciphertext || tag,不含 IV) - 如 RFC 9180 第 4 節所定義的
ct - 格式取決於使用的特定 HPKE AEAD
- 密文和標記 (即
以 X25519 Diffie-Hellman 為基礎的 KEM
如果是 X25519 DHKEM,值 enc 是傳送端的 32 位元組 Diffie-Hellman 公開金鑰。
ECIES-AEAD-HKDF
在 Tink 的 ECIES-AEAD-HKDF 實作中,encapsulated_key 是金鑰封裝機制 (KEM) 的輸出內容,encrypted_data 則是資料封裝機制 (DEM) 的輸出內容。
KEM
視金鑰類型而定,Tink 會使用壓縮和未壓縮的橢圓曲線點,並遵循 RFC 8422/ANSI.X9-62.2005 編碼標準。如果是未壓縮的點,位元組 0x04 後面會接續 x 和 y 座標,以固定大小的整數表示。如果是壓縮座標,則會使用位元組 0x02 或 0x03,以及固定大小整數的 x 座標。對於 X25519,
系統會使用 RFC 7748 定義 (x 座標為固定大小的整數)。
DEM
對於 encrypted_data,Tink 使用與 AEAD 相同的格式。這包括指定 IV。
金鑰衍生
首先,系統會計算共用點的 x 座標 x_ss。然後將 AEAD 的金鑰設為:
HKDF(ikm = encapsulated_key || x_ss, salt = salt_of_key, info = context_info, length = dem_key_size)
其中 encapsulated_key 是以位元組表示的完整 KEM 輸出內容。
數位簽章
Tink 會遵循對應的 RFC。原始物件可能會在產生的標記中加入 5 位元組的 Tink 輸出前置字元。
ECDSA
視金鑰中的 EcdsaSignatureEncoding 欄位而定,ECDSA 簽章的格式為 IEEE P1363 或 ASN.1 DER。
IEEE P1363 簽章的格式為 r || s,其中 r 和 s 會以零填補,且位元組大小與曲線順序相同。舉例來說,如果是 NIST P-256 曲線,r 和 s 會補零至 32 個位元組。
DER 簽章會使用 ASN.1 編碼:
ECDSA-Sig-Value :: = SEQUENCE { r INTEGER, s INTEGER }
具體來說,編碼為:
0x30 || totalLength || 0x02 || r's length || r || 0x02 || s's length || s
Tink 遵循簽章驗證最佳做法,只接受 DER 編碼的 ECDSA 簽章 (替代的 BER 編碼簽章無效)。
這有助於防範簽章延展性攻擊,這類攻擊通常會影響加密貨幣系統。
外部 Mu ML-DSA 前置雜湊值
Prehash 原始值會將訊息轉換為前置雜湊值,然後由 SignPrehash 原始值簽署。如 RFC 9881 所述,在外部 Mu 模式下,ML-DSA 的前置雜湊值為 69 個位元組,版面配置如下:
0xff || key_id (4 bytes, big-endian) || mu (64 bytes)
mu是 ML-DSA 訊息代表,計算方式為SHAKE256(tr || 0x00 || 0x00 || message, 64),其中tr是公開金鑰的 64 位元組雜湊值,第一個0x00是純 ML-DSA 的 FIPS 204 網域分隔符,第二個0x00是 (空白) 內容字串的長度。由於tr是從公開金鑰衍生而來,因此mu會以密碼編譯方式繫結至該金鑰,但檢查繫結需要原始訊息。- 5 位元組前置字元是 Tink 框架,不屬於
mu。這會將前置雜湊值與特定金鑰建立關聯,讓 SignPrehash 知道要使用哪個金鑰簽署,並拒絕沒有金鑰的前置雜湊值。前置字串是純中繼資料,不會以密碼編譯方式繫結任何內容,任何人都可以重新編寫。
如果輸入內容不是剛好 69 個位元組、開頭不是 0xff,或是金鑰 ID 與金鑰集中的已啟用金鑰不符,簽署作業就會拒絕。