JEP 329: ChaCha20 and Poly1305 Cryptographic Algorithms
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
- Provide ChaCha20 and ChaCha20-Poly1305
Cipher
implementations. These algorithms will be implemented in the SunJCE provider. - Provide a
KeyGenerator
implementation that creates keys suitable for ChaCha20 and ChaCha20-Poly1305 algorithms. - Provide an
AlgorithmParameters
implementation for use with the ChaCha20-Poly1305 algorithm.
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:
- Verify that the ChaCha20 and ChaCha20-Poly1305 ciphers pass all known-answer tests
- Verify the ChaCha20 key generator accepts the proper initialization forms and generates keys of the appropriate size
- Verify that the ciphers handle restrictions on initialization (no nonce reuse, etc.)
- Verify that the ciphers execute the proper re-initialization requirements between complete encryption/decryption operations
- Verify that our implementation interoperates with at least one other implementation
- Verfy the ChaCha20-Poly1305 AlgorithmParameters implementation accepts nonce data in the proper format.
Dependencies
The only significant dependency is the constant-time math APIs. These will be provided as part of JEP 324.