bitwarden_crypto/keys/
utils.rs

1use std::{cmp::max, pin::Pin};
2
3use generic_array::GenericArray;
4use typenum::U32;
5
6use super::Aes256CbcHmacKey;
7use crate::{util::hkdf_expand, CryptoError, Result};
8
9/// Stretch the given key using HKDF.
10/// This can be either a kdf-derived key (PIN/Master password) or
11/// a random key from key connector
12pub(super) fn stretch_key(key: &Pin<Box<GenericArray<u8, U32>>>) -> Result<Aes256CbcHmacKey> {
13    Ok(Aes256CbcHmacKey {
14        enc_key: hkdf_expand(key, Some("enc"))?,
15        mac_key: hkdf_expand(key, Some("mac"))?,
16    })
17}
18
19/// Pads bytes to a minimum length using PKCS7-like padding.
20/// The last N bytes of the padded bytes all have the value N. Minimum of 1 padding byte.
21/// For example, padded to size 4, the value 0,0 becomes 0,0,2,2.
22pub(crate) fn pad_bytes(bytes: &mut Vec<u8>, min_length: usize) {
23    // at least 1 byte of padding is required
24    let pad_bytes = min_length.saturating_sub(bytes.len()).max(1);
25    let padded_length = max(min_length, bytes.len() + 1);
26    bytes.resize(padded_length, pad_bytes as u8);
27}
28
29/// Unpads bytes that is padded using the PKCS7-like padding defined by [pad_bytes].
30/// The last N bytes of the padded bytes all have the value N. Minimum of 1 padding byte.
31/// For example, padded to size 4, the value 0,0 becomes 0,0,2,2.
32pub(crate) fn unpad_bytes(padded_bytes: &[u8]) -> Result<&[u8], CryptoError> {
33    let pad_len = *padded_bytes.last().ok_or(CryptoError::InvalidPadding)? as usize;
34    if pad_len >= padded_bytes.len() {
35        return Err(CryptoError::InvalidPadding);
36    }
37    Ok(padded_bytes[..(padded_bytes.len() - pad_len)].as_ref())
38}
39
40#[cfg(test)]
41mod tests {
42    use super::*;
43
44    #[test]
45    fn test_stretch_kdf_key() {
46        let key = Box::pin(
47            [
48                31, 79, 104, 226, 150, 71, 177, 90, 194, 80, 172, 209, 17, 129, 132, 81, 138, 167,
49                69, 167, 254, 149, 2, 27, 39, 197, 64, 42, 22, 195, 86, 75,
50            ]
51            .into(),
52        );
53        let stretched = stretch_key(&key).unwrap();
54
55        assert_eq!(
56            [
57                111, 31, 178, 45, 238, 152, 37, 114, 143, 215, 124, 83, 135, 173, 195, 23, 142,
58                134, 120, 249, 61, 132, 163, 182, 113, 197, 189, 204, 188, 21, 237, 96
59            ],
60            stretched.enc_key.as_slice()
61        );
62        assert_eq!(
63            [
64                221, 127, 206, 234, 101, 27, 202, 38, 86, 52, 34, 28, 78, 28, 185, 16, 48, 61, 127,
65                166, 209, 247, 194, 87, 232, 26, 48, 85, 193, 249, 179, 155
66            ],
67            stretched.mac_key.as_slice()
68        );
69    }
70
71    #[test]
72    fn test_pad_bytes_roundtrip() {
73        let original_bytes = vec![1u8; 10];
74        let mut cloned_bytes = original_bytes.clone();
75        let mut encoded_bytes = vec![1u8; 12];
76        encoded_bytes[10] = 2;
77        encoded_bytes[11] = 2;
78        pad_bytes(&mut cloned_bytes, 12);
79        assert_eq!(encoded_bytes, cloned_bytes);
80        let unpadded_bytes = unpad_bytes(&cloned_bytes).unwrap();
81        assert_eq!(original_bytes, unpadded_bytes);
82    }
83}