shadow_crypt_core/v3/
file.rs1use crate::{
2 errors::{CryptError, FileError, HeaderError},
3 file::{FileMetadata, PlaintextFile},
4 memory::{SecureBytes, SecureKey},
5 v3::{
6 key::KeyDerivationParams,
7 stream::{StreamOpener, StreamSealer},
8 },
9};
10
11use super::header::FileHeader;
12
13#[derive(Debug)]
19pub struct EncryptedFile {
20 header: FileHeader,
21 ciphertext: Vec<u8>,
22}
23
24impl EncryptedFile {
25 pub fn new(header: FileHeader, ciphertext: Vec<u8>) -> Self {
26 Self { header, ciphertext }
27 }
28
29 pub fn seal(
34 metadata: &FileMetadata,
35 content: &[u8],
36 key: &SecureKey,
37 kdf_params: KeyDerivationParams,
38 salt: [u8; 16],
39 nonce_prefix: [u8; 16],
40 metadata_nonce: [u8; 24],
41 ) -> Result<Self, FileError> {
42 let (header, mut sealer) = StreamSealer::begin(
43 metadata,
44 key,
45 kdf_params,
46 salt,
47 nonce_prefix,
48 metadata_nonce,
49 )?;
50
51 let chunk_size = sealer.chunk_plaintext_len();
52 let pieces: Vec<&[u8]> = if content.is_empty() {
53 vec![&[][..]]
54 } else {
55 content.chunks(chunk_size).collect()
56 };
57
58 let mut ciphertext = Vec::with_capacity(content.len() + pieces.len() * 16);
59 for (i, piece) in pieces.iter().enumerate() {
60 ciphertext.extend_from_slice(&sealer.seal_chunk(piece, i == pieces.len() - 1)?);
61 }
62
63 Ok(Self::new(header, ciphertext))
64 }
65
66 pub fn from_bytes(bytes: &[u8]) -> Result<Self, HeaderError> {
68 let header = FileHeader::try_deserialize(bytes)?;
69 let ciphertext = bytes[header.header_length()..].to_vec();
70
71 Ok(Self::new(header, ciphertext))
72 }
73
74 pub fn to_bytes(&self) -> Vec<u8> {
76 let mut bytes = self.header.serialize();
77 bytes.extend_from_slice(&self.ciphertext);
78 bytes
79 }
80
81 pub fn decrypt(&self, key: &SecureKey) -> Result<PlaintextFile, FileError> {
84 let metadata = self.header.decrypt_metadata(key)?;
85
86 let mut opener = StreamOpener::new(&self.header, key);
87 let piece_len = opener.chunk_ciphertext_len();
88 if self.ciphertext.is_empty() {
89 return Err(FileError::Crypt(CryptError::DecryptionError(
90 "invalid content stream: missing final chunk".to_string(),
91 )));
92 }
93
94 let pieces: Vec<&[u8]> = self.ciphertext.chunks(piece_len).collect();
95 let mut content = Vec::with_capacity(self.ciphertext.len());
96 for (i, piece) in pieces.iter().enumerate() {
97 let plaintext = opener.open_chunk(piece, i == pieces.len() - 1)?;
98 content.extend_from_slice(plaintext.as_slice());
99 }
100
101 Ok(PlaintextFile::new(
102 metadata.filename().clone(),
103 SecureBytes::new(content),
104 ))
105 }
106
107 pub fn header(&self) -> &FileHeader {
108 &self.header
109 }
110 pub fn ciphertext(&self) -> &[u8] {
111 &self.ciphertext
112 }
113}