Okay, let me go back to the beginning with some slightly weaker assumptions, and a slightly modified design. I've attempted to incorporate all the feedback I've received so far, but if you feel like I missed something, please let me know.

Assumptions:

A) We don't need to expose curve domain parameters over the API in the initial design/implementation. Curves can be specified using names (e.g. "X25519") or OIDs. The underlying implementation will likely support arbitrary Montgomery curves, but the JCA application will only be able to use the supported named curves. Support for arbitrary curve parameters in the API is something that can be considered as a separate feature in the future. B) We don't need specs and interfaces that are specific to RFC 7748 public/private keys. To allow interoperability between providers, and to make it easy to specify key values, we can use more general purpose spec classes and interfaces that can be reused by multiple algorithms. In particular, we should be able to reuse these interfaces/classes between RFC 7748 and RFC 8032.

Here is a high-level description of the proposed JCA API. A more detailed description will be provided during the JEP review.

1) The string "XDH" will be used in getInstance() to refer to all services related to RFC 7748 (KeyAgreement, KeyFactory, KeyPairGenerator, etc). This is a departure from the ECDH API that used "EC" for key generation (shared with ECDSA) and "ECDH" for KeyAgreement, and makes the RFC 7748 API more like "DiffieHellman" and other algorithms that use the same name for all services. 2) The new class java.security.spec.NamedParameterSpec (which implements AlgorithmParameterSpec) will be used to specify curves for RFC 7748. This class has a single String member which holds the name of the curve ("X25519" or "X448"). This parameter spec class can be reused by other crypto algorithms that similarly identify parameter sets using names (e.g. FFDHE3072 in DiffieHellman). This new class can be inserted into the hierarchy above ECGenParameterSpec. 3) There will be no classes in java.security.spec specific to RFC 7748 public keys and private keys. A new class called ByteArrayKeySpec will be added to java.security.spec. This class will hold an algorithm name String, an AlgorithmParameterSpec, and a byte array containing the raw key bytes. This class is suitable for keys in RFC 7748, RFC 8032, and possibly other algorithms. ByteArrayKeySpec can be viewed as a generalization of SecretKeySpec, and it will be used in a similar manner. The existing classes X509EncodedKeySpec and PKCS8EncodedKeySpec can also be used for RFC 7748 public and private key specs, respectively. 4) There will be no interfaces in java.security.interfaces specific to RFC 7748 public/private keys. A new interface called ByteArrayKey will be added to java.security.interfaces. This interface will expose an AlgorithmParameterSpec and a byte array containing the raw key value. Implementations of keys in RFC 7748, 8032 (and possibly other algorithms) can implement this interface to allow other providers to access the required information about the keys. Implementations can also achieve interoperability by using encoded representations of keys.

Here is how the API will be implemented in the SunEC provider:

1) The public key and private key implementation classes will extend sun.security.ec.X509Key and sun.security.ec.PKCS8Key, respectively. This is similar to ECPublicKeyImpl and ECPrivateKeyImpl. They will also both implement ByteArrayKey. 2) The KeyFactory for RFC 7748 will support translation from opaque keys and ByteArrayKey to X509EncodedKeySpec/PKCS8EncodedKeySpec and ByteArrayKeySpec (and vice-versa).

Example code:

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

KeyFactory kf = KeyFactory.getInstance("XDH");
byte[] rawKeyBytes = ...
ByteArrayKeySpec pubSpec = new ByteArrayKeySpec(
"XDH",
   new NamedParameterSpec("X25519"),
   rawKeyBytes);
PublicKey pubKey = kf.generatePublic(pubSpec);

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

Additional notes:
1) The ability to specify curve domain parameters over the API is a useful feature, and this design accommodates the addition of this feature in the future. Both ByteArrayKeySpec and ByteArrayKey hold an AlgorithmParameterSpec rather than a more concrete type. In addition to using NamedParameterSpec, the parameters could be specified using some other type that holds domain parameters.

2) Applications that need to choose their behavior based on the type of (for example) public key can do so for RFC 7748 keys by calling Key::getAlgorithm() to determine the algorithm and then using ByteArrayKey to extract the required information.

3) This design supports Xuelei's general EC proposal[1]. We can use names like "XDHWithX25519" or perhaps simply "X25519" to specify the "XDH" algorithm initialized with the "X25519" NamedParameterSpec.

[1] http://mail.openjdk.java.net/pipermail/security-dev/2017-August/016194.html

Reply via email to