lb_rs/model/
secret_filename.rs

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