Skip to content

Cryptographic Primitives

Draft

Single source of truth for primitives; other docs link here, never restate. Constants: capsule-core/src/crypto/primitives.rs.

PrimitiveChoiceUsed for
Cryptographic hashSHA-256Content addressing, integrity
Key derivationHKDF-SHA512Per-file, per-blob, per-album keys
Password-based KDFArgon2id, device-tier parametersEscrow and backup unwrap
Bulk AEADAES-256-GCM (+ STREAM)Asset and metadata ciphertext
MLS control AEADChaCha20-Poly1305MLS’s own messages
Signature schemeHybrid Ed25519 + ML-DSA-65Identity, device, manifest, write tier
KEMX-Wing (X25519 + ML-KEM-768)MLS HPKE
MLS ciphersuiteMLS_256_XWING_CHACHA20POLY1305_SHA256_Ed25519 (0x004D)Group key management
RandomnessOS CSPRNG (getrandom)All keys, salts, nonces
TransportTLS 1.3, hybrid X25519+ML-KEMClient–server, server–server

Mismatches fail closed:

IdentifierTypeOwnerCarried in
crypto_suite_idu16this docevery AssetManifest, metadata blob, backup MANIFEST.cbor
protocol_versionYYYY-MM-DDVersioningevery AssetManifest, every request (negotiation), the album’s MLS pin
sidecar_schemau16MetadataCBOR sidecar field 0
  • crypto_suite_id = 0x0001 is exactly the inventory above. Retiring a primitive adds a new suite id; it never edits the existing row. Old suite ids stay verifiable and decryptable forever.
  • Suite ids are a Capsule namespace, unrelated to MLS ciphersuite codepoints.
  • Manifest signatures cover crypto_suite_id and protocol_version, so a downgrade cannot be produced silently.

SHA-256 everywhere; the content-address hash is the value the signed manifest commits to and upload verifies.

HKDF-SHA512 (wider hash keeps ~256-bit margin under Grover). Every derivation MUST use a versioned info string and a scope-unique salt.

Argon2id, 32-byte random salt, recovery/bootstrap only. Cost is chosen by device tier at wrap time (DeviceTier::params), stored in-band, and honoured at unwrap on any device.

AES-256-GCM (hardware-accelerated on every target). Nonce misuse is closed structurally: a fresh per-encryption file key for STREAM, a fresh CSPRNG nonce per metadata blob (Re-keying on Rewrite).

Fixed by the MLS ciphersuite; MLS messages only, never user data.

SignatureScheme
Long-lived identity/evidence: user IK, device keys, manifests, write-tier keys, custody receipts and storage attestationsHybrid Ed25519 + ML-DSA-65; both halves MUST verify
Short-lived operational: server–server federation, federation capabilities, access-token JWTsEd25519 only
MLS LeafNodeEd25519 (ciphersuite-pinned); the ML-DSA half lives at the identity layer (MLS)

The boundary is signature lifetime, not signer: a server-signed custody receipt is long-lived evidence, so it is hybrid.

Defined by the MLS ciphersuite.

0x004D, the X-Wing suite from draft-mahy-mls-xwing that OpenMLS ships via libcrux — not draft-ietf-mls-pq-ciphersuites. Its SHA-256 is MLS-internal and independent of the content-address hash. Rationale and migration posture: MLS.

OS CSPRNG only; never a self-seeded PRNG or hand-rolled nonce.

  • Known-answer vectors (RFC, RustCrypto, OpenMLS) for every primitive.
  • Suite-id round-trip: persisted crypto_suite_id dispatches to exactly its row.
  • Downgrade rejection: a manifest whose declared suite differs from its signed envelope is rejected.