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:
-
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
-
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/
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
OneAsymmetricKeysequence 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:
The IETF draft [1] Appendix C.1 provides an example ML-DSA-44 private key with seed
000102...1e1f:Decoded by online tool: https://lapo.it/asn1js/#MDICAQAwCwYJYIZIAWUDBAMRBCAAAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHw
Generate the same key pair with the BC library and export the private key in PKCS#8 format:
The output is:
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/