Cryptography With Google Tink

Say goodbye to OpenSSL and hello to the best in practice

Cryptography With Google Tink

Say goodbye to OpenSSL and hello to the best in practice

It is over 40 years since the creation of the RSA method, and it has been a history of where cryptography methods have come and gone. New attack vectors have also surfaced, and where methods which looked invincible have crumbled. We now have a patch-work of cryptographic methods, and which often fail to properly secure data for confidentially, integrity and access rights.

And so we have DES, 3DES, RC4, RC4, RC5, MD5, SHA-1, SHA-2, SHA-3, RSA, … and so many others. Few software libraries have kept up with the development, and thus OpenSSL has been the de-facto place to look for the code which implements these methods. It is thus the place that developers have looked for methods which have been properly tested and known to work in a range of applications. But it’s creaking at the seams, and almost every month a new vulnerability is being found.

The Legacy of OpenSSL

OpenSSL is commonly used by Linux-based servers to implement the SSL/TLS connection, and bugs are common. It was started with Eric A Young and Tim Hudson, in Dec 1998, who created the first version of OpenSSL (SSLeay — SSL Eric AYoung), which then became Version 0.9.1. Eric finished his research and left to go off and do other things, and was involved with Cryptsoft (www.cryptsoft.com) before joining RSA Security, where he is currently a Distinguished Engineer. After Eric left, it was then left to Steve (from the US) and Stephen Henson (from the UK) to continue its development through the OpenSSL Software Foundation (OSF).

The code continued to grow with TLS support added to 1.0.1a on 14 March 2012. Unfortunately, a bug appeared on 1 Jan 2012 which implemented the Heartbeat protocol (RFC 6520), and which ended-up resulting in Heartbleed (Figure 1). The bug was actually introduced by the same person who wrote the RFC — Robin Seggleman, a German developer:

Figure 1: RFC 6520 led to Heartbleed

Every significant vulnerability gets a CVE number. Sometimes they are associated with a common name such as Heartbleed (CVE-2014–0160), BEAST (CVE-2011–3389), FREAK (CVE-2015–0204), and so on, but some, such as CVE-2016–2017, just doesn’t have a common name. This bug related to OpenSSL 1.0.1 and 1.0.2, and where an attacker could perform a MITM (Man-in-the-Middle) attack on AES encryption.

Anyone looking at the OpenSSL code will see that it has no real focus on creating a consistent approach to code integration, and needed a redesign, especially in a world which is focused on mobile devices. Google initially forked the OpenSSL code with BoringSSL, but have now formally released Google Tink.

Google Tink

Google has formally released Tink Version 1.2.0 in August 2018, as a multi-language (Java, C++ and Objective C), cross-platform cryptographic library. Its main focus is to replace OpenSSL and where there are complex bindings and which were often focused on specific systems, such as for DLLs in Windows systems. While Google created Tink, it is open-source and has the aim to create simple APIs for cryptography functions and which should make the infrastructure more portable, secure and stable.

For Tink — based on BoringSSL and now at Version 1.2.0 — the adoption has been good and is already integrated into AdMob, Google Pay, Google Assistant, and Firebase. It also integrates AEAD (Authenticated Encryption with Associated Data) methods and which integrates encryption keys, a hash function, and a message authentication code (MAC). Google, too, has analysed many cryptography weaknesses and have created code which addresses many of these problems.

The minimal standards for AEAD include [RFC5116]:

  • The plaintext and associated data can have any length (from 0 to 2³² bytes).
  • Supports 80-bit authentication.
  • CCA2 security (adaptive chosen-ciphertext attack).

Google Tink: Symmetric key

A basic cryptography operation is to use symmetric key encryption, and where Bob and Alice use the same key to encrypt and also to decrypt. Either Bob creates the key, and then passes it securely to Alice, or they use a key exchange method to generate a shared key:

Tink aims to simplify encryption processing and use the best methods possible for encryption. In the following we encrypt a string (“napier”) with a key of “qwerty123”:

package com.helloworld;
import com.google.crypto.tink.aead.AeadConfig;
import java.security.GeneralSecurityException;
import com.google.crypto.tink.Aead;
import com.google.crypto.tink.KeysetHandle;
import com.google.crypto.tink.aead.AeadFactory;
import com.google.crypto.tink.aead.AeadKeyTemplates;
public final class HelloWorld {
public static void main(String[] args) throws Exception {
AeadConfig.register();
try {
KeysetHandle keysetHandle = KeysetHandle.generateNew(AeadKeyTemplates.AES128_GCM);
Aead aead = AeadFactory.getPrimitive(keysetHandle);
String plaintext="napier";
String aad="qwerty123";
System.out.println("Text:"+plaintext);
byte[] ciphertext = aead.encrypt(plaintext.getBytes(), aad.getBytes());
System.out.println("Cipher:"+ciphertext.toString());
byte[] decrypted = aead.decrypt(ciphertext, aad.getBytes());
String s = new String(decrypted);
System.out.println("Text:"+s);
} catch (GeneralSecurityException e) {
System.out.println(e);
System.exit(1);
}
}
}

