diff --git a/src/sha256.rs b/src/sha256.rs index d9122a9..c01c68f 100644 --- a/src/sha256.rs +++ b/src/sha256.rs @@ -209,6 +209,18 @@ macro_rules! round( ); impl HashEngine { + /// Create a new [HashEngine] from a midstate. + pub fn from_midstate(midstate: Midstate, length: usize) -> HashEngine { + let mut ret = [0; 8]; + BigEndian::read_u32_into(&midstate[..], &mut ret); + + HashEngine { + buffer: [0; BLOCK_SIZE], + h: ret, + length: length, + } + } + // Algorithm copied from libsecp256k1 fn process_block(&mut self) { debug_assert_eq!(self.buffer.len(), BLOCK_SIZE); @@ -399,6 +411,55 @@ mod tests { ); } + #[test] + fn engine_with_state() { + let mut engine = sha256::Hash::engine(); + let midstate_engine = sha256::HashEngine::from_midstate(engine.midstate(), 0); + // Fresh engine and engine initialized with fresh state should have same state + assert_eq!(engine.h, midstate_engine.h); + + // Midstate changes after writing 64 bytes + engine.input(&[1; 63]); + assert_eq!(engine.h, midstate_engine.h); + engine.input(&[2; 1]); + assert_ne!(engine.h, midstate_engine.h); + + // Initializing an engine with midstate from another engine should result in + // both engines producing the same hashes + let data_vec = vec![vec![3; 1], vec![4; 63], vec![5; 65], vec![6; 66]]; + for data in data_vec { + let mut engine = engine.clone(); + let mut midstate_engine = + sha256::HashEngine::from_midstate(engine.midstate(), engine.length); + assert_eq!(engine.h, midstate_engine.h); + assert_eq!(engine.length, midstate_engine.length); + engine.input(&data); + midstate_engine.input(&data); + assert_eq!(engine.h, midstate_engine.h); + let hash1 = sha256::Hash::from_engine(engine); + let hash2 = sha256::Hash::from_engine(midstate_engine); + assert_eq!(hash1, hash2); + } + + // Test that a specific midstate results in a specific hash. Midstate was + // obtained by applying sha256 to sha256("MuSig coefficient")||sha256("MuSig + // coefficient"). + static MIDSTATE: [u8; 32] = [ + 0x0f, 0xd0, 0x69, 0x0c, 0xfe, 0xfe, 0xae, 0x97, 0x99, 0x6e, 0xac, 0x7f, 0x5c, 0x30, + 0xd8, 0x64, 0x8c, 0x4a, 0x05, 0x73, 0xac, 0xa1, 0xa2, 0x2f, 0x6f, 0x43, 0xb8, 0x01, + 0x85, 0xce, 0x27, 0xcd, + ]; + static HASH_EXPECTED: [u8; 32] = [ + 0x18, 0x84, 0xe4, 0x72, 0x40, 0x4e, 0xf4, 0x5a, 0xb4, 0x9c, 0x4e, 0xa4, 0x9a, 0xe6, + 0x23, 0xa8, 0x88, 0x52, 0x7f, 0x7d, 0x8a, 0x06, 0x94, 0x20, 0x8f, 0xf1, 0xf7, 0xa9, + 0xd5, 0x69, 0x09, 0x59, + ]; + let midstate_engine = + sha256::HashEngine::from_midstate(sha256::Midstate::from_inner(MIDSTATE), 64); + let hash = sha256::Hash::from_engine(midstate_engine); + assert_eq!(hash, sha256::Hash(HASH_EXPECTED)); + } + #[cfg(feature="serde")] #[test] fn sha256_serde() {