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
use crate::blake2b;
use crate::constants::{
CRYPTO_GENERICHASH_BLAKE2B_PERSONALBYTES, CRYPTO_GENERICHASH_BLAKE2B_SALTBYTES,
CRYPTO_KDF_BLAKE2B_BYTES_MAX, CRYPTO_KDF_BLAKE2B_BYTES_MIN, CRYPTO_KDF_CONTEXTBYTES,
CRYPTO_KDF_KEYBYTES,
};
use crate::error::Error;
pub type Key = [u8; CRYPTO_KDF_KEYBYTES];
pub type Context = [u8; CRYPTO_KDF_CONTEXTBYTES];
pub fn crypto_kdf_keygen() -> Key {
use crate::rng::copy_randombytes;
let mut key = Key::default();
copy_randombytes(&mut key);
key
}
pub fn crypto_kdf_derive_from_key(
subkey: &mut [u8],
subkey_id: u64,
context: &Context,
main_key: &Key,
) -> Result<(), Error> {
if subkey.len() < CRYPTO_KDF_BLAKE2B_BYTES_MIN || subkey.len() > CRYPTO_KDF_BLAKE2B_BYTES_MAX {
Err(dryoc_error!(format!(
"invalid subkey length {}, should be at least {} and no more than {}",
subkey.len(),
CRYPTO_KDF_BLAKE2B_BYTES_MIN,
CRYPTO_KDF_BLAKE2B_BYTES_MAX
)))
} else {
let mut ctx_padded = [0u8; CRYPTO_GENERICHASH_BLAKE2B_PERSONALBYTES];
let mut salt = [0u8; CRYPTO_GENERICHASH_BLAKE2B_SALTBYTES];
ctx_padded[..CRYPTO_KDF_CONTEXTBYTES].copy_from_slice(context);
salt[..8].copy_from_slice(&subkey_id.to_le_bytes());
let state = blake2b::State::init(
CRYPTO_KDF_KEYBYTES as u8,
Some(main_key),
Some(&salt),
Some(&ctx_padded),
)?;
state.finalize(subkey)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_derive_key() {
use sodiumoxide::crypto::{kdf, secretbox};
let main_key = crypto_kdf_keygen();
let context = b"hello123";
for i in 0..20 {
let mut key = Key::default();
crypto_kdf_derive_from_key(&mut key, i, context, &main_key).expect("kdf failed");
let mut so_key = secretbox::Key([0; secretbox::KEYBYTES]);
kdf::derive_from_key(
&mut so_key.0[..],
i,
*context,
&kdf::blake2b::Key::from_slice(&main_key).expect("key failed"),
)
.expect("so kdf failed");
assert_eq!(so_key.0, key);
}
}
}