I want to protect data from tampering

We recommend the Message Authentication Code (MAC) primitive with the HMAC_SHA256 key type for most use cases.

If you want to ensure that nobody can tamper with your data, we recommend the Message Authentication Code (MAC) primitive. It uses a single key to generate message authentication codes and verify them. MAC does not encrypt data. In most cases, protecting data with AEAD , which includes encryption and MAC, is preferable to MAC alone.

The following examples get you started using the MAC primitive:

C++

 // A command-line utility for showcasing using the Tink MAC primitive. 
 #include <cstdlib> 
 #include <fstream> 
 #include <iostream> 
 #include <memory> 
 #include <ostream> 
 #include <sstream> 
 #include <string> 
 #include <utility> 
 #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" 
 #include 
  
 "tink/keyset_handle.h" 
 #include 
  
 "tink/mac.h" 
 #include 
  
 "tink/mac/mac_config.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 {compute|verify}" 
 ); 
 ABSL_FLAG 
 ( 
 std 
 :: 
 string 
 , 
  
 data_filename 
 , 
  
 "" 
 , 
  
 "Data file name" 
 ); 
 ABSL_FLAG 
 ( 
 std 
 :: 
 string 
 , 
  
 tag_filename 
 , 
  
 "" 
 , 
  
 "Authentication tag file name" 
 ); 
 namespace 
  
 { 
 using 
  
 :: 
 crypto 
 :: 
 tink 
 :: 
 KeysetHandle 
 ; 
 using 
  
 :: 
 crypto 
 :: 
 tink 
 :: 
 Mac 
 ; 
 using 
  
 :: 
 crypto 
 :: 
 tink 
 :: 
 MacConfig 
 ; 
 using 
  
 :: 
 crypto 
 :: 
 tink 
 :: 
 util 
 :: 
 Status 
 ; 
 using 
  
 :: 
 crypto 
 :: 
 tink 
 :: 
 util 
 :: 
 StatusOr 
 ; 
 constexpr 
  
 absl 
 :: 
 string_view 
  
 kCompute 
  
 = 
  
 "compute" 
 ; 
 constexpr 
  
 absl 
 :: 
 string_view 
  
 kVerify 
  
 = 
  
 "verify" 
 ; 
 void 
  
 ValidateParams 
 () 
  
 { 
  
 // ... 
 } 
 } 
  
 // namespace 
 namespace 
  
 tink_cc_examples 
  
 { 
 // MAC example CLI implementation. 
 Status 
  
 MacCli 
 ( 
 absl 
 :: 
 string_view 
  
 mode 
 , 
  
 const 
  
 std 
 :: 
 string 
  
 keyset_filename 
 , 
  
 const 
  
 std 
 :: 
 string 
&  
 data_filename 
 , 
  
 const 
  
 std 
 :: 
 string 
&  
 tag_filename 
 ) 
  
 { 
  
 Status 
  
 result 
  
 = 
  
 MacConfig 
 :: 
 Register 
 (); 
  
 if 
  
 ( 
 ! 
 result 
 . 
 ok 
 ()) 
  
 return 
  
 result 
 ; 
  
 // Read the 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<Mac> 
>  
 mac_primitive 
  
 = 
  
 ( 
 * 
 keyset_handle 
 ) 
  
 - 
> GetPrimitive<crypto 
 :: 
 tink 
 :: 
 Mac 
> ( 
  
 crypto 
 :: 
 tink 
 :: 
 ConfigGlobalRegistry 
 ()); 
  
 if 
  
 ( 
 ! 
 mac_primitive 
 . 
 ok 
 ()) 
  
 return 
  
 mac_primitive 
 . 
 status 
 (); 
  
 // Read the input. 
  
 StatusOr<std 
 :: 
 string 
>  
 data_file_content 
  
 = 
  
 ReadFile 
 ( 
 data_filename 
 ); 
  
 if 
  
 ( 
 ! 
 data_file_content 
 . 
 ok 
 ()) 
  
 return 
  
 data_file_content 
 . 
 status 
 (); 
  
 std 
 :: 
 string 
  
 output 
 ; 
  
 if 
  
 ( 
 mode 
  
 == 
  
 kCompute 
 ) 
  
 { 
  
 // Compute authentication tag. 
  
 StatusOr<std 
 :: 
 string 
>  
 compute_result 
  
 = 
  
 ( 
 * 
 mac_primitive 
 ) 
 - 
> ComputeMac 
 ( 
 * 
 data_file_content 
 ); 
  
 if 
  
 ( 
 ! 
 compute_result 
 . 
 ok 
 ()) 
  
 return 
  
 compute_result 
 . 
 status 
 (); 
  
 // Write out the authentication tag to tag file. 
  
 return 
  
 WriteToFile 
 ( 
 * 
 compute_result 
 , 
  
 tag_filename 
 ); 
  
 } 
  
 else 
  
 { 
  
 // operation == kVerify. 
  
 // Read the authentication tag from tag file. 
  
 StatusOr<std 
 :: 
 string 
>  
 tag_result 
  
 = 
  
 ReadFile 
 ( 
 tag_filename 
 ); 
  
 if 
  
 ( 
 ! 
 tag_result 
 . 
 ok 
 ()) 
  
 { 
  
 std 
 :: 
 cerr 
 << 
 tag_result 
 . 
 status 
 (). 
 message 
 () 
 << 
 '\n' 
 ; 
  
 exit 
 ( 
 1 
 ); 
  
 } 
  
 // Verify authentication tag. 
  
 Status 
  
 verify_result 
  
 = 
  
 ( 
 * 
 mac_primitive 
 ) 
 - 
> VerifyMac 
 ( 
 * 
 tag_result 
 , 
  
 * 
 data_file_content 
 ); 
  
 if 
  
 ( 
 verify_result 
 . 
 ok 
 ()) 
  
 std 
 :: 
 clog 
 << 
 "Verification succeeded!" 
 << 
 '\n' 
 ; 
  
 return 
  
 verify_result 
 ; 
  
 } 
 } 
 } 
  
 // 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 
  
 data_filename 
  
 = 
  
 absl 
 :: 
 GetFlag 
 ( 
 FLAGS_data_filename 
 ); 
  
 std 
 :: 
 string 
  
 tag_filename 
  
 = 
  
 absl 
 :: 
 GetFlag 
 ( 
 FLAGS_tag_filename 
 ); 
  
 std 
 :: 
 clog 
 << 
 "Using keyset from file '" 
 << 
 keyset_filename 
 << 
 "' to " 
 << 
 mode 
 << 
 " authentication tag from file '" 
 << 
 tag_filename 
 << 
 "' for data file '" 
 << 
 data_filename 
 << 
 "'." 
 << 
 '\n' 
 ; 
  
 std 
 :: 
 clog 
 << 
 "The tag will be " 
 << 
 (( 
 mode 
  
 == 
  
 kCompute 
 ) 
  
 ? 
  
 "written to" 
  
 : 
  
 "read from" 
 ) 
 << 
 " file '" 
 << 
 tag_filename 
 << 
 "'." 
 << 
 '\n' 
 ; 
  
 CHECK_OK 
 ( 
 tink_cc_examples 
 :: 
 MacCli 
 ( 
 mode 
 , 
  
 keyset_filename 
 , 
  
 data_filename 
 , 
  
 tag_filename 
 )); 
  
 return 
  
 0 
 ; 
 } 
  

