Initial commit
+ Seems to be working, need to add more edge cases + Focus on performance profiling too + clippy + fmtmain
commit
f8965810ec
|
@ -0,0 +1,3 @@
|
|||
/target
|
||||
/Cargo.lock
|
||||
.idea/
|
|
@ -0,0 +1,12 @@
|
|||
[package]
|
||||
name = "smoll_sha"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[profile.dev]
|
||||
overflow-checks = false
|
||||
|
||||
[dependencies]
|
||||
|
|
@ -0,0 +1,191 @@
|
|||
const SHA1_CONST: &[u32] = &[0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0];
|
||||
|
||||
pub struct SHA1Hash {
|
||||
len: usize,
|
||||
data_len: usize,
|
||||
chunk: [u8; 64],
|
||||
state: [u32; 5],
|
||||
}
|
||||
|
||||
impl Default for SHA1Hash {
|
||||
fn default() -> Self {
|
||||
let mut starting_state = [0u32; 5];
|
||||
|
||||
starting_state.copy_from_slice(SHA1_CONST);
|
||||
|
||||
Self {
|
||||
len: 0,
|
||||
data_len: 0,
|
||||
chunk: [0; 64],
|
||||
state: starting_state,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SHA1Hash {
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn process_chunk(&mut self) {
|
||||
let mut buffer = [0u32; 80];
|
||||
|
||||
let mut buffer_ndx = 0;
|
||||
for chunk_ndx in (0..self.chunk.len()).step_by(4) {
|
||||
let mut word = [0u8; 4];
|
||||
word.copy_from_slice(&self.chunk[chunk_ndx..chunk_ndx + 4]);
|
||||
buffer[buffer_ndx] = u32::from_be_bytes(word);
|
||||
buffer_ndx += 1;
|
||||
}
|
||||
|
||||
for ndx in buffer_ndx..80 {
|
||||
buffer[ndx] = (buffer[ndx - 3] ^ buffer[ndx - 8] ^ buffer[ndx - 14] ^ buffer[ndx - 16])
|
||||
.rotate_left(1);
|
||||
}
|
||||
|
||||
let mut a = self.state[0];
|
||||
let mut b = self.state[1];
|
||||
let mut c = self.state[2];
|
||||
let mut d = self.state[3];
|
||||
let mut e = self.state[4];
|
||||
|
||||
let mut f;
|
||||
let mut k;
|
||||
|
||||
for (i, word) in buffer.iter().enumerate() {
|
||||
match i {
|
||||
0..=19 => {
|
||||
f = d ^ (b & (c ^ d));
|
||||
k = 0x5A827999;
|
||||
}
|
||||
20..=39 => {
|
||||
f = b ^ c ^ d;
|
||||
k = 0x6ED9EBA1;
|
||||
}
|
||||
40..=59 => {
|
||||
f = (b & c) | (b & d) | (c & d);
|
||||
k = 0x8F1BBCDC;
|
||||
}
|
||||
60..=79 => {
|
||||
f = b ^ c ^ d;
|
||||
k = 0xCA62C1D6;
|
||||
}
|
||||
_ => unreachable!("Loop range is 0 to 79"),
|
||||
}
|
||||
|
||||
let temp = a.rotate_left(5) + f + e + k + word;
|
||||
e = d;
|
||||
d = c;
|
||||
c = b.rotate_left(30);
|
||||
b = a;
|
||||
a = temp;
|
||||
}
|
||||
|
||||
self.state[0] += a;
|
||||
self.state[1] += b;
|
||||
self.state[2] += c;
|
||||
self.state[3] += d;
|
||||
self.state[4] += e;
|
||||
}
|
||||
|
||||
pub fn finalize(&mut self) -> [u8; 20] {
|
||||
self.len += self.data_len;
|
||||
self.chunk[self.data_len] = 0x80;
|
||||
self.data_len += 1;
|
||||
|
||||
if self.data_len < 56 {
|
||||
self.data_len = 56;
|
||||
}
|
||||
|
||||
let bit_len = self.len * 8;
|
||||
self.chunk[self.data_len..self.data_len + 8].copy_from_slice(&bit_len.to_be_bytes());
|
||||
|
||||
self.process_chunk();
|
||||
|
||||
let mut output = [0u8; 20];
|
||||
|
||||
output[0..4].copy_from_slice(&self.state[0].to_be_bytes());
|
||||
output[4..8].copy_from_slice(&self.state[1].to_be_bytes());
|
||||
output[8..12].copy_from_slice(&self.state[2].to_be_bytes());
|
||||
output[12..16].copy_from_slice(&self.state[3].to_be_bytes());
|
||||
output[16..20].copy_from_slice(&self.state[4].to_be_bytes());
|
||||
|
||||
output
|
||||
}
|
||||
|
||||
pub fn digest(&mut self, data: &[u8]) {
|
||||
let mut data_ndx = 0usize;
|
||||
|
||||
while data_ndx < data.len() {
|
||||
let data_needed = 64 - self.data_len;
|
||||
let data_left = data.len() - data_ndx;
|
||||
|
||||
let data_to_read = data_needed.clamp(0, data_left);
|
||||
|
||||
let data_end = (data_to_read + data_ndx).clamp(0, data.len());
|
||||
|
||||
self.chunk[self.data_len..self.data_len + data_to_read]
|
||||
.copy_from_slice(&data[data_ndx..data_end]);
|
||||
self.data_len += data_to_read;
|
||||
|
||||
if self.data_len == 64 {
|
||||
self.process_chunk();
|
||||
self.data_len = 0;
|
||||
self.len += 64;
|
||||
self.chunk.fill(0);
|
||||
}
|
||||
|
||||
data_ndx += data_to_read;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn verify_single_block() {
|
||||
let mut sha1 = SHA1Hash::new();
|
||||
|
||||
let msg = "The quick brown fox jumps over the lazy dog\n";
|
||||
|
||||
sha1.digest(msg.as_bytes());
|
||||
|
||||
let output = sha1.finalize();
|
||||
|
||||
let expected_hash =
|
||||
b"\xbe\x41\x77\x68\xb5\xc3\xc5\xc1\xd9\xbc\xb2\xe7\xc1\x19\x19\x6d\xd7\x6b\x55\x70";
|
||||
assert_eq!(&output, expected_hash);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn verify_multi_blocks() {
|
||||
let mut sha1 = SHA1Hash::new();
|
||||
|
||||
let msg = "OOPSIE WOOPSIE!! Uwu We make a fucky wucky!! A wittle fucko boingo! The code monkeys at our headquarters are working VEWY HAWD to fix this!\n";
|
||||
|
||||
sha1.digest(msg.as_bytes());
|
||||
|
||||
let output = sha1.finalize();
|
||||
|
||||
let expected_hash =
|
||||
b"\x14\x09\x49\x37\xfb\x7c\x3c\x30\x02\xda\x1a\x8d\x30\x84\x7f\x1d\xc9\x2b\x36\x0b";
|
||||
assert_eq!(&output, expected_hash);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn verify_64_bytes() {
|
||||
let mut sha1 = SHA1Hash::new();
|
||||
|
||||
let msg = "a".repeat(64);
|
||||
|
||||
sha1.digest(msg.as_bytes());
|
||||
|
||||
let output = sha1.finalize();
|
||||
|
||||
let expected_hash =
|
||||
b"\x00\x98\xba\x82\x4b\x5c\x16\x42\x7b\xd7\xa1\x12\x2a\x5a\x44\x2a\x25\xec\x64\x4d";
|
||||
assert_eq!(&output, expected_hash);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue