Tink は、鍵のローテーションを有効にするために鍵セットを使用します。正式には、鍵セットは鍵の空でないリスト1であり、そのうちの 1 つの鍵がプライマリとして指定されます(たとえば、新しいプレーンテキストの署名と暗号化に使用される鍵)。また、キーセット内のキーには一意の ID2 とキーのステータスが割り当てられます。これにより、キーセットからキーを削除することなく、キーを無効にできます。
キーセットは、ユーザーがキーにアクセスする主な方法です(KeysetHandle クラス経由)。これにより、すべてのユーザーが複数のキーを一度に処理するコードを持つことになります。暗号化のほとんどのユーザーにとって、複数の鍵の処理は必須です。鍵の変更が可能である必要があります(古い鍵が漏洩する可能性があるなど)。コードが実行されるマシンとすべての暗号文に、グローバルに、瞬時に適用できるアトミックな「次の鍵への切り替え」はほとんどありません。そのため、ユーザーは、あるキーから次のキーに切り替わったときに機能するコードを記述する必要があります。
例: AEAD
AEAD プリミティブの複数の鍵を含む AEAD 鍵セットについて考えてみましょう。前述のように、各キーは 2 つの関数( \(\mathrm{Enc}\) と \(\mathrm{Dec}\))を一意に指定します。キーセットでは、 \(\mathrm{Enc}\) と \(\mathrm{Dec}\) という 2 つの新しい関数も指定されるようになりました。 \(\mathrm{Enc}\) はキーセットの主キーの関数 \(\mathrm{Enc}\) と同じです。一方、関数 \(\mathrm{Dec}\) はすべてのキーで復号を試み、ある順序でキーを処理します(Tink がこのパフォーマンスをどのように改善するかについては、下記をご覧ください)。
鍵セットは完全な鍵であることに注意してください。つまり、使用される関数 \(\mathrm{Enc}\) と\(\mathrm{Dec}\) の完全な説明です。つまり、ユーザーは KeysetHandle を入力として受け取るクラスを記述できます。これは、クラスが適切に機能するにはオブジェクトの完全な説明 \(\mathrm{Enc}\) と \(\mathrm{Dec}\) が必要であることを表しています。これにより、ユーザーは、このクラスを使用するには暗号プリミティブの説明を提供する必要があることを伝える API を記述できます。
鍵のローテーション
Tink ユーザーが、まず KMS から鍵セットを取得し、この鍵セットから AEAD オブジェクトを作成し、最後にこのオブジェクトを使用して暗号文の暗号化と復号を行うプログラムを作成するとします。
このようなユーザーは、鍵のローテーションと、現在の選択が標準を満たさなくなった場合のアルゴリズムの切り替えが自動的に準備されます。
ただし、このような鍵のローテーションを実装する際には、ある程度の注意が必要です。まず、KMS は新しい鍵を鍵セットに追加する必要があります(ただし、まだプライマリとして設定しないでください)。次に、この鍵セットを使用するすべてのバイナリが鍵セット内の最新の鍵を持つように、新しい鍵セットをすべてのバイナリにロールアウトする必要があります。その後、新しい鍵をプライマリにし、結果の鍵セットを鍵セットを使用するすべてのバイナリに再び配布する必要があります。
暗号文のキー識別子
AEAD 鍵セットの例をもう一度見てみましょう。単純に実行すると、暗号文の復号には、鍵セットのすべての鍵で復号を試す必要があります。鍵セットの暗号化に使用された鍵を特定する方法がないためです。これにより、パフォーマンスのオーバーヘッドが大きくなる可能性があります。
このため、Tink では、ID から派生した 5 バイトの文字列を暗号テキストの接頭辞として使用できます。上記の「完全な鍵」の哲学に従い、この接頭辞は鍵の一部であり、この鍵で導出されたすべての暗号文にはこの接頭辞が含まれている必要があります。ユーザーが鍵を作成するときに、鍵でこのような接頭辞を使用するか、接頭辞のない暗号文形式を使用するかを選択できます。
鍵が鍵セットにある場合、Tink は鍵セット内の鍵の ID からこのタグを計算します。ID が鍵セット内で一意である2という事実は、タグが一意であることを意味します。したがって、タグ付きキーのみが使用されている場合、単一のキーで復号する場合と比較してパフォーマンスの低下はありません。Tink は復号時にキーの 1 つだけを試す必要があるためです。
ただし、タグはキーの一部であるため、キーが特定の ID を持つ場合にのみ、キーセットに存在できることも意味します。これは、さまざまな言語でキー オブジェクトの実装を説明する際に影響します。
ID 要件があるが、出力接頭辞がない鍵
特定の ID を持つ必要があり、出力に接頭辞を追加しないキーもあります。たとえば、NO_PREFIX_WITH_PREHASH_ID バリアントの署名鍵(出力接頭辞タイプ WITH_ID_REQUIREMENT で保存)は、接頭辞のない署名を生成します。このような鍵を Prehash プリミティブで使用すると、Tink は鍵 ID を prehash 値に書き込みます。これにより、リモート署名者は署名に使用する鍵を認識できます。
接頭辞を使用する鍵と同様に、このような鍵は 1 つの ID の鍵セットにのみ存在できます。事前ハッシュ値の鍵 ID は、出力接頭辞と同様にプレーン メタデータです。署名によってバインドされず、検証ツールに表示されることもありません。バイトレベルのレイアウトについては、Tink ワイヤー形式をご覧ください。
-
Tink の一部では、Keyset がセットとして扱われます。ただし、これは変更する必要があります。その理由は、一般的に順序が重要であるためです。たとえば、Aead を使用した鍵のローテーションの一般的なライフサイクルについて考えてみましょう。まず、新しい鍵がキーセットに追加されます。このキーはまだプライマリ キーではありませんが、アクティブです。この新しいキーセットはすべてのバイナリにロールアウトされます。すべてのバイナリが新しい鍵を認識すると、鍵がプライマリになります(この時点で初めてこの鍵を安全に使用できます)。この 2 つ目の手順では、鍵のローテーションで最後に追加された鍵を把握する必要があります。 ↩
-
Google 内部ライブラリとの互換性を確保するため、Tink では ID が繰り返される鍵セットを使用できます。このサポートは今後削除される予定です。 ↩