JEP 329: ChaCha20 and Poly1305 Cryptographic Algorithms

OwnerJamil Nimeh
TypeFeature
ScopeSE
StatusClosed / Delivered
Release11
Componentsecurity-libs / javax.crypto
Discussionsecurity dash dev at openjdk dot java dot net
EffortS
DurationS
Reviewed byBrian Goetz, Sean Mullan
Endorsed byBrian Goetz
Created2016/03/29 20:15
Updated2018/09/13 15:19
Issue8153028

Summary

Implement the ChaCha20 and ChaCha20-Poly1305 ciphers as specified in RFC 7539. ChaCha20 is a relatively new stream cipher that can replace the older, insecure RC4 stream cipher.

Goals

Non-Goals

TLS cipher suite support will not be part of this JEP. TLS support for these ciphers will be part of a follow-on enhancement.

Motivation

The only other widely adopted stream cipher, RC4, has long been deemed insecure. The industry consensus is that ChaCha20-Poly1305 is secure at this point in time, and it has seen fairly wide adoption across TLS implementations as well as in other cryptographic protocols. The JDK needs to be on par with other cryptographic toolkits and TLS implementations.

Additionally, TLS 1.3 only allows the use of AEAD-based cipher suites. Implementing the ChaCha20-Poly1305 algorithm is the first step in implementing different cipher suites that run in AEAD mode in case there were ever weaknesses to be found in AES or GCM.

Description

The ChaCha20 and ChaCha20-Poly1305 algorithms will implement the javax.crypto.CipherSpi API within the SunJCE provider. Ciphers will be instantiated in the same way as other ciphers, using the Cipher.getInstance() method. For both ciphers, two acceptable transforms are allowed. The single-name transform is the preferred approach, "ChaCha20" for ChaCha20 as a simple stream cipher with no authentication, and "ChaCha20-Poly1305" for ChaCha20 as an AEAD cipher using Poly1305 as the authenticator. "ChaCha20/None/NoPadding" and "ChaCha20-Poly1305/None/NoPadding" are also acceptable transform strings, though no other mode or padding values besides "None" and "NoPadding" will be accepted. Use of other mode or padding values will cause an exception to be thrown.

Initialization of the ChaCha20 cipher will accept a new AlgorithmParameterSpec implementation, javax.crypto.spec.ChaCha20ParameterSpec:

ChaCha20ParameterSpec(byte[] nonce, int counter);     // Constructor
public byte[] getNonce();     // Obtains a copy of the nonce value
public int getCounter();     // Obtains the initial counter value

The nonce value must be 96 bits in length (12 bytes). Any other length will cause an exception to be thrown. The integer counter value may be any integer value, even negative, in order to allow the full range of unsigned 32-bit values.

Initialization of this algorithm without a ChaCha20ParameterSpec will cause the cipher to generate its own 12-byte nonce internally and set the counter value to 1. The counter bytes may be obtained by invoking the Cipher.getIV() method.

Initialization for ChaCha20-Poly1305 may be accomplished by providing an instance of the current javax.crypto.spec.IvParameterSpec class which contains the 12-byte nonce. The decision to use IvParameterSpec over ChaCha20ParameterSpec allows ChaCha20-Poly1305 to be backported to earlier releases without making any API changes. Because IvParameterSpec does not set length requirements on the bytes it holds, the cipher object itself will enforce the 12-byte length requirement during initialization.

As with ChaCha20, ChaCha20-Poly1305 may be initialized without an IvParameterSpec, in which case the nonce will be randomly generated and may be obtained with Cipher.getIV().

Key objects provided through any of the init methods must have an algorithm type of "ChaCha20". A new KeyGenerator implementation will be created to support this. As with existing KeyGenerator implementations for other algorithms such as AES, RC2, ARCFOUR and the HmacSHA2 family, this KeyGenerator may not be initialized with an AlgorithmParameterSpec. If forms of the init method are invoked that allow an adjustable key length, that parameter must be set to 256 or an InvalidParameterException will be thrown.

Use of the ChaCha20 algorithm follows the existing Cipher API used for other stream ciphers. A simple single-part encryption could be written as follows:

// Get a Cipher instance and set up the parameters
// Assume SecretKey "key", 12-byte nonce "nonceBytes" and plaintext "pText"
// are coming from outside this code snippet
Cipher mambo = Cipher.getInstance("ChaCha20");
ChaCha20ParameterSpec mamboSpec
    = new ChaCha20ParameterSpec(nonceBytes, 7);   // Use a starting counter value of "7"
// Encrypt our input
mambo.init(Cipher.ENCRYPT_MODE, key, mamboSpec);
byte[] encryptedResult = mambo.doFinal(pText);

For ChaCha20 running in AEAD mode with the Poly1305 authenticator, only the nonce is required since RFC 7539 defines the initial counter value for data to begin at 1. In order to allow this Cipher implementation to be backportable and facilitate its use within our JSSE provider, javax.crypto.spec.IvParameterSpec will be used to provide the nonce.

When running in AEAD mode, output sizes may be different than inputs due to either the addition of the authentication tag (for encryption) or the consumption and verification of the tag (for decryption). Where allocation of an output buffer before encryption/decryption is desired the getOutputSize() method should be used. A sample single-part encryption follows:

// Get a Cipher instance and set up the parameters
// Assume SecretKey "key", 12-byte nonce "nonceBytes" and plaintext "pText"
// are coming from outside this code snippet
Cipher mambo = Cipher.getInstance("ChaCha20-Poly1305");
AlgorithmParameterSpec mamboSpec = new IvParameterSpec(nonceBytes);

// Encrypt our input
mambo.init(Cipher.ENCRYPT_MODE, key, mamboSpec);
byte[] encryptedResult = new byte[mambo.getOutputSize(pText.length)];
mambo.doFinal(pText, 0, pText.length, encryptedResult);

An important requirement for both ChaCha20 and ChaCha20-Poly1305 ciphers is that following a doFinal() call, a new init() call must be made which provides a different nonce from the one currently configured. This is similar to the requirement on encryption for AES-GCM, but in the cases of these two ciphers the init requirement must happen after both encryption and decryption operations. Subsequent calls to Cipher.update(), Cipher.updateAAD() or Cipher.doFinal() after a previous doFinal() and no init() call in-between will result in an IllegalStateException being thrown.

Testing

Testing will cover the following areas:

Dependencies

The only significant dependency is the constant-time math APIs. These will be provided as part of JEP 324.