diff --git a/.cargo/config b/.cargo/config new file mode 100644 index 0000000..5927d65 --- /dev/null +++ b/.cargo/config @@ -0,0 +1,3 @@ +[target.x86_64-unknown-linux-gnu] +linker = "/usr/bin/clang" +rustflags = ["-Clink-arg=-fuse-ld=lld", "-Clink-arg=-Wl,--no-rosegment"] \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index ceb9ee3..b8d3aea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,5 +8,8 @@ edition = "2021" [profile.dev] overflow-checks = false +[profile.release] +debug = true + [dependencies] diff --git a/src/lib.rs b/src/lib.rs index 229c562..6aff456 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,10 @@ const SHA1_CONST: &[u32] = &[0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0]; +fn u32_rotate_left(val: u32, places: u32) -> u32 { + val << places | (val >> (32 - places)) +} + pub struct SHA1Hash { len: usize, data_len: usize, @@ -33,11 +37,14 @@ impl SHA1Hash { 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]; + + let mut chunk_ndx = 0; + let mut word = [0u8; 4]; + while chunk_ndx < self.chunk.len() { word.copy_from_slice(&self.chunk[chunk_ndx..chunk_ndx + 4]); buffer[buffer_ndx] = u32::from_be_bytes(word); buffer_ndx += 1; + chunk_ndx += 4; } for ndx in buffer_ndx..80 { @@ -54,7 +61,10 @@ impl SHA1Hash { let mut f; let mut k; - for (i, word) in buffer.iter().enumerate() { + let mut i = 0; + while i < buffer.len() { + let word = buffer[i]; + match i { 0..=19 => { f = d ^ (b & (c ^ d)); @@ -75,12 +85,14 @@ impl SHA1Hash { _ => unreachable!("Loop range is 0 to 79"), } - let temp = a.rotate_left(5) + f + e + k + word; + let temp = u32_rotate_left(a, 5) + f + e + k + word; e = d; d = c; - c = b.rotate_left(30); + c = u32_rotate_left(b, 30); b = a; a = temp; + + i += 1; } self.state[0] += a; @@ -146,48 +158,60 @@ impl SHA1Hash { mod tests { use super::*; - #[test] - fn verify_single_block() { + fn verify_sha1(buf: &[u8], expected_hash: &[u8; 20]) { let mut sha1 = SHA1Hash::new(); - let msg = "The quick brown fox jumps over the lazy dog\n"; - - sha1.digest(msg.as_bytes()); + sha1.digest(buf); let output = sha1.finalize(); + assert_eq!(&output, expected_hash); + } + + #[test] + fn verify_single_block() { + let msg = "The quick brown fox jumps over the lazy dog\n"; + 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); + + verify_sha1(msg.as_bytes(), 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); + + verify_sha1(msg.as_bytes(), 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); + + verify_sha1(msg.as_bytes(), expected_hash) + } + + #[test] + fn verify_speed() { + let mut sha1 = SHA1Hash::new(); + let size = 1048576; // 1024 bytes * 1024 * 1024 = 1GB + let buf = [0x55u8; 1024]; + + for _ in 0..size { + sha1.digest(&buf); + } + + let output = sha1.finalize(); + + assert_eq!( + &output, + b"\xa4\x14\xa6\x96\x2c\x6f\x87\x47\x7d\xf2\xc0\x10\xae\x36\x27\xe5\xf1\x9f\x0e\xed" + ) } }