1
0

feat(p30): finish md4 algo

This commit is contained in:
2025-09-29 22:50:14 +08:00
parent b3ce1dfaef
commit e75379f234
2 changed files with 311 additions and 0 deletions

7
problems/p30/Cargo.toml Normal file
View File

@@ -0,0 +1,7 @@
[package]
name = "p30"
version = "0.1.0"
edition = "2024"
[dependencies]
hex = { workspace = true }

304
problems/p30/src/main.rs Normal file
View File

@@ -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 为 AAB 为 BBC 为 CCD 为 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 卷半数值算法第二版1981Addison-Wesley。表 2第 660 页。
//
// 3.5 步骤 5. 输出
//
// 输出的消息摘要为 A、B、C、D。即从 A 的低字节开始,到 D 的高字节结束。
//
// 这就是 MD4 的完整描述。附录中给出了 C 语言的参考实现。
///进行md4的bits填充和长度填充
fn md4_padding(data: &[u8]) -> Vec<u8> {
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}");
}