1use crate::{cipher, randomness, CryptoError, SecretBytes};
4use zeroize::Zeroizing;
5
6pub struct FileKey(Zeroizing<[u8; 32]>);
9
10impl FileKey {
11 pub fn generate() -> Result<Self, CryptoError> {
13 Ok(Self(randomness::secret_bytes()?))
14 }
15
16 pub fn from_bytes(bytes: [u8; 32]) -> Self {
18 Self(Zeroizing::new(bytes))
19 }
20
21 pub fn to_secret_bytes(&self) -> SecretBytes {
23 SecretBytes::new(self.0.to_vec())
24 }
25
26 pub fn seal_chunk(&self, path: &str, header: &[u8], index: u64, plaintext: &[u8]) -> Vec<u8> {
30 cipher::seal(&self.0, &nonce(index), &aad(path, header, index), plaintext)
31 }
32
33 pub fn open_chunk(
36 &self,
37 path: &str,
38 header: &[u8],
39 index: u64,
40 sealed: &[u8],
41 ) -> Result<Vec<u8>, CryptoError> {
42 cipher::open(&self.0, &nonce(index), &aad(path, header, index), sealed)
43 }
44}
45
46impl std::fmt::Debug for FileKey {
47 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48 f.write_str("FileKey([REDACTED])")
49 }
50}
51
52fn nonce(index: u64) -> [u8; 24] {
53 let mut nonce = [0; 24];
54 nonce[16..].copy_from_slice(&index.to_be_bytes());
55 nonce
56}
57
58fn aad(path: &str, header: &[u8], index: u64) -> Vec<u8> {
59 cipher::context(&[
60 b"coven/file-chunk/v1",
61 cipher::storage_path(path),
62 header,
63 &index.to_be_bytes(),
64 ])
65}
66
67#[cfg(test)]
68#[path = "file_key_tests.rs"]
69mod tests;