> ## Documentation Index
> Fetch the complete documentation index at: https://docs.futurex.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Post-quantum cryptography

> Use the CryptoHub-backed PKCS#11 module's PKCS#11 v3.2 interface for ML-DSA signatures and ML-KEM key encapsulation, including objects, parameter sets, and C examples.

The CryptoHub-backed PKCS#11 module reports Cryptoki version 3.2 and implements the PKCS#11 v3.2 post-quantum objects, mechanisms, attributes, and functions. This page describes the module's ML-DSA signature (FIPS 204) and ML-KEM key encapsulation (FIPS 203) support and shows how to use them from C. For the full advertised mechanism list, see [Appendix A](./Appendices/Appendix_A_Supported_mechanisms).

<Note>
  Post-quantum support depends on the module build and the backing CryptoHub release. Confirm the mechanisms and parameter sets your deployment advertises with `C_GetMechanismList` before you rely on them. The examples below reflect the build documented in [Overview](./Overview).
</Note>

## Post-quantum objects and attributes

The module exposes standardized PKCS#11 v3.2 post-quantum key types and attributes:

| Item                       | Purpose                                                              |
| -------------------------- | -------------------------------------------------------------------- |
| `CKK_ML_DSA`               | Key type for ML-DSA (FIPS 204) signature keys.                       |
| `CKK_ML_KEM`               | Key type for ML-KEM (FIPS 203) key-encapsulation keys.               |
| `CKA_PARAMETER_SET`        | Selects the parameter set for a post-quantum key at generation time. |
| `CKA_ENCAPSULATE`          | Marks a key usable for encapsulation.                                |
| `CKA_DECAPSULATE`          | Marks a key usable for decapsulation.                                |
| `CKA_ENCAPSULATE_TEMPLATE` | Template applied to the key produced by encapsulation.               |
| `CKA_DECAPSULATE_TEMPLATE` | Template applied to the key produced by decapsulation.               |

### Parameter sets

Post-quantum keys are selected by parameter set through `CKA_PARAMETER_SET`, not by a key length in bits:

* **ML-DSA:** `ML-DSA-44`, `ML-DSA-65`, `ML-DSA-87` (selected with `CKP_ML_DSA_44`, `CKP_ML_DSA_65`, `CKP_ML_DSA_87`).
* **ML-KEM:** `ML-KEM-512`, `ML-KEM-768`, `ML-KEM-1024` (selected with `CKP_ML_KEM_512`, `CKP_ML_KEM_768`, `CKP_ML_KEM_1024`).

### Usage requirements

* ML-DSA keys require sign and verify usage (`CKA_SIGN` on the private key, `CKA_VERIFY` on the public key).
* ML-KEM keys require the appropriate encapsulation or decapsulation usage and the correct public/private role: encapsulation uses an ML-KEM public key, and decapsulation requires the ML-KEM private key.

## Discover the post-quantum mechanisms

Before you generate keys, confirm the module advertises the mechanisms and parameter sets you expect. Enumerate the mechanisms with `C_GetMechanismList` and inspect each one's flags and ranges with `C_GetMechanismInfo`.

```c expandable lines wrap title="C" theme={null}
    CK_ULONG count = 0;
    rv = pFunctions->C_GetMechanismList(slotID, NULL_PTR, &count);
    if (rv != CKR_OK) { fprintf(stderr, "C_GetMechanismList (size) failed: 0x%lx\n", rv); return 1; }

    CK_MECHANISM_TYPE *mechs = malloc(count * sizeof(CK_MECHANISM_TYPE));
    rv = pFunctions->C_GetMechanismList(slotID, mechs, &count);
    if (rv != CKR_OK) { fprintf(stderr, "C_GetMechanismList failed: 0x%lx\n", rv); free(mechs); return 1; }

    for (CK_ULONG i = 0; i < count; i++) {
        CK_MECHANISM_INFO info;
        if (pFunctions->C_GetMechanismInfo(slotID, mechs[i], &info) == CKR_OK) {
            printf("mechanism 0x%08lx  min=%lu max=%lu flags=0x%lx\n",
                   mechs[i], info.ulMinKeySize, info.ulMaxKeySize, info.flags);
        }
    }
    free(mechs);
```

## Generate an ML-DSA key pair

Generate an ML-DSA signing key pair by passing `CKM_ML_DSA_KEY_PAIR_GEN` and selecting the parameter set with `CKA_PARAMETER_SET`.

