I want to encrypt data deterministically

We recommend using the Deterministic AEAD primitive with the AES256_SIV key type.

The Deterministic Authenticated Encryption with Associated Data (Deterministic AEAD) primitive produces stable ciphertexts: encrypting a given plaintext always returns the same ciphertext. It is symmetric, which means it uses a single key for both encryption and decryption.

The following examples get you started using the Deterministic AEAD primitive:

C++

 // A command-line utility for testing Tink Deterministic AEAD. 
 #include <iostream> 
 #include <memory> 
 #include <ostream> 
 #include <string> 
 #include 
  
 "absl/flags/flag.h" 
 #include 
  
 "absl/flags/parse.h" 
 #include 
  
 "absl/log/check.h" 
 #include 
  
 "absl/strings/string_view.h" 
 #include 
  
 "tink/config/global_registry.h" 
 #include 
  
 "tink/daead/deterministic_aead_config.h" 
 #include 
  
 "tink/deterministic_aead.h" 
 #include 
  
 "util/util.h" 
 #include 
  
 "tink/keyset_handle.h" 
 #include 
  
 "tink/util/status.h" 
 ABSL_FLAG 
 ( 
 std 
 :: 
 string 
 , 
  
 keyset_filename 
 , 
  
 "" 
 , 
  
 "Keyset file in JSON format" 
 ); 
 ABSL_FLAG 
 ( 
 std 
 :: 
 string 
 , 
  
 mode 
 , 
  
 "" 
 , 
  
 "Mode of operation {encrypt|decrypt}" 
 ); 
 ABSL_FLAG 
 ( 
 std 
 :: 
 string 
 , 
  
 input_filename 
 , 
  
 "" 
 , 
  
 "Filename to operate on" 
 ); 
 ABSL_FLAG 
 ( 
 std 
 :: 
 string 
 , 
  
 output_filename 
 , 
  
 "" 
 , 
  
 "Output file name" 
 ); 
 ABSL_FLAG 
 ( 
 std 
 :: 
 string 
 , 
  
 associated_data 
 , 
  
 "" 
 , 
  
 "Associated data for Deterministic AEAD (default: empty" 
 ); 
 namespace 
  
 { 
 using 
  
 :: 
 crypto 
 :: 
 tink 
 :: 
 DeterministicAead 
 ; 
 using 
  
 :: 
 crypto 
 :: 
 tink 
 :: 
 DeterministicAeadConfig 
 ; 
 using 
  
 :: 
 crypto 
 :: 
 tink 
 :: 
 KeysetHandle 
 ; 
 using 
  
 :: 
 crypto 
 :: 
 tink 
 :: 
 util 
 :: 
 Status 
 ; 
 using 
  
 :: 
 crypto 
 :: 
 tink 
 :: 
 util 
 :: 
 StatusOr 
 ; 
 constexpr 
  
 absl 
 :: 
 string_view 
  
 kEncrypt 
  
 = 
  
 "encrypt" 
 ; 
 constexpr 
  
 absl 
 :: 
 string_view 
  
 kDecrypt 
  
 = 
  
 "decrypt" 
 ; 
 void 
  
 ValidateParams 
 () 
  
 { 
  
 // ... 
 } 
 } 
  
 // namespace 
 namespace 
  
 tink_cc_examples 
  
 { 
 // Deterministic AEAD example CLI implementation. 
 Status 
  
 DeterministicAeadCli 
 ( 
 absl 
 :: 
 string_view 
  
 mode 
 , 
  
 const 
  
 std 
 :: 
 string 
&  
 keyset_filename 
 , 
  
 const 
  
 std 
 :: 
 string 
&  
 input_filename 
 , 
  
 const 
  
 std 
 :: 
 string 
&  
 output_filename 
 , 
  
 absl 
 :: 
 string_view 
  
 associated_data 
 ) 
  
 { 
  
 Status 
  
 result 
  
 = 
  
 DeterministicAeadConfig 
 :: 
 Register 
 (); 
  
 if 
  
 ( 
 ! 
 result 
 . 
 ok 
 ()) 
  
 return 
  
 result 
 ; 
  
 // Read keyset from file. 
  
 StatusOr<std 
 :: 
 unique_ptr<KeysetHandle> 
>  
 keyset_handle 
  
 = 
  
 ReadJsonCleartextKeyset 
 ( 
 keyset_filename 
 ); 
  
 if 
  
 ( 
 ! 
 keyset_handle 
 . 
 ok 
 ()) 
  
 return 
  
 keyset_handle 
 . 
 status 
 (); 
  
 // Get the primitive. 
  
 StatusOr<std 
 :: 
 unique_ptr<DeterministicAead> 
>  
 daead 
  
 = 
  
 ( 
 * 
 keyset_handle 
 ) 
  
 - 
> GetPrimitive<crypto 
 :: 
 tink 
 :: 
 DeterministicAead 
> ( 
  
 crypto 
 :: 
 tink 
 :: 
 ConfigGlobalRegistry 
 ()); 
  
 if 
  
 ( 
 ! 
 daead 
 . 
 ok 
 ()) 
  
 return 
  
 daead 
 . 
 status 
 (); 
  
 // Read the input. 
  
 StatusOr<std 
 :: 
 string 
>  
 input_file_content 
  
 = 
  
 ReadFile 
 ( 
 input_filename 
 ); 
  
 if 
  
 ( 
 ! 
 input_file_content 
 . 
 ok 
 ()) 
  
 return 
  
 input_file_content 
 . 
 status 
 (); 
  
 // Compute the output. 
  
 std 
 :: 
 string 
  
 output 
 ; 
  
 if 
  
 ( 
 mode 
  
 == 
  
 kEncrypt 
 ) 
  
 { 
  
 StatusOr<std 
 :: 
 string 
>  
 result 
  
 = 
  
 ( 
 * 
 daead 
 ) 
 - 
> EncryptDeterministically 
 ( 
  
 * 
 input_file_content 
 , 
  
 associated_data 
 ); 
  
 if 
  
 ( 
 ! 
 result 
 . 
 ok 
 ()) 
  
 return 
  
 result 
 . 
 status 
 (); 
  
 output 
  
 = 
  
 * 
 result 
 ; 
  
 } 
  
 else 
  
 if 
  
 ( 
 mode 
  
 == 
  
 kDecrypt 
 ) 
  
 { 
  
 StatusOr<std 
 :: 
 string 
>  
 result 
  
 = 
  
 ( 
 * 
 daead 
 ) 
 - 
> DecryptDeterministically 
 ( 
  
 * 
 input_file_content 
 , 
  
 associated_data 
 ); 
  
 if 
  
 ( 
 ! 
 result 
 . 
 ok 
 ()) 
  
 return 
  
 result 
 . 
 status 
 (); 
  
 output 
  
 = 
  
 * 
 result 
 ; 
  
 } 
  
 // Write output to file. 
  
 return 
  
 WriteToFile 
 ( 
 output 
 , 
  
 output_filename 
 ); 
 } 
 } 
  
 // namespace tink_cc_examples 
 int 
  
 main 
 ( 
 int 
  
 argc 
 , 
  
 char 
 ** 
  
 argv 
 ) 
  
 { 
  
 absl 
 :: 
 ParseCommandLine 
 ( 
 argc 
 , 
  
 argv 
 ); 
  
 ValidateParams 
 (); 
  
 std 
 :: 
 string 
  
 mode 
  
 = 
  
 absl 
 :: 
 GetFlag 
 ( 
 FLAGS_mode 
 ); 
  
 std 
 :: 
 string 
  
 keyset_filename 
  
 = 
  
 absl 
 :: 
 GetFlag 
 ( 
 FLAGS_keyset_filename 
 ); 
  
 std 
 :: 
 string 
  
 input_filename 
  
 = 
  
 absl 
 :: 
 GetFlag 
 ( 
 FLAGS_input_filename 
 ); 
  
 std 
 :: 
 string 
  
 output_filename 
  
 = 
  
 absl 
 :: 
 GetFlag 
 ( 
 FLAGS_output_filename 
 ); 
  
 std 
 :: 
 string 
  
 associated_data 
  
 = 
  
 absl 
 :: 
 GetFlag 
 ( 
 FLAGS_associated_data 
 ); 
  
 std 
 :: 
 clog 
 << 
 "Using keyset from file " 
 << 
 keyset_filename 
 << 
 " to Deterministic AEAD-" 
 << 
 mode 
 << 
 " file " 
 << 
 input_filename 
 << 
 " with associated data '" 
 << 
 associated_data 
 << 
 "'." 
 << 
 std 
 :: 
 endl 
 ; 
  
 std 
 :: 
 clog 
 << 
 "The resulting output will be written to " 
 << 
 output_filename 
 << 
 "." 
 << 
 std 
 :: 
 endl 
 ; 
  
 CHECK_OK 
 ( 
 tink_cc_examples 
 :: 
 DeterministicAeadCli 
 ( 
  
 mode 
 , 
  
 keyset_filename 
 , 
  
 input_filename 
 , 
  
 output_filename 
 , 
  
 associated_data 
 )); 
  
 return 
  
 0 
 ; 
 } 
  

