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;
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#[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 SecKey, SecKeyRef
126}
127impl_TCFType!(SecKey, SecKeyRef, SecKeyGetTypeID);
128
129unsafe impl Sync for SecKey {}
130unsafe impl Send for SecKey {}
131
132impl SecKey {
133 #[allow(deprecated)]
135 #[doc(alias = "SecKeyCreateRandomKey")]
136 pub fn new(options: &GenerateKeyOptions) -> Result<Self, CFError> {
137 Self::generate(options.to_dictionary())
138 }
139
140 #[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 #[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 #[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 #[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 #[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 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 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 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 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 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(¶ms);
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 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#[derive(Debug)]
330pub enum Token {
331 Software,
333 SecureEnclave,
336}
337
338#[derive(Debug, Default)]
342pub struct GenerateKeyOptions {
343 #[deprecated(note = "use set_key_type()")]
345 pub key_type: Option<KeyType>,
346 #[deprecated(note = "use set_size_in_bits()")]
348 pub size_in_bits: Option<u32>,
349 #[deprecated(note = "use set_label()")]
351 pub label: Option<String>,
352 #[deprecated(note = "use set_token()")]
354 pub token: Option<Token>,
355 #[deprecated(note = "use set_location()")]
357 pub location: Option<Location>,
358 #[deprecated(note = "use set_access_control()")]
360 pub access_control: Option<SecAccessControl>,
361 #[cfg(feature = "sync-keychain")]
363 synchronizable: Option<bool>,
364}
365
366#[allow(deprecated)]
367impl GenerateKeyOptions {
368 pub fn set_key_type(&mut self, key_type: KeyType) -> &mut Self {
370 self.key_type = Some(key_type);
371 self
372 }
373
374 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 pub fn set_label(&mut self, label: impl Into<String>) -> &mut Self {
382 self.label = Some(label.into());
383 self
384 }
385
386 pub fn set_token(&mut self, token: Token) -> &mut Self {
388 self.token = Some(token);
389 self
390 }
391
392 pub fn set_location(&mut self, location: Location) -> &mut Self {
394 self.location = Some(location);
395 self
396 }
397
398 pub fn set_access_control(&mut self, access_control: SecAccessControl) -> &mut Self {
400 self.access_control = Some(access_control);
401 self
402 }
403
404 #[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 #[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}