Skip to content

Commit 69beb1f

Browse files
chore(repo): align workspace structure with noyalib standard
Bring HSH's repo layout in line with the canonical noyalib shape so all of @sebastienrousseau's Rust projects share the same mental model and tooling. Top-level additions ------------------- - LICENSES/Apache-2.0.txt + LICENSES/MIT.txt — REUSE 3.3 canonical license-text location at the workspace root. - REUSE.toml — REUSE 3.3 aggregate annotations for files without inline SPDX headers (Cargo.toml, config files, fixtures, packaging templates, generated artefacts, CI workflows). - about.hbs — Handlebars template for cargo-about; produces NOTICE.md grouped by license with crate listings and license text. - GETTING_STARTED.md — on-ramp for new users; first hash/verify example + per-channel install matrix + common-paths table. - GLOSSARY.md — domain vocabulary used across the docs and code (PHC / MCF / KDF / Argon2id / pepper / OWASP-2025 / SLSA L3 / …). - PLAN.md — long-form roadmap with the Phase 0-7 mapping to milestone #1 issues, working invariants (CI-always-green, signed commits, forbid(unsafe_code), OS-CSPRNG-only, coverage >= 93%), and the v0.0.10 candidate list. Per-crate additions ------------------- - crates/<each>/LICENSE-APACHE + LICENSE-MIT — symlinks to the workspace-root files. Cargo bundles these into the published tarball so crates.io / docs.rs / lib.rs all show licensing alongside the crate-specific README. - crates/<each>/doc/internals.md — contributor-facing module map, data-flow diagrams (verify_and_upgrade dispatch flow, peppered hash wire format, etc.), "where to make a change" matrix, testing strategy. - crates/<each>/doc/errors.md — every Error / DigestError / PepperError variant with display prefix, when emitted, recovery guidance. Acts as the canonical reference for downstream `match` blocks; supplements rustdoc's auto-generated enum page. Scripts ------- - scripts/msrv-per-crate.sh — verify each crate's declared rust-version actually builds. Workspace-wide `cargo check` enforces the floor of the workspace root only; this walks every crate manifest, installs the declared toolchain, and runs --no-default-features + --all-features against it. Fails on drift. --- THE ARCHITECT ᛫ Sebastien Rousseau ᛫ https://sebastienrousseau.com THE ENGINE ᛞ EUXIS ᛫ Enterprise Unified Execution Intelligence System ᛫ https://euxis.co Assisted-by: Claude:claude-opus-4-7
1 parent 2a98e07 commit 69beb1f

24 files changed

Lines changed: 1727 additions & 0 deletions

GETTING_STARTED.md

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
<!-- SPDX-FileCopyrightText: 2023-2026 Hash (HSH) library contributors -->
2+
<!-- SPDX-License-Identifier: MIT OR Apache-2.0 -->
3+
4+
# Getting started with hsh
5+
6+
A focused walkthrough for someone who just landed on the repo and
7+
wants to *use* the library. The full reference is the
8+
[root README](./README.md); this page is the on-ramp.
9+
10+
## Install
11+
12+
### As a Rust library
13+
14+
```toml
15+
[dependencies]
16+
hsh = "0.0.9"
17+
```
18+
19+
For peppered / KMS-backed deployments, enable the optional feature:
20+
21+
```toml
22+
[dependencies]
23+
hsh = { version = "0.0.9", features = ["pepper"] }
24+
```
25+
26+
### As a CLI tool
27+
28+
The `hsh` binary ships through every mainstream package channel:
29+
30+
```sh
31+
cargo install hsh-cli --locked # crates.io
32+
brew tap sebastienrousseau/tap && brew install hsh # macOS
33+
yay -S hsh-bin # Arch / AUR
34+
```
35+
36+
The library crate is `hsh`; the binary crate that produces the
37+
`hsh` executable is `hsh-cli`. See the
38+
[Install](./README.md#install) section of the root README for the
39+
full per-channel matrix (Cargo, Homebrew, AUR, Scoop, Debian,
40+
Docker / GHCR, shell installer).
41+
42+
## First hash + verify
43+
44+
```rust
45+
use hsh::{api, Outcome, Policy};
46+
47+
fn main() -> Result<(), hsh::Error> {
48+
// 1. Pick a policy. OWASP Password Storage Cheat Sheet 2025 is
49+
// the right default; it uses Argon2id with m=19 456 KiB,
50+
// t=2, p=1.
51+
let policy = Policy::owasp_minimum_2025();
52+
53+
// 2. Hash the password. `api::hash` returns a PHC-format string
54+
// you store in your database as-is.
55+
let stored = api::hash(&policy, "correct horse battery staple")?;
56+
println!("stored: {stored}");
57+
58+
// 3. Verify on next login. `verify_and_upgrade` does both the
59+
// match check AND tells you whether the stored hash is below
60+
// current policy — if so, it hands you a freshly-hashed PHC
61+
// string to persist alongside the user row.
62+
let outcome = api::verify_and_upgrade(
63+
&policy,
64+
"correct horse battery staple",
65+
&stored,
66+
)?;
67+
68+
match outcome {
69+
Outcome::Valid { rehashed: Some(new_phc) } => {
70+
// Policy drifted — persist `new_phc` against the user
71+
// row. The next login reads the upgraded hash directly.
72+
let _ = new_phc;
73+
}
74+
Outcome::Valid { rehashed: None } => {
75+
// Match, no rehash needed. Common case.
76+
}
77+
Outcome::Invalid => {
78+
// Wrong password — return a generic auth-error to the
79+
// caller. Do NOT distinguish "wrong password" from
80+
// "user not found" in the response.
81+
}
82+
}
83+
Ok(())
84+
}
85+
```
86+
87+
This is the **only** API pair you need for 95 % of password-storage
88+
deployments. Everything else (FIPS contract, pepper / KMS, custom
89+
parameters, legacy migration) is built on top of these two calls.
90+
91+
## First CLI invocation
92+
93+
```sh
94+
# Hash a password from stdin
95+
$ echo -n "hunter2" | hsh hash --algorithm argon2id
96+
$argon2id$v=19$m=19456,t=2,p=1$…
97+
98+
# Verify against a stored PHC string
99+
$ echo -n "hunter2" | hsh verify -H '$argon2id$v=19$m=19456,t=2,p=1$…'
100+
valid
101+
102+
# Measure host-specific parameter cost
103+
$ hsh calibrate --algorithm argon2id --target-ms 500
104+
argon2id m=131072 t=2 p=1 ≈ 503 ms
105+
106+
# Generate shell completions
107+
$ hsh completions zsh > ~/.zsh/functions/_hsh
108+
```
109+
110+
The `hsh inspect <phc>` and `hsh rehash <phc>` subcommands round
111+
out the surface — see `hsh --help` for the full menu.
112+
113+
## Common paths from here
114+
115+
| If you need… | Read |
116+
|---|---|
117+
| **Migrating from another crate** (`argonautica`, `rust-argon2`, `bcrypt`, `password-auth`, `djangohashers`) | [`doc/MIGRATION-from-*.md`](./doc/) |
118+
| **FIPS 140-3 deployment** (PBKDF2 fail-closed routing) | [`doc/FIPS.md`](./doc/FIPS.md) |
119+
| **AWS / GCP / Azure / HashiCorp Vault peppering** | [`doc/KMS-INTEGRATION.md`](./doc/KMS-INTEGRATION.md) |
120+
| **Per-host benchmark calibration** | [`doc/BENCHMARKS.md`](./doc/BENCHMARKS.md) + `hsh calibrate` |
121+
| **Comparing `hsh` to other Rust password-hashing crates** | [`doc/COMPARISON.md`](./doc/COMPARISON.md) |
122+
| **Vocabulary** (PHC, MCF, OWASP, KDF, …) | [`GLOSSARY.md`](./GLOSSARY.md) |
123+
| **Architectural decisions** | [`doc/adr/`](./doc/adr/) |
124+
| **Stability tier per public symbol** | [`doc/API-STABILITY.md`](./doc/API-STABILITY.md) |
125+
| **Vulnerability reporting** | [`SECURITY.md`](./SECURITY.md) |
126+
127+
## What `hsh` is *not*
128+
129+
- **Not post-quantum cryptography.** Argon2id raises the cost of
130+
offline brute-force on classical and quantum hardware alike (Grover
131+
yields only a √-speedup), but it is not a PQ primitive. For ML-KEM
132+
/ ML-DSA / SLH-DSA, use [`aws-lc-rs`](https://crates.io/crates/aws-lc-rs).
133+
- **Not a self-validating FIPS 140-3 module.** The
134+
`Backend::Fips140Required` *contract* is enforced — `api::hash`
135+
refuses to mint non-FIPS-routed hashes — but the underlying crypto
136+
today is the pure-Rust RustCrypto stack. The `aws-lc-rs` validated
137+
backend is a follow-up.
138+
- **Not a general-purpose digest library.** For SHA-2 / SHA-3 /
139+
BLAKE3 content addressing, use the companion
140+
[`hsh-digest`](https://crates.io/crates/hsh-digest) crate.
141+
142+
If something on this page is unclear, please open an issue — every
143+
question becomes a future paragraph.

GLOSSARY.md

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
<!-- SPDX-FileCopyrightText: 2023-2026 Hash (HSH) library contributors -->
2+
<!-- SPDX-License-Identifier: MIT OR Apache-2.0 -->
3+
4+
# Glossary
5+
6+
Domain vocabulary used across `hsh`'s documentation, code, and commit
7+
messages. When a term has both a cryptographic-spec meaning and an
8+
`hsh`-specific meaning, both are listed.
9+
10+
## Password hashing & key derivation
11+
12+
**KDF (Key Derivation Function).** A function that turns a password
13+
into a fixed-length tag using deliberate computational cost. The four
14+
KDFs `hsh` ships with are Argon2id, bcrypt, scrypt, and PBKDF2.
15+
16+
**Argon2.** A memory-hard KDF — RFC 9106 — with three variants:
17+
*Argon2id* (hybrid, recommended for password storage),
18+
*Argon2i* (resistant to side-channel leakage; legacy),
19+
*Argon2d* (resistant to GPU cracking; legacy). `hsh` mints new
20+
hashes under Argon2id by default; the other two are accepted on the
21+
verify path so legacy stored hashes round-trip.
22+
23+
**Bcrypt.** A 1999-era KDF based on the Blowfish key schedule.
24+
Truncates input to 72 bytes silently — `hsh` enforces a hard 72-byte
25+
*safety rail* (CVE-2025-22228 class). Use `BcryptParams::with_prehash`
26+
to opt into an HMAC-SHA-256 pre-hash adapter for longer inputs.
27+
28+
**Scrypt.** A memory-hard KDF — RFC 7914 — tunable via `N` (work
29+
factor), `r` (block size), `p` (parallelism). OWASP-2025 minimum is
30+
`N = 2^17`, `r = 8`, `p = 1`.
31+
32+
**PBKDF2.** Iteration-hard KDF — RFC 8018 — with HMAC-SHA-256 or
33+
HMAC-SHA-512 as the PRF. OWASP-2025 minimums: 600 000 iterations for
34+
SHA-256, 210 000 for SHA-512. The only KDF with a FIPS 140-3
35+
validated implementation path today (via `aws-lc-rs`).
36+
37+
**Salt.** A per-password random value mixed into the KDF input to
38+
defeat rainbow-table precomputation. `hsh` draws every salt from the
39+
OS CSPRNG (`getrandom::OsRng`); `vrd` / `rand::thread_rng` /
40+
`fastrand::Rng::new` are explicitly banned via `clippy.toml`.
41+
42+
**Pepper.** A *server-side* secret applied to every password before
43+
the KDF, typically via HMAC-SHA-256. Unlike a salt (per-password,
44+
stored alongside the hash), the pepper is the same for every password
45+
and lives in a separate trust boundary — usually a KMS or HSM that
46+
the password database cannot read. See [`doc/KMS-INTEGRATION.md`](./doc/KMS-INTEGRATION.md).
47+
48+
**Pre-hash.** Hashing the password with a cheap fast hash (e.g.
49+
HMAC-SHA-256) before passing the digest to a length-bounded KDF
50+
like bcrypt. Lets you accept arbitrarily long inputs without silent
51+
truncation.
52+
53+
## Storage formats
54+
55+
**PHC string format.** Modular Crypt Format successor —
56+
`$<algo>$v=<ver>$<params>$<salt>$<hash>` — standardised at
57+
<https://github.com/P-H-C/phc-string-format>. `hsh` emits PHC for
58+
Argon2id, scrypt, and PBKDF2. Interoperable with Django, Devise,
59+
libsodium, the Argon2 reference CLI, and most other ecosystems.
60+
61+
**MCF (Modular Crypt Format).** The pre-PHC predecessor —
62+
`$<algo>$<rest>` — with per-algorithm bespoke `<rest>` encoding.
63+
`hsh` emits MCF for bcrypt (`$2b$<cost>$<salt-and-hash>`) because
64+
the `bcrypt` crate has no PHC encoder.
65+
66+
**`hsh-pepper:` wrapper.** Bespoke `hsh`-specific format
67+
(`hsh-pepper:<keyver>:<inner-phc-or-mcf>`) used when a `Policy`
68+
attaches a pepper provider. The key-version field lets the verifier
69+
locate the right HMAC key and triggers transparent rotation on next
70+
verify under a newer current key.
71+
72+
## Policy & verification
73+
74+
**Policy.** A versioned snapshot of the primary algorithm + per-
75+
algorithm parameters used by `api::hash`. Construct via the
76+
[`Policy::owasp_minimum_2025`](./doc/API-STABILITY.md) /
77+
`rfc9106_first_recommended` / `fips_140_pbkdf2` presets, or via
78+
`PolicyBuilder`.
79+
80+
**Auto-rehash on policy drift.** `api::verify_and_upgrade` returns
81+
`Outcome::Valid { rehashed: Some(_) }` whenever the stored hash
82+
falls below current policy — algorithm drift, parameter drift, PBKDF2
83+
PRF drift, or pepper-version drift. The caller persists the new PHC
84+
string on next successful login.
85+
86+
**Backend.** A *requirement* the caller declares on a `Policy`.
87+
`Backend::Native` accepts any KDF; `Backend::Fips140Required`
88+
restricts new-hash minting to PBKDF2 and refuses Argon2 / bcrypt /
89+
scrypt — see [`doc/FIPS.md`](./doc/FIPS.md). The actual FIPS-validated
90+
crypto routes through `aws-lc-rs` (Phase 4 follow-up).
91+
92+
## Security primitives
93+
94+
**Constant-time comparison.** Byte comparison that takes the same
95+
wall-clock time regardless of where the inputs differ. Defeats
96+
timing side-channel attacks on the verify path. `hsh` uses
97+
`subtle::ConstantTimeEq` everywhere a hash is compared.
98+
99+
**Zeroize.** Erasing a secret from memory on drop, defeating heap-
100+
residue forensic recovery. `hsh` uses `zeroize::ZeroizeOnDrop` for
101+
password / hash / salt / pepper-key buffers.
102+
103+
**OS CSPRNG.** Cryptographically-secure pseudorandom number
104+
generator provided by the operating system —
105+
`getrandom(2)` / `/dev/urandom` on Linux, `BCryptGenRandom` on
106+
Windows, `getentropy(2)` on macOS / BSD. The only acceptable salt
107+
source for password hashing.
108+
109+
## Standards & compliance
110+
111+
**OWASP-2025.** The OWASP Password Storage Cheat Sheet
112+
recommendations valid at the start of 2025 — Argon2id
113+
`m=19 456 KiB t=2 p=1`, bcrypt `cost=10`, scrypt
114+
`N=2^17 r=8 p=1`, PBKDF2 600 000 iters (SHA-256).
115+
116+
**FIPS 140-3.** US federal standard for cryptographic module
117+
validation — <https://csrc.nist.gov/projects/cryptographic-module-validation-program/standards>.
118+
`hsh` itself isn't validated; the `Backend::Fips140Required`
119+
contract delegates the primitive to a FIPS-validated module via
120+
`aws-lc-rs` (Phase 4 follow-up). See ADR-0004.
121+
122+
**RFC 9106.** Argon2 specification — <https://datatracker.ietf.org/doc/html/rfc9106>.
123+
§4 names the "first recommended" parameter set
124+
(`m=2^21`, `t=1`, `p=4`) and the "second recommended" set
125+
(`m=2^16`, `t=3`, `p=4`).
126+
127+
**RFC 7914.** Scrypt specification.
128+
129+
**RFC 8018.** PKCS #5 — PBKDF2 specification.
130+
131+
**SLSA L3.** Supply-chain Levels for Software Artifacts level 3 —
132+
<https://slsa.dev/spec/v1.0/levels>. `hsh` release artefacts ship
133+
with SLSA L3 build provenance attestations via
134+
`actions/attest-build-provenance`.
135+
136+
**Sigstore / cosign.** Keyless signing infrastructure —
137+
<https://www.sigstore.dev/>. Every `hsh` release artefact is signed
138+
via `cosign sign-blob` and verifiable via the Sigstore Rekor
139+
transparency log.
140+
141+
## Project-specific
142+
143+
**Phase N.** A discrete unit of work tracked in the v0.0.9
144+
milestone — see [`PLAN.md`](./PLAN.md). Phases 0-7 cover Foundation
145+
→ Core refactor → Operational hardening → Pepper/KMS → FIPS contract
146+
→ CLI → General hashing → v1.0 stabilisation.
147+
148+
**ADR.** Architecture Decision Record — short markdown documents
149+
under [`doc/adr/`](./doc/adr/) capturing irreversible design choices
150+
(scope, FIPS strategy, pepper-key versioning, zero-unsafe policy,
151+
v1.0 stability contract).
152+
153+
**Compat shim.** The `compat-v0_0_x` feature flag re-exposes the
154+
pre-0.0.9 stringly-typed API for one release cycle so existing
155+
callers can upgrade gradually. Slated for removal in v0.2.0 per
156+
[`doc/API-STABILITY.md`](./doc/API-STABILITY.md).
157+
158+
**Calibrate.** The `hsh calibrate <algorithm> --target-ms <N>`
159+
subcommand measures the host's KDF throughput and suggests a
160+
parameter set that lands within ±10 % of the target wall time.

0 commit comments

Comments
 (0)