I want to exchange data

We recommend the Hybrid Encryption primitive with the DHKEM_X25519_HKDF_SHA256, HKDF_SHA256, AES_256_GCM key type for most public key encryption use cases.

Public key encryption involves protecting data with two keys: one public and one private. The public key is used for encryption and the private key is used for decryption. This is a good choice if the sender cannot store secrets and needs to encrypt data with a public key.

The following examples get you started using the Hybrid Encryption primitive:

C++

 // A command-line utility for testing Tink Hybrid Encryption. 
 #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 
  
 "util/util.h" 
 #ifndef TINK_EXAMPLES_EXCLUDE_HPKE 
 #include 
  
 "tink/hybrid/hpke_config.h" 
 #endif 
 #include 
  
 "tink/hybrid/hybrid_config.h" 
 #include 
  
 "tink/hybrid_decrypt.h" 
 #include 
  
 "tink/hybrid_encrypt.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 
 , 
  
 "" 
 , 
  
 "Input file name" 
 ); 
 ABSL_FLAG 
 ( 
 std 
 :: 
 string 
 , 
  
 output_filename 
 , 
  
 "" 
 , 
  
 "Output file name" 
 ); 
 ABSL_FLAG 
 ( 
 std 
 :: 
 string 
 , 
  
 context_info 
 , 
  
 "" 
 , 
  
 "Context info for Hybrid Encryption/Decryption" 
 ); 
 namespace 
  
 { 
 using 
  
 :: 
 crypto 
 :: 
 tink 
 :: 
 HybridDecrypt 
 ; 
 using 
  
 :: 
 crypto 
 :: 
 tink 
 :: 
 HybridEncrypt 
 ; 
 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 
  
 { 
 Status 
  
 HybridCli 
 ( 
 absl 
 :: 
 string_view 
  
 mode 
 , 
  
 const 
  
 std 
 :: 
 string 
&  
 keyset_filename 
 , 
  
 const 
  
 std 
 :: 
 string 
&  
 input_filename 
 , 
  
 const 
  
 std 
 :: 
 string 
&  
 output_filename 
 , 
  
 absl 
 :: 
 string_view 
  
 context_info 
 ) 
  
 { 
  
 Status 
  
 result 
  
 = 
  
 crypto 
 :: 
 tink 
 :: 
 HybridConfig 
 :: 
 Register 
 (); 
  
 if 
  
 ( 
 ! 
 result 
 . 
 ok 
 ()) 
  
 return 
  
 result 
 ; 
 #ifndef TINK_EXAMPLES_EXCLUDE_HPKE 
  
 // HPKE isn't supported when using OpenSSL as a backend. 
  
 result 
  
 = 
  
 crypto 
 :: 
 tink 
 :: 
 RegisterHpke 
 (); 
  
 if 
  
 ( 
 ! 
 result 
 . 
 ok 
 ()) 
  
 return 
  
 result 
 ; 
 #endif 
  
 // Read the keyset from file. 
  
 StatusOr<std 
 :: 
 unique_ptr<KeysetHandle> 
>  
 keyset_handle 
  
 = 
  
 ReadJsonCleartextKeyset 
 ( 
 keyset_filename 
 ); 
  
 if 
  
 ( 
 ! 
 keyset_handle 
 . 
 ok 
 ()) 
  
 return 
  
 keyset_handle 
 . 
 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 
 ) 
  
 { 
  
 // Get the hybrid encryption primitive. 
  
 StatusOr<std 
 :: 
 unique_ptr<HybridEncrypt> 
>  
 hybrid_encrypt_primitive 
  
 = 
  
 ( 
 * 
 keyset_handle 
 ) 
  
 - 
> GetPrimitive<crypto 
 :: 
 tink 
 :: 
 HybridEncrypt 
> ( 
  
 crypto 
 :: 
 tink 
 :: 
 ConfigGlobalRegistry 
 ()); 
  
 if 
  
 ( 
 ! 
 hybrid_encrypt_primitive 
 . 
 ok 
 ()) 
  
 { 
  
 return 
  
 hybrid_encrypt_primitive 
 . 
 status 
 (); 
  
 } 
  
 // Generate the ciphertext. 
  
 StatusOr<std 
 :: 
 string 
>  
 encrypt_result 
  
 = 
  
 ( 
 * 
 hybrid_encrypt_primitive 
 ) 
 - 
> Encrypt 
 ( 
 * 
 input_file_content 
 , 
  
 context_info 
 ); 
  
 if 
  
 ( 
 ! 
 encrypt_result 
 . 
 ok 
 ()) 
  
 return 
  
 encrypt_result 
 . 
 status 
 (); 
  
 output 
  
 = 
  
 encrypt_result 
 . 
 value 
 (); 
  
 } 
  
 else 
  
 { 
  
 // operation == kDecrypt. 
  
 // Get the hybrid decryption primitive. 
  
 StatusOr<std 
 :: 
 unique_ptr<HybridDecrypt> 
>  
 hybrid_decrypt_primitive 
  
 = 
  
 ( 
 * 
 keyset_handle 
 ) 
  
 - 
> GetPrimitive<crypto 
 :: 
 tink 
 :: 
 HybridDecrypt 
> ( 
  
 crypto 
 :: 
 tink 
 :: 
 ConfigGlobalRegistry 
 ()); 
  
 if 
  
 ( 
 ! 
 hybrid_decrypt_primitive 
 . 
 ok 
 ()) 
  
 { 
  
 return 
  
 hybrid_decrypt_primitive 
 . 
 status 
 (); 
  
 } 
  
 // Recover the plaintext. 
  
 StatusOr<std 
 :: 
 string 
>  
 decrypt_result 
  
 = 
  
 ( 
 * 
 hybrid_decrypt_primitive 
 ) 
 - 
> Decrypt 
 ( 
 * 
 input_file_content 
 , 
  
 context_info 
 ); 
  
 if 
  
 ( 
 ! 
 decrypt_result 
 . 
 ok 
 ()) 
  
 return 
  
 decrypt_result 
 . 
 status 
 (); 
  
 output 
  
 = 
  
 decrypt_result 
 . 
 value 
 (); 
  
 } 
  
 // Write the output to the output 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 
  
 context_info 
  
 = 
  
 absl 
 :: 
 GetFlag 
 ( 
 FLAGS_context_info 
 ); 
  
 std 
 :: 
 clog 
 << 
 "Using keyset from file " 
 << 
 keyset_filename 
 << 
 " to hybrid " 
 << 
 mode 
 << 
 " file " 
 << 
 input_filename 
 << 
 " with context info '" 
 << 
 context_info 
 << 
 "'." 
 << 
 '\n' 
 ; 
  
 std 
 :: 
 clog 
 << 
 "The resulting output will be written to " 
 << 
 output_filename 
 << 
 '\n' 
 ; 
  
 CHECK_OK 
 ( 
 tink_cc_examples 
 :: 
 HybridCli 
 ( 
 mode 
 , 
  
 keyset_filename 
 , 
  
 input_filename 
 , 
  
 output_filename 
 , 
  
 context_info 
 )); 
  
 return 
  
 0 
 ; 
 } 
  

Go

 import 
  
 ( 
  
 "bytes" 
  
 "fmt" 
  
 "log" 
  
 "github.com/tink-crypto/tink-go/v2/hybrid" 
  
 "github.com/tink-crypto/tink-go/v2/insecurecleartextkeyset" 
  
 "github.com/tink-crypto/tink-go/v2/keyset" 
 ) 
 func 
  
 Example 
 () 
  
 { 
  
 // A private keyset created with 
  
 // "tinkey create-keyset --key-template=DHKEM_X25519_HKDF_SHA256_HKDF_SHA256_AES_256_GCM --out private_keyset.cfg". 
  
 // Note that this keyset has the secret key information in cleartext. 
  
 privateJSONKeyset 
  
 := 
  
 `{ 
 "key": [{ 
 "keyData": { 
 "keyMaterialType": 
 "ASYMMETRIC_PRIVATE", 
 "typeUrl": 
 "type.googleapis.com/google.crypto.tink.HpkePrivateKey", 
 "value": 
 "EioSBggBEAEYAhogVWQpmQoz74jcAp5WOD36KiBQ71MVCpn2iWfOzWLtKV4aINfn8qlMbyijNJcCzrafjsgJ493ZZGN256KTfKw0WN+p" 
 }, 
 "keyId": 958452012, 
 "outputPrefixType": "TINK", 
 "status": "ENABLED" 
 }], 
 "primaryKeyId": 958452012 
 }` 
  
 // The corresponding public keyset created with 
  
 // "tinkey create-public-keyset --in private_keyset.cfg". 
  
 publicJSONKeyset 
  
 := 
  
 `{ 
 "key": [{ 
 "keyData": { 
 "keyMaterialType": 
 "ASYMMETRIC_PUBLIC", 
 "typeUrl": 
 "type.googleapis.com/google.crypto.tink.HpkePublicKey", 
 "value": 
 "EgYIARABGAIaIFVkKZkKM++I3AKeVjg9+iogUO9TFQqZ9olnzs1i7Sle" 
 }, 
 "keyId": 958452012, 
 "outputPrefixType": "TINK", 
 "status": "ENABLED" 
 }], 
 "primaryKeyId": 958452012 
 }` 
  
 // Create a keyset handle from the keyset containing the public key. Because the 
  
 // public keyset does not contain any secrets, we can use [keyset.ReadWithNoSecrets]. 
  
 publicKeysetHandle 
 , 
  
 err 
  
 := 
  
 keyset 
 . 
 ReadWithNoSecrets 
 ( 
  
 keyset 
 . 
 NewJSONReader 
 ( 
 bytes 
 . 
 NewBufferString 
 ( 
 publicJSONKeyset 
 ))) 
  
 if 
  
 err 
  
 != 
  
 nil 
  
 { 
  
 log 
 . 
 Fatal 
 ( 
 err 
 ) 
  
 } 
  
 // Retrieve the HybridEncrypt primitive from publicKeysetHandle. 
  
 encPrimitive 
 , 
  
 err 
  
 := 
  
 hybrid 
 . 
 NewHybridEncrypt 
 ( 
 publicKeysetHandle 
 ) 
  
 if 
  
 err 
  
 != 
  
 nil 
  
 { 
  
 log 
 . 
 Fatal 
 ( 
 err 
 ) 
  
 } 
  
 plaintext 
  
 := 
  
 [] 
 byte 
 ( 
 "message" 
 ) 
  
 encryptionContext 
  
 := 
  
 [] 
 byte 
 ( 
 "encryption context" 
 ) 
  
 ciphertext 
 , 
  
 err 
  
 := 
  
 encPrimitive 
 . 
 Encrypt 
 ( 
 plaintext 
 , 
  
 encryptionContext 
 ) 
  
 if 
  
 err 
  
 != 
  
 nil 
  
 { 
  
 log 
 . 
 Fatal 
 ( 
 err 
 ) 
  
 } 
  
 // Create a keyset handle from the cleartext private keyset in the previous 
  
 // step. The keyset handle provides abstract access to the underlying keyset to 
  
 // limit the access of 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. 
  
 privateKeysetHandle 
 , 
  
 err 
  
 := 
  
 insecurecleartextkeyset 
 . 
 Read 
 ( 
  
 keyset 
 . 
 NewJSONReader 
 ( 
 bytes 
 . 
 NewBufferString 
 ( 
 privateJSONKeyset 
 ))) 
  
 if 
  
 err 
  
 != 
  
 nil 
  
 { 
  
 log 
 . 
 Fatal 
 ( 
 err 
 ) 
  
 } 
  
 // Retrieve the HybridDecrypt primitive from privateKeysetHandle. 
  
 decPrimitive 
 , 
  
 err 
  
 := 
  
 hybrid 
 . 
 NewHybridDecrypt 
 ( 
 privateKeysetHandle 
 ) 
  
 if 
  
 err 
  
 != 
  
 nil 
  
 { 
  
 log 
 . 
 Fatal 
 ( 
 err 
 ) 
  
 } 
  
 decrypted 
 , 
  
 err 
  
 := 
  
 decPrimitive 
 . 
 Decrypt 
 ( 
 ciphertext 
 , 
  
 encryptionContext 
 ) 
  
 if 
  
 err 
  
 != 
  
 nil 
  
 { 
  
 log 
 . 
 Fatal 
 ( 
 err 
 ) 
  
 } 
  
 fmt 
 . 
 Println 
 ( 
 string 
 ( 
 decrypted 
 )) 
  
 // Output: message 
 } 
  

Java

 package 
  
 hybrid 
 ; 
 import static 
  
 java.nio.charset.StandardCharsets.UTF_8 
 ; 
 import 
  
 com.google.crypto.tink.HybridDecrypt 
 ; 
 import 
  
 com.google.crypto.tink.HybridEncrypt 
 ; 
 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.hybrid.HybridConfig 
 ; 
 import 
  
 java.nio.file.Files 
 ; 
 import 
  
 java.nio.file.Path 
 ; 
 import 
  
 java.nio.file.Paths 
 ; 
 /** 
 * A command-line utility for hybrid encryption. 
 * 
 * <p>It loads cleartext keys from disk - this is not recommended! 
 * 
 * <p>It requires the following arguments: 
 * 
 * <ul> 
 *   <li>mode: either 'encrypt' or 'decrypt'. 
 *   <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] contex-info: Bind the encryption to this context info. 
 */ 
 public 
  
 final 
  
 class 
 HybridExample 
  
 { 
  
 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 HybridExample encrypt/decrypt key-file input-file output-file context-info" 
 ); 
  
 System 
 . 
 exit 
 ( 
 1 
 ); 
  
 } 
  
 String 
  
 mode 
  
 = 
  
 args 
 [ 
 0 
 ] 
 ; 
  
 if 
  
 ( 
 ! 
 mode 
 . 
 equals 
 ( 
 "encrypt" 
 ) 
 && 
 ! 
 mode 
 . 
 equals 
 ( 
 "decrypt" 
 )) 
  
 { 
  
 System 
 . 
 err 
 . 
 println 
 ( 
 "Incorrect mode. Please select encrypt or decrypt." 
 ); 
  
 System 
 . 
 exit 
 ( 
 1 
 ); 
  
 } 
  
 Path 
  
 keyFile 
  
 = 
  
 Paths 
 . 
 get 
 ( 
 args 
 [ 
 1 
 ] 
 ); 
  
 Path 
  
 inputFile 
  
 = 
  
 Paths 
 . 
 get 
 ( 
 args 
 [ 
 2 
 ] 
 ); 
  
 byte 
 [] 
  
 input 
  
 = 
  
 Files 
 . 
 readAllBytes 
 ( 
 inputFile 
 ); 
  
 Path 
  
 outputFile 
  
 = 
  
 Paths 
 . 
 get 
 ( 
 args 
 [ 
 3 
 ] 
 ); 
  
 byte 
 [] 
  
 contextInfo 
  
 = 
  
 new 
  
 byte 
 [ 
 0 
 ] 
 ; 
  
 if 
  
 ( 
 args 
 . 
 length 
  
 == 
  
 5 
 ) 
  
 { 
  
 contextInfo 
  
 = 
  
 args 
 [ 
 4 
 ] 
 . 
 getBytes 
 ( 
 UTF_8 
 ); 
  
 } 
  
 // Register all hybrid encryption key types with the Tink runtime. 
  
 HybridConfig 
 . 
 register 
 (); 
  
 // Read the keyset into a KeysetHandle. 
  
 KeysetHandle 
  
 handle 
  
 = 
  
 TinkJsonProtoKeysetFormat 
 . 
 parseKeyset 
 ( 
  
 new 
  
 String 
 ( 
 Files 
 . 
 readAllBytes 
 ( 
 keyFile 
 ), 
  
 UTF_8 
 ), 
  
 InsecureSecretKeyAccess 
 . 
 get 
 ()); 
  
 if 
  
 ( 
 mode 
 . 
 equals 
 ( 
 "encrypt" 
 )) 
  
 { 
  
 // Get the primitive. 
  
 HybridEncrypt 
  
 encryptor 
  
 = 
  
 handle 
 . 
 getPrimitive 
 ( 
 RegistryConfiguration 
 . 
 get 
 (), 
  
 HybridEncrypt 
 . 
 class 
 ); 
  
 // Use the primitive to encrypt data. 
  
 byte 
 [] 
  
 ciphertext 
  
 = 
  
 encryptor 
 . 
 encrypt 
 ( 
 input 
 , 
  
 contextInfo 
 ); 
  
 Files 
 . 
 write 
 ( 
 outputFile 
 , 
  
 ciphertext 
 ); 
  
 } 
  
 else 
  
 { 
  
 HybridDecrypt 
  
 decryptor 
  
 = 
  
 handle 
 . 
 getPrimitive 
 ( 
 RegistryConfiguration 
 . 
 get 
 (), 
  
 HybridDecrypt 
 . 
 class 
 ); 
  
 // Use the primitive to decrypt data. 
  
 byte 
 [] 
  
 plaintext 
  
 = 
  
 decryptor 
 . 
 decrypt 
 ( 
 input 
 , 
  
 contextInfo 
 ); 
  
 Files 
 . 
 write 
 ( 
 outputFile 
 , 
  
 plaintext 
 ); 
  
 } 
  
 } 
  
 private 
  
 HybridExample 
 () 
  
 {} 
 } 
  

