Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Proposal: Cryptography and Key Management

Capability-native abstractions for cryptographic keys and key sources. Keys are capability objects; key material never crosses cap boundaries. One interface serves every consumer — volume encryption, TLS, code signing, instance identity, authenticated backups, per-service secrets.

Problem

Nearly every forthcoming capOS subsystem wants cryptography. A partial list:

Without a shared abstraction each of these invents its own key interface, its own “where does the key live” story, and its own audit trail. That is how Linux ended up with dm-crypt, fscrypt, keyctl, PKCS#11, ssh-agent, gpg-agent, systemd-creds, TPM tools, and cloud-specific SDKs as mutually-unaware silos. capOS is young enough to avoid that.

Design Principle: Keys Are Capabilities

In every Unix-lineage system, a key is a byte string — a secret stored somewhere (keyring, file, memory, HSM handle), protected by a mechanism orthogonal to the system’s main abstractions (syscalls + files + processes). Every new subsystem therefore invents a new protection mechanism.

In capOS, a key is a capability object. Holding a SymmetricKey or PrivateKey cap means “you may compute with this key.” It does not mean “you may see this key.” Key material lives in the address space of the service that implements the cap; callers reach it by invoking methods.

Consequences:

  • Attenuation falls out of the capability model. A decrypt-only SymmetricKey is a wrapper CapObject that rejects encrypt. A key bound to a single AAD domain is a wrapper that fixes the aad argument. A sign-only PrivateKey is a wrapper that rejects decrypt. No new kernel mechanism is needed.
  • Revocation is a cap drop. Drop the cap, the key is gone from that holder’s reach. Other holders are unaffected.
  • Audit is intrinsic. Every method invocation can flow through an audit cap. A malicious service granted decrypt authority generates audit records for every use; it cannot exfiltrate the raw key material silently.
  • Hardware isolation composes cleanly. A TPM-backed key service implements the same PrivateKey interface as an in-process software key service; callers cannot distinguish, and should not need to.

A service granted a SymmetricKey with both encrypt and decrypt can still run arbitrary oracle queries against the key. That is weaker than “the key material never leaves an HSM” and stronger than “the key is a byte string in the process heap.” When stronger containment is required, the key service is a thin process sitting on top of a hardware primitive (TPM, Secure Enclave, cloud KMS).

Schemas

Symmetric keys

interface SymmetricKey {
    # Authenticated encryption. `aad` is bound into the tag and MUST
    # match on decrypt. Nonce is returned because some constructions
    # generate it internally.
    encrypt @0 (plaintext :Data, aad :Data)
            -> (ciphertext :Data, nonce :Data, tag :Data);
    decrypt @1 (ciphertext :Data, nonce :Data, tag :Data, aad :Data)
            -> (plaintext :Data);

    # HKDF-style subkey derivation. The returned subkey is bound to
    # (label, context); it cannot be used to derive siblings.
    deriveSubkey @2 (label :Data, context :Data, purpose :KeyPurpose)
                 -> (subkey :SymmetricKey);

    # Wrap/unwrap subordinate keys. Used for envelope encryption and
    # for transporting keys between services.
    wrapSymmetric   @3 (subordinate :SymmetricKey) -> (wrapped :Data);
    unwrapSymmetric @4 (wrapped :Data, purpose :KeyPurpose)
                    -> (subordinate :SymmetricKey);
    wrapPrivate     @5 (subordinate :PrivateKey)   -> (wrapped :Data);
    unwrapPrivate   @6 (wrapped :Data, algorithm :AsymmetricAlgorithm)
                    -> (subordinate :PrivateKey);

    # MAC-only modes for keys with `KeyPurpose.integrity`.
    mac    @7 (message :Data) -> (tag :Data);
    verify @8 (message :Data, tag :Data) -> (ok :Bool);

    info @9 () -> (algorithm :SymmetricAlgorithm,
                   purpose :KeyPurpose,
                   identifier :Data);
}

enum SymmetricAlgorithm {
    aes256Gcm         @0;
    aes256GcmSiv      @1;
    xchacha20Poly1305 @2;
    aes256Xts         @3;  # block-device only; no authentication
    hmacSha256        @4;  # mac/verify only
    hmacSha384        @5;
    hmacSha512        @6;
}

Asymmetric keys

interface PublicKey {
    verify    @0 (message :Data, signature :Data) -> (ok :Bool);
    # Public-key encryption (e.g. ECIES, RSA-OAEP). For hybrid systems
    # this returns a wrapped ephemeral symmetric key alongside the
    # ciphertext.
    encrypt   @1 (plaintext :Data, aad :Data)
              -> (ciphertext :Data);
    # Export raw public material (SPKI DER, JWK, OpenSSH, PGP) for
    # callers that need to distribute it. Public material is freely
    # shareable; the cap itself is an authority only to invoke
    # methods, not to "own" the public key.
    export    @2 (format :PublicKeyFormat) -> (encoded :Data);
    info      @3 () -> (algorithm :AsymmetricAlgorithm,
                        identifier :Data);
}

interface PrivateKey {
    sign      @0 (message :Data, scheme :SignatureScheme)
              -> (signature :Data);
    decrypt   @1 (ciphertext :Data, aad :Data)
              -> (plaintext :Data);
    # Key agreement (ECDH, X25519).
    agree     @2 (peer :PublicKey, info :Data)
              -> (shared :SymmetricKey);
    public    @3 () -> (pk :PublicKey);
    info      @4 () -> (algorithm :AsymmetricAlgorithm,
                        purpose :KeyPurpose,
                        identifier :Data);
}

enum AsymmetricAlgorithm {
    ed25519      @0;
    x25519       @1;
    p256         @2;
    p384         @3;
    rsa2048      @4;
    rsa3072      @5;
    rsa4096      @6;
    # Post-quantum placeholders; added as capOS ships them.
    mlKem768     @7;  # ML-KEM (Kyber) for KEM
    mlDsa65      @8;  # ML-DSA (Dilithium) for signatures
}

enum SignatureScheme {
    default      @0;  # algorithm's natural default (Ed25519 pure, RSA-PSS, etc.)
    ecdsaSha256  @1;
    ecdsaSha384  @2;
    rsaPssSha256 @3;
    rsaPssSha512 @4;
    rsaPkcs1Sha256 @5;  # for compatibility only
}

enum PublicKeyFormat {
    spkiDer     @0;
    jwk         @1;
    opensshWire @2;
    pgpPacket   @3;
}

Shared metadata

enum KeyPurpose {
    generic       @0;
    blockVolume   @1;
    objectStore   @2;
    envelope      @3;   # KEK — only wraps/unwraps
    integrity     @4;   # MAC-only
    tls           @5;
    codeSigning   @6;
    instanceIdentity @7;
    authToken     @8;   # session tokens, JWTs
    webauthn      @9;
    audit         @10;
    oauthClientAssertion @11;  # RFC 7523 private_key_jwt client auth
    oidcIdToken   @12;          # IdP-side ID token signing (LocalIdentityProvider)
    dpopBinding   @13;          # RFC 9449 proof-of-possession keypairs
}

identifier (bytes in info()) is an opaque, stable handle usable for logging, correlating audit records, and looking up the key in a KeyVault. It is not a secret. It is not a cryptographic hash of the key (that would let an attacker confirm a guessed key); it is a random ID chosen at key creation.

Key sources

A KeySource produces keys given some unlock context. Different implementations realize different trust models.

interface KeySource {
    # Produce a key given an unlock context (passphrase bytes, a
    # passkey assertion, a sealed blob, an attestation report, empty
    # for sources that hold keys directly).
    unlockSymmetric @0 (context :Data, purpose :KeyPurpose)
                    -> (key :SymmetricKey);
    unlockPrivate   @1 (context :Data, purpose :KeyPurpose)
                    -> (key :PrivateKey);

    # Seal a key under this source's policy. The returned blob can be
    # stored in the clear; unlock will refuse to produce the key
    # unless its policy is satisfied.
    sealSymmetric @2 (key :SymmetricKey, policy :SealPolicy)
                  -> (blob :Data);
    sealPrivate   @3 (key :PrivateKey, policy :SealPolicy)
                  -> (blob :Data);

    # Rewrap: unseal under current policy, reseal under new policy.
    # Used for KEK rotation without touching the underlying key.
    rewrap @4 (blob :Data, newPolicy :SealPolicy) -> (newBlob :Data);

    info @5 () -> (kind :KeySourceKind, identifier :Data);
}

