lb_rs/model/
secret_filename.rs

1use crate::model::crypto::{AESEncrypted, AESKey};
2use crate::model::symkey::{convert_key, generate_nonce};
3use aead::Aead;
4use aead::generic_array::GenericArray;
5use hmac::{Hmac, Mac, NewMac};
6use serde::{Deserialize, Serialize};
7use sha2::Sha256;
8use std::hash::Hash;
9
10use super::errors::{CryptoError, LbErrKind, LbResult, Unexpected};
11
12pub type HmacSha256 = Hmac<Sha256>;
13
14/// A secret value that can impl an equality check by hmac'ing the
15/// inner secret.
16#[derive(Serialize, Deserialize, Debug, Clone)]
17pub struct SecretFileName {
18    pub encrypted_value: AESEncrypted<String>,
19    pub hmac: [u8; 32],
20}
21
22impl SecretFileName {
23    pub fn from_str(to_encrypt: &str, key: &AESKey, parent_key: &AESKey) -> LbResult<Self> {
24        let serialized = bincode::serialize(to_encrypt).map_unexpected()?;
25
26        let hmac = {
27            let mut mac = HmacSha256::new_from_slice(parent_key).map_unexpected()?;
28            mac.update(serialized.as_ref());
29            mac.finalize().into_bytes()
30        }
31        .into();
32
33        let encrypted_value = {
34            let nonce = &generate_nonce();
35            let encrypted = convert_key(key)
36                .encrypt(
37                    GenericArray::from_slice(nonce),
38                    aead::Payload { msg: &serialized, aad: &[] },
39                )
40                .map_unexpected()?;
41            AESEncrypted::new(encrypted, nonce.to_vec())
42        };
43
44        Ok(SecretFileName { encrypted_value, hmac })
45    }
46
47    pub fn to_string(&self, key: &AESKey) -> LbResult<String> {
48        let nonce = GenericArray::from_slice(&self.encrypted_value.nonce);
49        let decrypted = convert_key(key)
50            .decrypt(nonce, aead::Payload { msg: &self.encrypted_value.value, aad: &[] })
51            .map_err(|err| LbErrKind::Crypto(CryptoError::Decryption(err)))?;
52        let deserialized = bincode::deserialize(&decrypted).map_unexpected()?;
53        Ok(deserialized)
54    }
55
56    pub fn verify_hmac(&self, key: &AESKey, parent_key: &AESKey) -> LbResult<()> {
57        let decrypted = self.to_string(key)?;
58        let mut mac = HmacSha256::new_from_slice(parent_key).map_unexpected()?;
59        mac.update(decrypted.as_ref());
60        mac.verify(&self.hmac)
61            .map_err(|err| LbErrKind::Crypto(CryptoError::HmacVerification(err)))?;
62        Ok(())
63    }
64}
65
66// Impl'd to avoid comparing encrypted values
67impl PartialEq for SecretFileName {
68    fn eq(&self, other: &Self) -> bool {
69        self.hmac == other.hmac
70    }
71}
72
73impl Eq for SecretFileName {}
74
75impl Hash for SecretFileName {
76    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
77        self.hmac.hash(state);
78    }
79}
80
81#[cfg(test)]
82mod unit_tests {
83    use crate::model::secret_filename::SecretFileName;
84    use crate::model::symkey::generate_key;
85    use uuid::Uuid;
86
87    #[test]
88    fn test_to_string_from_string() {
89        let key = generate_key();
90        let parent_key = generate_key();
91        let test_value = Uuid::new_v4().to_string();
92        let secret = SecretFileName::from_str(&test_value, &key, &parent_key).unwrap();
93        let decrypted = secret.to_string(&key).unwrap();
94
95        assert_eq!(test_value, decrypted);
96    }
97
98    #[test]
99    fn test_hmac_encryption_failure() {
100        let key = generate_key();
101        let parent_key = generate_key();
102        let test_value = Uuid::new_v4().to_string();
103        let mut secret = SecretFileName::from_str(&test_value, &key, &parent_key).unwrap();
104        secret.hmac[10] = !secret.hmac[10];
105        secret.hmac[11] = !secret.hmac[11];
106        secret.hmac[12] = !secret.hmac[12];
107        secret.hmac[13] = !secret.hmac[13];
108        secret.hmac[14] = !secret.hmac[14];
109        secret.verify_hmac(&key, &parent_key).unwrap_err();
110    }
111
112    #[test]
113    fn attempt_value_forge() {
114        let key = generate_key();
115        let parent_key = generate_key();
116
117        let test_value1 = Uuid::new_v4().to_string();
118        let test_value2 = Uuid::new_v4().to_string();
119        let secret1 = SecretFileName::from_str(&test_value1, &key, &parent_key).unwrap();
120        let mut secret2 = SecretFileName::from_str(&test_value2, &key, &parent_key).unwrap();
121
122        secret2.encrypted_value = secret1.encrypted_value;
123
124        secret2.verify_hmac(&key, &parent_key).unwrap_err();
125    }
126}