Skip to content

Support RFC 9802 LMS and XMSS in X.509 certificate and CSR generation#21

Open
Frauschi wants to merge 3 commits into
masterfrom
claude/lms-xmss-cert-generation-vYWUq
Open

Support RFC 9802 LMS and XMSS in X.509 certificate and CSR generation#21
Frauschi wants to merge 3 commits into
masterfrom
claude/lms-xmss-cert-generation-vYWUq

Conversation

@Frauschi
Copy link
Copy Markdown
Owner

@Frauschi Frauschi commented Jun 2, 2026

Overview

Builds on the existing RFC 9802 LMS/XMSS X.509 verification support by wiring HSS/LMS and XMSS/XMSS^MT keys into the certificate and PKCS#10 certificate-request generation API. These stateful hash-based schemes follow the same "sign the message directly, no pre-hash" pattern already used for Falcon / ML-DSA / SLH-DSA.

CSR support is included (per design discussion): cert signing and CSR proof-of-possession share the same MakeSignature() dispatch, and there's no reason to special-case CSRs out. The only caveat — each cert and each CSR consumes one of the key's finite one-time signatures — is inherent to the scheme and documented at the API surface.

Public API

  • New CertType selectors LMS_TYPE, XMSS_TYPE, XMSSMT_TYPE accepted by wc_MakeCert_ex, wc_SignCert_ex and wc_MakeCertReq_ex.
  • New cert_enums values LMS_KEY, XMSS_KEY, XMSSMT_KEY.
  • New wc_LmsKey_PublicKeyToDer / wc_XmssKey_PublicKeyToDer encoders that emit the RFC 9802 SubjectPublicKeyInfo via the existing SetAsymKeyDerPublic helper and the already-registered HSS_LMSk / XMSSk / XMSSMTk key OIDs.
  • Callers set cert->sigType to CTC_HSS_LMS / CTC_XMSS / CTC_XMSSMT.
LmsKey key; Cert cert; byte der[8192];
wc_LmsKey_Init(&key, heap, devId);
wc_LmsKey_SetParameters(&key, levels, height, winternitz);
wc_LmsKey_SetWriteCb/SetReadCb/SetContext(...);   /* persist state */
wc_LmsKey_MakeKey(&key, &rng);

wc_InitCert(&cert);
cert.sigType = CTC_HSS_LMS; cert.isCA = 1; cert.selfSigned = 1;
wc_MakeCert_ex(&cert, der, sizeof(der), LMS_TYPE, &key, &rng);
wc_SignCert_ex(cert.bodySz, cert.sigType, der, sizeof(der), LMS_TYPE, &key, &rng);

Implementation

  • Thread LmsKey*/XmssKey* through EncodePublicKey, MakeAnyCert, MakeCertReq, MakeSignature and SignCert (ASN.1 template path), mirroring the existing PQ-key plumbing.
  • SignCert sizes the signature buffer at runtime from wc_LmsKey_GetSigLen / wc_XmssKey_GetSigLen, since LMS/XMSS signatures are parameter-dependent and can exceed MAX_ENCODED_SIG_SZ. In WOLFSSL_NO_MALLOC builds (fixed-size buffer) an oversized signature fails cleanly with BUFFER_E rather than overflowing.
  • SignCert validates that sigType matches the key (LMS → CTC_HSS_LMS; XMSS → CTC_XMSS/CTC_XMSSMT per is_xmssmt), and the XMSS_TYPE/XMSSMT_TYPE selector is validated against the key's tree variant — so a mismatch returns an error instead of silently emitting a malformed certificate.
  • Forward-declare LmsKey/XmssKey in asn_public.h (guarded typedefs in wc_lms.h/wc_xmss.h) so the shared signatures compile regardless of feature macros.
  • Enable WC_ENABLE_ASYM_KEY_EXPORT/IMPORT for non-verify-only LMS/XMSS builds so the SPKI encoder is compiled.
  • LMS/XMSS cert generation is template-only (the original/non-template path has no LMS/XMSS support, consistent with verification); the non-template MakeAnyCert/MakeCertReq emit a clear diagnostic and ALGO_ID_E if handed such a key.

Tests

test_rfc9802_lms_x509_gen / test_rfc9802_xmss_x509_gen generate a self-signed root CA and a PKCS#10 CSR, then verify the cert via wolfSSL_CertManagerVerifyBuffer and the CSR proof-of-possession via wc_ParseCert. Coverage includes single-level LMS, multi-level HSS (L2), single-tree XMSS, and XMSS^MT, plus negative cases for sigType/keyType and selector mismatches.

