Skip to content
This repository was archived by the owner on Nov 30, 2022. It is now read-only.

Commit be81881

Browse files
authored
Merge pull request #39 from stevenroose/sha256-midstate
Add sha256::Midstate type
2 parents 3ec1070 + be133da commit be81881

File tree

1 file changed

+129
-9
lines changed

1 file changed

+129
-9
lines changed

src/sha256.rs

Lines changed: 129 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
1717
use byteorder::{ByteOrder, BigEndian};
1818

19+
use hex;
1920
use HashEngine as EngineTrait;
2021
use Hash as HashTrait;
2122
use Error;
@@ -42,20 +43,20 @@ impl Clone for HashEngine {
4243
}
4344

4445
impl EngineTrait for HashEngine {
45-
type MidState = [u8; 32];
46+
type MidState = Midstate;
4647

4748
#[cfg(not(feature = "fuzztarget"))]
48-
fn midstate(&self) -> [u8; 32] {
49+
fn midstate(&self) -> Midstate {
4950
let mut ret = [0; 32];
5051
BigEndian::write_u32_into(&self.h, &mut ret);
51-
ret
52+
Midstate(ret)
5253
}
5354

5455
#[cfg(feature = "fuzztarget")]
55-
fn midstate(&self) -> [u8; 32] {
56+
fn midstate(&self) -> Midstate {
5657
let mut ret = [0; 32];
5758
ret.copy_from_slice(&self.buffer[..32]);
58-
ret
59+
Midstate(ret)
5960
}
6061

6162
const BLOCK_SIZE: usize = 64;
@@ -104,12 +105,12 @@ impl HashTrait for Hash {
104105
e.write_u64::<BigEndian>(8 * data_len).unwrap();
105106
debug_assert_eq!(e.length % BLOCK_SIZE, 0);
106107

107-
Hash(e.midstate())
108+
Hash(e.midstate().into_inner())
108109
}
109110

110111
#[cfg(feature = "fuzztarget")]
111112
fn from_engine(e: HashEngine) -> Hash {
112-
Hash(e.midstate())
113+
Hash(e.midstate().into_inner())
113114
}
114115

115116
const LEN: usize = 32;
@@ -133,6 +134,59 @@ impl HashTrait for Hash {
133134
}
134135
}
135136

137+
/// Output of the SHA256 hash function
138+
#[derive(Copy, Clone, PartialEq, Eq, Default, PartialOrd, Ord, Hash)]
139+
pub struct Midstate([u8; 32]);
140+
141+
hex_fmt_impl!(Debug, Midstate);
142+
hex_fmt_impl!(Display, Midstate);
143+
hex_fmt_impl!(LowerHex, Midstate);
144+
index_impl!(Midstate);
145+
serde_impl!(Midstate, 32);
146+
borrow_slice_impl!(Midstate);
147+
148+
impl Midstate {
149+
/// Length of the midstate, in bytes.
150+
const LEN: usize = 32;
151+
152+
/// Flag indicating whether user-visible serializations of this hash
153+
/// should be backward. For some reason Satoshi decided this should be
154+
/// true for `Sha256dHash`, so here we are.
155+
const DISPLAY_BACKWARD: bool = true;
156+
157+
/// Construct a new midstate from the inner value.
158+
pub fn from_inner(inner: [u8; 32]) -> Self {
159+
Midstate(inner)
160+
}
161+
162+
/// Copies a byte slice into the [Midstate] object.
163+
pub fn from_slice(sl: &[u8]) -> Result<Midstate, Error> {
164+
if sl.len() != Self::LEN {
165+
Err(Error::InvalidLength(Self::LEN, sl.len()))
166+
} else {
167+
let mut ret = [0; 32];
168+
ret.copy_from_slice(sl);
169+
Ok(Midstate(ret))
170+
}
171+
}
172+
173+
/// Unwraps the [Midstate] and returns the underlying byte array.
174+
pub fn into_inner(self) -> [u8; 32] {
175+
self.0
176+
}
177+
}
178+
179+
impl hex::FromHex for Midstate {
180+
fn from_byte_iter<I>(iter: I) -> Result<Self, Error>
181+
where I: Iterator<Item=Result<u8, Error>> +
182+
ExactSizeIterator +
183+
DoubleEndedIterator,
184+
{
185+
// DISPLAY_BACKWARD is true
186+
Ok(Midstate::from_inner(hex::FromHex::from_byte_iter(iter.rev())?))
187+
}
188+
}
189+
136190
macro_rules! Ch( ($x:expr, $y:expr, $z:expr) => ($z ^ ($x & ($y ^ $z))) );
137191
macro_rules! Maj( ($x:expr, $y:expr, $z:expr) => (($x & $y) | ($z & ($x | $y))) );
138192
macro_rules! Sigma0( ($x:expr) => (circular_lshift32!(30, $x) ^ circular_lshift32!(19, $x) ^ circular_lshift32!(10, $x)) ); macro_rules! Sigma1( ($x:expr) => (circular_lshift32!(26, $x) ^ circular_lshift32!(21, $x) ^ circular_lshift32!(7, $x)) );
@@ -155,6 +209,23 @@ macro_rules! round(
155209
);
156210

157211
impl HashEngine {
212+
/// Create a new [HashEngine] from a midstate.
213+
///
214+
/// Be aware that this method panics when [length] is
215+
/// not a multiple of the block size.
216+
pub fn from_midstate(midstate: Midstate, length: usize) -> HashEngine {
217+
assert!(length % BLOCK_SIZE == 0, "length is no multiple of the block size");
218+
219+
let mut ret = [0; 8];
220+
BigEndian::read_u32_into(&midstate[..], &mut ret);
221+
222+
HashEngine {
223+
buffer: [0; BLOCK_SIZE],
224+
h: ret,
225+
length: length,
226+
}
227+
}
228+
158229
// Algorithm copied from libsecp256k1
159230
fn process_block(&mut self) {
160231
debug_assert_eq!(self.buffer.len(), BLOCK_SIZE);
@@ -336,15 +407,64 @@ mod tests {
336407
assert_eq!(
337408
engine.midstate(),
338409
// RPC output
339-
[
410+
sha256::Midstate::from_inner([
340411
0x0b, 0xcf, 0xe0, 0xe5, 0x4e, 0x6c, 0xc7, 0xd3,
341412
0x4f, 0x4f, 0x7c, 0x1d, 0xf0, 0xb0, 0xf5, 0x03,
342413
0xf2, 0xf7, 0x12, 0x91, 0x2a, 0x06, 0x05, 0xb4,
343414
0x14, 0xed, 0x33, 0x7f, 0x7f, 0x03, 0x2e, 0x03,
344-
]
415+
])
345416
);
346417
}
347418

419+
#[test]
420+
fn engine_with_state() {
421+
let mut engine = sha256::Hash::engine();
422+
let midstate_engine = sha256::HashEngine::from_midstate(engine.midstate(), 0);
423+
// Fresh engine and engine initialized with fresh state should have same state
424+
assert_eq!(engine.h, midstate_engine.h);
425+
426+
// Midstate changes after writing 64 bytes
427+
engine.input(&[1; 63]);
428+
assert_eq!(engine.h, midstate_engine.h);
429+
engine.input(&[2; 1]);
430+
assert_ne!(engine.h, midstate_engine.h);
431+
432+
// Initializing an engine with midstate from another engine should result in
433+
// both engines producing the same hashes
434+
let data_vec = vec![vec![3; 1], vec![4; 63], vec![5; 65], vec![6; 66]];
435+
for data in data_vec {
436+
let mut engine = engine.clone();
437+
let mut midstate_engine =
438+
sha256::HashEngine::from_midstate(engine.midstate(), engine.length);
439+
assert_eq!(engine.h, midstate_engine.h);
440+
assert_eq!(engine.length, midstate_engine.length);
441+
engine.input(&data);
442+
midstate_engine.input(&data);
443+
assert_eq!(engine.h, midstate_engine.h);
444+
let hash1 = sha256::Hash::from_engine(engine);
445+
let hash2 = sha256::Hash::from_engine(midstate_engine);
446+
assert_eq!(hash1, hash2);
447+
}
448+
449+
// Test that a specific midstate results in a specific hash. Midstate was
450+
// obtained by applying sha256 to sha256("MuSig coefficient")||sha256("MuSig
451+
// coefficient").
452+
static MIDSTATE: [u8; 32] = [
453+
0x0f, 0xd0, 0x69, 0x0c, 0xfe, 0xfe, 0xae, 0x97, 0x99, 0x6e, 0xac, 0x7f, 0x5c, 0x30,
454+
0xd8, 0x64, 0x8c, 0x4a, 0x05, 0x73, 0xac, 0xa1, 0xa2, 0x2f, 0x6f, 0x43, 0xb8, 0x01,
455+
0x85, 0xce, 0x27, 0xcd,
456+
];
457+
static HASH_EXPECTED: [u8; 32] = [
458+
0x18, 0x84, 0xe4, 0x72, 0x40, 0x4e, 0xf4, 0x5a, 0xb4, 0x9c, 0x4e, 0xa4, 0x9a, 0xe6,
459+
0x23, 0xa8, 0x88, 0x52, 0x7f, 0x7d, 0x8a, 0x06, 0x94, 0x20, 0x8f, 0xf1, 0xf7, 0xa9,
460+
0xd5, 0x69, 0x09, 0x59,
461+
];
462+
let midstate_engine =
463+
sha256::HashEngine::from_midstate(sha256::Midstate::from_inner(MIDSTATE), 64);
464+
let hash = sha256::Hash::from_engine(midstate_engine);
465+
assert_eq!(hash, sha256::Hash(HASH_EXPECTED));
466+
}
467+
348468
#[cfg(feature="serde")]
349469
#[test]
350470
fn sha256_serde() {

0 commit comments

Comments
 (0)