apple_security_framework/
key.rs1use std::fmt;
4
5#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
6use core_foundation::boolean::CFBoolean;
7#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
8use core_foundation::data::CFData;
9#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
10use core_foundation::dictionary::CFDictionary;
11#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
12use core_foundation::error::CFError;
13#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
14use core_foundation::error::CFErrorRef;
15#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
16use core_foundation::number::CFNumber;
17use core_foundation::{
18 base::{TCFType, ToVoid},
19 dictionary::CFMutableDictionary,
20 string::{CFString, CFStringRef},
21};
22#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
23use security_framework_sys::item::kSecAttrApplicationLabel;
24#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
25use security_framework_sys::item::kSecAttrIsPermanent;
26#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
27use security_framework_sys::item::kSecAttrKeySizeInBits;
28#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
29use security_framework_sys::item::kSecAttrKeyType;
30#[cfg(target_os = "macos")]
31use security_framework_sys::item::kSecAttrKeyType3DES;
32#[cfg(target_os = "macos")]
33use security_framework_sys::item::kSecAttrKeyTypeAES;
34#[cfg(target_os = "macos")]
35use security_framework_sys::item::kSecAttrKeyTypeCAST;
36#[cfg(target_os = "macos")]
37use security_framework_sys::item::kSecAttrKeyTypeDES;
38#[cfg(target_os = "macos")]
39use security_framework_sys::item::kSecAttrKeyTypeDSA;
40#[cfg(target_os = "macos")]
41use security_framework_sys::item::kSecAttrKeyTypeRC4;
42#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
43use security_framework_sys::item::kSecAttrLabel;
44#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
45use security_framework_sys::item::kSecPrivateKeyAttrs;
46#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
47pub use security_framework_sys::key::Algorithm;
48#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
49use security_framework_sys::key::SecKeyCopyAttributes;
50#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
51use security_framework_sys::key::SecKeyCopyExternalRepresentation;
52#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
53use security_framework_sys::key::SecKeyCopyPublicKey;
54#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
55use security_framework_sys::key::SecKeyCreateRandomKey;
56#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
57use security_framework_sys::key::SecKeyCreateSignature;
58use security_framework_sys::{
59 base::SecKeyRef,
60 item::{kSecAttrKeyTypeRSA, kSecValueRef},
61 key::SecKeyGetTypeID,
62 keychain_item::SecItemDelete,
63};
64
65#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
66use crate::item::Location;
67use crate::{base::Error, cvt};
68
69#[derive(Debug, Copy, Clone)]
71pub struct KeyType(CFStringRef);
72
73#[allow(missing_docs)]
74impl KeyType {
75 #[inline(always)]
76 #[must_use]
77 pub fn rsa() -> Self {
78 unsafe { Self(kSecAttrKeyTypeRSA) }
79 }
80
81 #[cfg(target_os = "macos")]
82 #[inline(always)]
83 #[must_use]
84 pub fn dsa() -> Self {
85 unsafe { Self(kSecAttrKeyTypeDSA) }
86 }
87
88 #[cfg(target_os = "macos")]
89 #[inline(always)]
90 #[must_use]
91 pub fn aes() -> Self {
92 unsafe { Self(kSecAttrKeyTypeAES) }
93 }
94
95 #[cfg(target_os = "macos")]
96 #[inline(always)]
97 #[must_use]
98 pub fn des() -> Self {
99 unsafe { Self(kSecAttrKeyTypeDES) }
100 }
101
102 #[cfg(target_os = "macos")]
103 #[inline(always)]
104 #[must_use]
105 pub fn triple_des() -> Self {
106 unsafe { Self(kSecAttrKeyType3DES) }
107 }
108
109 #[cfg(target_os = "macos")]
110 #[inline(always)]
111 #[must_use]
112 pub fn rc4() -> Self {
113 unsafe { Self(kSecAttrKeyTypeRC4) }
114 }
115
116 #[cfg(target_os = "macos")]
117 #[inline(always)]
118 #[must_use]
119 pub fn cast() -> Self {
120 unsafe { Self(kSecAttrKeyTypeCAST) }
121 }
122
123 #[cfg(any(feature = "OSX_10_9", target_os = "ios"))]
124 #[inline(always)]
125 #[must_use]
126 pub fn ec() -> Self {
127 use security_framework_sys::item::kSecAttrKeyTypeEC;
128
129 unsafe { Self(kSecAttrKeyTypeEC) }
130 }
131
132 pub(crate) fn to_str(self) -> CFString {
133 unsafe { CFString::wrap_under_get_rule(self.0) }
134 }
135}
136
137declare_TCFType! {
138 SecKey, SecKeyRef
140}
141impl_TCFType!(SecKey, SecKeyRef, SecKeyGetTypeID);
142
143unsafe impl Sync for SecKey {}
144unsafe impl Send for SecKey {}
145
146impl SecKey {
147 #[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
148 pub fn generate(attributes: CFDictionary) -> Result<Self, CFError> {
152 let mut error: CFErrorRef = ::std::ptr::null_mut();
153 let sec_key =
154 unsafe { SecKeyCreateRandomKey(attributes.as_concrete_TypeRef(), &mut error) };
155 if !error.is_null() {
156 Err(unsafe { CFError::wrap_under_create_rule(error) })
157 } else {
158 Ok(unsafe { SecKey::wrap_under_create_rule(sec_key) })
159 }
160 }
161
162 #[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
166 pub fn application_label(&self) -> Option<Vec<u8>> {
167 self.attributes()
168 .find(unsafe { kSecAttrApplicationLabel.to_void() })
169 .map(|v| unsafe { CFData::wrap_under_get_rule(v.cast()) }.to_vec())
170 }
171
172 #[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
173 #[must_use]
175 pub fn attributes(&self) -> CFDictionary {
176 let pka = unsafe { SecKeyCopyAttributes(self.to_void() as _) };
177 unsafe { CFDictionary::wrap_under_create_rule(pka) }
178 }
179
180 #[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
181 #[must_use]
183 pub fn external_representation(&self) -> Option<CFData> {
184 let mut error: CFErrorRef = ::std::ptr::null_mut();
185 let data = unsafe { SecKeyCopyExternalRepresentation(self.to_void() as _, &mut error) };
186 if data.is_null() {
187 return None;
188 }
189 Some(unsafe { CFData::wrap_under_create_rule(data) })
190 }
191
192 #[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
193 #[must_use]
195 pub fn public_key(&self) -> Option<Self> {
196 let pub_seckey = unsafe { SecKeyCopyPublicKey(self.0.cast()) };
197 if pub_seckey.is_null() {
198 return None;
199 }
200
201 Some(unsafe { SecKey::wrap_under_create_rule(pub_seckey) })
202 }
203
204 #[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
205 pub fn create_signature(&self, algorithm: Algorithm, input: &[u8]) -> Result<Vec<u8>, CFError> {
208 let mut error: CFErrorRef = std::ptr::null_mut();
209
210 let output = unsafe {
211 SecKeyCreateSignature(
212 self.as_concrete_TypeRef(),
213 algorithm.into(),
214 CFData::from_buffer(input).as_concrete_TypeRef(),
215 &mut error,
216 )
217 };
218
219 if !error.is_null() {
220 Err(unsafe { CFError::wrap_under_create_rule(error) })
221 } else {
222 let output = unsafe { CFData::wrap_under_create_rule(output) };
223 Ok(output.to_vec())
224 }
225 }
226
227 #[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
230 pub fn verify_signature(
231 &self,
232 algorithm: Algorithm,
233 signed_data: &[u8],
234 signature: &[u8],
235 ) -> Result<bool, CFError> {
236 use security_framework_sys::key::SecKeyVerifySignature;
237 let mut error: CFErrorRef = std::ptr::null_mut();
238
239 let valid = unsafe {
240 SecKeyVerifySignature(
241 self.as_concrete_TypeRef(),
242 algorithm.into(),
243 CFData::from_buffer(signed_data).as_concrete_TypeRef(),
244 CFData::from_buffer(signature).as_concrete_TypeRef(),
245 &mut error,
246 )
247 };
248
249 if !error.is_null() {
250 return Err(unsafe { CFError::wrap_under_create_rule(error) })?;
251 }
252 Ok(valid != 0)
253 }
254
255 pub fn delete(&self) -> Result<(), Error> {
257 let query = CFMutableDictionary::from_CFType_pairs(&[(
258 unsafe { kSecValueRef }.to_void(),
259 self.to_void(),
260 )]);
261
262 cvt(unsafe { SecItemDelete(query.as_concrete_TypeRef()) })
263 }
264}
265
266pub enum Token {
268 Software,
270 SecureEnclave,
273}
274
275#[derive(Default)]
279#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
280pub struct GenerateKeyOptions {
281 pub key_type: Option<KeyType>,
283 pub size_in_bits: Option<u32>,
285 pub label: Option<String>,
287 pub token: Option<Token>,
289 pub location: Option<Location>,
291}
292
293#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
294impl GenerateKeyOptions {
295 pub fn set_key_type(&mut self, key_type: KeyType) -> &mut Self {
297 self.key_type = Some(key_type);
298 self
299 }
300 pub fn set_size_in_bits(&mut self, size_in_bits: u32) -> &mut Self {
302 self.size_in_bits = Some(size_in_bits);
303 self
304 }
305 pub fn set_label(&mut self, label: impl Into<String>) -> &mut Self {
307 self.label = Some(label.into());
308 self
309 }
310 pub fn set_token(&mut self, token: Token) -> &mut Self {
312 self.token = Some(token);
313 self
314 }
315 pub fn set_location(&mut self, location: Location) -> &mut Self {
317 self.location = Some(location);
318 self
319 }
320
321 pub fn to_dictionary(&self) -> CFDictionary {
323 #[cfg(target_os = "macos")]
324 use security_framework_sys::item::kSecUseKeychain;
325 use security_framework_sys::item::{
326 kSecAttrTokenID, kSecAttrTokenIDSecureEnclave, kSecPublicKeyAttrs,
327 };
328
329 let is_permanent = CFBoolean::from(self.location.is_some());
330 let private_attributes = CFMutableDictionary::from_CFType_pairs(&[(
331 unsafe { kSecAttrIsPermanent }.to_void(),
332 is_permanent.to_void(),
333 )]);
334
335 let public_attributes = CFMutableDictionary::from_CFType_pairs(&[(
336 unsafe { kSecAttrIsPermanent }.to_void(),
337 is_permanent.to_void(),
338 )]);
339
340 let key_type = self.key_type.unwrap_or_else(KeyType::rsa).to_str();
341
342 let size_in_bits = self.size_in_bits.unwrap_or(match () {
343 _ if key_type == KeyType::rsa().to_str() => 2048,
344 _ if key_type == KeyType::ec().to_str() => 256,
345 _ => 256,
346 });
347 let size_in_bits = CFNumber::from(size_in_bits as i32);
348
349 let mut attribute_key_values = vec![
350 (unsafe { kSecAttrKeyType }.to_void(), key_type.to_void()),
351 (
352 unsafe { kSecAttrKeySizeInBits }.to_void(),
353 size_in_bits.to_void(),
354 ),
355 (
356 unsafe { kSecPrivateKeyAttrs }.to_void(),
357 private_attributes.to_void(),
358 ),
359 (
360 unsafe { kSecPublicKeyAttrs }.to_void(),
361 public_attributes.to_void(),
362 ),
363 ];
364 let label = self.label.as_deref().map(CFString::new);
365 if let Some(label) = &label {
366 attribute_key_values.push((unsafe { kSecAttrLabel }.to_void(), label.to_void()));
367 }
368
369 #[cfg(target_os = "macos")]
370 match &self.location {
371 #[cfg(feature = "OSX_10_15")]
372 Some(Location::DataProtectionKeychain) => {
373 use security_framework_sys::item::kSecUseDataProtectionKeychain;
374 attribute_key_values.push((
375 unsafe { kSecUseDataProtectionKeychain }.to_void(),
376 CFBoolean::true_value().to_void(),
377 ));
378 }
379 Some(Location::FileKeychain(keychain)) => {
380 attribute_key_values.push((
381 unsafe { kSecUseKeychain }.to_void(),
382 keychain.as_concrete_TypeRef().to_void(),
383 ));
384 }
385 _ => {}
386 }
387
388 match self.token.as_ref().unwrap_or(&Token::Software) {
389 Token::Software => {}
390 Token::SecureEnclave => {
391 attribute_key_values.push((
392 unsafe { kSecAttrTokenID }.to_void(),
393 unsafe { kSecAttrTokenIDSecureEnclave }.to_void(),
394 ));
395 }
396 }
397
398 CFMutableDictionary::from_CFType_pairs(&attribute_key_values).to_immutable()
399 }
400}
401
402impl fmt::Debug for SecKey {
403 #[cold]
404 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
405 fmt.debug_struct("SecKey").finish_non_exhaustive()
406 }
407}