78 lines
1.9 KiB
Rust
78 lines
1.9 KiB
Rust
//! SHA256d, a.k.a., double SHA2, a.k.a., 2 SHA 2 Furious
|
|
|
|
use std::{fmt, io::prelude::*};
|
|
|
|
use sha2::{Digest, Sha256};
|
|
|
|
/// An `io::Write` instance that produces a SHA256d output.
|
|
#[derive(Default)]
|
|
pub struct Writer {
|
|
hash: Sha256,
|
|
}
|
|
|
|
impl Writer {
|
|
/// Consume the Writer and produce the hash result.
|
|
pub fn finish(self) -> [u8; 32] {
|
|
let result1 = self.hash.finalize();
|
|
let result2 = Sha256::digest(&result1);
|
|
let mut buffer = [0u8; 32];
|
|
buffer[0..32].copy_from_slice(&result2[0..32]);
|
|
buffer
|
|
}
|
|
}
|
|
|
|
impl Write for Writer {
|
|
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
|
self.hash.update(buf);
|
|
Ok(buf.len())
|
|
}
|
|
|
|
fn flush(&mut self) -> std::io::Result<()> {
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
/// A 4-byte checksum using truncated double-SHA256 (two rounds of SHA256).
|
|
#[derive(Copy, Clone, Eq, PartialEq)]
|
|
pub struct Checksum(pub [u8; 4]);
|
|
|
|
impl<'a> From<&'a [u8]> for Checksum {
|
|
fn from(bytes: &'a [u8]) -> Self {
|
|
let hash1 = Sha256::digest(bytes);
|
|
let hash2 = Sha256::digest(&hash1);
|
|
let mut checksum = [0u8; 4];
|
|
checksum[0..4].copy_from_slice(&hash2[0..4]);
|
|
Self(checksum)
|
|
}
|
|
}
|
|
|
|
impl fmt::Debug for Checksum {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
f.debug_tuple("Sha256dChecksum")
|
|
.field(&hex::encode(&self.0))
|
|
.finish()
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn sha256d_checksum() {
|
|
// https://en.bitcoin.it/wiki/Protocol_documentation#Hashes
|
|
let input = b"hello";
|
|
let checksum = Checksum::from(&input[..]);
|
|
let expected = Checksum([0x95, 0x95, 0xc9, 0xdf]);
|
|
assert_eq!(checksum, expected);
|
|
}
|
|
|
|
#[test]
|
|
fn sha256d_checksum_debug() {
|
|
let input = b"hello";
|
|
let checksum = Checksum::from(&input[..]);
|
|
|
|
assert_eq!(format!("{:?}", checksum), "Sha256dChecksum(\"9595c9df\")");
|
|
}
|
|
}
|