I'd have to agree about PKCS 11.
One more thing about the PQC KEMs - the KDF step is built in. As you've
mentioned, previously there's been a lot of possible combinations with
key agreement, with PQC KEMs this has changed (of course, you could
still use a KDF too, but the original reasons for doing so no longer apply).
Regards,
David
On 21/8/22 13:52, Michael StJohns wrote:
On 8/20/2022 2:08 PM, David Hook wrote:
Hi Michael,
I don't know anything about bureaucrats, I am an engineer. You may
need to consult someone else on bureaucrats.
I apologize for my apparent deficiencies in this area, but would you
mind explaining how Cipher.wrap() is either supposed to take a public
key and create an encapsulation based on it and return a secret key
implicitly in one clean move, or why it even makes sense to do so.
The method was never conceived as providing the functionality for
what a KEM actually does, and when I did the initial PKCS11
implementation at Eracom in the late 90's and the team at Sun added
the wrap/unwrap functions to support it, this is definitely not was
intended either - it was for explicit key wrapping based on the key
that was passed to Cipher.init().
First - PKCS11 is a 40 year old API that probably needs to be
retired. I spent the better part of 2 years working with the PKCS11
Oasis group trying to get them to properly support master secrets and
KDFs and failed utterly. You should not use PKCS11 as an example
that the JCE should use as a goal.
At the base, a java class is a collection of objects. A Cipher object
Let's build a non-parameterized ECIES-KEM which implicitly uses AES256
bit keys to key a GCM cipher, and a KDF based on SP800-108 counter
mode with SHA256 as the underlying hash, and with a well known label
and context for the KDF since there is a new key for every wrap.
1) Implement CipherSpi -
public class EciesKemCipher extends CipherSpi {
private KeyAgreement ka;
private Cipher gcm;
private KeyPair kp;
private KeyPairGenerator kg;
EciesKemCipher() {
ka = KeyAgreement.getInstance("ECDH");
kpg = KeyPairGenerator.getInstance ("EC");
gcm = Cipher.getInstance ("AES/GCM/NoPadding");
}
// implement a single example
@override
protected void engineInit (int opMode, Key key, SecureRandom
dontcare) {
switch (opMode) {
Cipher.MODE_WRAP:
initWrap((ECPublicKey) key);
break;
default:
// unimpl
}
}
private void initWrap (ECPublicKey k) {
ECParameterSpec spec = k.getParams();
kpg.initialize(spec);
kp = kpg.genKeyPair();
ka.init (kp.getPrivate());
ka.doPhase (k, true);
byte[] sharedSecret = ka.generateSecret();
byte[] keyStream = kdf(sharedSecret, 32 + 12); // output 44
bytes for Key and IV
SecretKeySpec gcmKey = new SecretKeySpec (keyStream, 0, 32,
"AES");
IvParameterSpec gcmIv = new IVParameterSpec(keyStream, 32, 12);
gcm.init (Cipher.MODE_ENCRYPT, gcmKey, gcmIv);
// all ready to go
}
protected byte[] engineWrap (Key k) {
ByteBuffer outData = ByteBuffer.allocate
(k.getEncoded().length + kp.getPublic().getEncoded().length) + 16;
// Place a copy of the ephemeral public key I generated in
init here for the use of the receiver.
outData.put (kp.getEncoded());
// One s
outData.put (gcm.doFinal(k.getEncoded());
outData.flip();
byte[] result = outdata.remaining();
outData.get(result);
// kp = null; clear cipher if it hasn't already been
cleared, clear ka if necessary (e.g. un-init)
return result;
}
... and unwrap and kdf function
}
2) Implement a provider and add the above.
On BC's part, we've already implemented RFC 5990/SP 800-56B in Java
and the experience has, at best, been awkward. The new algorithms
have moved awkward to inappropriate. With the new algorithms, there's
no longer only one case of this, it's not an outlier, there should be
a general way of supporting KEMs in the API that doesn't involve over
engineering KeyGenerator and Cipher.
There's a big difference between the API and your underlying
implementation. Everything you want to do can be done using the
current APIs. As I said before, Cipher.wrap/unwrap are the
appropriate APIs for this as they meet the contract requirements you
need. Most Ciphers require some extra data -e.g. IVs - that have to
either be carried or implicitly derived. In this case, what needs to
be carried in addition to the encrypted key material is at least the
ephemeral public key the wrapper creates. I used a very simple
encoding scheme above and this assumes that both ends know exactly
what "ECIES-KEM" means. Obviously, there are 100s of possible
combinations of parameters and KDFs and key wrap algorithms. What I
would suggest is heading over to LAMPS at the IETF and proposing a
data encoding scheme for carrying the parameters. Once you have that
done, then come here and map JCE names against parameter sets to close
the loop. It won't require an API change.
I work with a team that has had to implement all of them and had to
make them fit into the JCA. We have done so. Like John, I am simply
relaying our experience. In about 18 months these algorithms are
going to become mandatory, what all of us think is irrelevant. We,
for our part, already have a solution, but we both realize it's not
"the solution" - we recognize that the JVM is uniquely positioned to
provide leadership on this and provide a universal way of doing it.
Then suggest an API and we'll start knocking it around. I personally
don't think its necessary at this time and will add to API bloat.
Of course, if it's felt that these algorithms should be ignored, it's
not my place to revolt, although I do feel obliged to argue. I will
simply try and do the best by my users, as I have no doubt will John.
Both of us have simply offered our comments in good faith and to
alert the community that things have changed and that with these new
algorithms there is room for a new approach. The ambiguity about how
these algorithms can be implemented and the excessive need to
fallback on propritary classes for them does suggest that there are
some additions to the JCA which would help. I appreciate to
understand this statement does involve actually understanding what
these algorithms do and may require some additional reading.
As I said, I'm an engineer, my users will be able to use these
algorithms properly, my team will ensure that, as I have no doubt
will John's. What John and myself, apparently mistakenly, care about
is that our users should also be able to use these algorithms portably.
Are you saying portability is no longer a consideration?
I have no idea where you got that idea.
Regards,
David
On 21/8/22 02:23, Michael StJohns wrote:
Hi David/John -
I would submit that you're trying too hard to make your life simple! :-)
Cipher.wrap/unwrap are the correct methods.
For example:
Cipher kem = Cipher.getInstance
("ECIES/GCM-128-64/KDF-SP800-108-COUNTER-SHA256");
kem.init (Cipher.WRAP_MODE, pubkey);
byte[] opaqueEncapsulatedKey = kem.wrap (someOtherKey);
The "opaqueEncapsulatedKey" would contain the data needed by the
unwrap function - specifically a) the ecies ephemeral public key, b)
the fact that the derived key is a GCM key of length 128 and that
the GCM tag is 64 bytes long, c) the KDF, d) (optional) any mixins
other than defaults required by the KDF - which would be passed in a
parameter blob during init. Cipher would NOT return the underlying
generated secret used to wrap the key. Just the public part of the
key pair used to do the ECDH operation against the passed in public
key. In the RSA case, the wrapped encrypting secret would be an
opaque data blob and would be part of the data passed to the unwrap
function.
If you want a key generated for other purposes, then the right thing
is using a KDF and a Key agreement function in tandem. Strangely
the KDF appears in the javacard API for 3.1, but not in the JCE/JDK API.
"What's the difference between a bureaucrat and an engineer? A
bureaucrat takes small solvable pieces and combines them into one
insoluble mass."
In this case, Java provides a number of flexible primitives that can
be combined as needed. In this case, the underlying Cipher
implementation would wrap key agreement and kdf and cipher (GCM)
instances. It should return UnsupportedOperationException for all
operations execept wrap/unwrap and the appropriate init methods.
Later, Mike
On 8/19/2022 6:38 PM, David Hook wrote:
Hi Mike,
KEMs can be used for key wrapping - we've actually implemented
support for this too. But they are not actually key wrapping ciphers.
Here's a simple example of using Kyber for key wrapping in BC:
SecretKey key =new SecretKeySpec(keyBytes,"AES");
w1.init(Cipher.WRAP_MODE, kp.getPublic(),new KEMParameterSpec("AES-KWP"));
byte[]data =w1.wrap(key);
Cipher w2 =Cipher.getInstance(algorithm,"BCPQC");
w2.init(Cipher.UNWRAP_MODE, kp.getPrivate(),new KEMParameterSpec("AES-KWP"));
Key k =w2.unwrap(data,"AES",Cipher.SECRET_KEY);
The behavior in this case is in line with what is given in RFC 5990 for the RSA
KEM. How it works is by using the key generated
by the KEM to create an AES-KWP key, which is then used to wrap keyBytes. The
shortcoming is it means you have to generate the
secret key separately.
This is the problem though - a KEM can actually be used to generate a secret
key for other purposes. For example, where
someone is trying to implement a hybrid KAS scheme. But there is currently no
mechanism in the Java APIs for being able to
take advantage of this directly, hence our use of the KeyGenerator class and
other people's attempts to make use of the KeyAgreement
class. The Cipher.wrap() returns a byte[] - to be used with a KEM for secret
generation it would also have to return the
generated secret (I would probably also argue that passing a public key to wrap
in order to generate an encapsulation of a
generated encrypted secret was not the correct use of the API either, but the
fact remains a byte[] is not really going to cut it).
If you have any further questions, please feel free to ask. For what it is
worth, I have been developing providers for the JCE/JCA since
the late 90's and am actually one of the people responsible for the
introduction of the existing wrap/unwrap API in the Cipher class.
Thanks,
David
On 20/8/22 07:53, Mike StJohns wrote:
Hi This implemented as part of Javax.crypto.Cipher. See the Java doc for the
wrap and unwrap methods.
Mike
Sent from my iPad
On Aug 19, 2022, at 12:56, John Gray<john.g...@entrust.com> wrote:
We are starting to make use of the new PQ algorithms adopted by NIST for
prototyping and development of standards. In particular we are working on a
composite KEM standard:
See:https://datatracker.ietf.org/doc/draft-ounsworth-pq-composite-kem/
However, there is no KEM interface in the JCA (which make sense because these
are new algorithms, although RSA-KEM has been out since 2010).
I can add one into our toolkit (and I think David may have already added on
into BC), but I assume at some point there will be an official one added in
Java and likely it won't be identical to what we do even if it is very close,
which would cause backwards compatibility pain... Perhaps we could
collaborate on extending the JCA to support KEM? Essentially it requires
methods.
ss, ct := encapsulate(PublicKey)
ss := decapsulate(PrivateKey, ct)
-ss is a shared secret (could come back as a Java SecretKey if you wanted as it
would usually be used to derive something like an AES afterwards)
-ct is a Cipher Text (a byte array would make sense)
-Public and Private Keys would use the regular public and private key interface.
-An object holding the ss and ct from the encapsulate() method could be
returned, with accessor methods to get the ss and ct. It could be called
'EncapsulatedKEMData' for example.
Likely you would want a new type of KEM crypto object (like you have for
Signature, MessageDigest, Cipher, Mac, SecureRandom, KeyAgreement.. etc).
Calling it KEM would seem to make sense. 😊 It could also use similar
calling patterns and have a KEM.initKEM(keypair.getPublic()) or
KEM.initKEM(keypair.getPrivate()), and then you would just call
KEM.encapsulate() or KEM.decapsulate(ct).
Then algorithms could be registered in providers as usual:
put("KEM.Kyber","com.blah.Kyber")
put("KEM.compositeKEM","com.entrust.toolkit.crypto.kem.compositeKEM")
Then the above methods (encapsulate and decapsulate) could be defined in that
new object type. Then we would be able to make use of it and not have to
worry about incompatibility issues down the road...
Cheers,
John Gray
Any email and files/attachments transmitted with it are confidential and are
intended solely for the use of the individual or entity to whom they are
addressed. If this message has been sent to you in error, you must not copy,
distribute or disclose of the information it contains. Please notify Entrust
immediately and delete the message from your system.