common_crypto/
cryptor.rs

1// TODO: Note that Apple's implementation will malloc/copy keys to an aligned
2// buffer if necessary.
3
4// TODO: enum of all algorithms so that we can force iv, padding, etc. to be
5// provided if required.
6
7use std::{ffi::c_void, fmt::Display};
8
9#[repr(C)]
10#[derive(Copy, Clone)]
11enum Operation {
12    Encrypt = 0,
13    Decrypt = 1,
14}
15
16#[repr(u32)]
17#[non_exhaustive]
18#[derive(Copy, Clone, Debug, PartialEq)]
19pub enum Mode {
20    ECB = 1,
21    CBC = 2,
22    CFB = 3,
23    CTR = 4,
24    // F8 = 5,
25    // LRW = 6,
26    OFB = 7,
27    XTS = 8,
28    /// Must be specified for RC4, and must not be specified for others.
29    // RC4 = 9,
30    CFB8 = 10,
31}
32
33#[repr(u32)]
34#[derive(Copy, Clone, Debug, PartialEq)]
35#[non_exhaustive]
36pub enum Padding {
37    None = 0,
38    PKCS7 = 1,
39}
40
41type CCCryptorRef = *mut c_void;
42
43extern "C" {
44    fn CCCryptorCreateWithMode(
45        operation: Operation,
46        mode: u32,
47        config: u32,
48        padding: Padding,
49        iv: *const c_void,
50        key: *const c_void,
51        key_length: usize,
52        tweak: *const c_void,
53        tweak_length: usize,
54        rounds: usize,
55        options: u32,
56        handle: *mut CCCryptorRef,
57    ) -> Status;
58
59    fn CCCryptorRelease(handle: CCCryptorRef) -> Status;
60
61    fn CCCryptorUpdate(
62        handle: CCCryptorRef,
63        input: *const c_void,
64        input_len: usize,
65        output: *mut c_void,
66        output_len: usize,
67        written: *mut usize,
68    ) -> Status;
69
70    fn CCCryptorFinal(
71        handle: CCCryptorRef,
72        output: *mut c_void,
73        output_len: usize,
74        written: *mut usize,
75    ) -> Status;
76
77    fn CCCryptorGetOutputLength(handle: CCCryptorRef, input_len: usize, finishing: bool) -> usize;
78}
79
80#[derive(Debug, PartialEq)]
81#[non_exhaustive]
82pub enum CryptorError {
83    Param,
84    Memory,
85    Alignment,
86    Decode,
87    Unimplemented,
88    RNGFailure,
89    Unspecified,
90    CallSequence,
91    KeySize,
92    Key,
93    InitializationVectorPresent,
94    Unexpected(i32),
95}
96
97impl std::error::Error for CryptorError {}
98
99impl Display for CryptorError {
100    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
101        let s = match self {
102            Self::Param => "illegal parameter value",
103            Self::Memory => "memory allocation failed",
104            Self::Alignment => "input size was nit aligned properly",
105            Self::Decode => "input data did not encode or decrypt properly",
106            Self::Unimplemented => "function not implemented for the current algorithm",
107            Self::RNGFailure => "random number generated failed",
108            Self::Unspecified => "an unspecified failure occurred",
109            Self::CallSequence => "call sequence failure",
110            Self::KeySize => "key size is invalid",
111            Self::Key => "key is invalid",
112            Self::InitializationVectorPresent => "ECB mode does not support initialization vectors",
113            Self::Unexpected(code) => {
114                let s = format!("unexpected error {}", code);
115                return f.write_str(&s);
116            }
117        };
118
119        f.write_str(s)
120    }
121}
122
123#[allow(dead_code)]
124#[derive(Copy, Clone, Debug, PartialEq)]
125#[repr(i32)]
126enum Status {
127    Success = 0,
128    ParamError = -4300,
129    BufferTooSmall = -4301,
130    MemoryFailure = -4302,
131    AlignmentError = -4303,
132    DecodeError = -4304,
133    Unimplemented = -4305,
134    Overflow = -4306,
135    RNGFailure = -4307,
136    UnspecifiedError = -4308,
137    CallSequenceError = -4309,
138    KeySizeError = -4310,
139    InvalidKey = -4311,
140}
141
142impl Into<CryptorError> for Status {
143    fn into(self) -> CryptorError {
144        match self {
145            Status::Success => unreachable!(),
146            Status::ParamError => CryptorError::Param,
147            Status::MemoryFailure => CryptorError::Memory,
148            Status::AlignmentError => CryptorError::Alignment,
149            Status::DecodeError => CryptorError::Decode,
150            Status::Unimplemented => CryptorError::Unimplemented,
151            Status::RNGFailure => CryptorError::RNGFailure,
152            Status::UnspecifiedError => CryptorError::Unspecified,
153            Status::CallSequenceError => CryptorError::CallSequence,
154            Status::KeySizeError => CryptorError::KeySize,
155            Status::InvalidKey => CryptorError::Key,
156            _ => CryptorError::Unexpected(self as i32),
157        }
158    }
159}
160
161#[non_exhaustive]
162#[derive(Clone, Copy, Debug, PartialEq)]
163/// The configuration for a [`Cryptor`].
164///
165/// ```
166/// # use common_crypto::cryptor::{Config, Mode};
167/// let config = Config::AES256 {
168///     mode: Mode::CTR,
169///     iv: Some(b"use random iv :)"),
170///     key: b"aes128 key must be 32 bytes long",
171/// };
172/// ```
173// TODO: padding, rounds
174pub enum Config<'a> {
175    AES128 {
176        mode: Mode,
177        iv: Option<&'a [u8; 16]>,
178        key: &'a [u8; 16],
179    },
180    AES192 {
181        mode: Mode,
182        iv: Option<&'a [u8; 16]>,
183        key: &'a [u8; 24],
184    },
185    AES256 {
186        mode: Mode,
187        iv: Option<&'a [u8; 16]>,
188        key: &'a [u8; 32],
189    },
190    DES {
191        mode: Mode,
192        iv: Option<&'a [u8; 8]>,
193        key: &'a [u8; 8],
194    },
195    TDES {
196        mode: Mode,
197        iv: Option<&'a [u8; 8]>,
198        key: &'a [u8; 24],
199    },
200    CAST {
201        mode: Mode,
202        iv: Option<&'a [u8; 8]>,
203        /// Valid key sizes are between 5 and 24.
204        key: &'a [u8],
205        padding: Padding,
206    },
207    RC4 {
208        /// Valid key sizes are between 1 and 512.
209        key: &'a [u8],
210    },
211    RC2 {
212        mode: Mode,
213        iv: Option<&'a [u8; 8]>,
214        /// Valid key sizes are between 1 and 128.
215        key: &'a [u8],
216    },
217    Blowfish {
218        mode: Mode,
219        iv: Option<&'a [u8; 8]>,
220        /// Valid key sizes are between 8 and 56.
221        key: &'a [u8],
222    },
223}
224
225impl<'a> From<&Config<'a>> for u32 {
226    fn from(config: &Config) -> Self {
227        match config {
228            Config::AES128 { .. } => 0,
229            Config::AES192 { .. } => 0,
230            Config::AES256 { .. } => 0,
231            Config::DES { .. } => 1,
232            Config::TDES { .. } => 2,
233            Config::CAST { .. } => 3,
234            Config::RC4 { .. } => 4,
235            Config::RC2 { .. } => 5,
236            Config::Blowfish { .. } => 6,
237        }
238    }
239}
240
241impl<'a> Config<'a> {
242    fn padding(&self) -> Padding {
243        match self {
244            Config::CAST { padding, .. } => *padding,
245            _ => Padding::None,
246        }
247    }
248
249    fn rounds(&self) -> usize {
250        0
251    }
252
253    const fn mode(&self) -> u32 {
254        match self {
255            Config::AES128 { mode, .. } => *mode as u32,
256            Config::AES192 { mode, .. } => *mode as u32,
257            Config::AES256 { mode, .. } => *mode as u32,
258            Config::DES { mode, .. } => *mode as u32,
259            Config::TDES { mode, .. } => *mode as u32,
260            Config::CAST { mode, .. } => *mode as u32,
261            Config::RC4 { .. } => 9,
262            Config::RC2 { mode, .. } => *mode as u32,
263            Config::Blowfish { mode, .. } => *mode as u32,
264        }
265    }
266
267    fn iv_ptr(&self) -> Result<*const u8, CryptorError> {
268        let (mode, ptr) = match self {
269            Config::AES128 { mode, iv, .. } if iv.is_some() => (mode, iv.unwrap().as_ptr()),
270            Config::AES192 { mode, iv, .. } if iv.is_some() => (mode, iv.unwrap().as_ptr()),
271            Config::AES256 { mode, iv, .. } if iv.is_some() => (mode, iv.unwrap().as_ptr()),
272            Config::DES { mode, iv, .. } if iv.is_some() => (mode, iv.unwrap().as_ptr()),
273            Config::CAST { mode, iv, .. } if iv.is_some() => (mode, iv.unwrap().as_ptr()),
274            Config::RC2 { mode, iv, .. } if iv.is_some() => (mode, iv.unwrap().as_ptr()),
275            Config::Blowfish { mode, iv, .. } if iv.is_some() => (mode, iv.unwrap().as_ptr()),
276            _ => return Ok(std::ptr::null()),
277        };
278
279        if mode == &Mode::ECB {
280            Err(CryptorError::InitializationVectorPresent)
281        } else {
282            Ok(ptr)
283        }
284    }
285
286    fn key(&self) -> &[u8] {
287        match self {
288            Config::AES128 { key, .. } => *key,
289            Config::AES192 { key, .. } => *key,
290            Config::AES256 { key, .. } => *key,
291            Config::DES { key, .. } => *key,
292            Config::TDES { key, .. } => *key,
293            Config::CAST { key, .. } => *key,
294            Config::RC4 { key, .. } => *key,
295            Config::RC2 { key, .. } => *key,
296            Config::Blowfish { key, .. } => *key,
297        }
298    }
299}
300
301/// A cryptor supporting all of the block and stream ciphers provided by the
302/// common crypto library.
303///
304/// ```
305/// # use common_crypto::cryptor::{Config, Cryptor};
306/// let config = Config::RC4 { key: b"Key" };
307/// assert_eq!(
308///     Cryptor::encrypt(&config, b"Plaintext").unwrap(),
309///     &[0xbb, 0xf3, 0x16, 0xe8, 0xd9, 0x40, 0xaf, 0x0a, 0xd3]
310/// );
311/// ```
312
313#[derive(Debug)]
314pub struct Cryptor {
315    handle: CCCryptorRef,
316}
317
318impl<'a> Drop for Cryptor {
319    fn drop(&mut self) {
320        unsafe {
321            CCCryptorRelease(self.handle);
322        }
323    }
324}
325
326impl Cryptor {
327    fn new<'a>(config: &Config<'a>, operation: Operation) -> Result<Cryptor, CryptorError> {
328        let mut handle: CCCryptorRef = std::ptr::null_mut();
329
330        let status = unsafe {
331            CCCryptorCreateWithMode(
332                operation,
333                config.mode(),
334                config.into(),
335                config.padding(),
336                config.iv_ptr()? as *const c_void,
337                config.key().as_ptr() as *const c_void,
338                config.key().len(),
339                // Tweak is unsued
340                std::ptr::null(),
341                0,
342                config.rounds(),
343                0,
344                &mut handle as *mut *mut c_void,
345            )
346        };
347
348        if status != Status::Success {
349            return Err(status.into());
350        }
351
352        Ok(Cryptor { handle })
353    }
354
355    pub fn new_encryptor<'a>(config: &Config<'a>) -> Result<Self, CryptorError> {
356        Self::new(config, Operation::Encrypt)
357    }
358
359    pub fn new_decryptor<'a>(config: &Config<'a>) -> Result<Self, CryptorError> {
360        Self::new(config, Operation::Decrypt)
361    }
362
363    /// Encrypts the data and writes to the provided buffer. The buffer will
364    /// be resized as required, and will be cleared on error.
365    pub fn update(
366        &self,
367        input: impl AsRef<[u8]>,
368        output: &mut Vec<u8>,
369    ) -> Result<(), CryptorError> {
370        let input = input.as_ref();
371        let mut written = 0usize;
372
373        output.resize(
374            unsafe { CCCryptorGetOutputLength(self.handle, input.len(), false) },
375            0,
376        );
377
378        let status = unsafe {
379            CCCryptorUpdate(
380                self.handle,
381                input.as_ptr() as *const c_void,
382                input.len(),
383                output.as_mut_ptr() as *mut c_void,
384                output.capacity(),
385                &mut written as *mut usize,
386            )
387        };
388
389        if status != Status::Success {
390            output.clear();
391            return Err(status.into());
392        }
393
394        output.resize(written, 0);
395
396        Ok(())
397    }
398
399    /// Finalises the encryption, returning any remaining data where
400    /// appropriate. The cryptor cannot be used again.
401    pub fn finish(self, output: &mut Vec<u8>) -> Result<(), CryptorError> {
402        let mut written = 0usize;
403
404        let status = unsafe {
405            CCCryptorFinal(
406                self.handle,
407                output.as_mut_ptr() as *mut c_void,
408                output.capacity(),
409                &mut written as *mut usize,
410            )
411        };
412
413        if status != Status::Success {
414            output.clear();
415            return Err(status.into());
416        }
417
418        output.resize(written, 0);
419
420        Ok(())
421    }
422}
423
424impl Cryptor {
425    pub fn encrypt<'a>(
426        config: &Config<'a>,
427        input: impl AsRef<[u8]>,
428    ) -> Result<Vec<u8>, CryptorError> {
429        let mut output = Vec::new();
430        Cryptor::new_encryptor(config)?.update(input, &mut output)?;
431        Ok(output)
432    }
433
434    pub fn decrypt<'a>(
435        config: &Config<'a>,
436        input: impl AsRef<[u8]>,
437    ) -> Result<Vec<u8>, CryptorError> {
438        let mut output = Vec::new();
439        Cryptor::new_decryptor(config)?.update(input, &mut output)?;
440        Ok(output)
441    }
442}