[EXTERNAL] Re: Is there a KEM (Key Encapsulation Mechanism) architecture being proposed for the JCA?
Franco Nieddu
franco.nieddu at iaik.tugraz.at
Fri Aug 26 11:44:31 UTC 2022
Hi again to John, David, and all!
Yeah, I am glad you guys initiated the discussion regarding the API
extension. Internally, we already wondered how the JCA would move
forward, and I think everyone who tried to map the PQC KEMs, realized
pretty fast that the existing API does not fit.
With that out of the way, here is a code fragment to use our API with
KYBER:
final String algorithm = "KYBER";
KeyPairGenerator kpg = KeyPairGenerator.getInstance(algorithm);
kpg.initialize(new KyberAlgorithmParameterSpec());
KeyPair keyPair = kpg.generateKeyPair();
//encap
KeyEncapsulationMechanism encap =
KeyEncapsulationMechanism.getInstance(algorithm);
encap.init(keyPair.getPublic());
//encap.init(keyPair.getPublic(), new SecureRandom());
//encap.init(keyPair.getPublic(), new SaberAlgorithmParameterSpec());
//encap.init(keyPair.getPublic(), new SaberAlgorithmParameterSpec(),
new SecureRandom());
byte[] sessionKeyEncap = new byte[32];
byte[] cipher = encap.encapsule(sessionKeyEncap);
//decap
KeyEncapsulationMechanism decap =
KeyEncapsulationMechanism.getInstance(algorithm);
decap.init(keyPair.getPrivate()); //the init methods are the same
byte[] sessionKeyDecap = new byte[32];
decap.decapsule(cipher, sessionKeyDecap);
For key generation, we use the existing KeyPairGenerator API. We
modeled the different parameter sets as AlgorithmParameterSpecs, where
a caller may request one of NIST's security levels, and the
implementation returns the appropriate parameter set. As you proposed,
we could use a generic KEMAlgorithmParameterSpecs and derive the
parameter sets from the provided keys. This approach works for the PQC
algorithms. However, this does not work with RSA-KEM (deriving the
parameters from the key). Anyhow, I do not know how important RSA-KEM
is
moving forward (it never was, to begin with, sadly). I just wanted to
point it out.
At the moment, there are four ways to initialize a KEM object. The
implementation derives the mode (encap, decap) from the provided key;
therefore, all init() methods require a key. Furthermore, if possible,
it derives the parameters from the key. The other init() methods are
self-explanatory, I guess.
Regarding the byte array initialization, yes, you are right. Your
approach is also possible. We went with this approach because we wanted
a possibility for the caller to specify the output length as the output
of KDFs is of variable length, and we use the AlgorithmParameterSpecs
already to map the different parameter sets. I would argue that the
size of the byte[] is not a problem because if two parties want to
share a secret, they need to know at least what they use the secret
for, and therefore, I think we can assume that they know their
respective public, global parameters (including the secret size). This
assumption may be too conservative, and I do not have a strong opinion,
so if there are arguments for a different approach, I would be glad to
change our interface.
Furthermore, we designed the API in a way that there is a strict
segregation from the KDF and underlying Public-Key Encryption. To be
honest, we did not consider a size mismatch of the provided byte array
and the KEM instance in our implementation because all KDFs we
included
so far are inherently unlimited in size, but maybe we did miss
something.
I am, of course, open to improvements and would be glad to discuss all
of this. Currently, I live in Central Europe (Austria, CEST/UTC+2). I
would rather discuss this in person instead of by e-mail, but finding a
suitable time slot could be difficult. I have no problem with getting
up early for a constructive discussion ;)
Cheers,
Franco
On Fri, 2022-08-26 at 08:36 +1000, David Hook wrote:
> Happy to offer any assistance I can.
> The existing API maps well for key wrapping if you follow the example
> of RFC 5990 - the RSA-KEM - but for actual secret sharing, every
> solution that "fits", including our current one, doesn't really fit
> either the JCA, or the algorithms themselves.
> Even with our current solution, overloading the KeyGenerator class,
> it only solves a specific use case. It's obvious there are others, so
> the solution means the burden on the parameter spec classes is just
> going to get worse. A more general solution would mean we would only
> have to explain things once, rather than once each.
> Regards,
> David
> On 26/8/22 00:49, John Gray wrote:
> > Thanks Franco,
> >
> > I am very familiar with the IAIK toolkit as our Entrust Java
> > toolkit actually makes use of the IAIK ASN.1 library from a 2003
> > version and IAIK and Entrust used to work very closely together
> > regarding our toolkits in the early 2000's. So I think between
> > the 3 of us we represent a majority of the JCA cypto toolkits that
> > exist (except for BSAFE which I have never used). So it is great
> > to see we agree that there needs to be KEM classes added to the JCA
> > as we have all struggled with fitting KEM into the JCA.
> >
> > We certainly want their to be an interoperable implementations of
> > KEM. Franco, could you send a simple sample of doing a KEM
> > keygen and then the encapsulate and decapsulate with your API? I
> > am assuming the public and private keys are initialized in an
> > init() method of some sorts? I see the encapsule(byte[] k) method
> > assumes you know the size of the shared-secret, and expects you to
> > size the array as a way to specify the size of the secret you
> > want. If you are using a KEM with a fixed Cipher size, or you use
> > the wrong size (by mistake), or you later decided to change to a
> > different KEM that uses a different size then maybe that might
> > cause issues? Perhaps just have a init() that takes a public or
> > private key and a KEMParameterSpec (of type AlgorithmParameterSpec)
> > that has an option to specify the SharedSecret size would be
> > better? If you don't specify anything it could use the default
> > provided by the KEM, or if you provide a size then it could be
> > used. Then returning the shared secret in a separate method, or
> > an Object that includes both the CipherText and shared-secret could
> > work. Then this would remove the possibility of byte array
> > initialization issues... Doing thing might also help to make the
> > same code more crypto agile as you could easily switch from say
> > Kyber to McEliece or Composite KEM just by changing the
> > getInstance() call and modifying the KEMParameterSpec object.
> >
> > Perhaps we could meet together sometime and hash out a KEM API we
> > all agree on and then propose it to Oracle/OpenJDK? If it is not
> > accepted and OpenJDK/Oracle has no plans to add a KEM API we could
> > just agree to implement it in our own toolkits and then at least we
> > would have a common KEM interface for use across our toolkits? We
> > could also do it over email if time-zones don't work out. I know
> > David is in Australia, I am in Canada (Eastern Time).
> >
> > Cheers,
> >
> > John Gray
> >
> >
> > -----Original Message-----
> > From: Franco Nieddu <franco.nieddu at iaik.tugraz.at>
> > Sent: Thursday, August 25, 2022 2:33 AM
> > To: John Gray <John.Gray at entrust.com>; David Hook <
> > dgh at cryptoworkshop.com>; Michael StJohns <mstjohns at comcast.net>
> > Cc: security-dev at openjdk.org
> > Subject: Re: [EXTERNAL] Re: Is there a KEM (Key Encapsulation
> > Mechanism) architecture being proposed for the JCA?
> >
> > Hi David, Michael, and John, and everyone else!
> >
> > I will give my two cents to the Cipher/KEM topic and add on the
> > latest mail from John. For why this topic is interesting to me, I
> > work at the TU Graz, where we have developed the IAIK-JCE Provider
> > since the beginning of the 2000s.
> >
> > Anticipating the end of NIST's PQC, we started implementing the
> > third- round KEMs approximately a year ago.
> >
> > I have to agree with David and John. KEMs and Ciphers are strictly
> > different. To map KEMs operations to the existing API, we would be
> > forced to break a lot of the contracts/definitions of multiple
> > classes, but at the bare minimum from the Cipher.wrap(Key) method
> > itself. Moving forward, KEMs will rise in popularity, and we should
> > reduce confusion for developers using our libraries.
> > It may be possible to use e.g., McEliece as an asymmetric cipher,
> > but I would not want my users to start using McEliece in such a
> > way. Clear segregation would help prevent this. The other option
> > will be to throw exceptions if a caller provides something
> > different than WRAP_MODE in Cipher.init(), breaking more contracts.
> > Furthermore, the Cipher.unwrap(byte[], String, int) method is
> > tedious to use, but maybe this is just me...
> >
> > In his last message, John showed, better than I could, that the
> > KeyAgreement classes only fit with great difficulty.
> >
> > The API mapping just does not work, and there is no denying that.
> > Nevertheless, the biggest issue I have (if we ignore all the broken
> > contracts for a moment) is the serialization/deserialization of the
> > ciphertext-secret pair. The Cipher.wrap() method simply does not
> > allow to return both values. Concating the two values into a single
> > byte array just begs for incompatibility between implementations.
> > Another option to return the ciphertext and the secret to the
> > caller is by defining a new class/interface like:
> >
> > public final class KEMSecret implements Key {
> >
> > private byte[] secret_;
> >
> >
> > public final void setSecret(byte[] secret) {
> > this.secret_ = secret;
> > }
> >
> > public final byte[] getSecret() {
> > return this.secret;
> > }
> > }
> >
> > Then enforce that for all KEMs, the Cipher.wrap() method uses this
> > KEMSecret as parameter, breaking another contract, as the KEMSecret
> > IS NOT a key (it can be but is not limited to).
> >
> >
> >
> > Our implementation for the moment looks something like this:
> >
> > public abstract class KeyEncapsulationMechanismSpi {
> > //some init methods....
> > protected abstract void engineDeriveKey(byte[] output,
> > byte[]...
> > input) throws DigestException;
> > protected abstract byte[] engineEncapsule(byte[] k) throws
> > InvalidKeyException, DigestException;
> > protected abstract void engineDecapsule(byte[] c, byte[] k)
> > throws InvalidKeyException, DigestException; }
> >
> > The engineDeriveKey() method is a call to a KDF (for the PQC always
> > SHAKE), where output is the derived key, and the varargs depict the
> > input to the KDF. At least in McEliece, there are multiple calls to
> > the KDF; therefore, adding a separate engine method seemed
> > reasonable.
> > Furthermore, it is possible to plug in different KDFs for the same
> > KEM instance. Taking inspiration from the Cipher classes, we could
> > do something like:
> >
> > KEM.getInstance("Kyber/SHAKE128") or KEM.getInstance("Kyber/KDF3")
> >
> > At the moment, this is not necessary, but already defining a
> > modular interface should at least be considered.
> >
> > engineEncapsule() returns the ciphertext of the encap operation.
> > Additionally, the implementation fills the provided byte[] with the
> > secret. The secret produced by KEMs is not strictly limited in
> > size; therefore, it seemed appropriate to let the caller decide the
> > size of the byte[].
> >
> > engineDecapsule() is the inverse of engineEncapsule().
> >
> > I am, of course, open to discussions regarding the actual
> > implementation, e.g. returning the result as some kind of
> > EncapsulatedKEMData as suggested by John, but I see the necessity
> > of adding KEM classes to the security API.
> >
> > To close this already long message, something about bureaucrats.
> > Bureaucrats, at least in my country, are fond of the status quo.
> > Luckily, we are not bureaucrats and are permitted to improve a
> > barely working solution.
> >
> > Cheers,
> > Franco
> >
> > On Wed, 2022-08-24 at 20:12 +0000, John Gray wrote:
> > > Just catching up on emails.
> > >
> > > I agree about PKCS11 as well, but vast amounts of people use it
> > > and I
> > > don’t think it is going away anytime soon.
> > >
> > > This is a great discussion.
> > >
> > > So Michael, I think you are saying we should be able to use a
> > > combination of KeyAgreement and Cipher to implement a KEM in the
> > > JCA. I think that works in practice for any existing Key
> > > Agreement
> > > or Cipher (that is what we essentially use in our IETF draft for
> > > turning existing Key agreement or Cipher algorithms into a KEM).
> > > https://urldefense.com/v3/__https://datatracker.ietf.org/doc/draft-oun
> > > sworth-pq-composite-kem/__;!!FJ-Y8qCqXTj2!dRvGTjD4FlxgYSD-
> > > gBr0pkO3DBu7
> > > Gf13fWJ4vYkzN8aahzgBH0UE1xfmZwNwVqx3ZsUUu2AVJSJMccMLY00KoUQ-
> > > K6fkXJ8$
> > >
> > > However, for a pure KEM like Kyber I don’t think you can just
> > > assume
> > > you will be able to break up the encapsulation() procedure which
> > > returns a CipherText and Shared-Secret given a Kyber public key.
> > > It defines the encapsulation and decapsulation operations (see
> > > https://urldefense.com/v3/__https://datatracker.ietf.org/doc/draft-cfrg-schwabe-kyber/__;!!FJ-Y8qCqXTj2!dRvGTjD4FlxgYSD-gBr0pkO3DBu7Gf13fWJ4vYkzN8aahzgBH0UE1xfmZwNwVqx3ZsUUu2AVJSJMccMLY00KoUQ-TMAnNfc$ ). A
> > > Cipher encrypt can’t model the Encapsulation operation as it just
> > > returns a byte[]. I suppose a Cipher Decrypt could model the
> > > Decapsulation() operation with the result being the shared
> > > secret,
> > > but that is only half the picture…. Kyber has internal
> > > functions that do encrypt and decrypt like operations which could
> > > be
> > > modeled as a Cipher in JCA (but the message would have to be some
> > > type
> > > of specially formatted structure containing the m and cpaSeed
> > > values,
> > > or if a keywrap the public key would have to be structured along
> > > with
> > > those required values). So even if we did that, how do you
> > > propose
> > > the rest of the Kyber KEM operations fit in the current
> > > Java JCA? It seems you would need to somehow split up the
> > > components of the algorithm across different parts of the JCA and
> > > would it be possible to hide the complexity as simply as adding a
> > > KEM
> > > JCA object type with an encapsulate() method and a decapsulate()
> > > method?
> > >
> > > From
> > > https://urldefense.com/v3/__https://datatracker.ietf.org/doc/draft-cfr
> > > g-schwabe-kyber/__;!!FJ-Y8qCqXTj2!dRvGTjD4FlxgYSD-
> > > gBr0pkO3DBu7Gf13fWJ4
> > > vYkzN8aahzgBH0UE1xfmZwNwVqx3ZsUUu2AVJSJMccMLY00KoUQ-TMAnNfc$
> > >
> > > 11.2. Encapsulation
> > >
> > > Kyber encapsulation takes a public key and a 32-octet seed
> > > and
> > > deterministically generates a shared secret and ciphertext
> > > for the
> > > public key as follows.
> > >
> > > 1. Compute
> > >
> > > 1. m = H(seed)
> > >
> > > 2. (Kbar, cpaSeed) = G(m || H(pk))
> > >
> > > 3. cpaCipherText = Kyber.CPAPKE.Enc(m, publicKey,
> > > cpaSeed)
> > >
> > > 2. Return
> > >
> > > 1. cipherText = cpaCipherText
> > >
> > > 2. sharedSecret = KDF(KBar || H(cpaCipherText))
> > >
> > >
> > > 11.3. Decapsulation
> > >
> > > Kyber decapsulation takes a private key and a cipher text and
> > > returns
> > > a shared secret as follows.
> > >
> > > 1. Split privateKey into
> > >
> > > 1. A 12*k*n/8-octet cpaPrivateKey
> > >
> > > 2. A 12*k*n/8+32-octet cpaPublicKey
> > >
> > > 3. A 32-octet h
> > >
> > > 4. A 32-octet z
> > >
> > > 2. Compute
> > >
> > > 1. m2 = Kyber.CPAPKE.Dec(cipherText, cpaPrivateKey)
> > >
> > > 2. (KBar2, cpaSeed2) = G(m2 || h)
> > >
> > > 3. cipherText2 = Kyber.CPAPKE.Enc(m2, cpaPublicKey,
> > > cpaSeed2)
> > >
> > > 4. K1 = KDF(KBar2 || H(cipherText))
> > >
> > > 5. K2 = KDF(z || H(cipherText))
> > >
> > > 3. In constant-time, set K = K1 if cipherText == cipherText2
> > > else
> > > set K = K2.
> > >
> > > 4. Return
> > >
> > > 1. sharedSecret = K
> > >
> > >
> > >
> > > It can *sort of* fit with a KeyAgreement if you do this, but its
> > > kludgy:
> > >
> > > On sending side:
> > > KeyAgreement kem = KeyAgreement.getInstance(“Kyber”);
> > >
> > > Kem.init(null, KEMparameters) - I’m generating the CipherText
> > > and
> > > shared-secret for 1 other person, I don’t have their private key
> > > and
> > > its not multi-party
> > >
> > > KEMCipherTextKey = Kem.doPhase(Key kemPublicKey, true) byte[]
> > > sharedSecret = generateSecret()
> > >
> > >
> > > The KEMCipherTextKey contains the CipherText that just happens to
> > > implements the Key interface. It is very weird, but we
> > > something to
> > > carry the cipher text.
> > >
> > > Send KEMCipherTextKey to the receiver:
> > >
> > > On receiving side:
> > > KeyAgreement kem = KeyAgreement.getInstance(“Kyber”);
> > >
> > > Kem.init(KEMCipherTextKey, KEMParameters); -> The CipherTExt
> > > is
> > > the KEMCipherTExtKey
> > > null = Kem.doPhase(KemPrivateKey, true) -> Shared secret is
> > > generated
> > > from CipherText and PrivateKey, but a Key object is not returned
> > > byte[] sharedSecret = generateSecret()
> > >
> > >
> > > So it can work, but it is kludgy. The placement of the keys
> > > could
> > > be reversed (the public and private keys could be passed in via
> > > init,
> > > then null in the first doPhase, and the CipherTextKey in the
> > > second
> > > doPhase. I don’t know which is better as it could work either
> > > way. This just shows how it doesn’t fit cleanly…
> > >
> > >
> > >
> > > In the openSSL-OQS port which is in C, they have KEM’s defined
> > > simply
> > > as follows:
> > > https://urldefense.com/v3/__https://github.com/open-quantum-safe/liboq
> > > s/blob/main/src/kem/kem.h__;!!FJ-Y8qCqXTj2!dRvGTjD4FlxgYSD-
> > > gBr0pkO3DBu
> > > 7Gf13fWJ4vYkzN8aahzgBH0UE1xfmZwNwVqx3ZsUUu2AVJSJMccMLY00KoUQ-
> > > st7vufc$
> > >
> > > Which follows which NIST outlines in
> > > https://urldefense.com/v3/__https://csrc.nist.gov/CSRC/media/Projects/
> > > Post-Quantum-Cryptography/documents/example-files/api-
> > > notes.pdf__;!!FJ
> > > -Y8qCqXTj2!dRvGTjD4FlxgYSD-
> > > gBr0pkO3DBu7Gf13fWJ4vYkzN8aahzgBH0UE1xfmZwN
> > > wVqx3ZsUUu2AVJSJMccMLY00KoUQ-ArPLprc$
> > >
> > >
> > > Obviously we can make it better in Java while keeping it
> > > simple. For
> > > example:
> > >
> > > KEMEncapsulation = KEM.encapsulate(publicKey); byte[] ss =
> > > KEM.decapsulate(privateKey, CipherText);
> > >
> > > KEMEncapsualtion simply contains the shared secret (ss) and
> > > CipherText…
> > >
> > >
> > > Or to fit with init() pattern the rest of them use:
> > >
> > > KEM.init(publicKey);
> > > KEMEncapsulation = KEM.encapsulate()
> > >
> > > byte[] cipherText = KEMEncapsulation.getCipherText();
> > >
> > > And then
> > > KEM.init(privateKey);
> > > byte[] ss = KEM.decapsulate(cipherText)
> > >
> > > or maybe even better:
> > > KEM.init(publicKey);
> > > byte[] cipherText = KEM.encapsulate()
> > > byte[] sharedSecret = KEM.getSharedSecret()
> > >
> > > And then
> > > KEM.init(privateKey);
> > > byte[] ss = KEM.decapsulate(cipherText)
> > >
> > >
> > > Cheers,
> > >
> > > John Gray
> > >
> > >
> > >
> > >
> > > From: David Hook <dgh at cryptoworkshop.com>
> > > Sent: Sunday, August 21, 2022 10:51 PM
> > > To: Michael StJohns <mstjohns at comcast.net>; John Gray <
> > > John.Gray at entrust.com>
> > > Cc: security-dev at openjdk.org
> > > Subject: [EXTERNAL] Re: Is there a KEM (Key Encapsulation
> > > Mechanism)
> > > architecture being proposed for the JCA?
> > >
> > > WARNING: This email originated outside of Entrust.
> > > DO NOT CLICK links or attachments unless you trust the sender and
> > > know
> > > the content is safe.
> > >
> > > 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 theJava doc for the wrap and
> > > > > > > > unwrap methods. Mike Sent from my iPad
> > > > > > > > > On Aug 19, 2022, at 12:56, John Gray <
> > > > > > > > > John.Gray at 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://urldefense.com/v3/__https://datatracker.ietf.org/d
> > > > > > > > > oc/draft-ounsworth-pq-composite-kem/__;!!FJ-
> > > > > > > > > Y8qCqXTj2!dRvG
> > > > > > > > > TjD4FlxgYSD-
> > > > > > > > > gBr0pkO3DBu7Gf13fWJ4vYkzN8aahzgBH0UE1xfmZwNwVq
> > > > > > > > > x3ZsUUu2AVJSJMccMLY00KoUQ-K6fkXJ8$
> > > > > > > > >
> > > > > > > > > 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.ke
> > > > > > > > > m.co
> > > > > > > > > mpositeKEM")
> > > > > > > > >
> > > > > > > > > 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.
> > > > > > >
> > > > > > >
> > > > > >
> > > > > >
> > > > >
> > > > >
> > > >
> > > >
> > >
> > --
> > Franco Nieddu
> > SIC - Software Engineer
> > Phone: +43 (316) 873 - 5507
> > SIC Homepage:
> > https://urldefense.com/v3/__https://jce.iaik.tugraz.at/__;!!FJ-Y8qCqXTj2!dRvGTjD4FlxgYSD-gBr0pkO3DBu7Gf13fWJ4vYkzN8aahzgBH0UE1xfmZwNwVqx3ZsUUu2AVJSJMccMLY00KoUQ-2W4oa1s$
> > IAIK Homepage:
> > https://urldefense.com/v3/__https://www.iaik.tugraz.at/__;!!FJ-Y8qCqXTj2!dRvGTjD4FlxgYSD-gBr0pkO3DBu7Gf13fWJ4vYkzN8aahzgBH0UE1xfmZwNwVqx3ZsUUu2AVJSJMccMLY00KoUQ-Ov0Iano$
> >
--
Franco Nieddu
SIC - Software Engineer
Phone: +43 (316) 873 - 5507
SIC Homepage: https://jce.iaik.tugraz.at/
IAIK Homepage: https://www.iaik.tugraz.at/
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/security-dev/attachments/20220826/b27f7a75/attachment.htm>
More information about the security-dev
mailing list