Contents
Install · Concepts · Quick Start · Provider matrix · Rotation playbook · Threat model · Examples · Documentation · License
Install
[]
= "0.0.9"
= { = "0.0.9", = ["pepper"] }
MSRV 1.75 stable. no_std-friendly for LocalPepper; KMS providers require std + tokio.
Provider features
| Feature | Status | Pulls in |
|---|---|---|
default |
always | LocalPepper + Pepper trait |
aws-kms |
stub | (future) aws-sdk-kms |
gcp-kms |
stub | (future) gcloud-kms |
azure-key-vault |
stub | (future) azure_security_keyvault |
hashicorp-vault |
stub | (future) vaultrs |
Provider features today expose the stable FetchOpts shape and a fetch_pepper stub that returns PepperError::Backend("not yet wired up"). Real implementations land incrementally as integration-test infrastructure (localstack / cloud-mock containers) comes online.
Concepts
| Concept | What it is |
|---|---|
| Pepper | A server-side secret applied to every password before it is hashed. Lives in a KMS / HSM, separate from the password database. |
Pepper trait |
Sync interface producing HMAC-SHA-256(key_at(version), password) → 32-byte tag. |
KeyVersion |
Monotonically increasing u32 carried alongside each peppered hash. Makes rotation non-destructive. |
LocalPepper |
In-memory pepper provider. Use for tests, short-lived workloads, or pin in-process secrets at startup. |
FetchOpts |
Per-provider options struct (aws::FetchOpts, gcp::FetchOpts, etc.) carrying the KMS key reference + encrypted key versions. |
Full design rationale: ADR-0003 — Pepper key-versioning scheme.
Quick Start
use ;
#
Then attach to a hsh policy:
use Arc;
use ;
use ;
#
Provider matrix
| Provider | Module | FetchOpts shape |
Status |
|---|---|---|---|
| In-memory | [LocalPepper] |
builder API (add / current / build) |
✅ live |
| AWS KMS | [aws] |
key_id, per-version encrypted blobs, current |
🚧 stub |
| GCP Cloud KMS | [gcp] |
key_resource, per-version encrypted blobs, current |
🚧 stub |
| Azure Key Vault | [azure] |
vault_url, secret_name, per-version refs, current |
🚧 stub |
| HashiCorp Vault | [vault] |
address, mount, key_name, encrypted blobs |
🚧 stub |
The trait shape is stable. Stubs return PepperError::Backend("not yet wired up"); replace with real implementations as your deployment requires.
Rotation playbook
- Generate a fresh 32-byte pepper, register it as
KeyVersion::new(N+1)in your KMS. - Add it to the
LocalPepperkeyset alongside the existing versions — do not remove old versions yet. - Bump
currenttoN+1and redeploy. - As users log in,
verify_and_upgradereturnsOutcome::Valid { rehashed: Some(new_phc) }carryingkeyver=N+1; persistnew_phc. - After a chosen window (e.g. 90 days), audit your DB for rows still on old keyvers. Force-rotate inactive users via fresh sign-in.
- Once no rows reference an old keyver, drop it from the keyset on the next deploy.
Full deployment guide: doc/KMS-INTEGRATION.md.
Threat model
Defends against
- Offline brute force after a password-DB breach (attacker doesn't have the pepper).
- Pepper-key compromise within a single rotation window (old hashes migrate transparently).
Does NOT defend against
- KMS compromise (the pepper is in the same trust boundary as your KMS).
- An attacker who can read both the password DB and execute code with the running app's privileges.
- Online brute force — rate-limit your login endpoint separately.
For FIPS deployments where the pepper must never leave the HSM, see doc/FIPS.md.
Examples
See crates/hsh-kms/examples/ for runnable demos:
local_pepper.rs— build aLocalPepperand apply it.rotation.rs— multi-version keyset + rotation simulation.refuse_without_pepper.rs— fail-closed behaviour demo.
Run with cargo run -p hsh-kms --example local_pepper.
Documentation
| Doc | What's in it |
|---|---|
adr/0003-pepper-key-versioning.md |
Storage format, rotation contract, fail-closed rationale |
KMS-INTEGRATION.md |
AWS / GCP / Azure / Vault deployment guides |
SECURITY.md |
Vulnerability reporting |
License
Dual-licensed under Apache 2.0 or MIT, at your option.