1use bitcoin_hashes::{hmac, sha512, Hash, HashEngine};
2
3const SALT_PREFIX: &'static str = "mnemonic";
4
5fn mnemonic_byte_len<M>(mnemonic: M) -> usize
7 where M: Iterator<Item = &'static str> + Clone,
8{
9 let mut len = 0;
10 for (i, word) in mnemonic.enumerate() {
11 if i > 0 {
12 len += 1;
13 }
14 len += word.len();
15 }
16 len
17}
18
19fn mnemonic_write_into<M>(mnemonic: M, engine: &mut sha512::HashEngine)
21 where M: Iterator<Item = &'static str> + Clone,
22{
23 for (i, word) in mnemonic.enumerate() {
24 if i > 0 {
25 engine.input(" ".as_bytes());
26 }
27 engine.input(word.as_bytes());
28 }
29}
30
31fn create_hmac_engine<M>(mnemonic: M) -> hmac::HmacEngine<sha512::Hash>
35 where M: Iterator<Item = &'static str> + Clone,
36{
37 let mut ipad = [0x36u8; 128];
39 let mut opad = [0x5cu8; 128];
40 let mut iengine = sha512::Hash::engine();
41 let mut oengine = sha512::Hash::engine();
42
43 if mnemonic_byte_len(mnemonic.clone()) > sha512::HashEngine::BLOCK_SIZE {
44 let hash = {
45 let mut engine = sha512::Hash::engine();
46 mnemonic_write_into(mnemonic, &mut engine);
47 sha512::Hash::from_engine(engine)
48 };
49
50 for (b_i, b_h) in ipad.iter_mut().zip(&hash[..]) {
51 *b_i ^= *b_h;
52 }
53 for (b_o, b_h) in opad.iter_mut().zip(&hash[..]) {
54 *b_o ^= *b_h;
55 }
56 } else {
57 let mut cursor = 0;
59 for (i, word) in mnemonic.enumerate() {
60 if i > 0 {
61 ipad[cursor] ^= ' ' as u8;
62 opad[cursor] ^= ' ' as u8;
63 cursor += 1;
64 }
65 for (b_i, b_h) in ipad.iter_mut().skip(cursor).zip(word.as_bytes()) {
66 *b_i ^= *b_h;
67 }
68 for (b_o, b_h) in opad.iter_mut().skip(cursor).zip(word.as_bytes()) {
69 *b_o ^= *b_h;
70 }
71 cursor += word.len();
72 assert!(cursor <= sha512::HashEngine::BLOCK_SIZE, "mnemonic_byte_len is broken");
73 }
74 };
75
76 iengine.input(&ipad[..sha512::HashEngine::BLOCK_SIZE]);
77 oengine.input(&opad[..sha512::HashEngine::BLOCK_SIZE]);
78 hmac::HmacEngine::from_inner_engines(iengine, oengine)
79}
80
81#[inline]
83fn u32_to_array_be(val: u32) -> [u8; 4] {
84 let mut res = [0; 4];
85 for i in 0..4 {
86 res[i] = ((val >> (4 - i - 1) * 8) & 0xff) as u8;
87 }
88 res
89}
90
91#[inline]
92fn xor(res: &mut [u8], salt: &[u8]) {
93 debug_assert!(salt.len() >= res.len(), "length mismatch in xor");
94
95 res.iter_mut().zip(salt.iter()).for_each(|(a, b)| *a ^= b);
96}
97
98pub(crate) fn pbkdf2<M>(mnemonic: M, unprefixed_salt: &[u8], c: usize, res: &mut [u8])
100 where M: Iterator<Item = &'static str> + Clone,
101{
102 let prf = create_hmac_engine(mnemonic);
103
104 for (i, chunk) in res.chunks_mut(sha512::Hash::LEN).enumerate() {
105 for v in chunk.iter_mut() {
106 *v = 0;
107 }
108
109 let mut salt = {
110 let mut prfc = prf.clone();
111 prfc.input(SALT_PREFIX.as_bytes());
112 prfc.input(unprefixed_salt);
113 prfc.input(&u32_to_array_be((i + 1) as u32));
114
115 let salt = hmac::Hmac::from_engine(prfc).to_byte_array();
116 xor(chunk, &salt);
117 salt
118 };
119
120 for _ in 1..c {
121 let mut prfc = prf.clone();
122 prfc.input(&salt);
123 salt = hmac::Hmac::from_engine(prfc).to_byte_array();
124
125 xor(chunk, &salt);
126 }
127 }
128}