diff --git a/src/main.rs b/src/main.rs index 844109c..6ade3e1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,10 +10,11 @@ mod set1; mod set2; mod set3; mod set4; +mod sha1; mod utils; fn main() { - const RUN_ALL: bool = false; + const RUN_ALL: bool = true; if RUN_ALL { set1::challenge1(); set1::challenge2(); @@ -43,7 +44,8 @@ fn main() { set4::challenge26(); set4::challenge27(); set4::challenge28(); + set4::challenge29(); } else { - set4::challenge28(); + set4::challenge29(); } } diff --git a/src/set4.rs b/src/set4.rs index 28e3683..e6639e5 100644 --- a/src/set4.rs +++ b/src/set4.rs @@ -1,4 +1,6 @@ -use crate::{bytes::Bytes, cbc, ctr, parser, utils}; +#![allow(arithmetic_overflow)] + +use crate::{bytes::Bytes, cbc, ctr, parser, sha1, utils}; pub fn challenge25() { let cipher = utils::read_base64("data/25.txt"); @@ -138,5 +140,50 @@ pub fn challenge27() { } pub fn challenge28() { - println!("[xxxx] Challenge 28: TBD"); + let i1 = Bytes(vec![b'a'; 64]); + let e1 = Bytes::from_hex("0098ba824b5c16427bd7a1122a5a442a25ec644d"); + let o1 = sha1::hash(&i1); + assert_eq!(e1, o1); + + let i2 = Bytes(vec![b'a'; 128]); + let e2 = Bytes::from_hex("ad5b3fdbcb526778c2839d2f151ea753995e26a0"); + let o2 = sha1::hash(&i2); + assert_eq!(e2, o2); + + let i3 = Bytes(vec![b'a'; 3]); + let e3 = Bytes::from_hex("7e240de74fb1ed08fa08d38063f6a6a91462a815"); + let o3 = sha1::hash(&i3); + assert_eq!(e3, o3); + + let i4 = Bytes(vec![]); + let e4 = Bytes::from_hex("da39a3ee5e6b4b0d3255bfef95601890afd80709"); + let o4 = sha1::hash(&i4); + assert_eq!(e4, o4); + + fn authenticate(message: &Bytes, key: &Bytes) -> Bytes { + // Write a function to authenticate a message under a secret key by using a + // secret-prefix MAC, which is simply: + // SHA1(key || message) + let mut c = vec![]; + c.append(&mut key.0.to_vec()); + c.append(&mut message.0.to_vec()); + // how to concatenate better: https://stackoverflow.com/a/56490417 + sha1::hash(&Bytes(c)) + } + + // Verify that you cannot tamper with the message without breaking the MAC + // you've produced, and that you can't produce a new MAC without knowing the + // secret key. + let mut message = Bytes::from_utf8("love, love, love"); + let key = Bytes::from_utf8("kisses!"); + let auth = authenticate(&message, &key); + message.flip_bit(2, 3); + let auth_tempered = authenticate(&message, &key); + assert!(auth != auth_tempered); + + println!("[okay] Challenge 28: implemented SHA-1"); +} + +pub fn challenge29() { + println!("[xxxx] Challenge 29: TBD"); } diff --git a/src/sha1.rs b/src/sha1.rs new file mode 100644 index 0000000..6ed6e1f --- /dev/null +++ b/src/sha1.rs @@ -0,0 +1,234 @@ +use crate::bytes::Bytes; +/// Sha1 implementation based on https://github.com/vog/sha1/blob/master/sha1.hpp +use std::num::Wrapping; + +const STATE_LEN: usize = 5; +const BLOCK_INTS: usize = 16; // number of 32bit integers per SHA1 block +const BLOCK_BYTES: usize = BLOCK_INTS * 4; // 64 +type Wu32 = Wrapping; +type Block = [Wu32; BLOCK_INTS]; + +pub struct Sha1 { + h: [Wu32; STATE_LEN], + block_len: u64, +} + +impl Default for Sha1 { + #[inline] + fn default() -> Self { + Self { + h: [ + Wrapping(0x67452301), + Wrapping(0xEFCDAB89), + Wrapping(0x98BADCFE), + Wrapping(0x10325476), + Wrapping(0xC3D2E1F0), + ], + block_len: 0, + } + } +} + +fn rol(value: Wu32, bits: usize) -> Wu32 { + return (value << bits) | (value >> (32 - bits)); +} + +fn blk(block: &Block, i: usize) -> Wu32 { + return rol( + block[(i + 13) & 15] ^ block[(i + 8) & 15] ^ block[(i + 2) & 15] ^ block[i], + 1, + ); +} + +fn r0(block: &Block, v: Wu32, w: &mut Wu32, x: Wu32, y: Wu32, z: &mut Wu32, i: usize) { + *z = *z + ((*w & (x ^ y)) ^ y) + block[i] + Wrapping(0x5a827999) + rol(v, 5); + *w = rol(*w, 30); +} + +fn r1(block: &mut Block, v: Wu32, w: &mut Wu32, x: Wu32, y: Wu32, z: &mut Wu32, i: usize) { + block[i] = blk(block, i); + *z = *z + ((*w & (x ^ y)) ^ y) + block[i] + Wrapping(0x5a827999) + rol(v, 5); + *w = rol(*w, 30); +} + +fn r2(block: &mut Block, v: Wu32, w: &mut Wu32, x: Wu32, y: Wu32, z: &mut Wu32, i: usize) { + block[i] = blk(block, i); + *z += (*w ^ x ^ y) + block[i] + Wrapping(0x6ed9eba1) + rol(v, 5); + *w = rol(*w, 30); +} + +fn r3(block: &mut Block, v: Wu32, w: &mut Wu32, x: Wu32, y: Wu32, z: &mut Wu32, i: usize) { + block[i] = blk(block, i); + *z += (((*w | x) & y) | (*w & x)) + block[i] + Wrapping(0x8f1bbcdc) + rol(v, 5); + *w = rol(*w, 30); +} + +fn r4(block: &mut Block, v: Wu32, w: &mut Wu32, x: Wu32, y: Wu32, z: &mut Wu32, i: usize) { + block[i] = blk(block, i); + *z = *z + (*w ^ x ^ y) + block[i] + Wrapping(0xca62c1d6) + rol(v, 5); + *w = rol(*w, 30); +} + +impl Sha1 { + #[inline] + fn _reset(&mut self) { + *self = Default::default(); + } + + fn transform(&mut self, block: &mut Block) { + /* Copy digest[] to working vars */ + let mut a = self.h[0]; + let mut b = self.h[1]; + let mut c = self.h[2]; + let mut d = self.h[3]; + let mut e = self.h[4]; + + /* 4 rounds of 20 operations each. Loop unrolled. */ + r0(block, a, &mut b, c, d, &mut e, 0); + r0(block, e, &mut a, b, c, &mut d, 1); + r0(block, d, &mut e, a, b, &mut c, 2); + r0(block, c, &mut d, e, a, &mut b, 3); + r0(block, b, &mut c, d, e, &mut a, 4); + r0(block, a, &mut b, c, d, &mut e, 5); + r0(block, e, &mut a, b, c, &mut d, 6); + r0(block, d, &mut e, a, b, &mut c, 7); + r0(block, c, &mut d, e, a, &mut b, 8); + r0(block, b, &mut c, d, e, &mut a, 9); + r0(block, a, &mut b, c, d, &mut e, 10); + r0(block, e, &mut a, b, c, &mut d, 11); + r0(block, d, &mut e, a, b, &mut c, 12); + r0(block, c, &mut d, e, a, &mut b, 13); + r0(block, b, &mut c, d, e, &mut a, 14); + r0(block, a, &mut b, c, d, &mut e, 15); + r1(block, e, &mut a, b, c, &mut d, 0); + r1(block, d, &mut e, a, b, &mut c, 1); + r1(block, c, &mut d, e, a, &mut b, 2); + r1(block, b, &mut c, d, e, &mut a, 3); + r2(block, a, &mut b, c, d, &mut e, 4); + r2(block, e, &mut a, b, c, &mut d, 5); + r2(block, d, &mut e, a, b, &mut c, 6); + r2(block, c, &mut d, e, a, &mut b, 7); + r2(block, b, &mut c, d, e, &mut a, 8); + r2(block, a, &mut b, c, d, &mut e, 9); + r2(block, e, &mut a, b, c, &mut d, 10); + r2(block, d, &mut e, a, b, &mut c, 11); + r2(block, c, &mut d, e, a, &mut b, 12); + r2(block, b, &mut c, d, e, &mut a, 13); + r2(block, a, &mut b, c, d, &mut e, 14); + r2(block, e, &mut a, b, c, &mut d, 15); + r2(block, d, &mut e, a, b, &mut c, 0); + r2(block, c, &mut d, e, a, &mut b, 1); + r2(block, b, &mut c, d, e, &mut a, 2); + r2(block, a, &mut b, c, d, &mut e, 3); + r2(block, e, &mut a, b, c, &mut d, 4); + r2(block, d, &mut e, a, b, &mut c, 5); + r2(block, c, &mut d, e, a, &mut b, 6); + r2(block, b, &mut c, d, e, &mut a, 7); + r3(block, a, &mut b, c, d, &mut e, 8); + r3(block, e, &mut a, b, c, &mut d, 9); + r3(block, d, &mut e, a, b, &mut c, 10); + r3(block, c, &mut d, e, a, &mut b, 11); + r3(block, b, &mut c, d, e, &mut a, 12); + r3(block, a, &mut b, c, d, &mut e, 13); + r3(block, e, &mut a, b, c, &mut d, 14); + r3(block, d, &mut e, a, b, &mut c, 15); + r3(block, c, &mut d, e, a, &mut b, 0); + r3(block, b, &mut c, d, e, &mut a, 1); + r3(block, a, &mut b, c, d, &mut e, 2); + r3(block, e, &mut a, b, c, &mut d, 3); + r3(block, d, &mut e, a, b, &mut c, 4); + r3(block, c, &mut d, e, a, &mut b, 5); + r3(block, b, &mut c, d, e, &mut a, 6); + r3(block, a, &mut b, c, d, &mut e, 7); + r3(block, e, &mut a, b, c, &mut d, 8); + r3(block, d, &mut e, a, b, &mut c, 9); + r3(block, c, &mut d, e, a, &mut b, 10); + r3(block, b, &mut c, d, e, &mut a, 11); + r4(block, a, &mut b, c, d, &mut e, 12); + r4(block, e, &mut a, b, c, &mut d, 13); + r4(block, d, &mut e, a, b, &mut c, 14); + r4(block, c, &mut d, e, a, &mut b, 15); + r4(block, b, &mut c, d, e, &mut a, 0); + r4(block, a, &mut b, c, d, &mut e, 1); + r4(block, e, &mut a, b, c, &mut d, 2); + r4(block, d, &mut e, a, b, &mut c, 3); + r4(block, c, &mut d, e, a, &mut b, 4); + r4(block, b, &mut c, d, e, &mut a, 5); + r4(block, a, &mut b, c, d, &mut e, 6); + r4(block, e, &mut a, b, c, &mut d, 7); + r4(block, d, &mut e, a, b, &mut c, 8); + r4(block, c, &mut d, e, a, &mut b, 9); + r4(block, b, &mut c, d, e, &mut a, 10); + r4(block, a, &mut b, c, d, &mut e, 11); + r4(block, e, &mut a, b, c, &mut d, 12); + r4(block, d, &mut e, a, b, &mut c, 13); + r4(block, c, &mut d, e, a, &mut b, 14); + r4(block, b, &mut c, d, e, &mut a, 15); + + /* Add the working vars back into digest[] */ + self.h[0] += a; + self.h[1] += b; + self.h[2] += c; + self.h[3] += d; + self.h[4] += e; + + /* Count the number of transformations */ + self.block_len += 1; + } + fn update(&mut self, bytes: &[u8]) { + if bytes.len() % BLOCK_BYTES != 0 { + panic!("we only support buffers that are a multiples of 64 atm") + } + let mut block = bytes_to_block(bytes); + self.transform(&mut block); + } + + fn finalize(&mut self, last_chunk: Vec) -> Bytes { + let mut buffer = last_chunk; + let total_bits: u64 = (self.block_len * (BLOCK_BYTES as u64) + buffer.len() as u64) * 8; + + buffer.push(0x80); + + let orig_size = buffer.len(); + while buffer.len() < BLOCK_BYTES { + buffer.push(0x0); + } + + let mut block = bytes_to_block(&buffer); + if orig_size > (BLOCK_BYTES - 8) as usize { + self.transform(&mut block); + for i in 0..(BLOCK_INTS - 2) { + block[i] = Wrapping(0); + } + } + + block[BLOCK_INTS - 1] = Wrapping(total_bits as u32); + block[BLOCK_INTS - 2] = Wrapping((total_bits >> 32) as u32); + self.transform(&mut block); + + Bytes(self.h.iter().map(|i| i.0.to_be_bytes()).flatten().collect()) + } +} + +fn bytes_to_block(bytes: &[u8]) -> Block { + assert_eq!(bytes.len(), BLOCK_BYTES); + // safety: unwraps work because BLOCK_BYTES length is asserted + let b: Vec = bytes + .chunks(4) + .map(|c| Wrapping(u32::from_be_bytes(c.try_into().unwrap()))) + .collect(); + b.try_into().unwrap() +} + +pub fn hash(bytes: &Bytes) -> Bytes { + let mut s = Sha1::default(); + let mut last_chunk = vec![]; + for bytes in bytes.0.chunks(BLOCK_BYTES) { + if bytes.len() == BLOCK_BYTES { + s.update(&bytes); + } else { + last_chunk = bytes.to_vec(); + } + } + s.finalize(last_chunk) +}