1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
use crate::utils::xor;
use cipher::{
    errors::LoopError, Block, BlockCipher, BlockEncrypt, FromBlockCipher, NewBlockCipher,
    StreamCipher,
};
use core::{marker::PhantomData, ops::Mul};
use generic_array::typenum::{
    type_operators::{IsGreater, IsLessOrEqual},
    Prod, Unsigned, U0, U1, U255,
};
use generic_array::{ArrayLength, GenericArray};

/// Output feedback (OFB) mode of operation as defined in GOST R 34.13-2015
///
/// Type parameters:
/// - `C`: block cipher.
/// - `Z`: nonce length in block sizes. Default: 1.
/// - `S`: number of block bytes used for message encryption. Default: block size.
///
/// With default parameters this mode is fully equivalent to the `Ofb` mode defined
/// in the `block-modes` crate.
#[derive(Clone)]
pub struct GostOfb<C, Z = U1, S = <C as BlockCipher>::BlockSize>
where
    C: BlockCipher + BlockEncrypt + NewBlockCipher,
    C::BlockSize: IsLessOrEqual<U255>,
    S: Unsigned + IsGreater<U0> + IsLessOrEqual<C::BlockSize>,
    Z: ArrayLength<Block<C>> + Unsigned + Mul<C::BlockSize> + IsGreater<U0> + IsLessOrEqual<U255>,
    Prod<Z, C::BlockSize>: ArrayLength<u8>,
{
    cipher: C,
    state: GenericArray<Block<C>, Z>,
    block_pos: u8,
    pos: u8,
    _p: PhantomData<S>,
}

impl<C, Z, S> FromBlockCipher for GostOfb<C, Z, S>
where
    C: BlockCipher + BlockEncrypt + NewBlockCipher,
    C::BlockSize: IsLessOrEqual<U255>,
    S: Unsigned + IsGreater<U0> + IsLessOrEqual<C::BlockSize>,
    Z: ArrayLength<Block<C>> + Unsigned + Mul<C::BlockSize> + IsGreater<U0> + IsLessOrEqual<U255>,
    Prod<Z, C::BlockSize>: ArrayLength<u8>,
{
    type BlockCipher = C;
    type NonceSize = Prod<Z, C::BlockSize>;

    fn from_block_cipher(cipher: C, nonce: &GenericArray<u8, Self::NonceSize>) -> Self {
        let bs = C::BlockSize::to_usize();
        let mut state: GenericArray<Block<C>, Z> = Default::default();
        for (chunk, block) in nonce.chunks_exact(bs).zip(state.iter_mut()) {
            let mut t = GenericArray::clone_from_slice(chunk);
            cipher.encrypt_block(&mut t);
            *block = t;
        }

        Self {
            cipher,
            state,
            block_pos: 0,
            pos: 0,
            _p: Default::default(),
        }
    }
}

impl<C, Z, S> StreamCipher for GostOfb<C, Z, S>
where
    C: BlockCipher + BlockEncrypt + NewBlockCipher,
    C::BlockSize: IsLessOrEqual<U255>,
    S: Unsigned + IsGreater<U0> + IsLessOrEqual<C::BlockSize>,
    Z: ArrayLength<Block<C>> + Unsigned + Mul<C::BlockSize> + IsGreater<U0> + IsLessOrEqual<U255>,
    Prod<Z, C::BlockSize>: ArrayLength<u8>,
{
    fn try_apply_keystream(&mut self, mut data: &mut [u8]) -> Result<(), LoopError> {
        let s = S::USIZE;
        let pos = self.pos as usize;
        let block_pos = self.block_pos as usize;

        if data.len() < s - pos {
            let n = data.len();
            xor(data, &self.state[block_pos][pos..pos + n]);
            self.pos += n as u8;
            return Ok(());
        } else if pos != 0 {
            let (l, r) = { data }.split_at_mut(s - pos);
            data = r;
            xor(l, &self.state[block_pos][pos..s]);
            self.pos = 0;
            self.cipher
                .encrypt_block(&mut self.state[self.block_pos as usize]);
            self.block_pos = (self.block_pos + 1) % Z::U8;
        }

        let mut iter = data.chunks_exact_mut(s);
        for chunk in &mut iter {
            xor(chunk, &self.state[self.block_pos as usize][..s]);
            self.cipher
                .encrypt_block(&mut self.state[self.block_pos as usize]);
            self.block_pos = (self.block_pos + 1) % Z::U8;
        }
        let rem = iter.into_remainder();
        xor(rem, &self.state[self.block_pos as usize][..rem.len()]);
        self.pos += rem.len() as u8;

        Ok(())
    }
}