literate_crypto/cipher/block/modes/
ctr.rs

1use {
2    crate::{BlockEncrypt, BlockMode, Cipher, CipherDecrypt, CipherEncrypt, OneTimePad},
3    docext::docext,
4    std::{convert::Infallible, fmt, iter, mem},
5};
6
7/// Block counter [mode](crate::BlockMode) is a block chaining mode which turns
8/// a block cipher into a stream cipher, and hence does not require a [padding
9/// scheme](crate::Padding).
10///
11/// The algorithm keeps a monotonically incrementing counter. The
12/// plaintext is split into blocks. Each block of plaintext is encrypted by
13/// converting the counter into bytes, converting the bytes into a block (by
14/// appending as many zero bytes as needed to reach the block size),
15/// encrypting that block with the underlying block cipher, and XORing the
16/// ciphertext block with the appropriate block of plaintext. Afterwards, the
17/// block counter is incremented, and the process is repeated until there are no
18/// blocks left.
19///
20/// If the last block of plaintext is shorter than the block size, the last
21/// block of ciphertext is simply truncated to the length of the remaining
22/// plaintext.
23///
24/// The block counter is first set to some initial value, called the nonce. Like
25/// the [IV](crate::Cbc#iv) for [CBC mode](crate::Cbc), the nonce does not need
26/// to be secret, but it needs to be unique.
27///
28/// Because the XOR operation cancels itself ($X \oplus Y \oplus Y = X$ for any
29/// $X, Y$), the decryption is exactly the same as encryption. Notably, it only
30/// relies on the [encryption function](crate::BlockEncrypt) of the underlying
31/// block cipher. The [decryption function](crate::BlockDecrypt) is never used.
32///
33/// The operation of counter mode essentially represents a [one-time
34/// pad](crate::OneTimePad), where the keystream is generated using the
35/// underlying block cipher and the block counter.
36#[docext]
37#[derive(Debug, Clone)]
38pub struct Ctr<Enc> {
39    enc: Enc,
40    nonce: u64,
41}
42
43impl<Enc> Cipher for Ctr<Enc>
44where
45    Enc: BlockEncrypt,
46    Enc::EncryptionBlock: IntoIterator<Item = u8> + AsMut<[u8]> + Default,
47    Enc::EncryptionKey: 'static + Clone,
48{
49    type Key = Enc::EncryptionKey;
50}
51
52impl<Enc> BlockMode for Ctr<Enc>
53where
54    Enc: BlockEncrypt,
55    Enc::EncryptionBlock: IntoIterator<Item = u8> + AsMut<[u8]> + Default,
56    Enc::EncryptionKey: 'static + Clone,
57{
58}
59
60impl<Enc, const BLOCK_SIZE: usize> Ctr<Enc>
61where
62    Enc: BlockEncrypt<EncryptionBlock = [u8; BLOCK_SIZE]>,
63{
64    pub fn new(enc: Enc, nonce: u64) -> Result<Self, BlockSizeTooSmall> {
65        if BLOCK_SIZE < mem::size_of_val(&nonce) {
66            Err(BlockSizeTooSmall)
67        } else {
68            Ok(Self { enc, nonce })
69        }
70    }
71}
72
73impl<Enc> CipherEncrypt for Ctr<Enc>
74where
75    Enc: BlockEncrypt,
76    Enc::EncryptionBlock: IntoIterator<Item = u8> + AsMut<[u8]> + Default,
77    Enc::EncryptionKey: 'static + Clone,
78{
79    type EncryptionErr = Infallible;
80    type EncryptionKey = Enc::EncryptionKey;
81
82    fn encrypt(
83        &self,
84        data: Vec<u8>,
85        key: Self::EncryptionKey,
86    ) -> Result<Vec<u8>, Self::EncryptionErr> {
87        Ok(OneTimePad::default()
88            .encrypt(data, keystream(&self.enc, key, self.nonce))
89            .expect("infinite keystream"))
90    }
91}
92
93impl<Enc> CipherDecrypt for Ctr<Enc>
94where
95    Enc: BlockEncrypt,
96    Enc::EncryptionBlock: IntoIterator<Item = u8> + AsMut<[u8]> + Default,
97    Enc::EncryptionKey: 'static + Clone,
98{
99    type DecryptionErr = Infallible;
100    type DecryptionKey = Enc::EncryptionKey;
101
102    fn decrypt(
103        &self,
104        data: Vec<u8>,
105        key: Self::DecryptionKey,
106    ) -> Result<Vec<u8>, Self::DecryptionErr> {
107        Ok(OneTimePad::default()
108            .decrypt(data, keystream(&self.enc, key, self.nonce))
109            .expect("infinite keystream"))
110    }
111}
112
113fn keystream<Enc>(enc: &Enc, key: Enc::EncryptionKey, nonce: u64) -> impl Iterator<Item = u8> + '_
114where
115    Enc: BlockEncrypt,
116    Enc::EncryptionBlock: IntoIterator<Item = u8> + AsMut<[u8]> + Default,
117    Enc::EncryptionKey: 'static + Clone,
118{
119    iter::successors(Some(nonce), |ctr| Some(ctr.wrapping_add(1))).flat_map(move |ctr| {
120        // Copy the counter bytes into a block and encrypt it.
121        let mut ctr_block = Enc::EncryptionBlock::default();
122        ctr_block
123            .as_mut()
124            .iter_mut()
125            .zip(ctr.to_le_bytes())
126            .for_each(|(b, n)| *b = n);
127        enc.encrypt(ctr_block, key.clone()).into_iter()
128    })
129}
130
131#[derive(Debug)]
132pub struct BlockSizeTooSmall;
133
134impl fmt::Display for BlockSizeTooSmall {
135    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
136        f.write_str("block size too small to fit counter")
137    }
138}