JDK-8198925 : ChaCha20 and ChaCha20-Poly1305 Cipher Implementations
  • Type: CSR
  • Component: security-libs
  • Sub-Component: javax.crypto
  • Priority: P3
  • Status: Closed
  • Resolution: Approved
  • Fix Versions: 11
  • Submitted: 2018-03-02
  • Updated: 2018-05-09
  • Resolved: 2018-05-09
Related Reports
CSR :  
Relates :  

Implement the ChaCha20 and ChaCha20-Poly1305 ciphers as specified in [RFC 7539][rfc7539]. ChaCha20 is a relatively new stream cipher that can replace the older, insecure RC4 stream cipher.  Poly1305 is a one-time authenticator that can be used with ChaCha20 to provide integrity checking within the cipher's encrypt and decrypt functions, similar to other AEAD ciphers such as AES-GCM.


The only stream cipher currently implemented in providers shipped with JDK is ARCFOUR.  This cipher has been considered insecure for a long time.  Implementing ChaCha20 and ChaCha20/Poly1305 would give us a newer stream cipher that is currently considered secure.  The JDK needs to be on par with other cryptographic toolkits and TLS implementations.


The overall solution is broken into three components.  The ChaCha20 and ChaCha20-Poly1305 algorithms themselves will be an implementation of `javax.crypto.CipherSpi` contained within the SunJCE provider.  A new `KeyGenerator` that creates ChaCha20 keys will be added the SunJCE provider.  Finally a new public `AlgorithmParameterSpec` class will be added to support the configurable inputs to the ChaCha20 cipher.

### Instantiation
Ciphers will be instantiated similar to the way in which other ciphers in SunJCE are done, using the `Cipher.getInstance` call.  For both ciphers, there are two acceptable transforms 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 throw an exception.

### Initialization
The ChaCha20 cipher supports most of the initialization forms outlined in the Cipher API.  The only forms of `Cipher.init` that are not supported are those that take an `AlgorithmParameters` object.  The reason for this is that there is no standardized encoding for ChaCha20 parameters at this time.  The decision was to not come up with a proprietary encoding scheme.  This can be added later if standards bodies choose to develop a parameter format.  For the ChaCha20-Poly1305 cipher, the form of `Cipher.init` that takes an `AlgorithmParameters` object is supported.  The object must be created from a DER-encoded OCTET_STRING containing the nonce value [(per RFC 8103)][rfc8103].
Those forms of Cipher.init that do not take an `AlgorithmParameterSpec` are supported.  These forms will cause the cipher to be initialized using a random 12-byte nonce and an initial counter value of one.
Those forms of `Cipher.init()` that take an `AlgorithmParameterSpec` will be supported as follows:

- For ChaCha20, a `javax.crypto.spec.ChaCha20ParameterSpec` object will be required.  This is a new class detailed in the Specification section below.
- For ChaCha20-Poly1305 a `javax.crypto.spec.IvParameterSpec` object will be required.  This decision was made to allow ChaCha20-Poly1305 to be easily backported.  This cipher sees use in cryptographic protocols like [IPsec (RFC 7634)][rfc7634] and [TLS (RFC 7905)][rfc7905] as well as in message formats like [CMS (RFC 8103).][rfc8103]

### Use
Use of these ciphers generally conforms to the same model as with any symmetric Cipher implementation, and all forms of Cipher.update and` Cipher.doFinal` methods are supported.  There are some behavioral items to note:

- Since the ChaCha20 cipher operates as a simple stream cipher, encryption and decryption operations operate on a 1-to-1 basis.  That is, the amount of output from an update or doFinal call will be the same as the input.
- For ChaCha20-Poly1305, the input/output characteristics depend on whether encryption or decryption is being done:
    - For encryption, output will be 1-to-1 until the doFinal call, where the output will be the same length as any additional input provided, plus the length of the Poly1305 tag (16 bytes).
    - For decryption, no plaintext output will be provided until the authentication tag has been provided and a doFinal call performed.
- Likewise the `Cipher.getOuputSize` method reflects the above behavior
    - ChaCha20: The returned output length will equal the inputLen parameter
    - ChaCha20-Poly1305: The returned output length will be the input length plus the tag length for encryption.  Note that an actual doUpdate may not return that many bytes since the tag will not be created until the doFinal call.  For decryption, the output length is either the inputLen minus the tag length, or zero (whichever is larger).

### ChaCha20 Key Generator
Keys used with either ChaCha20 or ChaCha20-Poly1305 Cipher objects must be provided during intialization.  If a randomly-generated key suitable for use with ChaCha20 is desired, a new `javax.crypto.KeyGenerator` algorithm `"ChaCha20"` will be supported.  The only important items of note with this `KeyGenerator` are the following:

- The `KeyGenerator.init` methods that take an `AlgorithmParameterSpec` object are not supported and will throw an exception
- The forms of `KeyGenerator.init` that take a configurable key size in bits must be set to 256 bits (the only key size allowed by these ciphers).  Any other key length will throw an exception.


As mentioned earlier, there is only one new class, `javax.crypto.spec.ChaCha20ParameterSpec`.  The spec for this class is listed below:

 * This class specifies the parameters used with the
 * <a href="https://tools.ietf.org/html/rfc7539"><i>ChaCha20</i></a>
 * algorithm.
 * <p> The parameters consist of a 12-byte nonce and an initial
 * counter value expressed as a 32-bit integer.
 * <p> This class can be used to initialize a {@code Cipher} object that
 * implements the <i>ChaCha20</i> algorithm.
 * @since 11
public final class ChaCha20ParameterSpec implements AlgorithmParameterSpec {

     * Constructs a parameter set for ChaCha20 from the given nonce
     * and counter.
     * @param nonce a 12-byte nonce value
     * @param counter the initial counter value
     * @throws NullPointerException if {@code nonce} is {@code null}
     * @throws IllegalArgumentException if {@code nonce} is not 12 bytes
     *      in length.
    public ChaCha20ParameterSpec(byte[] nonce, int counter);

     * Returns the nonce value.
     * @return the nonce value.  This method returns a new array each time
     * this method is called.
    public byte[] getNonce();

     * Returns the configured counter value.
     * @return the counter value
    public int getCounter();
There are also some minor additions to `javax.crypto.Cipher` that give recommended guidance for the use of ChaCha20 and ChaCha20-Poly1305 cipher instances, as it pertains to nonce reuse, similar to what exists already for AES-GCM:

--- a/src/java.base/share/classes/javax/crypto/Cipher.java
+++ b/src/java.base/share/classes/javax/crypto/Cipher.java
@@ -1,5 +1,5 @@
- * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved.
  * This code is free software; you can redistribute it and/or modify it
@@ -111,7 +111,7 @@
  * encryption with a given key. When IVs are repeated for GCM
  * encryption, such usages are subject to forgery attacks. Thus, after
  * each encryption operation using GCM mode, callers should re-initialize
- * the cipher objects with GCM parameters which has a different IV value.
+ * the cipher objects with GCM parameters which have a different IV value.
  * <pre>
  *     GCMParameterSpec s = ...;
  *     cipher.init(..., s);
@@ -131,6 +131,13 @@
  *     ...
  * </pre>
+ * The ChaCha20 and ChaCha20-Poly1305 algorithms have a similar requirement
+ * for unique nonces with a given key.  After each encryption or decryption
+ * operation, callers should re-initialize their ChaCha20 or ChaCha20-Poly1305
+ * ciphers with parameters that specify a different nonce value.  Please
+ * see <a href="https://tools.ietf.org/html/rfc7539">RFC 7539</a> for more
+ * information on the ChaCha20 and ChaCha20-Poly1305 algorithms.
+ * <p>
  * Every implementation of the Java platform is required to support
  * the following standard {@code Cipher} transformations with the keysizes
  * in parentheses:

Standard Names

The following new standard names will be introduced with this JEP:

### Cipher

* ChaCha20
* ChaCha20-Poly1305

### AlgorithmParameters

* ChaCha20-Poly1305

### KeyGenerator

* ChaCha20

[rfc7539]: https://tools.ietf.org/html/rfc7539
[rfc7634]: https://tools.ietf.org/html/rfc7634
[rfc7905]: https://tools.ietf.org/html/rfc7905
[rfc8103]: https://tools.ietf.org/html/rfc8103

Moving back to draft in order to accommodate a last-minute spec change to ChaCha20ParameterSpec. The getNonce and getCounter methods will have their "final" qualifiers removed since the class as a whole is final.

Moving to Approved.

Thanks for the feedback. I've already requested a review from seclibs engineers. We are targeting JDK 11 for this feature, and I'll get a proper scope on the CSR as well. I waffled on which way to go with the nonce length. Maybe you're right though, 12 bytes is better. I think originally I went with 96 bits because that's how it's stated in the specification, but the type is byte-aligned so it's better to go with byte lengths here. I will make that change. If the nonce is not 12 bytes exactly it will throw an exception. It's odd: my code has the proper language in the javadoc. I will fix it here to match.

I'm voting to accept your request and a few additional items need to be addressed before the request can be finalized for the second phase of CSR review: * One or more security libs engineers needs to review this CSR. * Please indicate which release this is targeted for, presumably JDK 11 * Is this a JDK feature or an SE feature? Please indicate in the Scope field visible when hitting "Edit". In other words, should all JDK implementations of a particular Java SE standard be required to support these algorithms? If so, I'd expect more API impact than indicated so far. The nonce value is referred to as an 96-bit value and is communicated with a byte array. In this implementation, it may be convenient to refer to this as a 12 byte value instead. @throws IllegalArgumentException if {@code nonce} is less than 96-bits * in length What happens if the nonce is more than 96 bits?