Tink 會使用金鑰集啟用金鑰輪替。正式來說,金鑰集是金鑰的非空白清單1,其中一個金鑰會指定為主要金鑰 (例如用於簽署及加密新純文字的金鑰)。此外,金鑰集中的金鑰會取得專屬 ID 2 和金鑰狀態,因此您可以在不從金鑰集中移除金鑰的情況下停用金鑰。
使用者主要透過鍵集存取鍵 (透過類別 KeysetHandle)。這可確保每位使用者都有程式碼可一次處理多個鍵。對大多數密碼學使用者而言,處理多個金鑰是必要的:必須能夠變更金鑰 (例如舊金鑰可能外洩),而且幾乎不會有「切換至下一個金鑰」的原子作業,可套用至程式碼執行的機器和所有密文,在全球範圍內即時生效。因此,使用者需要編寫程式碼,以便在從一個鍵變更為下一個鍵時運作。
範例:AEAD
假設您有一個 AEAD 金鑰組,其中包含多個 AEAD 基本體的金鑰。如先前所述,每個鍵都會唯一指定兩個函式: \(\mathrm{Enc}\) 和 \(\mathrm{Dec}\)。現在,金鑰集也會指定兩個新 函式: \(\mathrm{Enc}\) 和 \(\mathrm{Dec}\) - \(\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 中的所有金鑰解密,因為無法得知加密 Keyset 時使用的金鑰。這可能會造成大量效能負擔。
因此,Tink 允許在密文前面加上從 ID 衍生而來的 5 位元組字串。根據上述「完整金鑰」的哲學,這個前置字串是金鑰的一部分,且所有使用這個金鑰衍生的密文都應包含這個前置字串。使用者建立金鑰時,可以選擇金鑰是否應使用這類前置字元,或是否應使用不含前置字元的密文格式。
金鑰位於金鑰集時,Tink 會根據金鑰在金鑰集中的 ID 計算這個標記。ID 在鍵集內不得重複2,這表示標記不得重複。因此,如果只使用標記的金鑰,與使用單一金鑰解密相比,不會有任何效能損失:Tink 解密時只需要嘗試其中一個金鑰。
不過,由於標記是金鑰的一部分,這也表示金鑰只能在具有特定 ID 的金鑰集中。因此,以不同語言說明主要物件的實作方式時,請務必留意這點。
需要 ID 但沒有輸出內容前置字元的鍵
部分鍵必須有特定 ID,但輸出內容不得加上前置字串。舉例來說,使用 NO_PREFIX_WITH_PREHASH_ID 變體 (以輸出前置字元類型 WITH_ID_REQUIREMENT 儲存) 的簽名金鑰會產生不含前置字元的簽名。將這類金鑰與 Prehash 基本型別搭配使用時,Tink 會將金鑰 ID 寫入 prehash 值,讓遠端簽署者知道要使用哪一個金鑰簽署。
與使用前置字元的金鑰相同,這類金鑰只能位於該 ID 下的金鑰集。前雜湊值中的金鑰 ID 是純中繼資料,就像輸出前置字元一樣:簽章不會繫結金鑰 ID,驗證者也永遠不會看到。如需位元組層級的版面配置,請參閱「Tink 線路格式」。