From 58fbcdde0a6afe6976a3ef86ab1e99a0ce80a006 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Sat, 22 Aug 2026 02:33:47 +0200 Subject: [PATCH] deps: port node-forge to x509-cert 0.3 --- Cargo.lock | 35 +++---- crates/perry-ext-node-forge/Cargo.toml | 16 +-- crates/perry-ext-node-forge/src/crypto.rs | 113 +++++++++++++++------- crates/perry-stdlib/Cargo.toml | 6 +- crates/perry-stdlib/src/crypto/x509.rs | 92 ++++++++++-------- 5 files changed, 160 insertions(+), 102 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a71492aa2e..41bc540b6d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1747,8 +1747,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" dependencies = [ "const-oid 0.9.6", - "der_derive", - "flagset", "pem-rfc7468 0.7.0", "zeroize", ] @@ -1760,15 +1758,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71fd89660b2dc699704064e59e9dba0147b903e85319429e131620d022be411b" dependencies = [ "const-oid 0.10.2", + "der_derive", + "flagset", "pem-rfc7468 1.0.0", "zeroize", ] [[package]] name = "der_derive" -version = "0.7.3" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8034092389675178f570469e6c3b0465d3d30b4505c294a6550db47f3c17ad18" +checksum = "59600e2c2d636fde9b65e99cc6445ac770c63d3628195ff39932b8d6d7409903" dependencies = [ "proc-macro2", "quote", @@ -6072,17 +6072,17 @@ dependencies = [ name = "perry-ext-node-forge" version = "0.5.1516" dependencies = [ - "const-oid 0.9.6", - "der 0.7.10", + "const-oid 0.10.2", + "der 0.8.0", "pem 4.0.0", "perry-ffi", "perry-runtime", - "rand 0.8.7", - "rsa 0.9.10", + "rand 0.10.1", + "rsa 0.10.0-rc.18", "serde", "serde_json", - "sha2 0.10.9", - "spki 0.7.3", + "sha2 0.11.0", + "spki 0.8.0", "time", "x509-cert", ] @@ -7640,6 +7640,7 @@ dependencies = [ "pkcs1 0.8.0-rc.4", "pkcs8 0.11.0", "rand_core 0.10.1", + "sha2 0.11.0", "signature 3.0.0", "spki 0.8.0", "zeroize", @@ -11029,15 +11030,15 @@ dependencies = [ [[package]] name = "x509-cert" -version = "0.2.5" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1301e935010a701ae5f8655edc0ad17c44bad3ac5ce8c39185f75453b720ae94" +checksum = "105ef4642d9cb137ef83d623d0e4bf08b8adf69e9918ca904a174adb6d3d038b" dependencies = [ - "const-oid 0.9.6", - "der 0.7.10", - "sha1 0.10.6", - "signature 2.2.0", - "spki 0.7.3", + "const-oid 0.10.2", + "der 0.8.0", + "sha1 0.11.0", + "signature 3.0.0", + "spki 0.8.0", "tls_codec", ] diff --git a/crates/perry-ext-node-forge/Cargo.toml b/crates/perry-ext-node-forge/Cargo.toml index d9cafbe2be..a6c40a8f02 100644 --- a/crates/perry-ext-node-forge/Cargo.toml +++ b/crates/perry-ext-node-forge/Cargo.toml @@ -16,15 +16,15 @@ perry-ffi.workspace = true perry-runtime = { workspace = true, features = ["default", "stdlib"] } serde = { workspace = true } serde_json = { workspace = true } -# RustCrypto PKI stack — all already present in the workspace Cargo.lock. -rsa = { version = "0.9", features = ["sha2", "pem"] } -sha2 = "0.10" -x509-cert = { version = "0.2", features = ["builder", "hazmat"] } -der = { version = "0.7", features = ["oid"] } -spki = "0.7" -const-oid = "0.9" +# RustCrypto PKI stack. +rsa = { version = "0.10.0-rc.18", features = ["sha2"] } +sha2 = "0.11" +x509-cert = { version = "0.3", features = ["builder", "hazmat"] } +der = { version = "0.8", features = ["oid"] } +spki = "0.8" +const-oid = "0.10" pem = "4" -rand = "0.8" +rand = "0.10" time = { version = "0.3", features = ["parsing"] } [dev-dependencies] diff --git a/crates/perry-ext-node-forge/src/crypto.rs b/crates/perry-ext-node-forge/src/crypto.rs index 3aa9e07520..ec6b01e00a 100644 --- a/crates/perry-ext-node-forge/src/crypto.rs +++ b/crates/perry-ext-node-forge/src/crypto.rs @@ -17,7 +17,7 @@ use std::str::FromStr; use const_oid::ObjectIdentifier; -use der::asn1::{Ia5String, OctetString, SetOfVec, Utf8StringRef}; +use der::asn1::{Ia5String, OctetString, Utf8StringRef}; use der::flagset::FlagSet; use der::{Any, Decode, DecodePem, Encode, EncodePem}; use rsa::pkcs1::{DecodeRsaPrivateKey, EncodeRsaPrivateKey}; @@ -26,16 +26,18 @@ use rsa::pkcs8::{DecodePublicKey, EncodePublicKey}; use rsa::{RsaPrivateKey, RsaPublicKey}; use sha2::Sha256; use x509_cert::attr::AttributeTypeAndValue; -use x509_cert::builder::{Builder, CertificateBuilder, Profile}; +use x509_cert::builder::profile::BuilderProfile; +use x509_cert::builder::{Builder, CertificateBuilder}; +use x509_cert::certificate::TbsCertificate; use x509_cert::ext::pkix::constraints::BasicConstraints; use x509_cert::ext::pkix::name::{GeneralName, GeneralNames}; use x509_cert::ext::pkix::{ ExtendedKeyUsage, KeyUsage, KeyUsages, SubjectAltName, SubjectKeyIdentifier, }; -use x509_cert::ext::AsExtension; +use x509_cert::ext::{Criticality, Extension}; use x509_cert::name::{Name, RdnSequence, RelativeDistinguishedName}; use x509_cert::serial_number::SerialNumber; -use x509_cert::spki::SubjectPublicKeyInfoOwned; +use x509_cert::spki::{SubjectPublicKeyInfoOwned, SubjectPublicKeyInfoRef}; use x509_cert::time::{Time, Validity}; // ── OIDs ──────────────────────────────────────────────────────────── @@ -139,7 +141,7 @@ pub struct CertSpec { /// Generate an RSA keypair, returning `(privatePkcs1Pem, publicSpkiPem)`. pub fn generate_key_pair(bits: usize) -> Result<(String, String), String> { let bits = if bits == 0 { 2048 } else { bits }; - let mut rng = rand::thread_rng(); + let mut rng = rand::rng(); let priv_key = RsaPrivateKey::new(&mut rng, bits).map_err(|e| e.to_string())?; let pub_key = RsaPublicKey::from(&priv_key); let priv_pem = priv_key @@ -225,7 +227,7 @@ fn name_for(oid: &ObjectIdentifier) -> String { /// the leaf issuer from `certificateFromPem(ca).subject.attributes` — /// so `parse_name` (below) must be the exact inverse of this. fn build_name(attrs: &[Attr]) -> Result { - let mut rdns = Vec::with_capacity(attrs.len()); + let mut rdns = RdnSequence::default(); for a in attrs { let oid = oid_for(&a.key)?; let value = match a.value_tag.unwrap_or(DnValueTag::Utf8) { @@ -238,17 +240,17 @@ fn build_name(attrs: &[Attr]) -> Result { } }; let atv = AttributeTypeAndValue { oid, value }; - let set = SetOfVec::try_from(vec![atv]).map_err(|e| e.to_string())?; - rdns.push(RelativeDistinguishedName(set)); + let rdn = RelativeDistinguishedName::try_from(vec![atv]).map_err(|e| e.to_string())?; + rdns.push(rdn); } - Ok(RdnSequence(rdns)) + Ok(Name::hazmat_from_rdn_sequence(rdns)) } /// Parse a `Name` back into forge-shaped attributes, preserving order. pub fn parse_name(name: &Name) -> Vec { let mut out = Vec::new(); - for rdn in name.0.iter() { - for atv in rdn.0.iter() { + for rdn in name.iter_rdn() { + for atv in rdn.iter() { let decoded = atv .value .decode_as::>() @@ -370,12 +372,38 @@ impl Encode for ExtensionWithCritical<'_, E> { } } -impl AsExtension for ExtensionWithCritical<'_, E> { - fn critical(&self, _subject: &Name, _extensions: &[x509_cert::ext::Extension]) -> bool { +impl Criticality for ExtensionWithCritical<'_, E> { + fn criticality(&self, _subject: &Name, _extensions: &[Extension]) -> bool { self.critical } } +/// Builder profile matching x509-cert 0.2's `Profile::Manual`: use the +/// caller-provided names and inject no extensions of our own. +struct ManualProfile { + subject: Name, + issuer: Name, +} + +impl BuilderProfile for ManualProfile { + fn get_issuer(&self, _subject: &Name) -> Name { + self.issuer.clone() + } + + fn get_subject(&self) -> Name { + self.subject.clone() + } + + fn build_extensions( + &self, + _spk: SubjectPublicKeyInfoRef<'_>, + _issuer_spk: SubjectPublicKeyInfoRef<'_>, + _tbs: &TbsCertificate, + ) -> x509_cert::builder::Result> { + Ok(Vec::new()) + } +} + /// Build and sign a certificate. `signer_private_key_pem` is the /// ISSUER's private key (for a self-signed CA it is the same key whose /// public half is in `spec.public_key_pem`). @@ -386,10 +414,10 @@ pub fn build_and_sign(spec: &CertSpec, signer_private_key_pem: &str) -> Result Result Result() + .build::<_, rsa::pkcs1v15::Signature>(&signing_key) .map_err(|e| e.to_string())?; cert.to_pem(der::pem::LineEnding::LF) .map_err(|e| e.to_string()) @@ -473,7 +498,7 @@ fn compute_ski(spki_der: &[u8]) -> Result { /// `certificateFromPem(...).subject.attributes`). pub fn cert_subject_attrs(pem: &str) -> Result, String> { let cert = x509_cert::Certificate::from_pem(pem).map_err(|e| e.to_string())?; - Ok(parse_name(&cert.tbs_certificate.subject)) + Ok(parse_name(cert.tbs_certificate().subject())) } #[cfg(test)] @@ -552,13 +577,25 @@ mod tests { #[test] fn self_signed_ca_builds_and_parses() { let (priv_pem, pub_pem) = generate_key_pair(2048).unwrap(); - let ca_pem = build_and_sign(&ca_spec(&pub_pem), &priv_pem).unwrap(); + let spec = ca_spec(&pub_pem); + let ca_pem = build_and_sign(&spec, &priv_pem).unwrap(); assert!(ca_pem.contains("-----BEGIN CERTIFICATE-----")); let attrs = cert_subject_attrs(&ca_pem).unwrap(); // Order-preserving round-trip: CN first, O second. assert_eq!(attrs[0].key, "commonName"); assert_eq!(attrs[0].value, "Socket Security CA"); assert_eq!(attrs[1].key, "organizationName"); + + let cert = x509_cert::Certificate::from_pem(&ca_pem).unwrap(); + let validity = cert.tbs_certificate().validity(); + assert_eq!( + validity.not_before.to_unix_duration().as_secs(), + spec.not_before_unix as u64 + ); + assert_eq!( + validity.not_after.to_unix_duration().as_secs(), + spec.not_after_unix as u64 + ); } #[test] @@ -568,12 +605,21 @@ mod tests { let (priv_pem, pub_pem) = generate_key_pair(2048).unwrap(); let ca_pem = build_and_sign(&ca_spec(&pub_pem), &priv_pem).unwrap(); let ca = x509_cert::Certificate::from_pem(&ca_pem).unwrap(); - let parsed_attrs = parse_name(&ca.tbs_certificate.subject); - let rebuilt = build_name(&parsed_attrs).unwrap(); + let parsed_attrs = parse_name(ca.tbs_certificate().subject()); + + let mut leaf_spec = ca_spec(&pub_pem); + leaf_spec.subject = vec![Attr { + key: "commonName".into(), + value: "leaf.example".into(), + value_tag: None, + }]; + leaf_spec.issuer = parsed_attrs; + let leaf_pem = build_and_sign(&leaf_spec, &priv_pem).unwrap(); + let leaf = x509_cert::Certificate::from_pem(&leaf_pem).unwrap(); assert_eq!( - rebuilt.to_der().unwrap(), - ca.tbs_certificate.subject.to_der().unwrap(), - "rebuilt issuer DN must match CA subject DN exactly" + leaf.tbs_certificate().issuer().to_der().unwrap(), + ca.tbs_certificate().subject().to_der().unwrap(), + "leaf issuer DN must match CA subject DN exactly" ); } @@ -611,7 +657,8 @@ mod tests { spec.extensions.key_usage.as_mut().unwrap().critical = false; let pem = build_and_sign(&spec, &priv_pem).unwrap(); let cert = x509_cert::Certificate::from_pem(&pem).unwrap(); - let extensions = cert.tbs_certificate.extensions.as_ref().unwrap(); + let extensions = cert.tbs_certificate().extensions().unwrap(); + assert_eq!(extensions.len(), 3, "profile must not inject extensions"); let basic_constraints = extensions .iter() .find(|ext| ext.extn_id == BasicConstraints::OID) diff --git a/crates/perry-stdlib/Cargo.toml b/crates/perry-stdlib/Cargo.toml index ffcd946aad..5a3dbe0f85 100644 --- a/crates/perry-stdlib/Cargo.toml +++ b/crates/perry-stdlib/Cargo.toml @@ -407,9 +407,9 @@ p256 = { version = "0.13", optional = true, default-features = false, features = p384 = { version = "0.13", optional = true, default-features = false, features = ["pkcs8", "pem", "ecdsa", "ecdh"] } p521 = { version = "0.13", optional = true, default-features = false, features = ["pkcs8", "pem", "ecdsa", "ecdh"] } rsa = { version = "0.9", optional = true, default-features = false, features = ["pem", "pkcs5", "sha2"] } -# #1367 node:crypto X509Certificate — DER/PEM cert parsing (RustCrypto, reuses -# the der/spki/const-oid already in the lock). -x509-cert = { version = "0.2", optional = true, default-features = false, features = ["pem"] } +# #1367 node:crypto X509Certificate — DER/PEM cert parsing. x509-cert 0.3 uses +# the DER/SPKI 0.8 stack; the direct SPKI 0.7 pin remains for jsonwebtoken. +x509-cert = { version = "0.3", optional = true, default-features = false, features = ["pem"] } spki = { version = "0.7", optional = true, default-features = false, features = ["pem", "alloc"] } aes = { version = "0.8", optional = true } aes_09 = { package = "aes", version = "0.9", optional = true } diff --git a/crates/perry-stdlib/src/crypto/x509.rs b/crates/perry-stdlib/src/crypto/x509.rs index f5c7d1866e..7b62959f31 100644 --- a/crates/perry-stdlib/src/crypto/x509.rs +++ b/crates/perry-stdlib/src/crypto/x509.rs @@ -31,8 +31,8 @@ pub(super) fn x509_attr_short_name(oid: &str) -> String { /// do: one `TYPE=value` per line, newline-joined, in encoding order. pub(super) fn x509_format_name(name: &x509_cert::name::Name) -> String { let mut lines: Vec = Vec::new(); - for rdn in name.0.iter() { - for atv in rdn.0.iter() { + for rdn in name.iter_rdn() { + for atv in rdn.iter() { let oid = atv.oid.to_string(); let value = x509_attr_value(atv); lines.push(format!("{}={}", x509_attr_short_name(&oid), value)); @@ -107,9 +107,8 @@ fn x509_find_extension<'a>( cert: &'a x509_cert::Certificate, oid: &str, ) -> Option<&'a x509_cert::ext::Extension> { - cert.tbs_certificate - .extensions - .as_ref()? + cert.tbs_certificate() + .extensions()? .iter() .find(|ext| ext.extn_id.to_string() == oid) } @@ -166,8 +165,8 @@ fn x509_decoded_subject_alt_name( fn x509_subject_common_names(cert: &x509_cert::Certificate) -> Vec { let mut names = Vec::new(); - for rdn in cert.tbs_certificate.subject.0.iter() { - for atv in rdn.0.iter() { + for rdn in cert.tbs_certificate().subject().iter_rdn() { + for atv in rdn.iter() { if atv.oid.to_string() == "2.5.4.3" { names.push(x509_attr_value(atv)); } @@ -185,8 +184,8 @@ enum X509HostSubject { fn x509_subject_email_addresses(cert: &x509_cert::Certificate) -> Vec { let mut addresses = Vec::new(); - for rdn in cert.tbs_certificate.subject.0.iter() { - for atv in rdn.0.iter() { + for rdn in cert.tbs_certificate().subject().iter_rdn() { + for atv in rdn.iter() { if atv.oid.to_string() == "1.2.840.113549.1.9.1" { addresses.push(x509_attr_value(atv)); } @@ -499,10 +498,10 @@ fn x509_name_der(name: &x509_cert::name::Name) -> Option> { fn x509_check_issued_value(cert: &x509_cert::Certificate, issuer: &x509_cert::Certificate) -> bool { use x509_cert::der::Encode; - let Some(issuer_name) = x509_name_der(&cert.tbs_certificate.issuer) else { + let Some(issuer_name) = x509_name_der(cert.tbs_certificate().issuer()) else { return false; }; - let Some(subject_name) = x509_name_der(&issuer.tbs_certificate.subject) else { + let Some(subject_name) = x509_name_der(issuer.tbs_certificate().subject()) else { return false; }; if issuer_name != subject_name { @@ -515,13 +514,13 @@ fn x509_check_issued_value(cert: &x509_cert::Certificate, issuer: &x509_cert::Ce let Some((_, public_key)) = x509_rsa_public_key(issuer) else { return false; }; - let Some(signature_bytes) = cert.signature.as_bytes() else { + let Some(signature_bytes) = cert.signature().as_bytes() else { return false; }; let Ok(signature) = RsaPkcs1v15Signature::try_from(signature_bytes) else { return false; }; - let Ok(tbs_der) = cert.tbs_certificate.to_der() else { + let Ok(tbs_der) = cert.tbs_certificate().to_der() else { return false; }; @@ -586,7 +585,12 @@ fn x509_public_key_pem(spki_der: &[u8]) -> String { unsafe fn x509_public_key_value(handle: &X509Handle) -> f64 { use x509_cert::der::Encode; - let spki_der = match handle.cert.tbs_certificate.subject_public_key_info.to_der() { + let spki_der = match handle + .cert + .tbs_certificate() + .subject_public_key_info() + .to_der() + { Ok(der) => der, Err(_) => return nanbox_undefined(), }; @@ -599,7 +603,7 @@ unsafe fn x509_public_key_value(handle: &X509Handle) -> f64 { } fn x509_signature_algorithm_oid(cert: &x509_cert::Certificate) -> String { - cert.signature_algorithm.oid.to_string() + cert.signature_algorithm().oid.to_string() } fn x509_signature_algorithm_name(cert: &x509_cert::Certificate) -> Option<&'static str> { @@ -616,7 +620,7 @@ fn x509_signature_algorithm_name(cert: &x509_cert::Certificate) -> Option<&'stat } fn x509_rsa_signature_digest(cert: &x509_cert::Certificate) -> Option { - match cert.signature_algorithm.oid.to_string().as_str() { + match cert.signature_algorithm().oid.to_string().as_str() { "1.2.840.113549.1.1.11" => Some(RsaDigestKind::Sha256), "1.2.840.113549.1.1.12" => Some(RsaDigestKind::Sha384), "1.2.840.113549.1.1.13" => Some(RsaDigestKind::Sha512), @@ -625,14 +629,12 @@ fn x509_rsa_signature_digest(cert: &x509_cert::Certificate) -> Option f64 { - let attr_count = name.0.iter().map(|rdn| rdn.0.len()).sum::() as u32; + let attr_count = name.iter().count() as u32; let obj = js_object_alloc(0, attr_count); - for rdn in name.0.iter() { - for atv in rdn.0.iter() { - let key = x509_attr_short_name(&atv.oid.to_string()); - let value = x509_attr_value(atv); - set_object_string_field(obj, key.as_bytes(), &value); - } + for atv in name.iter() { + let key = x509_attr_short_name(&atv.oid.to_string()); + let value = x509_attr_value(atv); + set_object_string_field(obj, key.as_bytes(), &value); } nanbox_ptr(obj) } @@ -640,15 +642,19 @@ unsafe fn x509_name_legacy_object(name: &x509_cert::name::Name) -> f64 { fn x509_rsa_public_key(cert: &x509_cert::Certificate) -> Option<(Vec, RsaPublicKey)> { use x509_cert::der::Encode; - let spki_der = cert.tbs_certificate.subject_public_key_info.to_der().ok()?; + let spki_der = cert + .tbs_certificate() + .subject_public_key_info() + .to_der() + .ok()?; let pem = x509_public_key_pem(&spki_der); let public_key = parse_rsa_public_key_pem(&pem)?; Some((spki_der, public_key)) } fn x509_serial_number_hex(cert: &x509_cert::Certificate) -> String { - cert.tbs_certificate - .serial_number + cert.tbs_certificate() + .serial_number() .as_bytes() .iter() .map(|b| format!("{:02X}", b)) @@ -671,10 +677,10 @@ unsafe fn x509_to_legacy_object(handle: &X509Handle) -> f64 { use sha2::{Digest, Sha256, Sha512}; let obj = js_object_alloc(0, 20); - let tbs = &handle.cert.tbs_certificate; + let tbs = handle.cert.tbs_certificate(); - set_object_value_field(obj, b"subject", x509_name_legacy_object(&tbs.subject)); - set_object_value_field(obj, b"issuer", x509_name_legacy_object(&tbs.issuer)); + set_object_value_field(obj, b"subject", x509_name_legacy_object(tbs.subject())); + set_object_value_field(obj, b"issuer", x509_name_legacy_object(tbs.issuer())); match x509_subject_alt_name(&handle.cert) { Some(value) => set_object_string_field(obj, b"subjectaltname", &value), None => set_undefined_field(obj, b"subjectaltname"), @@ -713,9 +719,13 @@ unsafe fn x509_to_legacy_object(handle: &X509Handle) -> f64 { set_object_string_field( obj, b"valid_from", - &x509_format_time(&tbs.validity.not_before), + &x509_format_time(&tbs.validity().not_before), + ); + set_object_string_field( + obj, + b"valid_to", + &x509_format_time(&tbs.validity().not_after), ); - set_object_string_field(obj, b"valid_to", &x509_format_time(&tbs.validity.not_after)); set_object_string_field( obj, b"fingerprint", @@ -848,13 +858,13 @@ unsafe fn x509_verify_value(handle: &X509Handle, args: &[f64]) -> f64 { Some(key) => key, None => return js_bool(false), }; - let tbs_der = match handle.cert.tbs_certificate.to_der() { + let tbs_der = match handle.cert.tbs_certificate().to_der() { Ok(der) => der, Err(_) => return js_bool(false), }; let signature = match handle .cert - .signature + .signature() .as_bytes() .and_then(|bytes| RsaPkcs1v15Signature::try_from(bytes).ok()) { @@ -1017,17 +1027,17 @@ pub unsafe fn dispatch_x509_property(handle: i64, property: &str) -> f64 { Some(h) => h, None => return nanbox_undefined(), }; - let tbs = &h.cert.tbs_certificate; + let tbs = h.cert.tbs_certificate(); match property { - "subject" => x509_string_f64(&x509_format_name(&tbs.subject)), - "issuer" => x509_string_f64(&x509_format_name(&tbs.issuer)), - "validFrom" => x509_string_f64(&x509_format_time(&tbs.validity.not_before)), - "validTo" => x509_string_f64(&x509_format_time(&tbs.validity.not_after)), + "subject" => x509_string_f64(&x509_format_name(tbs.subject())), + "issuer" => x509_string_f64(&x509_format_name(tbs.issuer())), + "validFrom" => x509_string_f64(&x509_format_time(&tbs.validity().not_before)), + "validTo" => x509_string_f64(&x509_format_time(&tbs.validity().not_after)), "validFromDate" => perry_runtime::date::js_date_new_from_timestamp(x509_time_millis( - &tbs.validity.not_before, + &tbs.validity().not_before, )), "validToDate" => perry_runtime::date::js_date_new_from_timestamp(x509_time_millis( - &tbs.validity.not_after, + &tbs.validity().not_after, )), "serialNumber" => x509_string_f64(&x509_serial_number_hex(&h.cert)), "signatureAlgorithm" => match x509_signature_algorithm_name(&h.cert) { @@ -1170,7 +1180,7 @@ pub unsafe fn dispatch_x509_method_property(handle: i64, property: &str) -> f64 /// Extract the BasicConstraints `cA` flag (default false when absent). pub(super) fn x509_basic_constraints_ca(cert: &x509_cert::Certificate) -> bool { use x509_cert::der::Decode; - let Some(exts) = cert.tbs_certificate.extensions.as_ref() else { + let Some(exts) = cert.tbs_certificate().extensions() else { return false; }; for ext in exts.iter() {