Go

 import 
  
 ( 
  
 "bytes" 
  
 "fmt" 
  
 "log" 
  
 "github.com/tink-crypto/tink-go/v2/insecurecleartextkeyset" 
  
 "github.com/tink-crypto/tink-go/v2/keyset" 
  
 "github.com/tink-crypto/tink-go/v2/mac" 
 ) 
 func 
  
 Example 
 () 
  
 { 
  
 // A keyset created with "tinkey create-keyset --key-template=HMAC_SHA256_128BITTAG". 
  
 // Note that this keyset has the secret key information in cleartext. 
  
 jsonKeyset 
  
 := 
  
 `{ 
 "key": [{ 
 "keyData": { 
 "keyMaterialType": 
 "SYMMETRIC", 
 "typeUrl": 
 "type.googleapis.com/google.crypto.tink.HmacKey", 
 "value": 
 "EgQIAxAQGiA0LQjovcydWhVQV3k8W9ZSRkd7Ei4Y/TRWApE8guwV4Q==" 
 }, 
 "keyId": 1892702217, 
 "outputPrefixType": "TINK", 
 "status": "ENABLED" 
 }], 
 "primaryKeyId": 1892702217 
 }` 
  
 // 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 MAC primitive we want to use from the keyset handle. 
  
 primitive 
 , 
  
 err 
  
 := 
  
 mac 
 . 
 New 
 ( 
 keysetHandle 
 ) 
  
 if 
  
 err 
  
 != 
  
 nil 
  
 { 
  
 log 
 . 
 Fatal 
 ( 
 err 
 ) 
  
 } 
  
 // Use the primitive to create a MAC tag for some data. In this case the primary 
  
 // key of the keyset will be used (which is also the only key in this example). 
  
 data 
  
 := 
  
 [] 
 byte 
 ( 
 "data" 
 ) 
  
 tag 
 , 
  
 err 
  
 := 
  
 primitive 
 . 
 ComputeMAC 
 ( 
 data 
 ) 
  
 if 
  
 err 
  
 != 
  
 nil 
  
 { 
  
 log 
 . 
 Fatal 
 ( 
 err 
 ) 
  
 } 
  
 // Use the primitive to verify the tag. VerifyMAC finds the correct key in 
  
 // the keyset. If no key is found or verification fails, it returns an error. 
  
 err 
  
 = 
  
 primitive 
 . 
 VerifyMAC 
 ( 
 tag 
 , 
  
 data 
 ) 
  
 if 
  
 err 
  
 != 
  
 nil 
  
 { 
  
 log 
 . 
 Fatal 
 ( 
 err 
 ) 
  
 } 
  
 fmt 
 . 
 Printf 
 ( 
 "tag is valid" 
 ) 
  
 // Output: tag is valid 
 } 
  

Java

 package 
  
 mac 
 ; 
 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.Mac 
 ; 
 import 
  
 com.google.crypto.tink.RegistryConfiguration 
 ; 
 import 
  
 com.google.crypto.tink.TinkJsonProtoKeysetFormat 
 ; 
 import 
  
 com.google.crypto.tink.mac.MacConfig 
 ; 
 import 
  
 java.nio.file.Files 
 ; 
 import 
  
 java.nio.file.Path 
 ; 
 import 
  
 java.nio.file.Paths 
 ; 
 /** 
 * A command-line utility for checking file integrity with a Message Authentication Code (MAC). 
 * 
 * <p>It loads cleartext keys from disk - this is not recommended! 
 * 
 * <p>It requires the following arguments: 
 * 
 * <ul> 
 *   <li>mode: either 'compute' or 'verify'. 
 *   <li>key-file: Read the key material from this file. 
 *   <li>input-file: Read the input from this file. 
 *   <li>mac-file: name of the file containing a hexadecimal MAC of the input data. 
 */ 
 public 
  
 final 
  
 class 
 MacExample 
  
 { 
  
 public 
  
 static 
  
 void 
  
 main 
 ( 
 String 
 [] 
  
 args 
 ) 
  
 throws 
  
 Exception 
  
 { 
  
 if 
  
 ( 
 args 
 . 
 length 
  
 != 
  
 4 
 ) 
  
 { 
  
 System 
 . 
 err 
 . 
 printf 
 ( 
 "Expected 4 parameters, got %d\n" 
 , 
  
 args 
 . 
 length 
 ); 
  
 System 
 . 
 err 
 . 
 println 
 ( 
 "Usage: java MacExample compute/verify key-file input-file mac-file" 
 ); 
  
 System 
 . 
 exit 
 ( 
 1 
 ); 
  
 } 
  
 String 
  
 mode 
  
 = 
  
 args 
 [ 
 0 
 ] 
 ; 
  
 if 
  
 ( 
 ! 
 mode 
 . 
 equals 
 ( 
 "compute" 
 ) 
 && 
 ! 
 mode 
 . 
 equals 
 ( 
 "verify" 
 )) 
  
 { 
  
 System 
 . 
 err 
 . 
 println 
 ( 
 "Incorrect mode. Please select compute or verify." 
 ); 
  
 System 
 . 
 exit 
 ( 
 1 
 ); 
  
 } 
  
 Path 
  
 keyFile 
  
 = 
  
 Paths 
 . 
 get 
 ( 
 args 
 [ 
 1 
 ] 
 ); 
  
 byte 
 [] 
  
 msg 
  
 = 
  
 Files 
 . 
 readAllBytes 
 ( 
 Paths 
 . 
 get 
 ( 
 args 
 [ 
 2 
 ] 
 )); 
  
 Path 
  
 macFile 
  
 = 
  
 Paths 
 . 
 get 
 ( 
 args 
 [ 
 3 
 ] 
 ); 
  
 // Register all MAC key types with the Tink runtime. 
  
 MacConfig 
 . 
 register 
 (); 
  
 // Read the keyset into a KeysetHandle. 
  
 KeysetHandle 
  
 handle 
  
 = 
  
 TinkJsonProtoKeysetFormat 
 . 
 parseKeyset 
 ( 
  
 new 
  
 String 
 ( 
 Files 
 . 
 readAllBytes 
 ( 
 keyFile 
 ), 
  
 UTF_8 
 ), 
  
 InsecureSecretKeyAccess 
 . 
 get 
 ()); 
  
 // Get the primitive. 
  
 Mac 
  
 macPrimitive 
  
 = 
  
 handle 
 . 
 getPrimitive 
 ( 
 RegistryConfiguration 
 . 
 get 
 (), 
  
 Mac 
 . 
 class 
 ); 
  
 if 
  
 ( 
 mode 
 . 
 equals 
 ( 
 "compute" 
 )) 
  
 { 
  
 byte 
 [] 
  
 macTag 
  
 = 
  
 macPrimitive 
 . 
 computeMac 
 ( 
 msg 
 ); 
  
 Files 
 . 
 write 
 ( 
 macFile 
 , 
  
 macTag 
 ); 
  
 } 
  
 else 
  
 { 
  
 byte 
 [] 
  
 macTag 
  
 = 
  
 Files 
 . 
 readAllBytes 
 ( 
 macFile 
 ); 
  
 // This will throw a GeneralSecurityException if verification fails. 
  
 macPrimitive 
 . 
 verifyMac 
 ( 
 macTag 
 , 
  
 msg 
 ); 
  
 } 
  
 } 
  
 private 
  
 MacExample 
 () 
  
 {} 
 } 
  

