shadow_crypt_core/
memory.rs

1// shadow-core/src/memory.rs
2// Secure memory types with automatic zeroization
3// All types handle sensitive data that should be cleared from memory
4
5use zeroize::Zeroizing;
6
7/// Secure string wrapper to encapsulate zeroize dependency for passwords
8#[derive(Debug)]
9pub struct SecureString(Zeroizing<String>);
10
11impl SecureString {
12    /// Create a new SecureString that will be zeroized when dropped
13    pub fn new(s: String) -> Self {
14        Self(Zeroizing::new(s))
15    }
16
17    /// Get a string slice of the contents
18    pub fn as_str(&self) -> &str {
19        self.0.as_str()
20    }
21
22    /// Check if the string is empty
23    pub fn is_empty(&self) -> bool {
24        self.0.is_empty()
25    }
26}
27
28impl Clone for SecureString {
29    fn clone(&self) -> Self {
30        Self::new(self.0.as_str().to_string())
31    }
32}
33
34/// Secure key wrapper for cryptographic keys
35#[derive(Debug, Clone)]
36pub struct SecureKey(Zeroizing<[u8; 32]>);
37
38impl SecureKey {
39    /// Create a new SecureKey that will be zeroized when dropped
40    pub fn new(key: [u8; 32]) -> Self {
41        Self(Zeroizing::new(key))
42    }
43
44    /// Get a slice of the key bytes
45    pub fn as_bytes(&self) -> &[u8; 32] {
46        &self.0
47    }
48}
49
50/// Secure bytes for sensitive data like derived keys or intermediate values
51#[derive(Debug, Clone)]
52pub struct SecureBytes(Zeroizing<Vec<u8>>);
53
54impl SecureBytes {
55    /// Create a new SecureBytes that will be zeroized when dropped
56    pub fn new(data: Vec<u8>) -> Self {
57        Self(Zeroizing::new(data))
58    }
59
60    /// Get a slice of the bytes
61    pub fn as_slice(&self) -> &[u8] {
62        &self.0
63    }
64
65    /// Create SecureBytes with specified capacity
66    pub fn with_capacity(capacity: usize) -> Self {
67        Self(Zeroizing::new(Vec::with_capacity(capacity)))
68    }
69}
70
71#[cfg(test)]
72mod tests {
73    use super::*;
74
75    #[test]
76    fn test_secure_string_basic() {
77        let s = SecureString::new("test_password".to_string());
78        assert_eq!(s.as_str(), "test_password");
79        assert!(!s.is_empty());
80
81        let empty = SecureString::new(String::new());
82        assert!(empty.is_empty());
83    }
84
85    #[test]
86    fn test_secure_string_clone() {
87        let original = SecureString::new("test_password".to_string());
88        let cloned = original.clone();
89        assert_eq!(original.as_str(), cloned.as_str());
90    }
91
92    #[test]
93    fn test_secure_key_basic() {
94        let key_bytes = [42u8; 32];
95        let key = SecureKey::new(key_bytes);
96        assert_eq!(key.as_bytes(), &key_bytes);
97    }
98
99    #[test]
100    fn test_secure_key_clone() {
101        let key_bytes = [42u8; 32];
102        let original = SecureKey::new(key_bytes);
103        let cloned = original.clone();
104        assert_eq!(original.as_bytes(), cloned.as_bytes());
105    }
106
107    #[test]
108    fn test_secure_bytes_basic() {
109        let data = vec![1, 2, 3, 4, 5];
110        let secure_data = SecureBytes::new(data.clone());
111        assert_eq!(secure_data.as_slice(), data.as_slice());
112    }
113
114    #[test]
115    fn test_secure_bytes_with_capacity() {
116        let capacity = 100;
117        let secure_data = SecureBytes::with_capacity(capacity);
118        assert!(secure_data.as_slice().is_empty());
119        // Note: We can't directly check capacity, but we can ensure it's empty initially
120    }
121}