enum KeySourceKind {
    manifestEmbedded @0;  # dev/CI only
    passphrase       @1;
    passkeyPrf       @2;  # WebAuthn PRF extension
    tpm2             @3;
    secureEnclave    @4;
    cloudKms         @5;
    attestation      @6;  # SEV-SNP / TDX / Nitro
    network          @7;  # Tang/Clevis-style
    softwareStored   @8;  # encrypted-at-rest in a KeyVault
    oidcFederated    @9;  # OIDC AccessToken -> KMS / remote unlock, no baked creds
}

struct SealPolicy {
    union {
        none          @0 :Void;
        pcr           @1 :PcrPolicy;
        kms           @2 :KmsPolicy;
        attested      @3 :AttestationPolicy;
        composite     @4 :List(SealPolicy);  # AND of sub-policies
        tokenExchange @5 :TokenExchangePolicy;  # OIDC/OAuth2-gated unlock
    }
}

struct TokenExchangePolicy {
    # The OIDC issuer whose tokens satisfy this policy.
    issuer          @0 :Text;
    # Required token audience (the KMS / STS endpoint).
    audience        @1 :Text;
    # Required subject predicate. Union allows exact or pattern matches
    # without growing this struct; see oidc-and-oauth2-proposal for the
    # full pattern grammar.
    subjectPattern  @2 :Text;
    # Additional required claims (e.g. `groups`, tenant ID, attestation
    # fields). Values are JSON-encoded bytes.
    requiredClaims  @3 :List(NamedClaim);
    # Acceptable LoA levels mapped from `acr`/`amr`.
    minAuthStrength @4 :UInt8;
}

struct NamedClaim {
    name  @0 :Text;
    value @1 :Data;
}

struct PcrPolicy {
    pcrMask   @0 :UInt32;            # bitmap of PCR indices
    pcrDigest @1 :Data;              # expected composite digest
    bank      @2 :TpmHashBank;
}

struct KmsPolicy {
    provider    @0 :Text;            # "aws", "gcp", "azure", "vault", ...
    keyId       @1 :Text;
    grantTokens @2 :List(Text);
}

struct AttestationPolicy {
    platform        @0 :AttestationPlatform;
    measurement     @1 :Data;
    signerPublicKey @2 :Data;
    allowedVariant  @3 :List(Data);  # e.g. permitted firmware versions
}

enum AttestationPlatform {
    sevSnp @0;
    tdx    @1;
    nitro  @2;
}

Key lifecycle — the KeyVault

A KeyVault is a stateful service that stores sealed blobs, issues key handles, handles rotation, and emits audit events. It is distinct from KeySource: a KeySource is a factory producing keys; a KeyVault is a registry tracking the keys a deployment knows about.

interface KeyVault {
    # Generate a fresh key of the given algorithm and purpose. Seals
    # it under `policy` and stores the blob. Returns a handle for
    # later retrieval.
    generateSymmetric @0 (algorithm :SymmetricAlgorithm,
                          purpose :KeyPurpose,
                          policy :SealPolicy)
                      -> (handle :KeyHandle, key :SymmetricKey);
    generatePrivate   @1 (algorithm :AsymmetricAlgorithm,
                          purpose :KeyPurpose,
                          policy :SealPolicy)
                      -> (handle :KeyHandle, key :PrivateKey);

    # Import an externally-generated key. Useful for recovery and for
    # deployments that compute keys elsewhere (e.g. an HSM).
    importSymmetric @2 (material :Data,
                        algorithm :SymmetricAlgorithm,
                        purpose :KeyPurpose,
                        policy :SealPolicy)
                    -> (handle :KeyHandle);
    importPrivate   @3 (material :Data,
                        algorithm :AsymmetricAlgorithm,
                        purpose :KeyPurpose,
                        policy :SealPolicy)
                    -> (handle :KeyHandle);

    # Retrieve a previously-generated or -imported key. The caller
    # must supply the unlock context appropriate to the seal policy.
    openSymmetric @4 (handle :KeyHandle, unlock :Data)
                  -> (key :SymmetricKey);
    openPrivate   @5 (handle :KeyHandle, unlock :Data)
                  -> (key :PrivateKey);

    # Rotate the sealing KEK (cheap; metadata only — re-seals the
    # blob). Rotating the key *material* requires rewriting everywhere
    # it is used and is the caller's responsibility.
    rotateSeal @6 (handle :KeyHandle, newPolicy :SealPolicy) -> ();

