ML-KEM Programming Guide
Abbreviations and Acronyms
Term | Definition |
---|---|
API | Application Programming Interface |
KEM | Key Encapsulation Mechanism |
ML-DSA | Module-Lattice Key Encapsulation Algorithm |
PKCS | Public Key Cryptography Standards |
References
Document | Author | Title |
---|---|---|
FIPS 203 | NIST | Module-Lattice-Based Key-Encapsulation Mechanism Standard (nist.gov) |
OASIS | https://groups.oasis-open.org/higherlogic/ws/public/download/72453/pkcs11-spec-v3.2-wd08.docx |
Overview
This document discusses how the ML-KEM API, incorporated into the Luna HSM starting with firmware version 7.9.0, is made available to applications.
ML-KEM/Kyber services are supplied through mechanisms, objects and attributes of the Cryptoki interface.
NOTE Hybrid key agreement combines the secret from a legacy method (e.g. DH or RSA Transport) with the secret produced by ML-KEM to make the shared session symmetric key. That is outside the scope of this document.
If applications need to do Hybrid key derive then they must support that themselves, outside the HSM. This means that such applications will need to operate the HSM while it is not in FIPS approved configuration (formerly FIPS mode).
Refer to the NIST FIPS 203 standard publication for the definitive description of a KEM.
The shared secret created by the ML_KEM encryption and decryption is a 32 byte value which may to be used as a symmetric key (as it is randomly generated).
The Encapsulation and Decapsulation operations take a template specifying the attributes of the shared secret key object to be created.
The CKA_KEY_TYPE and CKA_VALUE_LENGTH may be used to create a symmetric key with a value length less than or equal 32 bytes.
ML-KEM Key Types and Sizes
ML-KEM keys may be generated with different sizes to balance size and performance.
CKA_KEY_TYPE CKA_PARAM_SET Public Key Private Key Cipher Shared Secret
CKA_KEY_TYPE | CKA_PARAM_SET | Public Key | Private Key | Cipher | Shared Secret |
---|---|---|---|---|---|
CKK_ML_KEM | CKP_ML_KEM_512 | 800 | 1632 | 768 | 32 |
CKK_ML_KEM | CKP_ML_KEM_768 | 1184 | 2400 | 1088 | 32 |
CKK_ML_KEM | CKP_ML_KEM_1024 | 1568 | 3168 | 1568 | 32 |
Lengths of keys and other values depend on the strength that is chosen.
ML-KEM Private Key Format
The decapsulation key dk has this format:
dk ← (dkPKE∥ek∥H(ek)∥z)
where:
>dk is the decapsulation key
>dkPEK is the low level decapsulation key
>ek is the encapsulation key (t || rho)
>H(ek) is a hash of the ek
>z is a random string used during implicit reject operation
A decapsulation key holds the value of the public key within it.
Public Keys
During the session establishment the public key value is passed from the party who did the key pair generation, to the party doing the encapsulation (and usually authenticated with an associated signature scheme).
The party who did the key pair generation does not use the public key object in the decapsulation operation. Instead, it uses the public key in the private key object.
Public keys should be validated before being used to encapsulate. Since public key objects need to be created before they can be used, validating them as the object is created precedes any use of the public key.
PKCS#8 style encoding of public keys is part of the PKCS#11 specification. Any private key and public key can provide a CKA_PUBLIC_KEY_INFO attribute, which (if present) holds a DER encoded public key. That is, an Algorithm OID and a public key value encoded together.
The C_CreateObject command recognizes either the CKA_VALUE or CKA_PUBLIC_KEY_INFO attributes as valid ways to create a public key object.
Key Cloning Backup and SKS
The ML-KEM keys are cloned during HA Key Cloning, which also enables backup (to compatible HSMs) with these keys.
Key Wrapping
ML-KEM Private keys may be represented in two forms.
1.Full: A collection of PKE keys, a hash and a random value = dkPKE∥ek∥H(ek)∥z
2.Seed: The Seeds that allows the Full representation to be re-generated; that is, two 32-byte random values
An HSM may internally store either (or both) form depending on performance/storage trade off.
Some applications specify the Seed form (i.e. ITEF).
The Luna HSM stores both Full and Seed forms in the Private key, in anticipation of PKCS#11 support for wrapping the Seed form. The Full form is always used for signing and the Seed form is used only for key wrapping/unwrapping.
Therefore, when cloning ML-KEM private keys, the full internal representation of the key is encoded and unwrapped into the target. That is, Seeds and Full components are cloned.
When wrapping an ML-KEM private key for export outside the Luna environment only the Seed format will be wrapped.
Wrapped Private Key Format PKCS#8
For wrapping ML-KEM private keys, the PKCS#8 scheme is used.
PKCS#8: Private-key information have ASN.1 type PrivateKeyInfo:
PrivateKeyInfo ::= SEQUENCE {
version Version,
privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
privateKey PrivateKey,
attributes [0] IMPLICIT Attributes OPTIONAL }
Version ::= INTEGER
PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier
PrivateKey ::= OCTET STRING
Attributes ::= SET OF Attribute
The fields of type PrivateKeyInfo have the following meanings:
version is the syntax version number, for compatibility with
future revisions. It is 0 for this version.
privateKeyAlgorithm identifies the private-key algorithm. See section below on CKA_PUBLIC_KEY_INFO attribute.
privateKey is an octet string whose contents are the value of the private key. The interpretation of the contents is defined in the registration of the private-key algorithm. For an ML-KEM private key the contents are the 64 byte ‘D|Z’ seed value (or potentially the expanded private key).
attributes is a set of attributes. These are the extended information that is encrypted along with the private-key information. ML-KEM attributes are absent.
See Internet X.509 Public Key Infrastructure - Algorithm Identifiers for Module-Lattice-Based Key-Encapsulation Mechanism (ML-KEM) (ietf.org) for details on ML_KEM encoding.
Capabilities and Policies
The container configuration flags CONTAINER_CONFIG_PRIVATE_KEY_UNWRAPPING (see Allow private key unwrapping) and CONTAINER_CONFIG_PRIVATE_KEY_WRAPPING (see Allow private key wrapping)should be enabled as appropriate for wrapping or unwrapping of ML-KEM private keys.
The container configuration flags CONTAINER_CONFIG_SECRET_KEY_UNWRAPPING (see Allow secret key unwrapping) and CONTAINER_CONFIG_SECRET_KEY_WRAPPING (see Allow secret key wrapping) should be enabled as appropriate for decapsulation or encapsulation operations.
Multiple Use Keys
There is no requirement in the FIPS 203 specification that ML-KEM must be used only once.
Therefore, you can generate a key pair and use it to establish different session keys for multiple sessions.
PKI
For the initial implementation, no Public Key Certs certificate creation is performed.
Key cloning Protocol in PQC hybrid mode
ML_KEM algorithm is added to cloning protocol version 4 (CPv4), but not to CPv1 or 2.
PKCS#11 Status
During the Luna HSM development and testing window for the release of firmware version 7.9.0, official specifications were still in draft status. To provide customers with usable functionality with respect to C_EncapsulateKey and C_DecapsulateKey functions, we have Implemented proprietary CA_EncapsulateKey and CA_DecapsulateKey, and will revisit standard versions when the specification solidifies.
Regarding ITEF progress on PKCS#8 format for ML_KEM private/public keys, we proceeded on the assumption that draft 8 of the ITEF spec describing PKCS#8 format for private and public keys will become official without change.
The wrapping of ML_KEM and ML_DSA private key SEEDs is not specified and therefore not implemented until an official API is known.
Unwrapping is possible for SEED or Expanded private ML_KEM keys in the initial release.
PKCS#11 C_EncapsulateKey and C_DecapsulateKey
Draft specification 3.2 adds mechanisms plus C_EncapsulateKey and C_DecapsulateKey functions plus new key types and attribute for ML-KEM.
The CKM_ML_KEM mechanism can be used only with these new functions.
Pending the official C_EncapsulateKey when PKCS#11 3.2 is officially released, we (Thales) provide a proprietary version with CA_EncapsulateKey.
C_EncapsulateKey
CA_EncapsulateKey
CK_RV C_EncapsulateKey(
CK_SESSION_HANDLE hSession, // IN session
CK_MECHANISM_PTR pMechanism, // IN encap mechanism and parameters (if any)
CK_OBJECT_HANDLE hPublicKey, // IN other parties’ public key
CK_ATTRIBUTE_PTR pTemplate, // IN attributes of new symmetric key
CK_ULONG ulAttributeCount, // IN
CK_BYTE_PTR pCiphertext, // OUT encrypted key or null
CK_ULONG_PTR pulCiphertextLen, // OUT length prediction allowed
CK_OBJECT_HANDLE_PTR phKey); // OUT handle of new key
The CKA_ENCAPSULATE attribute of the private key, which indicates whether the key supports encapsulation, MUST be CK_TRUE.
The new key has:
>the CKA_ALWAYS_SENSITIVE attribute set to CK_FALSE,
>the CKA_NEVER_EXTRACTABLE attribute set to CK_FALSE.
>the CKA_EXTRACTABLE set to the value of the input template with a default of CK_TRUE if not provided,
>the CKA_LOCAL attribute set to CKA_FALSE
If a call to C_EncapsulateKey cannot support the precise template supplied to it, it will fail and return without creating any key object.
The key object created by a successful call to C_EncapsulateKey has its CKA_LOCAL attribute set to CK_FALSE. In addition, the object created has a value for CKA_UNIQUE_ID generated and assigned.
C_DecapsulateKey
CA_DecapsulateKey
CK_RV C_DecapsulateKey(
CK_SESSION_HANDLE hSession, // IN session
CK_MECHANISM_PTR pMechanism, // IN mechanism and optional parameters
CK_OBJECT_HANDLE hPrivateKey, // IN decap key handle
CK_ATTRIBUTE_PTR pTemplate, // IN attributes of new key
CK_ULONG ulAttributeCount, // IN
CK_BYTE_PTR pCiphertextKey, // IN cipher texr
CK_ULONG ulCiphertextLen, // IN
CK_OBJECT_HANDLE_PTR phKey // OUT handle of new key
);
>C_Decapsulate creates a new secret key object based on the private key and cipher text generated by and encapsulate operation. The new key is identical the key returned by encapsulate. This function is a KEM style function.
>The CKA_DECAPSULATE attribute of the private key, which indicates whether the key supports decapsulation, MUST be CK_TRUE.
The new key has:
>the CKA_ALWAYS_SENSITIVE attribute set to CK_FALSE,
>the CKA_NEVER_EXTRACTABLE attribute set to CK_FALSE.
>the CKA_EXTRACTABLE set to the value of the input template with a default of CK_TRUE if not provided,
>the CKA_LOCAL attribute set to CKA_FALSE
If a call to C_DecapsulateKey cannot support the precise template supplied to it, it fails and returns without creating any key object.
FUNCTION LISTS
The older PKCS#11 specs defined a function table called CK_FUNCTION_LIST. The function C_GetFunctionList is used to return a pointer it.
From PKCS#11 version 3.0, cryptoki supports multiple function tables. As well as the original CK_FUNCTION_LIST you can fetch a CK_FUNCTION_LIST_3_0 or a CK_FUNCTION_LIST_3_2.
Each function table is a super-set of the preceding i.e. the first part of the CK_FUNCTION_LIST_3_0 is equal to CK_FUNCTION_LIST.
To manage these new function tables a new structure is defined. CK_INTERFACE is a structure which contains an interface name with a function list and flag.
The C_GetInterfaceList and C_GetInterface are new functions to query and fetch any supported function list (including Vendor defined).
PKCS#11 Constants
Here are the current working of KEM constantsvalues.
#define CKA_PARAMETER_SET 0x061d
#define CKA_ENCAPSULATE 0x0633
#define CKA_DECAPSULATE 0x0634
#define CKA_PUBLIC_KEY_INFO 0x0129
#define CKA_SEED TBD
#define CKK_ML_KEM 0x49
#define CKM_ML_KEM 0x17
#define CKM_ML_KEM_KEY_PAIR_GEN 0xf
typedef CK_ULONG CK_ML_KEM_PARAMETER_SET_TYPE;
Parameter set types:
CKP_ML_KEM_512 1
CKP_ML_KEM_768 2
CKP_ML_KEM_1024 3
Mechanism Flags include bits to show if the Mechanism supports Encapsulation or Decapsulation.
#define CKF_ENCAPSULATE 0x10000000UL
#define CKF_DECAPSULATE 0x20000000UL
ML_KEM Key Pair Generate
CKM_ML_KEM_KEY_PAIR_GEN is a mechanism that can be used with C_GenerateKeyPair functions.
The mechanism does not take a parameter.
For this mechanism, the ulMinKeySize and ulMaxKeySize fields of the CK_MECHANISM_INFO structure specify the supported range of ML-KEM public key in bytes.
The attribute CKA_PARAMETER_SET in public key template must be used to specify the key size required.
The CKA_PARAMETER_SET attribute is the only required content in either key template. All other attributes are set to a default value by the Key Generation mechanism.
Private Key Attributes
Attribute | KeyPair Gen Priv Template Value | Default | Comments |
---|---|---|---|
CKA_CLASS | CKO_PRIVATE_KEY | CKO_PRIVATE_KEY |
Must match default if present |
CKA_KEY_TYPE | CKK_ML_KEM | CKK_ML_KEM | Must match default if present |
CKA_TOKEN | True or False | False | |
CKA_PRIVATE | True | True | Must not be set false (depending on partition policies) |
CKA_LABEL | Optional string | Null |
User defined |
CKA_MODIFIABLE | True or False | True | User defined |
CKA_PARAMETER_SET | CK_ML_KEM_PARAMETER_SET_TYPE | May be supplied by caller | |
CKA_ID | Optional byte array | Null | User defined |
CKA_START_DATE | Optional | Null | User defined |
CKA_END_DATE | Optional | Null | User defined |
CKA_DERIVE | False | False | Default False |
CKA_SUBJECT | Optional string | Null | User defined |
CKA_SENSITIVE | True | True | Must match default if present |
CKA_DECRYPT | False | False | Default False |
CKA_SIGN | False | False | Default False |
CKA_SIGN_RECOVER | False | False | Default False |
CKA_UNWRAP | False | False | Default False |
CKA_EXTRACTABLE | False | Depends on partition policies | Default False |
CKA_UNWRAP_TEMPLATE | Optional template | Null |
User defined |
CKA_DECAPSULATE | True or False | True | Default to True |
The following attributes must not be specified in the private key template. They are all supplied by the mechanism. |
|||
CKA_VALUE | Decapsulation key (sensitive) | Provided by mechanism | |
CKA_SEED | Key Gen Seed (sensitive) | Provided by mechanism | |
CKA_LOCAL | True | Provided by mechanism | |
CKA_PUBLIC_KEY_INFO | DER encoded Encapsulation Key | Provided by mechanism | |
CKA_PUBLIC_KEY | Raw Encapsulation Key | Provided by mechanism | |
CKA_ALWAYS_SENSITIVE | Read only | Based on sensitive | Provided by mechanism |
CKA_NEVER_EXTRACTABLE | Read only | Based on extractable | Provided by mechanism |
Attributes described as “Provided by Mechanism” must not be put in the template during key generation.
Public Key Attributes
Attribute KeyPair Gen Pub Template Value Default Comments
Attribute | KeyPair Gen Priv Template Value | Default | Comments |
---|---|---|---|
CKA_CLASS | CKO_PUBLIC_KEY | CKO_PUBLIC_KEY | Must match default if present |
CKA_KEY_TYPE | CKK_ML_KEM | CKK_ML_KEM | Must match default if present |
CKA_TOKEN | True or False | False | |
CKA_PRIVATE | True | True | |
CKA_LABEL | Optional string | Null | User defined |
CKA_MODIFIABLE | True or False | True | User defined |
CKA_PARAMETER_SET | CK_ML_KEM_PARAMETER_SET_TYPE | Must be supplied during KeyGen – no default | |
CKA_ID | Optional byte array | Null | User defined |
CKA_START_DATE | Optional | Null | User defined |
CKA_END_DATE | Optional | Null | User defined |
CKA_DERIVE | False | False | Default False |
CKA_SUBJECT | Optional string | Null | User defined |
CKA_ENCRYPT | False | False | Default False |
CKA_VERIFY | False | False | Default False |
CKA_VERIFY_RECOVER | False | False | Default False |
CKA_WRAP | False | False | Default False |
CKA_ENCAPSULATE | True or False | True | Default to True |
The following attributes must not be specified in the public key template. They are all supplied by the mechanism. |
|||
CKA_VALUE | Encapsulation key | Provided by mechanism | |
CKA_PUBLIC_KEY_INFO | DER encoded Encapsulation Key | Provided by mechanism | |
CKA_LOCAL | True | Provided by mechanism |
CKA_PUBLIC_KEY_INFO attribute
The CKA_PUBLIC_KEY_INFO attribute holds the public key values as a DER encoded SubjectPublicKeyInfo (as used in X509 certs).
SubjectPublicKeyInfo ::= SEQUENCE {
algorithm AlgorithmIdentifier,
subjectPublicKey BIT STRING }
The ‘algorithm’ is a sequence with an OID and optional params.
AlgorithmIdentifier ::= SEQUENCE {
algorithm OBJECT IDENTIFIER,
parameter ANY DEFINED BY algorithm OPTIONAL }
The recommendation uses a different OID for each key size (does not use the algorithm parameter field).
kems OBJECT IDENTIFIER ::= { joint-iso-ccitt(2) country(16) us(840) organization(1) gov(101) csor(3) nistAlgorithm(4) kems(4) } id-alg-ml-kem-512 OBJECT IDENTIFIER ::= { kems 1 } id-alg-ml-kem-768 OBJECT IDENTIFIER ::= { kems 2 } id-alg-ml-kem-1024 OBJECT IDENTIFIER ::= { kems 3 }
2.16.840.1.101.3.4.4.1 (ML-KEM-512)
2.16.840.1.101.3.4.4.2 (ML-KEM-768)
2.16.840.1.101.3.4.4.3 (ML-KEM-1024)
DER Encoding for id-alg-ml-kem-512:
06 -- OID Tag
09 -- Length
60 86 48 01 65 03 04 04 01 -- OID ML-KEM-512
The subjectPublicKey is a bit string that holds the public key value. Its content format depends on the algorithm. For ML-KEM this is an octet array of size 800 or 1184 or 1568 (not a DER encoding – just the 800 etc bytes with the usual leading ‘00’ to indicate no bit padding).
The parameter field in the AlgorithmIdentifier is empty (missing).
Creating a ML_KEM Public Key Object
Cryptoki uses C_CreateObject to construct public key objects from known public keys.
The caller supplies sufficient attributes to allow the object to be created.
Attribute | KeyPair Gen Priv Template Value | Default | Comments |
---|---|---|---|
CKA_CLASS | CKO_PUBLIC_KEY | CKO_PUBLIC_KEY | Must be set |
CKA_KEY_TYPE | CKK_ML_KEM | CKK_ML_KEM | Must be set |
CKA_TOKEN | True or False |
False |
|
CKA_PRIVATE | True | True | |
CKA_LABEL | Optional string | Null | User defined |
CKA_MODIFIABLE | True or False | True | User defined |
CKA_PARAMETER_SET | CK_ML_KEM_PARAMETER_SET_TYPE | Optional, must match key length if present | |
CKA_ID | Optional byte array | Null | User defined |
CKA_START_DATE | Optional | Null | User defined |
CKA_END_DATE | Optional | Null | User defined |
CKA_DERIVE | False | False | DefaultFalse |
CKA_SUBJECT | Optional string | Null | User defined |
CKA_ENCRYPT | False | False | DefaultFalse |
CKA_VERIFY | False | False | DefaultFalse |
CKA_VERIFY_RECOVER | False | False | DefaultFalse |
CKA_WRAP | False | False | DefaultFalse |
CKA_ENCAPSULATE | True or False | True | Default to True |
CKA_VALUE | Encapsulation key | Either CKA_VALUE or CKA_PUBLIC_KEY_INFO must be provided | |
CKA_PUBLIC_KEY_INFO |
DER encoded Encapsulation Key |
||
The following attributes must not be specified in the public key template. They are all supplied by the mechanism. |
|||
CKA_LOCAL | False |
CKM_ML_KEM Key Agreement
The ML-KEM Key Agreement mechanism, denoted CKM_ML_KEM, is a mechanism for key encapsulation and decapsulation using ML-KEM. Both are defined in [FIPS 203].
It has no parameters.
When used by C_EncapsulateKey, this mechanism generates a 32 byte shared secret value.
The shared secret is then used to create a symmetric key object - as defined by the template.
The shared secret is also encapsulated using the ML-KEM Public Key and ML-KEM.Enc() operation to make a cipher text.
When used in C_DecapsulateKey, this mechanism recovers the shared secret from an encapsulated cipher text using the ML-KEM Private key with the ML-KEM.Dec() operation. The shared secret is then used to create a symmetric key object - as defined by the template.
The CKA_CLASS is supplied by the mechanism as CKO_SECRET_KEY.
The CKA_KEY_TYPE must be specified in the template and may be CKK_AES or CKK_GENERIC_SECRET.
The mechanism contributes the result as the CKA_VALUE attribute of the new key; other attributes required by the key type must be specified in the template.
The CKA_VALUE_LEN attribute of the template can be used to set the new key’s length where the key type is ambiguous e.g. CKK_AES.
If the output key type is CKK_GENERIC_SECRET then resulting keys must contain the full output of the underlying mechanism e.g. 32 bytes for ML_KEM.
Otherwise, if the output key is an AES and is shorter than the shared secret, then the CKA_VALUE will be selected from the left most bytes of the shared secret.
The container configuration flags CONTAINER_CONFIG_SECRET_KEY_UNWRAPPING and CONTAINER_CONFIG_SECRET_KEY_WRAPPING should be enabled as appropriate for decapsulation or encapsulation of Secret Keys.
The CKA_KEYRING_OUID may be provided in the template for the new symmetric key or be present in the Decapsulation Key or Encapsulation Key.
En/Decapsulation Key has CKA_KEYRING_OUID |
New Key Template has CKA_KEYRING_OUID |
Behavior |
---|---|---|
No | No | No action |
Yes | No | CKA_KEYRING_UOID of new key is set to value of Base key |
No | Yes | Verify OUID is a valid OUID and caller has access |
Yes | Yes | Both OUID values must match else CKR_ATTRIBUTE_VALUE_INVALID is returned |
Error Codes
TBD.
Key Attributes
There are new attributes - CKA_PARAMETER_SET, CKA_PUBLIC_KEY_INFO, CKA_SEED, CKA_DECAPSULATE and CKA_ENCAPSULATE.
Private Key Import/Export
Private keys can be imported and exported with existing PKCS#11 functions using the appropriate published DER encoding.
Key Backup and Cloning
Backups and cloning of ML_KEM keys is supported.