金鑰組

Tink 會使用金鑰組啟用金鑰輪替。正式的金鑰組是非空白清單1,其中含有一個金鑰的主 (例如用來簽署及加密新明文的金鑰)。此外,金鑰組中的金鑰會取得專屬 ID2 和金鑰狀態,藉此停用金鑰,而不必從金鑰組移除。

金鑰組是使用者可存取金鑰的主要方式 (透過類別 KeysetHandle),這可確保每位使用者有程式碼可一次處理多個鍵。對大部分密碼編譯使用者而言,處理多個金鑰是必要性,因為必須變更金鑰 (例如,舊金鑰可能會外洩),而且幾乎沒有一項不可分割的「切換為下一個金鑰」,該金鑰可以套用至程式碼執行的機器,以及全球和立即執行的所有密文。因此,使用者必須編寫程式碼,當其中一個金鑰變更至下一個金鑰時,使用者就必須編寫程式碼。

範例:AEAD

假設 AEAD 金鑰組包含 AEAD 原始的多個鍵,如上文所述,每個鍵都會指定兩個函式: \(\mathrm{Enc}\) 和 \(\mathrm{Dec}\)。該金鑰組現在也會指定兩個新函式: \(\mathrm{Enc}\) 且 \(\mathrm{Dec}\) - \(\mathrm{Enc}\) 等於 \(\mathrm{Enc}\) 按鍵集的主鍵函式 \(\mathrm{Enc}\) ,函式 \(\mathrm{Dec}\) 會嘗試使用所有鍵解密,而函式會依照某種順序逐一完成解密 (請參閱下文,瞭解 Tink 如何提升此效能。)

值得注意的是,金鑰組是「完整鍵」,是金鑰組的完整 \(\mathrm{Enc}\) 和\(\mathrm{Dec}\) 函式說明。這表示使用者可以編寫一個類別做為 KeysetHandle,表示該類別需要完整物件說明 \(\mathrm{Enc}\) 並 \(\mathrm{Dec}\) 正常運作。這樣做可讓使用者編寫 API,藉此傳達:如要使用這個類別,您需要提供加密編譯基元的說明。

金鑰輪替

以 Tink 使用者為例,編寫程式時,該程式會先從 KMS 取得金鑰組,然後從這個金鑰組建立 AEAD 物件,最後再使用這個物件加密及解密密文。

這類使用者會自動準備金鑰輪替;如果他們目前選擇不符合標準,就會切換演算法。

但是在實作這類金鑰輪替時,必須特別謹慎:首先,KMS 應在金鑰組中新增金鑰 (但尚未將其設為主要金鑰)。接著,您需要向所有二進位檔推出新的金鑰組,讓使用這個金鑰組的每個二進位檔在金鑰組中都有最新的金鑰。唯有如此,新金鑰才應設為主要金鑰,而產生的金鑰組會再次透過該金鑰組分配給所有二進位檔。

密文中的金鑰 ID

請再次參考 AEAD 金鑰組的範例。如果是單文進行解密,Tink 就無法使用 Keyset 中的所有金鑰解密,因為您無法知道要使用哪個金鑰將金鑰組加密。這可能會造成大量效能負擔。

因此,Tink 可使用從 ID 衍生的 5 位元組字串,在密文前面加上密文。在上述「完整金鑰」的原理後,這個前置字串是金鑰的一部分,而且以這組金鑰衍生的所有密文都應含有這個前置字串。使用者建立金鑰時,可選擇金鑰是否應使用這類前置字串,或不需要使用該金鑰的密文格式。

當鍵位於金鑰組時,Tink 會從該鍵組的 ID 計算這個標記。金鑰組內的 ID 是唯一值2,意味著這些標記是唯一的。因此,相較於使用單一金鑰解密,如果只使用標記的鍵,並不會降低效能:Tink 只需在解密時嘗試使用其中一個金鑰。

然而,由於標記是鍵的一部分,因此也意味著該鍵只能位於具備特定 ID 的金鑰組。這在說明不同語言鍵物件實作時會產生某些影響。


  1. Tink 的某些部分仍會將金鑰組視為集合。不過,這應該進行變更。原因在於順序通常非常重要:例如,考慮使用 Aead 進行金鑰輪替的一般生命週期。首先,金鑰必須加入一個金鑰組。這組金鑰尚未設為主要金鑰,但尚未設為主要金鑰。這個新的金鑰組現已推出至所有二進位檔。一旦所有二進位檔知道新金鑰,系統就會將金鑰設為主要金鑰 (在此時僅使用此金鑰是安全的)。在第二個步驟中,金鑰輪替必須知道最後新增的金鑰。 

  2. 為了與 Google 內部程式庫相容,Tink 允許具有重複 ID 的金鑰組。日後會移除這項支援。