backend_macos.rs (32987B)
1 /* -*- Mode: rust; rust-indent-offset: 4 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #![allow(non_upper_case_globals)] 7 8 use core_foundation::array::*; 9 use core_foundation::base::*; 10 use core_foundation::boolean::*; 11 use core_foundation::data::*; 12 use core_foundation::dictionary::*; 13 use core_foundation::error::*; 14 use core_foundation::number::*; 15 use core_foundation::string::*; 16 use libloading::{Library, Symbol}; 17 use log::error; 18 use pkcs11_bindings::*; 19 use rsclientcerts::cryptoki::*; 20 use rsclientcerts::manager::{ClientCertsBackend, CryptokiObject, Sign}; 21 use rsclientcerts_util::*; 22 use rsclientcerts_util::error::{Error, ErrorType}; 23 use std::collections::BTreeMap; 24 use std::convert::TryInto; 25 use std::os::raw::c_void; 26 use std::time::{Duration, Instant}; 27 use xpcom::interfaces::nsIEventTarget; 28 use xpcom::{RefPtr, XpCom}; 29 30 // Normally we would generate this with a build script, but macos is 31 // cross-compiled on linux, and we'd have to figure out e.g. include paths, 32 // etc.. This is easier. 33 include!("bindings_macos.rs"); 34 35 #[repr(C)] 36 pub struct __SecIdentity(c_void); 37 pub type SecIdentityRef = *const __SecIdentity; 38 declare_TCFType!(SecIdentity, SecIdentityRef); 39 impl_TCFType!(SecIdentity, SecIdentityRef, SecIdentityGetTypeID); 40 41 /// Safety: strictly speaking, it isn't safe to send `SecIdentity` across threads. The 42 /// implementation handles this by wrapping `SecIdentity` in `ThreadSpecificHandles`. However, in 43 /// order to implement `Drop` for `ThreadSpecificHandles`, the `SecIdentity` it holds must be sent 44 /// to the appropriate thread, hence this impl. 45 unsafe impl Send for SecIdentity {} 46 47 #[repr(C)] 48 pub struct __SecCertificate(c_void); 49 pub type SecCertificateRef = *const __SecCertificate; 50 declare_TCFType!(SecCertificate, SecCertificateRef); 51 impl_TCFType!(SecCertificate, SecCertificateRef, SecCertificateGetTypeID); 52 53 #[repr(C)] 54 pub struct __SecKey(c_void); 55 pub type SecKeyRef = *const __SecKey; 56 declare_TCFType!(SecKey, SecKeyRef); 57 impl_TCFType!(SecKey, SecKeyRef, SecKeyGetTypeID); 58 59 /// Safety: see the comment for the `Send` impl for `SecIdentity`. 60 unsafe impl Send for SecKey {} 61 62 #[repr(C)] 63 pub struct __SecPolicy(c_void); 64 pub type SecPolicyRef = *const __SecPolicy; 65 declare_TCFType!(SecPolicy, SecPolicyRef); 66 impl_TCFType!(SecPolicy, SecPolicyRef, SecPolicyGetTypeID); 67 68 #[repr(C)] 69 pub struct __SecTrust(c_void); 70 pub type SecTrustRef = *const __SecTrust; 71 declare_TCFType!(SecTrust, SecTrustRef); 72 impl_TCFType!(SecTrust, SecTrustRef, SecTrustGetTypeID); 73 74 type SecCertificateCopyKeyType = unsafe extern "C" fn(SecCertificateRef) -> SecKeyRef; 75 type SecTrustEvaluateWithErrorType = 76 unsafe extern "C" fn(trust: SecTrustRef, error: *mut CFErrorRef) -> bool; 77 78 #[derive(Ord, Eq, PartialOrd, PartialEq)] 79 enum SecStringConstant { 80 // These are available in macOS 10.13 81 SecKeyAlgorithmRSASignatureDigestPSSSHA1, 82 SecKeyAlgorithmRSASignatureDigestPSSSHA256, 83 SecKeyAlgorithmRSASignatureDigestPSSSHA384, 84 SecKeyAlgorithmRSASignatureDigestPSSSHA512, 85 } 86 87 /// This implementation uses security framework functions and constants that 88 /// are not provided by the version of the SDK we build with. To work around 89 /// this, we attempt to open and dynamically load these functions and symbols 90 /// at runtime. Unfortunately this does mean that if a user is not on a new 91 /// enough version of macOS, they will not be able to use client certificates 92 /// from their keychain in Firefox until they upgrade. 93 struct SecurityFramework<'a> { 94 sec_certificate_copy_key: Symbol<'a, SecCertificateCopyKeyType>, 95 sec_trust_evaluate_with_error: Symbol<'a, SecTrustEvaluateWithErrorType>, 96 sec_string_constants: BTreeMap<SecStringConstant, String>, 97 } 98 99 lazy_static! { 100 static ref SECURITY_LIBRARY: Result<Library, String> = unsafe { 101 Library::new("/System/Library/Frameworks/Security.framework/Security") 102 .map_err(|e| e.to_string()) 103 }; 104 } 105 106 impl<'a> SecurityFramework<'a> { 107 fn new() -> Result<SecurityFramework<'a>, Error> { 108 let library = match &*SECURITY_LIBRARY { 109 Ok(library) => library, 110 Err(e) => return Err(error_here!(ErrorType::ExternalError, e.clone())), 111 }; 112 let sec_certificate_copy_key = unsafe { 113 library 114 .get::<SecCertificateCopyKeyType>(b"SecCertificateCopyKey\0") 115 .map_err(|e| error_here!(ErrorType::ExternalError, e.to_string()))? 116 }; 117 let sec_trust_evaluate_with_error = unsafe { 118 library 119 .get::<SecTrustEvaluateWithErrorType>(b"SecTrustEvaluateWithError\0") 120 .map_err(|e| error_here!(ErrorType::ExternalError, e.to_string()))? 121 }; 122 let mut sec_string_constants = BTreeMap::new(); 123 let strings_to_load = vec![ 124 ( 125 b"kSecKeyAlgorithmRSASignatureDigestPSSSHA1\0".as_ref(), 126 SecStringConstant::SecKeyAlgorithmRSASignatureDigestPSSSHA1, 127 ), 128 ( 129 b"kSecKeyAlgorithmRSASignatureDigestPSSSHA256\0".as_ref(), 130 SecStringConstant::SecKeyAlgorithmRSASignatureDigestPSSSHA256, 131 ), 132 ( 133 b"kSecKeyAlgorithmRSASignatureDigestPSSSHA384\0".as_ref(), 134 SecStringConstant::SecKeyAlgorithmRSASignatureDigestPSSSHA384, 135 ), 136 ( 137 b"kSecKeyAlgorithmRSASignatureDigestPSSSHA512\0".as_ref(), 138 SecStringConstant::SecKeyAlgorithmRSASignatureDigestPSSSHA512, 139 ), 140 ]; 141 for (symbol_name, sec_string_constant) in strings_to_load { 142 let cfstring_symbol = unsafe { 143 library 144 .get::<*const CFStringRef>(symbol_name) 145 .map_err(|e| error_here!(ErrorType::ExternalError, e.to_string()))? 146 }; 147 let cfstring = unsafe { CFString::wrap_under_create_rule(**cfstring_symbol) }; 148 sec_string_constants.insert(sec_string_constant, cfstring.to_string()); 149 } 150 Ok(SecurityFramework { 151 sec_certificate_copy_key, 152 sec_trust_evaluate_with_error, 153 sec_string_constants, 154 }) 155 } 156 } 157 158 struct SecurityFrameworkHolder<'a> { 159 framework: Result<SecurityFramework<'a>, Error>, 160 } 161 162 impl<'a> SecurityFrameworkHolder<'a> { 163 fn new() -> SecurityFrameworkHolder<'a> { 164 SecurityFrameworkHolder { 165 framework: SecurityFramework::new(), 166 } 167 } 168 169 /// SecCertificateCopyKey is available in macOS 10.14 170 fn sec_certificate_copy_key(&self, certificate: &SecCertificate) -> Result<SecKey, Error> { 171 match &self.framework { 172 Ok(framework) => unsafe { 173 let result = 174 (framework.sec_certificate_copy_key)(certificate.as_concrete_TypeRef()); 175 if result.is_null() { 176 return Err(error_here!(ErrorType::ExternalError)); 177 } 178 Ok(SecKey::wrap_under_create_rule(result)) 179 }, 180 Err(e) => Err(e.clone()), 181 } 182 } 183 184 /// SecTrustEvaluateWithError is available in macOS 10.14 185 fn sec_trust_evaluate_with_error(&self, trust: &SecTrust) -> Result<bool, Error> { 186 match &self.framework { 187 Ok(framework) => unsafe { 188 Ok((framework.sec_trust_evaluate_with_error)( 189 trust.as_concrete_TypeRef(), 190 std::ptr::null_mut(), 191 )) 192 }, 193 Err(e) => Err(e.clone()), 194 } 195 } 196 197 fn get_sec_string_constant( 198 &self, 199 sec_string_constant: SecStringConstant, 200 ) -> Result<CFString, Error> { 201 match &self.framework { 202 Ok(framework) => match framework.sec_string_constants.get(&sec_string_constant) { 203 Some(string) => Ok(CFString::new(string)), 204 None => Err(error_here!(ErrorType::ExternalError)), 205 }, 206 Err(e) => Err(e.clone()), 207 } 208 } 209 } 210 211 lazy_static! { 212 static ref SECURITY_FRAMEWORK: SecurityFrameworkHolder<'static> = 213 SecurityFrameworkHolder::new(); 214 } 215 216 fn sec_key_create_signature( 217 key: &SecKey, 218 algorithm: SecKeyAlgorithm, 219 data: &CFData, 220 ) -> Result<CFData, Error> { 221 let mut error = std::ptr::null_mut(); 222 let signature = unsafe { 223 SecKeyCreateSignature( 224 key.as_concrete_TypeRef(), 225 algorithm, 226 data.as_concrete_TypeRef(), 227 &mut error, 228 ) 229 }; 230 if signature.is_null() { 231 if error.is_null() { 232 return Err(error_here!(ErrorType::ExternalError)); 233 } 234 let error = unsafe { CFError::wrap_under_create_rule(error) }; 235 return Err(error_here!( 236 ErrorType::ExternalError, 237 error.description().to_string() 238 )); 239 } 240 Ok(unsafe { CFData::wrap_under_create_rule(signature) }) 241 } 242 243 fn sec_key_copy_attributes<T: TCFType>(key: &SecKey) -> Result<CFDictionary<CFString, T>, Error> { 244 let attributes = unsafe { SecKeyCopyAttributes(key.as_concrete_TypeRef()) }; 245 if attributes.is_null() { 246 return Err(error_here!(ErrorType::ExternalError)); 247 } 248 Ok(unsafe { CFDictionary::wrap_under_create_rule(attributes) }) 249 } 250 251 fn sec_key_copy_external_representation(key: &SecKey) -> Result<CFData, Error> { 252 let mut error = std::ptr::null_mut(); 253 let representation = 254 unsafe { SecKeyCopyExternalRepresentation(key.as_concrete_TypeRef(), &mut error) }; 255 if representation.is_null() { 256 if error.is_null() { 257 return Err(error_here!(ErrorType::ExternalError)); 258 } 259 let error = unsafe { CFError::wrap_under_create_rule(error) }; 260 return Err(error_here!( 261 ErrorType::ExternalError, 262 error.description().to_string() 263 )); 264 } 265 Ok(unsafe { CFData::wrap_under_create_rule(representation) }) 266 } 267 268 fn sec_identity_copy_certificate(identity: &SecIdentity) -> Result<SecCertificate, Error> { 269 let mut certificate = std::ptr::null(); 270 let status = 271 unsafe { SecIdentityCopyCertificate(identity.as_concrete_TypeRef(), &mut certificate) }; 272 if status != errSecSuccess { 273 return Err(error_here!(ErrorType::ExternalError, status.to_string())); 274 } 275 if certificate.is_null() { 276 return Err(error_here!(ErrorType::ExternalError)); 277 } 278 Ok(unsafe { SecCertificate::wrap_under_create_rule(certificate) }) 279 } 280 281 fn sec_certificate_copy_subject_summary(certificate: &SecCertificate) -> Result<CFString, Error> { 282 let result = unsafe { SecCertificateCopySubjectSummary(certificate.as_concrete_TypeRef()) }; 283 if result.is_null() { 284 return Err(error_here!(ErrorType::ExternalError)); 285 } 286 Ok(unsafe { CFString::wrap_under_create_rule(result) }) 287 } 288 289 fn sec_certificate_copy_data(certificate: &SecCertificate) -> Result<CFData, Error> { 290 let result = unsafe { SecCertificateCopyData(certificate.as_concrete_TypeRef()) }; 291 if result.is_null() { 292 return Err(error_here!(ErrorType::ExternalError)); 293 } 294 Ok(unsafe { CFData::wrap_under_create_rule(result) }) 295 } 296 297 fn sec_identity_copy_private_key(identity: &SecIdentity) -> Result<SecKey, Error> { 298 let mut key = std::ptr::null(); 299 let status = unsafe { SecIdentityCopyPrivateKey(identity.as_concrete_TypeRef(), &mut key) }; 300 if status != errSecSuccess { 301 return Err(error_here!(ErrorType::ExternalError)); 302 } 303 if key.is_null() { 304 return Err(error_here!(ErrorType::ExternalError)); 305 } 306 Ok(unsafe { SecKey::wrap_under_create_rule(key) }) 307 } 308 309 fn new_cert_from_identity(identity: &SecIdentity) -> Result<CryptokiCert, Error> { 310 let certificate = sec_identity_copy_certificate(identity)?; 311 new_cert_from_certificate(&certificate) 312 } 313 314 fn new_cert_from_certificate(certificate: &SecCertificate) -> Result<CryptokiCert, Error> { 315 let der = sec_certificate_copy_data(certificate)?.bytes().to_vec(); 316 let label = sec_certificate_copy_subject_summary(certificate)?; 317 CryptokiCert::new(der, label.to_string().into_bytes()) 318 } 319 320 #[allow(clippy::upper_case_acronyms)] 321 enum SignParams<'a> { 322 EC(CFString, &'a [u8]), 323 RSA(CFString, &'a [u8]), 324 } 325 326 impl<'a> SignParams<'a> { 327 fn new( 328 key_type: KeyType, 329 data: &'a [u8], 330 params: &Option<CK_RSA_PKCS_PSS_PARAMS>, 331 ) -> Result<SignParams<'a>, Error> { 332 match key_type { 333 KeyType::EC(_) => SignParams::new_ec_params(data), 334 KeyType::RSA => SignParams::new_rsa_params(params, data), 335 } 336 } 337 338 fn new_ec_params(data: &'a [u8]) -> Result<SignParams<'a>, Error> { 339 let algorithm = unsafe { 340 CFString::wrap_under_get_rule(match data.len() { 341 20 => kSecKeyAlgorithmECDSASignatureDigestX962SHA1, 342 32 => kSecKeyAlgorithmECDSASignatureDigestX962SHA256, 343 48 => kSecKeyAlgorithmECDSASignatureDigestX962SHA384, 344 64 => kSecKeyAlgorithmECDSASignatureDigestX962SHA512, 345 _ => { 346 return Err(error_here!(ErrorType::UnsupportedInput)); 347 } 348 }) 349 }; 350 Ok(SignParams::EC(algorithm, data)) 351 } 352 353 fn new_rsa_params( 354 params: &Option<CK_RSA_PKCS_PSS_PARAMS>, 355 data: &'a [u8], 356 ) -> Result<SignParams<'a>, Error> { 357 if let Some(pss_params) = params { 358 let algorithm = { 359 let algorithm_id = match pss_params.hashAlg { 360 CKM_SHA_1 => SecStringConstant::SecKeyAlgorithmRSASignatureDigestPSSSHA1, 361 CKM_SHA256 => SecStringConstant::SecKeyAlgorithmRSASignatureDigestPSSSHA256, 362 CKM_SHA384 => SecStringConstant::SecKeyAlgorithmRSASignatureDigestPSSSHA384, 363 CKM_SHA512 => SecStringConstant::SecKeyAlgorithmRSASignatureDigestPSSSHA512, 364 _ => { 365 return Err(error_here!(ErrorType::UnsupportedInput)); 366 } 367 }; 368 SECURITY_FRAMEWORK.get_sec_string_constant(algorithm_id)? 369 }; 370 return Ok(SignParams::RSA(algorithm, data)); 371 } 372 373 // Handle the case where `data` is a DigestInfo. 374 if let Ok((digest_oid, hash)) = read_digest_info(data) { 375 let algorithm = unsafe { 376 CFString::wrap_under_create_rule(match digest_oid { 377 OID_BYTES_SHA_256 => kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA256, 378 OID_BYTES_SHA_384 => kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA384, 379 OID_BYTES_SHA_512 => kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA512, 380 OID_BYTES_SHA_1 => kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA1, 381 _ => return Err(error_here!(ErrorType::UnsupportedInput)), 382 }) 383 }; 384 return Ok(SignParams::RSA(algorithm, hash)); 385 } 386 387 // Handle the case where `data` is a TLS 1.0 MD5/SHA1 hash. 388 if data.len() == 36 { 389 let algorithm = unsafe { 390 CFString::wrap_under_get_rule(kSecKeyAlgorithmRSASignatureDigestPKCS1v15Raw) 391 }; 392 return Ok(SignParams::RSA(algorithm, data)); 393 } 394 395 // Otherwise, `data` will be an emsa-pss-encoded digest that should be signed with raw RSA. 396 Ok(SignParams::RSA( 397 unsafe { CFString::wrap_under_create_rule(kSecKeyAlgorithmRSASignatureRaw) }, 398 data, 399 )) 400 } 401 402 fn get_algorithm(&self) -> SecKeyAlgorithm { 403 match self { 404 SignParams::EC(algorithm, _) => algorithm.as_concrete_TypeRef(), 405 SignParams::RSA(algorithm, _) => algorithm.as_concrete_TypeRef(), 406 } 407 } 408 409 fn get_data_to_sign(&self) -> &'a [u8] { 410 match self { 411 SignParams::EC(_, data_to_sign) => data_to_sign, 412 SignParams::RSA(_, data_to_sign) => data_to_sign, 413 } 414 } 415 } 416 417 /// Helper struct to hold onto OS-specific handles that must only be used on a particular thread. 418 struct ThreadSpecificHandles { 419 /// The only thread that these handles may be used on. 420 thread: RefPtr<nsIEventTarget>, 421 /// OS handle on a certificate and corresponding private key. 422 identity: Option<SecIdentity>, 423 /// OS handle on a private key. 424 key: Option<SecKey>, 425 } 426 427 impl ThreadSpecificHandles { 428 fn new(identity: SecIdentity, thread: &nsIEventTarget) -> ThreadSpecificHandles { 429 ThreadSpecificHandles { 430 thread: RefPtr::new(thread), 431 identity: Some(identity), 432 key: None, 433 } 434 } 435 436 fn sign( 437 &mut self, 438 key_type: KeyType, 439 maybe_modulus: Option<Vec<u8>>, 440 data: &[u8], 441 params: &Option<CK_RSA_PKCS_PSS_PARAMS>, 442 ) -> Result<Vec<u8>, Error> { 443 let Some(identity) = self.identity.take() else { 444 return Err(error_here!(ErrorType::LibraryFailure)); 445 }; 446 let mut maybe_key = self.key.take(); 447 let thread = self.thread.clone(); 448 let data = data.to_vec(); 449 let params = params.clone(); 450 let task = moz_task::spawn_onto("sign", &thread, async move { 451 let result = sign_internal(&identity, &mut maybe_key, key_type, &data, ¶ms); 452 if result.is_ok() { 453 return (result, identity, maybe_key); 454 } 455 // Some devices appear to not work well when the key handle is held for too long or if a 456 // card is inserted/removed while Firefox is running. Try refreshing the key handle. 457 let _ = maybe_key.take(); 458 let result = sign_internal(&identity, &mut maybe_key, key_type, &data, ¶ms); 459 // If this succeeded, return the result. 460 if result.is_ok() { 461 return (result, identity, maybe_key); 462 } 463 // If signing failed and this is an RSA-PSS signature, perhaps the token the key is on does 464 // not support RSA-PSS. In that case, emsa-pss-encode the data (hash, really) and try 465 // signing with raw RSA. 466 let Some(params) = params.as_ref() else { 467 return (result, identity, maybe_key); 468 }; 469 // `params` should only be `Some` if this is an RSA key. 470 let Some(modulus) = maybe_modulus.as_ref() else { 471 return ( 472 Err(error_here!(ErrorType::LibraryFailure)), 473 identity, 474 maybe_key, 475 ); 476 }; 477 let emsa_pss_encoded = 478 match emsa_pss_encode(&data, modulus_bit_length(modulus) - 1, ¶ms) { 479 Ok(emsa_pss_encoded) => emsa_pss_encoded, 480 Err(e) => return (Err(e), identity, maybe_key), 481 }; 482 ( 483 sign_internal( 484 &identity, 485 &mut maybe_key, 486 key_type, 487 &emsa_pss_encoded, 488 &None, 489 ), 490 identity, 491 maybe_key, 492 ) 493 }); 494 let (signature_result, identity, maybe_key) = futures_executor::block_on(task); 495 self.identity = Some(identity); 496 self.key = maybe_key; 497 signature_result 498 } 499 } 500 501 fn sign_internal( 502 identity: &SecIdentity, 503 maybe_key: &mut Option<SecKey>, 504 key_type: KeyType, 505 data: &[u8], 506 params: &Option<CK_RSA_PKCS_PSS_PARAMS>, 507 ) -> Result<Vec<u8>, Error> { 508 // If this key hasn't been used for signing yet, there won't be a cached key handle. Obtain 509 // and cache it if this is the case. Doing so can cause the underlying implementation to 510 // show an authentication or pin prompt to the user. Caching the handle can avoid causing 511 // multiple prompts to be displayed in some cases. 512 if maybe_key.is_none() { 513 let _ = maybe_key.replace(sec_identity_copy_private_key(identity)?); 514 } 515 let Some(key) = maybe_key.as_ref() else { 516 return Err(error_here!(ErrorType::LibraryFailure)); 517 }; 518 let sign_params = SignParams::new(key_type, data, params)?; 519 let signing_algorithm = sign_params.get_algorithm(); 520 let data_to_sign = CFData::from_buffer(sign_params.get_data_to_sign()); 521 let signature = sec_key_create_signature(key, signing_algorithm, &data_to_sign)?; 522 let signature_value = match key_type { 523 KeyType::EC(coordinate_width) => { 524 // We need to convert the DER Ecdsa-Sig-Value to the 525 // concatenation of r and s, the coordinates of the point on 526 // the curve. r and s must be 0-padded to be coordinate_width 527 // total bytes. 528 der_ec_sig_to_raw(signature.bytes(), coordinate_width)? 529 } 530 KeyType::RSA => signature.bytes().to_vec(), 531 }; 532 Ok(signature_value) 533 } 534 535 impl Drop for ThreadSpecificHandles { 536 fn drop(&mut self) { 537 // Ensure any OS handles are dropped on the appropriate thread. 538 let identity = self.identity.take(); 539 let key = self.key.take(); 540 let thread = self.thread.clone(); 541 // It is possible that we're already on the appropriate thread (e.g. if an error was 542 // encountered in `find_objects` and these handles are being released shortly after being 543 // created). 544 if moz_task::is_on_current_thread(&thread) { 545 // `key` is obtained from `identity`, so drop it first, out of an abundance of caution. 546 drop(key); 547 drop(identity); 548 } else { 549 let task = moz_task::spawn_onto("drop", &thread, async move { 550 drop(key); 551 drop(identity); 552 }); 553 futures_executor::block_on(task) 554 } 555 } 556 } 557 558 pub struct Key { 559 handles: ThreadSpecificHandles, 560 cryptoki_key: CryptokiKey, 561 } 562 563 impl Key { 564 fn new(identity: &SecIdentity, thread: &nsIEventTarget) -> Result<Key, Error> { 565 let certificate = sec_identity_copy_certificate(identity)?; 566 let der = sec_certificate_copy_data(&certificate)?; 567 let key = SECURITY_FRAMEWORK.sec_certificate_copy_key(&certificate)?; 568 let key_type: CFString = get_key_attribute(&key, unsafe { kSecAttrKeyType })?; 569 let key_size_in_bits: CFNumber = get_key_attribute(&key, unsafe { kSecAttrKeySizeInBits })?; 570 let sec_attr_key_type_ec = 571 unsafe { CFString::wrap_under_create_rule(kSecAttrKeyTypeECSECPrimeRandom) }; 572 let (modulus, ec_params) = 573 if key_type.as_concrete_TypeRef() == unsafe { kSecAttrKeyTypeRSA } { 574 let public_key = sec_key_copy_external_representation(&key)?; 575 let modulus = read_rsa_modulus(public_key.bytes())?; 576 (Some(modulus), None) 577 } else if key_type == sec_attr_key_type_ec { 578 // Assume all EC keys are secp256r1, secp384r1, or secp521r1. This 579 // is wrong, but the API doesn't seem to give us a way to determine 580 // which curve this key is on. 581 // This might not matter in practice, because it seems all NSS uses 582 // this for is to get the signature size. 583 let key_size_in_bits = match key_size_in_bits.to_i64() { 584 Some(value) => value, 585 None => return Err(error_here!(ErrorType::ValueTooLarge)), 586 }; 587 let ec_params = match key_size_in_bits { 588 256 => ENCODED_OID_BYTES_SECP256R1.to_vec(), 589 384 => ENCODED_OID_BYTES_SECP384R1.to_vec(), 590 521 => ENCODED_OID_BYTES_SECP521R1.to_vec(), 591 _ => return Err(error_here!(ErrorType::UnsupportedInput)), 592 }; 593 (None, Some(ec_params)) 594 } else { 595 return Err(error_here!(ErrorType::LibraryFailure)); 596 }; 597 598 Ok(Key { 599 handles: ThreadSpecificHandles::new(identity.clone(), thread), 600 cryptoki_key: CryptokiKey::new(modulus, ec_params, der.bytes())?, 601 }) 602 } 603 } 604 605 impl CryptokiObject for Key { 606 fn matches(&self, attrs: &[(CK_ATTRIBUTE_TYPE, Vec<u8>)]) -> bool { 607 self.cryptoki_key.matches(attrs) 608 } 609 610 fn get_attribute(&self, attribute: CK_ATTRIBUTE_TYPE) -> Option<&[u8]> { 611 self.cryptoki_key.get_attribute(attribute) 612 } 613 } 614 615 impl Sign for Key { 616 fn get_signature_length( 617 &mut self, 618 data: &[u8], 619 params: &Option<CK_RSA_PKCS_PSS_PARAMS>, 620 ) -> Result<usize, Error> { 621 // Unfortunately we don't have a way of getting the length of a signature without creating 622 // one. 623 let dummy_signature_bytes = self.sign(data, params)?; 624 Ok(dummy_signature_bytes.len()) 625 } 626 627 // The input data is a hash. What algorithm we use depends on the size of the hash. 628 fn sign( 629 &mut self, 630 data: &[u8], 631 params: &Option<CK_RSA_PKCS_PSS_PARAMS>, 632 ) -> Result<Vec<u8>, Error> { 633 self.handles.sign( 634 self.cryptoki_key.key_type(), 635 self.cryptoki_key.modulus().clone(), 636 data, 637 params, 638 ) 639 } 640 } 641 642 fn get_key_attribute<T: TCFType + Clone>(key: &SecKey, attr: CFStringRef) -> Result<T, Error> { 643 let attributes: CFDictionary<CFString, T> = sec_key_copy_attributes(key)?; 644 match attributes.find(attr as *const _) { 645 Some(value) => Ok((*value).clone()), 646 None => Err(error_here!(ErrorType::ExternalError)), 647 } 648 } 649 650 // Given a SecIdentity, attempts to build as much of a path to a trust anchor as possible, gathers 651 // the CA certificates from that path, and returns them. The purpose of this function is not to 652 // validate the given certificate but to find CA certificates that gecko may need to do path 653 // building when filtering client certificates according to the acceptable CA list sent by the 654 // server during client authentication. 655 fn get_issuers(identity: &SecIdentity) -> Result<Vec<SecCertificate>, Error> { 656 let certificate = sec_identity_copy_certificate(identity)?; 657 let policy = unsafe { SecPolicyCreateSSL(false, std::ptr::null()) }; 658 if policy.is_null() { 659 return Err(error_here!(ErrorType::ExternalError)); 660 } 661 let policy = unsafe { SecPolicy::wrap_under_create_rule(policy) }; 662 let mut trust = std::ptr::null(); 663 // Each of SecTrustCreateWithCertificates' input arguments can be either single items or an 664 // array of items. Since we only want to specify one of each, we directly specify the arguments. 665 let status = unsafe { 666 SecTrustCreateWithCertificates( 667 certificate.as_concrete_TypeRef(), 668 policy.as_concrete_TypeRef(), 669 &mut trust, 670 ) 671 }; 672 if status != errSecSuccess { 673 return Err(error_here!(ErrorType::ExternalError)); 674 } 675 if trust.is_null() { 676 return Err(error_here!(ErrorType::ExternalError)); 677 } 678 let trust = unsafe { SecTrust::wrap_under_create_rule(trust) }; 679 // Disable AIA fetching so that SecTrustEvaluateWithError doesn't result in network I/O. 680 let status = unsafe { SecTrustSetNetworkFetchAllowed(trust.as_concrete_TypeRef(), 0) }; 681 if status != errSecSuccess { 682 return Err(error_here!(ErrorType::ExternalError)); 683 } 684 // We ignore the return value here because we don't care if the certificate is trusted or not - 685 // we're only doing this to build its issuer chain as much as possible. 686 let _ = SECURITY_FRAMEWORK.sec_trust_evaluate_with_error(&trust)?; 687 let certificate_count = unsafe { SecTrustGetCertificateCount(trust.as_concrete_TypeRef()) }; 688 let mut certificates = Vec::with_capacity( 689 certificate_count 690 .try_into() 691 .map_err(|_| error_here!(ErrorType::ValueTooLarge))?, 692 ); 693 for i in 1..certificate_count { 694 let certificate = unsafe { SecTrustGetCertificateAtIndex(trust.as_concrete_TypeRef(), i) }; 695 if certificate.is_null() { 696 error!("SecTrustGetCertificateAtIndex returned null certificate?"); 697 continue; 698 } 699 let certificate = unsafe { SecCertificate::wrap_under_get_rule(certificate) }; 700 certificates.push(certificate); 701 } 702 Ok(certificates) 703 } 704 705 pub struct Backend { 706 /// A background thread that all OS API calls will be done on. This is to prevent issues with 707 /// modules or implementations using thread-local state. 708 thread: RefPtr<nsIEventTarget>, 709 /// The last time a call to `find_objects` finished, to avoid searching for objects more than 710 /// once every 3 seconds. 711 last_scan_finished: Option<Instant>, 712 } 713 714 impl Backend { 715 pub fn new() -> Result<Backend, Error> { 716 let thread = moz_task::create_thread("osclientcerts").map_err(|nsresult| { 717 error_here!(ErrorType::LibraryFailure, nsresult.error_name().to_string()) 718 })?; 719 Ok(Backend { 720 thread: thread 721 .query_interface::<nsIEventTarget>() 722 .ok_or(error_here!(ErrorType::LibraryFailure))?, 723 last_scan_finished: None, 724 }) 725 } 726 } 727 728 const SLOT_DESCRIPTION_BYTES: &[u8; 64] = 729 b"OS Client Cert Slot "; 730 const TOKEN_LABEL_BYTES: &[u8; 32] = b"OS Client Cert Token "; 731 const TOKEN_MODEL_BYTES: &[u8; 16] = b"osclientcerts "; 732 const TOKEN_SERIAL_NUMBER_BYTES: &[u8; 16] = b"0000000000000000"; 733 734 impl ClientCertsBackend for Backend { 735 type Key = Key; 736 737 fn find_objects(&mut self) -> Result<(Vec<CryptokiCert>, Vec<Key>), Error> { 738 match self.last_scan_finished { 739 Some(last_scan_finished) => { 740 if Instant::now().duration_since(last_scan_finished) < Duration::new(3, 0) { 741 return Ok((Vec::new(), Vec::new())); 742 } 743 } 744 None => {} 745 } 746 747 let thread = self.thread.clone(); 748 let task = moz_task::spawn_onto("find_objects", &self.thread, async move { 749 find_objects(&thread) 750 }); 751 let result = futures_executor::block_on(task); 752 self.last_scan_finished = Some(Instant::now()); 753 result 754 } 755 756 fn get_slot_info(&self) -> CK_SLOT_INFO { 757 CK_SLOT_INFO { 758 slotDescription: *SLOT_DESCRIPTION_BYTES, 759 manufacturerID: *crate::MANUFACTURER_ID_BYTES, 760 flags: CKF_TOKEN_PRESENT, 761 ..Default::default() 762 } 763 } 764 765 fn get_token_info(&self) -> CK_TOKEN_INFO { 766 CK_TOKEN_INFO { 767 label: *TOKEN_LABEL_BYTES, 768 manufacturerID: *crate::MANUFACTURER_ID_BYTES, 769 model: *TOKEN_MODEL_BYTES, 770 serialNumber: *TOKEN_SERIAL_NUMBER_BYTES, 771 ..Default::default() 772 } 773 } 774 775 fn get_mechanism_list(&self) -> Vec<CK_MECHANISM_TYPE> { 776 vec![CKM_ECDSA, CKM_RSA_PKCS, CKM_RSA_PKCS_PSS] 777 } 778 } 779 780 fn find_objects(thread: &nsIEventTarget) -> Result<(Vec<CryptokiCert>, Vec<Key>), Error> { 781 let mut certs = Vec::new(); 782 let mut keys = Vec::new(); 783 let identities = unsafe { 784 let class_key = CFString::wrap_under_get_rule(kSecClass); 785 let class_value = CFString::wrap_under_get_rule(kSecClassIdentity); 786 let return_ref_key = CFString::wrap_under_get_rule(kSecReturnRef); 787 let return_ref_value = CFBoolean::wrap_under_get_rule(kCFBooleanTrue); 788 let match_key = CFString::wrap_under_get_rule(kSecMatchLimit); 789 let match_value = CFString::wrap_under_get_rule(kSecMatchLimitAll); 790 let vals = vec![ 791 (class_key.as_CFType(), class_value.as_CFType()), 792 (return_ref_key.as_CFType(), return_ref_value.as_CFType()), 793 (match_key.as_CFType(), match_value.as_CFType()), 794 ]; 795 let dict = CFDictionary::from_CFType_pairs(&vals); 796 let mut result = std::ptr::null(); 797 let status = SecItemCopyMatching(dict.as_CFTypeRef() as CFDictionaryRef, &mut result); 798 if status == errSecItemNotFound { 799 return Ok((certs, keys)); 800 } 801 if status != errSecSuccess { 802 return Err(error_here!(ErrorType::ExternalError, status.to_string())); 803 } 804 if result.is_null() { 805 return Err(error_here!(ErrorType::ExternalError)); 806 } 807 CFArray::<SecIdentityRef>::wrap_under_create_rule(result as CFArrayRef) 808 }; 809 for identity in identities.get_all_values().iter() { 810 let identity = unsafe { SecIdentity::wrap_under_get_rule(*identity as SecIdentityRef) }; 811 let cert = new_cert_from_identity(&identity); 812 let key = Key::new(&identity, thread); 813 if let (Ok(cert), Ok(key)) = (cert, key) { 814 certs.push(cert); 815 keys.push(key); 816 } else { 817 continue; 818 } 819 if let Ok(issuers) = get_issuers(&identity) { 820 for issuer in issuers { 821 if let Ok(cert) = new_cert_from_certificate(&issuer) { 822 certs.push(cert); 823 } 824 } 825 } 826 } 827 Ok((certs, keys)) 828 }