dcrypt_algorithms/block/modes/ctr/
mod.rs1#[cfg(not(feature = "std"))]
10use alloc::vec::Vec;
11use dcrypt_internal::zeroing::{
12 boxed_bytes_zeroed, Zeroize, ZeroizeOnDrop, Zeroizing, ZeroizingBytes,
13};
14
15use super::super::BlockCipher;
16use crate::error::{validate, Result};
17use crate::types::nonce::AesCtrCompatible;
18use crate::types::Nonce;
19
20use dcrypt_common::security::barrier;
22
23#[derive(Debug, Clone, Copy, PartialEq)]
25pub enum CounterPosition {
26 Prefix,
29
30 Postfix,
33
34 Custom(usize),
37}
38
39#[derive(Clone)]
41pub struct Ctr<B: BlockCipher + Zeroize> {
42 cipher: B,
43 counter_block: ZeroizingBytes,
44 counter_position: usize,
45 counter_size: usize,
46 keystream: ZeroizingBytes,
47 keystream_pos: usize,
48}
49
50impl<B: BlockCipher + Zeroize> Zeroize for Ctr<B> {
51 fn zeroize(&mut self) {
52 self.cipher.zeroize();
53 self.counter_block.zeroize();
54 self.counter_position.zeroize();
55 self.counter_size.zeroize();
56 self.keystream.zeroize();
57 self.keystream_pos.zeroize();
58 }
59}
60
61impl<B: BlockCipher + Zeroize> Drop for Ctr<B> {
62 fn drop(&mut self) {
63 self.zeroize();
64 }
65}
66
67impl<B: BlockCipher + Zeroize> ZeroizeOnDrop for Ctr<B> {}
68
69impl<B: BlockCipher + Zeroize> Ctr<B> {
70 pub fn new<const N: usize>(cipher: B, nonce: &Nonce<N>) -> Result<Self>
78 where
79 Nonce<N>: AesCtrCompatible,
80 {
81 Self::with_counter_params(cipher, nonce, CounterPosition::Postfix, 4)
83 }
84
85 pub fn with_counter_params<const N: usize>(
95 cipher: B,
96 nonce: &Nonce<N>,
97 counter_pos: CounterPosition,
98 counter_size: usize,
99 ) -> Result<Self>
100 where
101 Nonce<N>: AesCtrCompatible,
102 {
103 let block_size = B::block_size();
104
105 validate::parameter(
107 counter_size > 0 && counter_size <= 8,
108 "counter_size",
109 "Counter size must be between 1 and 8 bytes",
110 )?;
111
112 let position = match counter_pos {
114 CounterPosition::Prefix => 0,
115 CounterPosition::Postfix => block_size - counter_size,
116 CounterPosition::Custom(offset) => {
117 validate::parameter(
118 offset + counter_size <= block_size,
119 "counter_position",
120 "Counter with specified size doesn't fit at offset in block",
121 )?;
122 offset
123 }
124 };
125
126 let mut counter_block = Zeroizing::new(boxed_bytes_zeroed(block_size));
128
129 let max_nonce_size = block_size - counter_size;
131
132 let effective_nonce = if N > max_nonce_size {
134 &nonce.as_ref()[0..max_nonce_size]
135 } else {
136 nonce.as_ref()
137 };
138
139 if position == 0 {
141 counter_block[counter_size..counter_size + effective_nonce.len()]
143 .copy_from_slice(effective_nonce);
144 } else {
145 counter_block[0..effective_nonce.len()].copy_from_slice(effective_nonce);
147 }
148
149 Ok(Self {
150 cipher,
151 counter_block,
152 counter_position: position,
153 counter_size,
154 keystream: Zeroizing::new(boxed_bytes_zeroed(0)),
155 keystream_pos: 0,
156 })
157 }
158
159 fn generate_keystream(&mut self) -> Result<()> {
161 let block_size = B::block_size();
162
163 self.keystream = Zeroizing::new(boxed_bytes_zeroed(block_size));
165
166 barrier::compiler_fence_seq_cst();
168
169 self.keystream.copy_from_slice(&self.counter_block);
171
172 self.cipher.encrypt_block(&mut self.keystream)?;
174
175 self.increment_counter();
177
178 self.keystream_pos = 0;
179
180 barrier::compiler_fence_seq_cst();
182
183 Ok(())
184 }
185
186 fn increment_counter(&mut self) {
188 match self.counter_size {
189 8 => {
190 let mut counter = [0u8; 8];
191 counter.copy_from_slice(
192 &self.counter_block[self.counter_position..self.counter_position + 8],
193 );
194 let value = u64::from_be_bytes(counter);
195 counter.copy_from_slice(&value.wrapping_add(1).to_be_bytes());
196 self.counter_block[self.counter_position..self.counter_position + 8]
197 .copy_from_slice(&counter);
198
199 counter.zeroize();
201 }
202 4 => {
203 let mut counter = [0u8; 4];
204 counter.copy_from_slice(
205 &self.counter_block[self.counter_position..self.counter_position + 4],
206 );
207 let value = u32::from_be_bytes(counter);
208 counter.copy_from_slice(&value.wrapping_add(1).to_be_bytes());
209 self.counter_block[self.counter_position..self.counter_position + 4]
210 .copy_from_slice(&counter);
211
212 counter.zeroize();
214 }
215 size => {
217 let mut value: u64 = 0;
218
219 for i in 0..size {
221 value = (value << 8) | (self.counter_block[self.counter_position + i] as u64);
222 }
223
224 value = value.wrapping_add(1);
226
227 for i in 0..size {
229 self.counter_block[self.counter_position + size - 1 - i] = (value & 0xff) as u8;
230 value >>= 8;
231 }
232 }
233 }
234 }
235
236 pub fn encrypt(&mut self, plaintext: &[u8]) -> Result<Vec<u8>> {
238 let mut ciphertext = Zeroizing::new(boxed_bytes_zeroed(plaintext.len()));
239
240 barrier::compiler_fence_seq_cst();
242
243 for (output, &byte) in ciphertext.iter_mut().zip(plaintext) {
244 if self.keystream_pos >= self.keystream.len() {
245 self.generate_keystream()?;
246 }
247
248 *output = byte ^ self.keystream[self.keystream_pos];
249 self.keystream_pos += 1;
250 }
251
252 barrier::compiler_fence_seq_cst();
254
255 Ok(ciphertext.into_inner().into_vec())
256 }
257
258 pub fn decrypt(&mut self, ciphertext: &[u8]) -> Result<Vec<u8>> {
261 self.encrypt(ciphertext)
262 }
263
264 pub fn process(&mut self, data: &mut [u8]) -> Result<()> {
266 barrier::compiler_fence_seq_cst();
268
269 for byte in data.iter_mut() {
270 if self.keystream_pos >= self.keystream.len() {
272 self.generate_keystream()?;
273 }
274
275 *byte ^= self.keystream[self.keystream_pos];
277 self.keystream_pos += 1;
278 }
279
280 barrier::compiler_fence_seq_cst();
282
283 Ok(())
284 }
285
286 pub fn keystream(&mut self, output: &mut [u8]) -> Result<()> {
288 for byte in output.iter_mut() {
290 *byte = 0;
291 }
292
293 self.keystream_pos = self.keystream.len();
295
296 self.process(output)
298 }
299
300 pub fn seek(&mut self, block_offset: u32) {
305 let mut counter_value = [0u8; 8];
307 counter_value[4..].copy_from_slice(&block_offset.wrapping_add(1).to_be_bytes());
308
309 for i in 0..self.counter_size {
311 let idx = self.counter_position + self.counter_size - 1 - i;
312 self.counter_block[idx] = counter_value[7 - i];
313 }
314
315 self.keystream_pos = self.keystream.len();
317
318 self.keystream = Zeroizing::new(boxed_bytes_zeroed(0));
320
321 counter_value.zeroize();
323 }
324
325 pub fn set_counter(&mut self, counter: u32) {
333 let counter_pos = self.counter_position;
335
336 let counter_bytes = counter.to_be_bytes();
339 let start_idx = 4 - self.counter_size;
340
341 for i in 0..self.counter_size {
342 if start_idx + i < 4 {
343 self.counter_block[counter_pos + i] = counter_bytes[start_idx + i];
345 }
346 }
347
348 self.keystream_pos = self.keystream.len();
350 }
351
352 pub fn reset<const N: usize>(&mut self, nonce: Option<&Nonce<N>>, counter: u32) -> Result<()>
360 where
361 Nonce<N>: AesCtrCompatible,
362 {
363 barrier::compiler_fence_seq_cst();
365
366 if let Some(new_nonce) = nonce {
368 let block_size = B::block_size();
369 let max_nonce_size = block_size - self.counter_size;
370
371 let effective_nonce = if N > max_nonce_size {
373 &new_nonce.as_ref()[0..max_nonce_size]
374 } else {
375 new_nonce.as_ref()
376 };
377
378 for b in &mut *self.counter_block {
380 *b = 0;
381 }
382
383 let counter_pos = match self.counter_position {
385 0 => self.counter_size, _ => 0, };
388
389 self.counter_block[counter_pos..counter_pos + effective_nonce.len()]
391 .copy_from_slice(effective_nonce);
392 }
393
394 self.set_counter(counter);
396
397 self.keystream = Zeroizing::new(boxed_bytes_zeroed(0));
399 self.keystream_pos = 0;
400
401 barrier::compiler_fence_seq_cst();
403
404 Ok(())
405 }
406}
407
408#[cfg(test)]
409mod tests;