apple_security/os/macos/
encrypt_transform.rs

1//! Encryption and Decryption transform support.
2
3use core_foundation::base::TCFType;
4use core_foundation::data::CFData;
5use core_foundation::error::CFError;
6use core_foundation::string::CFString;
7use core_foundation_sys::data::CFDataRef;
8use core_foundation_sys::string::CFStringRef;
9use apple_security_sys::encrypt_transform::*;
10use apple_security_sys::transform::*;
11use std::ptr;
12
13use crate::key::SecKey;
14use crate::os::macos::transform::SecTransform;
15
16#[derive(Debug, Copy, Clone)]
17/// The padding scheme to use for encryption.
18pub struct Padding(CFStringRef);
19
20impl Padding {
21    /// Do not pad.
22    #[inline(always)]
23    #[must_use]
24    pub fn none() -> Self {
25        unsafe { Self(kSecPaddingNoneKey) }
26    }
27
28    /// Use PKCS#1 padding.
29    #[inline(always)]
30    #[must_use]
31    pub fn pkcs1() -> Self {
32        unsafe { Self(kSecPaddingPKCS1Key) }
33    }
34
35    /// Use PKCS#5 padding.
36    #[inline(always)]
37    #[must_use]
38    pub fn pkcs5() -> Self {
39        unsafe { Self(kSecPaddingPKCS5Key) }
40    }
41
42    /// Use PKCS#7 padding.
43    #[inline(always)]
44    #[must_use]
45    pub fn pkcs7() -> Self {
46        unsafe { Self(kSecPaddingPKCS7Key) }
47    }
48
49    /// Use OAEP padding.
50    #[inline(always)]
51    #[must_use]
52    pub fn oaep() -> Self {
53        unsafe { Self(kSecPaddingOAEPKey) }
54    }
55
56    #[inline]
57    fn to_str(self) -> CFString {
58        unsafe { CFString::wrap_under_get_rule(self.0) }
59    }
60}
61
62/// The cipher mode to use.
63///
64/// Only applies to AES encryption.
65#[derive(Debug, Copy, Clone)]
66pub struct Mode(CFStringRef);
67
68#[allow(missing_docs)]
69impl Mode {
70    #[inline(always)]
71    #[must_use]
72    pub fn none() -> Self {
73        unsafe { Self(kSecModeNoneKey) }
74    }
75
76    #[inline(always)]
77    #[must_use]
78    pub fn ecb() -> Self {
79        unsafe { Self(kSecModeECBKey) }
80    }
81
82    #[inline(always)]
83    #[must_use]
84    pub fn cbc() -> Self {
85        unsafe { Self(kSecModeCBCKey) }
86    }
87
88    #[inline(always)]
89    #[must_use]
90    pub fn cfb() -> Self {
91        unsafe { Self(kSecModeCFBKey) }
92    }
93
94    #[inline(always)]
95    #[must_use]
96    pub fn ofb() -> Self {
97        unsafe { Self(kSecModeOFBKey) }
98    }
99
100    fn to_str(self) -> CFString {
101        unsafe { CFString::wrap_under_get_rule(self.0) }
102    }
103}
104
105/// A builder for encryption and decryption transform operations.
106#[derive(Default)]
107pub struct Builder {
108    padding: Option<Padding>,
109    mode: Option<Mode>,
110    iv: Option<CFData>,
111}
112
113impl Builder {
114    /// Creates a new `Builder` with a default configuration.
115    #[inline(always)]
116    #[must_use]
117    pub fn new() -> Self {
118        Self::default()
119    }
120
121    /// Selects the padding scheme to use.
122    ///
123    /// If not set, an appropriate scheme will be selected for you.
124    #[inline(always)]
125    pub fn padding(&mut self, padding: Padding) -> &mut Self {
126        self.padding = Some(padding);
127        self
128    }
129
130    /// Selects the encryption mode to use.
131    ///
132    /// If not set, an appropriate mode will be selected for you.
133    #[inline(always)]
134    pub fn mode(&mut self, mode: Mode) -> &mut Self {
135        self.mode = Some(mode);
136        self
137    }
138
139    /// Sets the initialization vector to use.
140    ///
141    /// If not set, an appropriate value will be supplied for you.
142    #[inline(always)]
143    pub fn iv(&mut self, iv: CFData) -> &mut Self {
144        self.iv = Some(iv);
145        self
146    }
147
148    /// Encrypts data with a provided key.
149    pub fn encrypt(&self, key: &SecKey, data: &CFData) -> Result<CFData, CFError> {
150        unsafe {
151            let mut error = ptr::null_mut();
152            let transform = SecEncryptTransformCreate(key.as_concrete_TypeRef(), &mut error);
153            if transform.is_null() {
154                return Err(CFError::wrap_under_create_rule(error));
155            }
156            let transform = SecTransform::wrap_under_create_rule(transform);
157
158            self.finish(transform, data)
159        }
160    }
161
162    /// Decrypts data with a provided key.
163    pub fn decrypt(&self, key: &SecKey, data: &CFData) -> Result<CFData, CFError> {
164        unsafe {
165            let mut error = ptr::null_mut();
166            let transform = SecDecryptTransformCreate(key.as_concrete_TypeRef(), &mut error);
167            if transform.is_null() {
168                return Err(CFError::wrap_under_create_rule(error));
169            }
170            let transform = SecTransform::wrap_under_create_rule(transform);
171
172            self.finish(transform, data)
173        }
174    }
175
176    fn finish(&self, mut transform: SecTransform, data: &CFData) -> Result<CFData, CFError> {
177        unsafe {
178            if let Some(ref padding) = self.padding {
179                let key = CFString::wrap_under_get_rule(kSecPaddingKey);
180                transform.set_attribute(&key, &padding.to_str())?;
181            }
182
183            if let Some(ref mode) = self.mode {
184                let key = CFString::wrap_under_get_rule(kSecEncryptionMode);
185                transform.set_attribute(&key, &mode.to_str())?;
186            }
187
188            if let Some(ref iv) = self.iv {
189                let key = CFString::wrap_under_get_rule(kSecIVKey);
190                transform.set_attribute(&key, iv)?;
191            }
192
193            let key = CFString::wrap_under_get_rule(kSecTransformInputAttributeName);
194            transform.set_attribute(&key, data)?;
195
196            let result = transform.execute()?;
197            Ok(CFData::wrap_under_get_rule(
198                result.as_CFTypeRef() as CFDataRef
199            ))
200        }
201    }
202}
203
204#[cfg(test)]
205mod test {
206    use core_foundation::data::CFData;
207    use hex::FromHex;
208
209    use super::*;
210    use crate::key::SecKey;
211    use crate::os::macos::item::KeyType;
212    use crate::os::macos::key::SecKeyExt;
213
214    #[test]
215    fn cbc_mmt_256() {
216        // test 9
217        let key = "87725bd43a45608814180773f0e7ab95a3c859d83a2130e884190e44d14c6996";
218        let iv = "e49651988ebbb72eb8bb80bb9abbca34";
219        let ciphertext = "5b97a9d423f4b97413f388d9a341e727bb339f8e18a3fac2f2fb85abdc8f135deb30054a\
220                          1afdc9b6ed7da16c55eba6b0d4d10c74e1d9a7cf8edfaeaa684ac0bd9f9d24ba674955c7\
221                          9dc6be32aee1c260b558ff07e3a4d49d24162011ff254db8be078e8ad07e648e6bf56793\
222                          76cb4321a5ef01afe6ad8816fcc7634669c8c4389295c9241e45fff39f3225f7745032da\
223                          eebe99d4b19bcb215d1bfdb36eda2c24";
224        let plaintext = "bfe5c6354b7a3ff3e192e05775b9b75807de12e38a626b8bf0e12d5fff78e4f1775aa7d79\
225                         2d885162e66d88930f9c3b2cdf8654f56972504803190386270f0aa43645db187af41fcea\
226                         639b1f8026ccdd0c23e0de37094a8b941ecb7602998a4b2604e69fc04219585d854600e0a\
227                         d6f99a53b2504043c08b1c3e214d17cde053cbdf91daa999ed5b47c37983ba3ee254bc5c7\
228                         93837daaa8c85cfc12f7f54f699f";
229
230        let key = Vec::<u8>::from_hex(key).unwrap();
231        let key = CFData::from_buffer(&key);
232        let key = SecKey::from_data(KeyType::aes(), &key).unwrap();
233
234        let iv = Vec::<u8>::from_hex(iv).unwrap();
235
236        let ciphertext = Vec::<u8>::from_hex(ciphertext).unwrap();
237
238        let plaintext = Vec::<u8>::from_hex(plaintext).unwrap();
239
240        let decrypted = Builder::new()
241            .padding(Padding::none())
242            .iv(CFData::from_buffer(&iv))
243            .decrypt(&key, &CFData::from_buffer(&ciphertext))
244            .unwrap();
245
246        assert_eq!(plaintext, decrypted.bytes());
247
248        let encrypted = Builder::new()
249            .padding(Padding::none())
250            .iv(CFData::from_buffer(&iv))
251            .encrypt(&key, &CFData::from_buffer(&plaintext))
252            .unwrap();
253
254        assert_eq!(ciphertext, encrypted.bytes());
255    }
256}