Overcoming Nonce Reuse/Misuse: SIV Mode

We give away a little too much of our secrets, and often fail to protect one of our most important secret … our encryption keys. To…

Photo by Samantha Lam on Unsplash

Overcoming Nonce Reuse/Misuse: SIV Mode

We give away a little too much of our secrets, and often fail to protect one of our most important secret … our encryption keys. To overcome this we can use a method called key wrapping, and which protects the key. This is especially important where we transmit the key over untrusted channels or store them in places without strong access control. For a key wrapping, Rogaway et al proposed the SIV (Synthetic Initialization Vector) method [here] and that authenticates and encrypts, along with authenticating any additional data related to the key:

The method 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.

Here is the full code [here]:

package com.helloworld;
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];

KeysetHandle keysetHandle = KeysetHandle.generateNew(
DeterministicAeadKeyTemplates.AES256_SIV);
	      DeterministicAead daead = keysetHandle.getPrimitive(DeterministicAead.class); 
	      byte[] plaintext = pl.getBytes();

String additional="101112131415161718191a1b1c1d1e1f2021222324252627";
	      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("\nPrinting out key:");
	      System.out.println("\nPrinting out key:"+keysetHandle.toString());

System.out.println("\nAnd in a JSON format:");

ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
	      CleartextKeysetHandle.write(keysetHandle, JsonKeysetWriter.withOutputStream(outputStream));
	      System.out.println( new String(outputStream.toByteArray()));
  }
}

A sample run is [here]:

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"
}]
}