Skip to content

Bind vault ciphertext to its entry identity #283

Description

@forkwright

Finding

Vault AEAD authenticates only the secret bytes. It does not authenticate the fjall key (the credential name), credential type, or a versioned record identity. A writer who can edit the store can therefore transplant a valid serialized entry beneath another name; Vault::get decrypts it successfully and reports the caller-requested name.

Evidence

crates/kryphos/src/crypto.rs:71-111 calls Aead::encrypt(&nonce, plaintext) and decrypt(nonce, encrypted), so both operations use the convenience API with no associated data.

crates/kryphos/src/storage.rs:57-67 keeps credential_type, metadata, status, and history outside the encrypted secret. add encrypts only secret and inserts the serialized value under caller-supplied name at lines 228-264. get fetches by that name, decrypts the stored ciphertext, and constructs DecryptedEntry { name: CompactString::from(name), ... } at lines 277-297. Nothing checks that the ciphertext was created for that record identity.

Related #132 concerns confidentiality of additional columns; this is an authentication and record-substitution defect.

Why this matters

ChaCha20-Poly1305 proves that a ciphertext was produced under the vault key, but the current envelope does not prove which credential the ciphertext belongs to. Store corruption or a write-capable attacker can silently cause a caller asking for one credential to receive another valid secret. The operation succeeds, so downstream use and audit records can attribute the wrong credential without any cryptographic failure.

Desired correction

Define a versioned authenticated envelope and use AEAD associated data that canonically binds at least the vault/entry identity, credential name, credential type, and format version. Verify those fields on every decrypt and provide an explicit migration path for existing entries.

Done when a regression test moves a valid stored value beneath a different key (and separately mutates bound type/version fields) and get rejects it instead of returning a successfully decrypted, misidentified credential.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions