backend_windows.rs (29400B)
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_camel_case_types)] 7 8 use pkcs11_bindings::*; 9 use rsclientcerts::cryptoki::*; 10 use rsclientcerts::manager::{ClientCertsBackend, CryptokiObject, Sign}; 11 use rsclientcerts_util::*; 12 use rsclientcerts_util::error::{Error, ErrorType}; 13 use std::convert::TryInto; 14 use std::ffi::{c_void, CStr, CString}; 15 use std::ops::Deref; 16 use std::slice; 17 use std::time::{Duration, Instant}; 18 use winapi::shared::bcrypt::*; 19 use winapi::shared::minwindef::{DWORD, PBYTE}; 20 use winapi::um::errhandlingapi::GetLastError; 21 use winapi::um::ncrypt::*; 22 use winapi::um::wincrypt::{HCRYPTHASH, HCRYPTPROV, *}; 23 use xpcom::interfaces::nsIEventTarget; 24 use xpcom::{RefPtr, XpCom}; 25 26 // winapi has some support for ncrypt.h, but not for this function. 27 extern "system" { 28 fn NCryptSignHash( 29 hKey: NCRYPT_KEY_HANDLE, 30 pPaddingInfo: *mut c_void, 31 pbHashValue: PBYTE, 32 cbHashValue: DWORD, 33 pbSignature: PBYTE, 34 cbSignature: DWORD, 35 pcbResult: *mut DWORD, 36 dwFlags: DWORD, 37 ) -> SECURITY_STATUS; 38 } 39 40 /// Given a `CERT_INFO`, tries to return the bytes of the subject distinguished name as formatted by 41 /// `CertNameToStrA` using the flag `CERT_SIMPLE_NAME_STR`. This is used as the label for the 42 /// certificate. 43 fn get_cert_subject_dn(cert_info: &CERT_INFO) -> Result<Vec<u8>, Error> { 44 let mut cert_info_subject = cert_info.Subject; 45 let subject_dn_len = unsafe { 46 CertNameToStrA( 47 X509_ASN_ENCODING, 48 &mut cert_info_subject, 49 CERT_SIMPLE_NAME_STR, 50 std::ptr::null_mut(), 51 0, 52 ) 53 }; 54 // subject_dn_len includes the terminating null byte. 55 let mut subject_dn_string_bytes: Vec<u8> = vec![0; subject_dn_len as usize]; 56 let subject_dn_len = unsafe { 57 CertNameToStrA( 58 X509_ASN_ENCODING, 59 &mut cert_info_subject, 60 CERT_SIMPLE_NAME_STR, 61 subject_dn_string_bytes.as_mut_ptr() as *mut i8, 62 subject_dn_string_bytes 63 .len() 64 .try_into() 65 .map_err(|_| error_here!(ErrorType::ValueTooLarge))?, 66 ) 67 }; 68 if subject_dn_len as usize != subject_dn_string_bytes.len() { 69 return Err(error_here!(ErrorType::ExternalError)); 70 } 71 Ok(subject_dn_string_bytes) 72 } 73 74 fn new_cert(cert: PCCERT_CONTEXT) -> Result<CryptokiCert, Error> { 75 let der = 76 unsafe { slice::from_raw_parts((*cert).pbCertEncoded, (*cert).cbCertEncoded as usize) }; 77 let cert_info = unsafe { &*(*cert).pCertInfo }; 78 let label = get_cert_subject_dn(cert_info)?; 79 CryptokiCert::new(der.to_vec(), label) 80 } 81 82 struct CertContext(PCCERT_CONTEXT); 83 84 impl CertContext { 85 fn new(cert: PCCERT_CONTEXT) -> CertContext { 86 CertContext(unsafe { CertDuplicateCertificateContext(cert) }) 87 } 88 } 89 90 impl Drop for CertContext { 91 fn drop(&mut self) { 92 unsafe { 93 CertFreeCertificateContext(self.0); 94 } 95 } 96 } 97 98 impl Deref for CertContext { 99 type Target = PCCERT_CONTEXT; 100 101 fn deref(&self) -> &Self::Target { 102 &self.0 103 } 104 } 105 106 /// Safety: strictly speaking, it isn't safe to send `CertContext` across threads. The 107 /// implementation handles this by wrapping `CertContext` in `ThreadSpecificHandles`. However, in 108 /// order to implement `Drop` for `ThreadSpecificHandles`, the `CertContext` it holds must be sent 109 /// to the appropriate thread, hence this impl. 110 unsafe impl Send for CertContext {} 111 112 enum KeyHandle { 113 NCrypt(NCRYPT_KEY_HANDLE), 114 CryptoAPI(HCRYPTPROV, DWORD), 115 } 116 117 impl KeyHandle { 118 fn from_cert(cert: &CertContext) -> Result<KeyHandle, Error> { 119 let mut key_handle = 0; 120 let mut key_spec = 0; 121 let mut must_free = 0; 122 unsafe { 123 if CryptAcquireCertificatePrivateKey( 124 **cert, 125 CRYPT_ACQUIRE_PREFER_NCRYPT_KEY_FLAG, 126 std::ptr::null_mut(), 127 &mut key_handle, 128 &mut key_spec, 129 &mut must_free, 130 ) != 1 131 { 132 return Err(error_here!( 133 ErrorType::ExternalError, 134 GetLastError().to_string() 135 )); 136 } 137 } 138 if must_free == 0 { 139 return Err(error_here!(ErrorType::ExternalError)); 140 } 141 if key_spec == CERT_NCRYPT_KEY_SPEC { 142 Ok(KeyHandle::NCrypt(key_handle as NCRYPT_KEY_HANDLE)) 143 } else { 144 Ok(KeyHandle::CryptoAPI(key_handle as HCRYPTPROV, key_spec)) 145 } 146 } 147 148 fn sign( 149 &self, 150 data: &[u8], 151 params: &Option<CK_RSA_PKCS_PSS_PARAMS>, 152 do_signature: bool, 153 key_type: KeyType, 154 ) -> Result<Vec<u8>, Error> { 155 match &self { 156 KeyHandle::NCrypt(ncrypt_handle) => { 157 sign_ncrypt(ncrypt_handle, data, params, do_signature, key_type) 158 } 159 KeyHandle::CryptoAPI(hcryptprov, key_spec) => { 160 sign_cryptoapi(hcryptprov, key_spec, data, params, do_signature) 161 } 162 } 163 } 164 } 165 166 impl Drop for KeyHandle { 167 fn drop(&mut self) { 168 match self { 169 KeyHandle::NCrypt(ncrypt_handle) => unsafe { 170 let _ = NCryptFreeObject(*ncrypt_handle); 171 }, 172 KeyHandle::CryptoAPI(hcryptprov, _) => unsafe { 173 let _ = CryptReleaseContext(*hcryptprov, 0); 174 }, 175 } 176 } 177 } 178 179 /// Safety: see the comment for the `Send` impl for `CertContext`. 180 unsafe impl Send for KeyHandle {} 181 182 fn sign_ncrypt( 183 ncrypt_handle: &NCRYPT_KEY_HANDLE, 184 data: &[u8], 185 params: &Option<CK_RSA_PKCS_PSS_PARAMS>, 186 do_signature: bool, 187 key_type: KeyType, 188 ) -> Result<Vec<u8>, Error> { 189 let mut sign_params = SignParams::new(key_type, params)?; 190 let params_ptr = sign_params.params_ptr(); 191 let flags = sign_params.flags(); 192 let mut data = data.to_vec(); 193 let mut signature_len = 0; 194 // We call NCryptSignHash twice: the first time to get the size of the buffer we need to 195 // allocate and then again to actually sign the data, if `do_signature` is `true`. 196 let status = unsafe { 197 NCryptSignHash( 198 *ncrypt_handle, 199 params_ptr, 200 data.as_mut_ptr(), 201 data.len() 202 .try_into() 203 .map_err(|_| error_here!(ErrorType::ValueTooLarge))?, 204 std::ptr::null_mut(), 205 0, 206 &mut signature_len, 207 flags, 208 ) 209 }; 210 // 0 is "ERROR_SUCCESS" (but "ERROR_SUCCESS" is unsigned, whereas SECURITY_STATUS is signed) 211 if status != 0 { 212 return Err(error_here!(ErrorType::ExternalError, status.to_string())); 213 } 214 let mut signature = vec![0; signature_len as usize]; 215 if !do_signature { 216 return Ok(signature); 217 } 218 let mut final_signature_len = signature_len; 219 let status = unsafe { 220 NCryptSignHash( 221 *ncrypt_handle, 222 params_ptr, 223 data.as_mut_ptr(), 224 data.len() 225 .try_into() 226 .map_err(|_| error_here!(ErrorType::ValueTooLarge))?, 227 signature.as_mut_ptr(), 228 signature_len, 229 &mut final_signature_len, 230 flags, 231 ) 232 }; 233 if status != 0 { 234 return Err(error_here!(ErrorType::ExternalError, status.to_string())); 235 } 236 if final_signature_len != signature_len { 237 return Err(error_here!(ErrorType::ExternalError)); 238 } 239 Ok(signature) 240 } 241 242 fn sign_cryptoapi( 243 hcryptprov: &HCRYPTPROV, 244 key_spec: &DWORD, 245 data: &[u8], 246 params: &Option<CK_RSA_PKCS_PSS_PARAMS>, 247 do_signature: bool, 248 ) -> Result<Vec<u8>, Error> { 249 if params.is_some() { 250 return Err(error_here!(ErrorType::LibraryFailure)); 251 } 252 // data will be an encoded DigestInfo, which specifies the hash algorithm and bytes of the hash 253 // to sign. However, CryptoAPI requires directly specifying the bytes of the hash, so it must 254 // be extracted first. 255 let (_, hash_bytes) = read_digest_info(data)?; 256 let hash = HCryptHash::new(hcryptprov, hash_bytes)?; 257 let mut signature_len = 0; 258 if unsafe { 259 CryptSignHashW( 260 *hash, 261 *key_spec, 262 std::ptr::null_mut(), 263 0, 264 std::ptr::null_mut(), 265 &mut signature_len, 266 ) 267 } != 1 268 { 269 return Err(error_here!( 270 ErrorType::ExternalError, 271 unsafe { GetLastError() }.to_string() 272 )); 273 } 274 let mut signature = vec![0; signature_len as usize]; 275 if !do_signature { 276 return Ok(signature); 277 } 278 let mut final_signature_len = signature_len; 279 if unsafe { 280 CryptSignHashW( 281 *hash, 282 *key_spec, 283 std::ptr::null_mut(), 284 0, 285 signature.as_mut_ptr(), 286 &mut final_signature_len, 287 ) 288 } != 1 289 { 290 return Err(error_here!( 291 ErrorType::ExternalError, 292 unsafe { GetLastError() }.to_string() 293 )); 294 } 295 if final_signature_len != signature_len { 296 return Err(error_here!(ErrorType::ExternalError)); 297 } 298 // CryptoAPI returns the signature with the most significant byte last (little-endian), 299 // whereas PKCS#11 expects the most significant byte first (big-endian). 300 signature.reverse(); 301 Ok(signature) 302 } 303 304 struct HCryptHash(HCRYPTHASH); 305 306 impl HCryptHash { 307 fn new(hcryptprov: &HCRYPTPROV, hash_bytes: &[u8]) -> Result<HCryptHash, Error> { 308 let alg = match hash_bytes.len() { 309 20 => CALG_SHA1, 310 32 => CALG_SHA_256, 311 48 => CALG_SHA_384, 312 64 => CALG_SHA_512, 313 _ => { 314 return Err(error_here!(ErrorType::UnsupportedInput)); 315 } 316 }; 317 let mut hash: HCRYPTHASH = 0; 318 if unsafe { CryptCreateHash(*hcryptprov, alg, 0, 0, &mut hash) } != 1 { 319 return Err(error_here!( 320 ErrorType::ExternalError, 321 unsafe { GetLastError() }.to_string() 322 )); 323 } 324 if unsafe { CryptSetHashParam(hash, HP_HASHVAL, hash_bytes.as_ptr(), 0) } != 1 { 325 return Err(error_here!( 326 ErrorType::ExternalError, 327 unsafe { GetLastError() }.to_string() 328 )); 329 } 330 Ok(HCryptHash(hash)) 331 } 332 } 333 334 impl Drop for HCryptHash { 335 fn drop(&mut self) { 336 unsafe { 337 CryptDestroyHash(self.0); 338 } 339 } 340 } 341 342 impl Deref for HCryptHash { 343 type Target = HCRYPTHASH; 344 345 fn deref(&self) -> &Self::Target { 346 &self.0 347 } 348 } 349 350 // In some cases, the ncrypt API takes a pointer to a null-terminated wide-character string as a way 351 // of specifying an algorithm. The "right" way to do this would be to take the corresponding 352 // &'static str constant provided by the winapi crate, create an OsString from it, encode it as wide 353 // characters, and collect it into a Vec<u16>. However, since the implementation that provides this 354 // functionality isn't constant, we would have to manage the memory this creates and uses. Since 355 // rust structures generally can't be self-referrential, this memory would have to live elsewhere, 356 // and the nice abstractions we've created for this implementation start to break down. It's much 357 // simpler to hard-code the identifiers we support, since there are only four of them. 358 // The following arrays represent the identifiers "SHA1", "SHA256", "SHA384", and "SHA512", 359 // respectively. 360 const SHA1_ALGORITHM_STRING: &[u16] = &[83, 72, 65, 49, 0]; 361 const SHA256_ALGORITHM_STRING: &[u16] = &[83, 72, 65, 50, 53, 54, 0]; 362 const SHA384_ALGORITHM_STRING: &[u16] = &[83, 72, 65, 51, 56, 52, 0]; 363 const SHA512_ALGORITHM_STRING: &[u16] = &[83, 72, 65, 53, 49, 50, 0]; 364 365 enum SignParams { 366 EC, 367 RSA_PKCS1(BCRYPT_PKCS1_PADDING_INFO), 368 RSA_PSS(BCRYPT_PSS_PADDING_INFO), 369 } 370 371 impl SignParams { 372 fn new( 373 key_type: KeyType, 374 params: &Option<CK_RSA_PKCS_PSS_PARAMS>, 375 ) -> Result<SignParams, Error> { 376 // EC is easy, so handle that first. 377 match key_type { 378 KeyType::EC(_) => return Ok(SignParams::EC), 379 KeyType::RSA => {} 380 } 381 // If `params` is `Some`, we're doing RSA-PSS. If it is `None`, we're doing RSA-PKCS1. 382 let pss_params = match params { 383 Some(pss_params) => pss_params, 384 None => { 385 // The hash algorithm should be encoded in the data to be signed, so we don't have to 386 // (and don't want to) specify a particular algorithm here. 387 return Ok(SignParams::RSA_PKCS1(BCRYPT_PKCS1_PADDING_INFO { 388 pszAlgId: std::ptr::null(), 389 })); 390 } 391 }; 392 let algorithm_string = match pss_params.hashAlg { 393 CKM_SHA_1 => SHA1_ALGORITHM_STRING, 394 CKM_SHA256 => SHA256_ALGORITHM_STRING, 395 CKM_SHA384 => SHA384_ALGORITHM_STRING, 396 CKM_SHA512 => SHA512_ALGORITHM_STRING, 397 _ => { 398 return Err(error_here!(ErrorType::UnsupportedInput)); 399 } 400 }; 401 Ok(SignParams::RSA_PSS(BCRYPT_PSS_PADDING_INFO { 402 pszAlgId: algorithm_string.as_ptr(), 403 cbSalt: pss_params.sLen, 404 })) 405 } 406 407 fn params_ptr(&mut self) -> *mut std::ffi::c_void { 408 match self { 409 SignParams::EC => std::ptr::null_mut(), 410 SignParams::RSA_PKCS1(params) => { 411 params as *mut BCRYPT_PKCS1_PADDING_INFO as *mut std::ffi::c_void 412 } 413 SignParams::RSA_PSS(params) => { 414 params as *mut BCRYPT_PSS_PADDING_INFO as *mut std::ffi::c_void 415 } 416 } 417 } 418 419 fn flags(&self) -> u32 { 420 match *self { 421 SignParams::EC => 0, 422 SignParams::RSA_PKCS1(_) => NCRYPT_PAD_PKCS1_FLAG, 423 SignParams::RSA_PSS(_) => NCRYPT_PAD_PSS_FLAG, 424 } 425 } 426 } 427 428 /// Helper struct to hold onto OS-specific handles that must only be used on a particular thread. 429 struct ThreadSpecificHandles { 430 /// The only thread that these handles may be used on. 431 thread: RefPtr<nsIEventTarget>, 432 /// A handle on the OS mechanism that represents the certificate for a key. 433 cert: Option<CertContext>, 434 /// A handle on the OS mechanism that represents a key. 435 key: Option<KeyHandle>, 436 } 437 438 impl ThreadSpecificHandles { 439 fn new(cert: CertContext, thread: &nsIEventTarget) -> ThreadSpecificHandles { 440 ThreadSpecificHandles { 441 thread: RefPtr::new(thread), 442 cert: Some(cert), 443 key: None, 444 } 445 } 446 447 fn sign_or_get_signature_length( 448 &mut self, 449 key_type: KeyType, 450 data: &[u8], 451 params: &Option<CK_RSA_PKCS_PSS_PARAMS>, 452 do_signature: bool, 453 ) -> Result<Vec<u8>, Error> { 454 let Some(cert) = self.cert.take() else { 455 return Err(error_here!(ErrorType::LibraryFailure)); 456 }; 457 let mut maybe_key = self.key.take(); 458 let thread = self.thread.clone(); 459 let data = data.to_vec(); 460 let params = params.clone(); 461 let task = moz_task::spawn_onto("sign", &thread, async move { 462 let result = sign_internal( 463 &cert, 464 &mut maybe_key, 465 key_type, 466 &data, 467 ¶ms, 468 do_signature, 469 ); 470 if result.is_ok() { 471 return (result, cert, maybe_key); 472 } 473 // Some devices appear to not work well when the key handle is held for too long or if a 474 // card is inserted/removed while Firefox is running. Try refreshing the key handle. 475 let _ = maybe_key.take(); 476 ( 477 sign_internal( 478 &cert, 479 &mut maybe_key, 480 key_type, 481 &data, 482 ¶ms, 483 do_signature, 484 ), 485 cert, 486 maybe_key, 487 ) 488 }); 489 let (signature_result, cert, maybe_key) = futures_executor::block_on(task); 490 self.cert = Some(cert); 491 self.key = maybe_key; 492 signature_result 493 } 494 } 495 496 /// data: the data to sign 497 /// do_signature: if true, actually perform the signature. Otherwise, return a `Vec<u8>` of the 498 /// length the signature would be, if performed. 499 fn sign_internal( 500 cert: &CertContext, 501 maybe_key: &mut Option<KeyHandle>, 502 key_type: KeyType, 503 data: &[u8], 504 params: &Option<CK_RSA_PKCS_PSS_PARAMS>, 505 do_signature: bool, 506 ) -> Result<Vec<u8>, Error> { 507 // If this key hasn't been used for signing yet, there won't be a cached key handle. Obtain 508 // and cache it if this is the case. Doing so can cause the underlying implementation to 509 // show an authentication or pin prompt to the user. Caching the handle can avoid causing 510 // multiple prompts to be displayed in some cases. 511 if maybe_key.is_none() { 512 let _ = maybe_key.replace(KeyHandle::from_cert(cert)?); 513 } 514 let Some(key) = maybe_key.as_ref() else { 515 return Err(error_here!(ErrorType::LibraryFailure)); 516 }; 517 key.sign(data, params, do_signature, key_type) 518 } 519 520 impl Drop for ThreadSpecificHandles { 521 fn drop(&mut self) { 522 // Ensure any OS handles are dropped on the appropriate thread. 523 let cert = self.cert.take(); 524 let key = self.key.take(); 525 let thread = self.thread.clone(); 526 // It is possible that we're already on the appropriate thread (e.g. if an error was 527 // encountered in `find_objects` and these handles are being released shortly after being 528 // created). 529 if moz_task::is_on_current_thread(&thread) { 530 drop(cert); 531 drop(key); 532 } else { 533 let task = moz_task::spawn_onto("drop", &thread, async move { 534 drop(cert); 535 drop(key); 536 }); 537 futures_executor::block_on(task) 538 } 539 } 540 } 541 542 /// Represents a private key for which there exists a corresponding certificate. 543 pub struct Key { 544 /// The OS handles for this key. May only be used on the thread they were created on. 545 handles: ThreadSpecificHandles, 546 /// The decoded information about the key. 547 cryptoki_key: CryptokiKey, 548 } 549 550 impl Key { 551 fn new(cert_context: PCCERT_CONTEXT, thread: &nsIEventTarget) -> Result<Key, Error> { 552 let cert = unsafe { *cert_context }; 553 let cert_der = 554 unsafe { slice::from_raw_parts(cert.pbCertEncoded, cert.cbCertEncoded as usize) }; 555 let cert_info = unsafe { &*cert.pCertInfo }; 556 let spki = &cert_info.SubjectPublicKeyInfo; 557 let algorithm_oid = unsafe { CStr::from_ptr(spki.Algorithm.pszObjId) } 558 .to_str() 559 .map_err(|_| error_here!(ErrorType::ExternalError))?; 560 let (modulus, ec_params) = if algorithm_oid == szOID_RSA_RSA { 561 if spki.PublicKey.cUnusedBits != 0 { 562 return Err(error_here!(ErrorType::ExternalError)); 563 } 564 let public_key_bytes = unsafe { 565 std::slice::from_raw_parts(spki.PublicKey.pbData, spki.PublicKey.cbData as usize) 566 }; 567 let modulus = read_rsa_modulus(public_key_bytes)?; 568 (Some(modulus), None) 569 } else if algorithm_oid == szOID_ECC_PUBLIC_KEY { 570 let params = &spki.Algorithm.Parameters; 571 let ec_params = 572 unsafe { std::slice::from_raw_parts(params.pbData, params.cbData as usize) } 573 .to_vec(); 574 (None, Some(ec_params)) 575 } else { 576 return Err(error_here!(ErrorType::LibraryFailure)); 577 }; 578 let cert = CertContext::new(cert_context); 579 Ok(Key { 580 handles: ThreadSpecificHandles::new(cert, thread), 581 cryptoki_key: CryptokiKey::new(modulus, ec_params, cert_der)?, 582 }) 583 } 584 585 fn sign_or_get_signature_length( 586 &mut self, 587 data: &[u8], 588 params: &Option<CK_RSA_PKCS_PSS_PARAMS>, 589 do_signature: bool, 590 ) -> Result<Vec<u8>, Error> { 591 self.handles.sign_or_get_signature_length( 592 self.cryptoki_key.key_type(), 593 data, 594 params, 595 do_signature, 596 ) 597 } 598 } 599 600 impl Sign for Key { 601 fn get_signature_length( 602 &mut self, 603 data: &[u8], 604 params: &Option<CK_RSA_PKCS_PSS_PARAMS>, 605 ) -> Result<usize, Error> { 606 self.sign_or_get_signature_length(data, params, false) 607 .map(|signature| signature.len()) 608 } 609 610 fn sign( 611 &mut self, 612 data: &[u8], 613 params: &Option<CK_RSA_PKCS_PSS_PARAMS>, 614 ) -> Result<Vec<u8>, Error> { 615 self.sign_or_get_signature_length(data, params, true) 616 } 617 } 618 619 impl CryptokiObject for Key { 620 fn matches(&self, attrs: &[(CK_ATTRIBUTE_TYPE, Vec<u8>)]) -> bool { 621 self.cryptoki_key.matches(attrs) 622 } 623 624 fn get_attribute(&self, attribute: CK_ATTRIBUTE_TYPE) -> Option<&[u8]> { 625 self.cryptoki_key.get_attribute(attribute) 626 } 627 } 628 629 struct CertStore { 630 handle: HCERTSTORE, 631 } 632 633 impl Drop for CertStore { 634 fn drop(&mut self) { 635 if !self.handle.is_null() { 636 unsafe { 637 CertCloseStore(self.handle, 0); 638 } 639 } 640 } 641 } 642 643 impl Deref for CertStore { 644 type Target = HCERTSTORE; 645 646 fn deref(&self) -> &Self::Target { 647 &self.handle 648 } 649 } 650 651 impl CertStore { 652 fn new(handle: HCERTSTORE) -> CertStore { 653 CertStore { handle } 654 } 655 } 656 657 // Given a pointer to a CERT_CHAIN_CONTEXT, enumerates each chain in the context and each element 658 // in each chain to gather every CERT_CONTEXT pointed to by the CERT_CHAIN_CONTEXT. 659 // https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-cert_chain_context says 660 // that the 0th element of the 0th chain will be the end-entity certificate. This certificate (if 661 // present), will be the 0th element of the returned Vec. 662 fn gather_cert_contexts(cert_chain_context: *const CERT_CHAIN_CONTEXT) -> Vec<*const CERT_CONTEXT> { 663 let mut cert_contexts = Vec::new(); 664 if cert_chain_context.is_null() { 665 return cert_contexts; 666 } 667 let cert_chain_context = unsafe { &*cert_chain_context }; 668 let cert_chains = unsafe { 669 std::slice::from_raw_parts( 670 cert_chain_context.rgpChain, 671 cert_chain_context.cChain as usize, 672 ) 673 }; 674 for cert_chain in cert_chains { 675 // First dereference the borrow. 676 let cert_chain = *cert_chain; 677 if cert_chain.is_null() { 678 continue; 679 } 680 // Then dereference the pointer. 681 let cert_chain = unsafe { &*cert_chain }; 682 let chain_elements = unsafe { 683 std::slice::from_raw_parts(cert_chain.rgpElement, cert_chain.cElement as usize) 684 }; 685 for chain_element in chain_elements { 686 let chain_element = *chain_element; // dereference borrow 687 if chain_element.is_null() { 688 continue; 689 } 690 let chain_element = unsafe { &*chain_element }; // dereference pointer 691 cert_contexts.push(chain_element.pCertContext); 692 } 693 } 694 cert_contexts 695 } 696 697 pub struct Backend { 698 /// A background thread that all OS API calls will be done on. This is to prevent issues with 699 /// modules or implementations using thread-local state. 700 thread: RefPtr<nsIEventTarget>, 701 /// The last time a call to `find_objects` finished, to avoid searching for objects more than 702 /// once every 3 seconds. 703 last_scan_finished: Option<Instant>, 704 } 705 706 impl Backend { 707 pub fn new() -> Result<Backend, Error> { 708 let thread = moz_task::create_thread("osclientcerts").map_err(|nsresult| { 709 error_here!(ErrorType::LibraryFailure, nsresult.error_name().to_string()) 710 })?; 711 Ok(Backend { 712 thread: thread 713 .query_interface::<nsIEventTarget>() 714 .ok_or(error_here!(ErrorType::LibraryFailure))?, 715 last_scan_finished: None, 716 }) 717 } 718 } 719 720 const SLOT_DESCRIPTION_BYTES: &[u8; 64] = 721 b"OS Client Cert Slot "; 722 const TOKEN_LABEL_BYTES: &[u8; 32] = b"OS Client Cert Token "; 723 const TOKEN_MODEL_BYTES: &[u8; 16] = b"osclientcerts "; 724 const TOKEN_SERIAL_NUMBER_BYTES: &[u8; 16] = b"0000000000000000"; 725 726 impl ClientCertsBackend for Backend { 727 type Key = Key; 728 729 fn find_objects(&mut self) -> Result<(Vec<CryptokiCert>, Vec<Key>), Error> { 730 match self.last_scan_finished { 731 Some(last_scan_finished) => { 732 if Instant::now().duration_since(last_scan_finished) < Duration::new(3, 0) { 733 return Ok((Vec::new(), Vec::new())); 734 } 735 } 736 None => {} 737 } 738 739 let thread = self.thread.clone(); 740 let task = moz_task::spawn_onto("find_objects", &self.thread, async move { 741 find_objects(&thread) 742 }); 743 let result = futures_executor::block_on(task); 744 self.last_scan_finished = Some(Instant::now()); 745 result 746 } 747 748 fn get_slot_info(&self) -> CK_SLOT_INFO { 749 CK_SLOT_INFO { 750 slotDescription: *SLOT_DESCRIPTION_BYTES, 751 manufacturerID: *crate::MANUFACTURER_ID_BYTES, 752 flags: CKF_TOKEN_PRESENT, 753 ..Default::default() 754 } 755 } 756 757 fn get_token_info(&self) -> CK_TOKEN_INFO { 758 CK_TOKEN_INFO { 759 label: *TOKEN_LABEL_BYTES, 760 manufacturerID: *crate::MANUFACTURER_ID_BYTES, 761 model: *TOKEN_MODEL_BYTES, 762 serialNumber: *TOKEN_SERIAL_NUMBER_BYTES, 763 ..Default::default() 764 } 765 } 766 767 fn get_mechanism_list(&self) -> Vec<CK_MECHANISM_TYPE> { 768 vec![CKM_ECDSA, CKM_RSA_PKCS, CKM_RSA_PKCS_PSS] 769 } 770 } 771 772 /// Attempts to enumerate certificates with private keys exposed by the OS. Currently only looks in 773 /// the "My" cert store of the current user. In the future this may look in more locations. 774 fn find_objects(thread: &nsIEventTarget) -> Result<(Vec<CryptokiCert>, Vec<Key>), Error> { 775 let mut certs = Vec::new(); 776 let mut keys = Vec::new(); 777 let location_flags = 778 CERT_SYSTEM_STORE_CURRENT_USER | CERT_STORE_OPEN_EXISTING_FLAG | CERT_STORE_READONLY_FLAG; 779 let store_name = match CString::new("My") { 780 Ok(store_name) => store_name, 781 Err(_) => return Err(error_here!(ErrorType::LibraryFailure)), 782 }; 783 let store = CertStore::new(unsafe { 784 CertOpenStore( 785 CERT_STORE_PROV_SYSTEM_REGISTRY_A, 786 0, 787 0, 788 location_flags, 789 store_name.as_ptr() as *const winapi::ctypes::c_void, 790 ) 791 }); 792 if store.is_null() { 793 return Err(error_here!(ErrorType::ExternalError)); 794 } 795 let find_params = CERT_CHAIN_FIND_ISSUER_PARA { 796 cbSize: std::mem::size_of::<CERT_CHAIN_FIND_ISSUER_PARA>() as u32, 797 pszUsageIdentifier: std::ptr::null(), 798 dwKeySpec: 0, 799 dwAcquirePrivateKeyFlags: 0, 800 cIssuer: 0, 801 rgIssuer: std::ptr::null_mut(), 802 pfnFindCallback: None, 803 pvFindArg: std::ptr::null_mut(), 804 pdwIssuerChainIndex: std::ptr::null_mut(), 805 pdwIssuerElementIndex: std::ptr::null_mut(), 806 }; 807 let mut cert_chain_context: PCCERT_CHAIN_CONTEXT = std::ptr::null_mut(); 808 loop { 809 // CertFindChainInStore finds all certificates with private keys in the store. It also 810 // attempts to build a verified certificate chain to a trust anchor for each certificate. 811 // We gather and hold onto these extra certificates so that gecko can use them when 812 // filtering potential client certificates according to the acceptable CAs list sent by 813 // servers when they request client certificates. 814 cert_chain_context = unsafe { 815 CertFindChainInStore( 816 *store, 817 X509_ASN_ENCODING, 818 CERT_CHAIN_FIND_BY_ISSUER_CACHE_ONLY_FLAG 819 | CERT_CHAIN_FIND_BY_ISSUER_CACHE_ONLY_URL_FLAG, 820 CERT_CHAIN_FIND_BY_ISSUER, 821 &find_params as *const CERT_CHAIN_FIND_ISSUER_PARA as *const winapi::ctypes::c_void, 822 cert_chain_context, 823 ) 824 }; 825 if cert_chain_context.is_null() { 826 break; 827 } 828 let cert_contexts = gather_cert_contexts(cert_chain_context); 829 // The 0th CERT_CONTEXT is the end-entity (i.e. the certificate with the private key we're 830 // after). 831 match cert_contexts.get(0) { 832 Some(cert_context) => { 833 let key = match Key::new(*cert_context, thread) { 834 Ok(key) => key, 835 Err(_) => continue, 836 }; 837 let cert = match new_cert(*cert_context) { 838 Ok(cert) => cert, 839 Err(_) => continue, 840 }; 841 certs.push(cert); 842 keys.push(key); 843 } 844 None => {} 845 }; 846 for cert_context in cert_contexts.iter().skip(1) { 847 if let Ok(cert) = new_cert(*cert_context) { 848 certs.push(cert); 849 } 850 } 851 } 852 Ok((certs, keys)) 853 }