Files
boxmaker/docs/plans/M4a/01-proto-sha1.md
T
kyleandClaude Opus 5.5 0339dc13b2 Plan M4a: gatewayd in 15 tasks, with skeletons and given tests
Each task's tests were run against a reference at its end state; the end states were replayed
from master in order with the gate at each step (650 to 762 tests); each skeleton compiles
against its tests and fails them. The reference is kept off this machine. Lessons T27 (every
wait in a test has a limit) and T28 (mutate the reference before hand-over) come from this work.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-23 19:05:44 -07:00

2.7 KiB

M4a task 01: SHA-1 in proto

Branch: m4a (run git switch m4a; git status --short must be empty, otherwise stop) Commit subject: proto: sha1, for the WebSocket handshake check

Goal

gatewayd speaks WebSocket to Mattermost. The server proves it understood the handshake by sending back the SHA-1 of our key (RFC 6455, section 4.2.2), so we need SHA-1. It lives in proto, which has no dependencies of its own. It is used for that check and nothing else: it must never protect anything.

Files

  • Copy: crates/proto/tests/sha1.rs, and the skeleton crates/proto/src/sha1.rs
  • Modify: crates/proto/src/lib.rs (pub mod sha1;), docs/implementer-log.md

Interface (in the skeleton)

pub fn sha1(data: &[u8]) -> [u8; 20];          // written: new, update, finish

pub struct Sha1 { state: [u32; 5], block: [u8; 64], filled: usize, length: u64 }
impl Sha1 {
    pub fn new() -> Sha1;                        // written: the five initial words
    pub fn update(&mut self, data: &[u8]);       // todo
    pub fn finish(self) -> [u8; 20];             // todo
    fn compress(&mut self, block: &[u8; 64]);    // todo
}

length counts bits fed so far. Each todo!() has its steps above it. No indexing that can go out of bounds: w[i] on the fixed [u32; 80] inside compress is fine; slicing data or block by a computed range is not (use get, get_mut, split_at). Use as_chunks::<4>() to read big-endian words.

Steps

  • 1. Copy. git switch m4a, then cp docs/plans/M4a/files/crates/proto/tests/sha1.rs crates/proto/tests/ and cp docs/plans/M4a/files/crates/proto/src/sha1.rs crates/proto/src/. Add pub mod sha1; to crates/proto/src/lib.rs.
  • 2. See it fail. cargo test -p proto --test sha1. Expected: it compiles, and 4 tests fail with not yet implemented.
  • 3. Fill the three functions, one at a time: compress, then update, then finish. Run cargo check -p proto after each.
  • 4. See it pass. cargo test -p proto --test sha1. Expected: 4 passed. The tests include FIPS 180 and RFC 3174 vectors, a million as, the lengths around the 64-byte block where the padding changes shape (checked with sha1sum), and the RFC 6455 handshake example; every case is also fed in pieces.
  • 5. Run the gate. cargo fmt --all, then make gate. Expected last line: gate: ok.
  • 6. Log and commit. git add crates/proto docs/implementer-log.md Cargo.lock && git commit

Done when

  • cargo test -p proto --test sha1 passes; make gate prints gate: ok.

Stop and report if

  • A test vector fails and you cannot find why after checking compress against the comment twice.