JDK-8189806 : Elliptic Curves for Security in Crypto
  • Type: CSR
  • Component: security-libs
  • Sub-Component: javax.crypto
  • Priority: P3
  • Status: Closed
  • Resolution: Approved
  • Fix Versions: 11
  • Submitted: 2017-10-23
  • Updated: 2018-01-24
  • Resolved: 2018-01-24
Related Reports
CSR :  
Description
Summary
-------

Add support for X25519/X448 key agreement in the JCA API. 

Problem
-------

The JCA API contains classes and interfaces for key agreement with Diffie-Hellman, using both elliptic curves and (integer) finite fields. The X25519 and X448 key agreement mechanisms described in RFC 7748 use elliptic curve Diffie-Hellman in a way that is incompatible with the existing API classes and interfaces. So we need to define new API classes and interfaces in order to support these mechanisms. 

Solution
--------
Define a new set of API classes and interfaces that are mostly independent from the existing API for elliptic curve Diffie-Hellman. Define standard names for algorithms and curve parameter sets associated with X25519/X448. 

It is technically possible to use existing classes/interfaces for elliptic curve cryptography for these new mechanisms. In this approach, curve parameters would be interpreted differently when supplied to different elliptic curve crypto services. This approach was not selected because it would make the API confusing and error-prone, and may cause compatibility issues.

Example API usage:

    KeyPairGenerator kpg = KeyPairGenerator.getInstance("XDH");
    NamedParameterSpec paramSpec = new NamedParameterSpec("X25519");
    kpg.initialize(paramSpec); // equivalent to kpg.initialize(255)
    //alternatively: kpg = KeyPairGenerator.getInstance("X25519")
    KeyPair kp = kpg.generateKeyPair();

    KeyFactory kf = KeyFactory.getInstance("XDH");
    BigInteger u = ...
    XECPublicKeySpec pubSpec = new XECPublicKeySpec(paramSpec, u);
    PublicKey pubKey = kf.generatePublic(pubSpec);

    KeyAgreement ka = KeyAgreement.getInstance("XDH");
    ka.init(kp.getPrivate());
    ka.doPhase(pubKey, true);
    byte[] secret = ka.generateSecret();

The API uses the string "XDH" to refer to Diffie-Hellman key agreement using the operations and representation described in RFC 7748. The string "XEC" refers to these operations and representations in a way that is not specific to any particular cryptosystem. In the API, key interfaces and key spec classes are "XEC" so they can be reused with other cryptosystems (that are not Diffie-Hellman) based on RFC 7748. This XEC/XDH arrangement mirrors the existing EC/ECDH API classes/interfaces. This API does not standardize "XEC" algorithm names, so the programmer must use "XDH" as the algorithm name to obtain a `KeyPairGenerator` or `KeyFactory`. Requiring information about the intended use of the keys allows the implementation to apply algorithm-specific optimizations and ensure that keys are not misused.  

Specification
-------------

###JCA Identifier Strings###

In the Java Security Standard Algorithm Names Specification, define the following standard algorithm names for `KeyAgreement`, `KeyPairGenerator`, `KeyFactory`, and `AlgorithmParameters`:

Algorithm Name  |  Description 
--- | ---
XDH | Diffie-Hellman key agreement with elliptic curves as defined in RFC 7748 
X25519 | Diffie-Hellman key agreement with Curve25519 as defined in RFC 7748
X448 | Diffie-Hellman key agreement with Curve448 as defined in RFC 7748

A correct implementation of these algorithms must adhere to RFC 7748 exactly, and may only take liberty where allowed by that spec through use of language such as "may" or "should." In particular, assurance of contributory behavior is optional. None of these algorithms are required, and an implementation may support any subset of these algorithm names.  

The API includes the `NamedParameterSpec` class, which can be used to specify a set of algorithm parameters using a single name. In the Java Security Standard Algorithm Names Specification, add a new section which defines standard parameter set names. Like algorithm names, all standard names used with `NamedParameterSpec` are case-insensitive. This new section will contain the following table: 


Parameters Name |  Description 
--- | --- 
X25519 | Elliptic curve cryptography using the X25519 scalar multiplication function defined in RFC 7748
X448 | Elliptic curve cryptography using the X448 scalar multiplication function defined in RFC 7748

###API###

For the most part, the spec classes and interfaces for XDH mirror those used by the existing ECC API. Where the XDH API does not follow this pattern, additional explanation and motivation will be provided along with the spec. All files are new, except where otherwise noted.

**Parameter Specs**

Unlike the ECC API, the XDH API provides no way to specify arbitrary curve/field parameters for use with XDH. This feature is not commonly implemented/used in ECC, and it is even less desirable due to the nature of XDH. XDH parameters may be specified by a single standard name which identifies the curve and other parameters that will be used in the key agreement operation. The new class `NamedParameterSpec` is used to specify this name to the provider. 

```
package java.security.spec;

/**
 * This class is used to specify any algorithm parameters that are determined
 * by a standard name. This class also holds constants for standard parameter
 * set names. The names of these constants exactly match the corresponding 
 * parameter set name. For example, NamedParameterSpec.X25519 represents the 
 * parameter set identified by the string "X25519". These strings are defined
 * in the <a href=
 * "{@docRoot}/../specs/security/standard-names.html#sslcontext-algorithms">
 *          Java Security Standard Algorithm Names Specification</a>.
 *
 * @see AlgorithmParameterSpec
 *
 */
public class NamedParameterSpec implements AlgorithmParameterSpec {

   /**
    * The X25519 parameters
    */
    public static final NamedParameterSpec X25519 
        = new NamedParameterSpec("X25519");
   /**
    * The X448 parameters
    */
    public static final NamedParameterSpec X448 
        = new NamedParameterSpec("X448");

    private String name;

    /**
     * Creates a parameter specification using a standard (or predefined)
     * name {@code stdName}. For the
     * list of supported names, please consult the documentation
     * of the provider whose implementation will be used.
     *
     * @param stdName the standard name of the algorithm parameters
     *
     * @throws NullPointerException if {@code stdName}
     * is null.
     */
    public NamedParameterSpec(String stdName) {
        Objects.requireNonNull(stdName, "stdName must not be null");
        this.name = stdName;
    }

    /**
     * Returns the standard name that determines the algorithm parameters.
     * @return the standard name.
     */
    public String getName() {
        return name;
    }
}
```

`NamedParameterSpec` is a generalization of the existing class `ECGenParameterSpec`, which is modified to reflect this relationship. `ECGenParameterSpec` is modified to extend `NamedParameterSpec`, and all of its functionality is moved into `NamedParameterSpec`. In other words, `NamedParameterSpec` is an extracted superclass of `ECGenParameterSpec`. 

    package java.security.spec;

    public class ECGenParameterSpec extends NamedParameterSpec {
    
        /**
         * Creates a parameter specification for EC parameter
         * generation using a standard (or predefined) name
         * {@code stdName} in order to generate the corresponding
         * (precomputed) elliptic curve domain parameters. For the
         * list of supported names, please consult the documentation
         * of provider whose implementation will be used.
         *
         * @param stdName the standard name of the to-be-generated EC
         *                domain parameters.
         * @throws NullPointerException if {@code stdName}
         *                              is null.
         */
        public ECGenParameterSpec(String stdName) {
            super(stdName);
        }
    }

