use super::sha256block::block_generic;
use crate::compat;
use crate::encoding::binary::{ByteOrder, BIG_ENDIAN};
use crate::hash::{self, Hash};
use std::io::Write;
pub const SIZE: usize = 32;
pub const SIZE224: usize = 28;
pub const BLOCK_SIZE: usize = 64;
pub const CHUNK: usize = 64;
const INIT0: u32 = 0x6A09E667;
const INIT1: u32 = 0xBB67AE85;
const INIT2: u32 = 0x3C6EF372;
const INIT3: u32 = 0xA54FF53A;
const INIT4: u32 = 0x510E527F;
const INIT5: u32 = 0x9B05688C;
const INIT6: u32 = 0x1F83D9AB;
const INIT7: u32 = 0x5BE0CD19;
const INIT0_224: u32 = 0xC1059ED8;
const INIT1_224: u32 = 0x367CD507;
const INIT2_224: u32 = 0x3070DD17;
const INIT3_224: u32 = 0xF70E5939;
const INIT4_224: u32 = 0xFFC00B31;
const INIT5_224: u32 = 0x68581511;
const INIT6_224: u32 = 0x64F98FA7;
const INIT7_224: u32 = 0xBEFA4FA4;
#[derive(Copy, Clone)]
pub struct Digest {
h: [u32; 8],
x: [u8; CHUNK],
nx: usize,
len: u64,
is224: bool, }
impl Default for Digest {
fn default() -> Self {
Self::new()
}
}
impl Digest {
pub fn new() -> Self {
let mut d = Self {
h: [0; 8],
x: [0; CHUNK],
nx: 0,
len: 0,
is224: false,
};
d.reset();
d
}
pub fn new224() -> Self {
let mut d = Self::new();
d.is224 = true;
d.reset();
d
}
pub fn write(&mut self, p: &[u8]) {
let mut p = p;
self.len += p.len() as u64;
if self.nx > 0 {
let n = compat::copy(&mut self.x[self.nx..], p);
self.nx += n;
if self.nx == CHUNK {
block_generic(&mut self.h, &self.x[..]);
self.nx = 0
}
p = &p[n..];
}
if p.len() >= CHUNK {
let n = p.len() & !(CHUNK - 1);
block_generic(&mut self.h, &p[..n]);
p = &p[n..];
}
if !p.is_empty() {
self.nx = compat::copy(&mut self.x, p);
}
}
}
impl hash::Hash for Digest {
fn reset(&mut self) {
if !self.is224 {
self.h[0] = INIT0;
self.h[1] = INIT1;
self.h[2] = INIT2;
self.h[3] = INIT3;
self.h[4] = INIT4;
self.h[5] = INIT5;
self.h[6] = INIT6;
self.h[7] = INIT7;
} else {
self.h[0] = INIT0_224;
self.h[1] = INIT1_224;
self.h[2] = INIT2_224;
self.h[3] = INIT3_224;
self.h[4] = INIT4_224;
self.h[5] = INIT5_224;
self.h[6] = INIT6_224;
self.h[7] = INIT7_224;
}
self.nx = 0;
self.len = 0;
}
fn size(&self) -> usize {
if !self.is224 {
SIZE
} else {
SIZE224
}
}
fn block_size(&self) -> usize {
BLOCK_SIZE
}
fn sum(&self, b: &[u8]) -> Vec<u8> {
let mut d0 = *self;
let hash = d0.checksum();
let mut res = b.to_vec();
if d0.is224 {
res.extend_from_slice(&hash[..SIZE224]);
} else {
res.extend_from_slice(&hash);
}
res
}
}
impl std::io::Write for Digest {
fn write(&mut self, p: &[u8]) -> std::io::Result<usize> {
Digest::write(self, p);
Ok(p.len())
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
impl Digest {
fn checksum(&mut self) -> [u8; SIZE] {
let len = self.len;
let mut tmp = [0; 64 + 8]; tmp[0] = 0x80_u8;
let t: u64 = if len % 64 < 56 {
56 - len % 64
} else {
64 + 56 - len % 64
};
let len = len << 3;
let padlen = &mut tmp[0..(t + 8) as usize];
BIG_ENDIAN.put_uint64(&mut padlen[(t as usize)..], len);
self.write_all(padlen).expect("write error is not expected");
if self.nx != 0 {
panic!("self.nx != 0");
}
let mut digest = [0; SIZE];
BIG_ENDIAN.put_uint32(&mut digest[0..], self.h[0]);
BIG_ENDIAN.put_uint32(&mut digest[4..], self.h[1]);
BIG_ENDIAN.put_uint32(&mut digest[8..], self.h[2]);
BIG_ENDIAN.put_uint32(&mut digest[12..], self.h[3]);
BIG_ENDIAN.put_uint32(&mut digest[16..], self.h[4]);
BIG_ENDIAN.put_uint32(&mut digest[20..], self.h[5]);
BIG_ENDIAN.put_uint32(&mut digest[24..], self.h[6]);
if !self.is224 {
BIG_ENDIAN.put_uint32(&mut digest[28..], self.h[7]);
}
digest
}
}
pub fn sum256(data: &[u8]) -> [u8; SIZE] {
let mut d = Digest::new();
d.reset();
d.write_all(data).expect("write error is not expected");
d.checksum()
}
pub fn sum224(data: &[u8]) -> [u8; SIZE224] {
let mut d = Digest::new224();
d.write_all(data).unwrap();
let mut res: [u8; 28] = [0; 28];
res.copy_from_slice(&d.checksum()[0..28]);
res
}
#[cfg(test)]
mod tests {
use super::*;
use crate::encoding::hex;
#[test]
fn test_sha256() {
assert_eq!(
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
hex::encode_to_string(&sum256("".as_bytes()))
);
assert_eq!(
"ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb",
hex::encode_to_string(&sum256("a".as_bytes()))
);
assert_eq!(
"fb8e20fc2e4c3f248c60c39bd652f3c1347298bb977b8b4d5903b85055620603",
hex::encode_to_string(&sum256("ab".as_bytes()))
);
assert_eq!(
"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad",
hex::encode_to_string(&sum256("abc".as_bytes()))
);
assert_eq!(
"a948904f2f0f479b8f8197694b30184b0d2ed1c1cd2a1ec0fb85d299a192a447",
hex::encode_to_string(&sum256("hello world\n".as_bytes()))
);
}
}