1use hmac::{Hmac, Mac};
2use sha2::Sha256;
3
4use crate::*;
5
6pub fn hmac_sha256(key: &[u8], message: &[u8], output: &mut [u8; HMAC_SHA256_LENGTH]) -> Result<()> {
7 let mut mac = Hmac::<Sha256>::new_from_slice(key).map_err(|_| SigNetError::Crypto)?;
8 mac.update(message);
9 output.copy_from_slice(&mac.finalize().into_bytes());
10 Ok(())
11}
12
13pub fn hkdf_expand(prk: &[u8], info: &[u8], output: &mut [u8; DERIVED_KEY_LENGTH]) -> Result<()> {
14 let hk = hkdf::Hkdf::<Sha256>::from_prk(prk).map_err(|_| SigNetError::Crypto)?;
15 hk.expand(info, output).map_err(|_| SigNetError::Crypto)
16}
17
18pub fn derive_k0_from_passphrase(passphrase: &[u8], k0_output: &mut [u8; K0_KEY_LENGTH]) -> Result<()> {
19 if passphrase.is_empty() {
20 return Err(SigNetError::InvalidArgument);
21 }
22 pbkdf2::pbkdf2::<Hmac<Sha256>>(passphrase, PBKDF2_SALT, PBKDF2_ITERATIONS, k0_output)
23 .map_err(|_| SigNetError::Crypto)
24}
25
26pub fn derive_sender_key(k0: &[u8; K0_KEY_LENGTH], sender_key: &mut [u8; DERIVED_KEY_LENGTH]) -> Result<()> {
27 hkdf_expand(k0, HKDF_INFO_SENDER, sender_key)
28}
29
30pub fn derive_citizen_key(k0: &[u8; K0_KEY_LENGTH], citizen_key: &mut [u8; DERIVED_KEY_LENGTH]) -> Result<()> {
31 hkdf_expand(k0, HKDF_INFO_CITIZEN, citizen_key)
32}
33
34pub fn derive_manager_global_key(k0: &[u8; K0_KEY_LENGTH], mgr_key: &mut [u8; DERIVED_KEY_LENGTH]) -> Result<()> {
35 hkdf_expand(k0, HKDF_INFO_MANAGER_GLOBAL, mgr_key)
36}
37
38pub fn derive_manager_local_key(
39 k0: &[u8; K0_KEY_LENGTH],
40 tuid: &[u8; TUID_LENGTH],
41 mgr_local_key: &mut [u8; DERIVED_KEY_LENGTH],
42) -> Result<()> {
43 let hex = TUID(*tuid).to_hex_upper();
44 let mut info = [0u8; HKDF_INFO_INPUT_MAX];
45 let prefix_len = HKDF_INFO_MANAGER_LOCAL_PREFIX.len();
46 info[..prefix_len].copy_from_slice(HKDF_INFO_MANAGER_LOCAL_PREFIX);
47 info[prefix_len..prefix_len + TUID_HEX_LENGTH].copy_from_slice(&hex);
48 hkdf_expand(k0, &info[..prefix_len + TUID_HEX_LENGTH], mgr_local_key)
49}
50
51pub fn tuid_from_hex_string(hex_string: &[u8]) -> Result<[u8; TUID_LENGTH]> {
52 TUID::from_hex(hex_string).map(|t| t.0)
53}
54
55pub fn generate_dynamic_tuid(mfg_code: u16) -> Result<[u8; TUID_LENGTH]> {
56 let mut random_bytes = [0u8; 4];
57 getrandom::getrandom(&mut random_bytes).map_err(|_| SigNetError::Crypto)?;
58 let device_id = (random_bytes[0] as u32) << 24
59 | (random_bytes[1] as u32) << 16
60 | (random_bytes[2] as u32) << 8
61 | (random_bytes[3] as u32);
62 let device_id = (device_id | 0x80000000).min(0xFFFFFFEF);
63 let mut tuid = [0u8; TUID_LENGTH];
64 tuid[0] = (mfg_code >> 8) as u8;
65 tuid[1] = (mfg_code & 0xFF) as u8;
66 tuid[2] = (device_id >> 24) as u8;
67 tuid[3] = (device_id >> 16) as u8;
68 tuid[4] = (device_id >> 8) as u8;
69 tuid[5] = device_id as u8;
70 Ok(tuid)
71}
72
73#[derive(Debug, Clone)]
74pub struct PassphraseChecks {
75 pub length: usize,
76 pub length_ok: bool,
77 pub class_count: i32,
78 pub has_upper: bool,
79 pub has_lower: bool,
80 pub has_digit: bool,
81 pub has_symbol: bool,
82 pub classes_ok: bool,
83 pub no_identical: bool,
84 pub no_sequential: bool,
85}
86
87impl Default for PassphraseChecks {
88 fn default() -> Self {
89 PassphraseChecks {
90 length: 0,
91 length_ok: false,
92 class_count: 0,
93 has_upper: false,
94 has_lower: false,
95 has_digit: false,
96 has_symbol: false,
97 classes_ok: false,
98 no_identical: true,
99 no_sequential: true,
100 }
101 }
102}
103
104pub fn validate_passphrase(passphrase: &[u8]) -> Result<()> {
105 analyse_passphrase(passphrase).map(|_| ())
106}
107
108pub fn analyse_passphrase(passphrase: &[u8]) -> Result<PassphraseChecks> {
109 let length = passphrase.len();
110 let mut checks = PassphraseChecks { length, ..Default::default() };
111
112 if passphrase.is_empty() {
114 return Err(SigNetError::PassphraseTooShort);
115 }
116
117 checks.length_ok = checks.length >= PASSPHRASE_MIN_LENGTH && checks.length <= PASSPHRASE_MAX_LENGTH;
118 if !checks.length_ok {
119 return if checks.length < PASSPHRASE_MIN_LENGTH {
120 Err(SigNetError::PassphraseTooShort)
121 } else {
122 Err(SigNetError::PassphraseTooLong)
123 };
124 }
125
126 for &c in passphrase {
127 if c.is_ascii_uppercase() {
128 checks.has_upper = true;
129 } else if c.is_ascii_lowercase() {
130 checks.has_lower = true;
131 } else if c.is_ascii_digit() {
132 checks.has_digit = true;
133 } else if PASSPHRASE_SYMBOLS.contains(&c) {
134 checks.has_symbol = true;
135 }
136 }
137
138 checks.class_count = [checks.has_upper, checks.has_lower, checks.has_digit, checks.has_symbol]
139 .iter()
140 .filter(|&&b| b)
141 .count() as i32;
142 checks.classes_ok = checks.class_count >= 3;
143
144 for i in 2..passphrase.len() {
145 if passphrase[i] == passphrase[i - 1] && passphrase[i] == passphrase[i - 2] {
146 checks.no_identical = false;
147 break;
148 }
149 }
150
151 for i in 3..passphrase.len() {
153 let asc = passphrase[i] == passphrase[i - 1].wrapping_add(1)
154 && passphrase[i - 1] == passphrase[i - 2].wrapping_add(1)
155 && passphrase[i - 2] == passphrase[i - 3].wrapping_add(1);
156 let desc = passphrase[i] == passphrase[i - 1].wrapping_sub(1)
157 && passphrase[i - 1] == passphrase[i - 2].wrapping_sub(1)
158 && passphrase[i - 2] == passphrase[i - 3].wrapping_sub(1);
159 if asc || desc {
160 checks.no_sequential = false;
161 break;
162 }
163 }
164
165 if !checks.no_identical {
167 return Err(SigNetError::PassphraseConsecutiveIdentical);
168 }
169 if !checks.no_sequential {
170 return Err(SigNetError::PassphraseConsecutiveSequential);
171 }
172 if !checks.classes_ok {
173 return Err(SigNetError::PassphraseInsufficientClasses);
174 }
175
176 Ok(checks)
177}
178
179pub fn generate_random_passphrase(buf: &mut [u8; 11]) -> Result<()> {
180 let sets: &[&[u8]] = &[
181 PASSPHRASE_GEN_UPPERCASE,
182 PASSPHRASE_GEN_LOWERCASE,
183 PASSPHRASE_GEN_DIGITS,
184 PASSPHRASE_GEN_SYMBOLS,
185 ];
186
187 for _ in 0..100 {
188 let mut phrase = [0u8; PASSPHRASE_GENERATED_LENGTH];
189 getrandom::getrandom(&mut phrase).map_err(|_| SigNetError::Crypto)?;
190
191 for b in &mut phrase {
192 let idx = *b as usize;
193 let set_idx = idx % 4;
194 let set = sets[set_idx];
195 *b = set[idx % set.len()];
196 }
197
198 if analyse_passphrase(&phrase).is_ok() {
199 buf[..PASSPHRASE_GENERATED_LENGTH].copy_from_slice(&phrase);
200 buf[PASSPHRASE_GENERATED_LENGTH] = 0;
201 return Ok(());
202 }
203 }
204
205 Err(SigNetError::Crypto)
206}
207
208pub fn generate_random_k0(k0_output: &mut [u8; K0_KEY_LENGTH]) -> Result<()> {
209 getrandom::getrandom(k0_output).map_err(|_| SigNetError::Crypto)
210}
211
212pub struct GuestKeys {
216 pub km_global: [u8; DERIVED_KEY_LENGTH],
217 pub ks: [u8; DERIVED_KEY_LENGTH],
218 pub kc: [u8; DERIVED_KEY_LENGTH],
219}
220
221pub fn export_guest_keys(k0: &[u8; K0_KEY_LENGTH]) -> Result<GuestKeys> {
222 let mut keys = GuestKeys {
223 km_global: [0u8; DERIVED_KEY_LENGTH],
224 ks: [0u8; DERIVED_KEY_LENGTH],
225 kc: [0u8; DERIVED_KEY_LENGTH],
226 };
227 derive_manager_global_key(k0, &mut keys.km_global)?;
228 derive_sender_key(k0, &mut keys.ks)?;
229 derive_citizen_key(k0, &mut keys.kc)?;
230 Ok(keys)
231}
232
233pub fn build_hmac_input(
234 uri_string: &str,
235 options: &SigNetOptions,
236 payload: &[u8],
237 output: &mut [u8],
238) -> Result<usize> {
239 let mut pos = 0;
240 let uri = uri_string.as_bytes();
241 output[pos..pos + uri.len()].copy_from_slice(uri);
242 pos += uri.len();
243 output[pos] = options.security_mode;
244 pos += 1;
245 output[pos..pos + SENDER_ID_LENGTH].copy_from_slice(&options.sender_id);
246 pos += SENDER_ID_LENGTH;
247 output[pos..pos + 2].copy_from_slice(&options.mfg_code.to_be_bytes());
248 pos += 2;
249 output[pos..pos + 4].copy_from_slice(&options.session_id.to_be_bytes());
250 pos += 4;
251 output[pos..pos + 4].copy_from_slice(&options.seq_num.to_be_bytes());
252 pos += 4;
253 output[pos..pos + payload.len()].copy_from_slice(payload);
254 pos += payload.len();
255 Ok(pos)
256}
257
258pub fn verify_packet_hmac(
259 uri_string: &str,
260 options: &SigNetOptions,
261 payload: &[u8],
262 role_key: &[u8],
263) -> Result<()> {
264 use subtle::ConstantTimeEq;
265
266 let input_len = uri_string.len() + 1 + SENDER_ID_LENGTH + 2 + 4 + 4 + payload.len();
267 if input_len > HMAC_INPUT_MAX {
268 return Err(SigNetError::InvalidArgument);
269 }
270 let mut hmac_input = [0u8; HMAC_INPUT_MAX];
271 build_hmac_input(uri_string, options, payload, &mut hmac_input[..input_len])?;
272
273 let mut computed = [0u8; HMAC_SHA256_LENGTH];
274 hmac_sha256(role_key, &hmac_input[..input_len], &mut computed)?;
275
276 if computed.ct_eq(&options.hmac).into() {
277 Ok(())
278 } else {
279 Err(SigNetError::HmacFailed)
280 }
281}
282
283pub fn compute_packet_hmac(
284 uri_string: &str,
285 options: &SigNetOptions,
286 payload: &[u8],
287 signing_key: &[u8],
288) -> Result<[u8; HMAC_SHA256_LENGTH]> {
289 let input_len = uri_string.len() + 1 + SENDER_ID_LENGTH + 2 + 4 + 4 + payload.len();
290 if input_len > HMAC_INPUT_MAX {
291 return Err(SigNetError::InvalidArgument);
292 }
293 let mut hmac_input = [0u8; HMAC_INPUT_MAX];
294 build_hmac_input(uri_string, options, payload, &mut hmac_input[..input_len])?;
295 let mut hmac = [0u8; HMAC_SHA256_LENGTH];
296 hmac_sha256(signing_key, &hmac_input[..input_len], &mut hmac)?;
297 Ok(hmac)
298}