Skip to main content
This is unreleased documentation for the main (development) branch of crypto-glue.

cts/
cbc_cs2.rs

1use crate::{Decrypt, Encrypt, Error, cbc_dec, cbc_enc, xor};
2use cipher::{
3    Block, BlockCipherDecBackend, BlockCipherDecClosure, BlockCipherDecrypt, BlockCipherEncBackend,
4    BlockCipherEncClosure, BlockCipherEncrypt, BlockSizeUser, InnerIvInit, IvSizeUser,
5    array::{Array, ArraySize},
6    common::InnerUser,
7    inout::InOutBuf,
8    typenum::Unsigned,
9};
10
11/// The CBC-CS-2 ciphertext stealing mode.
12#[derive(Debug)]
13pub struct CbcCs2<C: BlockSizeUser> {
14    cipher: C,
15    iv: Block<C>,
16}
17
18impl<C: BlockSizeUser> InnerUser for CbcCs2<C> {
19    type Inner = C;
20}
21
22impl<C: BlockSizeUser> IvSizeUser for CbcCs2<C> {
23    type IvSize = C::BlockSize;
24}
25
26impl<C: BlockSizeUser> InnerIvInit for CbcCs2<C> {
27    fn inner_iv_init(cipher: Self::Inner, iv: &cipher::Iv<Self>) -> Self {
28        Self {
29            cipher,
30            iv: iv.clone(),
31        }
32    }
33}
34
35impl<C: BlockCipherEncrypt> Encrypt for CbcCs2<C> {
36    fn encrypt_inout(self, buf: InOutBuf<'_, '_, u8>) -> Result<(), Error> {
37        if buf.len() < C::BlockSize::USIZE {
38            return Err(Error);
39        }
40        let Self { cipher, iv } = self;
41        cipher.encrypt_with_backend(Closure { iv, buf });
42        Ok(())
43    }
44}
45
46impl<C: BlockCipherDecrypt> Decrypt for CbcCs2<C> {
47    fn decrypt_inout(self, buf: InOutBuf<'_, '_, u8>) -> Result<(), Error> {
48        if buf.len() < C::BlockSize::USIZE {
49            return Err(Error);
50        }
51        let Self { cipher, iv } = self;
52        cipher.decrypt_with_backend(Closure { iv, buf });
53        Ok(())
54    }
55}
56
57struct Closure<'a, BS: ArraySize> {
58    iv: Array<u8, BS>,
59    buf: InOutBuf<'a, 'a, u8>,
60}
61
62impl<BS: ArraySize> BlockSizeUser for Closure<'_, BS> {
63    type BlockSize = BS;
64}
65
66impl<BS: ArraySize> BlockCipherEncClosure for Closure<'_, BS> {
67    fn call<B: BlockCipherEncBackend<BlockSize = BS>>(self, cipher: &B) {
68        let Self { mut iv, mut buf } = self;
69        let (mut blocks, mut tail) = buf.reborrow().into_chunks();
70
71        cbc_enc(cipher, &mut iv, blocks.reborrow());
72
73        if tail.is_empty() {
74            return;
75        }
76
77        let mut block = Block::<B>::default();
78        block[..tail.len()].copy_from_slice(tail.get_in());
79        xor(&mut block, &iv);
80        cipher.encrypt_block_inplace(&mut block);
81
82        let penult_block = core::mem::replace(blocks.get_out().last_mut().unwrap(), block);
83        let tail_val = &penult_block[..tail.len()];
84        tail.get_out().copy_from_slice(tail_val);
85    }
86}
87
88impl<BS: ArraySize> BlockCipherDecClosure for Closure<'_, BS> {
89    fn call<B: BlockCipherDecBackend<BlockSize = BS>>(self, cipher: &B) {
90        let Self { mut iv, mut buf } = self;
91
92        let (mut blocks, tail) = buf.reborrow().into_chunks();
93
94        if !tail.is_empty() {
95            let mid = blocks.len() - 1;
96            blocks = blocks.split_at(mid).0;
97        };
98
99        cbc_dec(cipher, &mut iv, blocks);
100
101        if tail.is_empty() {
102            return;
103        }
104
105        let tail_len = tail.len();
106        let bs = B::BlockSize::USIZE;
107        let mid = buf.len() - (bs + tail_len);
108        let mut rem = buf.split_at(mid).1;
109
110        let n = rem.len() - bs;
111        let mut block1: Block<B> = rem.get_in()[..bs].try_into().unwrap();
112        cipher.decrypt_block_inplace(&mut block1);
113
114        let mut block2 = Block::<B>::default();
115        block2[..n].copy_from_slice(&rem.get_in()[bs..]);
116        block2[n..].copy_from_slice(&block1[n..]);
117
118        xor(&mut block1, &block2);
119
120        cipher.decrypt_block_inplace(&mut block2);
121        xor(&mut block2, &iv);
122
123        rem.get_out()[..bs].copy_from_slice(&block2);
124        rem.get_out()[bs..].copy_from_slice(&block1[..n]);
125    }
126}