security_framework/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 security_framework_sys::encrypt_transform::*;
10use security_framework_sys::transform::kSecTransformInputAttributeName;
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    // FIXME: deprecate and remove: don't expose CFData in Rust APIs.
150    pub fn encrypt(&self, key: &SecKey, data: &CFData) -> Result<CFData, CFError> {
151        unsafe {
152            let mut error = ptr::null_mut();
153            let transform = SecEncryptTransformCreate(key.as_concrete_TypeRef(), &mut error);
154            if transform.is_null() {
155                return Err(CFError::wrap_under_create_rule(error));
156            }
157            let transform = SecTransform::wrap_under_create_rule(transform);
158
159            self.finish(transform, data)
160        }
161    }
162
163    /// Decrypts data with a provided key.
164    // FIXME: deprecate and remove: don't expose CFData in Rust APIs.
165    pub fn decrypt(&self, key: &SecKey, data: &CFData) -> Result<CFData, CFError> {
166        unsafe {
167            let mut error = ptr::null_mut();
168            let transform = SecDecryptTransformCreate(key.as_concrete_TypeRef(), &mut error);
169            if transform.is_null() {
170                return Err(CFError::wrap_under_create_rule(error));
171            }
172            let transform = SecTransform::wrap_under_create_rule(transform);
173
174            self.finish(transform, data)
175        }
176    }
177
178    fn finish(&self, mut transform: SecTransform, data: &CFData) -> Result<CFData, CFError> {
179        unsafe {
180            if let Some(ref padding) = self.padding {
181                let key = CFString::wrap_under_get_rule(kSecPaddingKey);
182                transform.set_attribute(&key, &padding.to_str())?;
183            }
184
185            if let Some(ref mode) = self.mode {
186                let key = CFString::wrap_under_get_rule(kSecEncryptionMode);
187                transform.set_attribute(&key, &mode.to_str())?;
188            }
189
190            if let Some(ref iv) = self.iv {
191                let key = CFString::wrap_under_get_rule(kSecIVKey);
192                transform.set_attribute(&key, iv)?;
193            }
194
195            let key = CFString::wrap_under_get_rule(kSecTransformInputAttributeName);
196            transform.set_attribute(&key, data)?;
197
198            let result = transform.execute()?;
199            Ok(CFData::wrap_under_get_rule(result.as_CFTypeRef() as CFDataRef))
200        }
201    }
202}
203
204#[cfg(test)]
205mod test {
206    use hex::FromHex;
207
208    use super::*;
209    use crate::os::macos::item::KeyType;
210    use crate::os::macos::key::SecKeyExt;
211
212    #[test]
213    fn cbc_mmt_256() {
214        // test 9
215        let key = "87725bd43a45608814180773f0e7ab95a3c859d83a2130e884190e44d14c6996";
216        let iv = "e49651988ebbb72eb8bb80bb9abbca34";
217        let ciphertext = "5b97a9d423f4b97413f388d9a341e727bb339f8e18a3fac2f2fb85abdc8f135deb30054a\
218                          1afdc9b6ed7da16c55eba6b0d4d10c74e1d9a7cf8edfaeaa684ac0bd9f9d24ba674955c7\
219                          9dc6be32aee1c260b558ff07e3a4d49d24162011ff254db8be078e8ad07e648e6bf56793\
220                          76cb4321a5ef01afe6ad8816fcc7634669c8c4389295c9241e45fff39f3225f7745032da\
221                          eebe99d4b19bcb215d1bfdb36eda2c24";
222        let plaintext = "bfe5c6354b7a3ff3e192e05775b9b75807de12e38a626b8bf0e12d5fff78e4f1775aa7d79\
223                         2d885162e66d88930f9c3b2cdf8654f56972504803190386270f0aa43645db187af41fcea\
224                         639b1f8026ccdd0c23e0de37094a8b941ecb7602998a4b2604e69fc04219585d854600e0a\
225                         d6f99a53b2504043c08b1c3e214d17cde053cbdf91daa999ed5b47c37983ba3ee254bc5c7\
226                         93837daaa8c85cfc12f7f54f699f";
227
228        let key = Vec::<u8>::from_hex(key).unwrap();
229        let key = CFData::from_buffer(&key);
230        let key = SecKey::from_data(KeyType::aes(), &key).unwrap();
231
232        let iv = Vec::<u8>::from_hex(iv).unwrap();
233
234        let ciphertext = Vec::<u8>::from_hex(ciphertext).unwrap();
235
236        let plaintext = Vec::<u8>::from_hex(plaintext).unwrap();
237
238        let decrypted = Builder::new()
239            .padding(Padding::none())
240            .iv(CFData::from_buffer(&iv))
241            .decrypt(&key, &CFData::from_buffer(&ciphertext))
242            .unwrap();
243
244        assert_eq!(plaintext, decrypted.bytes());
245
246        let encrypted = Builder::new()
247            .padding(Padding::none())
248            .iv(CFData::from_buffer(&iv))
249            .encrypt(&key, &CFData::from_buffer(&plaintext))
250            .unwrap();
251
252        assert_eq!(ciphertext, encrypted.bytes());
253    }
254}