Notes / scope

  • No interop test: OpenSSL still lacks LMS/XMSS X.509 certificate signing (XMSS & LMS openssl/openssl#21360), and Bouncy Castle's encoding is not yet aligned with the final RFC 9802 — so there is no aligned third-party implementation to interoperate against.
  • Verified across full, WOLFSSL_ASN_ORIGINAL (non-template), and verify-only LMS/XMSS builds; the API test suite passes with 0 failures, and testwolfcrypt passes.

https://claude.ai/code/session_01RDkoNViK5aNkUAJqnuL8Y5


Generated by Claude Code

claude added 3 commits June 1, 2026 11:32
Builds on the existing RFC 9802 LMS/XMSS X.509 verification support by
wiring HSS/LMS and XMSS/XMSS^MT keys into the certificate and PKCS#10
certificate-request generation API. LMS/XMSS now follow the same
"sign the message directly, no pre-hash" pattern already used for
Falcon / ML-DSA / SLH-DSA.

Public API:
  - New CertType values LMS_TYPE, XMSS_TYPE, XMSSMT_TYPE accepted by
    wc_MakeCert_ex, wc_SignCert_ex and wc_MakeCertReq_ex.
  - New cert_enums values LMS_KEY, XMSS_KEY, XMSSMT_KEY.
  - New wc_LmsKey_PublicKeyToDer / wc_XmssKey_PublicKeyToDer encoders that
    emit the RFC 9802 SubjectPublicKeyInfo via the existing
    SetAsymKeyDerPublic helper and the already-registered HSS_LMSk /
    XMSSk / XMSSMTk key OIDs.
  - Callers set cert->sigType to CTC_HSS_LMS / CTC_XMSS / CTC_XMSSMT.

Implementation:
  - Thread LmsKey*/XmssKey* through EncodePublicKey, MakeAnyCert,
    MakeCertReq, MakeSignature and SignCert (ASN.1 template path).
  - SignCert sizes the signature buffer at runtime from
    wc_LmsKey_GetSigLen / wc_XmssKey_GetSigLen, since LMS/XMSS signatures
    are parameter-dependent and can exceed MAX_ENCODED_SIG_SZ.
  - Forward-declare LmsKey/XmssKey in asn_public.h (guarded typedefs in
    wc_lms.h/wc_xmss.h) so the shared signatures compile regardless of
    feature macros, mirroring the Falcon/ML-DSA/SLH-DSA convention.
  - Enable WC_ENABLE_ASYM_KEY_EXPORT/IMPORT for LMS/XMSS builds so the
    asymmetric SPKI encoder is compiled.
  - The original (non-template) asn_orig.c MakeAnyCert/MakeCertReq keep
    signature parity but ignore the new keys; LMS/XMSS cert generation is
    template-only, consistent with the verification support.

Like all stateful hash-based signatures, each generated certificate and
each CSR proof-of-possession consumes one one-time signature; callers
must persist key state through the read/write callbacks.

Tests:
  - test_rfc9802_lms_x509_gen and test_rfc9802_xmss_x509_gen generate a
    self-signed root CA and a PKCS#10 CSR for LMS and XMSS, then verify
    the cert through wolfSSL_CertManagerVerifyBuffer and the CSR's
    proof-of-possession through wc_ParseCert.

https://claude.ai/code/session_01RDkoNViK5aNkUAJqnuL8Y5
Addresses code review findings on the LMS/XMSS certificate/CSR generation
change:

- SignCert: in WOLFSSL_NO_MALLOC builds the CertSignCtx signature buffer is
  a fixed MAX_ENCODED_SIG_SZ array. The runtime LMS/XMSS sizing only grew
  the malloc'd buffer, so a signature larger than MAX_ENCODED_SIG_SZ would
  overflow the fixed array. Now hard-fail with BUFFER_E in that case instead
  of writing past the buffer.

- settings.h: only pull LMS/XMSS into WC_ENABLE_ASYM_KEY_EXPORT/IMPORT when
  not built verify-only, so verify-only configs no longer compile the
  unused private-key DER export/import paths.

- asn_public.h: document the stateful one-time-signature caveat on the new
  LMS_TYPE/XMSS_TYPE/XMSSMT_TYPE values where API users will see it.

- Tests: refactor the cert+CSR round trip into a shared helper and extend
  coverage to multi-level HSS (L2) and XMSS^MT (XMSSMT_TYPE / XMSSMTk OID /
  CTC_XMSSMT), which were previously unexercised; drop the redundant
  XMEMSET before wc_InitCert.

Verified with full, WOLFSSL_ASN_ORIGINAL (non-template), and verify-only
LMS/XMSS builds; api suite passes with 0 failures.

https://claude.ai/code/session_01RDkoNViK5aNkUAJqnuL8Y5
Addresses the second round of review feedback.

- SignCert now rejects a signature type that does not match the key:
  an LMS key requires CTC_HSS_LMS, and an XMSS key requires CTC_XMSS or
  CTC_XMSSMT according to key->is_xmssmt. Since MakeAnyCert derives the
  SubjectPublicKeyInfo OID from is_xmssmt, this guarantees the emitted
  signatureAlgorithm always agrees with the public key, so a mismatched
  sigType can no longer silently produce a malformed certificate. The
  check runs before any signing, so no one-time signature is consumed.

- The XMSS_TYPE/XMSSMT_TYPE selector passed to wc_MakeCert_ex,
  wc_MakeCertReq_ex and wc_SignCert_ex is now validated against the key's
  actual tree variant (BAD_FUNC_ARG on mismatch), so the two selectors are
  no longer silently interchangeable.

- asn_orig.c (non-template build): MakeAnyCert/MakeCertReq now emit a
  WOLFSSL_MSG and return ALGO_ID_E when handed an LMS/XMSS key, instead of
  a bare BAD_FUNC_ARG, making the "requires WOLFSSL_ASN_TEMPLATE"
  limitation diagnosable.

- Tests: add negative cases exercising the new validation (LMS key signed
  with CTC_XMSS -> ALGO_ID_E; XMSSMT_TYPE selector on a single-tree XMSS
  key -> BAD_FUNC_ARG; single-tree XMSS key signed with CTC_XMSSMT ->
  ALGO_ID_E). Documented the void* key parameter of the round-trip helper.

No interop test added: OpenSSL still lacks LMS/XMSS X.509 certificate
signing (openssl/openssl#21360) and Bouncy Castle's encoding is not yet
aligned with the final RFC 9802, so there is no aligned third-party
implementation to interoperate against.

https://claude.ai/code/session_01RDkoNViK5aNkUAJqnuL8Y5
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants