/* -*- Mode: rust; rust-indent-offset: 4 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #![allow(non_camel_case_types)] use pkcs11_bindings::*; use rsclientcerts::error::{Error, ErrorType}; use rsclientcerts::manager::{ClientCertsBackend, CryptokiObject, Sign, SlotType}; use rsclientcerts::util::*; use sha2::{Digest, Sha256}; use std::convert::TryInto; use std::ffi::{c_void, CStr, CString}; use std::ops::Deref; use std::slice; use winapi::shared::bcrypt::*; use winapi::shared::minwindef::{DWORD, PBYTE}; use winapi::um::errhandlingapi::GetLastError; use winapi::um::ncrypt::*; use winapi::um::wincrypt::{HCRYPTHASH, HCRYPTPROV, *}; // winapi has some support for ncrypt.h, but not for this function. extern "system" { fn NCryptSignHash( hKey: NCRYPT_KEY_HANDLE, pPaddingInfo: *mut c_void, pbHashValue: PBYTE, cbHashValue: DWORD, pbSignature: PBYTE, cbSignature: DWORD, pcbResult: *mut DWORD, dwFlags: DWORD, ) -> SECURITY_STATUS; } /// Given a `CERT_INFO`, tries to return the bytes of the subject distinguished name as formatted by /// `CertNameToStrA` using the flag `CERT_SIMPLE_NAME_STR`. This is used as the label for the /// certificate. fn get_cert_subject_dn(cert_info: &CERT_INFO) -> Result, Error> { let mut cert_info_subject = cert_info.Subject; let subject_dn_len = unsafe { CertNameToStrA( X509_ASN_ENCODING, &mut cert_info_subject, CERT_SIMPLE_NAME_STR, std::ptr::null_mut(), 0, ) }; // subject_dn_len includes the terminating null byte. let mut subject_dn_string_bytes: Vec = vec![0; subject_dn_len as usize]; let subject_dn_len = unsafe { CertNameToStrA( X509_ASN_ENCODING, &mut cert_info_subject, CERT_SIMPLE_NAME_STR, subject_dn_string_bytes.as_mut_ptr() as *mut i8, subject_dn_string_bytes .len() .try_into() .map_err(|_| error_here!(ErrorType::ValueTooLarge))?, ) }; if subject_dn_len as usize != subject_dn_string_bytes.len() { return Err(error_here!(ErrorType::ExternalError)); } Ok(subject_dn_string_bytes) } /// Represents a certificate for which there exists a corresponding private key. pub struct Cert { /// PKCS #11 object class. Will be `CKO_CERTIFICATE`. class: Vec, /// Whether or not this is on a token. Will be `CK_TRUE`. token: Vec, /// An identifier unique to this certificate. Must be the same as the ID for the private key. id: Vec, /// The bytes of a human-readable label for this certificate. Will be the subject DN. label: Vec, /// The DER bytes of the certificate. value: Vec, /// The DER bytes of the issuer distinguished name of the certificate. issuer: Vec, /// The DER bytes of the serial number of the certificate. serial_number: Vec, /// The DER bytes of the subject distinguished name of the certificate. subject: Vec, /// Which slot this certificate should be exposed on. slot_type: SlotType, } impl Cert { fn new(cert_context: PCCERT_CONTEXT) -> Result { let cert = unsafe { &*cert_context }; let cert_info = unsafe { &*cert.pCertInfo }; let value = unsafe { slice::from_raw_parts(cert.pbCertEncoded, cert.cbCertEncoded as usize) }; let value = value.to_vec(); let id = Sha256::digest(&value).to_vec(); let label = get_cert_subject_dn(cert_info)?; let (serial_number, issuer, subject) = read_encoded_certificate_identifiers(&value)?; Ok(Cert { class: serialize_uint(CKO_CERTIFICATE)?, token: serialize_uint(CK_TRUE)?, id, label, value, issuer, serial_number, subject, slot_type: SlotType::Modern, }) } fn class(&self) -> &[u8] { &self.class } fn token(&self) -> &[u8] { &self.token } fn id(&self) -> &[u8] { &self.id } fn label(&self) -> &[u8] { &self.label } fn value(&self) -> &[u8] { &self.value } fn issuer(&self) -> &[u8] { &self.issuer } fn serial_number(&self) -> &[u8] { &self.serial_number } fn subject(&self) -> &[u8] { &self.subject } } impl CryptokiObject for Cert { fn matches(&self, slot_type: SlotType, attrs: &[(CK_ATTRIBUTE_TYPE, Vec)]) -> bool { if slot_type != self.slot_type { return false; } for (attr_type, attr_value) in attrs { let comparison = match *attr_type { CKA_CLASS => self.class(), CKA_TOKEN => self.token(), CKA_LABEL => self.label(), CKA_ID => self.id(), CKA_VALUE => self.value(), CKA_ISSUER => self.issuer(), CKA_SERIAL_NUMBER => self.serial_number(), CKA_SUBJECT => self.subject(), _ => return false, }; if attr_value.as_slice() != comparison { return false; } } true } fn get_attribute(&self, attribute: CK_ATTRIBUTE_TYPE) -> Option<&[u8]> { let result = match attribute { CKA_CLASS => self.class(), CKA_TOKEN => self.token(), CKA_LABEL => self.label(), CKA_ID => self.id(), CKA_VALUE => self.value(), CKA_ISSUER => self.issuer(), CKA_SERIAL_NUMBER => self.serial_number(), CKA_SUBJECT => self.subject(), _ => return None, }; Some(result) } } struct CertContext(PCCERT_CONTEXT); impl CertContext { fn new(cert: PCCERT_CONTEXT) -> CertContext { CertContext(unsafe { CertDuplicateCertificateContext(cert) }) } } impl Drop for CertContext { fn drop(&mut self) { unsafe { CertFreeCertificateContext(self.0); } } } impl Deref for CertContext { type Target = PCCERT_CONTEXT; fn deref(&self) -> &Self::Target { &self.0 } } enum KeyHandle { NCrypt(NCRYPT_KEY_HANDLE), CryptoAPI(HCRYPTPROV, DWORD), } impl KeyHandle { fn from_cert(cert: &CertContext) -> Result { let mut key_handle = 0; let mut key_spec = 0; let mut must_free = 0; unsafe { if CryptAcquireCertificatePrivateKey( **cert, CRYPT_ACQUIRE_PREFER_NCRYPT_KEY_FLAG, std::ptr::null_mut(), &mut key_handle, &mut key_spec, &mut must_free, ) != 1 { return Err(error_here!( ErrorType::ExternalError, GetLastError().to_string() )); } } if must_free == 0 { return Err(error_here!(ErrorType::ExternalError)); } if key_spec == CERT_NCRYPT_KEY_SPEC { Ok(KeyHandle::NCrypt(key_handle as NCRYPT_KEY_HANDLE)) } else { Ok(KeyHandle::CryptoAPI(key_handle as HCRYPTPROV, key_spec)) } } fn sign( &self, data: &[u8], params: &Option, do_signature: bool, key_type: KeyType, ) -> Result, Error> { match &self { KeyHandle::NCrypt(ncrypt_handle) => { sign_ncrypt(ncrypt_handle, data, params, do_signature, key_type) } KeyHandle::CryptoAPI(hcryptprov, key_spec) => { sign_cryptoapi(hcryptprov, key_spec, data, params, do_signature) } } } } impl Drop for KeyHandle { fn drop(&mut self) { match self { KeyHandle::NCrypt(ncrypt_handle) => unsafe { let _ = NCryptFreeObject(*ncrypt_handle); }, KeyHandle::CryptoAPI(hcryptprov, _) => unsafe { let _ = CryptReleaseContext(*hcryptprov, 0); }, } } } fn sign_ncrypt( ncrypt_handle: &NCRYPT_KEY_HANDLE, data: &[u8], params: &Option, do_signature: bool, key_type: KeyType, ) -> Result, Error> { let mut sign_params = SignParams::new(key_type, params)?; let params_ptr = sign_params.params_ptr(); let flags = sign_params.flags(); let mut data = data.to_vec(); let mut signature_len = 0; // We call NCryptSignHash twice: the first time to get the size of the buffer we need to // allocate and then again to actually sign the data, if `do_signature` is `true`. let status = unsafe { NCryptSignHash( *ncrypt_handle, params_ptr, data.as_mut_ptr(), data.len() .try_into() .map_err(|_| error_here!(ErrorType::ValueTooLarge))?, std::ptr::null_mut(), 0, &mut signature_len, flags, ) }; // 0 is "ERROR_SUCCESS" (but "ERROR_SUCCESS" is unsigned, whereas SECURITY_STATUS is signed) if status != 0 { return Err(error_here!(ErrorType::ExternalError, status.to_string())); } let mut signature = vec![0; signature_len as usize]; if !do_signature { return Ok(signature); } let mut final_signature_len = signature_len; let status = unsafe { NCryptSignHash( *ncrypt_handle, params_ptr, data.as_mut_ptr(), data.len() .try_into() .map_err(|_| error_here!(ErrorType::ValueTooLarge))?, signature.as_mut_ptr(), signature_len, &mut final_signature_len, flags, ) }; if status != 0 { return Err(error_here!(ErrorType::ExternalError, status.to_string())); } if final_signature_len != signature_len { return Err(error_here!(ErrorType::ExternalError)); } Ok(signature) } fn sign_cryptoapi( hcryptprov: &HCRYPTPROV, key_spec: &DWORD, data: &[u8], params: &Option, do_signature: bool, ) -> Result, Error> { if params.is_some() { return Err(error_here!(ErrorType::LibraryFailure)); } // data will be an encoded DigestInfo, which specifies the hash algorithm and bytes of the hash // to sign. However, CryptoAPI requires directly specifying the bytes of the hash, so it must // be extracted first. let (_, hash_bytes) = read_digest_info(data)?; let hash = HCryptHash::new(hcryptprov, hash_bytes)?; let mut signature_len = 0; if unsafe { CryptSignHashW( *hash, *key_spec, std::ptr::null_mut(), 0, std::ptr::null_mut(), &mut signature_len, ) } != 1 { return Err(error_here!( ErrorType::ExternalError, unsafe { GetLastError() }.to_string() )); } let mut signature = vec![0; signature_len as usize]; if !do_signature { return Ok(signature); } let mut final_signature_len = signature_len; if unsafe { CryptSignHashW( *hash, *key_spec, std::ptr::null_mut(), 0, signature.as_mut_ptr(), &mut final_signature_len, ) } != 1 { return Err(error_here!( ErrorType::ExternalError, unsafe { GetLastError() }.to_string() )); } if final_signature_len != signature_len { return Err(error_here!(ErrorType::ExternalError)); } // CryptoAPI returns the signature with the most significant byte last (little-endian), // whereas PKCS#11 expects the most significant byte first (big-endian). signature.reverse(); Ok(signature) } struct HCryptHash(HCRYPTHASH); impl HCryptHash { fn new(hcryptprov: &HCRYPTPROV, hash_bytes: &[u8]) -> Result { let alg = match hash_bytes.len() { 20 => CALG_SHA1, 32 => CALG_SHA_256, 48 => CALG_SHA_384, 64 => CALG_SHA_512, _ => { return Err(error_here!(ErrorType::UnsupportedInput)); } }; let mut hash: HCRYPTHASH = 0; if unsafe { CryptCreateHash(*hcryptprov, alg, 0, 0, &mut hash) } != 1 { return Err(error_here!( ErrorType::ExternalError, unsafe { GetLastError() }.to_string() )); } if unsafe { CryptSetHashParam(hash, HP_HASHVAL, hash_bytes.as_ptr(), 0) } != 1 { return Err(error_here!( ErrorType::ExternalError, unsafe { GetLastError() }.to_string() )); } Ok(HCryptHash(hash)) } } impl Drop for HCryptHash { fn drop(&mut self) { unsafe { CryptDestroyHash(self.0); } } } impl Deref for HCryptHash { type Target = HCRYPTHASH; fn deref(&self) -> &Self::Target { &self.0 } } // In some cases, the ncrypt API takes a pointer to a null-terminated wide-character string as a way // of specifying an algorithm. The "right" way to do this would be to take the corresponding // &'static str constant provided by the winapi crate, create an OsString from it, encode it as wide // characters, and collect it into a Vec. However, since the implementation that provides this // functionality isn't constant, we would have to manage the memory this creates and uses. Since // rust structures generally can't be self-referrential, this memory would have to live elsewhere, // and the nice abstractions we've created for this implementation start to break down. It's much // simpler to hard-code the identifiers we support, since there are only four of them. // The following arrays represent the identifiers "SHA1", "SHA256", "SHA384", and "SHA512", // respectively. const SHA1_ALGORITHM_STRING: &[u16] = &[83, 72, 65, 49, 0]; const SHA256_ALGORITHM_STRING: &[u16] = &[83, 72, 65, 50, 53, 54, 0]; const SHA384_ALGORITHM_STRING: &[u16] = &[83, 72, 65, 51, 56, 52, 0]; const SHA512_ALGORITHM_STRING: &[u16] = &[83, 72, 65, 53, 49, 50, 0]; enum SignParams { EC, RSA_PKCS1(BCRYPT_PKCS1_PADDING_INFO), RSA_PSS(BCRYPT_PSS_PADDING_INFO), } impl SignParams { fn new( key_type: KeyType, params: &Option, ) -> Result { // EC is easy, so handle that first. match key_type { KeyType::EC => return Ok(SignParams::EC), KeyType::RSA => {} } // If `params` is `Some`, we're doing RSA-PSS. If it is `None`, we're doing RSA-PKCS1. let pss_params = match params { Some(pss_params) => pss_params, None => { // The hash algorithm should be encoded in the data to be signed, so we don't have to // (and don't want to) specify a particular algorithm here. return Ok(SignParams::RSA_PKCS1(BCRYPT_PKCS1_PADDING_INFO { pszAlgId: std::ptr::null(), })); } }; let algorithm_string = match pss_params.hashAlg { CKM_SHA_1 => SHA1_ALGORITHM_STRING, CKM_SHA256 => SHA256_ALGORITHM_STRING, CKM_SHA384 => SHA384_ALGORITHM_STRING, CKM_SHA512 => SHA512_ALGORITHM_STRING, _ => { return Err(error_here!(ErrorType::UnsupportedInput)); } }; Ok(SignParams::RSA_PSS(BCRYPT_PSS_PADDING_INFO { pszAlgId: algorithm_string.as_ptr(), cbSalt: pss_params.sLen, })) } fn params_ptr(&mut self) -> *mut std::ffi::c_void { match self { SignParams::EC => std::ptr::null_mut(), SignParams::RSA_PKCS1(params) => { params as *mut BCRYPT_PKCS1_PADDING_INFO as *mut std::ffi::c_void } SignParams::RSA_PSS(params) => { params as *mut BCRYPT_PSS_PADDING_INFO as *mut std::ffi::c_void } } } fn flags(&self) -> u32 { match *self { SignParams::EC => 0, SignParams::RSA_PKCS1(_) => NCRYPT_PAD_PKCS1_FLAG, SignParams::RSA_PSS(_) => NCRYPT_PAD_PSS_FLAG, } } } /// A helper enum to identify a private key's type. We support EC and RSA. #[allow(clippy::upper_case_acronyms)] #[derive(Clone, Copy, Debug)] pub enum KeyType { EC, RSA, } /// Represents a private key for which there exists a corresponding certificate. pub struct Key { /// A handle on the OS mechanism that represents the certificate for this key. cert: CertContext, /// PKCS #11 object class. Will be `CKO_PRIVATE_KEY`. class: Vec, /// Whether or not this is on a token. Will be `CK_TRUE`. token: Vec, /// An identifier unique to this key. Must be the same as the ID for the certificate. id: Vec, /// Whether or not this key is "private" (can it be exported?). Will be CK_TRUE (it can't be /// exported). private: Vec, /// PKCS #11 key type. Will be `CKK_EC` for EC, and `CKK_RSA` for RSA. key_type: Vec, /// If this is an RSA key, this is the value of the modulus as an unsigned integer. modulus: Option>, /// If this is an EC key, this is the DER bytes of the OID identifying the curve the key is on. ec_params: Option>, /// An enum identifying this key's type. key_type_enum: KeyType, /// Which slot this key should be exposed on. slot_type: SlotType, /// A handle on the OS mechanism that represents this key. key_handle: Option, } impl Key { fn new(cert_context: PCCERT_CONTEXT) -> Result { let cert = unsafe { *cert_context }; let cert_der = unsafe { slice::from_raw_parts(cert.pbCertEncoded, cert.cbCertEncoded as usize) }; let id = Sha256::digest(cert_der).to_vec(); let id = id.to_vec(); let cert_info = unsafe { &*cert.pCertInfo }; let mut modulus = None; let mut ec_params = None; let spki = &cert_info.SubjectPublicKeyInfo; let algorithm_oid = unsafe { CStr::from_ptr(spki.Algorithm.pszObjId) } .to_str() .map_err(|_| error_here!(ErrorType::ExternalError))?; let (key_type_enum, key_type_attribute) = if algorithm_oid == szOID_RSA_RSA { if spki.PublicKey.cUnusedBits != 0 { return Err(error_here!(ErrorType::ExternalError)); } let public_key_bytes = unsafe { std::slice::from_raw_parts(spki.PublicKey.pbData, spki.PublicKey.cbData as usize) }; let modulus_value = read_rsa_modulus(public_key_bytes)?; modulus = Some(modulus_value); (KeyType::RSA, CKK_RSA) } else if algorithm_oid == szOID_ECC_PUBLIC_KEY { let params = &spki.Algorithm.Parameters; ec_params = Some( unsafe { std::slice::from_raw_parts(params.pbData, params.cbData as usize) } .to_vec(), ); (KeyType::EC, CKK_EC) } else { return Err(error_here!(ErrorType::LibraryFailure)); }; let cert = CertContext::new(cert_context); Ok(Key { cert, class: serialize_uint(CKO_PRIVATE_KEY)?, token: serialize_uint(CK_TRUE)?, id, private: serialize_uint(CK_TRUE)?, key_type: serialize_uint(key_type_attribute)?, modulus, ec_params, key_type_enum, slot_type: SlotType::Modern, key_handle: None, }) } fn class(&self) -> &[u8] { &self.class } fn token(&self) -> &[u8] { &self.token } fn id(&self) -> &[u8] { &self.id } fn private(&self) -> &[u8] { &self.private } fn key_type(&self) -> &[u8] { &self.key_type } fn modulus(&self) -> Option<&[u8]> { match &self.modulus { Some(modulus) => Some(modulus.as_slice()), None => None, } } fn ec_params(&self) -> Option<&[u8]> { match &self.ec_params { Some(ec_params) => Some(ec_params.as_slice()), None => None, } } fn sign_with_retry( &mut self, data: &[u8], params: &Option, do_signature: bool, ) -> Result, Error> { let result = self.sign_internal(data, params, do_signature); if result.is_ok() { return result; } // Some devices appear to not work well when the key handle is held for too long or if a // card is inserted/removed while Firefox is running. Try refreshing the key handle. debug!("sign failed: refreshing key handle"); let _ = self.key_handle.take(); self.sign_internal(data, params, do_signature) } /// data: the data to sign /// do_signature: if true, actually perform the signature. Otherwise, return a `Vec` of the /// length the signature would be, if performed. fn sign_internal( &mut self, data: &[u8], params: &Option, do_signature: bool, ) -> Result, Error> { // If this key hasn't been used for signing yet, there won't be a cached key handle. Obtain // and cache it if this is the case. Doing so can cause the underlying implementation to // show an authentication or pin prompt to the user. Caching the handle can avoid causing // multiple prompts to be displayed in some cases. if self.key_handle.is_none() { let _ = self.key_handle.replace(KeyHandle::from_cert(&self.cert)?); } let key = match &self.key_handle { Some(key) => key, None => return Err(error_here!(ErrorType::LibraryFailure)), }; key.sign(data, params, do_signature, self.key_type_enum) } } impl CryptokiObject for Key { fn matches(&self, slot_type: SlotType, attrs: &[(CK_ATTRIBUTE_TYPE, Vec)]) -> bool { if slot_type != self.slot_type { return false; } for (attr_type, attr_value) in attrs { let comparison = match *attr_type { CKA_CLASS => self.class(), CKA_TOKEN => self.token(), CKA_ID => self.id(), CKA_PRIVATE => self.private(), CKA_KEY_TYPE => self.key_type(), CKA_MODULUS => { if let Some(modulus) = self.modulus() { modulus } else { return false; } } CKA_EC_PARAMS => { if let Some(ec_params) = self.ec_params() { ec_params } else { return false; } } _ => return false, }; if attr_value.as_slice() != comparison { return false; } } true } fn get_attribute(&self, attribute: CK_ATTRIBUTE_TYPE) -> Option<&[u8]> { match attribute { CKA_CLASS => Some(self.class()), CKA_TOKEN => Some(self.token()), CKA_ID => Some(self.id()), CKA_PRIVATE => Some(self.private()), CKA_KEY_TYPE => Some(self.key_type()), CKA_MODULUS => self.modulus(), CKA_EC_PARAMS => self.ec_params(), _ => None, } } } impl Sign for Key { fn get_signature_length( &mut self, data: &[u8], params: &Option, ) -> Result { match self.sign_with_retry(data, params, false) { Ok(dummy_signature_bytes) => Ok(dummy_signature_bytes.len()), Err(e) => Err(e), } } fn sign( &mut self, data: &[u8], params: &Option, ) -> Result, Error> { self.sign_with_retry(data, params, true) } } struct CertStore { handle: HCERTSTORE, } impl Drop for CertStore { fn drop(&mut self) { if !self.handle.is_null() { unsafe { CertCloseStore(self.handle, 0); } } } } impl Deref for CertStore { type Target = HCERTSTORE; fn deref(&self) -> &Self::Target { &self.handle } } impl CertStore { fn new(handle: HCERTSTORE) -> CertStore { CertStore { handle } } } // Given a pointer to a CERT_CHAIN_CONTEXT, enumerates each chain in the context and each element // in each chain to gather every CERT_CONTEXT pointed to by the CERT_CHAIN_CONTEXT. // https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-cert_chain_context says // that the 0th element of the 0th chain will be the end-entity certificate. This certificate (if // present), will be the 0th element of the returned Vec. fn gather_cert_contexts(cert_chain_context: *const CERT_CHAIN_CONTEXT) -> Vec<*const CERT_CONTEXT> { let mut cert_contexts = Vec::new(); if cert_chain_context.is_null() { return cert_contexts; } let cert_chain_context = unsafe { &*cert_chain_context }; let cert_chains = unsafe { std::slice::from_raw_parts( cert_chain_context.rgpChain, cert_chain_context.cChain as usize, ) }; for cert_chain in cert_chains { // First dereference the borrow. let cert_chain = *cert_chain; if cert_chain.is_null() { continue; } // Then dereference the pointer. let cert_chain = unsafe { &*cert_chain }; let chain_elements = unsafe { std::slice::from_raw_parts(cert_chain.rgpElement, cert_chain.cElement as usize) }; for chain_element in chain_elements { let chain_element = *chain_element; // dereference borrow if chain_element.is_null() { continue; } let chain_element = unsafe { &*chain_element }; // dereference pointer cert_contexts.push(chain_element.pCertContext); } } cert_contexts } pub struct Backend {} impl ClientCertsBackend for Backend { type Cert = Cert; type Key = Key; /// Attempts to enumerate certificates with private keys exposed by the OS. Currently only looks in /// the "My" cert store of the current user. In the future this may look in more locations. fn find_objects(&self) -> Result<(Vec, Vec), Error> { let mut certs = Vec::new(); let mut keys = Vec::new(); let location_flags = CERT_SYSTEM_STORE_CURRENT_USER | CERT_STORE_OPEN_EXISTING_FLAG | CERT_STORE_READONLY_FLAG; let store_name = match CString::new("My") { Ok(store_name) => store_name, Err(_) => return Err(error_here!(ErrorType::LibraryFailure)), }; let store = CertStore::new(unsafe { CertOpenStore( CERT_STORE_PROV_SYSTEM_REGISTRY_A, 0, 0, location_flags, store_name.as_ptr() as *const winapi::ctypes::c_void, ) }); if store.is_null() { return Err(error_here!(ErrorType::ExternalError)); } let find_params = CERT_CHAIN_FIND_ISSUER_PARA { cbSize: std::mem::size_of::() as u32, pszUsageIdentifier: std::ptr::null(), dwKeySpec: 0, dwAcquirePrivateKeyFlags: 0, cIssuer: 0, rgIssuer: std::ptr::null_mut(), pfnFindCallback: None, pvFindArg: std::ptr::null_mut(), pdwIssuerChainIndex: std::ptr::null_mut(), pdwIssuerElementIndex: std::ptr::null_mut(), }; let mut cert_chain_context: PCCERT_CHAIN_CONTEXT = std::ptr::null_mut(); loop { // CertFindChainInStore finds all certificates with private keys in the store. It also // attempts to build a verified certificate chain to a trust anchor for each certificate. // We gather and hold onto these extra certificates so that gecko can use them when // filtering potential client certificates according to the acceptable CAs list sent by // servers when they request client certificates. cert_chain_context = unsafe { CertFindChainInStore( *store, X509_ASN_ENCODING, CERT_CHAIN_FIND_BY_ISSUER_CACHE_ONLY_FLAG | CERT_CHAIN_FIND_BY_ISSUER_CACHE_ONLY_URL_FLAG, CERT_CHAIN_FIND_BY_ISSUER, &find_params as *const CERT_CHAIN_FIND_ISSUER_PARA as *const winapi::ctypes::c_void, cert_chain_context, ) }; if cert_chain_context.is_null() { break; } let cert_contexts = gather_cert_contexts(cert_chain_context); // The 0th CERT_CONTEXT is the end-entity (i.e. the certificate with the private key we're // after). match cert_contexts.get(0) { Some(cert_context) => { let key = match Key::new(*cert_context) { Ok(key) => key, Err(_) => continue, }; let cert = match Cert::new(*cert_context) { Ok(cert) => cert, Err(_) => continue, }; certs.push(cert); keys.push(key); } None => {} }; for cert_context in cert_contexts.iter().skip(1) { if let Ok(cert) = Cert::new(*cert_context) { certs.push(cert); } } } Ok((certs, keys)) } }