Repository navigation
Add BLAKE2b-512, BLAKE2s-256 (RFC 7693) and BLAKE3 (#123) - #180
roy-basmacier wants to merge 18 commits into
Conversation
blake2: - The suspended state records the parameter block's key length kk (one byte; states grow to 213 / 109 bytes). After the first compression the hash and MAC states were byte-identical, so a MAC state resumed as a hash finished the MAC with no key, and a hash state resumed as a MAC verified a plain digest as a tag. Resume now refuses kk != 0 on the hash side and kk != the key's kk on the MAC side; a valid key of the same length is still not detected after a compression (documented). - read_state rejects t > 0 with an empty buffer: update always keeps at least one byte back after a compression, and finishing such a state gave a digest of no byte string. - The t == 0 h checks compare in constant time (ct_eq_bytes over the serialized words, held in Secret buffers). blake3: - derive_key moves to its own type, BLAKE3DeriveKey (Hash + XOF), which is not Suspendable, nor is its squeezer: a derive_key state held up to 64 bytes of key material verbatim, which Suspendable forbids. BLAKE3::new_derive_key is removed; BLAKE3 is the hash mode only, and BLAKE3 / BLAKE3Squeezer resume refuse derive_key (and keyed) states. - BLAKE3Squeezer resume rejects an output block counter above 2^58 (s. 2.6: ell < 2^64 bytes). A counter near u64::MAX panicked in debug builds and wrapped in release, re-emitting output already handed out. - The hash-mode IV check compares in constant time. Both crates use one MIN_MAC_OUTPUT_LEN, now in bouncycastle_core::traits beside MAC, instead of a copy each. cli: one MAC key loader for every MAC command (helpers::load_mac_key, with the algorithm's key-length range). HMAC and KMAC now let --key-file win over --key, as their help text always said, and report bad hex or a missing key file instead of panicking. test_hmac.sh pins the precedence. factory: a Memory Usage section. BLAKE3's 1728-byte chaining-value stack makes HashFactory, XOFFactory and MACFactoryWorker 1888 bytes (SHA-3, at 440, was the largest before). docs: each spec quote on security strength now lives once, on the Algorithm impl, and the keyed BLAKE3 suspend note is stated once, on the SuspendableKeyed impl. The derivation: the key words enter only as the first chaining value of each chunk and parent (s. 2.4, 2.5), so while the prefix is 65..=1024 bytes with an empty CV stack the state finishes any extension within the first chunk without the key.
blake2: - The suspended state records the parameter block's key length kk (one byte; states grow to 213 / 109 bytes). After the first compression the hash and MAC states were byte-identical, so a MAC state resumed as a hash finished the MAC with no key, and a hash state resumed as a MAC verified a plain digest as a tag. Resume now refuses kk != 0 on the hash side and kk != the key's kk on the MAC side; a valid key of the same length is still not detected after a compression (documented). - read_state rejects t > 0 with an empty buffer: update always keeps at least one byte back after a compression, and finishing such a state gave a digest of no byte string. - The t == 0 h checks compare in constant time (ct_eq_bytes over the serialized words, held in Secret buffers). blake3: - derive_key moves to its own type, BLAKE3DeriveKey (Hash + XOF), which is not Suspendable, nor is its squeezer: a derive_key state held up to 64 bytes of key material verbatim, which Suspendable forbids. BLAKE3::new_derive_key is removed; BLAKE3 is the hash mode only, and BLAKE3 / BLAKE3Squeezer resume refuse derive_key (and keyed) states. - BLAKE3Squeezer resume rejects an output block counter above 2^58 (s. 2.6: ell < 2^64 bytes). A counter near u64::MAX panicked in debug builds and wrapped in release, re-emitting output already handed out. - The hash-mode IV check compares in constant time. Both crates use one MIN_MAC_OUTPUT_LEN, now in bouncycastle_core::traits beside MAC, instead of a copy each. cli: one MAC key loader for every MAC command (helpers::load_mac_key, with the algorithm's key-length range). HMAC and KMAC now let --key-file win over --key, as their help text always said, and report bad hex or a missing key file instead of panicking. test_hmac.sh pins the precedence. factory: a Memory Usage section. BLAKE3's 1728-byte chaining-value stack makes HashFactory, XOFFactory and MACFactoryWorker 1888 bytes (SHA-3, at 440, was the largest before). docs: each spec quote on security strength now lives once, on the Algorithm impl, and the keyed BLAKE3 suspend note is stated once, on the SuspendableKeyed impl. The derivation: the key words enter only as the first chaining value of each chunk and parent (s. 2.4, 2.5), so while the prefix is 65..=1024 bytes with an empty CV stack the state finishes any extension within the first chunk without the key.
6f5b43a to
a1016d6
Compare
3c170c9 to
81cef74
Compare
a1016d6 to
280b233
Compare
blake2: - The suspended state records the parameter block's key length kk (one byte; states grow to 213 / 109 bytes). After the first compression the hash and MAC states were byte-identical, so a MAC state resumed as a hash finished the MAC with no key, and a hash state resumed as a MAC verified a plain digest as a tag. Resume now refuses kk != 0 on the hash side and kk != the key's kk on the MAC side; a valid key of the same length is still not detected after a compression (documented). - read_state rejects t > 0 with an empty buffer: update always keeps at least one byte back after a compression, and finishing such a state gave a digest of no byte string. - The t == 0 h checks compare in constant time (ct_eq_bytes over the serialized words, held in Secret buffers). blake3: - derive_key moves to its own type, BLAKE3DeriveKey (Hash + XOF), which is not Suspendable, nor is its squeezer: a derive_key state held up to 64 bytes of key material verbatim, which Suspendable forbids. BLAKE3::new_derive_key is removed; BLAKE3 is the hash mode only, and BLAKE3 / BLAKE3Squeezer resume refuse derive_key (and keyed) states. - BLAKE3Squeezer resume rejects an output block counter above 2^58 (s. 2.6: ell < 2^64 bytes). A counter near u64::MAX panicked in debug builds and wrapped in release, re-emitting output already handed out. - The hash-mode IV check compares in constant time. Both crates use one MIN_MAC_OUTPUT_LEN, now in bouncycastle_core::traits beside MAC, instead of a copy each. cli: one MAC key loader for every MAC command (helpers::load_mac_key, with the algorithm's key-length range). HMAC and KMAC now let --key-file win over --key, as their help text always said, and report bad hex or a missing key file instead of panicking. test_hmac.sh pins the precedence. factory: a Memory Usage section. BLAKE3's 1728-byte chaining-value stack makes HashFactory, XOFFactory and MACFactoryWorker 1888 bytes (SHA-3, at 440, was the largest before). docs: each spec quote on security strength now lives once, on the Algorithm impl, and the keyed BLAKE3 suspend note is stated once, on the SuspendableKeyed impl. The derivation: the key words enter only as the first chaining value of each chunk and parent (s. 2.4, 2.5), so while the prefix is 65..=1024 bytes with an empty CV stack the state finishes any extension within the first chunk without the key.
blake2: - The suspended state records the parameter block's key length kk (one byte; states grow to 213 / 109 bytes). After the first compression the hash and MAC states were byte-identical, so a MAC state resumed as a hash finished the MAC with no key, and a hash state resumed as a MAC verified a plain digest as a tag. Resume now refuses kk != 0 on the hash side and kk != the key's kk on the MAC side; a valid key of the same length is still not detected after a compression (documented). - read_state rejects t > 0 with an empty buffer: update always keeps at least one byte back after a compression, and finishing such a state gave a digest of no byte string. - The t == 0 h checks compare in constant time (ct_eq_bytes over the serialized words, held in Secret buffers). blake3: - derive_key moves to its own type, BLAKE3DeriveKey (Hash + XOF), which is not Suspendable, nor is its squeezer: a derive_key state held up to 64 bytes of key material verbatim, which Suspendable forbids. BLAKE3::new_derive_key is removed; BLAKE3 is the hash mode only, and BLAKE3 / BLAKE3Squeezer resume refuse derive_key (and keyed) states. - BLAKE3Squeezer resume rejects an output block counter above 2^58 (s. 2.6: ell < 2^64 bytes). A counter near u64::MAX panicked in debug builds and wrapped in release, re-emitting output already handed out. - The hash-mode IV check compares in constant time. Both crates use one MIN_MAC_OUTPUT_LEN, now in bouncycastle_core::traits beside MAC, instead of a copy each. cli: one MAC key loader for every MAC command (helpers::load_mac_key, with the algorithm's key-length range). HMAC and KMAC now let --key-file win over --key, as their help text always said, and report bad hex or a missing key file instead of panicking. test_hmac.sh pins the precedence. factory: a Memory Usage section. BLAKE3's 1728-byte chaining-value stack makes HashFactory, XOFFactory and MACFactoryWorker 1888 bytes (SHA-3, at 440, was the largest before). docs: each spec quote on security strength now lives once, on the Algorithm impl, and the keyed BLAKE3 suspend note is stated once, on the SuspendableKeyed impl. The derivation: the key words enter only as the first chaining value of each chunk and parent (s. 2.4, 2.5), so while the prefix is 65..=1024 bytes with an empty CV stack the state finishes any extension within the first chunk without the key.
280b233 to
d43cc3b
Compare
|
Merged release/0.1.4alpha (now at 563978a, the fix/supplied-nonce squash) into this branch and pushed 25e7334 to the PR head. The merge also brought in the #192 squash, which removed |
…it in two as the signature and cipher workers are split and moves the tag length into the type (item 1 of the core API consistency review of 2026-11-09, the MAC roles being the one pair whose split stopped at the key): in core, Encrypting, Decrypting and the sealed Direction move from bouncycastle-cipher to bouncycastle_core::direction (cipher re-exports them), MAC is replaced by MACGenerator<MAC_LEN> (do_update, do_final_out into a [u8; MAC_LEN], do_final) and MACVerifier<MAC_LEN> (do_update, do_verify_final(&[u8; MAC_LEN]) -> Result<(), MACError>) with no one-shots on the worker, no run-time length or InvalidLength on the streaming path, verification a Result with MACError::VerificationFailed and MACError::RNGError for a nonce draw, both dyn-compatible; MacGenerationKey<MAC_LEN> / MacVerificationKey<MAC_LEN> bound their workers on the pair and their one-shots take and return the array, GMAC gets the nonce-bound role pair NonceMacGenerationKey / NonceMacVerificationKey it lacked (create_generator(rng) returning the nonce, create_generator_nonce, create_verifier(nonce), the mac / mac_out / mac_nonce / mac_nonce_out / verify one-shots) in place of the inherent pair on BlockCipherKey, MacMode<P, MAC_LEN> has generate_init / verify_init for mac_init and NonceMacMode's GATs are bound; HMAC<'k, H, Dir> implements the pair with the role impls bound H: HMACParams<MACKey = KeyMaterial<N>> under a const assertion N == H::OUTPUT_LEN, hazmat::finish_out infallible, MIN_FIPS_DIGEST_LEN gone with the truncating finals, HMAC::keygen_from_rng now HmacKey::keygen_from_rng, the HMAC_SHA256<'k, Dir> aliases and the worker not Clone; KMAC's output length is the key type's const parameter (KMAC128Key<MAC_LEN = 32>, KMAC256Key<MAC_LEN = 64>, with_params taking only the customization string) with the SP 800-185 Sec 8.4.2 floor and right_encode cap a compile-time check on the key constructors and on KMACInternal::finish, so no tag is produced or verified below the floor even from a resumed state (compile_fail doctests at lengths 0 and 3, a 4-byte suspend round trip), KMAC128<Dir, MAC_LEN> the worker whose suspended state carries the length; HKDF's expand loop runs HMAC through the hazmat halves; Cmac<'k, P, Dir, BLOCK_LEN> with the tag the full block (CMAC_MIN_TAG_LEN gone), Gmac's inherent methods the two trait impls, GMAC's nonce drawn through modes::iv::random_iv, which returns RNGError; Poly1305<Dir> from Poly1305Key::into_generator / into_verifier with the alloc dependency gone and the verifier holding its recomputed tag in a Secret as HMAC's and KMAC's do, the AEAD holding Poly1305<Dir> through a private trait; the RFC 6979 HMAC calls in ecdsa taking the array final; AES_CMAC_128<'k, Dir> and the ARIA and Camellia siblings; MACFactory as the run-time form with inherent mac / mac_out / verify over slices, output_len read off the key and worker types through one private TagLen trait, and MACFactoryWorker<'k, Dir> whose do_final_out finishes into out[..N] and reports InvalidLength for a wrong-length buffer or tag; the CLI's do_mac making only the direction a command runs and refusing a wrong-length --verify before reading input, kmac* computing through KMACXOF's length-binding final read compared in constant time through bouncycastle-utils, aes*-gmac through the nonce-bound roles; the harnesses naming the directional workers; and, from the review rounds, every key-level one-shot that can fail before its zeroizing helper (the five drawing forms and the four declared-length CCM forms) clearing its buffer first with core_test_framework::FailingRNG and a CCM<13, 16> refusal case pinning it, the _out rule in QUALITY_AND_STYLE.md stating its one exception (an infallible [u8; N] overwritten in full), and the test-framework suites taking the tag as an array, sweeping every single-bit change, and covering the nonce-bound pair. Dropped with no twin: tag truncation through the API (a protocol truncates the returned array; a truncated tag cannot be verified through the role or factory APIs, SP 800-38B Sec 6.3 is not implemented, and the Wycheproof HMAC truncated groups and RFC 4231 case 5 are prefix-compared), Poly1305Key::into_mac, MACFactory's role-trait impls, and KMAC128Key::<16>::with_params in expression position since an alias's default const is not applied there. Reviewed in two rounds by Megan's three-agent setup (mythos, codex, muse; 0 Bug, the three Hardening items and sixteen Notes all fixed, "ready to approve" at c308fa2) and by a final high-effort pass before this squash; mutants on apollo in the PR: hmac 67 (42/24/1 equivalent), poly1305 184 (165/14/5 equivalent), chacha20-poly1305 121 (77/44/0), factory mac_factory.rs 54 (26/28/0), sha3 kmac.rs 111 (55/54/2 equivalent), cipher cmac/gmac/key.rs with the aes tests 162 (75/82/5 equivalent or unreachable), hkdf 120 (84/36/0), core MAC roles 28 (20/8/0). feature/eccdh, feature/rsa-sig, feature/tdes, PR #177 and PR #180 take the new trait names by merge. Created by David Hook. Assisted-by: Claude:claude-mythos-5-1
Adds the three BLAKE hashes requested in bcgit#123 for OpenSSL provider provisioning, on the key-object API of feature/key-rework-memory (bcgit#172): two new crates wired into the factories, the CLI and the stack-memory benches. Other BLAKE2 digest lengths, salt/personalisation, BLAKE2bp/sp/X and tree mode are out of scope. blake2 (new crate bouncycastle-blake2): - BLAKE2b_512 and BLAKE2s_256 implement Hash and Suspendable; G, F, the parameter block and the final-t rule are commented against RFC 7693 s. 3.1-3.3. blake2b.rs and blake2s.rs are kept structurally identical. - The native keyed mode (not HMAC): key objects BLAKE2b_512_MACKey and BLAKE2s_256_MACKey (FromAnyKeyMaterial, MacGenerationKey, MacVerificationKey) hand out BLAKE2b_512_MAC / BLAKE2s_256_MAC workers. Key checks run type, then strength, then length (1..=64 / 1..=32). - AlgorithmOID: id-blake2b512 and id-blake2s256 (RFC 7693 s. 4), on the hash and MAC types. blake3 (new crate bouncycastle-blake3): - BLAKE3 implements Hash and XOF in hash mode (new) and derive_key mode (new_derive_key); BLAKE3Squeezer streams the extended output. - keyed_hash: key object BLAKE3KeyedHashKey (exactly 32 bytes) hands out BLAKE3KeyedHash workers, which keep an inherent into_squeezer for extended output. BLAKE3KeyedSqueezer is deliberately not Suspendable. - The engine follows the reference implementation's chunk state and 54-entry CV stack (spec s. 2.4-2.6, 5.1). Both crates: - MAC output truncates to the caller's buffer with a 4-byte floor (MIN_MAC_OUTPUT_LEN); every _out function zeroizes the buffer before any error check. Partial-bit input: 0 bits is a whole-byte message, 1..=7 returns InvalidInput, more returns InvalidLength. - Keyed workers implement SuspendableKeyed with the key object as the key. Suspended states hold no key bytes, and resume rebuilds the key-derived fields from the key object. - Design note: a suspended keyed state still lets its holder compute tags for extensions of the absorbed prefix (BLAKE2 once the key block is compressed; BLAKE3 only within the first chunk). HMAC does not have this property. It is documented and pinned by tests. factory: HashFactory gains BLAKE2b-512, BLAKE2s-256 and BLAKE3; XOFFactory gains BLAKE3; MACFactory gains the three BLAKE MAC keys and workers. cli: blake2b-512, blake2s-256, blake2b-512-mac, blake2s-256-mac, blake3, blake3-keyed-hash and blake3-derive-key, all streaming. cli/tests/ test_blake.sh adds 16 tests on the shell harness. tests: RFC 7693 Appendix A/B, selections from the official BLAKE2 KAT, the bc-test-data Blake2KAT.txt sweep (512 + 512 records), all 35 official BLAKE3 vectors in all three modes, and the core-test-framework suites for Hash, XOF, MAC keys, key construction and suspend/resume. Wycheproof has no BLAKE vectors. cargo mutants: blake2 378 tested, 10 missed; blake3 384 tested, 9 missed, 8 timeouts (non-terminating loops); every miss is an equivalent rewrite with a "Mutants note:" at the site. the Vec return types in core::traits::Hash. Assisted-by: Claude:claude-mythos-5-1 Assisted-by: Claude:claude-fable-5-1 Assisted-by: Claude:claude-opus-5-5 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
blake2: - The suspended state records the parameter block's key length kk (one byte; states grow to 213 / 109 bytes). After the first compression the hash and MAC states were byte-identical, so a MAC state resumed as a hash finished the MAC with no key, and a hash state resumed as a MAC verified a plain digest as a tag. Resume now refuses kk != 0 on the hash side and kk != the key's kk on the MAC side; a valid key of the same length is still not detected after a compression (documented). - read_state rejects t > 0 with an empty buffer: update always keeps at least one byte back after a compression, and finishing such a state gave a digest of no byte string. - The t == 0 h checks compare in constant time (ct_eq_bytes over the serialized words, held in Secret buffers). blake3: - derive_key moves to its own type, BLAKE3DeriveKey (Hash + XOF), which is not Suspendable, nor is its squeezer: a derive_key state held up to 64 bytes of key material verbatim, which Suspendable forbids. BLAKE3::new_derive_key is removed; BLAKE3 is the hash mode only, and BLAKE3 / BLAKE3Squeezer resume refuse derive_key (and keyed) states. - BLAKE3Squeezer resume rejects an output block counter above 2^58 (s. 2.6: ell < 2^64 bytes). A counter near u64::MAX panicked in debug builds and wrapped in release, re-emitting output already handed out. - The hash-mode IV check compares in constant time. Both crates use one MIN_MAC_OUTPUT_LEN, now in bouncycastle_core::traits beside MAC, instead of a copy each. cli: one MAC key loader for every MAC command (helpers::load_mac_key, with the algorithm's key-length range). HMAC and KMAC now let --key-file win over --key, as their help text always said, and report bad hex or a missing key file instead of panicking. test_hmac.sh pins the precedence. factory: a Memory Usage section. BLAKE3's 1728-byte chaining-value stack makes HashFactory, XOFFactory and MACFactoryWorker 1888 bytes (SHA-3, at 440, was the largest before). docs: each spec quote on security strength now lives once, on the Algorithm impl, and the keyed BLAKE3 suspend note is stated once, on the SuspendableKeyed impl. The derivation: the key words enter only as the first chaining value of each chunk and parent (s. 2.4, 2.5), so while the prefix is 65..=1024 bytes with an empty CV stack the state finishes any extension within the first chunk without the key.
…nges Rebased onto bb67b77, which reshaped Hash and MAC (711a571, 0e391c4): - Hash and MAC lose max_security_strength(&self) for the AlgorithmInfo supertrait, which every Algorithm implements. The BLAKE impls drop their own copies; the values come from the Algorithm impls as before. - The one-shot hash(data) / hash_out(data, out) move off Hash onto HashOneShot. BLAKE2b_512, BLAKE2s_256 and BLAKE3 implement it, so the call is BLAKE3::hash(msg). BLAKE3DeriveKey takes its context at construction, so like cSHAKE it has inherent hash / hash_out methods instead. - factory: the BLAKE arms are added to the AlgorithmInfo impls and to MACFactoryWorker's MAC impl, whose forwarding macro became written-out matches upstream. Tests, doc examples and the mem benches follow the same call forms. The CLI MAC key loader now replaces the upstream fix to mac_cmd's own loader (same file-wins order); upstream's HMAC precedence test is kept and this branch's duplicate dropped.
…ver the rebase from feature/key-rework-memory The release's CLI already reads every MAC key through mac_cmd's own load_mac_key, which takes any length (HMAC hashes a long key, KMAC encodes its length), so the review's shared 64-byte loader is not carried over; the bounded loader stays BLAKE-only as helpers::load_blake_mac_key, now over the release's load_secret input rule. FixedSeedRNG moved to bouncycastle-rng and lost its const parameter. Assisted-by: Claude:claude-opus-5-5
release/0.1.4alpha removed SuspendableKeyed from core with the KEM squash (bcgit#192, the bcgit#173 rule that a keyed worker is not rebuilt without its key and its state is key-derived material), so the BLAKE2b-512 MAC, BLAKE2s-256 MAC and BLAKE3 keyed-hash impls of it go, with their SUSPENDED_*_MAC_STATE_LEN / SUSPENDED_BLAKE3_KEYED_HASH_STATE_LEN constants, the write_state zero_buf switch that only they used, the lib.rs paragraphs and security notes on suspended MAC states, the keyed cases of the BLAKE2 suspend tests (the kk-above-maximum check stays as suspend_rejects_bad_kk), the BLAKE3 keyed suspend tests and the harness's MAC suspend bench. The hash types and the BLAKE3 squeezer keep Suspendable unchanged. Assisted-by: Claude:claude-mythos-5-1
…ables, zero-key warning On top of 25e7334 (the MAC workers no longer suspend): - blake2: with no keyed state to suspend, the suspended hash state's kk byte had nothing left to tell apart, so it goes: the states are 212 bytes (BLAKE2b-512) and 108 (BLAKE2s-256), and read_state loses its kk bound. The t == 0 check of h against the unkeyed initial state still refuses a keyed initial state, and a test now forges one. BLAKE2s objects shrink from 120 to 112 bytes. - blake2, blake3: the Memory Usage sections give measured peak stack per operation (a stack-painting probe over the whole call chain, release, x86_64); the massif harnesses only resolve their own println! here. - factory: XOFFactorySqueezer is 432 bytes and MACFactory 864, not 448 and 896; without BLAKE3 the largest variant is a SHA-3 or KMAC state of at most 440. - blake3: a test for the byte count BLAKE3DeriveKey::hash_out returns, which cargo mutants found unchecked. - mem_usage_benches: a BLAKE2s hash suspend bench beside the BLAKE2b one. - cli: the BLAKE MAC key loader warns on an all-zero key as load_mac_key does, through one helper, warn_if_zeroized_key. The HMAC/KMAC warning never fired (it tested KeyType::Zeroized, and loaded keys are tagged Unknown); the helper checks the bytes. cargo mutants: blake2 352 tested, 10 missed (documented equivalents); blake3 402 tested, 9 missed (documented equivalents), 8 timeouts (non-terminating loops). Assisted-by: Claude:claude-opus-5-5 Assisted-by: Claude:claude-sonnet-5-5
The rebase onto release/0.1.4alpha 6144224 (bcgit#194) replaced the MAC trait with MACGenerator<MAC_LEN> / MACVerifier<MAC_LEN> and moved the tag length into the type. The replayed commits keep the release's mac_factory.rs and its tests; the BLAKE wiring is redone here on the new shape. - blake2, blake3: the workers are BLAKE2b_512_MAC<Dir>, BLAKE2s_256_MAC<Dir> and BLAKE3KeyedHash<Dir>, Encrypting implementing MACGenerator and Decrypting MACVerifier, as HMAC<'k, H, Dir> does. The keys implement MacGenerationKey<N> / MacVerificationKey<N> at N = 64, 32 and 32. The verifiers recompute the tag into a Secret and return MACError::VerificationFailed. into_squeezer is on the BLAKE3 generator only. - Tag truncation goes, as it did for HMAC, KMAC and CMAC in bcgit#194, and with it core::traits::MIN_MAC_OUTPUT_LEN, which this PR had added. - The workers and BLAKE3KeyedSqueezer are no longer Clone (QUALITY_AND_STYLE: keyed workers implement neither Suspendable nor Clone); compile_fail doctests pin the workers, and the mac_clone tests go. - factory: MACFactory and MACFactoryWorker<'k, Dir> gain the three BLAKE variants, with their tag lengths through TagLen; the blake_mac_tests helper is rewritten for the slice-based factory methods. - cli: blake2*-mac and blake3-keyed-hash go through do_mac_with_key. The keyed-hash generate path streams stdin and squeezes to stdout through the shared helpers, replacing a hand-rolled loop that panicked on a read error and allocated the whole --length output. - Tests move from the truncation and bool-verify checks to the array forms, and the stack harnesses call the key's mac_out. cargo test --workspace, cargo build --workspace --all-targets, test_blake.sh (17/17) and test_hmac.sh pass. Assisted-by: Claude:claude-opus-5-5
As QUALITY_AND_STYLE's API section asks ("A worker borrows the key it came
from"), the workers take the key's lifetime: BLAKE2b_512_MAC<'k, Dir>,
BLAKE2s_256_MAC<'k, Dir> and BLAKE3KeyedHash<'k, Dir>, so a worker cannot
outlive its key.
- blake3: the keyed worker reads the key words through the borrow instead of
copying them. Blake3Core takes its key words as a type parameter, owned
(Secret<[u32; 8]>, the default) by the unkeyed and derive_key hashers and
&'k Secret<[u32; 8]> in the keyed worker; read_state moves to the owned
impl. BLAKE3KeyedHash shrinks from 1880 to 1856 bytes and
MACFactoryWorker from 1888 to 1872.
- blake2: the worker still copies the key's keyed state, since it advances
h and overwrites the buffered key block, so the borrow is the lifetime
only.
- Tests bind the key before the worker.
Assisted-by: Claude:claude-opus-5-5
…ers are Secrets From the review of bcgit#180: - read_state accepted any t that is a whole number of blocks, so a forged state with t = 2^64 - 64 (BLAKE2s) followed by one block of input wrapped the counter: a debug build panicked in add_to_t and a release build returned a digest over a wrapped t. A t above half the counter's range (RFC 7693 s. 2.1, "0 <= ll < 2**128" / "2**64") is now InvalidData, which leaves a resumed state at least 2^127 (2^63) bytes from wrapping. suspend_rejects_bad_t pins both sides of the bound. - suspend and from_suspended staged h and the buffered input in plain stack arrays; they are Secret<[u8; CORE_STATE_LEN]> now, as the struct's fields are. Assisted-by: Claude:claude-opus-5-5
…er, CVs in Secrets From the review of bcgit#180: - BLAKE3Squeezer's state no longer carries the 64-byte output buffer, which resume copied verbatim: a forged buffer handed out up to 63 bytes that were not root output, then carried on with genuine blocks. Resume recomputes it as root output block output_block_counter - 1 when the position is inside it. SUSPENDED_BLAKE3_SQUEEZER_STATE_LEN goes from 180 to 116. - Squeezer resume accepted root flags no root has (none, CHUNK_START alone, PARENT with a chunk flag, PARENT with b != 64). It now requires a parent node (PARENT, b = 64, s. 2.5) or a chunk's last block (CHUNK_END, with or without CHUNK_START, s. 2.4). - Hash resume refuses an empty chunk after the first (chunk_counter > 0, nothing compressed, nothing buffered), which no input produces (s. 5.1.1) and whose do_final would merge an empty chunk into the tree. Two tests that built such states now give the chunk a byte. - The chaining values and compression outputs held outside compress (the CV popped off the stack, the merged and chunk CVs, a block's compression output, the derive_key context key and a squeezer block) are Secrets, declared before their loops: in keyed_hash and derive_key mode they derive from the key. compress writes into the caller's buffer; its own working state stays unzeroized, as in the SHA-2 and BLAKE2 compression functions. cachegrind on a 1 MiB hash: 27.38M instructions before, 27.14M after. Assisted-by: Claude:claude-opus-5-5
From the review of bcgit#180, against QUALITY_AND_STYLE's "No internal implementation detail in public API docs": - blake2: do_final_partial_bits_out no longer compares itself with Ascon and KMACXOF; the MAC key docs drop how the key stores its keyed state; the crate's length-extension point is stated as the crate's own reasoning rather than read into RFC 7693 s. 3.2. - blake3: the suspended-state byte layouts move from the two SUSPENDED_*_STATE_LEN docs into // comments, BLAKE3::from_suspended's doc states what it refuses without the layout, and BLAKE3KeyedSqueezer, BLAKE3DeriveKey and BLAKE3DeriveKeySqueezer say only that their state would contain the key or key material. Assisted-by: Claude:claude-opus-5-5
The MAC rework, the borrowed BLAKE3 key words and the Secret CVs change the call chains, so every row is measured again with one method and toolchain: a probe paints 64 KiB below its frame, runs the operation through a non-inlined call and reports the deepest byte disturbed, rounded up to 8 (the key-construction rows reproduce the old figures exactly). The input is now a static rather than counted, so the hash rows read about 1 KiB (BLAKE2) or 4 KiB (BLAKE3) lower than before for that reason alone; the MAC rows are the key's mac_out. Release with thin LTO, x86_64, rustc 1.97.0-nightly (d3cd04068). Assisted-by: Claude:claude-opus-5-5
cargo mutants (bouncycastle-blake2, 326 tested) misses `>` -> `>=` on the new upper-half bound in read_state; t == MAX >> 1 is all ones and so already refused as not a whole number of blocks. Assisted-by: Claude:claude-opus-5-5
cargo mutants (bouncycastle-blake3, 422 tested) misses `<` -> `<=` on the resume-time recompute of the partly read output block; at position 64 that block is never read. Assisted-by: Claude:claude-opus-5-5
40aebcf to
d279232
Compare
…idiom, the shared zero check, and resume docs that say what a caller sees From the review of d279232: - BLAKE2's `from_suspended` compared `h` to the unkeyed initial state through `h_ct_eq`, which re-serialized both into two Secret buffers; it now serializes the public constant once and compares it against the state's own h bytes with `ct_eq_bytes`, as BLAKE3's resume compares its IV. `h_ct_eq` goes from both files. - `BLAKE3KeyedHashKey`'s strength check returned through `Err(..)?` beside an explicit `return Err(MACError::KeyMaterialError(..))`; it is spelled out like its neighbours. - The CLI's `warn_if_zeroized_key` hand-rolled an OR fold the optimiser may shorten; it uses `bouncycastle_utils::ct::ct_eq_zero_bytes`, which is barrier-hardened and already the MAC commands' comparison. - The public `from_suspended` docs on `BLAKE2b_512`, `BLAKE2s_256` and `BLAKE3Squeezer` listed the internal state combinations they refuse, which a caller cannot act on; they now say a state the type did not produce is `InvalidData`, and the checklist lives once on the private `read_state` and in the body's comments (QUALITY_AND_STYLE.md, "No internal implementation detail in public API docs"). Assisted-by: Claude:claude-mythos-5-1
…nerate Five `FromAnyKeyMaterial::generate` impls (HMAC, KMAC, BLAKE2b, BLAKE2s, BLAKE3) carried the same twelve-line hazardous-operations closure: draw `len` bytes into a longer buffer, set the length, tag it `MACKey`, cap the strength at the lesser of the RNG's and the length's. `KeyMaterial::from_rng` in core did the same draw with `CryptographicRandom` hard-coded. core gains `KeyMaterial::from_rng_as_type(rng, len, key_type)`: `len` bytes at most `KEY_LEN` (else `InputDataLongerThanKeyCapacity`), the caller's type, SP 800-90A r1 Section 8.4's `min(output_length, DRBG_security_strength)`, and a zero `len` back `Zeroized` without touching the RNG. `from_rng` is that call with `KEY_LEN` and `CryptographicRandom`, and each `generate` is one line over it. Found by the review of d279232 (PR bcgit#180). A core test covers the shorter typed draw, the cap, the zero draw and the capacity refusal; the MAC crates' own generate tests are unchanged. Assisted-by: Claude:claude-mythos-5-1
`load_blake_mac_key` was a third loader beside `mac_cmd::load_mac_key` and the block ciphers' `load_key_object_typed`, and the two MAC loaders tagged the same bytes differently: HMAC and KMAC keys went through a hazardous `set_key_type` on the secret buffer with no stated strength, BLAKE keys through a fresh `KeyMaterial512::from_bytes_as_type`, which gave them a length-derived strength and a second copy of the key. The only thing BLAKE needed was a length range with a message naming the limit. `helpers::load_mac_key(key, key_file, len)` is now the one loader: `len` is `Some((alg_name, range))` for an algorithm that bounds its key (BLAKE2b 1 to 64, BLAKE2s 1 to 32, BLAKE3 exactly 32) and `None` for HMAC and KMAC, which take any length the buffer holds; the tagging is the hazardous `set_key_type` for all of them, and the BLAKE commands build their keys allow-weak as they already did. The block ciphers' loader stays: it builds a typed fixed-length key object, a different shape. From the review of d279232 (item 3). Assisted-by: Claude:claude-mythos-5-1
|
Third review, at d279232, by the project's code-review pass at high effort. The reviewer checked out the head in a scratch worktree: blake2, blake3 and factory tests pass with no warnings, the criterion and stack benches compile, the mem-bench doctests pass, and the CLI reproduces RFC 7693 Appendix A and the official BLAKE3 Seven findings, on reuse, altitude and proportion. Six are fixed in the four commits above d279232 (5626d71, d3adf8e, c5b23f6, 667ced1); the seventh is the open question in the body.
Verified after the four commits (the fourth is docs and comments only): core, hmac, sha3, blake2, blake3, factory and core-test-framework suites; Assisted-by: Claude:claude-mythos-5-1 |
…rs to its checks The rest of the review's item 7 (d279232): `SUSPENDED_BLAKE2B_512_STATE_LEN` and `SUSPENDED_BLAKE2S_256_STATE_LEN` carried the serialized layout in their public docs, internal detail a caller cannot use; it is a `//` comment on the constant now, as the BLAKE3 constants already have it. `read_state`'s doc re-listed the rejections its body justifies check by check; it now says what the function does and leaves the why to the comments at each check, so the list has one home per file. Assisted-by: Claude:claude-mythos-5-1
dghgit
left a comment
There was a problem hiding this comment.
Rebased and reviewed with Mythos. A handful of minor issues fixed as described.
…E2s-256 (RFC 7693) and BLAKE3 (#123, for OpenSSL provider provisioning) as two new crates: bouncycastle-blake2 (BLAKE2b_512 and BLAKE2s_256 implementing Hash and Suspendable with G, F, the parameter block and the final-t rule commented against RFC 7693 Sec 3.1 to 3.3, the native keyed mode through the key objects BLAKE2b_512_MACKey and BLAKE2s_256_MACKey handing out BLAKE2b_512_MAC<'k, Dir> and BLAKE2s_256_MAC<'k, Dir> workers at the full 64 and 32-byte tag, key checks in the order type, strength, length, the id-blake2b512 and id-blake2s256 OIDs, suspended states of 212 and 108 bytes whose resume refuses an h that is not the unkeyed initial state at t == 0, a t that is not whole blocks or lies in the upper half of its range, and an empty buffer after a compression) and bouncycastle-blake3 (BLAKE3 implementing Hash and XOF with BLAKE3Squeezer streaming the extended output, both Suspendable with states of 1874 and 116 bytes, the hash resume refusing any chunk-state and CV-stack combination no input produces and the squeezer resume accepting only a root a tree can have, refusing an output block counter above 2^58 and recomputing the partly read block rather than storing it; BLAKE3DeriveKey and its squeezer as their own types that deliberately do not suspend, pinned by compile_fail doctests; BLAKE3KeyedHashKey handing out BLAKE3KeyedHash<'k, Dir> workers reading the key words through the borrow, with an inherent into_squeezer for extended keyed output; the engine following the reference implementation's chunk state and 54-entry chaining-value stack, with the chaining values and compression outputs outside the compression function in Secrets); in both crates the MAC workers follow #194 (Dir = Encrypting implements MACGenerator<N>, Dir = Decrypting MACVerifier<N>, no run-time truncation, verification a Result recomputing the tag into a Secret), borrow their key and implement neither Suspendable nor Clone, every _out function zeroizes its buffer before any error check, partial-bit input of 1 to 7 bits is InvalidInput, and the Memory Usage sections give the object sizes, state lengths and measured peak stack per operation; in core, KeyMaterial::from_rng_as_type(rng, len, key_type) is the one key draw that from_rng and the HMAC, KMAC and BLAKE generate methods wrap; in factory, HashFactory gains BLAKE2b-512, BLAKE2s-256 and BLAKE3, XOFFactory gains BLAKE3, and MACFactory and MACFactoryWorker<'k, Dir> gain the three BLAKE MAC keys and workers with their tag lengths through TagLen, which makes HashFactory and XOFFactory 1888 bytes and MACFactoryWorker 1872 because BLAKE3's hasher carries a 1728-byte chaining-value stack sized for the full 2^64-byte input range, documented in the factory crate's Memory Usage section and left as it stands; in the CLI, blake2b-512, blake2s-256, blake2b-512-mac, blake2s-256-mac, blake3, blake3-keyed-hash and blake3-derive-key, all streaming, with helpers::load_mac_key(key, key_file, len) the one MAC key loader (the length bound a parameter, None for HMAC and KMAC, --key-file winning everywhere) and the all-zero key warning through bouncycastle_utils::ct::ct_eq_zero_bytes, which also makes the HMAC/KMAC warning fire where it never had; the stack-memory harnesses; and the tests: RFC 7693 Appendix A/B, the official BLAKE2 KAT selections and the bc-test-data Blake2KAT.txt sweep, all 35 official BLAKE3 vectors in all three modes, the core-test-framework suites for Hash, XOF, MAC keys, key construction and suspend/resume, and cli/tests/test_blake.sh with 17 cases. Reviewed in three rounds (9059e06, 40aebcf, d279232: the interchangeable keyed states, the derive_key state holding the IKM, the squeezer counter wrap, the t and buffer bounds, the non-constant-time resume compares, the Clone keyed workers, the stored squeezer buffer, the impossible root flags, the unzeroized recomputed tags, the three MAC key loaders and the repeated spec quotes, all fixed); mutants on apollo in the PR: blake2 310 (230 caught, 70 unviable, 10 missed, the noted equivalents), blake3 417 (338 caught, 61 unviable, 9 missed as noted, 9 timeouts), factory mac_factory.rs 55 (25 caught, 30 unviable, 0 missed), core from_rng / from_rng_as_type 10 (6 caught, 4 unviable, 0 missed). Assisted-by: Claude:claude-mythos-5-1 Assisted-by: Claude:claude-fable-5-1 Assisted-by: Claude:claude-opus-5-5 Assisted-by: Claude:claude-sonnet-5-5
…nd BLAKE3) into feature/eccdh Two conflicts, both lists: the factory's dependency table (BLAKE2 and BLAKE3 beside concatkdf) and the CLI's use list (BLAKE2Variant beside ConcatKDFVariant and ECCDHAction), each keeping both sides. The factory, cli, eccdh, x963kdf and concatkdf suites, the eccdh, x963kdf, concatkdf, hkdf and blake CLI shell suites, the all-targets build and rustdoc pass on the result.
…ment and ECC CDH with the X9.63 and concatenation KDFs; PR #180 squash: BLAKE2b-512, BLAKE2s-256 and BLAKE3; the post-#193 cleanups) into feature/tdes Two keep-both conflicts: the mem_usage_benches manifest (the tdes stack bench beside the BLAKE2 and BLAKE3 ones) and the umbrella crate's re-exports (tdes beside x963kdf). One adaptation: the CLI's main thread stack goes from 8 MiB to 32 MiB. In a debug build the startup frame for the full subcommand set already sat close to 8 MiB on the release, and the eleven tdes-* / tdes2-* commands take it past, so the debug binary aborted on every invocation, --help included (the release-profile binary was unaffected). That bound is the release's to set once; it is raised here so the branch's shell suites run. The tdes, cipher, factory and cli suites, the tdes, blake, eccdh, concatkdf, x963kdf and hkdf CLI shell suites and the all-targets build pass.
…ment and ECC CDH with the X9.63 and concatenation KDFs; PR #180 squash: BLAKE2b-512, BLAKE2s-256 and BLAKE3; the post-#193 cleanups) into feature/rsa-sig No conflicts. One adaptation, as on feature/tdes: the CLI's main thread stack goes from 8 MiB to 32 MiB, since in a debug build the startup frame for the full subcommand set passes 8 MiB once the rsa commands join the release's and the debug binary aborted on every invocation (the release-profile binary was unaffected). That bound is the release's to set once. The rsa and cli suites, the blake and eccdh CLI shell suites and the all-targets build pass.
…of PR #194, the BLAKE hashes of PR #180, two-party key agreement and ECC CDH with the X9.63 and concatenation KDFs of PR #193, and the post-#193 cleanups) into feature/simple-ciphers-arm Two keep-both conflicts: the workspace dependency table (this branch's `default-features = false` on bouncycastle-cipher beside the release's concatkdf entry) and the imports of the AES CMAC key tests (this branch's AES*Params beside the release's Encrypting). The aes, cipher, asm and cli suites, the aes-gcm, aes-cbc and blake CLI shell suites pass, and the aarch64 library type-checks in the default, declared-feature (+aes,+neon) and asm-off configurations.
Adds the three BLAKE hashes requested in #123 for OpenSSL provider provisioning: two new crates, wired into the factories, the CLI and the stack-memory benches. Base: release/0.1.4alpha, rebased onto 6144224 (the #194 MAC rework). Other BLAKE2 digest lengths, salt/personalisation, BLAKE2bp/sp/X and tree mode are out of scope.
blake2 (new crate bouncycastle-blake2):
blake3 (new crate bouncycastle-blake3):
Both crates:
factory: HashFactory gains BLAKE2b-512, BLAKE2s-256 and BLAKE3; XOFFactory gains BLAKE3; MACFactory and MACFactoryWorker<'k, Dir> gain the three BLAKE MAC keys and workers, with their tag lengths through TagLen.
Open question: the factory enums' size. HashFactory and XOFFactory are 1888 bytes and MACFactoryWorker 1872, because BLAKE3's hasher carries a 1728-byte chaining-value stack sized for the full 2^64-byte input range (spec s. 5.4). Without BLAKE3 the largest variant is a SHA-3 or KMAC state of at most 440 bytes. An enum is as large as its largest variant, so every HashFactory / XOFFactory user pays this on its stack whichever algorithm it selects, and every move copies all of it. The factory crate's Memory Usage section documents it; nothing is changed yet. Options:
cli: blake2b-512, blake2s-256, blake2b-512-mac, blake2s-256-mac, blake3, blake3-keyed-hash and blake3-derive-key, all streaming (blake3-keyed-hash streams stdin in and the squeezed output out). cli/tests/test_blake.sh adds 17 tests on the shell harness. The BLAKE key loader reads --key / --key-file through the same load_secret as the HMAC/KMAC loader, so --key-file wins everywhere, as the help text says (test_hmac.sh pins this). It adds a per-algorithm length check with a message naming the limit. Both loaders warn on an all-zero key through one helper. That also fixes the HMAC/KMAC warning, which never fired: it tested for KeyType::Zeroized, and loaded keys are tagged Unknown.
tests: RFC 7693 Appendix A/B, selections from the official BLAKE2 KAT, the bc-test-data Blake2KAT.txt sweep (512 + 512 records), all 35 official BLAKE3 vectors in all three modes, and the core-test-framework suites for Hash, XOF, MAC keys, key construction and suspend/resume. Wycheproof has no BLAKE vectors. cargo mutants (apollo, 5014bcc; the later commits add mutants notes and docs only): blake2 326 tested, 244 caught, 70 unviable, 12 missed, all equivalent and noted at the site (two of them on the new t bound); blake3 422 tested, 342 caught, 61 unviable, 10 missed, 9 timeouts (mutants that make a fill or merge loop never end), every miss an equivalent noted at the site (one new, on the squeezer's buffer recompute); factory mac_factory.rs 55 tested, 25 caught, 30 unviable, 0 missed.
#![no_std] remains a todo in both crates, as in every hash crate, blocked by the Vec return types in core::traits::Hash.
Rebase onto 6144224: the six commits were replayed (the earlier merge commits of release are gone), with mac_factory.rs and its tests kept at the release's version during the replay, so the replayed commits in between do not build; the tip does. The port to the #194 traits is its own commit, followed by the review fixes below.
Review of 9059e06 (findings 1-9):
Review of 40aebcf (2026-11-10), all fixed:
Also from that review: the workers borrow their key, and public docs no longer carry internal detail (state layouts, other implementations' conventions).
Review of d279232 (2026-11-10, seven findings, no correctness bug):
generaterepeated HMAC's and KMAC's draw-and-cap closure: fixed in core,KeyMaterial::from_rng_as_type(rng, len, key_type)is the one draw (from_rngwraps it), and the fivegenerates are a line each.warn_if_zeroized_keyhand-rolled the all-zero check: fixed, it usesbouncycastle_utils::ct::ct_eq_zero_bytes.load_blake_mac_keywas a third MAC-key loader besideload_mac_keyandload_key_object_typed, tagging the same bytes differently: fixed,helpers::load_mac_key(key, key_file, len)is the one MAC loader, with the length bound a parameter (Nonefor HMAC and KMAC); the block ciphers' typed loader stays, being a different shape.h_ct_eqre-serialized both sides to comparehwith the initial state: fixed,from_suspendedcompares the state's own h bytes with the serialized constant, as BLAKE3 does, andh_ct_eqis gone.Err(..)?besidereturn Err(..)inBLAKE3KeyedHashKey's checks: fixed.from_suspendeddocs listed internal state combinations: fixed, they say a state the type did not produce isInvalidData; the checklist lives on the privateread_state(BLAKE3) or at the checks themselves (BLAKE2, whoseread_statedoc defers to them), and the BLAKE2 state-length constants carry their layout as a code comment, as BLAKE3's do, not as public docs.cargo mutants after the three commits (apollo): blake2 310 (230 caught, 70 unviable, 10 missed, the noted equivalents), blake3 417 (338 caught, 61 unviable, 9 missed, 9 timeouts, as noted), core
from_rng/from_rng_as_typewith the core, hmac and blake2 tests 10 (6 caught, 4 unviable, 0 missed).Assisted-by: Claude:claude-mythos-5-1
Assisted-by: Claude:claude-fable-5-1
Assisted-by: Claude:claude-opus-5-5