This document specifies a framework for binding cryptographic attestations with digital signatures from multiple independent post-quantum signature families. The framework requires that every attestation bundle carry valid signatures from at least three families, each rooted in a distinct mathematical hardness assumption. Forgery of any single attestation therefore requires the simultaneous compromise of all three assumptions.
The framework defines the supported families, the canonical byte layout of a multi-family signature bundle, the signing and verification algorithms, the key management lifecycle, and the conformance requirements for implementations.
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.
This document is a Draft specification. It has not been submitted to any standards body. The specification is published by H33.ai, Inc. for public review. Normative requirements are binding for implementations that claim multi-family PQ signature conformance.
Comments and errata SHOULD be directed to standard@h33.ai.
| Term | Definition |
|---|---|
| Signature Family | A class of digital signature algorithms that share a common mathematical hardness assumption. Two families are independent if the compromise of one does not imply the compromise of the other. |
| Hardness Assumption | A mathematical conjecture on which the security of a cryptographic algorithm is predicated. Examples: Module Learning With Errors (MLWE), NTRU lattice problems, second-preimage resistance of hash functions. |
| Signature Bundle | A byte sequence containing signatures from all supported families over the same canonical message digest. The bundle is the atomic unit of multi-family attestation. |
| Verification Order | The fixed sequence in which per-family signatures are verified within a bundle. Defined in Section 8. |
| Family Independence | The property that the compromise of any single family's hardness assumption does not reduce the security of the remaining families below their stated security levels. |
| Canonical Message Digest | A SHA3-256 hash of the canonical serialization of the message to be signed. All families sign this digest, not the raw message. |
| Property | Value |
|---|---|
| Algorithm | ML-DSA-65 (formerly Dilithium-3) |
| Standard | NIST FIPS 204 |
| Hardness Assumption | Module Learning With Errors (MLWE) |
| NIST Security Level | 3 |
| Public Key Size | 1,952 bytes |
| Signature Size | 3,309 bytes |
| Family Identifier | 0x01 |
| Property | Value |
|---|---|
| Algorithm | FALCON-512 |
| Standard | NIST PQC Round 3 (selected) |
| Hardness Assumption | NTRU Lattice Short Vector Problem |
| NIST Security Level | 1 |
| Public Key Size | 897 bytes |
| Signature Size | 690 bytes (variable; maximum 690) |
| Family Identifier | 0x02 |
| Property | Value |
|---|---|
| Algorithm | SLH-DSA-SHA2-128f-simple (SPHINCS+) |
| Standard | NIST FIPS 205 |
| Hardness Assumption | Second-preimage resistance of SHA-256 |
| NIST Security Level | 1 |
| Public Key Size | 32 bytes |
| Signature Size | 17,088 bytes |
| Family Identifier | 0x03 |
The three families are selected to provide independence across hardness assumptions. A successful forgery requires simultaneously breaking:
While Families 1 and 2 are both lattice-based, they rely on structurally different lattice problems. MLWE constructs lattices over polynomial rings with error terms; NTRU constructs lattices from short vectors in quotient polynomial rings. No known reduction maps the MLWE problem to the NTRU short vector problem or vice versa. A quantum algorithm efficient against one lattice structure does not necessarily extend to the other.
Family 3 relies on no lattice structure. Its security depends solely on the second-preimage resistance of SHA-256, a property that is well-studied and understood independently of lattice hardness.
The bundle security is bounded at NIST Level 1 (the minimum across all three families). The framework does not claim combined security beyond the weakest family. The value of multi-family signing is fault tolerance across hardness assumptions, not increased security level.
The signature bundle is a contiguous byte sequence with a fixed header and three variable-length signature segments. The total bundle size is 55,748 bytes when all signatures are at maximum length.
| Offset | Length | Field | Description |
|---|---|---|---|
0 | 4 bytes | magic | Bundle identifier: 0x48333350 (ASCII "H33P") |
4 | 1 byte | version | Bundle format version: 0x01 |
5 | 1 byte | family_count | Number of signature families: 0x03 |
6 | 32 bytes | message_digest | SHA3-256 digest of the canonical message |
38 | 2 bytes | sig1_length | ML-DSA-65 signature length (big-endian) |
40 | 3,309 bytes | sig1_data | ML-DSA-65 signature |
3,349 | 2 bytes | sig2_length | FALCON-512 signature length (big-endian) |
3,351 | ≤ 690 bytes | sig2_data | FALCON-512 signature |
3,351 + sig2_length | 2 bytes | sig3_length | SLH-DSA signature length (big-endian) |
3,353 + sig2_length | 17,088 bytes | sig3_data | SLH-DSA-SHA2-128f signature |
The order of families within the bundle is fixed: ML-DSA-65 first, FALCON-512 second, SLH-DSA third. Implementations MUST NOT reorder families. A bundle with families in any other order is malformed and MUST be rejected.
The signing algorithm proceeds as follows:
digest = SHA3-256(canonical_message). This digest is the input to all three signing algorithms.sig1 = ML-DSA-65.Sign(sk1, digest) using the ML-DSA-65 private key.sig2 = FALCON-512.Sign(sk2, digest) using the FALCON-512 private key.sig3 = SLH-DSA.Sign(sk3, digest) using the SLH-DSA private key.Implementations MUST sign with all three families. A bundle with fewer than three signatures is invalid. The signing order (ML-DSA-65 first, FALCON-512 second, SLH-DSA third) is RECOMMENDED but not required; the bundle layout order is required regardless of signing order.
// Pseudocode: multi-family signing
digest = sha3_256(canonical_serialize(message))
sig_mldsa = mldsa65_sign(sk_mldsa, digest)
sig_falcon = falcon512_sign(sk_falcon, digest)
sig_slhdsa = slhdsa_sha2_128f_sign(sk_slhdsa, digest)
bundle = assemble_bundle(digest, sig_mldsa, sig_falcon, sig_slhdsa)Verification is an AND-gate: the bundle is valid if and only if all three per-family signatures are independently valid against the same message digest.
magic == 0x48333350, version == 0x01, family_count == 0x03. Reject if any field does not match.message_digest from the bundle.result1 = ML-DSA-65.Verify(pk1, message_digest, sig1_data).result2 = FALCON-512.Verify(pk2, message_digest, sig2_data).result3 = SLH-DSA.Verify(pk3, message_digest, sig3_data).result1 AND result2 AND result3.Verification order is fixed: ML-DSA-65, then FALCON-512, then SLH-DSA. Implementations MAY short-circuit on the first failure (reject the bundle without verifying remaining families) but MUST report which family failed.
Implementations MUST NOT accept a bundle where any single family fails verification. There is no "2-of-3" mode. The AND-gate is a normative requirement.
The following table describes the system state under partial compromise of one or more families.
| Compromised Family | Remaining Security | Forgery Possible? |
|---|---|---|
| ML-DSA-65 only | NTRU lattice + SHA-256 preimage | No |
| FALCON-512 only | MLWE lattice + SHA-256 preimage | No |
| SLH-DSA only | MLWE lattice + NTRU lattice | No |
| ML-DSA-65 + FALCON-512 | SHA-256 preimage only | No |
| ML-DSA-65 + SLH-DSA | NTRU lattice only | No |
| FALCON-512 + SLH-DSA | MLWE lattice only | No |
| All three families | None | Yes |
The bundle remains unforgeable unless all three hardness assumptions are simultaneously broken. This is the core security property of the multi-family framework.
Each family's key pair MUST be generated independently using a cryptographically secure random number generator (CSRNG). Key material for one family MUST NOT be derived from or share entropy with another family's key material.
Key rotation MUST be performed per-family. Rotating one family's keys does not require rotating the other families. However, all three families MUST have valid (non-expired, non-revoked) keys at all times. A rotation that leaves any family without a valid key pair is a conformance violation.
Rotation SHOULD be staggered across families to avoid simultaneous key generation load. The RECOMMENDED rotation interval is implementation-defined but MUST NOT exceed 365 days for any single family.
Revocation of any single family's key MUST immediately trigger re-signing of all unexpired bundles signed with that key. Bundles signed with a revoked key are invalid even if the other two families remain valid. Implementations MUST maintain a revocation list per family.
The following measurements were taken on AWS Graviton4 (c8g.metal-48xl, 192 vCPUs, ARM64) using the H33 reference implementation in Rust.
| Operation | ML-DSA-65 | FALCON-512 | SLH-DSA-SHA2-128f | Bundle Total |
|---|---|---|---|---|
| Key Generation | 0.12 ms | 8.4 ms | 0.08 ms | 8.6 ms |
| Signing | 0.42 ms | 1.2 ms | 4.8 ms | 6.4 ms |
| Verification | 0.14 ms | 0.08 ms | 0.24 ms | 0.46 ms |
| Signature Size | 3,309 B | ≤ 690 B | 17,088 B | ≤ 21,087 B |
| Public Key Size | 1,952 B | 897 B | 32 B | 2,881 B |
At batch signing (32 attestations per batch), the H33 reference implementation sustains 2.2 million authentications per second with full multi-family signature verification in the pipeline.
SLH-DSA dominates signing latency (75% of bundle signing time). Verification latency is dominated by SLH-DSA (52%) followed by ML-DSA-65 (30%). FALCON-512 is the fastest verifier.
| Failure Mode | Detection | Required Behavior |
|---|---|---|
| Single family verification failure | Per-family Verify() returns false | Reject the entire bundle. Report the failing family identifier in the rejection response. MUST NOT accept on remaining families. |
| Key mismatch | Public key does not correspond to the signing key used | Verification fails for the affected family. Same behavior as single family failure. |
| Algorithm downgrade attempt | Bundle claims fewer than 3 families or substitutes a non-approved algorithm | Reject the bundle. Implementations MUST NOT verify bundles with family_count != 3 or unrecognized family identifiers. |
| Malformed bundle header | Magic, version, or family count field invalid | Reject without attempting any signature verification. |
| Digest mismatch | Recomputed message digest does not match message_digest in bundle | Reject. This indicates the message was modified after signing or the wrong message was presented for verification. |
| Truncated signature segment | Declared sig_length exceeds remaining bytes in bundle | Reject as malformed. Do not attempt partial verification. |
An implementation conforms to this specification if and only if:
Conformance is testable. A reference test vector set containing signed bundles, their expected verification results, and known-bad bundles (truncated, reordered, single-family) will be published alongside the first stable release.
Future versions of this specification MAY add or replace signature families subject to the following constraints:
family_count) is a backward-compatible change. The bundle version MUST be incremented to 0x02. Verifiers supporting v1 MUST reject v2 bundles (they will not recognize the additional segments).Planned migration: FALCON-512 to FALCON-1024 and SLH-DSA-SHA2-128f to SLH-DSA-SHA2-192f are under evaluation. These upgrades would raise the bundle floor from NIST Level 1 to Level 5 (FALCON-1024) and Level 3 (SLH-DSA-192f). The upgrade is pending Graviton4 A/B performance validation.
The following is a complete signing and verification flow for a sample message.
// Input
message = "governance_receipt:tenant_001:2026-05-22T00:00:00Z"
// Step 1: Canonical digest
digest = sha3_256(message)
= "b7c4e2f1...32 bytes"
// Step 2: Per-family signing
sig1 = mldsa65_sign(sk1, digest) // 3,309 bytes
sig2 = falcon512_sign(sk2, digest) // 666 bytes (variable)
sig3 = slhdsa_sign(sk3, digest) // 17,088 bytes
// Step 3: Bundle assembly
bundle = [
0x48, 0x33, 0x33, 0x50, // magic: "H33P"
0x01, // version
0x03, // family_count
...digest (32 bytes)...,
0x0C, 0xED, // sig1_length: 3309
...sig1 (3,309 bytes)...,
0x02, 0x9A, // sig2_length: 666
...sig2 (666 bytes)...,
0x42, 0xC0, // sig3_length: 17088
...sig3 (17,088 bytes)...
]
// Total bundle: 21,137 bytes
// Step 4: Verification (AND-gate)
v1 = mldsa65_verify(pk1, digest, sig1) // true
v2 = falcon512_verify(pk2, digest, sig2) // true
v3 = slhdsa_verify(pk3, digest, sig3) // true
result = v1 AND v2 AND v3 // true: bundle valid| Version | Date | Changes |
|---|---|---|
1.0.0 | 2026-05-22 | Initial draft specification. |
| Reference | Title | URL |
|---|---|---|
| [RFC 2119] | Key words for use in RFCs to Indicate Requirement Levels | rfc-editor.org/rfc/rfc2119 |
| [FIPS 204] | Module-Lattice-Based Digital Signature Standard (ML-DSA) | csrc.nist.gov |
| [FIPS 205] | Stateless Hash-Based Digital Signature Standard (SLH-DSA) | csrc.nist.gov |
| [FALCON] | FALCON: Fast-Fourier Lattice-based Compact Signatures over NTRU | falcon-sign.info |
| [SHA3-256] | NIST FIPS 202: SHA-3 Standard | csrc.nist.gov |
| [OIS] | Operational Integrity Score Specification v1.0.0 | h33.ai/specifications/ois/ |