```c expandable lines wrap title="C" theme={null}
    CK_MECHANISM mech = {CKM_ML_DSA_KEY_PAIR_GEN, NULL_PTR, 0};
    CK_ML_DSA_PARAMETER_SET_TYPE paramSet = CKP_ML_DSA_65;   // ML-DSA-44 / 65 / 87

    CK_ATTRIBUTE publicKeyTemplate[] = {
        {CKA_TOKEN, &(CK_BBOOL){CK_TRUE}, sizeof(CK_BBOOL)},
        {CKA_LABEL, "My ML-DSA Public Key", strlen("My ML-DSA Public Key")},
        {CKA_PARAMETER_SET, &paramSet, sizeof(paramSet)},
        {CKA_VERIFY, &(CK_BBOOL){CK_TRUE}, sizeof(CK_BBOOL)}
    };

    CK_ATTRIBUTE privateKeyTemplate[] = {
        {CKA_TOKEN, &(CK_BBOOL){CK_TRUE}, sizeof(CK_BBOOL)},
        {CKA_LABEL, "My ML-DSA Private Key", strlen("My ML-DSA Private Key")},
        {CKA_PRIVATE, &(CK_BBOOL){CK_TRUE}, sizeof(CK_BBOOL)},
        {CKA_SENSITIVE, &(CK_BBOOL){CK_TRUE}, sizeof(CK_BBOOL)},
        {CKA_SIGN, &(CK_BBOOL){CK_TRUE}, sizeof(CK_BBOOL)}
    };

    CK_OBJECT_HANDLE hPub, hPriv;
    rv = pFunctions->C_GenerateKeyPair(hSession, &mech,
            publicKeyTemplate, sizeof(publicKeyTemplate)/sizeof(CK_ATTRIBUTE),
            privateKeyTemplate, sizeof(privateKeyTemplate)/sizeof(CK_ATTRIBUTE),
            &hPub, &hPriv);
    if (rv != CKR_OK) { fprintf(stderr, "C_GenerateKeyPair failed: 0x%lx\n", rv); return 1; }
```

## Sign and verify with ML-DSA

Sign with the pure `CKM_ML_DSA` mechanism. To sign a pre-hashed (externalized) message digest, use one of the `CKM_HASH_ML_DSA*` mechanisms instead and supply the matching digest.

```c expandable lines wrap title="C" theme={null}
    CK_MECHANISM signMech = {CKM_ML_DSA, NULL_PTR, 0};
    CK_BYTE message[] = "message to sign";
    CK_BYTE signature[8192];
    CK_ULONG sigLen = sizeof(signature);

    rv = pFunctions->C_SignInit(hSession, &signMech, hPriv);
    if (rv != CKR_OK) { fprintf(stderr, "C_SignInit failed: 0x%lx\n", rv); return 1; }
    rv = pFunctions->C_Sign(hSession, message, sizeof(message) - 1, signature, &sigLen);
    if (rv != CKR_OK) { fprintf(stderr, "C_Sign failed: 0x%lx\n", rv); return 1; }

    rv = pFunctions->C_VerifyInit(hSession, &signMech, hPub);
    if (rv != CKR_OK) { fprintf(stderr, "C_VerifyInit failed: 0x%lx\n", rv); return 1; }
    rv = pFunctions->C_Verify(hSession, message, sizeof(message) - 1, signature, sigLen);
    printf("ML-DSA verify: %s\n", rv == CKR_OK ? "OK" : "FAILED");
```

## Generate an ML-KEM key pair

Generate an ML-KEM key pair with `CKM_ML_KEM_KEY_PAIR_GEN`, selecting the parameter set with `CKA_PARAMETER_SET` and marking the keys for encapsulation and decapsulation.

```c expandable lines wrap title="C" theme={null}
    CK_MECHANISM mech = {CKM_ML_KEM_KEY_PAIR_GEN, NULL_PTR, 0};
    CK_ML_KEM_PARAMETER_SET_TYPE paramSet = CKP_ML_KEM_768;  // ML-KEM-512 / 768 / 1024

    CK_ATTRIBUTE publicKeyTemplate[] = {
        {CKA_TOKEN, &(CK_BBOOL){CK_TRUE}, sizeof(CK_BBOOL)},
        {CKA_LABEL, "My ML-KEM Public Key", strlen("My ML-KEM Public Key")},
        {CKA_PARAMETER_SET, &paramSet, sizeof(paramSet)},
        {CKA_ENCAPSULATE, &(CK_BBOOL){CK_TRUE}, sizeof(CK_BBOOL)}
    };

    CK_ATTRIBUTE privateKeyTemplate[] = {
        {CKA_TOKEN, &(CK_BBOOL){CK_TRUE}, sizeof(CK_BBOOL)},
        {CKA_LABEL, "My ML-KEM Private Key", strlen("My ML-KEM Private Key")},
        {CKA_PRIVATE, &(CK_BBOOL){CK_TRUE}, sizeof(CK_BBOOL)},
        {CKA_SENSITIVE, &(CK_BBOOL){CK_TRUE}, sizeof(CK_BBOOL)},
        {CKA_DECAPSULATE, &(CK_BBOOL){CK_TRUE}, sizeof(CK_BBOOL)}
    };

    CK_OBJECT_HANDLE hPub, hPriv;
    rv = pFunctions->C_GenerateKeyPair(hSession, &mech,
            publicKeyTemplate, sizeof(publicKeyTemplate)/sizeof(CK_ATTRIBUTE),
            privateKeyTemplate, sizeof(privateKeyTemplate)/sizeof(CK_ATTRIBUTE),
            &hPub, &hPriv);
    if (rv != CKR_OK) { fprintf(stderr, "C_GenerateKeyPair failed: 0x%lx\n", rv); return 1; }
```

