craftio_rs/
cfb8.rs

1use aes::{
2    cipher::{consts::U16, generic_array::GenericArray, BlockCipherMut, NewBlockCipher},
3    Aes128,
4};
5#[cfg(feature = "backtrace")]
6use std::backtrace::Backtrace;
7use thiserror::Error;
8
9pub type CraftCipherResult<T> = Result<T, CipherError>;
10
11#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
12pub enum CipherComponent {
13    Key,
14    Iv,
15}
16
17#[derive(Debug, Error)]
18pub enum CipherError {
19    #[error("encryption is already enabled and cannot be enabled again")]
20    AlreadyEnabled {
21        #[cfg(feature = "backtrace")]
22        backtrace: Backtrace,
23    },
24    #[error("bad size '{size}' for '{component:?}'")]
25    BadSize {
26        size: usize,
27        component: CipherComponent,
28        #[cfg(feature = "backtrace")]
29        backtrace: Backtrace,
30    },
31}
32
33impl CipherError {
34    fn bad_size(component: CipherComponent, size: usize) -> Self {
35        CipherError::BadSize {
36            component,
37            size,
38            #[cfg(feature = "backtrace")]
39            backtrace: Backtrace::capture(),
40        }
41    }
42
43    fn already_enabled() -> Self {
44        CipherError::AlreadyEnabled {
45            #[cfg(feature = "backtrace")]
46            backtrace: Backtrace::capture(),
47        }
48    }
49}
50
51const BYTES_SIZE: usize = 16;
52
53pub struct CraftCipher {
54    iv: GenericArray<u8, U16>,
55    tmp: GenericArray<u8, U16>,
56    cipher: Aes128,
57}
58
59impl CraftCipher {
60    pub fn new(key: &[u8], iv: &[u8]) -> CraftCipherResult<Self> {
61        if iv.len() != BYTES_SIZE {
62            return Err(CipherError::bad_size(CipherComponent::Iv, iv.len()));
63        }
64
65        if key.len() != BYTES_SIZE {
66            return Err(CipherError::bad_size(CipherComponent::Key, key.len()));
67        }
68
69        let mut iv_out = [0u8; BYTES_SIZE];
70        iv_out.copy_from_slice(iv);
71
72        let mut key_out = [0u8; BYTES_SIZE];
73        key_out.copy_from_slice(key);
74
75        let tmp = [0u8; BYTES_SIZE];
76
77        Ok(Self {
78            iv: GenericArray::from(iv_out),
79            tmp: GenericArray::from(tmp),
80            cipher: Aes128::new(&GenericArray::from(key_out)),
81        })
82    }
83
84    pub fn encrypt(&mut self, data: &mut [u8]) {
85        unsafe { self.crypt(data, false) }
86    }
87
88    pub fn decrypt(&mut self, data: &mut [u8]) {
89        unsafe { self.crypt(data, true) }
90    }
91
92    unsafe fn crypt(&mut self, data: &mut [u8], decrypt: bool) {
93        let iv = &mut self.iv;
94        const IV_SIZE: usize = 16;
95        const IV_SIZE_MINUS_ONE: usize = IV_SIZE - 1;
96        let iv_ptr = iv.as_mut_ptr();
97        let iv_end_ptr = iv_ptr.offset(IV_SIZE_MINUS_ONE as isize);
98        let tmp_ptr = self.tmp.as_mut_ptr();
99        let tmp_offset_one_ptr = tmp_ptr.offset(1);
100        let cipher = &mut self.cipher;
101        let n = data.len();
102        let mut data_ptr = data.as_mut_ptr();
103        let data_end_ptr = data_ptr.offset(n as isize);
104
105        while data_ptr != data_end_ptr {
106            std::ptr::copy_nonoverlapping(iv_ptr, tmp_ptr, IV_SIZE);
107            cipher.encrypt_block(iv);
108            let orig = *data_ptr;
109            let updated = orig ^ *iv_ptr;
110            std::ptr::copy_nonoverlapping(tmp_offset_one_ptr, iv_ptr, IV_SIZE_MINUS_ONE);
111            if decrypt {
112                *iv_end_ptr = orig;
113            } else {
114                *iv_end_ptr = updated;
115            }
116            *data_ptr = updated;
117            data_ptr = data_ptr.offset(1);
118        }
119    }
120}
121
122pub(crate) fn setup_craft_cipher(
123    target: &mut Option<CraftCipher>,
124    key: &[u8],
125    iv: &[u8],
126) -> Result<(), CipherError> {
127    if target.is_some() {
128        Err(CipherError::already_enabled())
129    } else {
130        *target = Some(CraftCipher::new(key, iv)?);
131        Ok(())
132    }
133}