Obj-C

HOW-TO

Python

 import 
  
 tink 
 from 
  
 tink 
  
 import 
 hybrid 
 from 
  
 tink 
  
 import 
 secret_key_access 
 def 
  
 example 
 (): 
  
 """Encrypt and decrypt using hybrid encryption.""" 
 # Register the hybrid encryption key managers. This is needed to create 
 # HybridEncrypt and HybridDecrypt primitives later. 
 hybrid 
 . 
 register 
 () 
 # A private keyset created with 
 # tinkey create-keyset \ 
 #   --key-template=DHKEM_X25519_HKDF_SHA256_HKDF_SHA256_AES_256_GCM \ 
 #   --out private_keyset.cfg 
 # Note that this keyset has the secret key information in cleartext. 
 private_keyset 
 = 
 r 
 """{ 
 "key": [{ 
 "keyData": { 
 "keyMaterialType": 
 "ASYMMETRIC_PRIVATE", 
 "typeUrl": 
 "type.googleapis.com/google.crypto.tink.HpkePrivateKey", 
 "value": 
 "EioSBggBEAEYAhogVWQpmQoz74jcAp5WOD36KiBQ71MVCpn2iWfOzWLtKV4aINfn8qlMbyijNJcCzrafjsgJ493ZZGN256KTfKw0WN+p" 
 }, 
 "keyId": 958452012, 
 "outputPrefixType": "TINK", 
 "status": "ENABLED" 
 }], 
 "primaryKeyId": 958452012 
 }""" 
 # The corresponding public keyset created with 
 # "tinkey create-public-keyset --in private_keyset.cfg" 
 public_keyset 
 = 
 r 
 """{ 
 "key": [{ 
 "keyData": { 
 "keyMaterialType": 
 "ASYMMETRIC_PUBLIC", 
 "typeUrl": 
 "type.googleapis.com/google.crypto.tink.HpkePublicKey", 
 "value": 
 "EgYIARABGAIaIFVkKZkKM++I3AKeVjg9+iogUO9TFQqZ9olnzs1i7Sle"          }, 
 "keyId": 958452012, 
 "outputPrefixType": "TINK", 
 "status": "ENABLED" 
 }], 
 "primaryKeyId": 958452012 
 }""" 
 # Create a keyset handle from the keyset containing the public key. Because 
 # this keyset does not contain any secrets, we can use 
 # `parse_without_secret`. 
 public_keyset_handle 
 = 
 tink 
 . 
 json_proto_keyset_format 
 . 
 parse_without_secret 
 ( 
 public_keyset 
 ) 
 # Retrieve the HybridEncrypt primitive from the keyset handle. 
 enc_primitive 
 = 
 public_keyset_handle 
 . 
 primitive 
 ( 
 hybrid 
 . 
 HybridEncrypt 
 ) 
 # Use enc_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 
 = 
 enc_primitive 
 . 
 encrypt 
 ( 
 b 
 'message' 
 , 
 b 
 'context_info' 
 ) 
 # Create a keyset handle from the private keyset. 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 tink.json_proto_keyset_format.parse, as it implies that your key 
 # material is passed in cleartext which is a security risk. 
 private_keyset_handle 
 = 
 tink 
 . 
 json_proto_keyset_format 
 . 
 parse 
 ( 
 private_keyset 
 , 
 secret_key_access 
 . 
 TOKEN 
 ) 
 # Retrieve the HybridDecrypt primitive from the private keyset handle. 
 dec_primitive 
 = 
 private_keyset_handle 
 . 
 primitive 
 ( 
 hybrid 
 . 
 HybridDecrypt 
 ) 
 # Use dec_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. 
 decrypted 
 = 
 dec_primitive 
 . 
 decrypt 
 ( 
 ciphertext 
 , 
 b 
 'context_info' 
 ) 
  

