RFR: 8347938: Switch to latest ML-KEM private key encoding [v5]
Weijun Wang
weijun at openjdk.org
Mon Aug 4 21:07:09 UTC 2025
On Mon, 4 Aug 2025 19:59:50 GMT, Sebastian Stenzel <duke at openjdk.org> wrote:
>> `getEncoded` is the whole PKCS #8 encoding.
>> `getRawBytes` returns the content of the `privateKey` field inside the PKCS #8 encoding.
>> `getExpanded` is not related to encoding. It's what the underlying `NamedKEM` or `NamedSignature` will use.
>>
>> For ML-KEM, `getRawBytes` might be any of the 3 CHOICEs: seed, expandedKey, or both. `getExpanded` is always the expanded format (defined in FIPS 203). There is a slight difference even if `getRawBytes` is using the expandedKey choice: it has the OCTET STRING header but `getExpanded` does not.
>>
>> `engineTranslateKey` can re-encode the key depending on the "jdk.mlkem.pkcs8.encoding" security property. Therefore it works on the `getRawBytes` level.
>
> In X-Wing, the expanded decapsulation key is referred to as `sk_m`. I.e. *without* the CHOICE structure. So in order to have `NamedPKCS8Key.privKeyMaterial` contain the choice, I would have to do this:
>
>
> byte[] keyBytesWrappedInChoice = KeyChoices.writeToChoice(KeyChoices.Type.EXPANDED_KEY, null, sk_m);
> PrivateKey key = NamedPKCS8Key.internalCreate("ML-KEM", "ML-KEM-768", keyBytesWrappedInChoice, null);
>
>
> However, if I now call `keyFactory.translateKey(key)`, it'll throw:
>
>
> java.security.InvalidKeyException: key contains not enough info to translate
> at java.base/sun.security.util.KeyChoices.choiceToChoice(KeyChoices.java:220)
> at java.base/com.sun.crypto.provider.ML_KEM_Impls$KF.engineTranslateKey(ML_KEM_Impls.java:133)
> at java.base/java.security.KeyFactory.translateKey(KeyFactory.java:475)
>
>
> in base64 `sk_m` has this value (test case 1 from [X-Wing test vectors](https://datatracker.ietf.org/doc/html/draft-connolly-cfrg-xwing-kem-08#appendix-C)):
>
>
> IDjHPToyMBF3joBR7joyxgHNZdrAqPmpQIEj8GRIi+thihFD1Fqc+BbMlZuDnLsPH5y7DJWqXHsqhQNeiMRkT1VZjVF7psO12woyNCIYSHFQe8O/BspLfeOW/MFH4tONheMqoIKMUHvC28nEk4Q57YciB7y//iNoevxrdhG4/XrCetNairsm2RB0AQtsc1WhUUyluoExawtc3bmm2BmiGocA2PmbB4hBPVw8fXyhm4ACCJqKjKJ7BZBelLxh49fPLbaXxZYrC0s5O7Cbv/oujGxuk8K8NOpIFxZVSdota6ikQDe4ELd8Dhk0ppU3FjE0JhsjQshoNGtB2AkDgcDADDMb44giv+KWv8o1khgg52Q76KgrzlnMZdUn+ujHA1lK2FcYT+R52uyrJJec8WKCVtSPJdp+ajBrOaCoUlQMrRgMUMC0xnegLoubeWQBxdwUpSgloIjDG0RxVQRPCwgYczWfClLEaOaK/TlOiUisecKTeECk38gfr1IUvdocAtTO62klYLa4Z9fKO/uvXxwzHjap6uaWsJJeZ/GUMrCX+Helmdiu3DArkTgQl9kNIYgkapcOw1gKqxVDCtdHcntCp/fDy3sSDKaM1mZHdtmdZfMIbKW8sZiPHbyZqsJtZwEL0wjAzpeVnouGSCEvPSYuTfW3ThDLsKs+oFxI32S4EvgQNUd2vFd2kuUhmct6YDTLA9hh+Vs7jlEUYjtx1fAUIiO6F9tR1KWRskqkY8qWrWpJYBg61ySdKUq/kHEvhcieVqMGcId8DFpIUDhsCoIjmAQrabZyvTWb9ndKaboaa5kOFNGrEoA1iHovnOZgxhSQf/IXWncIZOJ0B+MSIYJ/OBgU2OrGvmq/28pxtHK1iSnBN5c2JXK/qhZdhkqdzHFhDbOyM7QOOyVnJuMXO3x+fXXPlCgrpVYivOZ5lhfJEuUQMPgm0abCeHScyGRSw2KXkXpg7icb/gWsTSedtEpGcTMie4BqeZaUZRYl
xQoGeRCeQPZvXIS7ajlPmBxge1S5DDCCw4Q9gUgCAXo6DNQ10ABGXTmba6kkUwFz+xREY1sapcykudONJ7eo8NUkerE0I/pGUErLZrBulhERusBpPuB9FIKXNdY6hEei9OxgfwKWfGMeUrtoUDOrv9hvViBycHXNP0UroNeTU2au3ycSwtkquVNHYtG94DOi8/Bu59ueXwEx4TZNrXNyW9NwknYF7TgZQBm55PVHurYtq1Cy4Wq2r5TLbFUe5DearAdyKZy746WLCOm29wyp7HRvIsSpTqlyYAla5PymyQov0lqbJexd9bAnXUhOByJqHXM0oou+JIpcVrJSQMuE39soR6iFHhB9icVUxjOKjlWr1zeu30EEzAhVD3JKk9gNrImWY+UJkXuWvfFcowE/EmIDDckeqHqrx8iVDsF0g0pLNYgTI6okoHMLccBHolui9abEBPcswpSRDuDOkDUbuESkSAqiXGRS9NClUTkKLToMyByJshhOHEhONaNrxlAxd4psk+aNscF84sAfABBjd/R84gU...
If you manually create an expanded-only key and then try to call`translateKey` on it but the security property is using the default value "seed", then of course the translation will not work.
Anyway, I don't think X-Wing implementation needs to touch that property. The X-Wing private key is always the 32-byte seed, and its "expanded" form can include the whole `expandDecapsulationKey` output, and then you can simply call internal `ML-KEM` methods on it.
-------------
PR Review Comment: https://git.openjdk.org/jdk/pull/24969#discussion_r2252579592
More information about the security-dev
mailing list