Python

 import 
  
 tink 
 from 
  
 tink 
  
 import 
 mac 
 from 
  
 tink 
  
 import 
 secret_key_access 
 def 
  
 example 
 (): 
  
 """Compute and verify MAC tags.""" 
 # Register the MAC key managers. This is needed to create a Mac primitive 
 # later. 
 mac 
 . 
 register 
 () 
 # Created with "tinkey create-keyset --key-template=HMAC_SHA256_128BITTAG". 
 # Note that this keyset has the secret key information in cleartext. 
 keyset 
 = 
 r 
 """{ 
 "key": [{ 
 "keyData": { 
 "keyMaterialType": 
 "SYMMETRIC", 
 "typeUrl": 
 "type.googleapis.com/google.crypto.tink.HmacKey", 
 "value": 
 "EgQIAxAQGiA0LQjovcydWhVQV3k8W9ZSRkd7Ei4Y/TRWApE8guwV4Q==" 
 }, 
 "keyId": 1892702217, 
 "outputPrefixType": "TINK", 
 "status": "ENABLED" 
 }], 
 "primaryKeyId": 1892702217 
 }""" 
 # Create a keyset handle from the cleartext keyset in the previous 
 # step. The keyset handle provides abstract access to the underlying keyset to 
 # limit access of the raw key material. WARNING: In practice, it is unlikely 
 # you will want to use tink.json_proto_keyset_format.parse, 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 Mac primitive we want to use from the keyset handle. 
 primitive 
 = 
 keyset_handle 
 . 
 primitive 
 ( 
 mac 
 . 
 Mac 
 ) 
 # Use the primitive to compute the MAC for a message. In this case the primary 
 # key of the keyset will be used (which is also the only key in this example). 
 data 
 = 
 b 
 'data' 
 tag 
 = 
 primitive 
 . 
 compute_mac 
 ( 
 data 
 ) 
 # Use the primitive to verify the MAC for the message. Verify finds the 
 # correct key in the keyset and verifies the MAC. If no key is found or 
 # verification fails, it raises an error. 
 primitive 
 . 
 verify_mac 
 ( 
 tag 
 , 
 data 
 ) 
  

Message Authentication Code (MAC)

The MAC primitive lets you to verify that no one has tampered with your data. A sender sharing a symmetric key with a recipient can compute an authentication tag for a given message, which allows the recipient to verify that a message is from the expected sender and has not been modified.

MAC has the following properties:

  • Authenticity: Knowing the key is the only way to create a verifiable MAC tag.
  • Symmetric: Computing and verifying the tag requires the same key.

MAC can be deterministic or randomized, depending on the algorithm. Tink does not implement non-deterministic MAC algorithms at the moment. You should use MAC only for message authentication, not for other purposes like generation of pseudorandom bytes (for that, see PRF ).

If you need an asymmetric primitive instead, see Digital Signature .

Choose a key type

We recommend using HMAC_SHA256for most uses, but there are other options as well.

In general, the following holds true:

  • HMAC_SHA512 may or may not be faster depending on your message size and the specifics of the hardware you use.
  • HMAC_SHA512 is the most conservative mode that can be used for practically unlimited number of messages.
  • AES256_CMAC is fastest on systems that support the AES-NI hardware acceleration.

Minimal security guarantees

  • At least 80-bit authentication strength
  • Secure against existential forgery under chosen plaintext attack
  • At least 128-bit security against key recovery attacks, and also in multi-user scenarios (when an attacker is not targeting a specific key, but any key from a set of up to 2 32 keys)
Create a Mobile Website
View Site in Mobile | Classic
Share by: