cloud_disk_sync/encryption/
types.rs1use rand::RngCore;
2use rand::rngs::OsRng;
3use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
7pub enum EncryptionAlgorithm {
8 Aes256Gcm,
10 Aes256Cbc,
12 ChaCha20Poly1305,
14 XChaCha20Poly1305,
16 Aes256GcmSiv,
18}
19
20impl EncryptionAlgorithm {
21 pub fn key_size(&self) -> usize {
22 match self {
23 Self::Aes256Gcm => 32,
24 Self::Aes256Cbc => 32,
25 Self::ChaCha20Poly1305 => 32,
26 Self::XChaCha20Poly1305 => 32,
27 Self::Aes256GcmSiv => 32,
28 }
29 }
30
31 pub fn iv_size(&self) -> usize {
32 match self {
33 Self::Aes256Gcm => 12,
34 Self::Aes256Cbc => 16,
35 Self::ChaCha20Poly1305 => 12,
36 Self::XChaCha20Poly1305 => 24,
37 Self::Aes256GcmSiv => 12,
38 }
39 }
40
41 pub fn tag_size(&self) -> usize {
42 match self {
43 Self::Aes256Gcm => 16,
44 Self::Aes256Cbc => 0, Self::ChaCha20Poly1305 => 16,
46 Self::XChaCha20Poly1305 => 16,
47 Self::Aes256GcmSiv => 16,
48 }
49 }
50
51 pub fn recommended_iv_mode(&self) -> IvMode {
52 match self {
53 Self::Aes256Gcm => IvMode::Random,
54 Self::Aes256Cbc => IvMode::Random,
55 Self::ChaCha20Poly1305 => IvMode::Random,
56 Self::XChaCha20Poly1305 => IvMode::Random,
57 Self::Aes256GcmSiv => IvMode::Random,
58 }
59 }
60}
61
62#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
63pub enum IvMode {
64 Random,
66 Derived,
68 Fixed,
70 Counter,
72 FileOffset,
74}
75
76impl IvMode {
77 pub fn requires_unique_per_file(&self) -> bool {
78 match self {
79 Self::Random => true,
80 Self::Derived => true,
81 Self::Fixed => false,
82 Self::Counter => true,
83 Self::FileOffset => true,
84 }
85 }
86
87 pub fn generate_iv(&self, context: &IvContext) -> Vec<u8> {
88 match self {
89 Self::Random => self.generate_random_iv(context.iv_size),
90 Self::Derived => self.generate_derived_iv(context),
91 Self::Fixed => context.fixed_iv.clone().unwrap_or_default(),
92 Self::Counter => self.generate_counter_iv(context),
93 Self::FileOffset => self.generate_file_offset_iv(context),
94 }
95 }
96
97 fn generate_random_iv(&self, size: usize) -> Vec<u8> {
98 let mut iv = vec![0u8; size];
99 for b in iv.iter_mut() {
100 *b = rand::random();
101 }
102 iv
103 }
104
105 fn generate_derived_iv(&self, context: &IvContext) -> Vec<u8> {
106 use sha2::{Digest, Sha256};
107 let mut hasher = Sha256::new();
108
109 if let Some(data) = &context.data {
110 hasher.update(data);
111 }
112
113 if let Some(file_path) = &context.file_path {
114 hasher.update(file_path.as_bytes());
115 }
116
117 if let Some(file_hash) = &context.file_hash {
118 hasher.update(file_hash);
119 }
120
121 let result = hasher.finalize();
122 result[..context.iv_size].to_vec()
123 }
124
125 fn generate_counter_iv(&self, context: &IvContext) -> Vec<u8> {
126 let counter = context.counter.unwrap_or(0);
127 let mut iv = vec![0u8; context.iv_size];
128
129 for i in 0..8 {
131 if i < context.iv_size {
132 iv[i] = ((counter >> (i * 8)) & 0xFF) as u8;
133 }
134 }
135
136 iv
137 }
138
139 fn generate_file_offset_iv(&self, context: &IvContext) -> Vec<u8> {
140 let offset = context.file_offset.unwrap_or(0);
141 let mut iv = vec![0u8; context.iv_size];
142
143 for i in 0..8 {
144 if i < context.iv_size {
145 iv[i] = ((offset >> (i * 8)) & 0xFF) as u8;
146 }
147 }
148
149 iv
150 }
151}
152
153#[derive(Debug, Clone)]
154pub struct IvContext {
155 pub iv_size: usize,
156 pub data: Option<Vec<u8>>,
157 pub file_path: Option<String>,
158 pub file_hash: Option<String>,
159 pub counter: Option<u64>,
160 pub file_offset: Option<u64>,
161 pub fixed_iv: Option<Vec<u8>>,
162}