dcrypt_algorithms/block/modes/cbc/
mod.rs

1//! Cipher Block Chaining (CBC) mode implementation
2//!
3//! CBC mode is a block cipher mode of operation that provides confidentiality
4//! by XORing each plaintext block with the previous ciphertext block before
5//! encryption. The first block is XORed with an initialization vector (IV).
6//!
7//! This implementation follows NIST SP 800-38A specifications and provides
8//! secure memory handling with automatic zeroization of sensitive data.
9
10#[cfg(not(feature = "std"))]
11use alloc::vec::Vec;
12use zeroize::{Zeroize, ZeroizeOnDrop};
13
14use super::super::{BlockCipher, CipherAlgorithm};
15use crate::error::{validate, Error, Result};
16use crate::types::Nonce;
17
18/// Marker trait for nonces that are compatible with CBC mode
19pub trait CbcCompatible: crate::types::sealed::Sealed {}
20
21// Implement CbcCompatible trait for Nonce types that match block sizes
22impl<const N: usize> CbcCompatible for Nonce<N> {}
23
24/// CBC mode implementation
25#[derive(Clone, Zeroize, ZeroizeOnDrop)]
26pub struct Cbc<B: BlockCipher + Zeroize + ZeroizeOnDrop> {
27    cipher: B,
28    iv: Vec<u8>,
29}
30
31impl<B: BlockCipher + CipherAlgorithm + Zeroize + ZeroizeOnDrop> Cbc<B> {
32    /// Creates a new CBC mode instance with the given cipher and IV
33    ///
34    /// The IV (nonce) must be the same size as the block size of the cipher.
35    pub fn new<const N: usize>(cipher: B, iv: &Nonce<N>) -> Result<Self>
36    where
37        Nonce<N>: CbcCompatible,
38    {
39        // Validate that the nonce size matches the block size at runtime
40        validate::length("CBC initialization vector", N, B::block_size())?;
41
42        Ok(Self {
43            cipher,
44            iv: iv.as_ref().to_vec(),
45        })
46    }
47
48    /// Encrypts a message using CBC mode
49    ///
50    /// The plaintext must be a multiple of the block size.
51    /// For plaintext that is not a multiple of the block size,
52    /// padding must be applied before calling this function.
53    pub fn encrypt(&self, plaintext: &[u8]) -> Result<Vec<u8>> {
54        // Validate plaintext length is a multiple of block size
55        let block_size = B::block_size();
56        if plaintext.len() % block_size != 0 {
57            let expected_len = ((plaintext.len() / block_size) + 1) * block_size;
58            return Err(Error::Length {
59                context: "CBC plaintext",
60                expected: expected_len,
61                actual: plaintext.len(),
62            });
63        }
64
65        let mut ciphertext = Vec::with_capacity(plaintext.len());
66        let mut prev_block = self.iv.clone();
67
68        // Process the plaintext in blocks
69        for chunk in plaintext.chunks(block_size) {
70            let mut block = [0u8; 16]; // AES block size is 16 bytes
71            block[..chunk.len()].copy_from_slice(chunk);
72
73            // XOR with previous ciphertext block (or IV for the first block)
74            for i in 0..block_size {
75                block[i] ^= prev_block[i];
76            }
77
78            // Encrypt the XORed block
79            self.cipher.encrypt_block(&mut block)?;
80
81            // Append to ciphertext and update previous block
82            ciphertext.extend_from_slice(&block);
83            prev_block = block.to_vec();
84        }
85
86        Ok(ciphertext)
87    }
88
89    /// Decrypts a message using CBC mode
90    ///
91    /// The ciphertext must be a multiple of the block size.
92    pub fn decrypt(&self, ciphertext: &[u8]) -> Result<Vec<u8>> {
93        // Validate ciphertext length is a multiple of block size
94        let block_size = B::block_size();
95        if ciphertext.len() % block_size != 0 {
96            let expected_len = ((ciphertext.len() / block_size) + 1) * block_size;
97            return Err(Error::Length {
98                context: "CBC ciphertext",
99                expected: expected_len,
100                actual: ciphertext.len(),
101            });
102        }
103
104        let mut plaintext = Vec::with_capacity(ciphertext.len());
105        let mut prev_block = self.iv.clone();
106
107        // Process the ciphertext in blocks
108        for chunk in ciphertext.chunks(block_size) {
109            let mut block = [0u8; 16]; // AES block size is 16 bytes
110            block[..chunk.len()].copy_from_slice(chunk);
111
112            // Save current ciphertext block
113            let current_block = block;
114
115            // Decrypt the block
116            self.cipher.decrypt_block(&mut block)?;
117
118            // XOR with previous ciphertext block (or IV for the first block)
119            for i in 0..block_size {
120                block[i] ^= prev_block[i];
121            }
122
123            // Append to plaintext and update previous block
124            plaintext.extend_from_slice(&block);
125            prev_block = current_block.to_vec();
126        }
127
128        Ok(plaintext)
129    }
130}
131
132#[cfg(test)]
133mod tests;