diff --git a/src/rust/src/x509/certificate.rs b/src/rust/src/x509/certificate.rs index 1201180d5..d4a540cd1 100644 --- a/src/rust/src/x509/certificate.rs +++ b/src/rust/src/x509/certificate.rs @@ -33,11 +33,10 @@ self_cell::self_cell!( } ); -// TODO: can't be frozen because extensions takes `&mut self` -#[pyo3::prelude::pyclass(module = "cryptography.hazmat.bindings._rust.x509")] +#[pyo3::prelude::pyclass(frozen, module = "cryptography.hazmat.bindings._rust.x509")] pub(crate) struct Certificate { pub(crate) raw: OwnedCertificate, - pub(crate) cached_extensions: Option, + pub(crate) cached_extensions: pyo3::once_cell::GILOnceCell, } #[pyo3::prelude::pymethods] @@ -248,11 +247,11 @@ impl Certificate { } #[getter] - fn extensions(&mut self, py: pyo3::Python<'_>) -> pyo3::PyResult { + fn extensions(&self, py: pyo3::Python<'_>) -> pyo3::PyResult { let x509_module = py.import(pyo3::intern!(py, "cryptography.x509"))?; x509::parse_and_cache_extensions( py, - &mut self.cached_extensions, + &self.cached_extensions, &self.raw.borrow_dependent().tbs_cert.raw_extensions, |ext| match ext.extn_id { oid::PRECERT_POISON_OID => { @@ -386,7 +385,7 @@ fn load_der_x509_certificate( Ok(Certificate { raw, - cached_extensions: None, + cached_extensions: pyo3::once_cell::GILOnceCell::new(), }) } diff --git a/src/rust/src/x509/common.rs b/src/rust/src/x509/common.rs index 81bf25326..3c64b2f68 100644 --- a/src/rust/src/x509/common.rs +++ b/src/rust/src/x509/common.rs @@ -387,48 +387,46 @@ pub(crate) fn parse_and_cache_extensions< F: Fn(&Extension<'_>) -> Result, CryptographyError>, >( py: pyo3::Python<'p>, - cached_extensions: &mut Option, + cached_extensions: &pyo3::once_cell::GILOnceCell, raw_extensions: &Option>, parse_ext: F, ) -> pyo3::PyResult { - if let Some(cached) = cached_extensions { - return Ok(cached.clone_ref(py)); - } + cached_extensions + .get_or_try_init(py, || { + let extensions = match Extensions::from_raw_extensions(raw_extensions.as_ref()) { + Ok(extensions) => extensions, + Err(DuplicateExtensionsError(oid)) => { + let oid_obj = oid_to_py_oid(py, &oid)?; + return Err(exceptions::DuplicateExtension::new_err(( + format!("Duplicate {} extension found", &oid), + oid_obj.into_py(py), + ))); + } + }; - let extensions = match Extensions::from_raw_extensions(raw_extensions.as_ref()) { - Ok(extensions) => extensions, - Err(DuplicateExtensionsError(oid)) => { - let oid_obj = oid_to_py_oid(py, &oid)?; - return Err(exceptions::DuplicateExtension::new_err(( - format!("Duplicate {} extension found", &oid), - oid_obj.into_py(py), - ))); - } - }; + let x509_module = py.import(pyo3::intern!(py, "cryptography.x509"))?; + let exts = pyo3::types::PyList::empty(py); + for raw_ext in extensions.iter() { + let oid_obj = oid_to_py_oid(py, &raw_ext.extn_id)?; - let x509_module = py.import(pyo3::intern!(py, "cryptography.x509"))?; - let exts = pyo3::types::PyList::empty(py); - for raw_ext in extensions.iter() { - let oid_obj = oid_to_py_oid(py, &raw_ext.extn_id)?; - - let extn_value = match parse_ext(&raw_ext)? { - Some(e) => e, - None => x509_module.call_method1( - pyo3::intern!(py, "UnrecognizedExtension"), - (oid_obj, raw_ext.extn_value), - )?, - }; - let ext_obj = x509_module.call_method1( - pyo3::intern!(py, "Extension"), - (oid_obj, raw_ext.critical, extn_value), - )?; - exts.append(ext_obj)?; - } - let extensions = x509_module - .call_method1(pyo3::intern!(py, "Extensions"), (exts,))? - .to_object(py); - *cached_extensions = Some(extensions.clone_ref(py)); - Ok(extensions) + let extn_value = match parse_ext(&raw_ext)? { + Some(e) => e, + None => x509_module.call_method1( + pyo3::intern!(py, "UnrecognizedExtension"), + (oid_obj, raw_ext.extn_value), + )?, + }; + let ext_obj = x509_module.call_method1( + pyo3::intern!(py, "Extension"), + (oid_obj, raw_ext.critical, extn_value), + )?; + exts.append(ext_obj)?; + } + Ok(x509_module + .call_method1(pyo3::intern!(py, "Extensions"), (exts,))? + .to_object(py)) + }) + .map(|p| p.clone_ref(py)) } pub(crate) fn encode_extensions< diff --git a/src/rust/src/x509/crl.rs b/src/rust/src/x509/crl.rs index dbbd1f912..126561a1d 100644 --- a/src/rust/src/x509/crl.rs +++ b/src/rust/src/x509/crl.rs @@ -43,7 +43,7 @@ fn load_der_x509_crl( Ok(CertificateRevocationList { owned: Arc::new(owned), revoked_certs: pyo3::once_cell::GILOnceCell::new(), - cached_extensions: None, + cached_extensions: pyo3::once_cell::GILOnceCell::new(), }) } @@ -71,13 +71,12 @@ self_cell::self_cell!( } ); -// TODO: can't be frozen because extensions required `&mut self`. -#[pyo3::prelude::pyclass(module = "cryptography.hazmat.bindings._rust.x509")] +#[pyo3::prelude::pyclass(frozen, module = "cryptography.hazmat.bindings._rust.x509")] struct CertificateRevocationList { owned: Arc, revoked_certs: pyo3::once_cell::GILOnceCell>, - cached_extensions: Option, + cached_extensions: pyo3::once_cell::GILOnceCell, } impl CertificateRevocationList { @@ -88,7 +87,7 @@ impl CertificateRevocationList { fn revoked_cert(&self, py: pyo3::Python<'_>, idx: usize) -> RevokedCertificate { RevokedCertificate { owned: self.revoked_certs.get(py).unwrap()[idx].clone(), - cached_extensions: None, + cached_extensions: pyo3::once_cell::GILOnceCell::new(), } } @@ -270,13 +269,13 @@ impl CertificateRevocationList { } #[getter] - fn extensions(&mut self, py: pyo3::Python<'_>) -> pyo3::PyResult { + fn extensions(&self, py: pyo3::Python<'_>) -> pyo3::PyResult { let tbs_cert_list = &self.owned.borrow_dependent().tbs_cert_list; let x509_module = py.import(pyo3::intern!(py, "cryptography.x509"))?; x509::parse_and_cache_extensions( py, - &mut self.cached_extensions, + &self.cached_extensions, &tbs_cert_list.raw_crl_extensions, |ext| match ext.extn_id { oid::CRL_NUMBER_OID => { @@ -359,7 +358,7 @@ impl CertificateRevocationList { } fn get_revoked_certificate_by_serial_number( - &mut self, + &self, py: pyo3::Python<'_>, serial: &pyo3::types::PyLong, ) -> pyo3::PyResult> { @@ -381,7 +380,7 @@ impl CertificateRevocationList { match owned { Ok(o) => Ok(Some(RevokedCertificate { owned: o, - cached_extensions: None, + cached_extensions: pyo3::once_cell::GILOnceCell::new(), })), Err(()) => Ok(None), } @@ -466,7 +465,7 @@ impl CRLIterator { .ok()?; Some(RevokedCertificate { owned: revoked, - cached_extensions: None, + cached_extensions: pyo3::once_cell::GILOnceCell::new(), }) } } @@ -491,11 +490,10 @@ impl Clone for OwnedRevokedCertificate { } } -// TODO: can't be frozen because extensions required `&mut self`. -#[pyo3::prelude::pyclass(module = "cryptography.hazmat.bindings._rust.x509")] +#[pyo3::prelude::pyclass(frozen, module = "cryptography.hazmat.bindings._rust.x509")] struct RevokedCertificate { owned: OwnedRevokedCertificate, - cached_extensions: Option, + cached_extensions: pyo3::once_cell::GILOnceCell, } #[pyo3::prelude::pymethods] @@ -517,10 +515,10 @@ impl RevokedCertificate { } #[getter] - fn extensions(&mut self, py: pyo3::Python<'_>) -> pyo3::PyResult { + fn extensions(&self, py: pyo3::Python<'_>) -> pyo3::PyResult { x509::parse_and_cache_extensions( py, - &mut self.cached_extensions, + &self.cached_extensions, &self.owned.borrow_dependent().raw_crl_entry_extensions, |ext| parse_crl_entry_ext(py, ext), ) diff --git a/src/rust/src/x509/csr.rs b/src/rust/src/x509/csr.rs index 62bf6e330..d0a27705e 100644 --- a/src/rust/src/x509/csr.rs +++ b/src/rust/src/x509/csr.rs @@ -22,11 +22,10 @@ self_cell::self_cell!( } ); -// TODO: can't be frozen extensions take `&mut self` -#[pyo3::prelude::pyclass(module = "cryptography.hazmat.bindings._rust.x509")] +#[pyo3::prelude::pyclass(frozen, module = "cryptography.hazmat.bindings._rust.x509")] struct CertificateSigningRequest { raw: OwnedCsr, - cached_extensions: Option, + cached_extensions: pyo3::once_cell::GILOnceCell, } #[pyo3::prelude::pymethods] @@ -179,7 +178,7 @@ impl CertificateSigningRequest { } #[getter] - fn attributes<'p>(&mut self, py: pyo3::Python<'p>) -> pyo3::PyResult<&'p pyo3::PyAny> { + fn attributes<'p>(&self, py: pyo3::Python<'p>) -> pyo3::PyResult<&'p pyo3::PyAny> { let pyattrs = pyo3::types::PyList::empty(py); for attribute in self .raw @@ -212,7 +211,7 @@ impl CertificateSigningRequest { } #[getter] - fn extensions(&mut self, py: pyo3::Python<'_>) -> pyo3::PyResult { + fn extensions(&self, py: pyo3::Python<'_>) -> pyo3::PyResult { let raw_exts = self .raw .borrow_dependent() @@ -224,7 +223,7 @@ impl CertificateSigningRequest { ) })?; - x509::parse_and_cache_extensions(py, &mut self.cached_extensions, &raw_exts, |ext| { + x509::parse_and_cache_extensions(py, &self.cached_extensions, &raw_exts, |ext| { certificate::parse_cert_ext(py, ext) }) } @@ -283,7 +282,7 @@ fn load_der_x509_csr( Ok(CertificateSigningRequest { raw, - cached_extensions: None, + cached_extensions: pyo3::once_cell::GILOnceCell::new(), }) } diff --git a/src/rust/src/x509/ocsp_req.rs b/src/rust/src/x509/ocsp_req.rs index d22064207..38704613f 100644 --- a/src/rust/src/x509/ocsp_req.rs +++ b/src/rust/src/x509/ocsp_req.rs @@ -45,16 +45,15 @@ fn load_der_ocsp_request( Ok(OCSPRequest { raw, - cached_extensions: None, + cached_extensions: pyo3::once_cell::GILOnceCell::new(), }) } -// TODO: can't be frozen because extensions takes `&mut self` -#[pyo3::prelude::pyclass(module = "cryptography.hazmat.bindings._rust.ocsp")] +#[pyo3::prelude::pyclass(frozen, module = "cryptography.hazmat.bindings._rust.ocsp")] struct OCSPRequest { raw: OwnedOCSPRequest, - cached_extensions: Option, + cached_extensions: pyo3::once_cell::GILOnceCell, } impl OCSPRequest { @@ -112,13 +111,13 @@ impl OCSPRequest { } #[getter] - fn extensions(&mut self, py: pyo3::Python<'_>) -> pyo3::PyResult { + fn extensions(&self, py: pyo3::Python<'_>) -> pyo3::PyResult { let tbs_request = &self.raw.borrow_dependent().tbs_request; let x509_module = py.import(pyo3::intern!(py, "cryptography.x509"))?; x509::parse_and_cache_extensions( py, - &mut self.cached_extensions, + &self.cached_extensions, &tbs_request.raw_request_extensions, |ext| { match ext.extn_id { diff --git a/src/rust/src/x509/ocsp_resp.rs b/src/rust/src/x509/ocsp_resp.rs index 4427ebf36..e6e8f7785 100644 --- a/src/rust/src/x509/ocsp_resp.rs +++ b/src/rust/src/x509/ocsp_resp.rs @@ -57,8 +57,8 @@ fn load_der_ocsp_response( }; Ok(OCSPResponse { raw: Arc::new(raw), - cached_extensions: None, - cached_single_extensions: None, + cached_extensions: pyo3::once_cell::GILOnceCell::new(), + cached_single_extensions: pyo3::once_cell::GILOnceCell::new(), }) } @@ -70,13 +70,12 @@ self_cell::self_cell!( } ); -// TODO: can't be frozen extensions and single_extensions take `&mut self` -#[pyo3::prelude::pyclass(module = "cryptography.hazmat.bindings._rust.ocsp")] +#[pyo3::prelude::pyclass(frozen, module = "cryptography.hazmat.bindings._rust.ocsp")] struct OCSPResponse { raw: Arc, - cached_extensions: Option, - cached_single_extensions: Option, + cached_extensions: pyo3::once_cell::GILOnceCell, + cached_single_extensions: pyo3::once_cell::GILOnceCell, } impl OCSPResponse { @@ -247,7 +246,7 @@ impl OCSPResponse { py, x509::certificate::Certificate { raw: raw_cert, - cached_extensions: None, + cached_extensions: pyo3::once_cell::GILOnceCell::new(), }, )?)?; } @@ -321,7 +320,7 @@ impl OCSPResponse { } #[getter] - fn extensions(&mut self, py: pyo3::Python<'_>) -> pyo3::PyResult { + fn extensions(&self, py: pyo3::Python<'_>) -> pyo3::PyResult { self.requires_successful_response()?; let response_data = &self @@ -337,7 +336,7 @@ impl OCSPResponse { let x509_module = py.import(pyo3::intern!(py, "cryptography.x509"))?; x509::parse_and_cache_extensions( py, - &mut self.cached_extensions, + &self.cached_extensions, &response_data.raw_response_extensions, |ext| { match &ext.extn_id { @@ -360,7 +359,7 @@ impl OCSPResponse { } #[getter] - fn single_extensions(&mut self, py: pyo3::Python<'_>) -> pyo3::PyResult { + fn single_extensions(&self, py: pyo3::Python<'_>) -> pyo3::PyResult { self.requires_successful_response()?; let single_resp = single_response( self.raw @@ -375,7 +374,7 @@ impl OCSPResponse { let x509_module = py.import(pyo3::intern!(py, "cryptography.x509"))?; x509::parse_and_cache_extensions( py, - &mut self.cached_single_extensions, + &self.cached_single_extensions, &single_resp.raw_single_extensions, |ext| match &ext.extn_id { &oid::SIGNED_CERTIFICATE_TIMESTAMPS_OID => {