394 lines
10 KiB
Rust
394 lines
10 KiB
Rust
use crate::common::*;
|
||
use crate::{Code, Error, Hash, Result};
|
||
use core::fmt;
|
||
use core::fmt::Formatter;
|
||
|
||
const K: [u32; 64] = [
|
||
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b,
|
||
0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01,
|
||
0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7,
|
||
0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
|
||
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152,
|
||
0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,
|
||
0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc,
|
||
0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
|
||
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819,
|
||
0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08,
|
||
0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f,
|
||
0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
|
||
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
|
||
];
|
||
|
||
const H0: [u32; 8] = [
|
||
0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F,
|
||
0x9B05688C, 0x1F83D9AB, 0x5BE0CD19,
|
||
];
|
||
|
||
const MB_SIZE: usize = 64;
|
||
|
||
pub const SIZE: usize = 32;
|
||
|
||
fn u32_to_chunk_in_place(x: u32, chunk: &mut [u8]) {
|
||
assert!(chunk.len() >= 4);
|
||
chunk[0] = ((x & 0xff000000) >> 24) as u8;
|
||
chunk[1] = ((x & 0x00ff0000) >> 16) as u8;
|
||
chunk[2] = ((x & 0x0000ff00) >> 8) as u8;
|
||
chunk[3] = (x & 0x000000ff) as u8;
|
||
}
|
||
|
||
#[derive(Debug, Clone, Copy)]
|
||
pub struct SHA256 {
|
||
mlen: u64,
|
||
i_hash: [u32; 8],
|
||
|
||
pub(crate) hresult: Code,
|
||
complete: bool,
|
||
|
||
mbi: usize,
|
||
mb: [u8; MB_SIZE],
|
||
}
|
||
|
||
impl fmt::Display for SHA256 {
|
||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||
write!(f, "SHA256<{:?}, ", self.hresult)?;
|
||
if !self.complete {
|
||
write!(f, "in")?;
|
||
}
|
||
|
||
write!(f, "complete>")
|
||
}
|
||
}
|
||
|
||
impl Default for SHA256 {
|
||
fn default() -> SHA256 {
|
||
SHA256::new()
|
||
}
|
||
}
|
||
|
||
impl SHA256 {
|
||
pub fn new() -> Self {
|
||
Self {
|
||
mlen: 0,
|
||
i_hash: H0,
|
||
hresult: Code::OK,
|
||
complete: false,
|
||
mbi: 0,
|
||
mb: [0; MB_SIZE],
|
||
}
|
||
}
|
||
|
||
fn chunk_to_u32(&self, offset: usize) -> u32 {
|
||
let mut chunk: u32 = 0;
|
||
let mut i: usize = offset;
|
||
|
||
while i < offset + 4 {
|
||
chunk <<= 8;
|
||
chunk += self.mb[i] as u32;
|
||
i += 1;
|
||
}
|
||
|
||
chunk
|
||
}
|
||
|
||
fn update_message_block(&mut self) {
|
||
let mut w: [u32; MB_SIZE] = [0; MB_SIZE];
|
||
let mut chunk = 0;
|
||
let mut i: usize = 0;
|
||
|
||
while i < 16 {
|
||
w[i] = self.chunk_to_u32(chunk);
|
||
chunk += 4;
|
||
i += 1;
|
||
}
|
||
|
||
self.mbi = 0;
|
||
i = 16;
|
||
while i < 64 {
|
||
w[i] = sha_σ1(w[i - 2])
|
||
.wrapping_add(w[i - 7])
|
||
.wrapping_add(sha_σ0(w[i - 15]))
|
||
.wrapping_add(w[i - 16]);
|
||
i += 1;
|
||
}
|
||
|
||
let mut a = self.i_hash[0];
|
||
let mut b = self.i_hash[1];
|
||
let mut c = self.i_hash[2];
|
||
let mut d = self.i_hash[3];
|
||
let mut e = self.i_hash[4];
|
||
let mut f = self.i_hash[5];
|
||
let mut g = self.i_hash[6];
|
||
let mut h = self.i_hash[7];
|
||
|
||
i = 0;
|
||
while i < 64 {
|
||
let t1 = h
|
||
.wrapping_add(sha_Σ1(e))
|
||
.wrapping_add(sha_ch(e, f, g))
|
||
.wrapping_add(K[i])
|
||
.wrapping_add(w[i]);
|
||
let t2 = sha_Σ0(a).wrapping_add(sha_maj(a, b, c));
|
||
h = g;
|
||
g = f;
|
||
f = e;
|
||
e = d.wrapping_add(t1);
|
||
d = c;
|
||
c = b;
|
||
b = a;
|
||
a = t1.wrapping_add(t2);
|
||
|
||
i += 1
|
||
}
|
||
|
||
self.i_hash[0] = self.i_hash[0].wrapping_add(a);
|
||
self.i_hash[1] = self.i_hash[1].wrapping_add(b);
|
||
self.i_hash[2] = self.i_hash[2].wrapping_add(c);
|
||
self.i_hash[3] = self.i_hash[3].wrapping_add(d);
|
||
self.i_hash[4] = self.i_hash[4].wrapping_add(e);
|
||
self.i_hash[5] = self.i_hash[5].wrapping_add(f);
|
||
self.i_hash[6] = self.i_hash[6].wrapping_add(g);
|
||
self.i_hash[7] = self.i_hash[7].wrapping_add(h);
|
||
}
|
||
|
||
fn add_length(&mut self, δ: usize) -> Result<()> {
|
||
let mδ = self.mlen + δ as u64;
|
||
if mδ < self.mlen {
|
||
return Err(Error::with(Code::InputTooLong));
|
||
}
|
||
|
||
self.mlen = mδ;
|
||
Ok(())
|
||
}
|
||
|
||
fn pad_message(&mut self, pc: u8) -> Result<()> {
|
||
if self.mbi < (MB_SIZE - 8) {
|
||
self.mb[self.mbi] = pc;
|
||
self.mbi += 1;
|
||
} else {
|
||
let mut pc_add = false;
|
||
|
||
if self.mbi < MB_SIZE - 1 {
|
||
self.mb[self.mbi] = pc;
|
||
self.mbi += 1;
|
||
pc_add = true;
|
||
}
|
||
|
||
while self.mbi < MB_SIZE {
|
||
self.mb[self.mbi] = 0;
|
||
self.mbi += 1;
|
||
}
|
||
|
||
self.update_message_block();
|
||
if !pc_add {
|
||
self.mb[self.mbi] = pc;
|
||
self.mbi += 1;
|
||
}
|
||
|
||
// Assumption: updating the message block has not left the
|
||
// context in a corrupted state.
|
||
debug_assert_eq!(self.hresult, Code::OK);
|
||
}
|
||
|
||
while self.mbi < (MB_SIZE - 8) {
|
||
self.mb[self.mbi] = 0;
|
||
self.mbi += 1;
|
||
}
|
||
|
||
const LSTART: usize = MB_SIZE - 8;
|
||
self.mb[LSTART] = (self.mlen >> 56) as u8;
|
||
self.mb[LSTART + 1] =
|
||
((self.mlen & 0x00ff000000000000) >> 48) as u8;
|
||
self.mb[LSTART + 2] =
|
||
((self.mlen & 0x0000ff0000000000) >> 40) as u8;
|
||
self.mb[LSTART + 3] =
|
||
((self.mlen & 0x000000ff00000000) >> 32) as u8;
|
||
self.mb[LSTART + 4] =
|
||
((self.mlen & 0x00000000ff000000) >> 24) as u8;
|
||
self.mb[LSTART + 5] =
|
||
((self.mlen & 0x0000000000ff0000) >> 16) as u8;
|
||
self.mb[LSTART + 6] =
|
||
((self.mlen & 0x000000000000ff00) >> 8) as u8;
|
||
self.mb[LSTART + 7] = (self.mlen & 0x00000000000000ff) as u8;
|
||
|
||
self.update_message_block();
|
||
|
||
// Assumption: updating the message block has not left the
|
||
// context in a corrupted state.
|
||
debug_assert_eq!(self.hresult, Code::OK);
|
||
Ok(())
|
||
}
|
||
|
||
fn copy_out_digest(&self, digest: &mut [u8]) {
|
||
u32_to_chunk_in_place(self.i_hash[0], digest);
|
||
u32_to_chunk_in_place(self.i_hash[1], &mut digest[4..]);
|
||
u32_to_chunk_in_place(self.i_hash[2], &mut digest[8..]);
|
||
u32_to_chunk_in_place(self.i_hash[3], &mut digest[12..]);
|
||
u32_to_chunk_in_place(self.i_hash[4], &mut digest[16..]);
|
||
u32_to_chunk_in_place(self.i_hash[5], &mut digest[20..]);
|
||
u32_to_chunk_in_place(self.i_hash[6], &mut digest[24..]);
|
||
u32_to_chunk_in_place(self.i_hash[7], &mut digest[28..]);
|
||
}
|
||
}
|
||
|
||
impl Hash for SHA256 {
|
||
fn reset(&mut self) -> Result<()> {
|
||
// The message block is set to the initial hash vector.
|
||
self.i_hash[0] = H0[0];
|
||
self.i_hash[1] = H0[1];
|
||
self.i_hash[2] = H0[2];
|
||
self.i_hash[3] = H0[3];
|
||
self.i_hash[4] = H0[4];
|
||
self.i_hash[5] = H0[5];
|
||
self.i_hash[6] = H0[6];
|
||
self.i_hash[7] = H0[7];
|
||
|
||
self.mb.fill(0);
|
||
self.mbi = 0;
|
||
self.hresult = Code::OK;
|
||
self.complete = false;
|
||
self.mlen = 0;
|
||
Ok(())
|
||
}
|
||
|
||
fn update(&mut self, msg: &[u8]) -> Result<()> {
|
||
if msg.is_empty() {
|
||
self.hresult = Code::OK;
|
||
return Ok(());
|
||
}
|
||
|
||
if self.hresult != Code::OK {
|
||
return Err(Error::with(self.hresult));
|
||
}
|
||
|
||
if self.complete {
|
||
return Err(Error::with(Code::InvalidState));
|
||
}
|
||
|
||
let mut i: usize = 0;
|
||
while i < msg.len() {
|
||
self.mb[self.mbi] = msg[i];
|
||
self.mbi += 1;
|
||
self.add_length(8)?;
|
||
if self.mbi == MB_SIZE {
|
||
self.update_message_block();
|
||
}
|
||
|
||
i += 1;
|
||
}
|
||
|
||
if self.hresult != Code::OK {
|
||
Err(Error::with(self.hresult))
|
||
} else {
|
||
Ok(())
|
||
}
|
||
}
|
||
|
||
fn finalize(&mut self, digest: &mut [u8]) -> Result<()> {
|
||
if digest.len() < SIZE {
|
||
return Err(Error::with(Code::BufferTooSmall));
|
||
}
|
||
|
||
if self.hresult != Code::OK {
|
||
return Err(Error::with(self.hresult));
|
||
}
|
||
|
||
if self.complete {
|
||
return Err(Error::with(Code::InvalidState));
|
||
}
|
||
|
||
self.pad_message(0x80)?;
|
||
|
||
let mut i: usize = 0;
|
||
while i < self.mb.len() {
|
||
self.mb[i] = 0;
|
||
i += 1;
|
||
}
|
||
|
||
self.complete = true;
|
||
self.mlen = 0;
|
||
|
||
self.copy_out_digest(digest);
|
||
self.hresult = Code::OK;
|
||
|
||
Ok(())
|
||
}
|
||
|
||
fn result(&self, digest: &mut [u8]) -> Result<()> {
|
||
if digest.len() < SIZE {
|
||
return Err(Error::with(Code::BufferTooSmall));
|
||
}
|
||
|
||
if self.hresult != Code::OK {
|
||
return Err(Error::with(self.hresult));
|
||
}
|
||
|
||
if !self.complete {
|
||
return Err(Error::with(Code::HashNotFinalized));
|
||
}
|
||
|
||
self.copy_out_digest(digest);
|
||
Ok(())
|
||
}
|
||
|
||
fn size() -> usize {
|
||
SIZE
|
||
}
|
||
}
|
||
|
||
fn run_test(input: &[u8], expected: &[u8]) -> Result<()> {
|
||
let mut h = SHA256::default();
|
||
let mut d: [u8; SIZE] = [0; SIZE];
|
||
|
||
h.update(input)?;
|
||
h.finalize(&mut d)?;
|
||
assert_eq!(d, expected);
|
||
|
||
d = [0; SIZE];
|
||
h.result(&mut d)?;
|
||
assert_eq!(d, expected);
|
||
|
||
Ok(())
|
||
}
|
||
|
||
fn run_reset_cycle() -> Result<()> {
|
||
let mut h = SHA256::default();
|
||
let mut d: [u8; SIZE] = [0; SIZE];
|
||
|
||
h.update(b"hello, world")?;
|
||
h.finalize(&mut d)?;
|
||
assert_eq!(&d, HELLO_WORLD);
|
||
|
||
d = [0; SIZE];
|
||
h.reset()?;
|
||
h.finalize(&mut d)?;
|
||
assert_eq!(&d, EMPTY_VECTOR);
|
||
|
||
h.reset()?;
|
||
h.update(b"hello, world")?;
|
||
h.finalize(&mut d)?;
|
||
assert_eq!(&d, HELLO_WORLD);
|
||
|
||
Ok(())
|
||
}
|
||
|
||
const EMPTY_VECTOR: &[u8; SIZE] = &[
|
||
0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4,
|
||
0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b,
|
||
0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55,
|
||
];
|
||
|
||
const HELLO_WORLD: &[u8; SIZE] = &[
|
||
0x09, 0xca, 0x7e, 0x4e, 0xaa, 0x6e, 0x8a, 0xe9, 0xc7, 0xd2, 0x61,
|
||
0x16, 0x71, 0x29, 0x18, 0x48, 0x83, 0x64, 0x4d, 0x07, 0xdf, 0xba,
|
||
0x7c, 0xbf, 0xbc, 0x4c, 0x8a, 0x2e, 0x08, 0x36, 0x0d, 0x5b,
|
||
];
|
||
|
||
/// self_test runs a quick test cycle, and can be used to validate
|
||
/// correctness in systems on startup.
|
||
pub fn self_test() -> Result<()> {
|
||
run_test(b"", EMPTY_VECTOR)?;
|
||
run_test(b"hello, world", HELLO_WORLD)?;
|
||
run_reset_cycle()?;
|
||
Ok(())
|
||
}
|