1use 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;
10#[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))]
11use core_foundation::boolean::CFBoolean;
12#[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))]
13use core_foundation::data::CFData;
14#[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))]
15use core_foundation::dictionary::CFDictionary;
16#[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))]
17use core_foundation::number::CFNumber;
18#[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))]
19use core_foundation::error::{CFError, CFErrorRef};
20
21use security_framework_sys::{
22 item::{kSecAttrKeyTypeRSA, kSecValueRef},
23 keychain_item::SecItemDelete,
24};
25#[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))]
26use security_framework_sys::{item::{
27 kSecAttrIsPermanent, kSecAttrLabel, kSecAttrKeyType,
28 kSecAttrKeySizeInBits, kSecPrivateKeyAttrs, kSecAttrAccessControl
29}};
30#[cfg(target_os = "macos")]
31use security_framework_sys::item::{
32 kSecAttrKeyType3DES, kSecAttrKeyTypeDSA, kSecAttrKeyTypeAES,
33 kSecAttrKeyTypeDES, kSecAttrKeyTypeRC4, kSecAttrKeyTypeCAST,
34};
35
36use security_framework_sys::base::SecKeyRef;
37use security_framework_sys::key::SecKeyGetTypeID;
38
39#[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))]
40pub use security_framework_sys::key::Algorithm;
41
42#[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))]
43use security_framework_sys::key::{
44 SecKeyCopyAttributes, SecKeyCopyExternalRepresentation,
45 SecKeyCreateSignature, SecKeyCreateRandomKey,
46 SecKeyCopyPublicKey,
47 SecKeyCreateDecryptedData, SecKeyCreateEncryptedData,
48};
49#[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))]
50use security_framework_sys::item::kSecAttrApplicationLabel;
51use std::fmt;
52
53
54use crate::base::Error;
55#[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))]
56use crate::item::Location;
57#[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))]
58use crate::access_control::SecAccessControl;
59#[derive(Debug, Copy, Clone)]
61pub struct KeyType(CFStringRef);
62
63#[allow(missing_docs)]
64impl KeyType {
65 #[inline(always)]
66 #[must_use]
67 pub fn rsa() -> Self {
68 unsafe { Self(kSecAttrKeyTypeRSA) }
69 }
70
71 #[cfg(target_os = "macos")]
72 #[inline(always)]
73 #[must_use]
74 pub fn dsa() -> Self {
75 unsafe { Self(kSecAttrKeyTypeDSA) }
76 }
77
78 #[cfg(target_os = "macos")]
79 #[inline(always)]
80 #[must_use]
81 pub fn aes() -> Self {
82 unsafe { Self(kSecAttrKeyTypeAES) }
83 }
84
85 #[cfg(target_os = "macos")]
86 #[inline(always)]
87 #[must_use]
88 pub fn des() -> Self {
89 unsafe { Self(kSecAttrKeyTypeDES) }
90 }
91
92 #[cfg(target_os = "macos")]
93 #[inline(always)]
94 #[must_use]
95 pub fn triple_des() -> Self {
96 unsafe { Self(kSecAttrKeyType3DES) }
97 }
98
99 #[cfg(target_os = "macos")]
100 #[inline(always)]
101 #[must_use]
102 pub fn rc4() -> Self {
103 unsafe { Self(kSecAttrKeyTypeRC4) }
104 }
105
106 #[cfg(target_os = "macos")]
107 #[inline(always)]
108 #[must_use]
109 pub fn cast() -> Self {
110 unsafe { Self(kSecAttrKeyTypeCAST) }
111 }
112
113 #[inline(always)]
114 #[must_use]
115 pub fn ec() -> Self {
116 use security_framework_sys::item::kSecAttrKeyTypeEC;
117
118 unsafe { Self(kSecAttrKeyTypeEC) }
119 }
120
121 #[inline(always)]
122 #[must_use]
123 pub fn ec_sec_prime_random() -> Self {
124 use security_framework_sys::item::kSecAttrKeyTypeECSECPrimeRandom;
125
126 unsafe { Self(kSecAttrKeyTypeECSECPrimeRandom) }
127 }
128
129 pub(crate) fn to_str(self) -> CFString {
130 unsafe { CFString::wrap_under_get_rule(self.0) }
131 }
132}
133
134declare_TCFType! {
135 SecKey, SecKeyRef
137}
138impl_TCFType!(SecKey, SecKeyRef, SecKeyGetTypeID);
139
140unsafe impl Sync for SecKey {}
141unsafe impl Send for SecKey {}
142
143impl SecKey {
144 #[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))]
146 #[allow(deprecated)]
147 #[doc(alias = "SecKeyCreateRandomKey")]
148 pub fn new(options: &GenerateKeyOptions) -> Result<Self, CFError> {
149 Self::generate(options.to_dictionary())
150 }
151
152 #[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))]
153 #[deprecated(note = "Use SecKey::new")]
156 pub fn generate(attributes: CFDictionary) -> Result<Self, CFError> {
157 let mut error: CFErrorRef = ::std::ptr::null_mut();
158 let sec_key = unsafe { SecKeyCreateRandomKey(attributes.as_concrete_TypeRef(), &mut error) };
159 if !error.is_null() {
160 Err(unsafe { CFError::wrap_under_create_rule(error) })
161 } else {
162 Ok(unsafe { Self::wrap_under_create_rule(sec_key) })
163 }
164 }
165
166 #[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))]
170 #[must_use]
171 pub fn application_label(&self) -> Option<Vec<u8>> {
172 self.attributes()
173 .find(unsafe { kSecAttrApplicationLabel.to_void() })
174 .map(|v| unsafe { CFData::wrap_under_get_rule(v.cast()) }.to_vec())
175 }
176
177 #[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))]
178 #[must_use]
181 pub fn attributes(&self) -> CFDictionary {
182 let pka = unsafe { SecKeyCopyAttributes(self.to_void() as _) };
183 unsafe { CFDictionary::wrap_under_create_rule(pka) }
184 }
185
186 #[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))]
187 #[must_use]
190 pub fn external_representation(&self) -> Option<CFData> {
191 let mut error: CFErrorRef = ::std::ptr::null_mut();
192 let data = unsafe { SecKeyCopyExternalRepresentation(self.to_void() as _, &mut error) };
193 if data.is_null() {
194 return None;
195 }
196 Some(unsafe { CFData::wrap_under_create_rule(data) })
197 }
198
199 #[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))]
200 #[must_use]
202 pub fn public_key(&self) -> Option<Self> {
203 let pub_seckey = unsafe { SecKeyCopyPublicKey(self.0.cast()) };
204 if pub_seckey.is_null() {
205 return None;
206 }
207
208 Some(unsafe { Self::wrap_under_create_rule(pub_seckey) })
209 }
210
211 #[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))]
212 pub fn encrypt_data(&self, algorithm: Algorithm, input: &[u8]) -> Result<Vec<u8>, CFError> {
214 let mut error: CFErrorRef = std::ptr::null_mut();
215
216 let output = unsafe {
217 SecKeyCreateEncryptedData(self.as_concrete_TypeRef(), algorithm.into(), CFData::from_buffer(input).as_concrete_TypeRef(), &mut error)
218 };
219
220 if error.is_null() {
221 let output = unsafe { CFData::wrap_under_create_rule(output) };
222 Ok(output.to_vec())
223 } else {
224 Err(unsafe { CFError::wrap_under_create_rule(error) })
225 }
226 }
227
228 #[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))]
229 pub fn decrypt_data(&self, algorithm: Algorithm, input: &[u8]) -> Result<Vec<u8>, CFError> {
231 let mut error: CFErrorRef = std::ptr::null_mut();
232
233 let output = unsafe {
234 SecKeyCreateDecryptedData(self.as_concrete_TypeRef(), algorithm.into(), CFData::from_buffer(input).as_concrete_TypeRef(), &mut error)
235 };
236
237 if error.is_null() {
238 let output = unsafe { CFData::wrap_under_create_rule(output) };
239 Ok(output.to_vec())
240 } else {
241 Err(unsafe { CFError::wrap_under_create_rule(error) })
242 }
243 }
244
245 #[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))]
246 pub fn create_signature(&self, algorithm: Algorithm, input: &[u8]) -> Result<Vec<u8>, CFError> {
249 let mut error: CFErrorRef = std::ptr::null_mut();
250
251 let output = unsafe {
252 SecKeyCreateSignature(
253 self.as_concrete_TypeRef(),
254 algorithm.into(),
255 CFData::from_buffer(input).as_concrete_TypeRef(),
256 &mut error,
257 )
258 };
259
260 if error.is_null() {
261 let output = unsafe { CFData::wrap_under_create_rule(output) };
262 Ok(output.to_vec())
263 } else {
264 Err(unsafe { CFError::wrap_under_create_rule(error) })
265 }
266 }
267
268 #[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))]
271 pub fn verify_signature(&self, algorithm: Algorithm, signed_data: &[u8], signature: &[u8]) -> Result<bool, CFError> {
272 use security_framework_sys::key::SecKeyVerifySignature;
273 let mut error: CFErrorRef = std::ptr::null_mut();
274
275 let valid = unsafe {
276 SecKeyVerifySignature(
277 self.as_concrete_TypeRef(),
278 algorithm.into(),
279 CFData::from_buffer(signed_data).as_concrete_TypeRef(),
280 CFData::from_buffer(signature).as_concrete_TypeRef(),
281 &mut error,
282 )
283 };
284
285 if !error.is_null() {
286 return Err(unsafe { CFError::wrap_under_create_rule(error) })?;
287 }
288 Ok(valid != 0)
289 }
290
291 #[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))]
293 pub fn key_exchange(
294 &self,
295 algorithm: Algorithm,
296 public_key: &Self,
297 requested_size: usize,
298 shared_info: Option<&[u8]>,
299 ) -> Result<Vec<u8>, CFError> {
300 use core_foundation::data::CFData;
301 use security_framework_sys::item::{
302 kSecKeyKeyExchangeParameterRequestedSize, kSecKeyKeyExchangeParameterSharedInfo,
303 };
304
305 unsafe {
306 let mut params = vec![(
307 CFString::wrap_under_get_rule(kSecKeyKeyExchangeParameterRequestedSize),
308 CFNumber::from(requested_size as i64).into_CFType(),
309 )];
310
311 if let Some(shared_info) = shared_info {
312 params.push((
313 CFString::wrap_under_get_rule(kSecKeyKeyExchangeParameterSharedInfo),
314 CFData::from_buffer(shared_info).as_CFType(),
315 ));
316 };
317
318 let parameters = CFDictionary::from_CFType_pairs(¶ms);
319
320 let mut error: CFErrorRef = std::ptr::null_mut();
321
322 let output = security_framework_sys::key::SecKeyCopyKeyExchangeResult(
323 self.as_concrete_TypeRef(),
324 algorithm.into(),
325 public_key.as_concrete_TypeRef(),
326 parameters.as_concrete_TypeRef(),
327 &mut error,
328 );
329
330 if error.is_null() {
331 let output = CFData::wrap_under_create_rule(output);
332 Ok(output.to_vec())
333 } else {
334 Err(CFError::wrap_under_create_rule(error))
335 }
336 }
337 }
338
339 pub fn delete(&self) -> Result<(), Error> {
341 let query = CFMutableDictionary::from_CFType_pairs(&[(
342 unsafe { kSecValueRef }.to_void(),
343 self.to_void(),
344 )]);
345
346 cvt(unsafe { SecItemDelete(query.as_concrete_TypeRef()) })
347 }
348}
349
350#[derive(Debug)]
352pub enum Token {
353 Software,
355 SecureEnclave,
358}
359
360#[derive(Debug, Default)]
364#[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))]
365pub struct GenerateKeyOptions {
366 #[deprecated(note = "use set_key_type()")]
368 pub key_type: Option<KeyType>,
369 #[deprecated(note = "use set_size_in_bits()")]
371 pub size_in_bits: Option<u32>,
372 #[deprecated(note = "use set_label()")]
374 pub label: Option<String>,
375 #[deprecated(note = "use set_token()")]
377 pub token: Option<Token>,
378 #[deprecated(note = "use set_location()")]
380 pub location: Option<Location>,
381 #[deprecated(note = "use set_access_control()")]
383 pub access_control: Option<SecAccessControl>,
384 #[cfg(feature = "sync-keychain")]
386 synchronizable: Option<bool>,
387}
388
389#[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))]
390#[allow(deprecated)]
391impl GenerateKeyOptions {
392 pub fn set_key_type(&mut self, key_type: KeyType) -> &mut Self {
394 self.key_type = Some(key_type);
395 self
396 }
397
398 pub fn set_size_in_bits(&mut self, size_in_bits: u32) -> &mut Self {
400 self.size_in_bits = Some(size_in_bits);
401 self
402 }
403
404 pub fn set_label(&mut self, label: impl Into<String>) -> &mut Self {
406 self.label = Some(label.into());
407 self
408 }
409
410 pub fn set_token(&mut self, token: Token) -> &mut Self {
412 self.token = Some(token);
413 self
414 }
415
416 pub fn set_location(&mut self, location: Location) -> &mut Self {
418 self.location = Some(location);
419 self
420 }
421
422 pub fn set_access_control(&mut self, access_control: SecAccessControl) -> &mut Self {
424 self.access_control = Some(access_control);
425 self
426 }
427
428 #[cfg(feature = "sync-keychain")]
430 pub fn set_synchronizable(&mut self, synchronizable: bool) -> &mut Self {
431 self.synchronizable = Some(synchronizable);
432 self
433 }
434
435 #[deprecated(note = "Pass the options to SecKey::new")]
438 pub fn to_dictionary(&self) -> CFDictionary {
439 #[cfg(target_os = "macos")]
440 use security_framework_sys::item::kSecUseKeychain;
441 use security_framework_sys::item::{
442 kSecAttrTokenID, kSecAttrTokenIDSecureEnclave, kSecPublicKeyAttrs,
443 };
444
445 let is_permanent = CFBoolean::from(self.location.is_some());
446 let mut private_attributes = CFMutableDictionary::from_CFType_pairs(&[(
447 unsafe { kSecAttrIsPermanent }.to_void(),
448 is_permanent.to_void(),
449 )]);
450 if let Some(access_control) = &self.access_control {
451 private_attributes.set(unsafe { kSecAttrAccessControl }.to_void(), access_control.to_void());
452 }
453
454 let public_attributes = CFMutableDictionary::from_CFType_pairs(&[(
455 unsafe { kSecAttrIsPermanent }.to_void(),
456 is_permanent.to_void(),
457 )]);
458
459 let key_type = self.key_type.unwrap_or_else(KeyType::rsa).to_str();
460
461 let size_in_bits = self.size_in_bits.unwrap_or(match () {
462 #[cfg(target_os = "macos")]
463 _ if key_type == KeyType::aes().to_str() => 256,
464 _ if key_type == KeyType::rsa().to_str() => 2048,
465 _ if key_type == KeyType::ec().to_str() => 256,
466 _ if key_type == KeyType::ec_sec_prime_random().to_str() => 256,
467 _ => 256,
468 });
469 let size_in_bits = CFNumber::from(size_in_bits as i32);
470
471 let mut attribute_key_values = vec![
472 (unsafe { kSecAttrKeyType }.to_void(), key_type.to_void()),
473 (unsafe { kSecAttrKeySizeInBits }.to_void(), size_in_bits.to_void()),
474 ];
475 #[cfg(target_os = "macos")]
476 if key_type != KeyType::aes().to_str() {
477 attribute_key_values.push((unsafe { kSecPublicKeyAttrs }.to_void(), public_attributes.to_void()));
478 attribute_key_values.push((unsafe { kSecPrivateKeyAttrs }.to_void(), private_attributes.to_void()));
479 }
480
481 let label = self.label.as_deref().map(CFString::new);
482 if let Some(label) = &label {
483 attribute_key_values.push((unsafe { kSecAttrLabel }.to_void(), label.to_void()));
484 }
485
486 #[cfg(target_os = "macos")]
487 match &self.location {
488 #[cfg(feature = "OSX_10_15")]
489 Some(Location::DataProtectionKeychain) => {
490 use security_framework_sys::item::kSecUseDataProtectionKeychain;
491 attribute_key_values.push((
492 unsafe { kSecUseDataProtectionKeychain }.to_void(),
493 CFBoolean::true_value().to_void(),
494 ));
495 }
496 Some(Location::FileKeychain(keychain)) => {
497 attribute_key_values.push((
498 unsafe { kSecUseKeychain }.to_void(),
499 keychain.as_concrete_TypeRef().to_void(),
500 ));
501 }
502 _ => {}
503 }
504
505 match self.token.as_ref().unwrap_or(&Token::Software) {
506 Token::Software => {},
507 Token::SecureEnclave => {
508 attribute_key_values.push((
509 unsafe { kSecAttrTokenID }.to_void(),
510 unsafe { kSecAttrTokenIDSecureEnclave }.to_void(),
511 ));
512 }
513 }
514
515 #[cfg(feature = "sync-keychain")]
516 if let Some(ref synchronizable) = self.synchronizable {
517 attribute_key_values.push((
518 unsafe { security_framework_sys::item::kSecAttrSynchronizable }.to_void(),
519 CFBoolean::from(*synchronizable).to_void(),
520 ));
521 }
522
523 CFMutableDictionary::from_CFType_pairs(&attribute_key_values).to_immutable()
524 }
525}
526
527impl fmt::Debug for SecKey {
528 #[cold]
529 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
530 fmt.debug_struct("SecKey").finish_non_exhaustive()
531 }
532}