apple_security_framework/os/macos/
encrypt_transform.rs

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