From e75379f2347ef8f42fca23adeb5e80c1e1891ec3 Mon Sep 17 00:00:00 2001 From: sangge <2251250136@qq.com> Date: Mon, 29 Sep 2025 22:50:14 +0800 Subject: [PATCH] feat(p30): finish md4 algo --- problems/p30/Cargo.toml | 7 + problems/p30/src/main.rs | 304 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 311 insertions(+) create mode 100644 problems/p30/Cargo.toml create mode 100644 problems/p30/src/main.rs diff --git a/problems/p30/Cargo.toml b/problems/p30/Cargo.toml new file mode 100644 index 0000000..237ecf0 --- /dev/null +++ b/problems/p30/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "p30" +version = "0.1.0" +edition = "2024" + +[dependencies] +hex = { workspace = true } diff --git a/problems/p30/src/main.rs b/problems/p30/src/main.rs new file mode 100644 index 0000000..2c8fcfb --- /dev/null +++ b/problems/p30/src/main.rs @@ -0,0 +1,304 @@ +// MD4 算法描述 +// +// 我们假设有一个 b 位的消息作为输入,并且我们希望找到它的消息摘要。 +// 这里 b 是一个任意的非负整数;b 可以为零,不必是 8 的倍数, +// 并且可以任意大。我们设想消息的各个位如下所示: +// +// m_0 m_1 ... m_{b-1} +// +// 以下五个步骤用于计算消息的摘要: +// +// 3.1 步骤 1. 添加填充位 +// +// 对消息进行“填充”(扩展),使其长度(以位为单位)模 512 余 448。 +// 也就是说,消息被扩展,使其长度距离 512 位的倍数还差 64 位。 +// 无论消息长度是否已经模 512 余 448,都要进行填充。 +// +// 填充方法如下:先在消息末尾添加一个“1”位,然后添加“0”位, +// 直到填充后的消息长度模 512 余 448。总共至少添加 1 位,最多添加 512 位。 +// +// 3.2 步骤 2. 添加长度 +// +// 在上一步结果后,添加一个 64 位的 b(即填充前消息的长度)的表示。 +// 如果 b 大于 2^64,则只使用 b 的低 64 位。 +// (这些位作为两个 32 位字添加,且低位字在前,遵循之前的约定。) +// +// 此时,经过填充和添加长度后的消息,其长度正好是 512 位的倍数。 +// 等价地说,该消息长度正好是 16 个(32 位)字的倍数。 +// 令 M[0 ... N-1] 表示结果消息的各个字,其中 N 是 16 的倍数。 +// +// 3.3 步骤 3. 初始化 MD 缓冲区 +// +// 使用一个四字缓冲区(A,B,C,D)来计算消息摘要。 +// 这里每个 A、B、C、D 都是 32 位寄存器。 +// 这些寄存器初始化为如下十六进制值(低字节在前): +// +// 字 A: 01 23 45 67 +// 字 B: 89 ab cd ef +// 字 C: fe dc ba 98 +// 字 D: 76 54 32 10 +// +// 3.4 步骤 4. 以 16 字块处理消息 +// +// 首先定义三个辅助函数,每个函数输入三个 32 位字,输出一个 32 位字。 +// +// F(X,Y,Z) = XY 或 非(X)Z +// G(X,Y,Z) = XY 或 XZ 或 YZ +// H(X,Y,Z) = X 异或 Y 异或 Z +// +// 在每个位上,F 充当条件判断:如果 X 为 1,则取 Y,否则取 Z。 +// F 也可以用 + 代替或,因为 XY 和 非(X)Z 在同一位上不会同时为 1。 +// G 在每个位上充当多数函数:如果 X、Y、Z 至少有两个为 1, +// 则 G 的该位为 1,否则为 0。值得注意的是, +// 如果 X、Y、Z 的各位独立且均匀分布, +// 则 f(X,Y,Z) 的每一位也将独立且均匀分布,g(X,Y,Z) 亦然。 +// H 是按位异或(奇偶)函数,性质与 F、G 类似。 +// +// 执行如下操作: +// +// 处理每个 16 字块。 +// 对 i = 0 到 N/16-1 执行: +// +// /* 将第 i 块复制到 X。 */ +// 对 j = 0 到 15 执行: +// 令 X[j] = M[i*16+j]。 +// 结束 j 循环 +// +// /* 保存 A 为 AA,B 为 BB,C 为 CC,D 为 DD。 */ +// AA = A +// BB = B +// CC = C +// DD = D +// +// /* 第一轮。 */ +// /* 记 [abcd k s] 表示操作 +// a = (a + F(b,c,d) + X[k]) 循环左移 s 位。 */ +// /* 执行以下 16 个操作。 */ +// [ABCD 0 3] [DABC 1 7] [CDAB 2 11] [BCDA 3 19] +// [ABCD 4 3] [DABC 5 7] [CDAB 6 11] [BCDA 7 19] +// [ABCD 8 3] [DABC 9 7] [CDAB 10 11] [BCDA 11 19] +// [ABCD 12 3] [DABC 13 7] [CDAB 14 11] [BCDA 15 19] +// +// /* 第二轮。 */ +// /* 记 [abcd k s] 表示操作 +// a = (a + G(b,c,d) + X[k] + 5A827999) 循环左移 s 位。 */ +// /* 执行以下 16 个操作。 */ +// [ABCD 0 3] [DABC 4 5] [CDAB 8 9] [BCDA 12 13] +// [ABCD 1 3] [DABC 5 5] [CDAB 9 9] [BCDA 13 13] +// [ABCD 2 3] [DABC 6 5] [CDAB 10 9] [BCDA 14 13] +// [ABCD 3 3] [DABC 7 5] [CDAB 11 9] [BCDA 15 13] +// /* 第三轮。 */ +// /* 记 [abcd k s] 表示操作 +// a = (a + H(b,c,d) + X[k] + 6ED9EBA1) 循环左移 s 位。 */ +// /* 执行以下 16 个操作。 */ +// [ABCD 0 3] [DABC 8 9] [CDAB 4 11] [BCDA 12 15] +// [ABCD 2 3] [DABC 10 9] [CDAB 6 11] [BCDA 14 15] +// [ABCD 1 3] [DABC 9 9] [CDAB 5 11] [BCDA 13 15] +// [ABCD 3 3] [DABC 11 9] [CDAB 7 11] [BCDA 15 15] +// +// /* 然后执行如下加法(即每个寄存器加上本块处理前的值): */ +// A = A + AA +// B = B + BB +// C = C + CC +// D = D + DD +// +// 结束 i 循环 +// +// 注:值 5A..99 是一个 32 位十六进制常量,高位在前。 +// 该常量表示 2 的平方根。其八进制值为 013240474631。 +// +// 值 6E..A1 是一个 32 位十六进制常量,高位在前。 +// 该常量表示 3 的平方根。其八进制值为 015666365641。 +// +// 参考 Knuth,《计算机程序设计艺术》第 2 卷(半数值算法),第二版(1981),Addison-Wesley。表 2,第 660 页。 +// +// 3.5 步骤 5. 输出 +// +// 输出的消息摘要为 A、B、C、D。即从 A 的低字节开始,到 D 的高字节结束。 +// +// 这就是 MD4 的完整描述。附录中给出了 C 语言的参考实现。 + +///进行md4的bits填充和长度填充 +fn md4_padding(data: &[u8]) -> Vec { + let length = data.len(); + let length_mod = length % 64; + let padding_length = match length_mod { + 0..56 => 56 - length_mod, + 56..64 => 64 - length_mod + 56, + _ => panic!("Shouldn't happen"), + }; + let zero_padding_block_length = padding_length - 1; + let mut buf = data.to_vec(); + buf.extend(vec![128]); + buf.extend(vec![0; zero_padding_block_length]); + buf.extend(((length * 8) as u64).to_le_bytes()); + buf +} +#[test] +fn test_md4_padding() { + // Test with empty input + let padded = md4_padding(&[]); + assert_eq!(padded.len(), 64); + assert_eq!(padded[0], 0x80); + assert_eq!(&padded[56..], &(0u64.to_le_bytes())); + + // Test with 1 byte input + let padded = md4_padding(&[0x01]); + assert_eq!(padded.len(), 64); + assert_eq!(padded[1], 0x80); + assert_eq!(&padded[2..56], &[0; 54]); + assert_eq!(&padded[56..], &(8u64.to_le_bytes())); + + // Test with 55 bytes input (should pad to 64 bytes) + let data = vec![0xAA; 55]; + let padded = md4_padding(&data); + assert_eq!(padded.len(), 64); + assert_eq!(padded[55], 0x80); + assert_eq!(&padded[56..], &(440u64.to_le_bytes())); + + // Test with 56 bytes input (should pad to 128 bytes) + let data = vec![0xBB; 56]; + let padded = md4_padding(&data); + assert_eq!(padded.len(), 128); + assert_eq!(padded[56], 0x80); + assert_eq!(&padded[57..120], &[0; 63]); + assert_eq!(&padded[120..], &(448u64.to_le_bytes())); +} + +// F函数:条件选择函数 +fn f_func(x: &u32, y: &u32, z: &u32) -> u32 { + (x & y) | (!x & z) +} + +// G函数:多数决定函数 +fn g_func(x: &u32, y: &u32, z: &u32) -> u32 { + (x & y) | (x & z) | (y & z) +} + +// H函数:奇偶校验函数 +fn h_func(x: &u32, y: &u32, z: &u32) -> u32 { + x ^ y ^ z +} + +fn first_round(a: &mut u32, b: &u32, c: &u32, d: &u32, x_k: u32, s: u32) { + *a = (a.wrapping_add(f_func(b, c, d)).wrapping_add(x_k)).rotate_left(s); +} + +fn second_round(a: &mut u32, b: &u32, c: &u32, d: &u32, x_k: u32, s: u32) { + *a = (a + .wrapping_add(g_func(b, c, d)) + .wrapping_add(x_k) + .wrapping_add(0x5a827999)) + .rotate_left(s); +} + +fn third_round(a: &mut u32, b: &u32, c: &u32, d: &u32, x_k: u32, s: u32) { + *a = (a + .wrapping_add(h_func(b, c, d)) + .wrapping_add(x_k) + .wrapping_add(0x6ed9eba1)) + .rotate_left(s); +} + +fn md4(data: &[u8]) -> [u8; 16] { + let data_with_padding = md4_padding(data); + let mut a: u32 = 0x67452301; + let mut b: u32 = 0xefcdab89; + let mut c: u32 = 0x98badcfe; + let mut d: u32 = 0x10325476; + + for i in 0..data_with_padding.len() / 64 { + // 直接转成 u32 数组 + let mut x = [0u32; 16]; + (0..16).for_each(|j| { + let offset = i * 64 + j * 4; + x[j] = u32::from_le_bytes([ + data_with_padding[offset], + data_with_padding[offset + 1], + data_with_padding[offset + 2], + data_with_padding[offset + 3], + ]); + }); + + let aa = a; + let bb = b; + let cc = c; + let dd = d; + + // Round 1 - 16 个操作 + first_round(&mut a, &b, &c, &d, x[0], 3); + first_round(&mut d, &a, &b, &c, x[1], 7); + first_round(&mut c, &d, &a, &b, x[2], 11); + first_round(&mut b, &c, &d, &a, x[3], 19); + + first_round(&mut a, &b, &c, &d, x[4], 3); + first_round(&mut d, &a, &b, &c, x[5], 7); + first_round(&mut c, &d, &a, &b, x[6], 11); + first_round(&mut b, &c, &d, &a, x[7], 19); + + first_round(&mut a, &b, &c, &d, x[8], 3); + first_round(&mut d, &a, &b, &c, x[9], 7); + first_round(&mut c, &d, &a, &b, x[10], 11); + first_round(&mut b, &c, &d, &a, x[11], 19); + + first_round(&mut a, &b, &c, &d, x[12], 3); + first_round(&mut d, &a, &b, &c, x[13], 7); + first_round(&mut c, &d, &a, &b, x[14], 11); + first_round(&mut b, &c, &d, &a, x[15], 19); + + // Round 2 + let order2 = [0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15]; + for k in 0..16 { + let s = match k % 4 { + 0 => 3, + 1 => 5, + 2 => 9, + _ => 13, + }; + match k % 4 { + 0 => second_round(&mut a, &b, &c, &d, x[order2[k]], s), + 1 => second_round(&mut d, &a, &b, &c, x[order2[k]], s), + 2 => second_round(&mut c, &d, &a, &b, x[order2[k]], s), + _ => second_round(&mut b, &c, &d, &a, x[order2[k]], s), + } + } + + // Round 3 + let order3 = [0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15]; + for k in 0..16 { + let s = match k % 4 { + 0 => 3, + 1 => 9, + 2 => 11, + _ => 15, + }; + match k % 4 { + 0 => third_round(&mut a, &b, &c, &d, x[order3[k]], s), + 1 => third_round(&mut d, &a, &b, &c, x[order3[k]], s), + 2 => third_round(&mut c, &d, &a, &b, x[order3[k]], s), + _ => third_round(&mut b, &c, &d, &a, x[order3[k]], s), + } + } + + // 添加到原值 + a = a.wrapping_add(aa); + b = b.wrapping_add(bb); + c = c.wrapping_add(cc); + d = d.wrapping_add(dd); + } + + // 输出摘要 + let mut digest = [0u8; 16]; + digest[..4].copy_from_slice(&a.to_le_bytes()); + digest[4..8].copy_from_slice(&b.to_le_bytes()); + digest[8..12].copy_from_slice(&c.to_le_bytes()); + digest[12..].copy_from_slice(&d.to_le_bytes()); + digest +} + +fn main() { + let plaintext = "Hello, World!".as_bytes(); + let digest = md4(plaintext); + let digest = hex::encode(digest); + println!("{digest}"); +}