Skip to main content

bitwarden_crypto/keys/
shareable_key.rs

1use std::pin::Pin;
2
3use hmac::{KeyInit, Mac};
4use hybrid_array::Array;
5use typenum::{U32, U64};
6use zeroize::{Zeroize, Zeroizing};
7
8use super::Aes256CbcHmacKey;
9use crate::util::{PbkdfSha256Hmac, hkdf_expand};
10
11/// Derive a shareable key using hkdf from secret and name.
12///
13/// A specialized variant of this function was called `CryptoService.makeSendKey` in the Bitwarden
14/// `clients` repository.
15pub fn derive_shareable_key(
16    secret: Zeroizing<[u8; 16]>,
17    name: &str,
18    info: Option<&str>,
19) -> Aes256CbcHmacKey {
20    // Because all inputs are fixed size, we can unwrap all errors here without issue
21    let res = Zeroizing::new(
22        PbkdfSha256Hmac::new_from_slice(format!("bitwarden-{name}").as_bytes())
23            .expect("hmac new_from_slice should not fail")
24            .chain_update(secret)
25            .finalize()
26            .into_bytes(),
27    );
28
29    let mut key: Pin<Box<Array<u8, U64>>> = hkdf_expand(&res, info).expect("Input is a valid size");
30    let enc_key = Box::pin(Array::<u8, U32>::try_from(&key[..32]).expect("slice is 32 bytes"));
31    let mac_key = Box::pin(Array::<u8, U32>::try_from(&key[32..]).expect("slice is 32 bytes"));
32    key.zeroize();
33    Aes256CbcHmacKey { enc_key, mac_key }
34}
35
36#[cfg(test)]
37mod tests {
38    use zeroize::Zeroizing;
39
40    use super::derive_shareable_key;
41    use crate::SymmetricCryptoKey;
42
43    #[test]
44    fn test_derive_shareable_key() {
45        let key = derive_shareable_key(Zeroizing::new(*b"&/$%F1a895g67HlX"), "test_key", None);
46        assert_eq!(
47            SymmetricCryptoKey::Aes256CbcHmacKey(key)
48                .to_base64()
49                .to_string(),
50            "4PV6+PcmF2w7YHRatvyMcVQtI7zvCyssv/wFWmzjiH6Iv9altjmDkuBD1aagLVaLezbthbSe+ktR+U6qswxNnQ=="
51        );
52
53        let key = derive_shareable_key(
54            Zeroizing::new(*b"67t9b5g67$%Dh89n"),
55            "test_key",
56            Some("test"),
57        );
58        assert_eq!(
59            SymmetricCryptoKey::Aes256CbcHmacKey(key)
60                .to_base64()
61                .to_string(),
62            "F9jVQmrACGx9VUPjuzfMYDjr726JtL300Y3Yg+VYUnVQtQ1s8oImJ5xtp1KALC9h2nav04++1LDW4iFD+infng=="
63        );
64    }
65}