On 8/10/2017 7:36 PM, Xuelei Fan wrote:
Hi Michael,
Good points! See comments inlines.
On 8/10/2017 3:20 PM, Michael StJohns wrote:
Instead of converting, I was thinking about mapping. E.g. Montgomery
A and B matches the A and B of the curve. But the "x" of the
Montgomery point is just the "x" of the ECPoint with the "y" left as
null. For Edwards, it looks like you would map "d" to A. For [3] I'd
map "r" to A. I'd leave B as null for both- no reason to throw an
unsupported exception as the code generally has a clue about what
types of keys they're dealing with (or we provide a marker so they
can figure it out).
The conversion in and out for points is a conversion from little
endian to big endian and vice versa, but that only has to be done if
you're importing or exporting a parameter set and that's an
implementation issue not an API issue.
Basically, all the math is BigIntegers under the hood. The
curve25519 RFC specifies an implementation that's little endian, but
the actual math is just math and things like the public key is really
just a BigInteger.
Old code would just continue to work - since it would not be using
the new curves. New code would have to look for the curve type
marker (e.g. the ECField) if there was the possibility of confusion.
I understand your points. The mapping may be confusing to application
developers, but no problem for new codes if following the new coding
guideline. I'm not very sure of the old code, for similar reason to
use the converting solution.
For example, an Edwards curve form of the SubjectPublicKeyInfo field
in a X.509 cert is parsed as X509EncodedKeySpec, and "EC" KeyFactory
is used to generate the ECPublicKey. The algorithm name of the
ECPublicKey instance is "EC", and the parameter is an instance of
ECParameterSpec. Somehow, the ECPublicKey leave the key generation
environment, and the curve OID is unknown in the new environment.
Then the public could be used improperly. In the past, it's fine as
the only supported form is Weierstrass form, there is no need to tell
the curve forms in a crypto implementation. However, when a new form
is introduces, identify the EC form of a key is an essential part for
the following crypto operations. Old providers or codes may not be
able to tell the form, as may result in compatibility issues.
I don't think any of this is an issue. An X509EncodedKeySpec for
either type of key has a id-ecPublicKey OID identifying it (embedded in
the SubjectPublicKeyInfo encoding). In the key body, there's the
EcpkParameters structure which is a 'namedCurve' which consists of an
OID. The curve OIDs for 25519 and 447 are different than any of the
Weiserstrass keys. When the KeyFactory factory implementation reads
the byte stream its going to build a JCA ECPublicKey that matches the
OID AND that's a concrete ECPublicKey class of the key factory provider.
If the factory implementation doesn't understand the oid, then the
provider throws an error. I forget which one.
The concrete class for the ECPublic key is specific to the provider.
Some providers may support the new key forms, some may not. There's no
guarantee (and there never has been a guarantee) that an ECPublic key
from one provider can be used with another provider (e.g. PKCS11
provider vs a software provider) - you have to convert the key into a
keyspec and then run the factory method on it.
So I don't think there's anything we have to worry about here - no
violation of the API contract as far as I can tell.
(As a more complete example - consider what happens when you have an F2M
EC provider and an Fp EC provider both generating public keys and
encoding them. Neither provider can decode the other's encoded key
because they don't have the OIDs and the parameter sets).
However, I'm not very sure of the compatibility impact (see above).
3. Where we are not now?
Using named curves is popular. There is a ECGenParameterSpec class
using named curves:
ECGenParameterSpec ecgp =
new ECGenParameterSpec(secp256r1);
KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC");
kpg.initialize(ecpg);
KeyPair kp = kpg.generateKeyPair();
ECPublicKey pubKey = (ECPublicKey)kp.getPublic();
String keyAlgorithm = pubKey.getAlgorithm(); // "EC"
However, it is used for key generation only. Once the keys are
generated, there is no public API to know the name of the curve in
ECKey. ECKey.getAlgorithm() will return "EC" only. If it is
required to known whether a key is of a named curve, the solution is
not straightforward.
This ties back to "getEncoded()" representations. Under the hood, if
you do a getEncoded() there's a "which name does this parameter set
match up to" search which checks various tables for an OID and uses
that in an X.509 SPKI output object. On input, the table lookup has
to see whether or not it understands the curve OID (or the key type
OID - depending).
To deal with this without having to modify the internal parameter
tables I currently match keys against parameter sets that have known
OIDs.
I see.
4. A general proposal
Support named curves could be a solution for #2 and #3 concerns
above. For named curves, the parameters are defined explicitly. So,
it is REQUIRED to have the public APIs for named curves' parameters
any more. It can be something hidden in the implementation layer.
The key pair generation may looks like:
KeyPairGenerator kpg =
KeyPairGenerator.getInstance("ECWithSecp256k1");
KeyPair kp = kpg.generateKeyPair();
PublicKey pubKey = kp.getPublic();
String keyAlgorithm = pubKey.getAlgorithm(); // "ECWithSecp256k1"
As no explicit parameters is required, the EllipticCurve issue for
Edwards curve form and Montgomery curve form in #2 is not a issue
any more here.
The compatibility impact is limited as the name "ECWithSecp256k1" is
not used in the past, and the Weierstrass form APIs, like
ECKey/ECParameterSpec/EllipticCurve, are not necessarily to be used
in this solution.
The benefits: simplify the APIs for named curves (including the
Weierstrass form), and simplify the support of named curves for
Edwards curve form and Montgomery curve form.
The disadvantages: no support of arbitrary curves (no surprise as
this is a named curve solution), and new learn curve to use this new
solution.
Right now there are 3 major APIs (JCA, PKCS11 and Microsoft CSP) and
at least 4 major representational domains (Raw, PKIX, XML and JSON).
In the current situation, I can take a JCA EC Public key and convert
it to pretty much any of the other APIs or representations. For much
of the hardware based stuff (ie, smart cards), I go straight from JCA
into raw and vice versa. Assuming you left the "getEncoded()" stuff
in the API and the encoding was PKIX, I'd have to encode to PKIX,
decode the PKIX to extract the actual raw key or encode a PKIX blob
and hope that the KeyFactory stuff actually worked.
It's not just support of arbitrary keys, but the ability to convert
things without having to do multiple steps or stages.
Good point! It would be nice if transaction between two formats could
be done simply. Using X.509 encoding is doable as you said above, but
maybe there are spaces to get improvements.
I need more time to think about it. Please let me know if any one
have a solution to simplify the transaction if keeping use the
proposed named curves solution.
Your solution would probably work reasonably well for TLS or IPSEC -
but would not work well for anything else.
Properly a little bit more than TLS and IPSEC, but definitely not
everything else.
5. Can be more aggressive?
It looks amazing to support arbitrary curves for Edwards curve form
and Montgomery curve form, as JDK did for Weierstrass form. However,
because of the compatibility impact (see #2), a new set of
algorithms names, interfaces and specs may be required. It could be
overloaded if the requirements are not so strong in practice. If
arbitrary curves support is strong, it can be re-considered in the
future.
Per my understanding, supporting named curves and arbitrary curves
can be two things, and can be considered in different stages.
However, the design needs to take care of the potential conflicts
between the two solutions.
As I understand it, the JEP process takes some time and right now
proposed changes *might* make it into JDK10? Do you really want
to do multiple JEPs to handle multiple new Edwards and Montgomery
curves?
It really depends per my understanding. If I have a good idea, I
would do it all in one JEP. Otherwise, I may do it step by step so
that the high priority requirements are not delayed before I have a
mature solution to meet more requirements.
There's doing it quickly and doing it right. Not sure you can have both.
If we can hide most of this under the current EC covers, then the
implementations can just implement the plugin interface and do that now.
You are right. If everything goes smoothly, no public APIs update is
expected. It's a kind of a provider implementation job, although some
external algorithm names may be defined (like "ECWithSecp256k1" or
"XDH") for this proposal.
Yup - but that's a registry entry rather than an implementation change.
Thanks & Regards,
Xuelei