dcrypt_algorithms/block/modes/cbc/
mod.rs1#[cfg(not(feature = "std"))]
11use alloc::vec::Vec;
12use dcrypt_internal::zeroing::{boxed_bytes_zeroed, Zeroize, ZeroizeOnDrop, Zeroizing};
13
14use super::super::{BlockCipher, CipherAlgorithm};
15use crate::error::{validate, Error, Result};
16use crate::types::Nonce;
17
18pub trait CbcCompatible: crate::types::sealed::Sealed {}
20
21impl<const N: usize> CbcCompatible for Nonce<N> {}
23
24#[derive(Clone)]
26pub struct Cbc<B: BlockCipher + Zeroize + ZeroizeOnDrop> {
27 cipher: B,
28 iv: Vec<u8>,
29}
30
31impl<B: BlockCipher + Zeroize + ZeroizeOnDrop> Zeroize for Cbc<B> {
32 fn zeroize(&mut self) {
33 self.cipher.zeroize();
34 self.iv.zeroize();
35 }
36}
37
38impl<B: BlockCipher + Zeroize + ZeroizeOnDrop> Drop for Cbc<B> {
39 fn drop(&mut self) {
40 self.zeroize();
41 }
42}
43
44impl<B: BlockCipher + Zeroize + ZeroizeOnDrop> ZeroizeOnDrop for Cbc<B> {}
45
46impl<B: BlockCipher + CipherAlgorithm + Zeroize + ZeroizeOnDrop> Cbc<B> {
47 pub fn new<const N: usize>(cipher: B, iv: &Nonce<N>) -> Result<Self>
51 where
52 Nonce<N>: CbcCompatible,
53 {
54 validate::length("CBC initialization vector", N, B::block_size())?;
56
57 Ok(Self {
58 cipher,
59 iv: iv.as_ref().to_vec(),
60 })
61 }
62
63 pub fn encrypt(&self, plaintext: &[u8]) -> Result<Vec<u8>> {
69 let block_size = B::block_size();
71 if plaintext.len() % block_size != 0 {
72 let expected_len = ((plaintext.len() / block_size) + 1) * block_size;
73 return Err(Error::Length {
74 context: "CBC plaintext",
75 expected: expected_len,
76 actual: plaintext.len(),
77 });
78 }
79
80 let mut ciphertext = Vec::with_capacity(plaintext.len());
81 let mut prev_block = self.iv.clone();
82
83 for chunk in plaintext.chunks(block_size) {
85 let mut block = Zeroizing::new([0u8; 16]); block[..chunk.len()].copy_from_slice(chunk);
87
88 for i in 0..block_size {
90 block[i] ^= prev_block[i];
91 }
92
93 self.cipher.encrypt_block(&mut block[..])?;
95
96 ciphertext.extend_from_slice(&block[..]);
98 prev_block = block.to_vec();
99 }
100
101 Ok(ciphertext)
102 }
103
104 pub fn decrypt(&self, ciphertext: &[u8]) -> Result<Vec<u8>> {
108 let block_size = B::block_size();
110 if ciphertext.len() % block_size != 0 {
111 let expected_len = ((ciphertext.len() / block_size) + 1) * block_size;
112 return Err(Error::Length {
113 context: "CBC ciphertext",
114 expected: expected_len,
115 actual: ciphertext.len(),
116 });
117 }
118
119 let mut plaintext = Zeroizing::new(boxed_bytes_zeroed(ciphertext.len()));
120 let mut prev_block = self.iv.clone();
121 let mut plaintext_offset = 0usize;
122
123 for chunk in ciphertext.chunks(block_size) {
125 let mut block = Zeroizing::new([0u8; 16]); block[..chunk.len()].copy_from_slice(chunk);
127
128 let current_block = *block;
130
131 self.cipher.decrypt_block(&mut block[..])?;
133
134 for i in 0..block_size {
136 block[i] ^= prev_block[i];
137 }
138
139 plaintext[plaintext_offset..plaintext_offset + block_size]
141 .copy_from_slice(&block[..block_size]);
142 plaintext_offset += block_size;
143 prev_block = current_block.to_vec();
144 }
145
146 Ok(plaintext.into_inner().into_vec())
147 }
148}
149
150#[cfg(test)]
151mod tests;