Anyone have any additional thoughts on this?
I think the most significant item we need to discuss is the extent to
which JCA should allow curve parameters for RFC 7748/8032 to be
specified over the API. Does anyone know of a particular use case (that
we haven't discuss already) that would require a provider to support
arbitrary curves? Any other arguments for or against this feature?
On 8/7/2017 4:37 PM, Adam Petcher wrote:
I'm working on the Java implementation of RFC 7748 (Diffie-Hellman
with X25519 and X448). I know some of you have been anxious to talk
about how this would fit into JCA, and I appreciate your patience
while I learned enough about JCA and existing crypto implementations
to develop this API proposal. This API/design proposal is for RFC 7748
only, and it does not include the API for RFC 8032 (EdDSA). Of course,
I expect many of the decisions that we make for RFC 7748 will also
impact RFC 8032.
First off, I think it is important to separate RFC 7748 from the
existing ECDH API and implementation. RFC 7748 is a different
standard, it uses different encodings and algorithms, and it has
different properties. Further, this separation will reduce the
probability of programming errors (e.g. accidentally interpreting a
Weierstrass point as an RFC 7748 point). So I propose that we use
distinct algorithm names for RFC 7748, and that we don't use any of
the existing EC classes like EllipticCurve and ECPoint with RFC 7748.
We can achieve this separation without duplicating a lot of code if we
start with some simplifying assumptions. My goal is to remove
functionality that nobody needs in order to simplify the design and
API. If I am simplifying away something that you think you will need,
please let me know.
A) We don't need to expose actual curve parameters over the API.
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.
B) We don't need direct interoperability between different providers
using opaque key representations. We can communicate with other
providers using X509/PKCS8 encoding, or by using KeyFactory and key
specs.
These two assumptions greatly simplify the API. We won't need classes
that mirror ECParameterSpec, EllipticCurve, ECPoint, ECField,
ECPublicKey, etc. for X25519/X448.
Now that the motivation and assumptions are out of the way, here is a
description of the proposed JCA API:
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 for EC public keys
and private keys. An RFC 7748 implementation can use the existing
classes X509EncodedKeySpec and PKCS8EncodedKeySpec for public and
private key specs, respectively.
4) There will be no interfaces in java.security.interfaces for RFC
7748 public/private keys. Public/private key implementation classes
will implement java.security.PublicKey and java.security.PrivateKey,
which allows access to their encoded representations.
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.
2) The KeyFactory for RFC 7748 will support translation to/from opaque
keys and X509EncodedKeySpec/PKCS8EncodedKeySpec.
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");
X509EncodedKeySpec pubSpec = ...
PublicKey pubKey = kf.generatePublic(pubSpec);
KeyAgreement ka = KeyAgreement.getInstance("XDH");
ka.init(kp.getPrivate());
ka.doPhase(pubKey, true);
byte[] secret = ka.generateSecret();
One thing that is missing from the "core" API proposal is a way to
easily produce a public/private key from an encoded numeric value. Of
course, it's possible to put this value into a complete encoded
X509/PKCS8 key, but that is not very convenient. Perhaps we can add
another key spec class (that holds e.g. an AlgorithmParameterSpec and
a byte array) to make this easier, but I don't know how valuable this
would be.
I appreciate any feedback the experts on the mailing list may have for
me. Please let me know what you think.