Go

 import 
  
 ( 
  
 "bytes" 
  
 "fmt" 
  
 "log" 
  
 "github.com/tink-crypto/tink-go/v2/daead" 
  
 "github.com/tink-crypto/tink-go/v2/insecurecleartextkeyset" 
  
 "github.com/tink-crypto/tink-go/v2/keyset" 
 ) 
 func 
  
 Example 
 () 
  
 { 
  
 // A keyset created with "tinkey create-keyset --key-template=AES256_SIV". Note 
  
 // that this keyset has the secret key information in cleartext. 
  
 jsonKeyset 
  
 := 
  
 `{ 
 "key": [{ 
 "keyData": { 
 "keyMaterialType": 
 "SYMMETRIC", 
 "typeUrl": 
 "type.googleapis.com/google.crypto.tink.AesSivKey", 
 "value": 
 "EkAl9HCMmKTN1p3V186uhZpJQ+tivyc4IKyE+opg6SsEbWQ/WesWHzwCRrlgRuxdaggvgMzwWhjPnkk9gptBnGLK" 
 }, 
 "keyId": 1919301694, 
 "outputPrefixType": "TINK", 
 "status": "ENABLED" 
 }], 
 "primaryKeyId": 1919301694 
 }` 
  
 // Create a keyset handle from the cleartext keyset in the previous 
  
 // step. The keyset handle provides abstract access to the underlying keyset to 
  
 // limit the exposure of accessing the raw key material. WARNING: In practice, 
  
 // it is unlikely you will want to use a insecurecleartextkeyset, as it implies 
  
 // that your key material is passed in cleartext, which is a security risk. 
  
 // Consider encrypting it with a remote key in Cloud KMS, AWS KMS or HashiCorp Vault. 
  
 // See https://github.com/google/tink/blob/master/docs/GOLANG-HOWTO.md#storing-and-loading-existing-keysets. 
  
 keysetHandle 
 , 
  
 err 
  
 := 
  
 insecurecleartextkeyset 
 . 
 Read 
 ( 
  
 keyset 
 . 
 NewJSONReader 
 ( 
 bytes 
 . 
 NewBufferString 
 ( 
 jsonKeyset 
 ))) 
  
 if 
  
 err 
  
 != 
  
 nil 
  
 { 
  
 log 
 . 
 Fatal 
 ( 
 err 
 ) 
  
 } 
  
 // Retrieve the DAEAD primitive we want to use from the keyset handle. 
  
 primitive 
 , 
  
 err 
  
 := 
  
 daead 
 . 
 New 
 ( 
 keysetHandle 
 ) 
  
 if 
  
 err 
  
 != 
  
 nil 
  
 { 
  
 log 
 . 
 Fatal 
 ( 
 err 
 ) 
  
 } 
  
 // Use the primitive to encrypt a message. In this case the primary key of the 
  
 // keyset will be used (which is also the only key in this example). 
  
 plaintext 
  
 := 
  
 [] 
 byte 
 ( 
 "message" 
 ) 
  
 associatedData 
  
 := 
  
 [] 
 byte 
 ( 
 "associated data" 
 ) 
  
 ciphertext 
 , 
  
 err 
  
 := 
  
 primitive 
 . 
 EncryptDeterministically 
 ( 
 plaintext 
 , 
  
 associatedData 
 ) 
  
 if 
  
 err 
  
 != 
  
 nil 
  
 { 
  
 log 
 . 
 Fatal 
 ( 
 err 
 ) 
  
 } 
  
 // Use the primitive to decrypt the message. Decrypt finds the correct key in 
  
 // the keyset and decrypts the ciphertext. If no key is found or decryption 
  
 // fails, it returns an error. 
  
 decrypted 
 , 
  
 err 
  
 := 
  
 primitive 
 . 
 DecryptDeterministically 
 ( 
 ciphertext 
 , 
  
 associatedData 
 ) 
  
 if 
  
 err 
  
 != 
  
 nil 
  
 { 
  
 log 
 . 
 Fatal 
 ( 
 err 
 ) 
  
 } 
  
 fmt 
 . 
 Println 
 ( 
 ciphertext 
 ) 
  
 fmt 
 . 
 Println 
 ( 
 string 
 ( 
 decrypted 
 )) 
  
 // Output: 
  
 // [1 114 102 56 62 150 98 146 84 99 211 36 127 214 229 231 157 56 143 192 250 132 32 153 124 244 238 112] 
  
 // message 
 } 
  

