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

ML-KEM and ML-DSA private key ASN.1 encoding format does not comply with IETF draft #1969

Open
bukodi opened this issue Jan 16, 2025 · 1 comment
Assignees

Comments

@bukodi
Copy link

bukodi commented Jan 16, 2025

Description:
The ASN.1 encoding of the ML-DSA and ML-KEM private keys generated by the Bouncy Castle library does not comply with the IETF draft specifications.

According to Chapter 6 of the Dilithium IETF draft [1], the 'privateKey' component of the OneAsymmetricKey sequence should contain the raw octet string encoding of the 32-octet seed. However, the BC library generates an OCTET STRING that contains another OCTET STRING, which in turn contains the 32-octet seed. This additional OCTET STRING tag affects the import and export operations of both ML-KEM and ML-DSA private keys for all key sizes.

(Used Bouncy Castle version :1.79)

Reproduction steps for ML-DSA-44 key export:

  1. The IETF draft [1] Appendix C.1 provides an example ML-DSA-44 private key with seed 000102...1e1f:

    -----BEGIN PRIVATE KEY-----
    MDICAQAwCwYJYIZIAWUDBAMRBCAAAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRob
    HB0eHw==
    -----END PRIVATE KEY-----
    

    Decoded by online tool: https://lapo.it/asn1js/#MDICAQAwCwYJYIZIAWUDBAMRBCAAAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHw

  2. Generate the same key pair with the BC library and export the private key in PKCS#8 format:

        KeyPairGenerator kpg = KeyPairGenerator.getInstance(MLDSAParameterSpec.ml_dsa_44.getName(), "BC");
        kpg.initialize(MLDSAParameterSpec.ml_dsa_44, new FixedSecureRandom( new byte[] {
                0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
                0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
        }));
        KeyPair kp = kpg.generateKeyPair();
    
        StringWriter sw = new StringWriter();
        try( JcaPEMWriter pemWriter = new JcaPEMWriter(sw) ) {
            pemWriter.writeObject( new JcaMiscPEMGenerator(kp.getPrivate()));;
        };
        System.out.println("Private key PEM: \n" + sw);

The output is:

 -----BEGIN PRIVATE KEY-----
 MDQCAQAwCwYJYIZIAWUDBAMRBCIEIAABAgMEBQYHCAkKCwwNDg8QERITFBUWFxgZ
 GhscHR4f
 -----END PRIVATE KEY-----

Decoded by online tool: https://lapo.it/asn1js/#MDQCAQAwCwYJYIZIAWUDBAMRBCIEIAABAgMEBQYHCAkKCwwNDg8QERITFBUWFxgZGhscHR4f

References:
[1] https://datatracker.ietf.org/doc/draft-ietf-lamps-dilithium-certificates/06/
[2] https://datatracker.ietf.org/doc/draft-ietf-lamps-kyber-certificates/07/

@dghgit dghgit self-assigned this Jan 20, 2025
@dghgit
Copy link
Contributor

dghgit commented Jan 22, 2025

This is kind of fixed in 1.80 (it would have been difficult in 1.79 as it came out a month before the decision about raw octets was made) but there's still a discussion going on about this.

Unfortunately seed only keys for ML-DSA and ML-KEM create an issue for FIPS modules where the ASN.1 parsing is done outside the cryptographic boundary. For these modules you need the long form keys as Section 6 of both FIPS PUB 203 and FIPS PUB 204 do not allow the internal key pair generation function to be exposed via an API except for testing, effectively ruling out the use of seed only in this case. This being the case (OpenSSL is an example of a module like this, I gather the issue also trips up PKCS#11 as well) it is unlikely we will be able to produce the IETF proposal, although I think we will be able to read it. In our case it appears we'll need to make use of the OASIS PKCS#11 proposal, which is a based around a SEQUENCE with 2 optional fields.

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