|
2 | 2 | // 2.0, and the BSD License. See the LICENSE file in the root of this repository |
3 | 3 | // for complete details. |
4 | 4 |
|
| 5 | +use std::collections::HashMap; |
| 6 | + |
5 | 7 | use cryptography_x509::certificate::SerialNumber; |
6 | 8 | use cryptography_x509::common::{self, Asn1Read}; |
7 | 9 | use cryptography_x509::crl::{ |
@@ -46,6 +48,7 @@ pub(crate) fn load_der_x509_crl( |
46 | 48 | Ok(CertificateRevocationList { |
47 | 49 | owned, |
48 | 50 | revoked_certs: pyo3::sync::PyOnceLock::new(), |
| 51 | + serial_number_map: pyo3::sync::PyOnceLock::new(), |
49 | 52 | cached_extensions: pyo3::sync::PyOnceLock::new(), |
50 | 53 | cached_issuer: pyo3::sync::PyOnceLock::new(), |
51 | 54 | cached_signature_algorithm_oid: pyo3::sync::PyOnceLock::new(), |
@@ -87,6 +90,7 @@ pub(crate) struct CertificateRevocationList { |
87 | 90 | owned: OwnedCertificateRevocationList, |
88 | 91 |
|
89 | 92 | revoked_certs: pyo3::sync::PyOnceLock<Vec<OwnedRevokedCertificate>>, |
| 93 | + serial_number_map: pyo3::sync::PyOnceLock<HashMap<Vec<u8>, OwnedRevokedCertificate>>, |
90 | 94 | cached_extensions: pyo3::sync::PyOnceLock<pyo3::Py<pyo3::PyAny>>, |
91 | 95 | cached_issuer: pyo3::sync::PyOnceLock<pyo3::Py<pyo3::PyAny>>, |
92 | 96 | cached_signature_algorithm_oid: pyo3::sync::PyOnceLock<pyo3::Py<pyo3::PyAny>>, |
@@ -417,21 +421,43 @@ impl CertificateRevocationList { |
417 | 421 | ) -> pyo3::PyResult<Option<RevokedCertificate>> { |
418 | 422 | let serial_bytes = py_uint_to_big_endian_bytes(py, serial)?; |
419 | 423 |
|
420 | | - // Use try_map_crl_to_revoked_cert to soundly extract the certificate |
421 | | - let owned = try_map_crl_to_revoked_cert(&self.owned, py, |crl| { |
422 | | - let certs = crl.tbs_cert_list.revoked_certificates.as_ref()?; |
423 | | - |
424 | | - // TODO: linear scan. Make a hash or bisect! |
425 | | - certs |
426 | | - .unwrap_read() |
427 | | - .clone() |
428 | | - .find(|cert| serial_bytes == cert.user_certificate.as_bytes()) |
| 424 | + let map = self.serial_number_map.get_or_init(py, || { |
| 425 | + let mut map = HashMap::new(); |
| 426 | + let mut it_data = map_crl_to_iterator_data(&self.owned, py, |crl| { |
| 427 | + crl.tbs_cert_list |
| 428 | + .revoked_certificates |
| 429 | + .as_ref() |
| 430 | + .map(|v| v.unwrap_read().clone()) |
| 431 | + }); |
| 432 | + loop { |
| 433 | + let revoked = try_map_arc_data_mut_crl_iterator(py, &mut it_data, |v| match v { |
| 434 | + Some(v) => match v.next() { |
| 435 | + Some(revoked) => Ok(revoked), |
| 436 | + None => Err(()), |
| 437 | + }, |
| 438 | + None => Err(()), |
| 439 | + }); |
| 440 | + match revoked { |
| 441 | + Ok(owned) => { |
| 442 | + let key = owned |
| 443 | + .borrow_dependent() |
| 444 | + .user_certificate |
| 445 | + .as_bytes() |
| 446 | + .to_vec(); |
| 447 | + map.insert(key, owned); |
| 448 | + } |
| 449 | + Err(()) => break, |
| 450 | + } |
| 451 | + } |
| 452 | + map |
429 | 453 | }); |
430 | 454 |
|
431 | | - Ok(owned.map(|o| RevokedCertificate { |
432 | | - owned: o, |
433 | | - cached_extensions: pyo3::sync::PyOnceLock::new(), |
434 | | - })) |
| 455 | + Ok(map |
| 456 | + .get(serial_bytes.as_ref()) |
| 457 | + .map(|owned| RevokedCertificate { |
| 458 | + owned: owned.clone_with_py(py), |
| 459 | + cached_extensions: pyo3::sync::PyOnceLock::new(), |
| 460 | + })) |
435 | 461 | } |
436 | 462 |
|
437 | 463 | fn is_signature_valid<'p>( |
@@ -500,33 +526,6 @@ where |
500 | 526 | }) |
501 | 527 | } |
502 | 528 |
|
503 | | -// Open-coded implementation of the API discussed in |
504 | | -// https://github.com/joshua-maros/ouroboros/issues/38 |
505 | | -fn try_map_crl_to_revoked_cert<F>( |
506 | | - source: &OwnedCertificateRevocationList, |
507 | | - py: pyo3::Python<'_>, |
508 | | - f: F, |
509 | | -) -> Option<OwnedRevokedCertificate> |
510 | | -where |
511 | | - F: for<'a> FnOnce(&'a RawCertificateRevocationList<'a>) -> Option<RawRevokedCertificate<'a>>, |
512 | | -{ |
513 | | - OwnedRevokedCertificate::try_new(source.borrow_owner().clone_ref(py), |_| { |
514 | | - // SAFETY: This is safe because cloning the PyBytes Py<> ensures the data is |
515 | | - // alive, but Rust doesn't understand the lifetime relationship it |
516 | | - // produces. |
517 | | - match f(unsafe { |
518 | | - std::mem::transmute::< |
519 | | - &RawCertificateRevocationList<'_>, |
520 | | - &RawCertificateRevocationList<'_>, |
521 | | - >(source.borrow_dependent()) |
522 | | - }) { |
523 | | - Some(cert) => Ok(cert), |
524 | | - None => Err(()), |
525 | | - } |
526 | | - }) |
527 | | - .ok() |
528 | | -} |
529 | | - |
530 | 529 | // Open-coded implementation of the API discussed in |
531 | 530 | // https://github.com/joshua-maros/ouroboros/issues/38 |
532 | 531 | fn map_revoked_cert<F>( |
|
0 commit comments