Java

 package 
  
 deterministicaead 
 ; 
 import static 
  
 java.nio.charset.StandardCharsets.UTF_8 
 ; 
 import 
  
 com.google.crypto.tink.DeterministicAead 
 ; 
 import 
  
 com.google.crypto.tink.InsecureSecretKeyAccess 
 ; 
 import 
  
 com.google.crypto.tink.KeysetHandle 
 ; 
 import 
  
 com.google.crypto.tink.RegistryConfiguration 
 ; 
 import 
  
 com.google.crypto.tink.TinkJsonProtoKeysetFormat 
 ; 
 import 
  
 com.google.crypto.tink.daead.DeterministicAeadConfig 
 ; 
 import 
  
 java.nio.file.Files 
 ; 
 import 
  
 java.nio.file.Path 
 ; 
 import 
  
 java.nio.file.Paths 
 ; 
 /** 
 * A command-line utility for encrypting small files with Deterministic AEAD. 
 * 
 * <p>It loads cleartext keys from disk - this is not recommended! 
 * 
 * <p>It requires the following arguments: 
 * 
 * <ul> 
 *   <li>mode: Can be "encrypt" or "decrypt" to encrypt/decrypt the input to the output. 
 *   <li>key-file: Read the key material from this file. 
 *   <li>input-file: Read the input from this file. 
 *   <li>output-file: Write the result to this file. 
 *   <li>[optional] associated-data: Associated data used for the encryption or decryption. 
 */ 
 public 
  
 final 
  
 class 
 DeterministicAeadExample 
  
 { 
  
 private 
  
 static 
  
 final 
  
 String 
  
 MODE_ENCRYPT 
  
 = 
  
 "encrypt" 
 ; 
  
 private 
  
 static 
  
 final 
  
 String 
  
 MODE_DECRYPT 
  
 = 
  
 "decrypt" 
 ; 
  
 public 
  
 static 
  
 void 
  
 main 
 ( 
 String 
 [] 
  
 args 
 ) 
  
 throws 
  
 Exception 
  
 { 
  
 if 
  
 ( 
 args 
 . 
 length 
  
 != 
  
 4 
 && 
 args 
 . 
 length 
  
 != 
  
 5 
 ) 
  
 { 
  
 System 
 . 
 err 
 . 
 printf 
 ( 
 "Expected 4 or 5 parameters, got %d\n" 
 , 
  
 args 
 . 
 length 
 ); 
  
 System 
 . 
 err 
 . 
 println 
 ( 
  
 "Usage: java DeterministicAeadExample encrypt/decrypt key-file input-file output-file" 
  
 + 
  
 " [associated-data]" 
 ); 
  
 System 
 . 
 exit 
 ( 
 1 
 ); 
  
 } 
  
 String 
  
 mode 
  
 = 
  
 args 
 [ 
 0 
 ] 
 ; 
  
 Path 
  
 keyFile 
  
 = 
  
 Paths 
 . 
 get 
 ( 
 args 
 [ 
 1 
 ] 
 ); 
  
 Path 
  
 inputFile 
  
 = 
  
 Paths 
 . 
 get 
 ( 
 args 
 [ 
 2 
 ] 
 ); 
  
 Path 
  
 outputFile 
  
 = 
  
 Paths 
 . 
 get 
 ( 
 args 
 [ 
 3 
 ] 
 ); 
  
 byte 
 [] 
  
 associatedData 
  
 = 
  
 new 
  
 byte 
 [ 
 0 
 ] 
 ; 
  
 if 
  
 ( 
 args 
 . 
 length 
  
 == 
  
 5 
 ) 
  
 { 
  
 associatedData 
  
 = 
  
 args 
 [ 
 4 
 ] 
 . 
 getBytes 
 ( 
 UTF_8 
 ); 
  
 } 
  
 // Initialise Tink: register all Deterministic AEAD key types with the Tink runtime 
  
 DeterministicAeadConfig 
 . 
 register 
 (); 
  
 // Read the keyset into a KeysetHandle 
  
 KeysetHandle 
  
 handle 
  
 = 
  
 TinkJsonProtoKeysetFormat 
 . 
 parseKeyset 
 ( 
  
 new 
  
 String 
 ( 
 Files 
 . 
 readAllBytes 
 ( 
 keyFile 
 ), 
  
 UTF_8 
 ), 
  
 InsecureSecretKeyAccess 
 . 
 get 
 ()); 
  
 // Get the primitive 
  
 DeterministicAead 
  
 daead 
  
 = 
  
 handle 
 . 
 getPrimitive 
 ( 
 RegistryConfiguration 
 . 
 get 
 (), 
  
 DeterministicAead 
 . 
 class 
 ); 
  
 // Use the primitive to encrypt/decrypt files. 
  
 if 
  
 ( 
 MODE_ENCRYPT 
 . 
 equals 
 ( 
 mode 
 )) 
  
 { 
  
 byte 
 [] 
  
 plaintext 
  
 = 
  
 Files 
 . 
 readAllBytes 
 ( 
 inputFile 
 ); 
  
 byte 
 [] 
  
 ciphertext 
  
 = 
  
 daead 
 . 
 encryptDeterministically 
 ( 
 plaintext 
 , 
  
 associatedData 
 ); 
  
 Files 
 . 
 write 
 ( 
 outputFile 
 , 
  
 ciphertext 
 ); 
  
 } 
  
 else 
  
 if 
  
 ( 
 MODE_DECRYPT 
 . 
 equals 
 ( 
 mode 
 )) 
  
 { 
  
 byte 
 [] 
  
 ciphertext 
  
 = 
  
 Files 
 . 
 readAllBytes 
 ( 
 inputFile 
 ); 
  
 byte 
 [] 
  
 plaintext 
  
 = 
  
 daead 
 . 
 decryptDeterministically 
 ( 
 ciphertext 
 , 
  
 associatedData 
 ); 
  
 Files 
 . 
 write 
 ( 
 outputFile 
 , 
  
 plaintext 
 ); 
  
 } 
  
 else 
  
 { 
  
 System 
 . 
 err 
 . 
 println 
 ( 
 "The first argument must be either encrypt or decrypt, got: " 
  
 + 
  
 mode 
 ); 
  
 System 
 . 
 exit 
 ( 
 1 
 ); 
  
 } 
  
 System 
 . 
 exit 
 ( 
 0 
 ); 
  
 } 
  
 private 
  
 DeterministicAeadExample 
 () 
  
 {} 
 } 
  

