1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
use crate::{Error, Result, SecretBytes};
use digest::{ExtendableOutput, Update, XofReader};
use tor_llcrypto::d::{Sha1, Sha256, Shake256};
use zeroize::Zeroizing;
pub(crate) trait Kdf {
fn derive(&self, seed: &[u8], n_bytes: usize) -> Result<SecretBytes>;
}
pub(crate) struct LegacyKdf {
idx: u8,
}
pub(crate) struct Ntor1Kdf<'a, 'b> {
t_key: &'a [u8],
m_expand: &'b [u8],
}
pub(crate) struct ShakeKdf();
impl LegacyKdf {
pub(crate) fn new(idx: u8) -> Self {
LegacyKdf { idx }
}
}
impl Kdf for LegacyKdf {
fn derive(&self, seed: &[u8], n_bytes: usize) -> Result<SecretBytes> {
use digest::Digest;
let mut result = Zeroizing::new(Vec::with_capacity(n_bytes + Sha1::output_size()));
let mut k = self.idx;
if n_bytes > Sha1::output_size() * (256 - (k as usize)) {
return Err(Error::InvalidOutputLength);
}
while result.len() < n_bytes {
let mut d = Sha1::new();
Digest::update(&mut d, seed);
Digest::update(&mut d, &[k]);
result.extend(d.finalize());
k += 1;
}
result.truncate(n_bytes);
Ok(result)
}
}
impl<'a, 'b> Ntor1Kdf<'a, 'b> {
pub(crate) fn new(t_key: &'a [u8], m_expand: &'b [u8]) -> Self {
Ntor1Kdf { t_key, m_expand }
}
}
impl Kdf for Ntor1Kdf<'_, '_> {
fn derive(&self, seed: &[u8], n_bytes: usize) -> Result<SecretBytes> {
let hkdf = hkdf::Hkdf::<Sha256>::new(Some(self.t_key), seed);
let mut result = Zeroizing::new(vec![0; n_bytes]);
hkdf.expand(self.m_expand, &mut result[..])
.map_err(|_| Error::InvalidOutputLength)?;
Ok(result)
}
}
impl ShakeKdf {
pub(crate) fn new() -> Self {
ShakeKdf()
}
}
impl Kdf for ShakeKdf {
fn derive(&self, seed: &[u8], n_bytes: usize) -> Result<SecretBytes> {
let mut xof = Shake256::default();
xof.update(seed);
let mut result = Zeroizing::new(vec![0; n_bytes]);
xof.finalize_xof().read(&mut result);
Ok(result)
}
}
#[cfg(test)]
mod test {
#![allow(clippy::unwrap_used)]
use super::*;
use hex_literal::hex;
#[test]
fn clearbox_tap_kdf() {
use digest::Digest;
let input = b"here is an example key seed that we will expand";
let result = LegacyKdf::new(6).derive(input, 99).unwrap();
let mut expect_result = Vec::new();
let mut k0: Vec<u8> = Vec::new();
k0.extend(&input[..]);
for x in 6..11 {
k0.push(x);
expect_result.extend(Sha1::digest(&k0));
k0.pop();
}
expect_result.truncate(99);
assert_eq!(&result[..], &expect_result[..]);
}
#[test]
fn testvec_tap_kdf() {
fn expand(b: &[u8]) -> SecretBytes {
LegacyKdf::new(0).derive(b, 100).unwrap()
}
let expect = hex!(
"5ba93c9db0cff93f52b521d7420e43f6eda2784fbf8b4530d8
d246dd74ac53a13471bba17941dff7c4ea21bb365bbeeaf5f2
c654883e56d11e43c44e9842926af7ca0a8cca12604f945414
f07b01e13da42c6cf1de3abfdea9b95f34687cbbe92b9a7383"
);
assert_eq!(&expand(&b""[..])[..], &expect[..]);
let expect = hex!(
"776c6214fc647aaa5f683c737ee66ec44f03d0372e1cce6922
7950f236ddf1e329a7ce7c227903303f525a8c6662426e8034
870642a6dabbd41b5d97ec9bf2312ea729992f48f8ea2d0ba8
3f45dfda1a80bdc8b80de01b23e3e0ffae099b3e4ccf28dc28"
);
assert_eq!(&expand(&b"Tor"[..])[..], &expect[..]);
let brunner_quote = b"AN ALARMING ITEM TO FIND ON A MONTHLY AUTO-DEBIT NOTICE";
let expect = hex!(
"a340b5d126086c3ab29c2af4179196dbf95e1c72431419d331
4844bf8f6afb6098db952b95581fb6c33625709d6f4400b8e7
ace18a70579fad83c0982ef73f89395bcc39493ad53a685854
daf2ba9b78733b805d9a6824c907ee1dba5ac27a1e466d4d10"
);
assert_eq!(&expand(&brunner_quote[..])[..], &expect[..]);
}
#[test]
fn fail_tap_kdf() {
let result = LegacyKdf::new(6).derive(&b"x"[..], 10000);
assert!(result.is_err());
}
#[test]
fn clearbox_ntor1_kdf() {
let input = b"another example key seed that we will expand";
let result = Ntor1Kdf::new(&b"key"[..], &b"expand"[..])
.derive(input, 99)
.unwrap();
let kdf = hkdf::Hkdf::<Sha256>::new(Some(&b"key"[..]), &input[..]);
let mut expect_result = vec![0_u8; 99];
kdf.expand(&b"expand"[..], &mut expect_result[..]).unwrap();
assert_eq!(&expect_result[..], &result[..]);
}
#[test]
fn testvec_ntor1_kdf() {
fn expand(b: &[u8]) -> SecretBytes {
let t_key = b"ntor-curve25519-sha256-1:key_extract";
let m_expand = b"ntor-curve25519-sha256-1:key_expand";
Ntor1Kdf::new(&t_key[..], &m_expand[..])
.derive(b, 100)
.unwrap()
}
let expect = hex!(
"5521492a85139a8d9107a2d5c0d9c91610d0f95989975ebee6
c02a4f8d622a6cfdf9b7c7edd3832e2760ded1eac309b76f8d
66c4a3c4d6225429b3a016e3c3d45911152fc87bc2de9630c3
961be9fdb9f93197ea8e5977180801926d3321fa21513e59ac"
);
assert_eq!(&expand(&b"Tor"[..])[..], &expect[..]);
let brunner_quote = b"AN ALARMING ITEM TO FIND ON YOUR CREDIT-RATING STATEMENT";
let expect = hex!(
"a2aa9b50da7e481d30463adb8f233ff06e9571a0ca6ab6df0f
b206fa34e5bc78d063fc291501beec53b36e5a0e434561200c
5f8bd13e0f88b3459600b4dc21d69363e2895321c06184879d
94b18f078411be70b767c7fc40679a9440a0c95ea83a23efbf"
);
assert_eq!(&expand(&brunner_quote[..])[..], &expect[..]);
}
#[test]
fn testvec_shake_kdf() {
let input = hex!(
"76891a7bcc6c04490035b743152f64a8dd2ea18ab472b8d36ecf45
858d0b0046"
);
let expected = hex!(
"e8447df87d01beeb724c9a2a38ab00fcc24e9bd17860e673b02122
2d621a7810e5d3"
);
let result = ShakeKdf::new().derive(&input[..], expected.len());
assert_eq!(&result.unwrap()[..], &expected[..]);
}
}