A sample run proves the process:

Text:  hello123
Password: qwerty
Type: 1
Enc type: 128-bit AES GCM
Cipher: AQbLoE0ino8ofgrvuSSLOKTaYjdPc/ovwWznuMeYfjP+TO1fc6cn7DE=
Cipher: 4151624C6F4530696E6F386F666772767553534C4F4B5461596A6450632F6F7677577A6E754D6559666A502B544F31666336636E3744453D
Decrypted: hello123

In this case we use 128-bit AES with GCM (Galois/counter mode). Our AEAD object is created with:

KeysetHandle keysetHandle = KeysetHandle.generateNew(AeadKeyTemplates.AES128_GCM);
Aead aead = AeadFactory.getPrimitive(keysetHandle);

and then the encrypt() and decrypt() methods are used to create the cipher stream and then decipher it. If we view the key (in a Json format), we see a key of “GhBruFowER7fvpmnsKYNpAuK”, and which is defined by the AesGcmKey format:

{
“primaryKeyId”: 689326870,
“key”: [{
“keyData”: {
“typeUrl”: “type.googleapis.com/google.crypto.tink.AesGcmKey”,
“keyMaterialType”: “SYMMETRIC”,
“value”: “GhBruFowER7fvpmnsKYNpAuK”
},
“outputPrefixType”: “TINK”,
“keyId”: 689326870,
“status”: “ENABLED”
}]
}

A demo of these methods is here.

Google aims to focus the industry on strong encryption methods using AEAD and with integrated authentication: AES-EAX (encrypt-then-authenticate-then-translate), AES-GCM, AES-CTR-HMAC (Counter reset), KMS Envelope. For streaming encryption, these methods are converted into: AES-GCM-HKDF-STREAMING, and AES-CTR-HMAC-STREAMING .

This AeadKeyTemplates object has the following properties:

  • AES128_CTR_HMAC_SHA25. 16 byte AES key size. IV size: 16 bytes. HMAC key size: 32 bytes.HMAC tag size: 16 bytes. HMAC hash function: SHA256.
  • AES128_EAX. Key size: 16 bytes. IV size: 16 bytes.
  • AES128_GCM Key size: 16 bytes.
  • AES256_CTR_HMAC_SHA25. AES key size: 32 bytes. AES IV size: 16 bytes. HMAC key size: 32 bytes. HMAC tag size: 32 bytes. HMAC hash function: SHA256.
  • AES256_EAX. Key size: 32 bytes. IV size: 16 bytes.
  • AES256_GCM. Key size: 32 bytes.
  • CHACHA20_POLY1305.

Google Tink: MAC

One of the standard methods that we use in cryptography is to sign a message. For this we generate a signing key, and which is kept secret for a range of messages. This could relate to a single conversation between Bob and Alice, or for long-term communications between them.

Either Bob or Alice (or both of them) create a shared private key, and then pass it securely. Only Bob and Alice will have this key, and Eve will not be able to discover it. Every time Bob sends a message to Alice he sends the message with a MAC (Message Authentication Code), and which is the message encrypted with the private key and then produced as a hash code (SHA-256). This method is known as HMAC (Hash-based Message Authentication Code). Alice takes the signing key and checks that she gets the same MAC. If she does, she has checked that it is still Bob who is signing the messages. If it doesn’t tie-up, she assumes that Eve is pretending to be Bob, and discards the message.

Now let’s look at how Google Tink generates MAC codes. For this we generate a new MAC key with getPrimitive() method for MacFactory, and then use the computeMac() to create and verifyMac() to check:

KeysetHandle keysetHandle = KeysetHandle.generateNew(MacKeyTemplates.HMAC_SHA256_128BITTAG);
    Mac mac = MacFactory.getPrimitive(keysetHandle);
    byte[] tag = mac.computeMac(plaintext.getBytes());
   mac.verifyMac(tag,plaintext.getBytes());

A sample run which show the signature for a message of “hello123” for Base-64 and Hex formats:

Text:		hello123
MAC:	ASJFEIAQEqk9MvGaIsJyKcLiN2iw
MAC: 41534A464549415145716B394D76476149734A794B634C694E326977
Valid MAC

