# M2a task 01: SHA-256 in `proto` **Branch:** `m2a` (create it from `master`: `git switch master && git switch -c m2a`) **Commit subject:** `Add SHA-256 to proto, wrapping the emsha crate` ## Goal Give the workspace one SHA-256 function. It wraps the `emsha` crate, which the project's owner wrote. Nothing else in the workspace may use `emsha` directly, so that the choice of implementation stays in one file. ## Context The hash is used to check that the inference server runs the chat template we expect, and later for the audit log. An earlier release of `emsha` hashed every message of 63, 127, 191, … bytes wrongly, because of a mistake at a padding boundary. That is fixed in 1.0.4, and it is why the test file checks lengths on both sides of every boundary and feeds each message in two pieces. ## Files - Copy: `crates/proto/tests/hash.rs` - Create: `crates/proto/src/hash.rs` - Modify: `Cargo.toml`, `crates/proto/Cargo.toml`, `crates/proto/src/lib.rs`, `docs/dependencies.md`, `docs/implementer-log.md`, `Cargo.lock` (generated) ## Interfaces Consumes: `proto::Hash32` (`Hash32::from_bytes([u8; 32])`). Produces, in `crates/proto/src/hash.rs`, re-exported from the crate root: ```rust pub struct HashError; // Debug, Clone, Copy, PartialEq, Eq; Display; std::error::Error pub struct Sha256(/* private */); impl Sha256 { pub fn new() -> Self; pub fn update(&mut self, data: &[u8]) -> Result<(), HashError>; // may be called many times pub fn finish(self) -> Result; } impl Default for Sha256 { /* same as new() */ } pub fn sha256(data: &[u8]) -> Result; // new, update, finish ``` Every `emsha` error becomes `HashError`. No `unwrap` or `expect`. ## API notes (`emsha` 1.0.4, read from its source on 2026-09-17) ```rust use emsha::Hash; // the trait that has update and finalize let mut h = emsha::sha256::SHA256::new(); h.update(b"bytes")?; // fn update(&mut self, msg: &[u8]) -> emsha::Result<()> let mut out = [0u8; emsha::sha256::SIZE]; // SIZE is 32 h.finalize(&mut out)?; // fn finalize(&mut self, digest: &mut [u8]) -> emsha::Result<()> ``` `emsha::Result` is `Result`. The crate has no dependencies and is `no_std`. ## Steps - [ ] **1. Branch, copy the test, add the dependency.** ```sh git switch master && git switch -c m2a cp docs/plans/M2a/files/crates/proto/tests/hash.rs crates/proto/tests/ ``` Add to `[workspace.dependencies]` in the root `Cargo.toml`: `emsha = "1.0.4"`. Add to `[dependencies]` in `crates/proto/Cargo.toml`: `emsha.workspace = true`. Add this row to `docs/dependencies.md`: ```markdown | `emsha` | 1.0.4 | `proto` | SHA-256. Written by the owner; no dependencies, no `unsafe`. Checked against `sha256sum` on 3,204 inputs. | ``` - [ ] **2. See the test fail.** `cargo test -p proto --test hash`. Expected: it does not compile. - [ ] **3. Write `hash.rs`,** and in `lib.rs` add `pub mod hash;` and `pub use hash::{HashError, Sha256, sha256};`. Run `cargo fmt --all`. - [ ] **4. See the test pass.** `cargo test -p proto --test hash`. Expected: `4 passed`. - [ ] **5. Run the gate.** `cargo build`, then `make gate`. Expected last line: `gate: ok`. `cargo deny` prints a `no-license-field` warning for `emsha`; that is expected, because the crate's licence is a file and not an SPDX name. Do not edit `deny.toml`. - [ ] **6. Log and commit.** ```sh git add Cargo.toml Cargo.lock crates/proto docs/dependencies.md docs/implementer-log.md git commit ``` ## Done when - `cargo test -p proto --test hash` reports 4 passed; `make gate` prints `gate: ok`. - `grep -rn emsha crates/*/src` shows `crates/proto/src/hash.rs` only. ## Stop and report if - A vector in `hash.rs` fails. Do not change the vector: it would mean `emsha` is wrong again. - `cargo deny` reports an error, not a warning, for `emsha`.