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 ctr;
mod ecb;
mod md4;
mod mt19937;
mod mtcipher;
mod parser;
@@ -47,6 +48,8 @@ fn main() {
set4::challenge29();
set4::challenge30();
} else {
set4::challenge28();
set4::challenge29();
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() {
let cipher = utils::read_base64("data/25.txt");
@@ -139,28 +139,28 @@ pub fn challenge27() {
pub fn challenge28() {
let mut sha1 = sha1::Sha1::default();
let i1 = Bytes(vec![b'a'; 64]);
let e1 = Bytes::from_hex("0098ba824b5c16427bd7a1122a5a442a25ec644d");
let o1 = sha1.hash(&i1);
assert_eq!(e1, o1);
assert_eq!(
Bytes::from_hex("0098ba824b5c16427bd7a1122a5a442a25ec644d"),
sha1.hash(&Bytes(vec![b'a'; 64]))
);
sha1.reset();
let i2 = Bytes(vec![b'a'; 128]);
let e2 = Bytes::from_hex("ad5b3fdbcb526778c2839d2f151ea753995e26a0");
let o2 = sha1.hash(&i2);
assert_eq!(e2, o2);
assert_eq!(
Bytes::from_hex("ad5b3fdbcb526778c2839d2f151ea753995e26a0"),
sha1.hash(&Bytes(vec![b'a'; 128]))
);
sha1.reset();
let i3 = Bytes(vec![b'a'; 3]);
let e3 = Bytes::from_hex("7e240de74fb1ed08fa08d38063f6a6a91462a815");
let o3 = sha1.hash(&i3);
assert_eq!(e3, o3);
assert_eq!(
Bytes::from_hex("7e240de74fb1ed08fa08d38063f6a6a91462a815"),
sha1.hash(&Bytes(vec![b'a'; 3])),
);
sha1.reset();
let i4 = Bytes(vec![]);
let e4 = Bytes::from_hex("da39a3ee5e6b4b0d3255bfef95601890afd80709");
let o4 = sha1.hash(&i4);
assert_eq!(e4, o4);
assert_eq!(
Bytes::from_hex("da39a3ee5e6b4b0d3255bfef95601890afd80709"),
sha1.hash(&Bytes(vec![])),
);
// 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
@@ -232,5 +232,42 @@ pub fn challenge29() {
}
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");
}

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 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<u32>;
type Block = [Wu32; BLOCK_INTS];
type Block = [u32; BLOCK_INTS];
#[derive(Debug)]
pub struct Sha1 {
h: [Wu32; STATE_LEN],
h: [u32; STATE_LEN],
byte_len: u64,
}
@@ -18,13 +16,7 @@ impl Default for Sha1 {
#[inline]
fn default() -> Self {
Self {
h: [
Wrapping(0x67452301),
Wrapping(0xEFCDAB89),
Wrapping(0x98BADCFE),
Wrapping(0x10325476),
Wrapping(0xC3D2E1F0),
],
h: [ 0x6745_2301, 0xEFCD_AB89, 0x98BA_DCFE, 0x1032_5476, 0xC3D2_E1F0, ],
byte_len: 0,
}
}
@@ -33,50 +25,65 @@ impl Default for Sha1 {
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<Wu32> = bytes
let b: Vec<u32> = bytes
.chunks(4)
.map(|c| Wrapping(u32::from_be_bytes(c.try_into().unwrap())))
.map(|c| u32::from_be_bytes(c.try_into().unwrap()))
.collect();
b.try_into().unwrap()
}
fn rol(value: Wu32, bits: usize) -> Wu32 {
fn rol(value: u32, bits: usize) -> u32 {
return (value << bits) | (value >> (32 - bits));
}
fn blk(block: &Block, i: usize) -> Wu32 {
fn blk(block: &Block, i: usize) -> u32 {
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);
fn r0(block: &Block, v: u32, w: &mut u32, x: u32, y: u32, z: &mut u32, i: usize) {
*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);
}
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);
*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);
}
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);
*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);
}
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);
*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);
}
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);
*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);
}
@@ -88,7 +95,7 @@ impl Sha1 {
pub fn fix(&mut self, fixate: [u32; STATE_LEN], byte_len: u64) {
for i in 0..5 {
self.h[i] = Wrapping(fixate[i]);
self.h[i] = fixate[i];
}
self.byte_len = byte_len;
}
@@ -232,7 +239,7 @@ impl Sha1 {
for bytes in final_bytes.chunks(BLOCK_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())
}
}