feat(keys): the content-address layer — canonical hash derivation and key encoding (ADR-002)

Wave 1 task core-hashing-keys: the crate's declared one-way door,
implemented byte-exact to ADR-002.

- src/hashing.rs: Algorithm (exactly GitBlobSha256 canonical /
  GitBlobSha1 tolerated; BLAKE3 absent), Digest (known-length
  derivation H("blob <len>\0" + content) as primary constructor;
  from_git_oid_hex interop constructor, lowercase-only acceptance),
  and the hashing::registry constants (0x01/0x02, 0x00 reserved; new
  algorithm = new ADR)
- src/keys.rs: typed Key wrapper; as_bytes/from_bytes are the only
  backend-boundary conversions; 1 algorithm byte + digest bytes
  (33/21 total); rejects-never-guesses validation -> Error::KeyInvalid
- tests/git_interop.rs: the load-bearing test — byte-exact derivation
  against the `git hash-object` CLI, both algorithms, small and
  75-KiB content; external-oid round-trip into pool keys; both oid
  formats coexisting in one flat pool
- named design point (oid hex policy): lowercase-only acceptance,
  recorded in the task Notes and on from_git_oid_hex

Verified: cargo test (15 passed), cargo clippy --all-targets -- -D
warnings, cargo fmt --check, cargo test --all-features — all green.
This commit is contained in:
glm-5.3-flash committed 2026-10-11 18:20:12 +00:00
1 parent fc5ab57d18
commit fef05b4ec5
6 files changed
+775 -13

No files matched your search

+63 -10
View File
@@ -1,7 +1,7 @@
---
id: core-hashing-keys
name: Hashing & keys — the canonical derivation and the one-way-door encoding
status: pending
status: completed
depends_on: [core-error-model]
scope: narrow
risk: medium
@@ -61,19 +61,19 @@ line-by-line at the wave gate.
## Acceptance Criteria
- [ ] `Digest`/`Key`/algorithm enum per ADR-002; encoding byte-exact
- [x] `Digest`/`Key`/algorithm enum per ADR-002; encoding byte-exact
(33/21 total lengths; 0x01/0x02 tags; 0x00 rejected)
- [ ] Canonical derivation matches `git hash-object` byte-exact for
- [x] Canonical derivation matches `git hash-object` byte-exact for
both algorithms (CLI-driven test, both a small and a multi-KiB
content case)
- [ ] `from_git_oid_hex` round-trips external oids into pool keys
- [ ] Rejects: truncated keys, unknown algorithm bytes (incl. 0x00,
- [x] `from_git_oid_hex` round-trips external oids into pool keys
- [x] Rejects: truncated keys, unknown algorithm bytes (incl. 0x00,
0xFF), wrong digest lengths → `Error::KeyInvalid`, never guesses
- [ ] Total-order sort stability asserted (mixed-algorithm collection
- [x] Total-order sort stability asserted (mixed-algorithm collection
sorts deterministically)
- [ ] Doc comments carry the one-way-door warning and the registry
- [x] Doc comments carry the one-way-door warning and the registry
note (new algorithm = new ADR)
- [ ] `cargo test`, `cargo clippy --all-targets -- -D warnings`,
- [x] `cargo test`, `cargo clippy --all-targets -- -D warnings`,
`cargo fmt --check` green
## References
@@ -85,8 +85,61 @@ line-by-line at the wave gate.
## Notes
> To be filled by implementation agent
- Module layout: two files, one module per file — `src/hashing.rs`
(the hash side: `Algorithm` + `Digest` + the derivation + the
interop constructor) and `src/keys.rs` (the key side: the typed
`Key` wrapper + the physical encoding). The scaffold's stub doc on
`hashing.rs` was replaced by the full module doc. Both re-exported
from `lib.rs` (`Algorithm`, `Digest`, `Key`, mirroring the
`Error` re-export).
- **Named design point resolved — oid hex parsing policy:
lowercase-only acceptance.** `git hash-object`'s own output is
always lowercase; strictness at the boundary is the
rejects-never-guesses posture. Uppercase oids are rejected as
malformed (`Error::KeyInvalid`) — the check is a byte scan for
`[0-9a-f]` before decode (the `hex` crate's `decode` alone would
accept uppercase). Policy stated verbatim on
`Digest::from_git_oid_hex` and covered by test. If an external
interop consumer ever needs uppercase acceptance, that is a change
at this boundary with its own test.
- `Algorithm` is declared `GitBlobSha256` before `GitBlobSha1` so
derived `Ord` matches registry-byte order; the sort-stability test
asserts derived order == `as_bytes()` byte order. `Digest` keeps
its fields private behind `from_parts` (pub(crate)), so every
digest's length stays pinned by its algorithm.
- Registry constants live in `hashing::registry` (`GIT_BLOB_SHA256`
0x01, `GIT_BLOB_SHA1` 0x02, `RESERVED` 0x00) with the new-ADR doc
note. `Algorithm::try_from_byte` is public; `Key::from_bytes` is
the boundary consumer.
- Deps pinned as the task scoped: `sha2 0.10`, `sha1 0.10`, `hex
0.4` (RustCrypto + hex). The generic derivation helper is bound
over the RustCrypto digest trait so the derivation body has zero
per-algorithm special-casing; the match arms pick only the hasher.
- No `unwrap`/`expect`/panic outside tests; library paths are
`Result`/`Option` only (error.rs's `Error::KeyInvalid` carries the
boundary rejections).
## Summary
> To be filled on completion
`src/hashing.rs` + `src/keys.rs` implement the content-address layer
per ADR-002, the crate's declared one-way door. `Algorithm` (exactly
`GitBlobSha256` canonical / `GitBlobSha1` tolerated; BLAKE3 absent),
`Digest` (known-length derivation `H("blob <len>\0" + content)` as
primary constructor; `from_git_oid_hex` interop constructor with
lowercase-only acceptance, both oid formats selected by length), and
`Key` (typed wrapper; `as_bytes`/`from_bytes` the only backend-boundary
conversions; `1 algorithm byte + digest bytes` — 0x01+32, 0x02+20,
33/21 total) with rejects-never-guesses validation (`Error::KeyInvalid`
for truncated/unknown-tag/wrong-length/reserved-0x00; never a guess).
Registry constants in `hashing::registry` with the new-ADR doc note;
one-way-door warnings in both module docs. Tests: byte-exact
derivation against the live `git hash-object` CLI (the load-bearing
test — both algorithms × empty/short/binary-NUL/75-KiB, sha256 repos
via `git init --object-format=sha256` per POC finding 4's harness
gotcha), external-oid round-trip into pool keys, both-oid-formats
coexistence in one flat pool, hardcoded git-computed vectors, encoding
byte-exactness, full rejection matrix, and derived-order == byte-order
sort stability on mixed-algorithm collections. Verified: `cargo test`
(15 passed), `cargo clippy --all-targets -- -D warnings`,
`cargo fmt --check`, and `cargo test --all-features` all green. Deps
added: `sha1`, `sha2`, `hex`.