Dependency policy
This repository implements cryptography, so its dependency graph is part of its attack surface. The rules below exist to keep that surface small and reviewable, and to make the reasoning behind a pin visible instead of implicit.
Two rules
- One implementation of a cryptographic primitive per lockfile. If two versions of the same hash or signature crate resolve into the same build, the binary contains two implementations of the same primitive. That doubles the code that must be reviewed, makes “which one produced this digest” ambiguous at the call site, and means a fix in one does not reach the other. Prefer a single resolved version even when that means staying on an older release.
- A hash-dependency bump must be justified by known-answer tests, not by a green suite. “All tests pass” is not evidence that the digest is unchanged, because almost every test asserts a relationship between digests (equal inputs agree, unequal inputs differ) rather than a value. Such a suite stays green if the digest changes wholesale. Known-answer vectors computed independently of this code are the only evidence that survives.
Current state: sha2 is deliberately held at 0.10
sha2 0.11 is released, and neither repository should move to it yet.
stellar-xdr 28 — the Stellar XDR crate this repository needs for the Soroban
boundary — requires sha2 0.10, and so does the pinned crucible-simulator
revision this repository compiles. Raising this repository’s own sha2 to
0.11 therefore resolves two sha2 versions, and two digest
generations (0.10 and 0.11), into a single build. That is rule 1 violated to
gain nothing: the digest output is byte-identical either way.
Evidence, reproducible from a checkout:
$ sed -i 's/^sha2 = "0.10"$/sha2 = "0.11"/' Cargo.toml
$ cargo update -p sha2
Adding sha2 v0.11.0
$ grep -A1 'name = "sha2"' Cargo.lock | grep version
version = "0.10.9"
version = "0.11.0"
$ grep -c 'name = "generic-array"' Cargo.lock
1
$ cargo tree -i sha2@0.10.9
sha2 v0.10.9
├── crucible-core v0.1.0 (https://github.com/Crucible-TDA/crucible-simulator?rev=…)
└── stellar-xdr v28.0.0
Note also that generic-array 0.14 stays in the tree regardless, because it
arrives through the same upstream that holds sha2 back — so the bump does
not even reduce the dependency count.
Revisit when stellar-xdr and the pinned simulator both accept sha2
0.11. At that point the bump is a one-line change, and the acceptance test is
that the known-answer vectors below still pass unchanged.
What protects the hash surface today
Both repositories now pin their digests with known-answer vectors computed
with an independent implementation (Python’s hashlib), so a change in the
hash, the encoding, or the domain separator fails loudly rather than silently
rewriting persisted state:
| Repository | Vectors | Pins |
|---|---|---|
crucible-prover | crucible-interfaces (proof_provider::proof) | ArtifactChecksum::from_bytes over fixed byte strings |
crucible-prover | crucible-artifacts (checksum) | the whole-tree digest over a fixed entry list, plus a test asserting the result is not a hash of a hash |
crucible-simulator | crucible-core (hashing) | hash_bytes / hash_str over fixed domains and payloads, plus a test asserting the digest is not a bare SHA-256 of the value |
Any change to these constants in a diff is a change to a wire or state format and must be justified as such.