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.


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.


Reply via email to