Files
emsha-rs/src/sha256.rs

394 lines
10 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 = self.mlen + δ as u64;
if < self.mlen {
return Err(Error::with(Code::InputTooLong));
}
self.mlen = ;
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(())
}