Exqub Protocol Technical Specification
Complete technical documentation for implementing Exqub credentials: cryptographic primitives, data structures, verification pipeline, and security model.
System Roles
Issuer
Enterprise or authority that creates and signs credentials with ML-DSA-65. Publishes revocation state via Sparse Merkle Tree.
issuer_key: ML-DSA-65 (1952 bytes public) Holder
Agent, service, or device that receives credentials, stores in wallet, controls presentation and attribute disclosure.
device_key: ML-DSA-65 (bound to credential) Verifier
Any party that receives presentations, checks signatures, revocation status, and attribute proofs. Stateless operation.
verification: <50ms (full credential) Revocation Registry
Sparse Merkle Tree with signed root snapshots published by issuer, consumed by verifiers.
depth: 256, proof: ~8KB for 1M credentials Credential Structure
ExqubCredential {
version: u8 // 0x01
credential_id: [u8; 32] // unique, issuer-assigned
credential_type: u8 // 0x01=Standard, 0x02=Delegation, 0x04=Attestation
issuer_id: [u8; 32] // SHA3-256(issuer public key)
holder_id: [u8; 32] // holder identity
issued_at: u64 // Unix timestamp
expires_at: u64 // hard TTL
attr_count: u32 // number of attributes (≤64)
attr_root: [u8; 32] // Merkle root of attribute tree
signature: [u8; 3309] // ML-DSA-65 (FIPS 204)
} Total wire size: 9-12 KB including attributes (smaller than most JWT-based tokens)
Attribute Tree & Selective Disclosure
Each attribute is a Merkle tree leaf computed as:
leaf = SHA3-256(
EXQUB_ATTR_LEAF_ // domain separator
|| len(key) // u16 big-endian
|| key // UTF-8
|| salt // [u8; 32], per-attribute
|| len(value) || value)
Per-Attribute Salts
Prevents dictionary attacks on undisclosed attributes
Independent Disclosure
Logarithmic scaling with attribute count
Merkle Proof
Holder provides key, value, salt, and sibling path
Signature Construction
The 166-byte sig_input uses fixed-layout concatenation for deterministic signing:
- 0x00–0x0F: EXQUB_SIG_V1 domain separator (16 bytes)
- 0x10: version (1 byte)
- 0x11: credential_type (1 byte)
- 0x12–0x31: credential_id (32 bytes)
- 0x32–0x51: issuer_id (32 bytes)
- 0x52–0x71: holder_id (32 bytes)
- 0x72–0xA5: issued_at + expires_at + attr_count + attr_root
Note: Fixed-layout byte concatenation (not CBOR) ensures deterministic, implementation-independent signing
Presentation Format
ExqubPresentation {
credential: ExqubCredential
disclosed_attrs: Vec<(key, value, salt, proof)>
device_signature: [u8; 3309] // ML-DSA-65 over content
nonce: [u8; 32] // verifier-provided
} 10-Step Verification Pipeline
DoS-resistant ordering: cheap checks first, expensive crypto last
Parse CBOR
Reject malformed input before any work
~1.14µs Version and type check
Reject unknown versions or credential types
~0.001ms Freshness + nonce check
Reject stale/wrong-session presentations
~0.01ms Work bounding
Reject oversized proofs (DoS protection)
~0.001ms SMT membership
Check non-revocation before expensive signature verification
~1ms (hashing) Issuer signature
Verify credential authenticity (ML-DSA-65)
~144µs EXPENSIVE Credential validity window
Check expiry (issued_at, expires_at)
~0.001ms Attribute Merkle proofs
Verify disclosed attributes match the credential
~0.5ms (moderate) Device signature
Verify holder controls the device key (ML-DSA-65)
~144µs EXPENSIVE Policy evaluation
Application-layer constraints, replay cache
Variable Full verify_presentation(): ~301µs on native hardware, ~1-4ms on WASM
Revocation System
Sparse Merkle Tree
- • Depth 256, each credential ID maps to leaf
- • Valid if leaf present, revoked if absent
- • Signed, monotonically-indexed snapshots
- • Transport-agnostic (HTTPS, CDN, P2P gossip)
Performance
- • 1M credentials = 256 hashes (~8 KB proof)
- • No bulk CRL downloads
- • DoS resistance: check before signature verification
- • Offline verification with cached snapshots
Device Binding
Mandatory Security Feature
- ✓ Every credential bound to holder's device key (ML-DSA-65 public key embedded and issuer-signed)
- ✓ Every presentation requires fresh device co-signature covering credential + disclosed attributes + verifier nonce
- ✓ Prevents credential theft (different device lacks private key)
- ✓ Prevents replay attacks (nonce binding)
Note: Mandatory in Exqub (unlike optional SD-JWT or absent W3C VC + BBS+)
Threat Model
| Threat | Mitigation | Residual Risk |
|---|---|---|
| Forge credential | ML-DSA-65 signatures (128-bit quantum security) | None with current tech |
| Escalate privileges | Merkle tree binds attributes cryptographically | None |
| Replay attack | Verifier nonce + device co-signature | None |
| Steal credential | Device binding (needs private key) | Device compromise |
| Quantum attack | ML-DSA-65 (NIST PQC standard) | None known |
Important Limitation: Exqub credentials attest scope and the verifier enforces it. If a backend service does not embed the verifier SDK, the credential cannot prevent unauthorised actions. This is analogous to TLS: the protocol secures the channel, but only if the server requires it.
Competitive Landscape
| System | What It Does | Exqub Difference |
|---|---|---|
| W3C VC + BBS+ | Verifiable credentials with multi-show unlinkability | PQC sigs, mandatory device binding, native sub-delegation |
| Macaroons | Attenuated capability tokens with contextual caveats | PQC sigs, per-attribute selective disclosure, SMT revocation |
| SPIFFE / SPIRE | Workload identity via x509-SVIDs | Capability constraints, selective disclosure, PQC, agent delegation |
| OAuth DPoP / GNAP | Proof-of-possession tokens for APIs | Any-party verification, native sub-delegation, compound constraints |
| SD-JWT (eIDAS 2.0) | Selective disclosure via salted hashes | Logarithmic disclosure scaling, PQC, mandatory device binding |
Implementation Status
Core Implementation
- • Pure Rust, 8 crates
- • 1,363+ tests, Clippy clean
- • 63 conformance test vectors
- • WASM verified, no_std core
Performance
- • no_std core engine
- • Zero heap allocations in verification
- • ~144µs ML-DSA-65 verify
- • ~301µs full presentation
Cryptography
- • ML-DSA-65 (FIPS 204)
- • 3,309 byte signatures
- • 1,952 byte public keys
- • 128-bit quantum security
# Integration example
from exqub import verifier, presenter
# Two lines to verify
result = verifier.verify(presentation)
if result.valid and result.spend_cap >= amount:
process_transaction() Protocol Roadmap
V2 Production Hardening
Device key rotation, conformance tests, OID4VP + COSE interop, batch revocation, TypeScript/Python SDKs
Agent Framework Integration
LangChain, AutoGen, CrewAI, LlamaIndex tooling with two-line integration
Research Directions
Threshold credentials (k-of-n), conditional disclosure, blind signatures, formal verification (Lean 4)