I want to encrypt large files or data streams

  • Streaming AEAD is recommended for encrypting large files or live data streams, especially when data exceeds memory capacity.

  • It utilizes a single key for both encryption and decryption, similar to standard AEAD.

  • The AES128_GCM_HKDF_1MB key type is the preferred choice for most file encryption scenarios.

  • Code samples in Go, Java, and Python demonstrate how to implement Streaming AEAD for file encryption.

We recommend the Streaming AEAD primitive with AES128_GCM_HKDF_1MB key type for most file encryption use cases.

The Streaming Authenticated Encryption with Associated Data (Streaming AEAD) primitive is useful for encrypting live data streams or large files that don't fit in memory. Similar to AEAD, it is symmetric, using a single key for both encryption and decryption.

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

Go

 import 
  
 ( 
  
 "bytes" 
  
 "fmt" 
  
 "io" 
  
 "log" 
  
 "os" 
  
 "path/filepath" 
  
 "github.com/tink-crypto/tink-go/v2/insecurecleartextkeyset" 
  
 "github.com/tink-crypto/tink-go/v2/keyset" 
  
 "github.com/tink-crypto/tink-go/v2/streamingaead" 
 ) 
 func 
  
 Example 
 () 
  
 { 
  
 // A keyset created with "tinkey create-keyset --key-template=AES256_CTR_HMAC_SHA256_1MB". Note 
  
 // that this keyset has the secret key information in cleartext. 
  
 jsonKeyset 
  
 := 
  
 `{ 
 "primaryKeyId": 1720777699, 
 "key": [{ 
 "keyData": { 
 "typeUrl": "type.googleapis.com/google.crypto.tink.AesCtrHmacStreamingKey", 
 "keyMaterialType": "SYMMETRIC", 
 "value": "Eg0IgCAQIBgDIgQIAxAgGiDtesd/4gCnQdTrh+AXodwpm2b6BFJkp043n+8mqx0YGw==" 
 }, 
 "outputPrefixType": "RAW", 
 "keyId": 1720777699, 
 "status": "ENABLED" 
 }] 
 }` 
  
 // 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 an 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 StreamingAEAD primitive we want to use from the keyset handle. 
  
 primitive 
 , 
  
 err 
  
 := 
  
 streamingaead 
 . 
 New 
 ( 
 keysetHandle 
 ) 
  
 if 
  
 err 
  
 != 
  
 nil 
  
 { 
  
 log 
 . 
 Fatal 
 ( 
 err 
 ) 
  
 } 
  
 // Create a file with the plaintext. 
  
 dir 
 , 
  
 err 
  
 := 
  
 os 
 . 
 MkdirTemp 
 ( 
 "" 
 , 
  
 "streamingaead" 
 ) 
  
 if 
  
 err 
  
 != 
  
 nil 
  
 { 
  
 log 
 . 
 Fatal 
 ( 
 err 
 ) 
  
 } 
  
 defer 
  
 os 
 . 
 RemoveAll 
 ( 
 dir 
 ) 
  
 plaintextPath 
  
 := 
  
 filepath 
 . 
 Join 
 ( 
 dir 
 , 
  
 "plaintext" 
 ) 
  
 if 
  
 err 
  
 := 
  
 os 
 . 
 WriteFile 
 ( 
 plaintextPath 
 , 
  
 [] 
 byte 
 ( 
 "this data needs to be encrypted" 
 ), 
  
 0666 
 ); 
  
 err 
  
 != 
  
 nil 
  
 { 
  
 log 
 . 
 Fatal 
 ( 
 err 
 ) 
  
 } 
  
 plaintextFile 
 , 
  
 err 
  
 := 
  
 os 
 . 
 Open 
 ( 
 plaintextPath 
 ) 
  
 if 
  
 err 
  
 != 
  
 nil 
  
 { 
  
 log 
 . 
 Fatal 
 ( 
 err 
 ) 
  
 } 
  
 // associatedData defines the context of the encryption. Here, we include the path of the 
  
 // plaintext file. 
  
 associatedData 
  
 := 
  
 [] 
 byte 
 ( 
 "associatedData for " 
  
 + 
  
 plaintextPath 
 ) 
  
 // Encrypt the plaintext file and write the output to the ciphertext file. In this case the 
  
 // primary key of the keyset will be used (which is also the only key in this example). 
  
 ciphertextPath 
  
 := 
  
 filepath 
 . 
 Join 
 ( 
 dir 
 , 
  
 "ciphertext" 
 ) 
  
 ciphertextFile 
 , 
  
 err 
  
 := 
  
 os 
 . 
 Create 
 ( 
 ciphertextPath 
 ) 
  
 if 
  
 err 
  
 != 
  
 nil 
  
 { 
  
 log 
 . 
 Fatal 
 ( 
 err 
 ) 
  
 } 
  
 w 
 , 
  
 err 
  
 := 
  
 primitive 
 . 
 NewEncryptingWriter 
 ( 
 ciphertextFile 
 , 
  
 associatedData 
 ) 
  
 if 
  
 err 
  
 != 
  
 nil 
  
 { 
  
 log 
 . 
 Fatal 
 ( 
 err 
 ) 
  
 } 
  
 if 
  
 _ 
 , 
  
 err 
  
 := 
  
 io 
 . 
 Copy 
 ( 
 w 
 , 
  
 plaintextFile 
 ); 
  
 err 
  
 != 
  
 nil 
  
 { 
  
 log 
 . 
 Fatal 
 ( 
 err 
 ) 
  
 } 
  
 if 
  
 err 
  
 := 
  
 w 
 . 
 Close 
 (); 
  
 err 
  
 != 
  
 nil 
  
 { 
  
 log 
 . 
 Fatal 
 ( 
 err 
 ) 
  
 } 
  
 if 
  
 err 
  
 := 
  
 ciphertextFile 
 . 
 Close 
 (); 
  
 err 
  
 != 
  
 nil 
  
 { 
  
 log 
 . 
 Fatal 
 ( 
 err 
 ) 
  
 } 
  
 if 
  
 err 
  
 := 
  
 plaintextFile 
 . 
 Close 
 (); 
  
 err 
  
 != 
  
 nil 
  
 { 
  
 log 
 . 
 Fatal 
 ( 
 err 
 ) 
  
 } 
  
 // Decrypt the ciphertext file and write the output to the decrypted file. The 
  
 // decryption finds the correct key in the keyset and decrypts the ciphertext. 
  
 // If no key is found or decryption fails, it returns an error. 
  
 ciphertextFile 
 , 
  
 err 
  
 = 
  
 os 
 . 
 Open 
 ( 
 ciphertextPath 
 ) 
  
 if 
  
 err 
  
 != 
  
 nil 
  
 { 
  
 log 
 . 
 Fatal 
 ( 
 err 
 ) 
  
 } 
  
 decryptedPath 
  
 := 
  
 filepath 
 . 
 Join 
 ( 
 dir 
 , 
  
 "decrypted" 
 ) 
  
 decryptedFile 
 , 
  
 err 
  
 := 
  
 os 
 . 
 Create 
 ( 
 decryptedPath 
 ) 
  
 if 
  
 err 
  
 != 
  
 nil 
  
 { 
  
 log 
 . 
 Fatal 
 ( 
 err 
 ) 
  
 } 
  
 r 
 , 
  
 err 
  
 := 
  
 primitive 
 . 
 NewDecryptingReader 
 ( 
 ciphertextFile 
 , 
  
 associatedData 
 ) 
  
 if 
  
 err 
  
 != 
  
 nil 
  
 { 
  
 log 
 . 
 Fatal 
 ( 
 err 
 ) 
  
 } 
  
 if 
  
 _ 
 , 
  
 err 
  
 := 
  
 io 
 . 
 Copy 
 ( 
 decryptedFile 
 , 
  
 r 
 ); 
  
 err 
  
 != 
  
 nil 
  
 { 
  
 log 
 . 
 Fatal 
 ( 
 err 
 ) 
  
 } 
  
 if 
  
 err 
  
 := 
  
 decryptedFile 
 . 
 Close 
 (); 
  
 err 
  
 != 
  
 nil 
  
 { 
  
 log 
 . 
 Fatal 
 ( 
 err 
 ) 
  
 } 
  
 if 
  
 err 
  
 := 
  
 ciphertextFile 
 . 
 Close 
 (); 
  
 err 
  
 != 
  
 nil 
  
 { 
  
 log 
 . 
 Fatal 
 ( 
 err 
 ) 
  
 } 
  
 // Print the content of the decrypted file. 
  
 b 
 , 
  
 err 
  
 := 
  
 os 
 . 
 ReadFile 
 ( 
 decryptedPath 
 ) 
  
 if 
  
 err 
  
 != 
  
 nil 
  
 { 
  
 log 
 . 
 Fatal 
 ( 
 err 
 ) 
  
 } 
  
 fmt 
 . 
 Println 
 ( 
 string 
 ( 
 b 
 )) 
  
 // Output: this data needs to be encrypted 
 } 
  

Java

 package 
  
 streamingaead 
 ; 
 import static 
  
 java.nio.charset.StandardCharsets.UTF_8 
 ; 
 import 
  
 com.google.crypto.tink.InsecureSecretKeyAccess 
 ; 
 import 
  
 com.google.crypto.tink.KeysetHandle 
 ; 
 import 
  
 com.google.crypto.tink.RegistryConfiguration 
 ; 
 import 
  
 com.google.crypto.tink.StreamingAead 
 ; 
 import 
  
 com.google.crypto.tink.TinkJsonProtoKeysetFormat 
 ; 
 import 
  
 com.google.crypto.tink.streamingaead.StreamingAeadConfig 
 ; 
 import 
  
 java.io.IOException 
 ; 
 import 
  
 java.nio.ByteBuffer 
 ; 
 import 
  
 java.nio.channels.FileChannel 
 ; 
 import 
  
 java.nio.channels.ReadableByteChannel 
 ; 
 import 
  
 java.nio.channels.WritableByteChannel 
 ; 
 import 
  
 java.nio.file.Files 
 ; 
 import 
  
 java.nio.file.Path 
 ; 
 import 
  
 java.nio.file.Paths 
 ; 
 import 
  
 java.nio.file.StandardOpenOption 
 ; 
 import 
  
 java.security.GeneralSecurityException 
 ; 
 /** 
 * A command-line utility for encrypting files with Streaming 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 
 StreamingAeadExample 
  
 { 
  
 private 
  
 static 
  
 final 
  
 String 
  
 MODE_ENCRYPT 
  
 = 
  
 "encrypt" 
 ; 
  
 private 
  
 static 
  
 final 
  
 String 
  
 MODE_DECRYPT 
  
 = 
  
 "decrypt" 
 ; 
  
 private 
  
 static 
  
 final 
  
 int 
  
 BLOCK_SIZE_IN_BYTES 
  
 = 
  
 8 
  
 * 
  
 1024 
 ; 
  
 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 StreamingAeadExample 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 
 ); 
  
 } 
  
 // Initialize Tink: register all Streaming AEAD key types with the Tink runtime 
  
 StreamingAeadConfig 
 . 
 register 
 (); 
  
 // Read the keyset into a KeysetHandle 
  
 KeysetHandle 
  
 handle 
  
 = 
  
 TinkJsonProtoKeysetFormat 
 . 
 parseKeyset 
 ( 
  
 new 
  
 String 
 ( 
 Files 
 . 
 readAllBytes 
 ( 
 keyFile 
 ), 
  
 UTF_8 
 ), 
  
 InsecureSecretKeyAccess 
 . 
 get 
 ()); 
  
 // Get the primitive 
  
 StreamingAead 
  
 streamingAead 
  
 = 
  
 handle 
 . 
 getPrimitive 
 ( 
 RegistryConfiguration 
 . 
 get 
 (), 
  
 StreamingAead 
 . 
 class 
 ); 
  
 // Use the primitive to encrypt/decrypt files 
  
 if 
  
 ( 
 mode 
 . 
 equals 
 ( 
 MODE_ENCRYPT 
 )) 
  
 { 
  
 encryptFile 
 ( 
 streamingAead 
 , 
  
 inputFile 
 , 
  
 outputFile 
 , 
  
 associatedData 
 ); 
  
 } 
  
 else 
  
 if 
  
 ( 
 mode 
 . 
 equals 
 ( 
 MODE_DECRYPT 
 )) 
  
 { 
  
 decryptFile 
 ( 
 streamingAead 
 , 
  
 inputFile 
 , 
  
 outputFile 
 , 
  
 associatedData 
 ); 
  
 } 
  
 else 
  
 { 
  
 System 
 . 
 err 
 . 
 println 
 ( 
  
 "The first argument must be either " 
  
 + 
  
 MODE_ENCRYPT 
  
 + 
  
 " or " 
  
 + 
  
 MODE_DECRYPT 
  
 + 
  
 ", got: " 
  
 + 
  
 mode 
 ); 
  
 System 
 . 
 exit 
 ( 
 1 
 ); 
  
 } 
  
 } 
  
 private 
  
 static 
  
 void 
  
 encryptFile 
 ( 
  
 StreamingAead 
  
 streamingAead 
 , 
  
 Path 
  
 inputFile 
 , 
  
 Path 
  
 outputFile 
 , 
  
 byte 
 [] 
  
 associatedData 
 ) 
  
 throws 
  
 GeneralSecurityException 
 , 
  
 IOException 
  
 { 
  
 try 
  
 ( 
 WritableByteChannel 
  
 encryptingChannel 
  
 = 
  
 streamingAead 
 . 
 newEncryptingChannel 
 ( 
  
 FileChannel 
 . 
 open 
 ( 
 outputFile 
 , 
  
 StandardOpenOption 
 . 
 WRITE 
 , 
  
 StandardOpenOption 
 . 
 CREATE 
 ), 
  
 associatedData 
 ); 
  
 FileChannel 
  
 inputChannel 
  
 = 
  
 FileChannel 
 . 
 open 
 ( 
 inputFile 
 , 
  
 StandardOpenOption 
 . 
 READ 
 )) 
  
 { 
  
 ByteBuffer 
  
 byteBuffer 
  
 = 
  
 ByteBuffer 
 . 
 allocate 
 ( 
 BLOCK_SIZE_IN_BYTES 
 ); 
  
 while 
  
 ( 
 true 
 ) 
  
 { 
  
 int 
  
 read 
  
 = 
  
 inputChannel 
 . 
 read 
 ( 
 byteBuffer 
 ); 
  
 if 
  
 ( 
 read 
  
< = 
  
 0 
 ) 
  
 { 
  
 return 
 ; 
  
 } 
  
 byteBuffer 
 . 
 flip 
 (); 
  
 while 
  
 ( 
 byteBuffer 
 . 
 hasRemaining 
 ()) 
  
 { 
  
 encryptingChannel 
 . 
 write 
 ( 
 byteBuffer 
 ); 
  
 } 
  
 byteBuffer 
 . 
 clear 
 (); 
  
 } 
  
 } 
  
 } 
  
 private 
  
 static 
  
 void 
  
 decryptFile 
 ( 
  
 StreamingAead 
  
 streamingAead 
 , 
  
 Path 
  
 inputFile 
 , 
  
 Path 
  
 outputFile 
 , 
  
 byte 
 [] 
  
 associatedData 
 ) 
  
 throws 
  
 GeneralSecurityException 
 , 
  
 IOException 
  
 { 
  
 try 
  
 ( 
 ReadableByteChannel 
  
 decryptingChannel 
  
 = 
  
 streamingAead 
 . 
 newDecryptingChannel 
 ( 
  
 FileChannel 
 . 
 open 
 ( 
 inputFile 
 , 
  
 StandardOpenOption 
 . 
 READ 
 ), 
  
 associatedData 
 ); 
  
 FileChannel 
  
 outputChannel 
  
 = 
  
 FileChannel 
 . 
 open 
 ( 
 outputFile 
 , 
  
 StandardOpenOption 
 . 
 WRITE 
 , 
  
 StandardOpenOption 
 . 
 CREATE 
 )) 
  
 { 
  
 ByteBuffer 
  
 byteBuffer 
  
 = 
  
 ByteBuffer 
 . 
 allocate 
 ( 
 BLOCK_SIZE_IN_BYTES 
 ); 
  
 while 
  
 ( 
 true 
 ) 
  
 { 
  
 int 
  
 read 
  
 = 
  
 decryptingChannel 
 . 
 read 
 ( 
 byteBuffer 
 ); 
  
 if 
  
 ( 
 read 
  
< = 
  
 0 
 ) 
  
 { 
  
 return 
 ; 
  
 } 
  
 byteBuffer 
 . 
 flip 
 (); 
  
 while 
  
 ( 
 byteBuffer 
 . 
 hasRemaining 
 ()) 
  
 { 
  
 outputChannel 
 . 
 write 
 ( 
 byteBuffer 
 ); 
  
 } 
  
 byteBuffer 
 . 
 clear 
 (); 
  
 } 
  
 } 
  
 } 
  
 private 
  
 StreamingAeadExample 
 () 
  
 {} 
 } 
  

Python

 """A command-line utility for using streaming AEAD for a file. 
 It loads cleartext keys from disk - this is not recommended! 
 It requires 4 arguments (and one optional one): 
 mode: either 'encrypt' or 'decrypt' 
 keyset_path: name of the file with the keyset to be used for encryption or 
 decryption 
 input_path: name of the file with the input data to be encrypted or decrypted 
 output_path: name of the file to write the ciphertext respectively plaintext 
 to 
 [optional] associated_data: the associated data used for encryption/decryption 
 provided as a string. 
 """ 
 from 
  
 typing 
  
 import 
 BinaryIO 
 from 
  
 absl 
  
 import 
 app 
 from 
  
 absl 
  
 import 
 flags 
 from 
  
 absl 
  
 import 
 logging 
 import 
  
 tink 
 from 
  
 tink 
  
 import 
 secret_key_access 
 from 
  
 tink 
  
 import 
 streaming_aead 
 FLAGS 
 = 
 flags 
 . 
 FLAGS 
 BLOCK_SIZE 
 = 
 1024 
 * 
 1024 
 # The CLI tool will read/write at most 1 MB at once. 
 flags 
 . 
 DEFINE_enum 
 ( 
 'mode' 
 , 
 None 
 , 
 [ 
 'encrypt' 
 , 
 'decrypt' 
 ], 
 'Selects if the file should be encrypted or decrypted.' 
 ) 
 flags 
 . 
 DEFINE_string 
 ( 
 'keyset_path' 
 , 
 None 
 , 
 'Path to the keyset used for encryption or decryption.' 
 ) 
 flags 
 . 
 DEFINE_string 
 ( 
 'input_path' 
 , 
 None 
 , 
 'Path to the input file.' 
 ) 
 flags 
 . 
 DEFINE_string 
 ( 
 'output_path' 
 , 
 None 
 , 
 'Path to the output file.' 
 ) 
 flags 
 . 
 DEFINE_string 
 ( 
 'associated_data' 
 , 
 None 
 , 
 'Associated data used for the encryption or decryption.' 
 ) 
 def 
  
 read_as_blocks 
 ( 
 file 
 : 
 BinaryIO 
 ): 
  
 """Generator function to read from a file BLOCK_SIZE bytes. 
 Args: 
 file: The file object to read from. 
 Yields: 
 Returns up to BLOCK_SIZE bytes from the file. 
 """ 
 while 
 True 
 : 
 data 
 = 
 file 
 . 
 read 
 ( 
 BLOCK_SIZE 
 ) 
 # If file was opened in rawIO, EOF is only reached when b'' is returned. 
 # pylint: disable=g-explicit-bool-comparison 
 if 
 data 
 == 
 b 
 '' 
 : 
 break 
 # pylint: enable=g-explicit-bool-comparison 
 yield 
 data 
 def 
  
 encrypt_file 
 ( 
 input_file 
 : 
 BinaryIO 
 , 
 output_file 
 : 
 BinaryIO 
 , 
 associated_data 
 : 
 bytes 
 , 
 primitive 
 : 
 streaming_aead 
 . 
 StreamingAead 
 ): 
  
 """Encrypts a file with the given streaming AEAD primitive. 
 Args: 
 input_file: File to read from. 
 output_file: File to write to. 
 associated_data: Associated data provided for the AEAD. 
 primitive: The streaming AEAD primitive used for encryption. 
 """ 
 with 
 primitive 
 . 
 new_encrypting_stream 
 ( 
 output_file 
 , 
 associated_data 
 ) 
 as 
 enc_stream 
 : 
 for 
 data_block 
 in 
 read_as_blocks 
 ( 
 input_file 
 ): 
 enc_stream 
 . 
 write 
 ( 
 data_block 
 ) 
 def 
  
 decrypt_file 
 ( 
 input_file 
 : 
 BinaryIO 
 , 
 output_file 
 : 
 BinaryIO 
 , 
 associated_data 
 : 
 bytes 
 , 
 primitive 
 : 
 streaming_aead 
 . 
 StreamingAead 
 ): 
  
 """Decrypts a file with the given streaming AEAD primitive. 
 This function will cause the program to exit with 1 if the decryption fails. 
 Args: 
 input_file: File to read from. 
 output_file: File to write to. 
 associated_data: Associated data provided for the AEAD. 
 primitive: The streaming AEAD primitive used for decryption. 
 """ 
 try 
 : 
 with 
 primitive 
 . 
 new_decrypting_stream 
 ( 
 input_file 
 , 
 associated_data 
 ) 
 as 
 dec_stream 
 : 
 for 
 data_block 
 in 
 read_as_blocks 
 ( 
 dec_stream 
 ): 
 output_file 
 . 
 write 
 ( 
 data_block 
 ) 
 except 
 tink 
 . 
 TinkError 
 as 
 e 
 : 
 logging 
 . 
 exception 
 ( 
 'Error decrypting ciphertext: 
 %s 
 ' 
 , 
 e 
 ) 
 exit 
 ( 
 1 
 ) 
 def 
  
 main 
 ( 
 argv 
 ): 
 del 
 argv 
 associated_data 
 = 
 b 
 '' 
 if 
 not 
 FLAGS 
 . 
 associated_data 
 else 
 bytes 
 ( 
 FLAGS 
 . 
 associated_data 
 , 
 'utf-8' 
 ) 
 # Initialise Tink. 
 try 
 : 
 streaming_aead 
 . 
 register 
 () 
 except 
 tink 
 . 
 TinkError 
 as 
 e 
 : 
 logging 
 . 
 exception 
 ( 
 'Error initialising Tink: 
 %s 
 ' 
 , 
 e 
 ) 
 return 
 1 
 # Read the keyset into a keyset_handle. 
 with 
 open 
 ( 
 FLAGS 
 . 
 keyset_path 
 , 
 'rt' 
 ) 
 as 
 keyset_file 
 : 
 try 
 : 
 text 
 = 
 keyset_file 
 . 
 read 
 () 
 keyset_handle 
 = 
 tink 
 . 
 json_proto_keyset_format 
 . 
 parse 
 ( 
 text 
 , 
 secret_key_access 
 . 
 TOKEN 
 ) 
 except 
 tink 
 . 
 TinkError 
 as 
 e 
 : 
 logging 
 . 
 exception 
 ( 
 'Error reading key: 
 %s 
 ' 
 , 
 e 
 ) 
 return 
 1 
 # Get the primitive. 
 try 
 : 
 streaming_aead_primitive 
 = 
 keyset_handle 
 . 
 primitive 
 ( 
 streaming_aead 
 . 
 StreamingAead 
 ) 
 except 
 tink 
 . 
 TinkError 
 as 
 e 
 : 
 logging 
 . 
 exception 
 ( 
 'Error creating streaming AEAD primitive from keyset: 
 %s 
 ' 
 , 
 e 
 ) 
 return 
 1 
 # Encrypt or decrypt the file. 
 with 
 open 
 ( 
 FLAGS 
 . 
 input_path 
 , 
 'rb' 
 ) 
 as 
 input_file 
 : 
 with 
 open 
 ( 
 FLAGS 
 . 
 output_path 
 , 
 'wb' 
 ) 
 as 
 output_file 
 : 
 if 
 FLAGS 
 . 
 mode 
 == 
 'encrypt' 
 : 
 encrypt_file 
 ( 
 input_file 
 , 
 output_file 
 , 
 associated_data 
 , 
 streaming_aead_primitive 
 ) 
 elif 
 FLAGS 
 . 
 mode 
 == 
 'decrypt' 
 : 
 decrypt_file 
 ( 
 input_file 
 , 
 output_file 
 , 
 associated_data 
 , 
 streaming_aead_primitive 
 ) 
 if 
 __name__ 
 == 
 '__main__' 
 : 
 flags 
 . 
 mark_flag_as_required 
 ( 
 'mode' 
 ) 
 flags 
 . 
 mark_flag_as_required 
 ( 
 'keyset_path' 
 ) 
 flags 
 . 
 mark_flag_as_required 
 ( 
 'input_path' 
 ) 
 flags 
 . 
 mark_flag_as_required 
 ( 
 'output_path' 
 ) 
 app 
 . 
 run 
 ( 
 main 
 ) 
  

Streaming AEAD

The Streaming AEAD primitive provides authenticated encryption for streaming data. It is useful when the data to be encrypted is too large to be processed in a single step. Typical use cases include encryption of large files or live data streams.

Encryption is done in segments, which are bound to their location within a ciphertext and cannot be removed or reordered. Segments from one ciphertext cannot be inserted into another ciphertext. To modify an existing ciphertext, the entire data stream must be re-encrypted. 1

Decryption is fast because only a portion of the ciphertext is decrypted and authenticated at a time. Partial plaintexts are obtainable without processing the entire ciphertext.

Streaming AEAD implementations fulfill the AEAD definition and are nOAE-secure . They have the following properties:

  • Secrecy: Nothing about the plaintext is known, except its length.
  • 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.
  • Randomization: Encryption is randomized. Two messages with the same plaintext yield different ciphertexts. Attackers cannot know which ciphertext corresponds to a given plaintext.

Associated data

The Streaming AEAD primitive can be used to tie ciphertext to specific associated data . Suppose 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 AES128_GCM_HKDF_1MBfor most uses. Generally:

  • AES-GCM-HKDF
    • AES128_GCM_HKDF_1MB (or AES256_GCM_HKDF_1MB) is the faster option. It can encrypt 2 64 files with up to 2 64 bytes each. ~1 MB of memory is consumed during the encryption and decryption process.
    • AES128_GCM_HKDF_4KB consumes ~4 KB of memory and is a good choice if your system doesn't have a lot of memory.
  • AES-CTR HMAC
    • AES128_CTR_HMAC_SHA256_1MB (or AES256_CTR_HMAC_SHA256_1MB) is a more conservative option.

Security guarantees

Streaming AEAD implementations offer:

  • CCA2 security.
  • At least 80-bit authentication strength.
  • The ability to encrypt at least 2 64 messages 3 with a total of 2 51 bytes 2 . No attack with up to 2 32 chosen plaintexts or chosen ciphertexts has a probability of success larger than 2 -32 .

  1. A reason for this restriction is the use of the AES-GCM cipher. Encrypting a different plaintext segment at the same location would be equivalent to reusing the IV, which violates the security guarantees of AES-GCM. Another reason is that this prevents roll-back attacks, where the attacker may try to restore a previous version of the file without detection. 

  2. 2 32 segments are supported, with each segment containing segment_size - tag_size bytes of plaintext. For 1 MB segments, the total plaintext size is 2 32 * (2 20 -16) ~= 2 51 bytes. 

  3. Streaming AEAD becomes insecure when a derived key (128-bit) and nonce prefix (independent random 7-byte value) combination is repeated. We have 184-bit collision resistance, which roughly translates to 2 64 messages if we want success probability to be less than 2 -32

Design a Mobile Site
View Site in Mobile | Classic
Share by: