Skip to main content

ave_actors_actor/helpers/
encrypted_key.rs

1use std::sync::Arc;
2
3use memsecurity::EncryptedMem;
4use zeroize::Zeroizing;
5
6use crate::error::Error;
7
8/// A 32-byte cryptographic key stored encrypted in memory using ASCON AEAD (`memsecurity`).
9///
10/// Cloning is cheap — all clones share the same `Arc<EncryptedMem>`. Decrypted
11/// bytes are returned in a [`Zeroizing`] container that wipes memory on drop,
12/// minimizing the window where the raw key is exposed.
13#[derive(Clone)]
14pub struct EncryptedKey {
15    key: Arc<EncryptedMem>,
16}
17
18impl EncryptedKey {
19    /// Encrypts `key` and stores it in secure memory.
20    ///
21    /// Returns an error if the underlying ASCON encryption fails, which typically
22    /// indicates an out-of-memory or platform security error.
23    pub fn new(key: &[u8; 32]) -> Result<Self, Error> {
24        let mut encrypted_mem = EncryptedMem::new();
25
26        encrypted_mem.encrypt(key).map_err(|_| {
27            tracing::error!("Key encryption failed");
28            Error::Helper {
29                name: "encryption".to_owned(),
30                reason: "Failed to encrypt key".to_owned(),
31            }
32        })?;
33
34        tracing::debug!("EncryptedKey created");
35        Ok(Self {
36            key: Arc::new(encrypted_mem),
37        })
38    }
39
40    /// Decrypts and returns the key in a [`Zeroizing`] container.
41    ///
42    /// The returned bytes are automatically wiped from memory when the container
43    /// is dropped. Returns an error if decryption fails, which indicates memory
44    /// corruption or tampering.
45    pub fn key(&self) -> Result<Zeroizing<[u8; 32]>, Error> {
46        let decrypted = self.key.decrypt().map_err(|_| {
47            tracing::error!(
48                "Key decryption failed, possible memory corruption"
49            );
50            Error::Helper {
51                name: "decryption".to_owned(),
52                reason: "Failed to decrypt key".to_owned(),
53            }
54        })?;
55
56        let mut key_array = Zeroizing::new([0u8; 32]);
57        key_array.copy_from_slice(decrypted.as_ref());
58
59        tracing::debug!("Key accessed");
60        Ok(key_array)
61    }
62}
63
64#[cfg(test)]
65mod tests {
66    use super::*;
67
68    #[test]
69    fn test_encrypted_hash() {
70        let key = [1u8; 32];
71        let eh = EncryptedKey::new(&key).unwrap();
72        let key1 = eh.key().unwrap();
73        let eh_cloned = eh.clone();
74        let key2 = eh_cloned.key().unwrap();
75        assert_eq!(*key1, *key2);
76    }
77
78    #[test]
79    fn test_encrypted_hash_deterministic() {
80        // Same key should produce same decrypted value
81        let key = [42u8; 32];
82        let eh1 = EncryptedKey::new(&key).unwrap();
83        let eh2 = EncryptedKey::new(&key).unwrap();
84
85        let key1 = eh1.key().unwrap();
86        let key2 = eh2.key().unwrap();
87
88        assert_eq!(*key1, *key2);
89    }
90
91    #[test]
92    fn test_encrypted_hash_different() {
93        // Different keys should be different
94        let key1_data = [1u8; 32];
95        let key2_data = [2u8; 32];
96        let eh1 = EncryptedKey::new(&key1_data).unwrap();
97        let eh2 = EncryptedKey::new(&key2_data).unwrap();
98
99        let key1 = eh1.key().unwrap();
100        let key2 = eh2.key().unwrap();
101
102        assert_ne!(*key1, *key2);
103    }
104
105    #[test]
106    fn test_key_access() {
107        let original = [123u8; 32];
108        let eh = EncryptedKey::new(&original).unwrap();
109        let decrypted = eh.key().unwrap();
110
111        // Key should match original
112        assert_eq!(*decrypted, original);
113
114        // Verify we can still decrypt after first access
115        let decrypted2 = eh.key().unwrap();
116        assert_eq!(*decrypted2, original);
117    }
118
119    #[test]
120    fn test_zeroizing() {
121        let original = [99u8; 32];
122        let eh = EncryptedKey::new(&original).unwrap();
123
124        {
125            let key = eh.key().unwrap();
126            assert_eq!(*key, original);
127            // When key is dropped here, memory should be zeroized
128        }
129
130        // Should still be able to decrypt
131        let key2 = eh.key().unwrap();
132        assert_eq!(*key2, original);
133    }
134
135    #[test]
136    fn test_clone_is_cheap() {
137        // Cloning should share the same Arc, not duplicate encrypted data
138        let original = [77u8; 32];
139        let eh = EncryptedKey::new(&original).unwrap();
140        let eh_clone = eh.clone();
141
142        // Both should point to the same Arc (same strong count)
143        assert_eq!(Arc::strong_count(&eh.key), 2);
144        assert_eq!(Arc::strong_count(&eh_clone.key), 2);
145
146        // Both should decrypt to the same value
147        let key1 = eh.key().unwrap();
148        let key2 = eh_clone.key().unwrap();
149        assert_eq!(*key1, *key2);
150    }
151}