Tink Deterministic Aead (AES SIV)Deterministic Aead provides authenticity without the need for a a nonce or IV. The SIV (Synthetic Initialization Vector) mode calculates an IV based on the entire input. A single bit of change in the plaintext results in ciphertext that cannot be differentiated from other ciphertext. SIV is defined in [RFC 5297]. [Tink Symmetric key][Tink MAC][Tink ECDSA/Ed25519]][Tink Hybrid encryption][Tink Envelope encryption][HKDF][Deterministic Authenticated Encryption]: |
Outline
AES-SIV is now standardized with RFC 5297 [here]. With enhanced encryption methods, we can both authenticate the cipher and prove its integrity. This is known as Authenticated Encryption with Associated Data (AEAD). For this we provide additional data to authenticate the encryption process, and where we can identify where the ciphertext has been modified, and for it not to be decrypted. With most conventional AEAD methods we create a nonce value and add additional data (AD) that is authenticated but not encrypted. With a nonce-less approach, we can use a key wrapping method, and which has often been used to protect encryption keys. The additional data can include [here]:
addresses, ports, sequence numbers, protocol version numbers, and other fields that indicate how the plaintext or ciphertext should be handled, forwarded, or processed
In this way, we can bind network packets to the encrypted data, and provide integrity, so that an intruder cannot copy-and-paste valid ciphertext from other transmissions. For example, if we bind to a packet sequence number and the port, the authentication would fail for another sequence number or another port.
In the following code we will use the additional information of ”101112131415161718191a1b1c1d1e1f2021222324252627", but in practice, this can be of any length. Within SIV, too, we can have multiple sources of this additional data (known as a vector of strings). This information could be spread across multiple sources and is likely to be difficult for an intruder to capture and replicate. This is one of the special features of SIV and allows multiple strings to be used for the authentication, rather than having to merge these into a single string for additional information:
byte[] plaintext = pl.getBytes(); String additional="101112131415161718191a1b1c1d1e1f2021222324252627";byte[] ciphertext = daead.encryptDeterministically(plaintext, Hex.decode(additional));
Traditional AEAD methods can be weak in terms of nonce reuse and also nonce misuse. In many systems, we use a non-deterministic method of generating a nonce, so there is no way of knowing if previously generated values have not been used before. Another weakness is where a nonce can be “played-back” by rolling back a virtual machine and discovering the nonce value used. Thus most AEAD methods are secure with a unique nonce, but where there is no guarantee of security if the nonce is not unique. As the nonce value is limited in size, there will always be a chance to reuse it with the same key, and thus we might have to change our keys on a regular basis. SIV provides more protection for nonce reuse/misuse, and where an attacker can only determine that a given plaintext value and a given set of associated data was protected using the defined key and nonce value.
The code is:
package com.helloworld; import com.google.crypto.tink.subtle.Base64; import com.google.crypto.tink.subtle.Hex; import com.google.crypto.tink.CleartextKeysetHandle; import com.google.crypto.tink.DeterministicAead; import com.google.crypto.tink.JsonKeysetReader; import com.google.crypto.tink.JsonKeysetWriter; import com.google.crypto.tink.KeysetHandle; import com.google.crypto.tink.KeysetWriter; import com.google.crypto.tink.aead.subtle.AeadFactory; import com.google.crypto.tink.daead.DeterministicAeadConfig; import com.google.crypto.tink.daead.DeterministicAeadKeyTemplates; import java.io.ByteArrayOutputStream; import java.nio.charset.StandardCharsets; public final class HelloWorld { public static void main(String[] args) throws Exception { DeterministicAeadConfig.register(); String pl="napier"; if (args.length>0) pl=args[0]; // Use random key KeysetHandle mykey = KeysetHandle.generateNew( DeterministicAeadKeyTemplates.AES256_SIV); DeterministicAead daead = mykey.getPrimitive(DeterministicAead.class); // Use fixed key // String res=writeJson("EkCM0SBfDTMTSQCZO71Qd48FFtzaARXFyT4+kCqmyLUclFTfOPk4M+7mPzZgjbl9Y9vJuBk+T/d44MVJGvq0SdD6"); // KeysetHandle mykey = CleartextKeysetHandle.read(JsonKeysetReader.withString(res)); // DeterministicAead daead = mykey.getPrimitive(DeterministicAead.class); // plaintext = Hex.decode("112233445566778899aabbccddee"); // define fixed input byte[] plaintext = pl.getBytes(); String additional="101112131415161718191a1b1c1d1e1f2021222324252627"; // keysetHandle.writeNoSecret(c); byte[] ciphertext = daead.encryptDeterministically(plaintext, Hex.decode(additional)); byte[] plres = daead.decryptDeterministically(ciphertext, Hex.decode(additional)); System.out.println("Plaintext: "+pl); System.out.println("Cipher: "+Hex.encode(ciphertext)); String str = new String(plres, StandardCharsets.UTF_8); System.out.println("Plaintext: "+pl); System.out.println("Associated data: "+additional); System.out.println("\nPrinting out key:"); // System.out.println("\nPrinting out key:"+keysetHandle.toString()); System.out.println("\nIn a JSON format:"); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); CleartextKeysetHandle.write(mykey, JsonKeysetWriter.withOutputStream(outputStream)); System.out.println( new String(outputStream.toByteArray())); } private static String writeJson(String value) { int keyId = 1234567; // fix String str = "{\n"; str = str + " \"primaryKeyId\": " + keyId + ",\n"; str = str + " \"key\": [{\n"; str = str + " \"keyData\": {\n"; str = str + " \"typeUrl\": \"type.googleapis.com/google.crypto.tink.AesSivKey\",\n"; str = str + " \"keyMaterialType\": \"SYMMETRIC\",\n"; str = str + " \"value\": \"" + value + "\"\n"; str = str + " },\n"; str = str + " \"outputPrefixType\": \"TINK\",\n"; str = str + " \"keyId\": " + keyId + ",\n"; str = str + " \"status\": \"ENABLED\"\n"; str = str + " }]\n"; str = str + "}"; return str; } }
A sample run is:
Plaintext: napier Cipher: 01552079bef36fc26a63783d9a0404bfa9a987afcf61ac60abe3d3 Plaintext: napier Printing out key: { "primaryKeyId": 1428191678, "key": [{ "keyData": { "typeUrl": "type.googleapis.com/google.crypto.tink.AesSivKey", "keyMaterialType": "SYMMETRIC", "value": "EkAFkhmlhYkmClmpz/vGzojJVgA/IQIMSty7rL8TXxyu9m/W0ZtzCddmSLFj7r8V/R0CywJ89KxdMVzdR+GDQH2w" }, "outputPrefixType": "TINK", "keyId": 1428191678, "status": "ENABLED" }] }
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.
Presentation
The following is a presentation related to Google Tink [slides]: