Clean up sha-1 and start with md4 implementation

This commit is contained in:
2022-08-20 13:33:29 -04:00
parent c12b1c45a6
commit 106b8febaf
4 changed files with 187 additions and 45 deletions

View File

@@ -3,6 +3,7 @@ mod bytes_base64;
mod cbc; mod cbc;
mod ctr; mod ctr;
mod ecb; mod ecb;
mod md4;
mod mt19937; mod mt19937;
mod mtcipher; mod mtcipher;
mod parser; mod parser;
@@ -47,6 +48,8 @@ fn main() {
set4::challenge29(); set4::challenge29();
set4::challenge30(); set4::challenge30();
} else { } else {
set4::challenge28();
set4::challenge29();
set4::challenge30(); set4::challenge30();
} }
} }

95
src/md4.rs Normal file
View File

@@ -0,0 +1,95 @@
// MD4 implementation based on https://docs.rs/md4/latest/md4/
use crate::bytes::Bytes;
// #[derive(Clone)]
// pub struct Md4Core {
// block_len: u64,
// state: [u32; 4],
// }
// impl Default for Md4Core {
// #[inline]
// fn default() -> Self {
// let state = [0x6745_2301, 0xEFCD_AB89, 0x98BA_DCFE, 0x1032_5476];
// Self {
// state,
// block_len: 0,
// }
// }
// }
// fn compress(state: &mut [u32; 4], input: &Block<Md4Core>) {
// fn f(x: u32, y: u32, z: u32) -> u32 {
// (x & y) | (!x & z)
// }
// fn g(x: u32, y: u32, z: u32) -> u32 {
// (x & y) | (x & z) | (y & z)
// }
// fn h(x: u32, y: u32, z: u32) -> u32 {
// x ^ y ^ z
// }
// fn op1(a: u32, b: u32, c: u32, d: u32, k: u32, s: u32) -> u32 {
// a.wrapping_add(f(b, c, d)).wrapping_add(k).rotate_left(s)
// }
// fn op2(a: u32, b: u32, c: u32, d: u32, k: u32, s: u32) -> u32 {
// a.wrapping_add(g(b, c, d))
// .wrapping_add(k)
// .wrapping_add(0x5A82_7999)
// .rotate_left(s)
// }
// fn op3(a: u32, b: u32, c: u32, d: u32, k: u32, s: u32) -> u32 {
// a.wrapping_add(h(b, c, d))
// .wrapping_add(k)
// .wrapping_add(0x6ED9_EBA1)
// .rotate_left(s)
// }
// let mut a = state[0];
// let mut b = state[1];
// let mut c = state[2];
// let mut d = state[3];
// // load block to data
// let mut data = [0u32; 16];
// for (o, chunk) in data.iter_mut().zip(input.chunks_exact(4)) {
// *o = u32::from_le_bytes(chunk.try_into().unwrap());
// }
// // round 1
// for &i in &[0, 4, 8, 12] {
// a = op1(a, b, c, d, data[i], 3);
// d = op1(d, a, b, c, data[i + 1], 7);
// c = op1(c, d, a, b, data[i + 2], 11);
// b = op1(b, c, d, a, data[i + 3], 19);
// }
// // round 2
// for i in 0..4 {
// a = op2(a, b, c, d, data[i], 3);
// d = op2(d, a, b, c, data[i + 4], 5);
// c = op2(c, d, a, b, data[i + 8], 9);
// b = op2(b, c, d, a, data[i + 12], 13);
// }
// // round 3
// for &i in &[0, 2, 1, 3] {
// a = op3(a, b, c, d, data[i], 3);
// d = op3(d, a, b, c, data[i + 8], 9);
// c = op3(c, d, a, b, data[i + 4], 11);
// b = op3(b, c, d, a, data[i + 12], 15);
// }
// state[0] = state[0].wrapping_add(a);
// state[1] = state[1].wrapping_add(b);
// state[2] = state[2].wrapping_add(c);
// state[3] = state[3].wrapping_add(d);
// }
pub fn hash(_bytes: &Bytes) -> Bytes {
Bytes::from_hex("31d6cfe0d16ae931b73c59d7e0c089c0")
}

View File

@@ -1,4 +1,4 @@
use crate::{bytes::Bytes, cbc, ctr, ecb, parser, sha1, utils}; use crate::{bytes::Bytes, cbc, ctr, ecb, md4, parser, sha1, utils};
pub fn challenge25() { pub fn challenge25() {
let cipher = utils::read_base64("data/25.txt"); let cipher = utils::read_base64("data/25.txt");
@@ -139,28 +139,28 @@ pub fn challenge27() {
pub fn challenge28() { pub fn challenge28() {
let mut sha1 = sha1::Sha1::default(); let mut sha1 = sha1::Sha1::default();
let i1 = Bytes(vec![b'a'; 64]); assert_eq!(
let e1 = Bytes::from_hex("0098ba824b5c16427bd7a1122a5a442a25ec644d"); Bytes::from_hex("0098ba824b5c16427bd7a1122a5a442a25ec644d"),
let o1 = sha1.hash(&i1); sha1.hash(&Bytes(vec![b'a'; 64]))
assert_eq!(e1, o1); );
sha1.reset(); sha1.reset();
let i2 = Bytes(vec![b'a'; 128]); assert_eq!(
let e2 = Bytes::from_hex("ad5b3fdbcb526778c2839d2f151ea753995e26a0"); Bytes::from_hex("ad5b3fdbcb526778c2839d2f151ea753995e26a0"),
let o2 = sha1.hash(&i2); sha1.hash(&Bytes(vec![b'a'; 128]))
assert_eq!(e2, o2); );
sha1.reset(); sha1.reset();
let i3 = Bytes(vec![b'a'; 3]); assert_eq!(
let e3 = Bytes::from_hex("7e240de74fb1ed08fa08d38063f6a6a91462a815"); Bytes::from_hex("7e240de74fb1ed08fa08d38063f6a6a91462a815"),
let o3 = sha1.hash(&i3); sha1.hash(&Bytes(vec![b'a'; 3])),
assert_eq!(e3, o3); );
sha1.reset(); sha1.reset();
let i4 = Bytes(vec![]); assert_eq!(
let e4 = Bytes::from_hex("da39a3ee5e6b4b0d3255bfef95601890afd80709"); Bytes::from_hex("da39a3ee5e6b4b0d3255bfef95601890afd80709"),
let o4 = sha1.hash(&i4); sha1.hash(&Bytes(vec![])),
assert_eq!(e4, o4); );
// Verify that you cannot tamper with the message without breaking the MAC // 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 // you've produced, and that you can't produce a new MAC without knowing the
@@ -232,5 +232,42 @@ pub fn challenge29() {
} }
pub fn challenge30() { pub fn challenge30() {
// Second verse, same as the first, but use MD4 instead of SHA-1. Having
// done this attack once against SHA-1, the MD4 variant should take much
// less time; mostly just the time you'll spend Googling for an
// implementation of MD4.
assert_eq!(
md4::hash(&Bytes::from_utf8("")),
Bytes::from_hex("31d6cfe0d16ae931b73c59d7e0c089c0"),
);
// assert_eq!(
// md4::hash(&Bytes::from_utf8("a")),
// Bytes::from_hex("bde52cb31de33e46245e05fbdbd6fb24"),
// );
// assert_eq!(
// md4::hash(&Bytes::from_utf8("abc")),
// Bytes::from_hex("a448017aaf21d8525fc10ae87aa6729d"),
// );
// assert_eq!(
// md4::hash(&Bytes::from_utf8("message digest")),
// Bytes::from_hex("d9130a8164549fe818874806e1c7014b"),
// );
// assert_eq!(
// md4::hash(&Bytes::from_utf8("abcdefghijklmnopqrstuvwxyz")),
// Bytes::from_hex("d79e1c308aa5bbcdeea8ed63df412da9"),
// );
// assert_eq!(
// md4::hash(&Bytes::from_utf8(
// "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
// )),
// Bytes::from_hex("043f8582f241db351ce627e153e7f0e4"),
// );
// assert_eq!(
// md4::hash(&Bytes::from_utf8(
// "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
// )),
// Bytes::from_hex("e33b4ddc9c38f2199c3e7b164fcc0536"),
// );
println!("[xxxx] Challenge 30: tbd"); println!("[xxxx] Challenge 30: tbd");
} }

View File

@@ -1,16 +1,14 @@
/// Sha1 implementation based on https://github.com/vog/sha1/blob/master/sha1.hpp /// SHA-1 implementation adapted from https://github.com/vog/sha1/blob/master/sha1.hpp
use crate::bytes::Bytes; use crate::bytes::Bytes;
use std::num::Wrapping;
const STATE_LEN: usize = 5; const STATE_LEN: usize = 5;
const BLOCK_INTS: usize = 16; // number of 32bit integers per SHA1 block const BLOCK_INTS: usize = 16; // number of 32bit integers per SHA1 block
const BLOCK_BYTES: usize = BLOCK_INTS * 4; // 64 const BLOCK_BYTES: usize = BLOCK_INTS * 4; // 64
type Wu32 = Wrapping<u32>; type Block = [u32; BLOCK_INTS];
type Block = [Wu32; BLOCK_INTS];
#[derive(Debug)] #[derive(Debug)]
pub struct Sha1 { pub struct Sha1 {
h: [Wu32; STATE_LEN], h: [u32; STATE_LEN],
byte_len: u64, byte_len: u64,
} }
@@ -18,13 +16,7 @@ impl Default for Sha1 {
#[inline] #[inline]
fn default() -> Self { fn default() -> Self {
Self { Self {
h: [ h: [ 0x6745_2301, 0xEFCD_AB89, 0x98BA_DCFE, 0x1032_5476, 0xC3D2_E1F0, ],
Wrapping(0x67452301),
Wrapping(0xEFCDAB89),
Wrapping(0x98BADCFE),
Wrapping(0x10325476),
Wrapping(0xC3D2E1F0),
],
byte_len: 0, byte_len: 0,
} }
} }
@@ -33,50 +25,65 @@ impl Default for Sha1 {
fn bytes_to_block(bytes: &[u8]) -> Block { fn bytes_to_block(bytes: &[u8]) -> Block {
assert_eq!(bytes.len(), BLOCK_BYTES); assert_eq!(bytes.len(), BLOCK_BYTES);
// safety: unwraps work because BLOCK_BYTES length is asserted // safety: unwraps work because BLOCK_BYTES length is asserted
let b: Vec<Wu32> = bytes let b: Vec<u32> = bytes
.chunks(4) .chunks(4)
.map(|c| Wrapping(u32::from_be_bytes(c.try_into().unwrap()))) .map(|c| u32::from_be_bytes(c.try_into().unwrap()))
.collect(); .collect();
b.try_into().unwrap() b.try_into().unwrap()
} }
fn rol(value: Wu32, bits: usize) -> Wu32 { fn rol(value: u32, bits: usize) -> u32 {
return (value << bits) | (value >> (32 - bits)); return (value << bits) | (value >> (32 - bits));
} }
fn blk(block: &Block, i: usize) -> Wu32 { fn blk(block: &Block, i: usize) -> u32 {
return rol( return rol(
block[(i + 13) & 15] ^ block[(i + 8) & 15] ^ block[(i + 2) & 15] ^ block[i], block[(i + 13) & 15] ^ block[(i + 8) & 15] ^ block[(i + 2) & 15] ^ block[i],
1, 1,
); );
} }
fn r0(block: &Block, v: Wu32, w: &mut Wu32, x: Wu32, y: Wu32, z: &mut Wu32, i: usize) { fn r0(block: &Block, v: u32, w: &mut u32, x: u32, y: u32, z: &mut u32, i: usize) {
*z = *z + ((*w & (x ^ y)) ^ y) + block[i] + Wrapping(0x5a827999) + rol(v, 5); *z = z.wrapping_add((*w & (x ^ y)) ^ y)
.wrapping_add(block[i])
.wrapping_add(0x5a82_7999)
.wrapping_add(rol(v, 5));
*w = rol(*w, 30); *w = rol(*w, 30);
} }
fn r1(block: &mut Block, v: Wu32, w: &mut Wu32, x: Wu32, y: Wu32, z: &mut Wu32, i: usize) { fn r1(block: &mut Block, v: u32, w: &mut u32, x: u32, y: u32, z: &mut u32, i: usize) {
block[i] = blk(block, i); block[i] = blk(block, i);
*z = *z + ((*w & (x ^ y)) ^ y) + block[i] + Wrapping(0x5a827999) + rol(v, 5); *z = z.wrapping_add((*w & (x ^ y)) ^ y)
.wrapping_add(block[i])
.wrapping_add(0x5a82_7999)
.wrapping_add(rol(v, 5));
*w = rol(*w, 30); *w = rol(*w, 30);
} }
fn r2(block: &mut Block, v: Wu32, w: &mut Wu32, x: Wu32, y: Wu32, z: &mut Wu32, i: usize) { fn r2(block: &mut Block, v: u32, w: &mut u32, x: u32, y: u32, z: &mut u32, i: usize) {
block[i] = blk(block, i); block[i] = blk(block, i);
*z += (*w ^ x ^ y) + block[i] + Wrapping(0x6ed9eba1) + rol(v, 5); *z = z.wrapping_add(*w ^ x ^ y)
.wrapping_add(block[i])
.wrapping_add(0x6ed9_eba1)
.wrapping_add(rol(v, 5));
*w = rol(*w, 30); *w = rol(*w, 30);
} }
fn r3(block: &mut Block, v: Wu32, w: &mut Wu32, x: Wu32, y: Wu32, z: &mut Wu32, i: usize) { fn r3(block: &mut Block, v: u32, w: &mut u32, x: u32, y: u32, z: &mut u32, i: usize) {
block[i] = blk(block, i); block[i] = blk(block, i);
*z += (((*w | x) & y) | (*w & x)) + block[i] + Wrapping(0x8f1bbcdc) + rol(v, 5); *z = z.wrapping_add(((*w | x) & y) | (*w & x))
.wrapping_add(block[i])
.wrapping_add(0x8f1b_bcdc)
.wrapping_add(rol(v, 5));
*w = rol(*w, 30); *w = rol(*w, 30);
} }
fn r4(block: &mut Block, v: Wu32, w: &mut Wu32, x: Wu32, y: Wu32, z: &mut Wu32, i: usize) { fn r4(block: &mut Block, v: u32, w: &mut u32, x: u32, y: u32, z: &mut u32, i: usize) {
block[i] = blk(block, i); block[i] = blk(block, i);
*z = *z + (*w ^ x ^ y) + block[i] + Wrapping(0xca62c1d6) + rol(v, 5); *z = z.wrapping_add(*w ^ x ^ y)
.wrapping_add(block[i])
.wrapping_add(0xca62_c1d6)
.wrapping_add(rol(v, 5));
*w = rol(*w, 30); *w = rol(*w, 30);
} }
@@ -88,7 +95,7 @@ impl Sha1 {
pub fn fix(&mut self, fixate: [u32; STATE_LEN], byte_len: u64) { pub fn fix(&mut self, fixate: [u32; STATE_LEN], byte_len: u64) {
for i in 0..5 { for i in 0..5 {
self.h[i] = Wrapping(fixate[i]); self.h[i] = fixate[i];
} }
self.byte_len = byte_len; self.byte_len = byte_len;
} }
@@ -232,7 +239,7 @@ impl Sha1 {
for bytes in final_bytes.chunks(BLOCK_BYTES) { for bytes in final_bytes.chunks(BLOCK_BYTES) {
self.update(&bytes); self.update(&bytes);
} }
Bytes(self.h.iter().map(|i| i.0.to_be_bytes()).flatten().collect()) Bytes(self.h.iter().map(|i| i.to_be_bytes()).flatten().collect())
} }
} }