aboutsummaryrefslogtreecommitdiff
path: root/src/cfb8.rs
blob: 3b15e0be176fa0ffed4cf2af5dd8a01a1cab77c7 (plain)
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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
use aes::{
    cipher::{consts::U16, generic_array::GenericArray, BlockCipherMut, NewBlockCipher},
    Aes128,
};
#[cfg(feature = "backtrace")]
use std::backtrace::Backtrace;
use thiserror::Error;

pub type CraftCipherResult<T> = Result<T, CipherError>;

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum CipherComponent {
    Key,
    Iv,
}

#[derive(Debug, Error)]
pub enum CipherError {
    #[error("encryption is already enabled and cannot be enabled again")]
    AlreadyEnabled {
        #[cfg(feature = "backtrace")]
        backtrace: Backtrace,
    },
    #[error("bad size '{size}' for '{component:?}'")]
    BadSize {
        size: usize,
        component: CipherComponent,
        #[cfg(feature = "backtrace")]
        backtrace: Backtrace,
    },
}

impl CipherError {
    fn bad_size(component: CipherComponent, size: usize) -> Self {
        CipherError::BadSize {
            component,
            size,
            #[cfg(feature = "backtrace")]
            backtrace: Backtrace::capture(),
        }
    }

    fn already_enabled() -> Self {
        CipherError::AlreadyEnabled {
            #[cfg(feature = "backtrace")]
            backtrace: Backtrace::capture(),
        }
    }
}

const BYTES_SIZE: usize = 16;

pub struct CraftCipher {
    iv: GenericArray<u8, U16>,
    tmp: GenericArray<u8, U16>,
    cipher: Aes128,
}

impl CraftCipher {
    pub fn new(key: &[u8], iv: &[u8]) -> CraftCipherResult<Self> {
        if iv.len() != BYTES_SIZE {
            return Err(CipherError::bad_size(CipherComponent::Iv, iv.len()));
        }

        if key.len() != BYTES_SIZE {
            return Err(CipherError::bad_size(CipherComponent::Key, key.len()));
        }

        let mut iv_out = [0u8; BYTES_SIZE];
        iv_out.copy_from_slice(iv);

        let mut key_out = [0u8; BYTES_SIZE];
        key_out.copy_from_slice(key);

        let tmp = [0u8; BYTES_SIZE];

        Ok(Self {
            iv: GenericArray::from(iv_out),
            tmp: GenericArray::from(tmp),
            cipher: Aes128::new(&GenericArray::from(key_out)),
        })
    }

    pub fn encrypt(&mut self, data: &mut [u8]) {
        unsafe { self.crypt(data, false) }
    }

    pub fn decrypt(&mut self, data: &mut [u8]) {
        unsafe { self.crypt(data, true) }
    }

    unsafe fn crypt(&mut self, data: &mut [u8], decrypt: bool) {
        let iv = &mut self.iv;
        const IV_SIZE: usize = 16;
        const IV_SIZE_MINUS_ONE: usize = IV_SIZE - 1;
        let iv_ptr = iv.as_mut_ptr();
        let iv_end_ptr = iv_ptr.offset(IV_SIZE_MINUS_ONE as isize);
        let tmp_ptr = self.tmp.as_mut_ptr();
        let tmp_offset_one_ptr = tmp_ptr.offset(1);
        let cipher = &mut self.cipher;
        let n = data.len();
        let mut data_ptr = data.as_mut_ptr();
        let data_end_ptr = data_ptr.offset(n as isize);

        while data_ptr != data_end_ptr {
            std::ptr::copy_nonoverlapping(iv_ptr, tmp_ptr, IV_SIZE);
            cipher.encrypt_block(iv);
            let orig = *data_ptr;
            let updated = orig ^ *iv_ptr;
            std::ptr::copy_nonoverlapping(tmp_offset_one_ptr, iv_ptr, IV_SIZE_MINUS_ONE);
            if decrypt {
                *iv_end_ptr = orig;
            } else {
                *iv_end_ptr = updated;
            }
            *data_ptr = updated;
            data_ptr = data_ptr.offset(1);
        }
    }
}

pub(crate) fn setup_craft_cipher(
    target: &mut Option<CraftCipher>,
    key: &[u8],
    iv: &[u8],
) -> Result<(), CipherError> {
    if target.is_some() {
        Err(CipherError::already_enabled())
    } else {
        *target = Some(CraftCipher::new(key, iv)?);
        Ok(())
    }
}