feat(p30): finish md4 algo
This commit is contained in:
7
problems/p30/Cargo.toml
Normal file
7
problems/p30/Cargo.toml
Normal 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
304
problems/p30/src/main.rs
Normal 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 为 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<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}");
|
||||
}
|
||||
Reference in New Issue
Block a user