Hybrid Encryption

The Hybrid Encryption primitive combines the efficiency of symmetric encryption with the convenience of public key (asymmetric) cryptography. Anyone can encrypt data using the public key, but only users with the private key can decrypt the data.

For Hybrid Encryption, the sender generates a fresh symmetric key to encrypt the plaintext of each message to produce a ciphertext. That symmetric key is encapsulated with the recipient's public key. For Hybrid Decryption, the symmetric key is decapsulated by the recipient and then used to decrypt the ciphertext to recover the original plaintext. See Tink Hybrid Encryption wire format for details on how to store or transmit the ciphertext along with the key encapsulation.

Hybrid Encryption has the following properties:

  • Secrecy: No one is able to get any information about the encrypted plaintext (except the length), unless they have access to the private key.
  • Asymmetry: Encrypting the ciphertext can be done with the public key, but for decryption, the private key is required.
  • Randomization: The encryption is randomized. Two messages with the same plaintext will not yield the same ciphertext. This prevents attackers from knowing which ciphertext corresponds to a given plaintext.

Hybrid Encryption is represented in Tink as a pair of primitives:

  • HybridEncrypt for encryption
  • HybridDecrypt for decryption

Context info parameter

In addition to the plaintext, Hybrid Encryption accepts an extra parameter, context_info , which is usually public data implicit from the context, but should be bound to the resulting ciphertext. This means that the ciphertext allows you to confirm the integrity of the context info but there are no guarantees for its secrecy or authenticity. The actual context info can be empty or null, but to ensure the correct decryption of the resulting ciphertext, the same context info value must be provided for decryption.

