Add SHA-256 to proto, wrapping the emsha crate

Implemented-By: Laguna S 2.1 (OpenCode)
This commit is contained in:
2026-09-17 14:11:51 -07:00
parent db0ba081db
commit bc1e727c19
8 changed files with 156 additions and 0 deletions
+1
View File
@@ -12,6 +12,7 @@ workspace = true
serde.workspace = true
serde_json.workspace = true
humantime.workspace = true
emsha.workspace = true
[dev-dependencies]
toml.workspace = true
+43
View File
@@ -0,0 +1,43 @@
use crate::Hash32;
use emsha::Hash as _;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct HashError;
impl std::fmt::Display for HashError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "sha256 error")
}
}
impl std::error::Error for HashError {}
pub struct Sha256(emsha::sha256::SHA256);
impl Sha256 {
pub fn new() -> Self {
Sha256(emsha::sha256::SHA256::new())
}
pub fn update(&mut self, data: &[u8]) -> Result<(), HashError> {
self.0.update(data).map_err(|_| HashError)
}
pub fn finish(mut self) -> Result<Hash32, HashError> {
let mut out = [0u8; emsha::sha256::SIZE];
self.0.finalize(&mut out).map_err(|_| HashError)?;
Ok(Hash32::from_bytes(out))
}
}
impl Default for Sha256 {
fn default() -> Self {
Self::new()
}
}
pub fn sha256(data: &[u8]) -> Result<Hash32, HashError> {
let mut h = Sha256::new();
h.update(data)?;
h.finish()
}
+2
View File
@@ -4,6 +4,7 @@ pub mod audit;
pub mod class;
pub mod frame;
pub mod grant;
pub mod hash;
pub mod ids;
pub mod log;
pub mod wire;
@@ -12,6 +13,7 @@ pub use audit::{AuditRecord, DecisionRecord};
pub use class::DataClass;
pub use frame::{FrameError, MAX_FRAME, read_frame, write_frame};
pub use grant::{Constraints, Grant, Mode};
pub use hash::{HashError, Sha256, sha256};
pub use ids::{CallId, Epoch, Hash32, SessionId, Timestamp, ValueError};
pub use log::{LogRecord, ToolCall};
pub use wire::{
+100
View File
@@ -0,0 +1,100 @@
//! SHA-256 vectors. Do not edit: these define the required behaviour.
//!
//! The lengths 55, 56, 63, 64 and 65 sit on either side of the padding boundaries of SHA-256,
//! which is where implementations go wrong. Expected values come from `sha256sum`.
use proto::{Sha256, sha256};
const VECTORS: &[(usize, &str)] = &[
(
0,
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
),
(
1,
"ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb",
),
(
55,
"9f4390f8d30c2dd92ec9f095b65e2b9ae9b0a925a5258e241c9f1e910f734318",
),
(
56,
"b35439a4ac6f0948b6d6f9e3c6af0f5f590ce20f1bde7090ef7970686ec6738a",
),
(
63,
"7d3e74a05d7db15bce4ad9ec0658ea98e3f06eeecf16b4c6fff2da457ddc2f34",
),
(
64,
"ffe054fe7ae0cb6dc65c3af9b61d5209f439851db43d0ba5997337df154668eb",
),
(
65,
"635361c48bb9eab14198e76ea8ab7f1a41685d6ad62aa9146d301d4f17eb0ae0",
),
(
119,
"31eba51c313a5c08226adf18d4a359cfdfd8d2e816b13f4af952f7ea6584dcfb",
),
(
127,
"c57e9278af78fa3cab38667bef4ce29d783787a2f731d4e12200270f0c32320a",
),
(
128,
"6836cf13bac400e9105071cd6af47084dfacad4e5e302c94bfed24e013afb73e",
),
(
1000,
"41edece42d63e8d9bf515a9ba6932e1c20cbc9f5a5d134645adb5db1b9737ea3",
),
];
/// `n` bytes of the letter `a`.
fn letters(n: usize) -> Vec<u8> {
vec![b'a'; n]
}
#[test]
fn abc() {
let want = "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad";
assert_eq!(sha256(b"abc").unwrap().to_hex(), want);
}
#[test]
fn lengths_around_the_padding_boundaries() {
for (n, want) in VECTORS {
assert_eq!(sha256(&letters(*n)).unwrap().to_hex(), *want, "{n} bytes");
}
}
#[test]
fn input_in_two_pieces_gives_the_same_hash() {
for (n, want) in VECTORS {
let data = letters(*n);
for split in [0, 1, 55, 56, 63, 64, 65, *n / 2, *n] {
let split = split.min(*n);
let mut h = Sha256::new();
h.update(&data[..split]).unwrap();
h.update(&data[split..]).unwrap();
assert_eq!(
h.finish().unwrap().to_hex(),
*want,
"{n} bytes split at {split}"
);
}
}
}
#[test]
fn one_million_letters() {
let want = "cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0";
assert_eq!(sha256(&letters(1_000_000)).unwrap().to_hex(), want);
let mut h = Sha256::default();
for _ in 0..1000 {
h.update(&letters(1000)).unwrap();
}
assert_eq!(h.finish().unwrap().to_hex(), want);
}