1use crate::prelude::*;
2
3use hmac::Mac as _;
4use sha1::Digest as _;
5
6fn hkdf_expand_sha256(prk: &[u8], info: &[u8], out: &mut [u8]) -> Result<()> {
7 const HASH_LEN: usize = 32;
8 if out.len() > 255 * HASH_LEN {
9 return Err(Error::HkdfExpand);
10 }
11 let mut prev: [u8; HASH_LEN] = [0; HASH_LEN];
12 let mut prev_len = 0;
13 let mut counter: u8 = 1;
14 let mut offset = 0;
15 while offset < out.len() {
16 let mut mac = hmac::Hmac::<sha2::Sha256>::new_from_slice(prk)
17 .map_err(|_| Error::HkdfExpand)?;
18 mac.update(&prev[..prev_len]);
19 mac.update(info);
20 mac.update(&[counter]);
21 let block = mac.finalize().into_bytes();
22 let take = (out.len() - offset).min(HASH_LEN);
23 out[offset..offset + take].copy_from_slice(&block[..take]);
24 prev.copy_from_slice(&block);
25 prev_len = HASH_LEN;
26 offset += take;
27 counter = counter.wrapping_add(1);
28 }
29 Ok(())
30}
31
32pub struct Identity {
33 pub email: String,
34 pub keys: crate::locked::Keys,
35 pub master_password_hash: crate::locked::PasswordHash,
36}
37
38impl Identity {
39 pub fn new(
40 email: &str,
41 password: &crate::locked::Password,
42 kdf: crate::api::KdfType,
43 iterations: u32,
44 memory: Option<u32>,
45 parallelism: Option<u32>,
46 ) -> Result<Self> {
47 let email = email.trim().to_lowercase();
48
49 let iterations = std::num::NonZeroU32::new(iterations)
50 .ok_or(Error::Pbkdf2ZeroIterations)?;
51
52 let mut keys = crate::locked::Vec::new();
53 keys.extend(std::iter::repeat_n(0, 64));
54
55 let enc_key = &mut keys.data_mut()[0..32];
56
57 match kdf {
58 crate::api::KdfType::Pbkdf2 => {
59 pbkdf2::pbkdf2::<hmac::Hmac<sha2::Sha256>>(
60 password.password(),
61 email.as_bytes(),
62 iterations.get(),
63 enc_key,
64 )
65 .map_err(|_| Error::Pbkdf2)?;
66 }
67
68 crate::api::KdfType::Argon2id => {
69 let mut hasher = sha2::Sha256::new();
70 hasher.update(email.as_bytes());
71 let salt = hasher.finalize();
72
73 let argon2_config = argon2::Argon2::new(
74 argon2::Algorithm::Argon2id,
75 argon2::Version::V0x13,
76 argon2::Params::new(
77 memory.unwrap() * 1024,
78 iterations.get(),
79 parallelism.unwrap(),
80 Some(32),
81 )
82 .unwrap(),
83 );
84 argon2::Argon2::hash_password_into(
85 &argon2_config,
86 password.password(),
87 &salt,
88 enc_key,
89 )
90 .map_err(|_| Error::Argon2)?;
91 }
92 }
93
94 let mut hash = crate::locked::Vec::new();
95 hash.extend(std::iter::repeat_n(0, 32));
96 pbkdf2::pbkdf2::<hmac::Hmac<sha2::Sha256>>(
97 enc_key,
98 password.password(),
99 1,
100 hash.data_mut(),
101 )
102 .map_err(|_| Error::Pbkdf2)?;
103
104 let mut prk: [u8; 32] = [0; 32];
105 prk.copy_from_slice(enc_key);
106 hkdf_expand_sha256(&prk, b"enc", enc_key)?;
107 let mac_key = &mut keys.data_mut()[32..64];
108 hkdf_expand_sha256(&prk, b"mac", mac_key)?;
109
110 let keys = crate::locked::Keys::new(keys);
111 let master_password_hash = crate::locked::PasswordHash::new(hash);
112
113 Ok(Self {
114 email: email.clone(),
115 keys,
116 master_password_hash,
117 })
118 }
119}
120
121#[test]
122fn test_hkdf_expand_sha256_rfc5869_a1() {
123 let prk: [u8; 32] = [
125 0x07, 0x77, 0x09, 0x36, 0x2c, 0x2e, 0x32, 0xdf, 0x0d, 0xdc, 0x3f,
126 0x0d, 0xc4, 0x7b, 0xba, 0x63, 0x90, 0xb6, 0xc7, 0x3b, 0xb5, 0x0f,
127 0x9c, 0x31, 0x22, 0xec, 0x84, 0x4a, 0xd7, 0xc2, 0xb3, 0xe5,
128 ];
129 let info: [u8; 10] =
130 [0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9];
131 let expected: [u8; 42] = [
132 0x3c, 0xb2, 0x5f, 0x25, 0xfa, 0xac, 0xd5, 0x7a, 0x90, 0x43, 0x4f,
133 0x64, 0xd0, 0x36, 0x2f, 0x2a, 0x2d, 0x2d, 0x0a, 0x90, 0xcf, 0x1a,
134 0x5a, 0x4c, 0x5d, 0xb0, 0x2d, 0x56, 0xec, 0xc4, 0xc5, 0xbf, 0x34,
135 0x00, 0x72, 0x08, 0xd5, 0xb8, 0x87, 0x18, 0x58, 0x65,
136 ];
137 let mut out = [0_u8; 42];
138 hkdf_expand_sha256(&prk, &info, &mut out).unwrap();
139 assert_eq!(out, expected);
140}
141
142#[test]
143fn test_hkdf_expand_sha256_rfc5869_a2() {
144 let prk: [u8; 32] = [
147 0x06, 0xa6, 0xb8, 0x8c, 0x58, 0x53, 0x36, 0x1a, 0x06, 0x10, 0x4c,
148 0x9c, 0xeb, 0x35, 0xb4, 0x5c, 0xef, 0x76, 0x00, 0x14, 0x90, 0x46,
149 0x71, 0x01, 0x4a, 0x19, 0x3f, 0x40, 0xc1, 0x5f, 0xc2, 0x44,
150 ];
151 let info: [u8; 80] = [
152 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba,
153 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5,
154 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0,
155 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb,
156 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6,
157 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1,
158 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc,
159 0xfd, 0xfe, 0xff,
160 ];
161 let expected: [u8; 82] = [
162 0xb1, 0x1e, 0x39, 0x8d, 0xc8, 0x03, 0x27, 0xa1, 0xc8, 0xe7, 0xf7,
163 0x8c, 0x59, 0x6a, 0x49, 0x34, 0x4f, 0x01, 0x2e, 0xda, 0x2d, 0x4e,
164 0xfa, 0xd8, 0xa0, 0x50, 0xcc, 0x4c, 0x19, 0xaf, 0xa9, 0x7c, 0x59,
165 0x04, 0x5a, 0x99, 0xca, 0xc7, 0x82, 0x72, 0x71, 0xcb, 0x41, 0xc6,
166 0x5e, 0x59, 0x0e, 0x09, 0xda, 0x32, 0x75, 0x60, 0x0c, 0x2f, 0x09,
167 0xb8, 0x36, 0x77, 0x93, 0xa9, 0xac, 0xa3, 0xdb, 0x71, 0xcc, 0x30,
168 0xc5, 0x81, 0x79, 0xec, 0x3e, 0x87, 0xc1, 0x4c, 0x01, 0xd5, 0xc1,
169 0xf3, 0x43, 0x4f, 0x1d, 0x87,
170 ];
171 let mut out = [0_u8; 82];
172 hkdf_expand_sha256(&prk, &info, &mut out).unwrap();
173 assert_eq!(out, expected);
174}
175
176#[test]
177fn test_hkdf_expand_sha256_rfc5869_a3() {
178 let prk: [u8; 32] = [
181 0x19, 0xef, 0x24, 0xa3, 0x2c, 0x71, 0x7b, 0x16, 0x7f, 0x33, 0xa9,
182 0x1d, 0x6f, 0x64, 0x8b, 0xdf, 0x96, 0x59, 0x67, 0x76, 0xaf, 0xdb,
183 0x63, 0x77, 0xac, 0x43, 0x4c, 0x1c, 0x29, 0x3c, 0xcb, 0x04,
184 ];
185 let expected: [u8; 42] = [
186 0x8d, 0xa4, 0xe7, 0x75, 0xa5, 0x63, 0xc1, 0x8f, 0x71, 0x5f, 0x80,
187 0x2a, 0x06, 0x3c, 0x5a, 0x31, 0xb8, 0xa1, 0x1f, 0x5c, 0x5e, 0xe1,
188 0x87, 0x9e, 0xc3, 0x45, 0x4e, 0x5f, 0x3c, 0x73, 0x8d, 0x2d, 0x9d,
189 0x20, 0x13, 0x95, 0xfa, 0xa4, 0xb6, 0x1a, 0x96, 0xc8,
190 ];
191 let mut out = [0_u8; 42];
192 hkdf_expand_sha256(&prk, &[], &mut out).unwrap();
193 assert_eq!(out, expected);
194}
195
196#[test]
197fn test_hkdf_expand_sha256_rejects_oversize_output() {
198 let prk = [0u8; 32];
200 let mut out = vec![0_u8; 8161];
201 assert!(hkdf_expand_sha256(&prk, &[], &mut out).is_err());
202}