Python

 import 
  
 tink 
 from 
  
 tink 
  
 import 
 daead 
 from 
  
 tink 
  
 import 
 secret_key_access 
 def 
  
 example 
 (): 
  
 """Encrypt and decrypt using deterministic AEAD.""" 
 # Register the deterministic AEAD key manager. This is needed to create a 
 # DeterministicAead primitive later. 
 daead 
 . 
 register 
 () 
 # A keyset created with "tinkey create-keyset --key-template=AES256_SIV". Note 
 # that this keyset has the secret key information in cleartext. 
 keyset 
 = 
 r 
 """{ 
 "key": [{ 
 "keyData": { 
 "keyMaterialType": 
 "SYMMETRIC", 
 "typeUrl": 
 "type.googleapis.com/google.crypto.tink.AesSivKey", 
 "value": 
 "EkAl9HCMmKTN1p3V186uhZpJQ+tivyc4IKyE+opg6SsEbWQ/WesWHzwCRrlgRuxdaggvgMzwWhjPnkk9gptBnGLK" 
 }, 
 "keyId": 1919301694, 
 "outputPrefixType": "TINK", 
 "status": "ENABLED" 
 }], 
 "primaryKeyId": 1919301694 
 }""" 
 # Create a keyset handle from the cleartext keyset in the previous 
 # step. The keyset handle provides abstract access to the underlying keyset to 
 # limit the exposure of accessing the raw key material. WARNING: In practice, 
 # it is unlikely you will want to use a cleartext_keyset_handle, as it implies 
 # that your key material is passed in cleartext which is a security risk. 
 keyset_handle 
 = 
 tink 
 . 
 json_proto_keyset_format 
 . 
 parse 
 ( 
 keyset 
 , 
 secret_key_access 
 . 
 TOKEN 
 ) 
 # Retrieve the DeterministicAead primitive we want to use from the keyset 
 # handle. 
 primitive 
 = 
 keyset_handle 
 . 
 primitive 
 ( 
 daead 
 . 
 DeterministicAead 
 ) 
 # Use the primitive to encrypt a message. In this case the primary key of the 
 # keyset will be used (which is also the only key in this example). 
 ciphertext 
 = 
 primitive 
 . 
 encrypt_deterministically 
 ( 
 b 
 'msg' 
 , 
 b 
 'associated_data' 
 ) 
 # Use the primitive to decrypt the message. Decrypt finds the correct key in 
 # the keyset and decrypts the ciphertext. If no key is found or decryption 
 # fails, it raises an error. 
 output 
 = 
 primitive 
 . 
 decrypt_deterministically 
 ( 
 ciphertext 
 , 
 b 
 'associated_data' 
 ) 
  

Deterministic AEAD

The Deterministic Authenticated Encryption with Associated Data (Deterministic AEAD) primitive provides encryption with a deterministic property: encrypting the same data always yields the same ciphertext. This type of encryption is useful for key wrapping or for some schemes for searching on encrypted data (see RFC 5297, Section 1.3 for more info). Because of its deterministic property, implementations of this primitive can lead to loss of secrecy because an attacker only needs to find out the ciphertext for a given message to identify other instances of that message.

Deterministic AEAD has the following properties:

  • Secrecy: Nothing about the plaintext is known, except its length and the equality of repeated plaintexts.
  • Authenticity: It is impossible to change the encrypted plaintext underlying the ciphertext without being detected.
  • Symmetric: Encrypting the plaintext and decrypting the ciphertext is done with the same key.
  • Deterministic: As long as the primary key is not changed, encrypting a plaintext twice with the same parameters results in the same ciphertext.

Associated data

Deterministic AEAD can also be used to tie ciphertext to specific associated data . For example, if you have a database with the fields user-id and encrypted-medical-history : In this scenario, user-id can be used as associated data when encrypting encrypted-medical-history . This prevents an attacker from moving medical history from one user to another.

Choose a key type

We recommend the AES256_SIVkey type for all use cases.

Security guarantees

  • At least 80-bit authentication strength.
  • The plaintext and associated data can have arbitrary lengths (within the range 0..2 32 bytes).
  • 128-bit security level against key recovery attacks, and also in multi-user attacks with up to 2 32 keys — that means if an adversary obtains 2 32 ciphertexts of the same message encrypted under 2 32 keys, they need to do 2 128 computations to obtain a single key.
  • The ability to safely encrypt 2 38 messages, provided each is less than 1MB in length.
Create a Mobile Website
View Site in Mobile | Classic
Share by: