22// 2.0, and the BSD License. See the LICENSE file in the root of this repository
33// for complete details.
44
5- use std:: sync:: Arc ;
6-
75use cryptography_x509:: certificate:: SerialNumber ;
86use cryptography_x509:: common:: { self , Asn1Read } ;
97use cryptography_x509:: crl:: {
@@ -46,7 +44,7 @@ pub(crate) fn load_der_x509_crl(
4644 }
4745
4846 Ok ( CertificateRevocationList {
49- owned : Arc :: new ( owned ) ,
47+ owned,
5048 revoked_certs : pyo3:: sync:: PyOnceLock :: new ( ) ,
5149 cached_extensions : pyo3:: sync:: PyOnceLock :: new ( ) ,
5250 } )
@@ -83,7 +81,7 @@ self_cell::self_cell!(
8381
8482#[ pyo3:: pyclass( frozen, module = "cryptography.hazmat.bindings._rust.x509" ) ]
8583pub ( crate ) struct CertificateRevocationList {
86- owned : Arc < OwnedCertificateRevocationList > ,
84+ owned : OwnedCertificateRevocationList ,
8785
8886 revoked_certs : pyo3:: sync:: PyOnceLock < Vec < OwnedRevokedCertificate > > ,
8987 cached_extensions : pyo3:: sync:: PyOnceLock < pyo3:: Py < pyo3:: PyAny > > ,
@@ -96,7 +94,7 @@ impl CertificateRevocationList {
9694
9795 fn revoked_cert ( & self , py : pyo3:: Python < ' _ > , idx : usize ) -> RevokedCertificate {
9896 RevokedCertificate {
99- owned : self . revoked_certs . get ( py) . unwrap ( ) [ idx] . clone ( ) ,
97+ owned : self . revoked_certs . get ( py) . unwrap ( ) [ idx] . clone_with_py ( py ) ,
10098 cached_extensions : pyo3:: sync:: PyOnceLock :: new ( ) ,
10199 }
102100 }
@@ -121,18 +119,15 @@ impl CertificateRevocationList {
121119 self . len ( )
122120 }
123121
124- fn __iter__ ( & self ) -> CRLIterator {
122+ fn __iter__ ( slf : pyo3:: PyRef < ' _ , Self > ) -> CRLIterator {
123+ let py = slf. py ( ) ;
125124 CRLIterator {
126- contents : OwnedCRLIteratorData :: try_new ( Arc :: clone ( & self . owned ) , |v| {
127- Ok :: < _ , ( ) > (
128- v. borrow_dependent ( )
129- . tbs_cert_list
130- . revoked_certificates
131- . as_ref ( )
132- . map ( |v| v. unwrap_read ( ) . clone ( ) ) ,
133- )
134- } )
135- . unwrap ( ) ,
125+ contents : map_crl_to_iterator_data ( & slf. owned , py, |crl| {
126+ crl. tbs_cert_list
127+ . revoked_certificates
128+ . as_ref ( )
129+ . map ( |v| v. unwrap_read ( ) . clone ( ) )
130+ } ) ,
136131 }
137132 }
138133
@@ -143,9 +138,24 @@ impl CertificateRevocationList {
143138 ) -> pyo3:: PyResult < pyo3:: Bound < ' p , pyo3:: PyAny > > {
144139 self . revoked_certs . get_or_init ( py, || {
145140 let mut revoked_certs = vec ! [ ] ;
146- let mut it = self . __iter__ ( ) ;
147- while let Some ( c) = it. __next__ ( ) {
148- revoked_certs. push ( c. owned ) ;
141+ let mut it_data = map_crl_to_iterator_data ( & self . owned , py, |crl| {
142+ crl. tbs_cert_list
143+ . revoked_certificates
144+ . as_ref ( )
145+ . map ( |v| v. unwrap_read ( ) . clone ( ) )
146+ } ) ;
147+ loop {
148+ let revoked = try_map_arc_data_mut_crl_iterator ( py, & mut it_data, |v| match v {
149+ Some ( v) => match v. next ( ) {
150+ Some ( revoked) => Ok ( revoked) ,
151+ None => Err ( ( ) ) ,
152+ } ,
153+ None => Err ( ( ) ) ,
154+ } ) ;
155+ match revoked {
156+ Ok ( owned) => revoked_certs. push ( owned) ,
157+ Err ( ( ) ) => break ,
158+ }
149159 }
150160 revoked_certs
151161 } ) ;
@@ -379,27 +389,22 @@ impl CertificateRevocationList {
379389 serial : pyo3:: Bound < ' _ , pyo3:: types:: PyInt > ,
380390 ) -> pyo3:: PyResult < Option < RevokedCertificate > > {
381391 let serial_bytes = py_uint_to_big_endian_bytes ( py, serial) ?;
382- let owned = OwnedRevokedCertificate :: try_new ( Arc :: clone ( & self . owned ) , |v| {
383- let certs = match & v. borrow_dependent ( ) . tbs_cert_list . revoked_certificates {
384- Some ( certs) => certs. unwrap_read ( ) . clone ( ) ,
385- None => return Err ( ( ) ) ,
386- } ;
392+
393+ // Use try_map_crl_to_revoked_cert to soundly extract the certificate
394+ let owned = try_map_crl_to_revoked_cert ( & self . owned , py, |crl| {
395+ let certs = crl. tbs_cert_list . revoked_certificates . as_ref ( ) ?;
387396
388397 // TODO: linear scan. Make a hash or bisect!
389- for cert in certs {
390- if serial_bytes == cert. user_certificate . as_bytes ( ) {
391- return Ok ( cert) ;
392- }
393- }
394- Err ( ( ) )
398+ certs
399+ . unwrap_read ( )
400+ . clone ( )
401+ . find ( |cert| serial_bytes == cert. user_certificate . as_bytes ( ) )
395402 } ) ;
396- match owned {
397- Ok ( o) => Ok ( Some ( RevokedCertificate {
398- owned : o,
399- cached_extensions : pyo3:: sync:: PyOnceLock :: new ( ) ,
400- } ) ) ,
401- Err ( ( ) ) => Ok ( None ) ,
402- }
403+
404+ Ok ( owned. map ( |o| RevokedCertificate {
405+ owned : o,
406+ cached_extensions : pyo3:: sync:: PyOnceLock :: new ( ) ,
407+ } ) )
403408 }
404409
405410 fn is_signature_valid < ' p > (
@@ -431,7 +436,7 @@ impl CertificateRevocationList {
431436type RawCRLIterator < ' a > = Option < asn1:: SequenceOf < ' a , crl:: RevokedCertificate < ' a > > > ;
432437self_cell:: self_cell!(
433438 struct OwnedCRLIteratorData {
434- owner: Arc < OwnedCertificateRevocationList >,
439+ owner: pyo3 :: Py <pyo3 :: types :: PyBytes >,
435440
436441 #[ covariant]
437442 dependent: RawCRLIterator ,
@@ -443,22 +448,96 @@ struct CRLIterator {
443448 contents : OwnedCRLIteratorData ,
444449}
445450
451+ // Open-coded implementation of the API discussed in
452+ // https://github.com/joshua-maros/ouroboros/issues/38
453+ fn map_crl_to_iterator_data < F > (
454+ source : & OwnedCertificateRevocationList ,
455+ py : pyo3:: Python < ' _ > ,
456+ f : F ,
457+ ) -> OwnedCRLIteratorData
458+ where
459+ F : for < ' a > FnOnce (
460+ & ' a RawCertificateRevocationList < ' a > ,
461+ ) -> Option < asn1:: SequenceOf < ' a , crl:: RevokedCertificate < ' a > > > ,
462+ {
463+ OwnedCRLIteratorData :: new ( source. borrow_owner ( ) . clone_ref ( py) , |_| {
464+ // SAFETY: This is safe because cloning the PyBytes Py<> ensures the data is
465+ // alive, but Rust doesn't understand the lifetime relationship it
466+ // produces.
467+ f ( unsafe {
468+ std:: mem:: transmute :: <
469+ & RawCertificateRevocationList < ' _ > ,
470+ & RawCertificateRevocationList < ' _ > ,
471+ > ( source. borrow_dependent ( ) )
472+ } )
473+ } )
474+ }
475+
476+ // Open-coded implementation of the API discussed in
477+ // https://github.com/joshua-maros/ouroboros/issues/38
478+ fn try_map_crl_to_revoked_cert < F > (
479+ source : & OwnedCertificateRevocationList ,
480+ py : pyo3:: Python < ' _ > ,
481+ f : F ,
482+ ) -> Option < OwnedRevokedCertificate >
483+ where
484+ F : for < ' a > FnOnce ( & ' a RawCertificateRevocationList < ' a > ) -> Option < RawRevokedCertificate < ' a > > ,
485+ {
486+ OwnedRevokedCertificate :: try_new ( source. borrow_owner ( ) . clone_ref ( py) , |_| {
487+ // SAFETY: This is safe because cloning the PyBytes Py<> ensures the data is
488+ // alive, but Rust doesn't understand the lifetime relationship it
489+ // produces.
490+ match f ( unsafe {
491+ std:: mem:: transmute :: <
492+ & RawCertificateRevocationList < ' _ > ,
493+ & RawCertificateRevocationList < ' _ > ,
494+ > ( source. borrow_dependent ( ) )
495+ } ) {
496+ Some ( cert) => Ok ( cert) ,
497+ None => Err ( ( ) ) ,
498+ }
499+ } )
500+ . ok ( )
501+ }
502+
503+ // Open-coded implementation of the API discussed in
504+ // https://github.com/joshua-maros/ouroboros/issues/38
505+ fn map_revoked_cert < F > (
506+ source : & OwnedRevokedCertificate ,
507+ py : pyo3:: Python < ' _ > ,
508+ f : F ,
509+ ) -> OwnedRevokedCertificate
510+ where
511+ F : for < ' a > FnOnce ( & ' a crl:: RevokedCertificate < ' a > ) -> crl:: RevokedCertificate < ' a > ,
512+ {
513+ OwnedRevokedCertificate :: 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+ f ( unsafe {
518+ std:: mem:: transmute :: < & crl:: RevokedCertificate < ' _ > , & crl:: RevokedCertificate < ' _ > > (
519+ source. borrow_dependent ( ) ,
520+ )
521+ } )
522+ } )
523+ }
524+
446525// Open-coded implementation of the API discussed in
447526// https://github.com/joshua-maros/ouroboros/issues/38
448527fn try_map_arc_data_mut_crl_iterator < E > (
528+ py : pyo3:: Python < ' _ > ,
449529 it : & mut OwnedCRLIteratorData ,
450530 f : impl for < ' this > FnOnce (
451- & ' this OwnedCertificateRevocationList ,
452531 & mut Option < asn1:: SequenceOf < ' this , crl:: RevokedCertificate < ' this > > > ,
453532 ) -> Result < crl:: RevokedCertificate < ' this > , E > ,
454533) -> Result < OwnedRevokedCertificate , E > {
455- OwnedRevokedCertificate :: try_new ( Arc :: clone ( it. borrow_owner ( ) ) , |inner_it | {
534+ OwnedRevokedCertificate :: try_new ( it. borrow_owner ( ) . clone_ref ( py ) , |_pybytes | {
456535 it. with_dependent_mut ( |_, value| {
457- // SAFETY: This is safe because `Arc::clone` ensures the data is
536+ // SAFETY: This is safe because cloning the PyBytes Py<> ensures the data is
458537 // alive, but Rust doesn't understand the lifetime relationship it
459538 // produces. Open-coded implementation of the API discussed in
460539 // https://github.com/joshua-maros/ouroboros/issues/38
461- f ( inner_it , unsafe {
540+ f ( unsafe {
462541 std:: mem:: transmute :: <
463542 & mut Option < asn1:: SequenceOf < ' _ , crl:: RevokedCertificate < ' _ > > > ,
464543 & mut Option < asn1:: SequenceOf < ' _ , crl:: RevokedCertificate < ' _ > > > ,
@@ -481,8 +560,11 @@ impl CRLIterator {
481560 slf
482561 }
483562
484- fn __next__ ( & mut self ) -> Option < RevokedCertificate > {
485- let revoked = try_map_arc_data_mut_crl_iterator ( & mut self . contents , |_data, v| match v {
563+ fn __next__ (
564+ & mut self ,
565+ py : pyo3:: Python < ' _ > ,
566+ ) -> Option < RevokedCertificate > {
567+ let revoked = try_map_arc_data_mut_crl_iterator ( py, & mut self . contents , |v| match v {
486568 Some ( v) => match v. next ( ) {
487569 Some ( revoked) => Ok ( revoked) ,
488570 None => Err ( ( ) ) ,
@@ -499,21 +581,15 @@ impl CRLIterator {
499581
500582self_cell:: self_cell!(
501583 struct OwnedRevokedCertificate {
502- owner: Arc < OwnedCertificateRevocationList >,
584+ owner: pyo3 :: Py <pyo3 :: types :: PyBytes >,
503585 #[ covariant]
504586 dependent: RawRevokedCertificate ,
505587 }
506588) ;
507589
508- impl Clone for OwnedRevokedCertificate {
509- fn clone ( & self ) -> OwnedRevokedCertificate {
510- // SAFETY: This is safe because `Arc::clone` ensures the data is
511- // alive, but Rust doesn't understand the lifetime relationship it
512- // produces. Open-coded implementation of the API discussed in
513- // https://github.com/joshua-maros/ouroboros/issues/38
514- OwnedRevokedCertificate :: new ( Arc :: clone ( self . borrow_owner ( ) ) , |_| unsafe {
515- std:: mem:: transmute ( self . borrow_dependent ( ) . clone ( ) )
516- } )
590+ impl OwnedRevokedCertificate {
591+ fn clone_with_py ( & self , py : pyo3:: Python < ' _ > ) -> OwnedRevokedCertificate {
592+ map_revoked_cert ( self , py, |cert| cert. clone ( ) )
517593 }
518594}
519595
0 commit comments