The key can also be printed and which shows the symmetric key used (and that we are using HMAC):

Printing out key:
MAC:	{
"primaryKeyId": 574951552,
"key": [{
"keyData": {
"typeUrl": "type.googleapis.com/google.crypto.tink.HmacKey",
"keyMaterialType": "SYMMETRIC",
"value": "EgQIAxAQGiAU/p0/1SEV+O1WE/fvQufi7z+rxQ0W6cJeRtgHHtqMQg=="
},
"outputPrefixType": "TINK",
"keyId": 574951552,
"status": "ENABLED"
}]
}

We can see that the encryption key used is “EgQIAxAQGiAU/p0/1SEV+O1WE/fvQufi7z+rxQ0W6cJeRtgHHtqMQg==” and that the format of the HmacKey is defined

If you want to try this it, try here. Here is the code:

package com.helloworld;
import java.util.Base64;
import com.google.crypto.tink.aead.AeadConfig;
import java.security.GeneralSecurityException;
import java.io.ByteArrayOutputStream;
import com.google.crypto.tink.Aead;
import com.google.crypto.tink.KeysetHandle;
import com.google.crypto.tink.Mac;
import com.google.crypto.tink.mac.MacFactory;
import com.google.crypto.tink.mac.MacKeyTemplates;
import org.apache.commons.codec.binary.Hex;
import com.google.crypto.tink.CleartextKeysetHandle;
import com.google.crypto.tink.JsonKeysetWriter;

public final class HelloWorld {
  public static void main(String[] args) throws Exception {

    AeadConfig.register();
    try {
    String plaintext="napier";

    if (args.length>0) plaintext=args[0];

    System.out.println("Text:\t"+plaintext);

KeysetHandle keysetHandle = KeysetHandle.generateNew(MacKeyTemplates.HMAC_SHA256_128BITTAG);
    Mac mac = MacFactory.getPrimitive(keysetHandle);
    
byte[] tag = mac.computeMac(plaintext.getBytes());
    byte[] encoded = Base64.getEncoder().encode(tag);
System.out.println("\nMAC (Base64):\t"+ new String(encoded));
System.out.println("MAC (Hex):\t"+ toHexString(encoded));
try {
mac.verifyMac(tag,plaintext.getBytes());
System.out.println("\nValid MAC");
} catch (GeneralSecurityException e) {
System.out.println("In Valid MAC");
}
      
System.out.println("\nPrinting out key:");
    
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    CleartextKeysetHandle.write(keysetHandle, JsonKeysetWriter.withOutputStream(outputStream));
    System.out.println("\nMAC:\t"+ new String(outputStream.toByteArray()));

    } catch (GeneralSecurityException e) {
System.out.println(e);
System.exit(1);
}
}
  public static String toHexString( byte[] bytes )
{
StringBuffer sb = new StringBuffer( bytes.length*2 );
for( int i = 0; i < bytes.length; i++ )
{
sb.append( toHex(bytes[i] >> 4) );
sb.append( toHex(bytes[i]) );
}
      return sb.toString();
}
private static char toHex(int nibble)
{
final char[] hexDigit =
{
'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'
};
return hexDigit[nibble & 0xF];
}
}

A demo of this method is here.

Google Tink: Digital Signatures

One of the standard methods that we use in cryptography is to sign a message with a private key, and to prove the signing with the public. Thus if Bob has a key pair, he uses his private key to sign the message, and then Alice will prove that it was Bob who signed it, using Bob’s public key. It will also prove that the message has not been changed by Eve.

We use this method is many applications. An example is in Bitcoin transfers and where Bob signs a transaction to pay Alice a given number of bitcoins. He signs this transaction with his private key (which is in his wallet), and then adds it, with his public key onto the blockchain. Anyone who wants to check the transfer will check the signature with Bob’s public key.

Now let’s look at how Google Tink generates digital signatures codes. The core method is to use ECDSA (Elliptic Curve Digital Signature Algorithm). For this we generate a new key pair with getPrimitive() method for PublicKeySignFactory. We can then use the private key of the key pair to sign a message with the sign() method:

KeysetHandle privateKeysetHandle = KeysetHandle.generateNew(SignatureKeyTemplates.ECDSA_P256);
PublicKeySign signer = PublicKeySignFactory.getPrimitive(privateKeysetHandle);
byte[] signature = signer.sign(plaintext.getBytes());

A sample run which shows the signature for a message of “hello” for Base-64 and Hex formats is:

Text:	hello
MAC (Base64):	ATHO2ZIwRAIgd+JV6SOM08i01AFsrGR8JLenLDWtPKzoWUDRh4tBqC8CIHEtnVf8OR9QxLQitKVvwxm7FlrkfLnVMC0G0nql3j5d
MAC (Hex):4154484F325A497752414967642B4A5636534F4D3038693031414673724752384A4C656E4C445774504B7A6F575544526834744271433843494845746E5666384F523951784C5169744B567677786D37466C726B664C6E564D433047306E716C336A3564
Valid Signature

In terms of the key pair we can view the key pair here:

Printing out key:
{
"primaryKeyId": 835639698,
"key": [{
"keyData": {
"typeUrl": "type.googleapis.com/google.crypto.tink.EcdsaPrivateKey",
"keyMaterialType": "ASYMMETRIC_PRIVATE",
"value": "EkwSBggDEAIYAhogafr30IV05SpchJZvJfQ6ChyWxhNIVSjpmztkJ+wYRusiIHCl4fYPSpuISdC18N9OhWQ9jZvky6B0ytnL97HqafbrGiEAxJzrrM7H3ONlzTRMdoPwWJ/PwkaQ2rCsUK5i3wGZMZQ="
},
"outputPrefixType": "TINK",
"keyId": 835639698,
"status": "ENABLED"
}]
}

We can see that the encryption key used is “EkwSBggDEAIYAho…GZMZQ=” and that the format of the EcdsPrivateKey is defined. The code is:

package com.helloworld;
import java.util.Base64;
import com.google.crypto.tink.aead.AeadConfig;
import java.security.GeneralSecurityException;
import java.io.ByteArrayOutputStream;
import com.google.crypto.tink.KeysetHandle;
import com.google.crypto.tink.PublicKeySign;
import com.google.crypto.tink.PublicKeyVerify;
import com.google.crypto.tink.signature.PublicKeySignFactory;
import com.google.crypto.tink.signature.PublicKeyVerifyFactory;
import com.google.crypto.tink.signature.SignatureKeyTemplates;
import org.apache.commons.codec.binary.Hex;
import com.google.crypto.tink.CleartextKeysetHandle;
import com.google.crypto.tink.JsonKeysetWriter;
import com.google.crypto.tink.config.TinkConfig;
public final class HelloWorld {
public static void main(String[] args) throws Exception {
TinkConfig.register();
try {
String plaintext="napier";
if (args.length>0) plaintext=args[0];
System.out.println("Text:\t"+plaintext);

KeysetHandle privateKeysetHandle = KeysetHandle.generateNew(SignatureKeyTemplates.ECDSA_P256);
PublicKeySign signer = PublicKeySignFactory.getPrimitive(privateKeysetHandle);
byte[] signature = signer.sign(plaintext.getBytes());
byte[] encoded = Base64.getEncoder().encode(signature);
System.out.println("\nMAC (Base64):\t"+ new String(encoded));
System.out.println("MAC (Hex):"+ toHexString(encoded));
KeysetHandle publicKeysetHandle =        privateKeysetHandle.getPublicKeysetHandle();
PublicKeyVerify verifier = PublicKeyVerifyFactory.getPrimitive( publicKeysetHandle);
try {
verifier.verify(signature, plaintext.getBytes());
System.out.println("\nValid Signature");
} catch (GeneralSecurityException e) {
System.out.println("In Valid Signature");
}
System.out.println("\nPrinting out key:");
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
CleartextKeysetHandle.write( privateKeysetHandle, JsonKeysetWriter.withOutputStream(outputStream));
System.out.println(new String(outputStream.toByteArray()));
} catch (GeneralSecurityException e) {
System.out.println(e);
System.exit(1);
}
}
public static String toHexString( byte[] bytes )
{
StringBuffer sb = new StringBuffer( bytes.length*2 );
for( int i = 0; i < bytes.length; i++ )
{
sb.append( toHex(bytes[i] >> 4) );
sb.append( toHex(bytes[i]) );
}
return sb.toString();
}
private static char toHex(int nibble)
{
final char[] hexDigit =
{
'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'
};
return hexDigit[nibble & 0xF];
}
}

A demo of this method is here.

Conclusions

With Google Tink we finally get a code based which has now been hacked together over the years (such as with OpenSSL) and which is consistent in its usage and focused on API integration. It is a code based for a modern world. A signing with the private key is one of the most fundamental actions that we have in our modern world, and it is basically our identity. So we need to protect our private keys, otherwise we will have our digital identity stolen.

Join Coinmonks Telegram Channel and Youtube Channel get daily Crypto News

Also, Read