Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8189806

Elliptic Curves for Security in Crypto

    Details

    • Type: CSR
    • Status: Closed
    • Priority: P3
    • Resolution: Approved
    • Fix Version/s: 11
    • Component/s: security-libs
    • Labels:
      None
    • Subcomponent:
    • Compatibility Kind:
      behavioral
    • Compatibility Risk:
      minimal
    • Compatibility Risk Description:
      Hide
      The only change to the existing API is a refactoring of java.security.spec.ECGenParameterSpec to insert a new class above it in the hierarchy. This change can only break behavioral compatibility in cases where the class hierarchy is examined using reflection.
      Show
      The only change to the existing API is a refactoring of java.security.spec.ECGenParameterSpec to insert a new class above it in the hierarchy. This change can only break behavioral compatibility in cases where the class hierarchy is examined using reflection.
    • Interface Kind:
      Java API
    • Scope:
      SE

      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;
          }
      }

        Attachments

          Issue Links

            Activity

              People

              • Assignee:
                apetcher Adam Petcher (Inactive)
                Reporter:
                xuelei Xue-Lei Fan
                Reviewed By:
                Xue-Lei Fan
              • Votes:
                0 Vote for this issue
                Watchers:
                4 Start watching this issue

                Dates

                • Created:
                  Updated:
                  Resolved: