kanidm_lib_crypto/
crypt_md5.rs

1use md5::{Digest, Md5};
2use std::cmp::min;
3
4/// Maximium salt length.
5const MD5_MAGIC: &str = "$1$";
6const MD5_TRANSPOSE: &[u8] = b"\x0c\x06\x00\x0d\x07\x01\x0e\x08\x02\x0f\x09\x03\x05\x0a\x04\x0b";
7
8const CRYPT_HASH64: &[u8] = b"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
9
10pub fn md5_sha2_hash64_encode(bs: &[u8]) -> String {
11    let ngroups = bs.len().div_ceil(3);
12    let mut out = String::with_capacity(ngroups * 4);
13    for g in 0..ngroups {
14        let mut g_idx = g * 3;
15        let mut enc = 0u32;
16        for _ in 0..3 {
17            let b = (if g_idx < bs.len() { bs[g_idx] } else { 0 }) as u32;
18            enc >>= 8;
19            enc |= b << 16;
20            g_idx += 1;
21        }
22        for _ in 0..4 {
23            out.push(char::from_u32(CRYPT_HASH64[(enc & 0x3F) as usize] as u32).unwrap_or('!'));
24            enc >>= 6;
25        }
26    }
27    match bs.len() % 3 {
28        1 => {
29            out.pop();
30            out.pop();
31        }
32        2 => {
33            out.pop();
34        }
35        _ => (),
36    }
37    out
38}
39
40pub fn do_md5_crypt(pass: &[u8], salt: &[u8]) -> Vec<u8> {
41    let mut dgst_b = Md5::new();
42    dgst_b.update(pass);
43    dgst_b.update(salt);
44    dgst_b.update(pass);
45    let mut hash_b = dgst_b.finalize();
46
47    let mut dgst_a = Md5::new();
48    dgst_a.update(pass);
49    dgst_a.update(MD5_MAGIC.as_bytes());
50    dgst_a.update(salt);
51
52    let mut plen = pass.len();
53    while plen > 0 {
54        dgst_a.update(&hash_b[..min(plen, 16)]);
55        if plen < 16 {
56            break;
57        }
58        plen -= 16;
59    }
60
61    plen = pass.len();
62    while plen > 0 {
63        if plen & 1 == 0 {
64            dgst_a.update(&pass[..1])
65        } else {
66            dgst_a.update([0u8])
67        }
68        plen >>= 1;
69    }
70
71    let mut hash_a = dgst_a.finalize();
72
73    for r in 0..1000 {
74        let mut dgst_a = Md5::new();
75        if r % 2 == 1 {
76            dgst_a.update(pass);
77        } else {
78            dgst_a.update(hash_a);
79        }
80        if r % 3 > 0 {
81            dgst_a.update(salt);
82        }
83        if r % 7 > 0 {
84            dgst_a.update(pass);
85        }
86        if r % 2 == 0 {
87            dgst_a.update(pass);
88        } else {
89            dgst_a.update(hash_a);
90        }
91        hash_a = dgst_a.finalize();
92    }
93
94    for (i, &ti) in MD5_TRANSPOSE.iter().enumerate() {
95        hash_b[i] = hash_a[ti as usize];
96    }
97
98    md5_sha2_hash64_encode(&hash_b).into_bytes()
99}