`ECGenParametersSpec` is an existing class. For clarity, here is the diff that produces the above text:

    diff -r 5d71ff193033 src/java.base/share/classes/java/security/spec/ECGenParameterSpec.java
    --- a/src/java.base/share/classes/java/security/spec/ECGenParameterSpec.java	Thu Jul 20 06:49:20 2017 -0700
    +++ b/src/java.base/share/classes/java/security/spec/ECGenParameterSpec.java	Thu Oct 26 10:46:33 2017 -0400
    @@ -34,9 +34,7 @@
      *
      * @since 1.5
    */
    -public class ECGenParameterSpec implements AlgorithmParameterSpec {
    -
    -    private String name;
    +public class ECGenParameterSpec extends NamedParameterSpec {
    
            /**
             * Creates a parameter specification for EC parameter
             @@ -45,24 +43,14 @@
              * (precomputed) elliptic curve domain parameters. For the
              * list of supported names, please consult the documentation
              * of provider whose implementation will be used.
             +     *
              * @param stdName the standard name of the to-be-generated EC
            -     * domain parameters.
            -     * @exception NullPointerException if {@code stdName}
            -     * is null.
            +     *                domain parameters.
            +     * @throws NullPointerException if {@code stdName}
            +     *                              is null.
             */
            public ECGenParameterSpec(String stdName) {
                -        if (stdName == null) {
                    -            throw new NullPointerException("stdName is null");
                    -        }
                -        this.name = stdName;
                -    }
    -
    -    /**
    -     * Returns the standard or predefined name of the
    -     * to-be-generated EC domain parameters.
    -     * @return the standard or predefined name.
    -     */
    -    public String getName() {
                -        return name;
                +        super(stdName);
            }
        }


**Key Interfaces**

There are interfaces for public and private keys, along with a superinterface that allows access to algorithm parameters. The parameters are returned as the abstract type `AlgorithmParameterSpec` to allow some flexibility in how curve/field parameters are expressed in this API. 

```
package java.security.interfaces;

import java.security.spec.AlgorithmParameterSpec;

/**
 * XECKey is an interface for an RFC 7748 public/private key which allows
 * access to the algorithm parameters associated with that key.
 */
public interface XECKey {
    /**
     * Returns the algorithm parameters associated
     * with the key.
     *
     * @return the associated algorithm parameters
     */
    AlgorithmParameterSpec getParams();
}
```


Private key interface:

```
package java.security.interfaces;

import java.security.PrivateKey;
import java.util.Optional;

/**
 * An XEC private key is an encoded scalar value as described in RFC 7748.
 * The decoding procedure defined in this RFC includes an operation that forces
 * certain bits of the key to either 1 or 0. This operation is known as
 * "pruning" or "clamping" the private key. Arrays returned by this interface
 * are unpruned, and implementations will need to prune the array before
 * using it in any numerical operations.
 */
public interface XECPrivateKey extends XECKey, PrivateKey {

    /**
     * Get the scalar value encoded as an unpruned byte array. A new copy of
     * the array is returned each time this method is called.
     *
     * @return the unpruned encoded scalar value, or an empty Optional if the
     *     scalar may not be extracted
     */
    Optional<byte[]> getScalar();
}
```

Public key interface:

```
package java.security.interfaces;

import java.math.BigInteger;
import java.security.PublicKey;

/**
 * An XEC public key is a particular point on the curve, which is represented
 * using only its u-coordinate as described in RFC 7748. A u-coordinate is an
 * element of the field of integers modulo some value that is determined by
 * the algorithm parameters. This field element is represented by a BigInteger
 * which may hold any value. That is, the BigInteger is not restricted to the
 * range of canonical field elements.
 *
 */
public interface XECPublicKey extends XECKey, PublicKey {

    /**
     * Get the u coordinate of the point.
     *
     * @return the u-coordinate, represented using a BigInteger which may hold
     *          any value
     */
    BigInteger getU();

}
```


**Key Specs**

The API provides transparent spec classes for public and private keys.

Private key spec:
(There is a bug in Jira/Markdown related to square brackets in code blocks. The code below has a backslash in the comment block as a workaround. This character can be ignored.)
```
package java.security.spec;

/**
 * An XECPrivateKeySpec object specifies an RFC 7748 private key, including the
 * curve and other algorithm parameters. The private key is an encoded scalar
 * value as described in RFC 7748. The decoding procedure defined in this RFC
 * includes an operation that forces certain bits of the key to either 1 or 0.
 * This operation is known as "pruning" or "clamping" the private key. All
 * arrays in this spec are unpruned, and implementations will need to prune
 * the array before using it in any numerical operations.
 */
public class XECPrivateKeySpec implements KeySpec {

    private final AlgorithmParameterSpec params;
    private final byte[] scalar;

    /**
     * Construct a private key spec using the supplied parameters and
     * encoded scalar value.
     *
     * @param params the algorithm parameters
     * @param scalar the unpruned encoded scalar value. This array is copied
     *               to protect against subsequent modification.
     *
     * @throws NullPointerException if {@code params} or {@code scalar}
     *                              is null.
     */
    public XECPrivateKeySpec(AlgorithmParameterSpec params, byte[] scalar) {
        Objects.requireNonNull(params, "params must not be null");
        Objects.requireNonNull(scalar, "scalar must not be null");

        this.params = params;
        this.scalar = scalar.clone();
    }

    /**
     \* Get the algorithm parameters that define the curve and other settings.
     *
     * @return the algorithm parameters
     */
    public AlgorithmParameterSpec getParams() {
        return params;
    }

    /**
     * Get the scalar value encoded as an unpruned byte array. A new copy of
     * the array is returned each time this method is called.
     *
     * @return the unpruned encoded scalar value
     */
    public byte[] getScalar() {
        return scalar.clone();
    }
}
``` 

Public key spec:

```
package java.security.spec;

import java.math.BigInteger;

/**
 * An XECPublicKeySpec object specifies an RFC 7748 public key, including the
 * curve and other algorithm parameters. The public key is a particular point
 * on the curve, which is represented using only its u-coordinate as described
 * in RFC 7748. A u-coordinate is an element of the field of integers modulo
 * some value that is determined by the algorithm parameters. This field
 * element is represented by a BigInteger which may hold any value. That is,
 * the BigInteger is not restricted to the range of canonical field elements.
 *
 */
public class XECPublicKeySpec implements KeySpec {

    private final AlgorithmParameterSpec params;
    private final BigInteger u;

    /**
     * Construct a public key spec using the supplied parameters and
     * u coordinate.
     *
     * @param params the algorithm parameters
     * @param u the u-coordinate of the point, represented using a BigInteger
     *          which may hold any value
     *
     * @throws NullPointerException if {@code params} or {@code u}
     *                              is null.
     */
    public XECPublicKeySpec(AlgorithmParameterSpec params, BigInteger u) {
        Objects.requireNonNull(params, "params must not be null");
        Objects.requireNonNull(u, "u must not be null");

        this.params = params;
        this.u = u;
    }

    /**
     * Get the algorithm parameters that define the curve and other settings.
     *
     * @return the parameters
     */
    public AlgorithmParameterSpec getParams() {
        return params;
    }

    /**
     * Get the u coordinate of the point.
     *
     * @return the u-coordinate, represented using a BigInteger which may hold
     *          any value
     */
    public BigInteger getU() {
        return u;
    }
}
```
Comments
Moving to approved. Please add @since tags before pushing for the new classes if they are not already present.
24-01-2018

As a code review comment, a more up-to-date way to code a null check like if (u == null) { throw new NullPointerException("u is null"); } is to use java.util.Objects, such as Object.requireNonNull���(u, "u is null"); Moving to Provisional.
10-01-2018