On 8/8/2017 11:25 AM, Adam Petcher wrote:
Thanks for the feedback. See below for my responses.
On 8/7/2017 5:52 PM, Michael StJohns wrote:
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).
So you're expecting yet another set of APIs to cover those as well?
Rather than one API to cover all of the new curves? Seems to be a
bit short sighted.
I'm expecting that we don't need to expose curve parameters/points in
the API, so we won't need any new API for EdDSA, other than the
algorithm name. If we decide we need to expose curve parameters, then
we may want to back up and consider how EdDSA fits into this.
We'll leave this for later. But generally, the JCA is a general
interface to a set of crypto primitives modeled on just a few key
types. To go in the direction you want to go it you need to explain why
its impossible to model an elliptic curve as an elliptic curve. As I
noted, I think that the inclusion of extension of ECField is probably
all that's necessary for representing both public and private key pairs
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's still an elliptic curve. Note that there is already a worked
example here - F2m vs Fp curves.
it uses different encodings and algorithms,
From a JCA point of view, the public key gets encoded as an
SubjectPublicKeyInfo and the private key gets encoded as a PKCS8 -
that's for lots and lots of compatibility reasons. The public point
of the public key might be encoded (inside the SPKI) as little endian
OCTET STRING array vs a big endian ASN1 INTEGER, but its still just
an integer internally.
The algorithms are Key Agreement and Signature - those are at least
what JCA will see them as. The actual
KeyAgreement.getInstance("name") is of course going to be different
than KeyAgreement.getInstance("ECDH") for example.
and it has different properties.
Details please? Or do you mean that you can't use a given type of
key for both key agreement and signature?
Specifically, RFC 7748 resists (some) side-channel attacks and
invalid-point attacks. The "ECDH" algorithm in JCA (PKCS#3) does not
have these properties, so I want to make sure programmers don't get
them confused. This difference in security properties partially
motivates the use of a new algorithm name, rather than reusing "ECDH"
for RFC 7748.
There is no problem with using new names for new algorithms - especially
if the math is different. Ask the java folks to register the names as
Standard Names. That's what will be used in the
KeyAgreement.getInstance and Signature.getInstance calls.
Further, this separation will reduce the probability of programming
errors (e.g. accidentally interpreting a Weierstrass point as an RFC
Um. What? It actually won't.
This is the sort of problem I want to avoid:
KeyPairGenerator kpg = KeyPairGenerator.getInstance("ECDH");
KeyPair kp = kpg.generateKeyPair();
KeyFactory eckf = KeyFactory.getInstance("ECDH");
ECPrivateKeySpec priSpec = eckf.getKeySpec(kf.getPrivate(),
KeyFactory xdhkf = KeyFactory.getInstance("XDH");
PrivateKey xdhPrivate = xdhkf.generatePrivate(priSpec);
// Now use xdhPrivate for key agreement, which uses the wrong
algorithm and curve, and may leak information about the private key
This is setting up a strawman and knocking it down. It's already
possible to do the above with any software based key - either directly
or by pulling out the data. Creating the API as you suggest will still
not prevent this as long as I can retrieve the private value from the key.
If you want absolute protection from this - go to hardware based keys.
This sort of thing can happen if we use the existing EC spec classes
for RFC 7748 (e.g. redefining what "a" and "b" mean in EllipticCurve
when used with a Montgomery curve, leaving "y" null in ECPoint). Of
course, we can prevent it by tagging these objects and checking the
tags to make sure they are used with the correct algorithms, but I
would prefer to use separate classes (if necessary) and let the type
checker do this.
That's the responsibility of the plugin provider - not the public API.
My intention is that errors like the one above are impossible in my
Nope. See above.
We should be able to accomplish this by only using encoded key specs
(which already have checking based on OID), or by using new classes,
if this is necessary (and I hope it is not).
Nope. It doesn't work that way. Key spec's mostly don't do checking...
the checking is done by the factory classes.
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.
No. (My opinion but...) It's *hard* to add new meta classes for
keys. Just considering the EC stuff you have ECKey, ECPublicKey,
ECPrivateKey, EllipticCurve, ECPublicKeySpec, ECPrivateKeySpec,
ECPoint, ECParameterSpec, ECGenParameterSpec, EllipticCurve and
ECField (with ECFieldF2M and ECFieldF2P being the differentiator for
all of the various keys within this space).
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.
There's a difference with what you do with the public API vs what you
do with the plugin provider. Throwing away all of the
"functionality that nobody needs" will probably come back to bite
those who come later with something that looks *almost* like what you
did, but needs just one more parameter than you were kind enough to
I agree, but we have to draw the line somewhere, and I think that line
should be determined by the most common use cases. I would prefer to
put in too little, and add more to the API later when people ask for
it. Adding things to the API isn't trivial, but it is much easier than
removing things that we don't need that are complicating the API and
Adding MANY different new APIs because you haven't done the work to
harmonize them seems to be crossing the line substantially. Not
Because names and OIDs get assigned later than the curve parameters.
There are two parts to the JCA - the general crypto part and then
there's the PKIX part. For the EC stuff, they sort of overlap because
of a desire not to have to have everyone remember each of the parameter
sets (curves) and those sets are tagged by name(s) and OID. But its
still perfectly possible to do EC math on curves that were generated
elsewhere (or even with a curve where everything but the basepoint is
the same with a public curve).
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.
Strangely, this hasn't turned out all that well. There needs to be a
name, OID in the public space (primarily for the encodings and PKIX
stuff) and to be honest - you really want the parameters in public
space as well (the ECParameterSpec and its ilk) so that a given key
can be used with different providers or even to play around
internally with new curves before giving them a name.
I don't understand why we need public curve parameters to allow keys
to be used with different providers. It seems like this should work as
long as the providers all understand the OIDs or curve names. Can you
explain this part a bit more?
What you need to be able to do is to pass to an "older" provider a
"newer" curve - assuming the curve fits within the math already
implemented. There's really no good reason to implement a whole new set
of API changes just to permit a single new curve.
Related to tinkering with new curves that don't have a name: I don't
think that this is a feature that JCA needs to have. In the common use
case, the programmer wants to only use standard algorithms and curves,
and I think we should focus on that use case.
The common use case is much wider than you think it is. I find myself
using the curve parameters much more than I would like - specifically
because I use JCA in conjunction with PKCS11, HSMs and smart cards. So
no - focusing on a software only subset of things is really not the
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
I don't actually understand that statement. Keys of different
providers generally don't interoperate anyways, but you can mostly
take an "encoded" one and create a new one in a new provider via the
Keyfactory. KeySpecs provide you with a way of manually building a
key - and that turns out to be VERY necessary, especially when you're
dealing with adapting hardware modules to the JCA.
I'll admit that it is a strange statement. The only reason why I
stated this assumption is that ECPublicKey and ECPrivateKey expose a
lot of information that allows direct interoperability with opaque key
objects (that implement these interfaces). I don't know if this is
necessary/valuable, and I don't have any particular desire to allow
the same thing with RFC 7748 keys.
*sigh* Both of those classes allow for a full representation of the key
in software. But the ECPrivateKey allows for the concept of a key
handle that just references a hardware key without making the sensitive
data accessible. E.g. getS() returns null or an exception (I wish this
were better specified, but much of this is discussed in the JCA guide).
Spec's ALWAYS provide a full non-opaque representation and are a
required part of the JCA for key types.
Key.getEncoded() usually provides a full non-opaque representation (e.g.
SPKI or PKCS8 or just a byte array for SecretKeys)
These two assumptions greatly simplify the API. We won't need
classes that mirror ECParameterSpec, EllipticCurve, ECPoint,
ECField, ECPublicKey, etc. for X25519/X448.
That assumption holds only if your various other assumptions hold. My
opinion is that they probably don't. (BTW - I'm pretty sure, given
that every single asymmetric JCA crypto api takes a PublicKey or
PrivateKey you're going to need to mirror those classes at least;
you'll also need a ParameterSpec and a GenParameterSpec class with
whatever underlying supporting classes are required to deal with
I agree with the second part of your parenthetical statement, but I
need more information about the first. It sounds like what you are
saying is that I will need something like XDHPublicKey and
XDHPrivateKey in java.security.interfaces. Can you tell me why? What
is it that we can't do without these interfaces?
The method signatures for Signature.initSign(PrivateKey
privateKey[,SecureRandom random]), Signature.initVerify(PublicKey
publicKey) should give you a clue. E.g. the calls to the JCA provider
classes require that you submit either a PublicKey or a PrivateKey. So
you're going to need a concrete class with a subinterface that matches
the key type needed by the signature instance that is a sub interface
for PublicKey or PrivateKey.
KeyAgreement requires a "Key" (both the public and private keys are
passed in a Key's).
KeyPairGenerator.init requires an AlgorithmParameter or a key size in bits.
KeyFactory requires a KeySpec.
Basically, you can't sign, perform a key agreement, generate a random
key pair or generate a key from an encoded value without these classes.
It's scary that you asked this question because it implies perhaps a
need to spend a bit more time looking at the JCA and how its structured.
Now that the motivation and assumptions are out of the way, here is
a description of the proposed JCA API:
I'd suggest getting agreement on the above before proceeding.