On 8/8/2017 5:00 PM, Adam Petcher wrote:
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.
The engine implementation (provider plugin) is responsible for
preventing the "common programming errors", not the API. The JCA really
doesn't have support for a type system style assurance.
<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.
Pretty much. This is why I've been pushing back so hard on "just one
more key type" style arguments.
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?
Because the hardware tends to work either very generally or very
specifically. A PKCS11 big iron HSM for example mostly doesn't do
multiple curves and requires that you just give them the curve OID, but
the JCOP smart cards work over a broad set of curves - as long as you
give them the entire curve data set. But the JCOP cards don't do ASN1
so I keep getting to have to convert raw EC points into formatted EC
public keys. One set of programs I have has to deal with at least
three different representations - all of which are appropriate at
various points.
As it stands, I find the JCOP smart cards more flexible than the PKCS11
HSMs, but the PKCS11 HSMs more able to deal with large amounts of
calculations (and generally better at keeping things secret than the
smart cards).
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.
I think I understand what you're asking. Signature needs a PublicKey.
PublicKey is an interface so you can "implement" just that interface in
your concrete class. However, you should mostly NEVER be looking at
whether or not a given key is an instance of a particular internal class
as this will break at some point. Any implementation should be checking
for public markers - either directly as an instance of ECPublicKey or
indirectly looking at the parameter set of the public key.
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?
There's somewhat of a contract that says that public keys should be
convertible. So while you might get away with hiding the class info for
a private key, you really should provide a mechanism for a common
representation of the public keys so that things like signature
verification just work across providers.
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).
You mean like "ECKey"? This is implemented by both public and private
EC keys and mostly contains the ECParameterSpec set.
Mike