shadow_crypt_core/v3/
mod.rs1use crate::algorithm::Algorithm;
25
26pub mod crypt;
28
29pub mod file;
31
32pub mod header;
34
35pub mod key;
37
38pub mod metadata;
40
41pub mod stream;
43
44pub const ALGORITHM: Algorithm = Algorithm::XChaCha20Poly1305;
46
47#[cfg(test)]
48mod tests {
49 use std::time::{Duration, UNIX_EPOCH};
50
51 use crate::{
52 file::FileMetadata,
53 memory::{SecureKey, SecureString},
54 v3::{file::EncryptedFile, key::KeyDerivationParams},
55 };
56
57 fn test_metadata() -> FileMetadata {
58 FileMetadata::new(
59 SecureString::new("name.txt".to_string()),
60 Some(UNIX_EPOCH + Duration::new(1_700_000_000, 500)),
61 Some(0o640),
62 )
63 }
64
65 fn derive_test_key(password: &[u8], salt: &[u8; 16]) -> SecureKey {
66 let (key, _) = KeyDerivationParams::test_defaults()
67 .derive_key(password, salt)
68 .unwrap();
69 key
70 }
71
72 #[test]
75 fn seal_decrypt_round_trip_via_bytes() {
76 let salt = [1u8; 16];
77 let key = derive_test_key(b"password", &salt);
78
79 let sealed = EncryptedFile::seal(
80 &test_metadata(),
81 b"hello streaming world",
82 &key,
83 KeyDerivationParams::test_defaults(),
84 salt,
85 [2u8; 16],
86 [3u8; 24],
87 )
88 .unwrap();
89
90 let parsed = EncryptedFile::from_bytes(&sealed.to_bytes()).unwrap();
91 let decrypted = parsed.decrypt(&key).unwrap();
92
93 assert_eq!(decrypted.filename().as_str(), "name.txt");
94 assert_eq!(decrypted.content().as_slice(), b"hello streaming world");
95
96 let metadata = parsed.header().decrypt_metadata(&key).unwrap();
98 assert_eq!(metadata.mtime(), test_metadata().mtime());
99 assert_eq!(metadata.mode(), Some(0o640));
100 }
101
102 #[test]
103 fn empty_content_round_trip() {
104 let salt = [1u8; 16];
105 let key = derive_test_key(b"password", &salt);
106
107 let sealed = EncryptedFile::seal(
108 &test_metadata(),
109 b"",
110 &key,
111 KeyDerivationParams::test_defaults(),
112 salt,
113 [2u8; 16],
114 [3u8; 24],
115 )
116 .unwrap();
117
118 let decrypted = EncryptedFile::from_bytes(&sealed.to_bytes())
119 .unwrap()
120 .decrypt(&key)
121 .unwrap();
122 assert!(decrypted.content().as_slice().is_empty());
123 }
124
125 #[test]
126 fn seal_decrypt_with_wrong_key_fails() {
127 let salt = [1u8; 16];
128 let key = derive_test_key(b"password", &salt);
129 let wrong_key = derive_test_key(b"wrong", &salt);
130
131 let sealed = EncryptedFile::seal(
132 &test_metadata(),
133 b"content",
134 &key,
135 KeyDerivationParams::test_defaults(),
136 salt,
137 [2u8; 16],
138 [3u8; 24],
139 )
140 .unwrap();
141
142 assert!(sealed.decrypt(&wrong_key).is_err());
143 }
144
145 #[test]
148 fn truncated_content_fails() {
149 let salt = [1u8; 16];
150 let key = derive_test_key(b"password", &salt);
151
152 let sealed = EncryptedFile::seal(
153 &test_metadata(),
154 b"some content",
155 &key,
156 KeyDerivationParams::test_defaults(),
157 salt,
158 [2u8; 16],
159 [3u8; 24],
160 )
161 .unwrap();
162
163 let mut bytes = sealed.to_bytes();
164 bytes.truncate(sealed.header().header_length());
166 let truncated = EncryptedFile::from_bytes(&bytes).unwrap();
167 assert!(truncated.decrypt(&key).is_err());
168 }
169}