    # Delete a key. Subject to policy (audit log retention, legal
    # hold, etc.) — the concrete vault decides.
    destroy @7 (handle :KeyHandle, reason :Text) -> ();

    # List handles matching a filter. Returns only metadata.
    list @8 (filter :KeyFilter) -> (entries :List(KeyEntry));
}

struct KeyHandle {
    identifier @0 :Data;
}

struct KeyEntry {
    handle    @0 :KeyHandle;
    algorithm @1 :KeyAlgorithm;   # union of symmetric/asymmetric
    purpose   @2 :KeyPurpose;
    createdAt @3 :UInt64;
    lastUsed  @4 :UInt64;
    policy    @5 :SealPolicy;
}

struct KeyFilter {
    purpose   @0 :List(KeyPurpose);      # OR
    algorithm @1 :List(KeyAlgorithm);    # OR
    createdSince @2 :UInt64;
}

struct KeyAlgorithm {
    union {
        symmetric  @0 :SymmetricAlgorithm;
        asymmetric @1 :AsymmetricAlgorithm;
    }
}

Concrete Key Sources

Not all of these ship on day one. Phases below give a sequence.

ManifestEmbeddedKeySource — development and CI only

Key material baked into SystemManifest. Unsealable. Boot-time validation refuses to build a production-profile image against this source. Used for QEMU smoke tests and hermetic CI.

PassphraseKeySource — interactive unlock

Consumes a passphrase from the console login flow (boot-to-shell-proposal.md), runs Argon2id with per-source parameters, derives a KEK, unwraps sealed blobs. No persistent state beyond the salt and KDF parameters (which are public).

PasskeyPrfKeySource — session unlock from WebAuthn

Consumes a WebAuthn assertion whose hmac-secret / PRF extension yields a per-credential symmetric secret. Derives a KEK from the PRF output; KEK unwraps the user’s sealed DEK. Key material never leaves the authenticator; the PRF output never leaves the key service process.

Tpm2KeySource — hardware-bound, measured-boot-gated

A TPM 2.0 driver service holds the TPM; this source wraps it. Seal policies bind keys to PCR digests; unseal succeeds only if the running boot chain matches. Enables unattended boot while keeping the key off the disk.

SecureEnclaveKeySource — platform key stores

Analog for Apple Secure Enclave, Android StrongBox, Intel CSE. Same interface shape as Tpm2KeySource; different backing primitive.

CloudKmsKeySource — cloud envelope encryption

Wraps a cloud KMS (AWS KMS, GCP KMS, Azure Key Vault, HashiCorp Vault, KMIP). Unlock calls the KMS Decrypt operation with a wrapped DEK and returns the plaintext DEK as a SymmetricKey cap. Seal calls KMS Encrypt under a named KEK.

Authentication to KMS uses the InstanceIdentity cap from cloud-metadata-proposal.md; no long-lived credentials live in the capOS image.

Properties the system gets by following the envelope pattern:

  • Free KEK rotation (rewrap the DEK; volume data is untouched).
  • Revocation by disabling the KMS key or revoking the IAM grant.
  • Cross-account / cross-region access via KMS grants.
  • Every unwrap appears in the cloud provider’s audit log — observability comes for free.

AttestationKeySource — confidential computing

Consumes SEV-SNP, TDX, or Nitro attestation reports. unlock submits the report to a remote verifier (often cloud KMS with attestation policy) which returns the unwrapped DEK only if the report matches an approved measurement. Enables “only this specific capOS image, running on genuine attested hardware, can decrypt this volume.”

NetworkKeySource — Tang / Clevis-style

Unlock derives a key by interacting with one or more remote servers; no single server sees the plaintext key (when combined with secret sharing). Supports the “revoke access by taking the server offline” model without physical-access requirements.

SoftwareStoredKeySource — encrypted on disk, under another source

The recursive case: a source whose seal policy points at another source. Used to compose, e.g., a file-backed key store encrypted under a TPM-sealed master key. The outer source provides integrity (TPM seal); the inner source provides convenience (named key lookup).

OidcFederatedKeySource — token-exchange-gated unlock

Derives a key from a short-lived OIDC/OAuth2 access token. The source holds an OAuthClient or WorkloadIdentityFederation cap (from oidc-and-oauth2-proposal.md). unlock obtains a fresh token for the configured audience — either by exchanging a local InstanceIdentity JWT, a Kubernetes projected service-account token, or a user session’s access token — then presents it to a remote KMS / STS / custom key service which returns the wrapped DEK.

Two common shapes:

  1. Cloud KMS with workload identity federation. Audience is the cloud STS; after token exchange the resulting cloud credential calls KMS Decrypt. Replaces every baked long-lived cloud IAM credential in the image.
  2. Per-user volume. Audience is a capOS-internal key service; the user’s AccessToken cap proves the caller is Alice; the key service enforces TokenExchangePolicy and returns Alice’s DEK.

Properties the envelope + token-exchange pattern gets the system:

  • No long-lived credentials in any capOS image.
  • Per-principal KMS audit (the token sub appears in every KMS decrypt log).
  • Revocation by IdP account disable, token revocation, or KMS grant removal.
  • Step-up authentication gating: a TokenExchangePolicy requiring minAuthStrength >= loa3 means Alice must have MFA-backed acr/amr claims before her volume unlocks.

Consumers

A non-exhaustive list of how this interface is meant to be used. Each consumer either exists as a proposal or is called out as future work.

ConsumerInterfaceKey source
EncryptedBlockDevicesymmetricany
EncryptedNamespacesymmetricpassphrase / passkeyPrf / KMS
TLS termination (web gateway)bothpassphrase / KMS / cloud certs
SSH host key signingprivateKeyVault / softwareStored / KMS
SSH public-key loginpublicCredentialStore / authorized key store
mTLS between servicesbothKeyVault with KMS seal
Instance identity JWT signingprivatecloudKms / softwareStored
Signed audit logsprivateKeyVault, append-only policy
WebAuthn verificationpublicCredentialStore (public keys)
Signed boot manifestspublicpublic key baked into firmware
Encrypted swapsymmetricper-boot ephemeral (in-RAM)
Encrypted backupssymmetricdedicated KMS key
Session tokens (HMAC)symmetricKeyVault, rotated frequently

Relationship to CredentialStore

The CredentialStore in boot-to-shell-proposal.md stores verifiers — WebAuthn public keys, password hashes, recovery codes. Its job is authentication: matching a claim from a user against a stored verifier.

The KeyVault proposed here stores keys — symmetric DEKs, signing private keys, KEKs. Its job is cryptography: producing keys for use by capOS services.

Overlap happens at passkey unlock: the CredentialStore verifies the WebAuthn assertion; the resulting PRF output feeds a PasskeyPrfKeySource that produces a SymmetricKey usable by EncryptedNamespace. Two services, one flow.

Keeping these distinct matters because their audit, retention, and exposure models differ. A CredentialStore can expose every stored entry as metadata (public keys are public) without leaking secrets; a KeyVault cannot. A deployment may want different replication, backup, and recovery policies for authenticators vs. encryption keys.

Threat Model

Separate from the consumer-specific threat models, the crypto/key management service itself has these:

  1. Memory scraping of a live key service. The service holds plaintext keys in RAM. Mitigation: small trusted-computing-base (one crate, audited), mlock the heap (no swap leakage), zeroize on drop, no panic-induced core dumps, cap-scoped access so only callers with a Key cap can trigger operations. Against a kernel exploit, no defense; that is a separate threat.
  2. Oracle abuse. A malicious service granted a SymmetricKey cap uses it as a decryption oracle. Mitigation: granting callers attenuated caps (decrypt-only, aad-pinned). Audit records make abuse detectable.
  3. Side-channel leakage. Timing, cache, power. Mitigation: use constant-time implementations (aes crate’s hardware backend; chacha20poly1305 crate is constant-time), prefer AEAD modes that resist nonce-reuse gracefully (GCM-SIV), avoid bespoke crypto.
  4. Downgrade attacks on algorithm selection. A caller requests a weak algorithm on a key that supports stronger modes. Mitigation: info() records the canonical algorithm; KeyPurpose constrains the method set; algorithm negotiation is the caller’s job, not a feature of the key cap.
  5. Key persistence in unintended places. Kernel DMA buffers, swap, crash dumps, core files. Mitigations are deployment-level (no swap, or encrypted swap with a per-boot key; disable core dumps for the key service process; measure the boot chain so a tampered kernel is detectable).

Phases