## Encapsulate and decapsulate with ML-KEM

ML-KEM uses the PKCS#11 v3.2 `C_EncapsulateKey` and `C_DecapsulateKey` functions. Encapsulation takes the recipient's ML-KEM public key, produces a ciphertext, and returns a handle to a freshly derived shared-secret key. Decapsulation takes the ciphertext and the recipient's private key and returns a handle to the same shared-secret key. Use the result-key template to shape the derived key (for example, as an AES key), and handle the ciphertext-sizing call by querying the required length first.

```c expandable lines wrap title="C" theme={null}
    CK_MECHANISM kemMech = {CKM_ML_KEM, NULL_PTR, 0};

    // Template for the derived shared-secret key (for example, a 256-bit AES key).
    CK_KEY_TYPE aesType = CKK_AES;
    CK_OBJECT_CLASS secretClass = CKO_SECRET_KEY;
    CK_ATTRIBUTE resultTemplate[] = {
        {CKA_CLASS, &secretClass, sizeof(secretClass)},
        {CKA_KEY_TYPE, &aesType, sizeof(aesType)},
        {CKA_TOKEN, &(CK_BBOOL){CK_FALSE}, sizeof(CK_BBOOL)},
        {CKA_ENCRYPT, &(CK_BBOOL){CK_TRUE}, sizeof(CK_BBOOL)},
        {CKA_DECRYPT, &(CK_BBOOL){CK_TRUE}, sizeof(CK_BBOOL)}
    };

    // First call with a NULL ciphertext buffer to learn the required size.
    CK_ULONG ctLen = 0;
    CK_OBJECT_HANDLE hSharedEnc = CK_INVALID_HANDLE;
    rv = pFunctions->C_EncapsulateKey(hSession, &kemMech, hPub,
            resultTemplate, sizeof(resultTemplate)/sizeof(CK_ATTRIBUTE),
            NULL_PTR, &ctLen, &hSharedEnc);
    if (rv != CKR_OK) { fprintf(stderr, "C_EncapsulateKey (size) failed: 0x%lx\n", rv); return 1; }

    CK_BYTE *ciphertext = malloc(ctLen);
    rv = pFunctions->C_EncapsulateKey(hSession, &kemMech, hPub,
            resultTemplate, sizeof(resultTemplate)/sizeof(CK_ATTRIBUTE),
            ciphertext, &ctLen, &hSharedEnc);
    if (rv != CKR_OK) { fprintf(stderr, "C_EncapsulateKey failed: 0x%lx\n", rv); free(ciphertext); return 1; }

    // The recipient decapsulates the ciphertext with the ML-KEM private key.
    CK_OBJECT_HANDLE hSharedDec = CK_INVALID_HANDLE;
    rv = pFunctions->C_DecapsulateKey(hSession, &kemMech, hPriv,
            resultTemplate, sizeof(resultTemplate)/sizeof(CK_ATTRIBUTE),
            ciphertext, ctLen, &hSharedDec);
    if (rv != CKR_OK) { fprintf(stderr, "C_DecapsulateKey failed: 0x%lx\n", rv); free(ciphertext); return 1; }

    free(ciphertext);
    // hSharedEnc and hSharedDec now reference the same shared secret.
```

## Handle post-quantum errors

Check for these return values when working with post-quantum mechanisms:

| Return value                      | Meaning                                                                                                                               |
| --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| `CKR_PARAMETER_SET_NOT_SUPPORTED` | The requested `CKA_PARAMETER_SET` is not available on this module or backend.                                                         |
| `CKR_TEMPLATE_INCONSISTENT`       | The key template conflicts with the mechanism or parameter set — for example, requesting ML-KEM encapsulation usage on an ML-DSA key. |
| `CKR_MECHANISM_INVALID`           | The mechanism is not advertised for the selected slot. Re-check with `C_GetMechanismList`.                                            |

## Java and SunPKCS11 limitations

<Warning>
  Java's SunPKCS11 provider and the JDK may lag the module's PKCS#11 v3.2 post-quantum and KEM features. The native provider can expose mechanisms that the SunPKCS11 layer cannot name or invoke. Do not assume a Java application reaches every mechanism the native module advertises. For post-quantum operations, use the native C Cryptoki interface, or confirm your specific JDK and SunPKCS11 build support the ML-DSA and ML-KEM mechanisms before relying on them.
</Warning>
