Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unpacking Kyber keys from a FIPS 203 x509 key spec #496

Closed
jmcrawford45 opened this issue Apr 22, 2024 · 10 comments
Closed

Unpacking Kyber keys from a FIPS 203 x509 key spec #496

jmcrawford45 opened this issue Apr 22, 2024 · 10 comments

Comments

@jmcrawford45
Copy link

Hi all,

I'm encountering an issue where I'm able to correctly encapsulate / decapsulate with a randomly generated key, but I fail to decapsulate when unpacking from a FIPS 203 x509 key spec produced by BouncyCastle. I'm loading the public key from t + rho, and I'm loading the private key from s + t + rho + hpk + z. Does this sound like a reasonable mapping / are there some expected transformations required to agree on the same key?

Best,
Jared

@bwesterb
Copy link
Member

Did you use #470 ?

@jmcrawford45
Copy link
Author

Yes, this is using #470

@bwesterb
Copy link
Member

Which version of bouncy castle? They did some weird things with the private key, and your version might still be on round 3 instead of the IPD.

@jmcrawford45
Copy link
Author

This is on latest release (1.78.1).

@bwesterb
Copy link
Member

Ok, what is this "x509 key spec" about?

@jmcrawford45
Copy link
Author

jmcrawford45 commented Apr 22, 2024

The x509 aspect isn't relevant. Basically I'm passing the output of getEncoded from the key parameters into the relevant unpack functions. E.g. for a public key this is

static byte[] getEncoded(byte[] t, byte[] rho)
    {
        return Arrays.concatenate(t, rho);
    }

and for a private key this is

public byte[] getEncoded()
    {
        return Arrays.concatenate(new byte[][]{ s, t, rho, hpk, nonce });
    }

Here's my relevant go code

	pqPubKeyBytes, err := base64.StdEncoding.DecodeString("...")
	if err != nil {
		return nil, err
	}
	pqPubKey := mlkem768.PublicKey{}
	pqPubKey.Unpack(pqPubKeyBytes)
	if err != nil {
		return nil, err
	}
	pqPrivKeyBytes, err := base64.StdEncoding.DecodeString(...)

	pqPrivKey := mlkem768.PrivateKey{}
	pqPrivKey.Unpack(pqPrivKeyBytes)

@bwesterb
Copy link
Member

bwesterb commented Apr 23, 2024

With any sane implementation of Kyber, the user should never have to handle the constituents (t, s, etc) of the keys themselves. What is that about?

@jmcrawford45
Copy link
Author

jmcrawford45 commented Apr 23, 2024

I'm have two use cases for loading an existing key generated by BouncyCastle. One is for known answer tests to verify compatibility, and the other is the more standard use case of key encapsulation which requires loading the public key. I'm not directly accessing the constituents, I'm using the getEncoded method and the unpack methods which from reading the code seem to agree on the arrangement and format of constituents. Is there another preferred way to load an existing mlkem key pair in circl?

@bwesterb
Copy link
Member

ML-KEM public and private keys are opaque byte strings. You shouldn't have to deal with the constituents. We pass the test vectors from the reference implementation (round 3, ipd), which checks the private and public key format. It's likely something is wrong on the BouncyCastle side of things. Happy to help debug, but it's hard from a distance.

@jmcrawford45
Copy link
Author

This was an issue with BouncyCastle version conflicts. Thanks again for taking a look!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants