Summary
-------
Add support for the EdDSA signature algorithm in the JCA API.
Problem
-------
EdDSA is a new signature algorithm that we want to support in the JCA API. This algorithm uses elliptic curves, but it represents curves, points, and keys in a way that is different than the existing mechanisms in ECDSA, ECDH, and XDH. So we need to define new API classes and interfaces in order to support this new algorithm and its keys.
Solution
--------
Define a new set of API classes and interfaces that are mostly independent from the existing API for ECDSA and other elliptic curve algorithms. Define standard names for algorithms and curve parameter sets associated with EdDSA.
Example API usage:
// example: generate a key pair and sign
KeyPairGenerator kpg = KeyPairGenerator.getInstance("Ed25519");
KeyPair kp = kpg.generateKeyPair();
Signature sig = Signature.getInstance("Ed25519");
// mode is Ed25519ph with a context
byte[] context = ...
EdDSAParameterSpec edParamSpec = new EdDSAParameterSpec(true, context);
sig.setParameter(edParamSpec);
sig.initSign(kp.getPrivate());
sig.update(msg);
byte[] s = sig.sign();
// example: use KeyFactory to construct a public key
KeyFactory kf = KeyFactory.getInstance("EdDSA");
boolean xOdd = ...
BigInteger y = ...
NamedParameterSpec paramSpec = new NamedParameterSpec("Ed25519");
EdECPublicKeySpec pubSpec = new EdECPublicKeySpec(paramSpec,
new EdPoint(xOdd, y));
PublicKey pubKey = kf.generatePublic(pubSpec);
The API uses "EdDSA" to refer to the family of signature scheme defined in RFC 8032. "EdEC" refers to the underlying elliptic curve operations and representations in a way that is not specific to any particular cryptosystem. In the API, key interfaces and key spec classes are "EdEC" so they can be reused with other cryptosystems (that may not be signature schemes) based on RFC 8032. This EdEC/EdDSA arrangement mirrors the existing EC/ECDH API classes/interfaces. This API does not standardize "EdEC" algorithm names, so the programmer must use "EdDSA" (or a more specific name like "Ed25519") 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 `Signature`, `KeyPairGenerator`, `KeyFactory`, and `AlgorithmParameters`:
Algorithm Name | Description
--- | ---
EdDSA | The EdDSA signature scheme family as defined in RFC 8032
Ed25519| The Ed25519 signature scheme family based on Curve25519 as defined in RFC 8032
Ed448 | The Ed448 signature scheme family based on Curve448 as defined in RFC 8032
A correct implementation of these algorithms must adhere to RFC 8032 exactly, and may only take liberty where allowed by that spec through use of language such as "MAY" or "SHOULD." An implementation that supports some curve SHOULD implement the entire family of signature schemes for that curve. For example, an implementation that supports Ed25519 SHOULD support the "pure" mode as well as Ed25519ctx and Ed25519ph. None of these algorithm names are mandatory, and an implementation MAY implement any subset of the curves.
The EdDSA API provides no way to specify arbitrary curve/field parameters for use with EdDSA. This feature is not commonly implemented/used in ECC, and it is even less desirable due to the nature of EdDSA. The existing `NamedParameterSpec` class will be used to specify curve parameters. The following standard names will be added for use with this class:
Parameters Name | Description
--- | ---
Ed25519 | Elliptic curve cryptography using Curve25519 in twisted Edwards form as defined in RFC 8032
Ed448 | Elliptic curve cryptography using Curve448 in twisted Edwards form as defined in RFC 8032
###API###
For the most part, the spec classes and interfaces for EdDSA mirror those used by the existing XDH and ECC APIs.
**Parameter Specs**
The existing `NamedParameterSpec` class is used to specify curve/key parameters. This class holds static members for the standard parameter spec names. Two new static members are added to this class for the standard EdDSA parameter sets:
```
public class NamedParameterSpec implements AlgorithmParameterSpec {
(existing definitions)
/**
* The Ed25519 parameters
*/
public static final NamedParameterSpec ED25519
= new NamedParameterSpec("Ed25519");
/**
* The Ed448 parameters
*/
public static final NamedParameterSpec ED448
= new NamedParameterSpec("Ed448");
(existing definitions)
}
```
In addition to curve parameters, the API needs a way to specify the parameters of the signature mode. These parameters are: (1) whether the message should be "prehashed" and (2) an arbitrary context value that is bound to the signature. Signature parameters are specified with the new class `EdDSAParameterSpec`. This class is supplied to the `Signature` object using the `setParameter` method. If no `EdDSAParameterSpec` is supplied, then the default value is (1) not prehashed, and (2) no context. `EdDSAParameterSpec` does not include curve parameters, because these parameters are supplied in the key during `initSign` or `initVerify`.
```
package java.security.spec;
import java.util.Objects;
import java.util.Optional;
/**
* A class used to specify EdDSA signature and verification parameters. All
* algorithm modes in <a href="https://tools.ietf.org/html/rfc8032">RFC 8032:
* Edwards-Curve Digital Signature Algorithm (EdDSA)</a> can be specified using
* combinations of the settings in this class.
*
* <ul>
* <li>If prehash is true, then the mode is Ed25519ph or Ed448ph</li>
* <li>Otherwise, if a context is present, the mode is Ed25519ctx or Ed448</li>
* <li>Otherwise, the mode is Ed25519 or Ed448</li>
* </ul>
*
* @since 15
*/
public final class EdDSAParameterSpec implements AlgorithmParameterSpec {
private final boolean prehash;
private final byte[] context;
/**
* Construct an EdDSAParameterSpec by specifying whether the prehash mode
* is used. No context is provided so this constructor specifies a mode
* in which the context is absent. Note that this mode may be different
* than the mode in which an empty array is used as the context.
*
* @param prehash whether the prehash mode is specified
*/
public EdDSAParameterSpec(boolean prehash) {
this.prehash = prehash;
this.context = null;
}
/**
* Construct an EdDSAParameterSpec by specifying a context and whether the
* prehash mode is used. The context may not be null, but it may be an
* empty array. The mode used when the context is an empty array may not be
* the same as the mode used when the context is absent.
*
* @param prehash whether the prehash mode is specified
* @param context the context that is bound to the signature
*
* @throws NullPointerException if context is null
*/
public EdDSAParameterSpec(boolean prehash, byte[] context) {
Objects.requireNonNull(context, "context may not be null");
this.prehash = prehash;
this.context = context.clone();
}
/**
* Get whether the prehash mode is specified.
*
* @return whether the prehash mode is specified
*/
public boolean isPrehash() {
return prehash;
}
/**
* Get the context that should be used, if any
*
* @return the context that is bound to the signature
*/
public Optional<byte[]> getContext() {
if (context == null) {
return Optional.empty();
} else {
return Optional.of(context.clone());
}
}
}
```
**Key Interfaces**
There are interfaces for public and private keys, along with a superinterface that allows access to algorithm parameters. NamedParameterSpec are returned to identify the defined curves. Additional or arbitrary curves are not defined with this interface. It is not expected support will be needed beyond defined curves, but if it were to happen, parameters can be subclassed under NamedParameterSpec.
```
package java.security.interfaces;
import java.security.spec.NamedParameterSpec;
/**
* An interface for an elliptic curve public/private key as defined by
* <a href="https://tools.ietf.org/html/rfc8032">RFC 8032: Edwards-Curve
* Digital Signature Algorithm (EdDSA)</a>. These keys are distinct from the
* keys represented by {@code ECKey}, and they are intended for use with
* algorithms based on RFC 8032 such as the EdDSA {@code Signature} algorithm.
* This interface allows access to the algorithm parameters associated with
* the key.
*
* @since 15
*/
public interface EdECKey {
/**
* Returns the algorithm parameters associated
* with the key.
*
* @return the associated algorithm parameters
*/
NamedParameterSpec getParams();
}
```
Private key interface:
```
package java.security.interfaces;
import java.security.PrivateKey;
import java.util.Optional;
/**
* An interface for an elliptic curve private key as defined by
* <a href="https://tools.ietf.org/html/rfc8032">RFC 8032: Edwards-Curve
* Digital Signature Algorithm (EdDSA)</a>. These keys are distinct from the
* keys represented by {@code ECPrivateKey}, and they are intended for use
* with algorithms based on RFC 8032 such as the EdDSA {@code Signature}
* algorithm.
*
* An EdEC private key is a bit string. This interface only supports bit
* string lengths that are a multiple of 8, and the key is represented using
* a byte array.
*
* @since 15
*/
public interface EdECPrivateKey extends EdECKey, PrivateKey {
/**
* Get the byte array representing the private key. This method may return
* an empty Optional if the implementation is not willing to produce
* the private key value.
*
* @return the private key as a byte array
*/
Optional<byte[]> getBytes();
}
```
Public key interface and points:
```
package java.security.interfaces;
import java.security.PublicKey;
import java.security.spec.EdECPoint;
/**
* An interface for an elliptic curve public key as defined by
* <a href="https://tools.ietf.org/html/rfc8032">RFC 8032: Edwards-Curve
* Digital Signature Algorithm (EdDSA)</a>. These keys are distinct from the
* keys represented by {@code ECPublicKey}, and they are intended for use with
* algorithms based on RFC 8032 such as the EdDSA {@code Signature} algorithm.
*
* An EdEC public key is a point on the curve, which is represented using an
* EdECPoint.
*
* @since 15
*/
public interface EdECPublicKey extends EdECKey, PublicKey {
/**
* Get the point representing the public key.
*
* @return the EdECPoint representing the public key
*/
EdECPoint getPoint();
}
```
```
package java.security.spec;
import java.math.BigInteger;
import java.util.Objects;
/**
* An elliptic curve point used to specify keys as defined by
* <a href="https://tools.ietf.org/html/rfc8032">RFC 8032: Edwards-Curve
* Digital Signature Algorithm (EdDSA)</a>. These points are distinct from the
* points represented by {@code ECPoint}, and they are intended for use with
* algorithms based on RFC 8032 such as the EdDSA {@code Signature} algorithm.
*
* An EdEC point is specified by its y-coordinate value and a boolean that
* indicates whether the x-coordinate is odd. The y-coordinate is an
* element of the field of integers modulo some value p that is determined by
* the algorithm parameters. This field element is represented by a BigInteger,
* and implementations that consume objects of this class may reject integer
* values which are not in the range [0, p).
*
* @since 15
*/
public final class EdECPoint {
private final boolean xOdd;
private final BigInteger y;
/**
* Construct an EdECPoint
*
* @param xOdd whether the x-coordinate is odd
* @param y the y-coordinate, represented using a BigInteger
*
* @throws NullPointerException if {@code y} is null.
*/
public EdECPoint(boolean xOdd, BigInteger y) {
Objects.requireNonNull(y, "y must not be null");
this.xOdd = xOdd;
this.y = y;
}
/**
* Get whether the x-coordinate of the point is odd
*
* @return a boolean indicating whether the x-coordinate is odd
*/
public boolean isXOdd() {
return xOdd;
}
/**
* Get the y-coordinate of the point.
*
* @return the y-coordinate, represented using a BigInteger
*/
public BigInteger getY() {
return y;
}
}
```
**Key Specs**
The API provides transparent spec classes for public and private keys.
Private key spec:
```
package java.security.spec;
import java.util.Objects;
/**
* A class representing elliptic curve private keys as defined in
* <a href="https://tools.ietf.org/html/rfc8032">RFC 8032: Edwards-Curve
* Digital Signature Algorithm (EdDSA)</a>, including the curve and other
* algorithm parameters. The private key is a bit string represented using
* a byte array. This class only supports bit string lengths that are a
* multiple of 8.
*
* @since 15
*/
public final class EdECPrivateKeySpec implements KeySpec {
private final NamedParameterSpec params;
private final byte[] bytes;
/**
* Construct a private key spec using the supplied parameters and
* bit string.
*
* @param params the algorithm parameters
* @param bytes the key as a byte array. This array is copied
* to protect against subsequent modification.
*
* @throws NullPointerException if {@code params} or {@code bytes}
* is null.
*/
public EdECPrivateKeySpec(NamedParameterSpec params, byte[] bytes) {
Objects.requireNonNull(params, "params must not be null");
Objects.requireNonNull(bytes, "bytes must not be null");
this.params = params;
this.bytes = bytes.clone();
}
/**
* Get the algorithm parameters that define the curve and other settings.
*
* @return the algorithm parameters
*/
public NamedParameterSpec getParams() {
return params;
}
/**
* Get the byte array representing the private key. A new copy of the array
* is returned each time this method is called.
*
* @return the private key as a byte array
*/
public byte[] getBytes() {
return bytes.clone();
}
}
```
Public key spec:
```
package java.security.spec;
import java.util.Objects;
/**
* A class representing elliptic curve public keys as defined in
* <a href="https://tools.ietf.org/html/rfc8032">RFC 8032: Edwards-Curve
* Digital Signature Algorithm (EdDSA)</a>, including the curve and other
* algorithm parameters. The public key is a point on the curve, which is
* represented using an EdECPoint.
*
* @since 15
*/
public final class EdECPublicKeySpec implements KeySpec {
private final AlgorithmParameterSpec params;
private final EdECPoint point;
/**
* Construct a public key spec using the supplied parameters and
* point.
*
* @param params the algorithm parameters
* @param point the point representing the public key
*
* @throws NullPointerException if {@code params} or {@code point}
* is null.
*/
public EdECPublicKeySpec(AlgorithmParameterSpec params, EdECPoint point) {
Objects.requireNonNull(params, "params must not be null");
Objects.requireNonNull(point, "point must not be null");
this.params = params;
this.point = point;
}
/**
* Get the algorithm parameters that define the curve and other settings.
*
* @return the parameters
*/
public NamedParameterSpec getParams() {
return params;
}
/**
* Get the point representing the public key
*
* @return the EdECPoint representing the public key
*/
public EdECPoint getPoint() {
return point;
}
}
```
Rejected Alternative Solutions
--------
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.
For example, consider an ECPrivateKeySpec that describes an ECDSA private key. ECPrivateKeySpec contains an ECParameterSpec that includes all of the curve/field parameters including the curve coefficients a and b. For ECDSA, these are the coefficients of a Weierstrass curve y^2 = x^3 + ax + b. For EdDSA, we could use the same class and interpret a and b as the coefficients of a twisted Edwards curve ax^2 + y^2 = 1 + bx^2y^2. Perhaps we would add a field to the ECParameterSpec to indicate how a and b should be interpreted, so that EdDSA implementations could reject ECDSA keys, and vice-versa.
The above solution is problematic when EdDSA private keys are given to an existing ECDSA implementation that has not been modified to check the new field indicating how coefficients should be interpreted. The ECDSA implementation could interpret the coefficients as Weierstrass curve parameters, and perform the signing operation using that curve, instead of the intended Edwards curve. There is no reason to expect that this misinterpreted Weierstrass curve is secure, and the resulting signature could leak information about the private key. There is no reason why a program should supply an EdDSA key to an ECDSA implementation, but it could easily be done inadvertently if they have the same type.