On 8/8/2017 12:50 PM, Michael StJohns wrote:
Further, this separation will reduce the probability of programming
errors (e.g. accidentally interpreting a Weierstrass point as an
RFC 7748 point).
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(),
ECPrivateKeySpec.class);
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.
The goal is the prevention of common programming errors that lead to
security issues, not absolute protection like you would get from
hardware crypto. More like the kind of assurance you get from a type
system.
<snip>
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?
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).
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.
Okay, thanks. If I am reading this right, this feature supports
interoperability with providers that don't know about a specific curve
name/OID, but support all curves within some family. Without a way to
express the curve parameters in JCA, you would resort to using some
provider-specific parameter spec, which would be unfortunate.
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
right approach.
I actually would have expected hardware crypto to have *less* support
for arbitrary curves, and so this issue would come up more with software
implementations. Why does this come up so frequently in hardware?
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
KeyFactory's)
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.
Does my Signature service need to support a public subinterface of (for
example) PublicKey? At a minimum, I need a concrete class that
implements PublicKey. This class could be entirely internal to my
provider. In Signature.initVerify(PublicKey), I can check whether the
provided key is an instance of my internal concrete class, and fail
otherwise. In this circumstance, my concrete key class doesn't need to
implement any public subinterface of PublicKey.
The above arrangement basically works, but is it okay? Of course,
objects of my concrete class could not be used by other providers,
because they don't know how to interpret them. Is there some requirement
or expectation of interoperability with other providers that I am missing?
Another option to consider is that we don't have subinterfaces for RFC
7748 public/private keys, but rather we use some common subinterface
that provides enough information (e.g. the encoded number and the curve
parameters).
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.
Later, Mike