Feature 1 — Post-Quantum Hybrid KEM (X25519 + ML-KEM-768): - Create hybrid_kem.rs with keygen, encrypt, decrypt + 11 unit tests - Wire format: version(1) | x25519_eph_pk(32) | mlkem_ct(1088) | nonce(12) | ct - Add uploadHybridKey/fetchHybridKey RPCs to node.capnp schema - Server: hybrid key storage in FileBackedStore + RPC handlers - Client: hybrid keypair in StoredState, auto-wrap/unwrap in send/recv/invite/join - demo-group runs full hybrid PQ envelope round-trip Feature 2 — SQLCipher Persistence: - Extract Store trait from FileBackedStore API - Create SqlStore (rusqlite + bundled-sqlcipher) with encrypted-at-rest SQLite - Schema: key_packages, deliveries, hybrid_keys tables with indexes - Server CLI: --store-backend=sql, --db-path, --db-key flags - 5 unit tests for SqlStore (FIFO, round-trip, upsert, channel isolation) Also includes: client lib.rs refactor, auth config, TOML config file support, mdBook documentation, and various cleanups by user. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
7.3 KiB
Auth Schema
Schema file: schemas/auth.capnp
File ID: @0xb3a8f1c2e4d97650
The AuthenticationService interface defines the RPC contract for uploading and fetching MLS KeyPackages. It is the standalone version of the Authentication Service; in the current architecture, these methods are integrated into the unified NodeService interface.
Full schema listing
# auth.capnp -- Authentication Service RPC interface.
#
# Clients call uploadKeyPackage before joining any group so that peers can
# fetch their key material to add them. Each KeyPackage is single-use (MLS
# requirement): fetchKeyPackage removes and returns one package atomically.
#
# The server indexes packages by the raw Ed25519 public key bytes (32 bytes),
# not a fingerprint, so callers must know the target's identity public key
# out-of-band (e.g. from a directory or QR code scan).
#
# ID generated with: capnp id
@0xb3a8f1c2e4d97650;
interface AuthenticationService {
# Upload a single-use KeyPackage for later retrieval by peers.
#
# identityKey : Ed25519 public key bytes (exactly 32 bytes).
# package : openmls-serialised KeyPackage blob (TLS encoding).
#
# Returns the SHA-256 fingerprint of `package`. Clients should record this
# and compare it against the fingerprint returned by a peer's fetchKeyPackage
# to detect tampering.
uploadKeyPackage @0 (identityKey :Data, package :Data) -> (fingerprint :Data);
# Fetch and atomically remove one KeyPackage for a given identity key.
#
# Returns empty Data if no KeyPackage is currently stored for this identity.
# Callers should handle the empty case by asking the target to upload more
# packages before retrying.
fetchKeyPackage @1 (identityKey :Data) -> (package :Data);
}
Method-by-method analysis
uploadKeyPackage @0
uploadKeyPackage (identityKey :Data, package :Data) -> (fingerprint :Data)
Purpose: A client uploads a single-use MLS KeyPackage so that peers can later fetch it to add the client to a group.
Parameters:
| Parameter | Type | Size | Description |
|---|---|---|---|
identityKey |
Data |
Exactly 32 bytes | The uploader's raw Ed25519 public key bytes. This is the index key under which the package is stored. |
package |
Data |
Variable (bounded by transport max) | An openmls-serialised KeyPackage blob in TLS encoding. Contains the client's HPKE init key, credential, and signature. |
Return value:
| Field | Type | Size | Description |
|---|---|---|---|
fingerprint |
Data |
32 bytes | SHA-256 digest of the uploaded package bytes. |
Fingerprint semantics: The returned fingerprint allows the uploading client to verify that the server stored the package correctly. More importantly, when a peer later fetches a KeyPackage, it can compare the fetched package's SHA-256 hash against the fingerprint (communicated out-of-band) to detect tampering by a malicious server.
Idempotency: Uploading the same package twice appends a second copy to the queue. The server does not deduplicate. Clients should avoid uploading duplicates to conserve their KeyPackage supply.
fetchKeyPackage @1
fetchKeyPackage (identityKey :Data) -> (package :Data)
Purpose: Fetch and atomically remove one KeyPackage for a given identity. This is the mechanism by which a group creator obtains a peer's key material in order to add them to a group via MLS add_members().
Parameters:
| Parameter | Type | Size | Description |
|---|---|---|---|
identityKey |
Data |
Exactly 32 bytes | The raw Ed25519 public key of the target peer whose KeyPackage is being requested. |
Return value:
| Field | Type | Size | Description |
|---|---|---|---|
package |
Data |
Variable, or 0 bytes | The fetched KeyPackage blob, or empty Data if no packages are stored for this identity. |
Atomic removal: The fetch operation is destructive: it removes the returned KeyPackage from the server's store in the same operation that returns it. This guarantees MLS's single-use requirement -- a KeyPackage is never served to two different requesters.
Empty response handling: Callers must check for an empty response. An empty package means the target has no KeyPackages available. The caller should either:
- Retry after a delay, hoping the target uploads more packages.
- Signal the user that the target is unreachable for group addition.
Indexing by raw Ed25519 public key
The Authentication Service indexes KeyPackages by the raw 32-byte Ed25519 public key, not by a fingerprint or any higher-level identifier. This design choice has several implications:
-
No directory service required for lookup. The caller must already know the target's Ed25519 public key (obtained out-of-band via QR code scan, manual exchange, or a future directory service).
-
Consistent with DS indexing. The Delivery Service uses the same 32-byte Ed25519 key as its queue index, so a single key serves as the universal identifier across both services.
-
No ambiguity. Unlike fingerprints (which could collide if truncated) or human-readable names (which require a mapping layer), the raw public key is the canonical, collision-resistant identifier.
Single-use semantics
MLS requires that each KeyPackage be used at most once to preserve the forward secrecy of the initial key exchange. The Authentication Service enforces this by atomically removing the KeyPackage on fetch.
Consequences for clients:
- Clients should pre-upload multiple KeyPackages after generating their identity, so that several peers can add them to groups concurrently without exhausting the supply.
- Clients should monitor their KeyPackage count on the server (via a future monitoring endpoint or periodic re-upload) and replenish when the supply runs low.
- If a client has zero KeyPackages stored, it is effectively unreachable for new group invitations until it uploads more.
For the design rationale behind single-use KeyPackages, see ADR-005: Single-Use KeyPackages.
Relationship to NodeService
In the current unified architecture, the Authentication Service methods are exposed as part of the NodeService interface:
| AuthenticationService Method | NodeService Method | Additional Parameters |
|---|---|---|
uploadKeyPackage @0 |
uploadKeyPackage @0 |
auth :Auth |
fetchKeyPackage @1 |
fetchKeyPackage @1 |
auth :Auth |
The standalone AuthenticationService interface remains in the schema for documentation purposes and for use in contexts where the full NodeService is not needed.
Further reading
- Wire Format Overview -- serialisation pipeline context
- NodeService Schema -- unified interface that subsumes AuthenticationService
- Delivery Schema -- the companion service for message routing
- Envelope Schema -- legacy framing that used
keyPackageUpload/keyPackageFetchmessage types - ADR-005: Single-Use KeyPackages -- design rationale for atomic removal on fetch
- ADR-004: MLS-Unaware Delivery Service -- why the server does not inspect MLS content