crypto/sha2: publish hash and block lengths in bytes - #1606
Conversation
Working notes; the PR title and description are the commit message. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016HvbYkBMYWwQECL7myLhqs
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016HvbYkBMYWwQECL7myLhqs
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
functionalscript | a9635bc | Commit Preview URL Branch Preview URL |
Aug 16 2026, 03:47 AM |
o2alexanderfedin
left a comment
There was a problem hiding this comment.
Checked the rounding premise at runtime rather than assuming it, since divUp8 and >> 3n diverge for any bit length that isn't a multiple of 8. All six variants have both lengths ≡ 0 mod 8 — sha224 224/512, sha256 256/512, sha384 384/1024, sha512 512/1024, sha512x224 224/1024, sha512x256 256/1024 → hashBytes 28/32/48/64/28/32, blockBytes 64/64/128/128/128/128 — each matching >> 3n. The PR's table is accurate, including that there are six variants rather than the four the todo claimed.
Differential: 456/456 rows byte-identical against origin/main. 300 HMAC digests (6 variants × 10 key byte-lengths 0/1/32/63/64/65/127/128/129/200, straddling both the 64- and 128-byte block boundaries and the length(k) > blockLength branch, × 5 message lengths), 120 sign signatures (5 curves incl. secp521r1 × 6 hashes × 4 messages), 30 computeK nonces. RFC 4231 case 1 reproduces exactly for SHA-256 (b0344c61…2e32cff7) and SHA-512 (87aa7cde…3a126854), so the harness is comparing real output rather than two matched failures.
No half-migration: both fields derive in sha2(...) from the same inputs the old sites converted, and grep over fjs/crypto/ finds no remaining >> 3n / divUp8 / / 8n. hmac's residual blockLength uses are genuine bit quantities.
Proofs discriminate: byteLengths asserts against hand-written 28/32/48/64/64/128, and mutations hashBytes ±1n, blockBytes ±1n, and hashBytes: divUp8(chunkLength) each fail all six.
npx tsc exit 0; 2910 pass / 0 fail; npm run cov exit 0 at 100/100/100 under the new #1605 gate; ci-update leaves the tree clean.
Implements
fjs/crypto/sha2/todo/byte-length-fields.md(deleted here).Why
Sha2published only bit counts, so every consumer needing bytes re-did the conversion — in two different spellings:Whether the two roundings agree was left for the reader to work out per site.
What
sha2(...)computes both once and the type publishes them:hmacreadsblockBytes,signreadshashBytes, anddivUp8drops out ofsign's imports entirely.divUp8is the spelling kept for both: it agrees with>> 3non every SHA-2 variant, and it is the rounding RFC 6979 actually specifies (ceil(hlen/8)), so a hypothetical non-byte-multiplehashLength—sha2takes it as a parameter — stays correct rather than silently truncating.Verification
I checked the "the two roundings agree" premise rather than assuming it, across every variant:
All equal
>> 3n. (The TODO said "all four variants" — there are six.)npx tscclean;fjs test2910 pass, 0 fail;npm run cov100% aggregate, which Fail npm run cov if aggregate coverage falls under 100% #1605 now gates on.byteLengthsproof covers all six, asserting each byte count against both its expected value and the bit length it derives from, so the identity is pinned rather than the numbers alone.hmac, the consumer whose changed value feeds a branch (length(k) > blockLength): 6 variants × 10 key lengths straddling both block sizes (0, 1, 32, 63, 64, 65, 127, 128, 129, 200 bytes) × 5 message lengths = 300 digests, identical.sign/computeK: 4 curves × 6 hashes × 4 messages = 96 signatures, identical.One note on method: my first
hmacdifferential script was wrong — it passed a list where aVecwas expected and crashed on both sides, socmpreported "identical" on two equal stack traces. I caught it, fixed the call, and added a row-count and non-empty-digest guard to both scripts so a silent failure cannot masquerade as a match again. The numbers above are from the corrected runs.Changelog:
crypto/sha2:Sha2publisheshashBytesandblockBytes, so consumers sizing byte buffers read them instead of converting bit lengths themselves🤖 Generated with Claude Code
https://claude.ai/code/session_016HvbYkBMYWwQECL7myLhqs
Generated by Claude Code