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>
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
# 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)
|
||||
|
||||
```rust
|
||||
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 `a`s, 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.
|
||||
Reference in New Issue
Block a user