A concrete implementation of Hybrid Encryption can bind context info to the ciphertext in various ways, for example:

  • Use context_info as associated data input for AEAD symmetric encryption (cf. RFC 5116 ).
  • Use context_info as “CtxInfo" input for HKDF (if the implementation uses HKDF as key derivation function, cf. RFC 5869 ).

Choose a key type

We recommend using the DHKEM_X25519_HKDF_SHA256_HKDF_SHA256_AES_256_GCM key type for most use cases. This key type implements the Hybrid Public Key Encryption (HPKE) standard as specified in RFC 9180 . HPKE consists of a key encapsulation mechanism (KEM), a key derivation function (KDF), and an authenticated encryption with associated data (AEAD) algorithm.

DHKEM_X25519_HKDF_SHA256_HKDF_SHA256_AES_256_GCM specifically employs:

  • KEM: Diffie–Hellman over Curve25519 with HKDF-SHA-256 to derive the shared secret.
  • KDF: HKDF-SHA-256 to derive the sender and receiver context.
  • AEAD: AES-256-GCM with 12-byte nonces generated according to the HPKE standard.

Other supported HPKE key types include, but are not limited to, the following:

  • DHKEM_X25519_HKDF_SHA256_HKDF_SHA256_AES_128_GCM
  • DHKEM_X25519_HKDF_SHA256_HKDF_SHA256_CHACHA20_POLY1305
  • DHKEM_P256_HKDF_SHA256_HKDF_SHA256_AES_128_GCM
  • DHKEM_P521_HKDF_SHA512_HKDF_SHA512_AES_256_GCM

See RFC 9180 for more details on the algorithm choices for the KEM, KDF, and AEAD.

Although no longer recommended, Tink also supports some variations of ECIES as described in Victor Shoup's ISO 18033-2 standard . Some supported ECIES key types are listed below:

  • ECIES_P256_HKDF_HMAC_SHA256_AES128_GCM
  • ECIES_P256_COMPRESSED_HKDF_HMAC_SHA256_AES128_GCM
  • ECIES_P256_HKDF_HMAC_SHA256_AES128_CTR_HMAC_SHA256
  • ECIES_P256_COMPRESSED_HKDF_HMAC_SHA256_AES128_CTR_HMAC_SHA256

Minimal properties

  • Plaintext and context info can have arbitrary length (within the range 0..2 32 bytes)
  • Secure against adaptive chosen ciphertext attacks
  • 128-bit security for elliptic curve based schemes
Create a Mobile Website
View Site in Mobile | Classic
Share by: