暗号には中心となる原則があり、出力は決定的または非決定的のいずれかになります。決定的な出力では、同じ入力に対して常に同じ出力が得られます。たとえば、平文から暗号を作成する場合、出力は常に同じ暗号文になります。非決定的の場合、プロセスで何らかのランダム化が使用されているため、出力がどうなるかを予測できません。署名では、ECDSAは非決定的であり、EdDSAは決定的です。
対称鍵方式に関しては、決定的な性質はデータの検索に役立ちますが、敵対者が入力を出力にマッピングできるため、セキュリティの観点からは脆弱になります。この場合、決定的な性質とAssociated Dataによる認証暗号化(Deterministic AEAD)を組み合わせ、AES-SIVモードを使用します。
私たちは秘密を少し公開しすぎており、最も重要な秘密の1つである暗号化キーを保護できていないことがよくあります。これを克服するために、キーラッピングと呼ばれる方法を使用できます。これはキーを保護します。これは、信頼できないチャネルを介してキーを送信したり、強力なアクセス制御なしでキーを保存したりする場合に特に重要です。キーラッピングに関して、RogawayらはSIV(Synthetic Initialisation Vector)方式を提案しました[here]。これは、認証と暗号化、およびキーに関連する追加データの認証を行います。
この方法は現在、RFC 5297 [here]で標準化されています。強化された暗号化方式を使用すると、暗号を認証し、その整合性を証明できます。これは、Associated Dataによる認証暗号化(AEAD)として知られています。このために、暗号化プロセスを認証するための追加データを提供し、暗号文が変更された場所を特定して、復号化できないようにします。従来のAEAD方式のほとんどでは、nonce値を作成し、認証されるが暗号化されない追加データ(AD)を追加します。nonceレスのアプローチでは、キーラッピング方式を使用できます。これは、暗号化キーを保護するためによく使用されます。追加データには、以下を含めることができます[here]:
addresses, ports, sequence numbers, protocol version numbers, and other fields that indicate how the plaintext or ciphertext should be handled, forwarded, or processed
このようにして、ネットワークパケットを暗号化されたデータにバインドし、侵入者が他の送信からの有効な暗号文をコピーして貼り付けることができないように、整合性を提供できます。たとえば、パケットシーケンス番号とポートにバインドすると、別のシーケンス番号または別のポートでは認証が失敗します。
次のコードでは、「101112131415161718191a1b1c1d1e1f2021222324252627」の追加情報を使用しますが、実際には、これは任意の長さにすることができます。SIV内では、この追加データの複数のソース(文字列のベクトルとして知られています)を持つこともできます。この情報は複数のソースに分散している可能性があり、侵入者がキャプチャして複製することは困難である可能性があります。これはSIVの特別な機能の1つであり、追加情報のためにこれらを単一の文字列にマージするのではなく、認証に複数の文字列を使用できます。
byte[] plaintext = pl.getBytes();
String additional="101112131415161718191a1b1c1d1e1f2021222324252627";
byte[] ciphertext = daead.encryptDeterministically(plaintext, Hex.decode(additional));
従来のAEAD方式は、nonceの再利用やnonceの誤用の点で脆弱になる可能性があります。多くのシステムでは、nonceを生成する非決定的な方法を使用しているため、以前に生成された値が以前に使用されていないかどうかを知る方法はありません。もう1つの弱点は、仮想マシンをロールバックし、使用されたnonce値を検出することで、nonceを「再生」できることです。したがって、ほとんどのAEAD方式は一意のnonceで安全ですが、nonceが一意でない場合はセキュリティが保証されません。nonce値のサイズは限られているため、同じキーで再利用する可能性が常にあり、したがって、定期的にキーを変更する必要がある場合があります。SIVは、nonceの再利用/誤用に対する保護を強化し、攻撃者は、特定の平文値と特定の関連データセットが、定義されたキーとnonce値を使用して保護されていることを判断できるだけです。
次のコードでは、SIVキーを作成し、次に暗号文の値を作成します。これらは両方とも同じである必要があります[here]:
package main
import (
"fmt"
"os"
"github.com/tink-crypto/tink-go/v2/daead"
"github.com/tink-crypto/tink-go/v2/keyset"
)
func main() {
msg:="Hello"
associated:="Hello"
argCount := len(os.Args[1:])
if (argCount>0) {msg = os.Args[1]}
if (argCount>1) {associated =os.Args[2]}
handle, _ := keyset.NewHandle(daead.AESSIVKeyTemplate())
primitive, _ := daead.New(handle)
plaintext := []byte(msg)
associatedData := []byte(associated)
ciphertext1, _:= primitive.EncryptDeterministically(plaintext, associatedData)
ciphertext2, _:= primitive.EncryptDeterministically(plaintext, associatedData)
res, _ := primitive.DecryptDeterministically(ciphertext1, associatedData)
fmt.Printf("Plaintext is %s\n",msg)
fmt.Printf("Associated data is %s\n\n",associated)
fmt.Printf("Key is %s\n\n",handle)
fmt.Printf("Cipher1 is %x\n\n",ciphertext1)
fmt.Printf("Cipher2 is %x\n\n",ciphertext2)
fmt.Printf("Decrypted is %s",res)
}
サンプル実行では、決定的に暗号化すると同じ暗号になることがわかります[here]:
Plaintext is Testing 123
Associated data is qwerty123
Key is primary_key_id:1537855825 key_info:{type_url:"type.googleapis.com/google.crypto.tink.AesSivKey" status:ENABLED key_id:1537855825 output_prefix_type:TINK}
Cipher1 is 015ba9d1518f3a5d213ce9630294f87af76ce86011c8631842aa96571d0dee03
Cipher2 is 015ba9d1518f3a5d213ce9630294f87af76ce86011c8631842aa96571d0dee03
Decrypted is Testing 123