tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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, &params);
    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, &params);
    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, &params) {
    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 }