Enterprise editionstability: Experimental
Post-quantum signing preview — Deep Reference
At a glance
Section titled “At a glance”This page is the contract-level reference for the post-quantum signing (PQS) preview surface in NextPDF Enterprise. It covers three public symbols: the Pkcs11PqsAlgorithm parameter-set enum, the PqsPreviewFeature process gate, and the PqsCapabilityStatus descriptor. It also documents the NEXTPDF_FEATURE_PREVIEW_PQS_HSM environment gate.
The surface is experimental and default-off. It recognises ML-DSA (FIPS 204) and SLH-DSA (FIPS 205) algorithm identifiers, parameter sets, and signature lengths. Recognition is not a validation verdict. There is no post-quantum verification path. No AdES, FIPS-validation, or conformance claim is made, and the preview flag cannot create one. The consuming signing entry point, Pkcs11Signer::signPqs(), is described on the capability page.
Availability & licensing
Section titled “Availability & licensing”This capability ships in NextPDF Enterprise (nextpdf/enterprise) and activates with an Enterprise-tier license envelope. A deployment without that entitlement does not load the capability’s classes. Compare editions and get a license.
The license activates the Enterprise PKCS#11 surface as a whole. The post-quantum path inside it stays a preview regardless of license tier. Two independent opt-ins are still required: the process gate documented here and the per-signer constructor flag on Pkcs11Signer.
Public API surface
Section titled “Public API surface”| Symbol | Parameters | Default behavior | Returns | Throws or fails with | Notes |
|---|---|---|---|---|---|
Pkcs11PqsAlgorithm | string-backed enum, 15 cases | Names one FIPS 204 / FIPS 205 parameter set per case | enum case | Nothing on case access | Case values are the parameter-set names, e.g. ML-DSA-65. |
Pkcs11PqsAlgorithm::isMlDsa() | none | Family test | bool | Does not throw | true for MlDsa44, MlDsa65, MlDsa87. |
Pkcs11PqsAlgorithm::isSlhDsa() | none | Negation of isMlDsa() | bool | Does not throw | true for the twelve SLH-DSA cases. |
Pkcs11PqsAlgorithm::mechanismId() | none | Maps the family to the candidate PKCS#11 v3.1 PQ mechanism id | int | PHP Error when the runtime lacks the provisional Pkcs11 PQ constants | CKM_ML_DSA or CKM_SLH_DSA; both ids are provisional. |
Pkcs11PqsAlgorithm::parameterSetId() | none | Maps the case to the OASIS parameter-set discriminator | int | PHP Error when the runtime lacks the provisional Pkcs11 PQ constants | CKP_* values; provisional. |
Pkcs11PqsAlgorithm::signatureLength() | none | FIPS-mandated signature byte length for the case | int (positive) | Does not throw | Consumed by the signing path to reject a returned signature of unexpected length. |
Pkcs11PqsAlgorithm::nistCategory() | none | Claimed NIST security-strength category | int | Does not throw | Returns 1, 2, 3, or 5. |
PqsPreviewFeature | string-backed enum, 1 case | Single case PREVIEW_PQS_HSM; constant ENV_PREVIEW_PQS_HSM | enum case | Nothing on case access | The process-level preview gate. |
PqsPreviewFeature::isEnabled() | none | Reads getenv() live; strict comparison against the string 1 | bool | Does not throw | Absent variable or any other value, including 0, true, yes, is off. |
PqsCapabilityStatus::__construct() | nine named readonly fields | Builds an arbitrary descriptor instance | PqsCapabilityStatus | Does not throw | current() is the canonical constructor. |
PqsCapabilityStatus::current() | none | Builds the descriptor for the surrounding process | PqsCapabilityStatus | Does not throw | Every claim boolean is fixed; only hsmRoundtripPreviewEnabled varies with the gate. |
PqsCapabilityStatus::summary() | none | One-line status text | string | Does not throw | Phrasing carries no availability, archival, or validation claim. |
enum Pkcs11PqsAlgorithm: string
case MlDsa44 = 'ML-DSA-44';case MlDsa65 = 'ML-DSA-65';case MlDsa87 = 'ML-DSA-87';
case SlhDsaSha2_128s = 'SLH-DSA-SHA2-128s';case SlhDsaShake_128s = 'SLH-DSA-SHAKE-128s';case SlhDsaSha2_128f = 'SLH-DSA-SHA2-128f';case SlhDsaShake_128f = 'SLH-DSA-SHAKE-128f';
case SlhDsaSha2_192s = 'SLH-DSA-SHA2-192s';case SlhDsaShake_192s = 'SLH-DSA-SHAKE-192s';case SlhDsaSha2_192f = 'SLH-DSA-SHA2-192f';case SlhDsaShake_192f = 'SLH-DSA-SHAKE-192f';
case SlhDsaSha2_256s = 'SLH-DSA-SHA2-256s';case SlhDsaShake_256s = 'SLH-DSA-SHAKE-256s';case SlhDsaSha2_256f = 'SLH-DSA-SHA2-256f';case SlhDsaShake_256f = 'SLH-DSA-SHAKE-256f';
public function isMlDsa(): boolpublic function isSlhDsa(): boolpublic function mechanismId(): intpublic function parameterSetId(): intpublic function signatureLength(): intpublic function nistCategory(): intenum PqsPreviewFeature: string
case PREVIEW_PQS_HSM = 'preview_pqs_hsm';
public const string ENV_PREVIEW_PQS_HSM = 'NEXTPDF_FEATURE_PREVIEW_PQS_HSM';
public function isEnabled(): boolfinal readonly class PqsCapabilityStatus
public const string MATURITY_PREVIEW_EXPERIMENTAL = 'preview-experimental';public const string MECHANISM_STATUS_PROVISIONAL = 'provisional';
public function __construct( public bool $hsmRoundtripPreviewEnabled, public bool $generallyAvailable, public bool $adesCompliant, public bool $verificationAvailable, public bool $conformanceClaimed, public bool $recognitionOnly, public string $maturity, public string $mechanismIdStatus, public string $envGate,)
public static function current(): selfpublic function summary(): stringBehavior contract
Section titled “Behavior contract”- Parameter-set catalogue.
NextPDF\Enterprise\Security\Signature\Hsm\Pkcs11PqsAlgorithmenumerates three ML-DSA sets (FIPS 204) and twelve SLH-DSA sets (FIPS 205 §11.p12, Table 2). Each case maps to a provisional mechanism id, a parameter-set discriminator, a FIPS-mandated signature byte length, and a claimed NIST category. - Signature lengths.
signatureLength()returns 2420, 3309, and 4627 bytes forMlDsa44,MlDsa65, andMlDsa87, per FIPS 204 §4.p15 (Table 2). The SLH-DSA cases return 7856, 17088, 16224, 35664, 29792, and 49856 bytes by level and variant, per FIPS 205 §11 (Table 2). The consuming signer throwsHsmOperationExceptionwhen a returned signature has a different length, mirroring the FIPS 204 §x34 length-rejection discipline. - Categories.
nistCategory()returns 2, 3, and 5 for the ML-DSA cases, per FIPS 204 §4.p9. The SLH-DSA cases return 1, 3, and 5 by security-parameter level. - Process gate.
PqsPreviewFeature::PREVIEW_PQS_HSMis off by default.isEnabled()returnstrueonly when the environment variableNEXTPDF_FEATURE_PREVIEW_PQS_HSMequals the string1exactly. The read is live on every call; nothing is memoised. - Complementary gating. The process gate is separate from the per-signer
$enablePostQuantumconstructor opt-in onPkcs11Signer. The signing call fails closed without the per-signer opt-in. The process gate exists as a single auditable boundary for any future round-trip or archival behavior. - Honesty invariant.
NextPDF\Enterprise\Security\Signature\Hsm\PqsCapabilityStatus::current()hard-codesgenerallyAvailable,adesCompliant,verificationAvailable, andconformanceClaimedtofalse, andrecognitionOnlytotrue. No configuration, constructor option, or environment flag flips a claim on. OnlyhsmRoundtripPreviewEnabledreflects the gate. - No verification path. NextPDF has no post-quantum verification path. A recognised algorithm identifier or a well-formed signature length is never an acceptance verdict.
Edge cases & failure modes
Section titled “Edge cases & failure modes”- Setting the gate variable to
0,true,yes,on, or an empty string leaves the gate off. Only the exact string1enables it. putenv()changes take effect on the nextisEnabled()call because the read is live. A gate toggled mid-process is observed immediately.mechanismId()andparameterSetId()resolve constants from thePkcs11extension namespace. A runtime without the provisional post-quantum extension constants fails with a PHPError(undefined constant) at call time.- The mechanism and parameter-set ids are provisional. OASIS has not finalised the PKCS#11 v3.1 post-quantum registry. A token whose firmware assigns different ids will fail at the PKCS#11 layer; operators must confirm firmware ids before enabling the preview.
- The signing context accepted by the consuming signer is bounded at 255 bytes, matching the FIPS 204 signing input contract (§x43.p2). A longer context throws
InvalidArgumentExceptionbefore any token call. PqsCapabilityStatus::__construct()is public, so a hand-built instance can carry arbitrary booleans. Such an instance is only a value object. It does not alter any signing behavior.current()is the canonical, hard-coded constructor.- The randomized-versus-deterministic choice on the consuming signer follows FIPS 205 §x65.p7 semantics: hedged signing is the default. The flag is ignored for ML-DSA, which always randomises via its own nonce.
FIPS-mode behavior
Section titled “FIPS-mode behavior”ML-DSA and SLH-DSA are FIPS 204 and FIPS 205 algorithms, but this preview carries no FIPS 140-3 validation claim. No FIPS-validated post-quantum HSM round-trip has been established for this path. The Enterprise FIPS-mode crypto-policy profile, documented on the Security deep reference, gates the classical signing algorithms; it does not admit the PQS surface into a validated set. Enabling FIPS mode does not make post-quantum signing FIPS-validated. Do not deploy the preview where a FIPS-validated signature is required.
Conformance
Section titled “Conformance”| Claim | Standard | Clause |
|---|---|---|
| ML-DSA-44/65/87 carry claimed NIST categories 2, 3, 5. | FIPS 204 | §4.p9 |
| ML-DSA signature sizes are 2420, 3309, 4627 bytes. | FIPS 204 | §4.p15 (Table 2) |
| The signing context byte string is bounded at 255 bytes. | FIPS 204 | §x43.p2 |
| A wrong-length signature or key must be rejected. | FIPS 204 | §x34 |
| Twelve SLH-DSA parameter sets are approved. | FIPS 205 | §11.p12 (Table 2) |
| SLH-DSA signature sizes follow Table 2 (7856 bytes for 128s). | FIPS 205 | §11.p6 |
| Hedged signing is the default; a deterministic variant exists. | FIPS 205 | §x65.p7 |
| The CAdES/PAdES suites catalogue profiles RSA and EC-DSA only. | ETSI TS 119 312 V1.5.1 | §7.x7.p10 (Table A.1) |
| The PKCS#11 PQ mechanism ids are provisional. | OASIS PKCS#11 v3.1 | product-source grounded |
All clauses are paraphrased. NextPDF does not reproduce normative text. The statements above are structural-alignment statements about identifiers, lengths, and bounds. PqsCapabilityStatus encodes this preview posture in code: conformanceClaimed is false, adesCompliant is false, and verificationAvailable is false, in every configuration. A signature produced by this preview is not AdES-compliant for long-term archival, and most PDF viewers reject it at validation time.
Development notes
Section titled “Development notes”-
The OASIS PKCS#11 post-quantum mechanism registry is not finalised; the
CKM_ML_DSA/CKM_SLH_DSAids and parameter-set constants used here are provisional and grounded from the product source, not a spec citation. -
The present milestone is mock-tested readiness. No real post-quantum-firmware HSM round-trip has been validated yet.
-
Keep both gates off in production. The preview adds no production capability that the classical RSA/ECDSA PKCS#11 path lacks.
-
Before any evaluation with real hardware, confirm the token firmware’s mechanism and parameter-set ids against the provisional values. A mismatch fails at the PKCS#11 layer, not inside NextPDF.
-
Treat
PqsCapabilityStatus::current()as the single source of truth when surfacing PQS status in tooling or UI. Do not restate its booleans by hand. -
summary()output is safe for logs and status endpoints; it is phrased to carry no availability or validation claim.
See also
Section titled “See also”- Post-quantum HSM signing (PQS) preview — capability page
- Security — Deep Reference (HSM, PKCS#11, FIPS-mode)
- Signature — Deep Reference
- HSM signing setup
- Security / Signing (Core)
Publication boundary
Section titled “Publication boundary”This page documents externally observable behavior and the supported public API surface only. Internal namespace paths, helper classes, mechanism tables, runbook filenames, and ticket prefixes are out of scope.