Skip to main content

security_framework/
key.rs

1//! Encryption key support
2
3use core_foundation::{declare_TCFType, impl_TCFType};
4use crate::cvt;
5use core_foundation::{
6    base::TCFType, string::{CFStringRef, CFString},
7    dictionary::CFMutableDictionary,
8};
9use core_foundation::base::ToVoid;
10use core_foundation::boolean::CFBoolean;
11use core_foundation::data::CFData;
12use core_foundation::dictionary::CFDictionary;
13use core_foundation::number::CFNumber;
14use core_foundation::error::{CFError, CFErrorRef};
15
16use security_framework_sys::{
17    item::{kSecAttrKeyTypeRSA, kSecValueRef},
18    keychain_item::SecItemDelete,
19};
20use security_framework_sys::{item::{
21    kSecAttrIsPermanent, kSecAttrLabel, kSecAttrKeyType,
22    kSecAttrKeySizeInBits, kSecAttrAccessControl
23}};
24#[cfg(target_os = "macos")]
25use security_framework_sys::item::{
26    kSecAttrKeyType3DES, kSecAttrKeyTypeDSA, kSecAttrKeyTypeAES,
27    kSecAttrKeyTypeDES, kSecAttrKeyTypeRC4, kSecAttrKeyTypeCAST,
28};
29
30use security_framework_sys::base::SecKeyRef;
31use security_framework_sys::key::SecKeyGetTypeID;
32
33pub use security_framework_sys::key::Algorithm;
34
35use security_framework_sys::key::{
36    SecKeyCopyAttributes, SecKeyCopyExternalRepresentation,
37    SecKeyCreateSignature, SecKeyCreateRandomKey,
38    SecKeyCopyPublicKey,
39    SecKeyCreateDecryptedData, SecKeyCreateEncryptedData,
40};
41use security_framework_sys::item::kSecAttrApplicationLabel;
42use std::fmt;
43
44
45use crate::base::Error;
46use crate::item::Location;
47use crate::access_control::SecAccessControl;
48/// Types of `SecKey`s.
49#[derive(Debug, Copy, Clone)]
50pub struct KeyType(CFStringRef);
51
52#[allow(missing_docs)]
53impl KeyType {
54    #[inline(always)]
55    #[must_use]
56    pub fn rsa() -> Self {
57        unsafe { Self(kSecAttrKeyTypeRSA) }
58    }
59
60    #[cfg(target_os = "macos")]
61    #[inline(always)]
62    #[must_use]
63    pub fn dsa() -> Self {
64        unsafe { Self(kSecAttrKeyTypeDSA) }
65    }
66
67    #[cfg(target_os = "macos")]
68    #[inline(always)]
69    #[must_use]
70    pub fn aes() -> Self {
71        unsafe { Self(kSecAttrKeyTypeAES) }
72    }
73
74    #[cfg(target_os = "macos")]
75    #[inline(always)]
76    #[must_use]
77    pub fn des() -> Self {
78        unsafe { Self(kSecAttrKeyTypeDES) }
79    }
80
81    #[cfg(target_os = "macos")]
82    #[inline(always)]
83    #[must_use]
84    pub fn triple_des() -> Self {
85        unsafe { Self(kSecAttrKeyType3DES) }
86    }
87
88    #[cfg(target_os = "macos")]
89    #[inline(always)]
90    #[must_use]
91    pub fn rc4() -> Self {
92        unsafe { Self(kSecAttrKeyTypeRC4) }
93    }
94
95    #[cfg(target_os = "macos")]
96    #[inline(always)]
97    #[must_use]
98    pub fn cast() -> Self {
99        unsafe { Self(kSecAttrKeyTypeCAST) }
100    }
101
102    #[inline(always)]
103    #[must_use]
104    pub fn ec() -> Self {
105        use security_framework_sys::item::kSecAttrKeyTypeEC;
106
107        unsafe { Self(kSecAttrKeyTypeEC) }
108    }
109
110    #[inline(always)]
111    #[must_use]
112    pub fn ec_sec_prime_random() -> Self {
113        use security_framework_sys::item::kSecAttrKeyTypeECSECPrimeRandom;
114
115        unsafe { Self(kSecAttrKeyTypeECSECPrimeRandom) }
116    }
117
118    pub(crate) fn to_str(self) -> CFString {
119        unsafe { CFString::wrap_under_get_rule(self.0) }
120    }
121}
122
123declare_TCFType! {
124    /// A type representing an encryption key.
125    SecKey, SecKeyRef
126}
127impl_TCFType!(SecKey, SecKeyRef, SecKeyGetTypeID);
128
129unsafe impl Sync for SecKey {}
130unsafe impl Send for SecKey {}
131
132impl SecKey {
133    /// Translates to `SecKeyCreateRandomKey`
134    #[allow(deprecated)]
135    #[doc(alias = "SecKeyCreateRandomKey")]
136    pub fn new(options: &GenerateKeyOptions) -> Result<Self, CFError> {
137        Self::generate(options.to_dictionary())
138    }
139
140    /// Translates to `SecKeyCreateRandomKey`
141    /// `GenerateKeyOptions` provides a helper to create an attribute `CFDictionary`.
142    #[deprecated(note = "Use SecKey::new")]
143    pub fn generate(attributes: CFDictionary) -> Result<Self, CFError> {
144        let mut error: CFErrorRef = ::std::ptr::null_mut();
145        let sec_key = unsafe { SecKeyCreateRandomKey(attributes.as_concrete_TypeRef(), &mut error) };
146        if error.is_null() {
147            Ok(unsafe { Self::wrap_under_create_rule(sec_key) })
148        } else {
149            Err(unsafe { CFError::wrap_under_create_rule(error) })
150        }
151    }
152
153    /// Returns the programmatic identifier for the key. For keys of class
154    /// kSecAttrKeyClassPublic and kSecAttrKeyClassPrivate, the value is the
155    /// hash of the public key.
156    #[must_use]
157    pub fn application_label(&self) -> Option<Vec<u8>> {
158        self.attributes()
159            .find(unsafe { kSecAttrApplicationLabel.to_void() })
160            .map(|v| unsafe { CFData::wrap_under_get_rule(v.cast()) }.to_vec())
161    }
162
163    /// Translates to `SecKeyCopyAttributes`
164    // TODO: deprecate and remove. CFDictionary should not be exposed in public Rust APIs.
165    #[must_use]
166    pub fn attributes(&self) -> CFDictionary {
167        let pka = unsafe { SecKeyCopyAttributes(self.to_void() as _) };
168        unsafe { CFDictionary::wrap_under_create_rule(pka) }
169    }
170
171    /// Translates to `SecKeyCopyExternalRepresentation`
172    // TODO: deprecate and remove. CFData should not be exposed in public Rust APIs.
173    #[must_use]
174    pub fn external_representation(&self) -> Option<CFData> {
175        let mut error: CFErrorRef = ::std::ptr::null_mut();
176        let data = unsafe { SecKeyCopyExternalRepresentation(self.to_void() as _, &mut error) };
177        if data.is_null() {
178            return None;
179        }
180        Some(unsafe { CFData::wrap_under_create_rule(data) })
181    }
182
183    /// Translates to `SecKeyCopyPublicKey`
184    #[must_use]
185    pub fn public_key(&self) -> Option<Self> {
186        let pub_seckey = unsafe { SecKeyCopyPublicKey(self.0.cast()) };
187        if pub_seckey.is_null() {
188            return None;
189        }
190
191        Some(unsafe { Self::wrap_under_create_rule(pub_seckey) })
192    }
193
194    /// Encrypts a block of data using a public key and specified algorithm
195    pub fn encrypt_data(&self, algorithm: Algorithm, input: &[u8]) -> Result<Vec<u8>, CFError> {
196        let mut error: CFErrorRef = std::ptr::null_mut();
197
198        let output = unsafe {
199            SecKeyCreateEncryptedData(self.as_concrete_TypeRef(), algorithm.into(), CFData::from_buffer(input).as_concrete_TypeRef(), &mut error)
200        };
201
202        if error.is_null() {
203            let output = unsafe { CFData::wrap_under_create_rule(output) };
204            Ok(output.to_vec())
205        } else {
206            Err(unsafe { CFError::wrap_under_create_rule(error) })
207        }
208    }
209
210    /// Decrypts a block of data using a private key and specified algorithm
211    pub fn decrypt_data(&self, algorithm: Algorithm, input: &[u8]) -> Result<Vec<u8>, CFError> {
212        let mut error: CFErrorRef = std::ptr::null_mut();
213
214        let output = unsafe {
215            SecKeyCreateDecryptedData(self.as_concrete_TypeRef(), algorithm.into(), CFData::from_buffer(input).as_concrete_TypeRef(), &mut error)
216        };
217
218        if error.is_null() {
219            let output = unsafe { CFData::wrap_under_create_rule(output) };
220            Ok(output.to_vec())
221        } else {
222            Err(unsafe { CFError::wrap_under_create_rule(error) })
223        }
224    }
225
226    /// Creates the cryptographic signature for a block of data using a private
227    /// key and specified algorithm.
228    pub fn create_signature(&self, algorithm: Algorithm, input: &[u8]) -> Result<Vec<u8>, CFError> {
229        let mut error: CFErrorRef = std::ptr::null_mut();
230
231        let output = unsafe {
232            SecKeyCreateSignature(
233                self.as_concrete_TypeRef(),
234                algorithm.into(),
235                CFData::from_buffer(input).as_concrete_TypeRef(),
236                &mut error,
237            )
238        };
239
240        if error.is_null() {
241            let output = unsafe { CFData::wrap_under_create_rule(output) };
242            Ok(output.to_vec())
243        } else {
244            Err(unsafe { CFError::wrap_under_create_rule(error) })
245        }
246    }
247
248    /// Verifies the cryptographic signature for a block of data using a public
249    /// key and specified algorithm.
250    pub fn verify_signature(&self, algorithm: Algorithm, signed_data: &[u8], signature: &[u8]) -> Result<bool, CFError> {
251        use security_framework_sys::key::SecKeyVerifySignature;
252        let mut error: CFErrorRef = std::ptr::null_mut();
253
254        let valid = unsafe {
255            SecKeyVerifySignature(
256                self.as_concrete_TypeRef(),
257                algorithm.into(),
258                CFData::from_buffer(signed_data).as_concrete_TypeRef(),
259                CFData::from_buffer(signature).as_concrete_TypeRef(),
260                &mut error,
261            )
262        };
263
264        if !error.is_null() {
265            return Err(unsafe { CFError::wrap_under_create_rule(error) })?;
266        }
267        Ok(valid != 0)
268    }
269
270    /// Performs the Diffie-Hellman style of key exchange.
271    pub fn key_exchange(
272        &self,
273        algorithm: Algorithm,
274        public_key: &Self,
275        requested_size: usize,
276        shared_info: Option<&[u8]>,
277    ) -> Result<Vec<u8>, CFError> {
278        use core_foundation::data::CFData;
279        use security_framework_sys::item::{
280            kSecKeyKeyExchangeParameterRequestedSize, kSecKeyKeyExchangeParameterSharedInfo,
281        };
282
283        unsafe {
284            let mut params = vec![(
285                CFString::wrap_under_get_rule(kSecKeyKeyExchangeParameterRequestedSize),
286                CFNumber::from(requested_size as i64).into_CFType(),
287            )];
288
289            if let Some(shared_info) = shared_info {
290                params.push((
291                    CFString::wrap_under_get_rule(kSecKeyKeyExchangeParameterSharedInfo),
292                    CFData::from_buffer(shared_info).as_CFType(),
293                ));
294            }
295
296            let parameters = CFDictionary::from_CFType_pairs(&params);
297
298            let mut error: CFErrorRef = std::ptr::null_mut();
299
300            let output = security_framework_sys::key::SecKeyCopyKeyExchangeResult(
301                self.as_concrete_TypeRef(),
302                algorithm.into(),
303                public_key.as_concrete_TypeRef(),
304                parameters.as_concrete_TypeRef(),
305                &mut error,
306            );
307
308            if error.is_null() {
309                let output = CFData::wrap_under_create_rule(output);
310                Ok(output.to_vec())
311            } else {
312                Err(CFError::wrap_under_create_rule(error))
313            }
314        }
315    }
316
317    /// Translates to `SecItemDelete`, passing in the `SecKeyRef`
318    pub fn delete(&self) -> Result<(), Error> {
319        let query = CFMutableDictionary::from_CFType_pairs(&[(
320            unsafe { kSecValueRef }.to_void(),
321            self.to_void(),
322        )]);
323
324        cvt(unsafe { SecItemDelete(query.as_concrete_TypeRef()) })
325    }
326}
327
328/// Where to generate the key.
329#[derive(Debug)]
330pub enum Token {
331    /// Generate the key in software, compatible with all `KeyType`s.
332    Software,
333    /// Generate the key in the Secure Enclave such that the private key is not
334    /// extractable. Only compatible with `KeyType::ec()`.
335    SecureEnclave,
336}
337
338/// Helper for creating `CFDictionary` attributes for `SecKey::generate`
339/// Recommended reading:
340/// <https://developer.apple.com/documentation/technotes/tn3137-on-mac-keychains>
341#[derive(Debug, Default)]
342pub struct GenerateKeyOptions {
343    /// kSecAttrKeyType
344    #[deprecated(note = "use set_key_type()")]
345    pub key_type: Option<KeyType>,
346    /// kSecAttrKeySizeInBits
347    #[deprecated(note = "use set_size_in_bits()")]
348    pub size_in_bits: Option<u32>,
349    /// kSecAttrLabel
350    #[deprecated(note = "use set_label()")]
351    pub label: Option<String>,
352    /// kSecAttrTokenID
353    #[deprecated(note = "use set_token()")]
354    pub token: Option<Token>,
355    /// Which keychain to store the key in, if any.
356    #[deprecated(note = "use set_location()")]
357    pub location: Option<Location>,
358    /// Access control
359    #[deprecated(note = "use set_access_control()")]
360    pub access_control: Option<SecAccessControl>,
361    /// `kSecAttrSynchronizable`
362    #[cfg(feature = "sync-keychain")]
363    synchronizable: Option<bool>,
364}
365
366#[allow(deprecated)]
367impl GenerateKeyOptions {
368    /// Set `key_type`
369    pub fn set_key_type(&mut self, key_type: KeyType) -> &mut Self {
370        self.key_type = Some(key_type);
371        self
372    }
373
374    /// Set `size_in_bits`
375    pub fn set_size_in_bits(&mut self, size_in_bits: u32) -> &mut Self {
376        self.size_in_bits = Some(size_in_bits);
377        self
378    }
379
380    /// Set `label`
381    pub fn set_label(&mut self, label: impl Into<String>) -> &mut Self {
382        self.label = Some(label.into());
383        self
384    }
385
386    /// Set `token`
387    pub fn set_token(&mut self, token: Token) -> &mut Self {
388        self.token = Some(token);
389        self
390    }
391
392    /// Set `location`
393    pub fn set_location(&mut self, location: Location) -> &mut Self {
394        self.location = Some(location);
395        self
396    }
397
398    /// Set `access_control`
399    pub fn set_access_control(&mut self, access_control: SecAccessControl) -> &mut Self {
400        self.access_control = Some(access_control);
401        self
402    }
403
404    /// Set `synchronizable` (`kSecAttrSynchronizable`)
405    #[cfg(feature = "sync-keychain")]
406    pub fn set_synchronizable(&mut self, synchronizable: bool) -> &mut Self {
407        self.synchronizable = Some(synchronizable);
408        self
409    }
410
411    /// Collect options into a `CFDictioanry`
412    // CFDictionary should not be exposed in public Rust APIs.
413    #[deprecated(note = "Pass the options to SecKey::new")]
414    pub fn to_dictionary(&self) -> CFDictionary {
415        use security_framework_sys::item::{kSecAttrTokenID, kSecAttrTokenIDSecureEnclave};
416
417        let is_permanent = CFBoolean::from(self.location.is_some());
418        let mut private_attributes = CFMutableDictionary::from_CFType_pairs(&[(
419            unsafe { kSecAttrIsPermanent }.to_void(),
420            is_permanent.to_void(),
421        )]);
422        if let Some(access_control) = &self.access_control {
423            private_attributes.set(unsafe { kSecAttrAccessControl }.to_void(), access_control.to_void());
424        }
425
426        #[cfg(target_os = "macos")]
427        let public_attributes = CFMutableDictionary::from_CFType_pairs(&[(
428            unsafe { kSecAttrIsPermanent }.to_void(),
429            is_permanent.to_void(),
430        )]);
431
432        let key_type = self.key_type.unwrap_or_else(KeyType::rsa).to_str();
433
434        let size_in_bits = self.size_in_bits.unwrap_or(match () {
435            #[cfg(target_os = "macos")]
436            () if key_type == KeyType::aes().to_str() => 256,
437            () if key_type == KeyType::rsa().to_str() => 2048,
438            () if key_type == KeyType::ec().to_str() => 256,
439            () if key_type == KeyType::ec_sec_prime_random().to_str() => 256,
440            () => 256,
441        });
442        let size_in_bits = CFNumber::from(size_in_bits as i32);
443
444        let mut attribute_key_values = vec![
445            (unsafe { kSecAttrKeyType }.to_void(), key_type.to_void()),
446            (unsafe { kSecAttrKeySizeInBits }.to_void(), size_in_bits.to_void()),
447        ];
448        #[cfg(target_os = "macos")]
449        if key_type != KeyType::aes().to_str() {
450            attribute_key_values.push((unsafe { security_framework_sys::item::kSecPublicKeyAttrs }.to_void(), public_attributes.to_void()));
451            attribute_key_values.push((unsafe { security_framework_sys::item::kSecPrivateKeyAttrs }.to_void(), private_attributes.to_void()));
452        }
453
454        let label = self.label.as_deref().map(CFString::new);
455        if let Some(label) = &label {
456            attribute_key_values.push((unsafe { kSecAttrLabel }.to_void(), label.to_void()));
457        }
458
459        #[cfg(target_os = "macos")]
460        match &self.location {
461            #[cfg(feature = "OSX_10_15")]
462            Some(Location::DataProtectionKeychain) => {
463                use security_framework_sys::item::kSecUseDataProtectionKeychain;
464                attribute_key_values.push((
465                    unsafe { kSecUseDataProtectionKeychain }.to_void(),
466                    CFBoolean::true_value().to_void(),
467                ));
468            }
469            Some(Location::FileKeychain(keychain)) => {
470                attribute_key_values.push((
471                    unsafe { security_framework_sys::item::kSecUseKeychain }.to_void(),
472                    keychain.as_concrete_TypeRef().to_void(),
473                ));
474            },
475            _ => {},
476        }
477
478        match self.token.as_ref().unwrap_or(&Token::Software) {
479            Token::Software => {},
480            Token::SecureEnclave => {
481                attribute_key_values.push((
482                    unsafe { kSecAttrTokenID }.to_void(),
483                    unsafe { kSecAttrTokenIDSecureEnclave }.to_void(),
484                ));
485            }
486        }
487
488        #[cfg(feature = "sync-keychain")]
489        if let Some(synchronizable) = &self.synchronizable {
490            attribute_key_values.push((
491                unsafe { security_framework_sys::item::kSecAttrSynchronizable }.to_void(),
492                CFBoolean::from(*synchronizable).to_void(),
493            ));
494        }
495
496        CFMutableDictionary::from_CFType_pairs(&attribute_key_values).to_immutable()
497    }
498}
499
500impl fmt::Debug for SecKey {
501    #[cold]
502    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
503        fmt.debug_struct("SecKey").finish_non_exhaustive()
504    }
505}