Phases align with the subsystems that need keys. Crypto primitives come first; consumers follow their own proposals’ phases.

Phase 1 — Interfaces and RAM-only implementation

  • Add SymmetricKey, PrivateKey, PublicKey, KeySource, KeyVault, and the enums/structs above to schema/capos.capnp.
  • Implement a RAM-only key service using vetted Rust crates (aes-gcm-siv, chacha20poly1305, ed25519-dalek, x25519-dalek, rsa, hmac, hkdf). No persistence. Pure interface exercise.
  • ManifestEmbeddedKeySource for dev/CI.
  • Host tests: AEAD round-trips, signature round-trips, key agreement, fuzz the decrypt/verify paths.

Phase 2 — KeyVault with in-memory storage

  • Key generation, sealed blob storage, handle-based lookup.
  • rotateSeal implementation (metadata-only KEK rotation).
  • Policy enforcement for seal/unseal.
  • Audit cap integration (system-monitoring-proposal.md).

Phase 3 — Persistent KeyVault over the Store

  • Sealed blobs live in a Store or Namespace.
  • Access control: KeyVault cap is itself attenuable (read-only, purpose-filtered).
  • Cross-reboot survival requires the Store, which requires persistent storage tracked in docs/roadmap.md.

Phase 4 — PassphraseKeySource and PasskeyPrfKeySource

  • Passphrase flow wires into console login.
  • PasskeyPRF flow wires into WebAuthn assertions from the web text shell gateway.
  • Per-user EncryptedNamespace becomes implementable end-to-end.

Phase 5 — Tpm2KeySource

  • TPM 2.0 driver as a userspace service (separate crate; talks to the TPM over x86 platform TIS or a virtio passthrough in cloud VMs).
  • Seal policies bound to PCR digests.
  • Measured-boot chain definition (firmware → bootloader → kernel → init → key service). PCR composition documented.

Phase 6 — CloudKmsKeySource

  • AWS KMS first; GCP KMS, Azure Key Vault, HashiCorp Vault, KMIP follow.
  • Depends on InstanceIdentity from cloud-metadata and a functioning network stack.
  • Cross-region / cross-account grant handling documented.

Phase 6b — OidcFederatedKeySource

  • Depends on OAuthClient and WorkloadIdentityFederation from oidc-and-oauth2-proposal.md.
  • Workload identity federation to cloud KMS (no baked long-lived IAM credentials). Subject token sources: InstanceIdentity, attestation report envelope, Kubernetes projected token, GitHub Actions OIDC.
  • Per-user volume unlock via user AccessToken against a capOS-internal key service honoring SealPolicy.tokenExchange.
  • TokenExchangePolicy enforcement for seal/unseal.

Phase 7 — AttestationKeySource

  • SEV-SNP, TDX, or Nitro — whichever the first target cloud environment requires.
  • Verifier can be cloud KMS with attestation policy or a standalone service.

Phase 8 — Post-quantum migration

  • Add ML-KEM and ML-DSA to the algorithm enums when capOS picks its PQ stack. Primarily a schema evolution and an added sign / agree path; no change to the interface shape.

Relationship to Other Proposals

  • volume-encryption-proposal.md — primary first consumer. Uses SymmetricKey for block and object encryption, EncryptedBlockDevice factory as a wrapper over a raw BlockDevice + a SymmetricKey cap.
  • boot-to-shell-proposal.mdCredentialStore stores authenticator verifiers; PasskeyPrfKeySource here produces keys from assertions that pass CredentialStore verification.
  • networking-proposal.md — TLS and mTLS need PrivateKey/PublicKey; instance mTLS bootstraps from a CloudKmsKeySource or KeyVault-issued service identity key.
  • ssh-shell-proposal.md — SSH host keys are sign-only PrivateKey wrappers backed by KeyVault; accepted OpenSSH-format public keys are verifier material that map to sessions but never grant shell authority directly.
  • certificates-and-tls-proposal.md — layers X.509, trust stores, CT, OCSP, pinning, ACME, and TLS config on top of the keys defined here. A TLS server’s private key lives in a KeyVault from this proposal; its chain, verifier, and stapler live in that one.
  • oidc-and-oauth2-proposal.md — OIDC/OAuth2 client, token, JWKS, JWT wrapper, DPoP, and workload identity federation caps compose with the keys defined here. OidcFederatedKeySource and SealPolicy.tokenExchange live in this proposal because they are key-source shapes; the token protocol frame, discovery, JWKS handling, grant types, and verifier live there. KeyPurpose.oauthClientAssertion / .oidcIdToken / .dpopBinding mark keys used by that proposal’s flows. JwtSigner / JwtVerifier are thin wrappers defined there over PrivateKey / PublicKey from here.
  • cloud-metadata-proposal.mdInstanceIdentity cap consumed by CloudKmsKeySource and AttestationKeySource.
  • user-identity-and-policy-proposal.md — per-user keys are bound to session identity; the same cap chain that says “you are Alice” yields Alice’s SymmetricKey via PasskeyPrfKeySource.
  • cloud-deployment-proposal.md — hardware abstraction for self-encrypting drives sets up a future SelfEncryptingBlockDevice cap with hardware-held keys, a distinct trust model from software-crypto keys here.
  • security-and-verification-proposal.md — crypto is a top target for tiered tooling: constant-time linting, AEAD fuzzing, Loom models of the unlock state machine, Kani-style proofs of nonce-uniqueness.
  • system-monitoring-proposal.md — every Key method call, every KeyVault operation, and every KeySource.unlock should flow through the audit cap. Schema for audit events is defined there; key-management produces a specific event family.
  • formal-mac-mic-proposal.md — includes GOST-style modeling. GOST symmetric (Kuznyechik, Magma) and asymmetric (Streebog-signed schemes) algorithms can be added to the enums when a deployment requires them.
  • storage-and-naming-proposal.md — Open Question #5 (manifest trust, secure boot) is a prerequisite for Tpm2KeySource to be meaningful.

Open Questions

  1. Canonical algorithm set for v1. Overshooting the enum invites implementation sprawl; undershooting forces schema evolution early. Proposed minimum: aes256GcmSiv, xchacha20Poly1305, hmacSha256, ed25519, x25519. Add rsa*, p256, post-quantum as real consumers arrive.
  2. Does SymmetricKey expose raw encrypt-without-AAD? AEAD with empty AAD is trivially expressible, but some callers may want explicit guarantees that non-AEAD modes are unavailable. Decide whether the interface permits aad == Data() universally or whether KeyPurpose constrains it.
  3. Public key distribution. PublicKey is a cap, but public material is public — should there be a “public key is freely-shareable bytes” escape hatch outside the cap system? Probably yes; export() exists for exactly that reason. How does a caller obtain a PublicKey cap from raw bytes? Via a PublicKeyImporter factory that verifies format, or directly in KeyVault.importPublic?
  4. Revocation of in-flight caps. If a SymmetricKey cap is granted to 10 services and the key is compromised, can the issuer revoke it? capOS cap revocation is generally “drop at each holder”; this might warrant a KeyVault.revoke(handle) that breaks the server-side object so every encrypt/decrypt returns an error. Worth designing explicitly rather than leaving implicit.
  5. Audit record granularity. Logging every encrypt call for a high-throughput volume is noisy; logging only unseal events misses oracle abuse. Probably: unseal and policy-violation events are always logged; per-operation logging is a per-KeyVault policy, off by default.
  6. Key-use quotas. Rate-limit decrypt operations per cap-holder to contain oracle abuse? Nice to have; not clear whether it belongs at the Key interface or at a KeyVault policy.
  7. HSM integration. PKCS#11 is the de facto standard for HSM access. Does capOS grow a Pkcs11KeySource, or does each HSM vendor ship a capability-native driver? The cap-native path is cleaner but depends on vendor cooperation.
  8. Backwards compatibility with stored blobs. SealPolicy, algorithm IDs, and seal blob formats will evolve. Define a versioned envelope around every sealed blob from day one, so rolling upgrades are possible.
  9. Side-channel guarantees per implementation. Document the expectation for each KeyAlgorithm (e.g. “constant-time required for aes*; use the aes crate’s hardware backend on x86_64 and bit-sliced implementation elsewhere”). Without this, the security posture varies silently across builds.
  10. GOST and other jurisdiction-mandated algorithms. The formal-mac-mic-proposal.md carves out a GOST-style track. Adding Kuznyechik, Magma, and Streebog-signed schemes is an additive extension; what matters is that the enums stay forward- compatible so a GOST-capable build does not require a schema fork.