bcgit / bc-java

Bouncy Castle Java Distribution (Mirror)
https://www.bouncycastle.org/java.html
MIT License
2.32k stars 1.14k forks source link

Question about ML-DSA private key length in BC 1.79 #1900

Open mrshih opened 5 days ago

mrshih commented 5 days ago

Hello,

I'm using Bouncy Castle 1.79 and generating ML-DSA key pairs. I've noticed that the private key is only 32 bytes long when using .getPrivate().getEncoded().

According to NIST 204:

If additional space is available, one can precompute and store 𝐀̂ to speed up signing and verifying. Alternatively, if one wants to reduce the space needed for the private key, one can store only the 32-byte seed 𝜉, which is sufficient to generate the other parts of the private key.

My questions are:

  1. Is there a way to obtain the full private key representation?
  2. Is there a method to generate the complete private key from the seed?

Thank you very much for your help!

Best regards

johngray-dev commented 4 days ago

At the IETF 121 LAMPS working group, and other working group discussions, it was decided that only the 32 byte seed was going to be support in standard formats. You can of course keep the private key encoded in an expanded form internally if that is what you want, but when exporting the key use the seed.

dghgit commented 4 days ago

John's correct about what's happened with getEncoded(). Would adding a getPrivateData() method which returned the regular long form help on this one?

With generating from a seed, you could do this by creating a FixedSecureRandom using the seed as it's input, it sounds a bit more like what you really need is a getPrivateData() on MLDSAPrivateKey though.

mrshih commented 2 days ago

John's correct about what's happened with getEncoded(). Would adding a getPrivateData() method which returned the regular long form help on this one?

With generating from a seed, you could do this by creating a FixedSecureRandom using the seed as it's input, it sounds a bit more like what you really need is a getPrivateData() on MLDSAPrivateKey though.

Thanks for your suggestion. I agree that implementing getPrivateData() in MLDSAPrivateKey would be helpful for accessing the regular long form of the private key.

However, since standard formats (as discussed in IETF 121 LAMPS working group) will only support the 32-byte seed representation for ML-DSA private keys, I'm wondering about key restoration scenarios. Currently, when I restore a private key using .generatePrivate(new PKCS8EncodedKeySpec(pkcs8PrivateKeyBytes)), I'm working with just the 32-byte seed.

Would it make sense to introduce an additional key specification class (perhaps something like MLDSAPrivateKeySpec) that could handle the regular long form returned by getPrivateData()?

Or perhaps, if the performance overhead of regenerating the full private key from the 32-byte seed is negligible, sticking with "only" the standard format might be a better approach? Based on my benchmark on a modest laptop, even with a single thread implementation, it can achieve 10K ops/s for private key restoration from the seed.

What are your thoughts on this approach?

dghgit commented 11 hours ago

I'd stick with the standard format where you can. We've added MLDSAPrivateKeyParameterSpec as well now which can also be used to process a long form private key with the a key factory. It's now in the current beta at https://www.bouncycastle.org/betas you'll find the code on github as well.