gost_modes/
ofb.rs

1use crate::utils::xor;
2use cipher::{
3    errors::LoopError, Block, BlockCipher, BlockEncrypt, FromBlockCipher, NewBlockCipher,
4    StreamCipher,
5};
6use core::{marker::PhantomData, ops::Mul};
7use generic_array::typenum::{
8    type_operators::{IsGreater, IsLessOrEqual},
9    Prod, Unsigned, U0, U1, U255,
10};
11use generic_array::{ArrayLength, GenericArray};
12
13/// Output feedback (OFB) mode of operation as defined in GOST R 34.13-2015
14///
15/// Type parameters:
16/// - `C`: block cipher.
17/// - `Z`: nonce length in block sizes. Default: 1.
18/// - `S`: number of block bytes used for message encryption. Default: block size.
19///
20/// With default parameters this mode is fully equivalent to the `Ofb` mode defined
21/// in the `block-modes` crate.
22#[derive(Clone)]
23pub struct GostOfb<C, Z = U1, S = <C as BlockCipher>::BlockSize>
24where
25    C: BlockCipher + BlockEncrypt + NewBlockCipher,
26    C::BlockSize: IsLessOrEqual<U255>,
27    S: Unsigned + IsGreater<U0> + IsLessOrEqual<C::BlockSize>,
28    Z: ArrayLength<Block<C>> + Unsigned + Mul<C::BlockSize> + IsGreater<U0> + IsLessOrEqual<U255>,
29    Prod<Z, C::BlockSize>: ArrayLength<u8>,
30{
31    cipher: C,
32    state: GenericArray<Block<C>, Z>,
33    block_pos: u8,
34    pos: u8,
35    _p: PhantomData<S>,
36}
37
38impl<C, Z, S> FromBlockCipher for GostOfb<C, Z, S>
39where
40    C: BlockCipher + BlockEncrypt + NewBlockCipher,
41    C::BlockSize: IsLessOrEqual<U255>,
42    S: Unsigned + IsGreater<U0> + IsLessOrEqual<C::BlockSize>,
43    Z: ArrayLength<Block<C>> + Unsigned + Mul<C::BlockSize> + IsGreater<U0> + IsLessOrEqual<U255>,
44    Prod<Z, C::BlockSize>: ArrayLength<u8>,
45{
46    type BlockCipher = C;
47    type NonceSize = Prod<Z, C::BlockSize>;
48
49    fn from_block_cipher(cipher: C, nonce: &GenericArray<u8, Self::NonceSize>) -> Self {
50        let bs = C::BlockSize::to_usize();
51        let mut state: GenericArray<Block<C>, Z> = Default::default();
52        for (chunk, block) in nonce.chunks_exact(bs).zip(state.iter_mut()) {
53            let mut t = GenericArray::clone_from_slice(chunk);
54            cipher.encrypt_block(&mut t);
55            *block = t;
56        }
57
58        Self {
59            cipher,
60            state,
61            block_pos: 0,
62            pos: 0,
63            _p: Default::default(),
64        }
65    }
66}
67
68impl<C, Z, S> StreamCipher for GostOfb<C, Z, S>
69where
70    C: BlockCipher + BlockEncrypt + NewBlockCipher,
71    C::BlockSize: IsLessOrEqual<U255>,
72    S: Unsigned + IsGreater<U0> + IsLessOrEqual<C::BlockSize>,
73    Z: ArrayLength<Block<C>> + Unsigned + Mul<C::BlockSize> + IsGreater<U0> + IsLessOrEqual<U255>,
74    Prod<Z, C::BlockSize>: ArrayLength<u8>,
75{
76    fn try_apply_keystream(&mut self, mut data: &mut [u8]) -> Result<(), LoopError> {
77        let s = S::USIZE;
78        let pos = self.pos as usize;
79        let block_pos = self.block_pos as usize;
80
81        if data.len() < s - pos {
82            let n = data.len();
83            xor(data, &self.state[block_pos][pos..pos + n]);
84            self.pos += n as u8;
85            return Ok(());
86        } else if pos != 0 {
87            let (l, r) = { data }.split_at_mut(s - pos);
88            data = r;
89            xor(l, &self.state[block_pos][pos..s]);
90            self.pos = 0;
91            self.cipher
92                .encrypt_block(&mut self.state[self.block_pos as usize]);
93            self.block_pos = (self.block_pos + 1) % Z::U8;
94        }
95
96        let mut iter = data.chunks_exact_mut(s);
97        for chunk in &mut iter {
98            xor(chunk, &self.state[self.block_pos as usize][..s]);
99            self.cipher
100                .encrypt_block(&mut self.state[self.block_pos as usize]);
101            self.block_pos = (self.block_pos + 1) % Z::U8;
102        }
103        let rem = iter.into_remainder();
104        xor(rem, &self.state[self.block_pos as usize][..rem.len()]);
105        self.pos += rem.len() as u8;
106
107        Ok(())
108    }
109}