Skip to content

Commit

Permalink
Keep the original DER for an SPKI around (#10058)
Browse files Browse the repository at this point in the history
This lets us parse it without needing to re-serialize.

Eventually we can extend this to TBS data itself.
  • Loading branch information
alex authored Dec 30, 2023
1 parent 8cee865 commit 201851a
Show file tree
Hide file tree
Showing 6 changed files with 66 additions and 6 deletions.
2 changes: 1 addition & 1 deletion src/rust/cryptography-x509/src/certificate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ pub struct TbsCertificate<'a> {
pub validity: Validity,
pub subject: name::Name<'a>,

pub spki: common::SubjectPublicKeyInfo<'a>,
pub spki: common::WithTlv<'a, common::SubjectPublicKeyInfo<'a>>,
#[implicit(1)]
pub issuer_unique_id: Option<asn1::BitString<'a>>,
#[implicit(2)]
Expand Down
62 changes: 61 additions & 1 deletion src/rust/cryptography-x509/src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -354,9 +354,61 @@ impl<'a> asn1::SimpleAsn1Writable for UnvalidatedVisibleString<'a> {
}
}

#[derive(Clone)]
pub struct WithTlv<'a, T> {
tlv: asn1::Tlv<'a>,
value: T,
}

impl<'a, T> WithTlv<'a, T> {
pub fn tlv(&self) -> &asn1::Tlv<'a> {
&self.tlv
}
}

impl<T> std::ops::Deref for WithTlv<'_, T> {
type Target = T;

fn deref(&self) -> &Self::Target {
&self.value
}
}

impl<'a, T: asn1::Asn1Readable<'a>> asn1::Asn1Readable<'a> for WithTlv<'a, T> {
fn parse(p: &mut asn1::Parser<'a>) -> asn1::ParseResult<Self> {
let tlv = p.read_element::<asn1::Tlv<'a>>()?;
Ok(Self {
tlv,
value: tlv.parse()?,
})
}

fn can_parse(t: asn1::Tag) -> bool {
T::can_parse(t)
}
}

impl<'a, T: asn1::Asn1Writable> asn1::Asn1Writable for WithTlv<'a, T> {
fn write(&self, w: &mut asn1::Writer<'_>) -> asn1::WriteResult<()> {
self.value.write(w)
}
}

impl<T: PartialEq> PartialEq for WithTlv<'_, T> {
fn eq(&self, other: &Self) -> bool {
self.value == other.value
}
}
impl<T: Eq> Eq for WithTlv<'_, T> {}
impl<T: std::hash::Hash> std::hash::Hash for WithTlv<'_, T> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.value.hash(state)
}
}

#[cfg(test)]
mod tests {
use super::{Asn1ReadableOrWritable, RawTlv, UnvalidatedVisibleString};
use super::{Asn1ReadableOrWritable, RawTlv, UnvalidatedVisibleString, WithTlv};
use asn1::Asn1Readable;

#[test]
Expand All @@ -383,4 +435,12 @@ mod tests {
let t = asn1::Tag::from_bytes(&[0]).unwrap().0;
assert!(RawTlv::can_parse(t));
}

#[test]
fn test_with_raw_tlv_can_parse() {
let t = asn1::Tag::from_bytes(&[0x30]).unwrap().0;

assert!(WithTlv::<asn1::Sequence<'_>>::can_parse(t));
assert!(!WithTlv::<bool>::can_parse(t));
}
}
2 changes: 1 addition & 1 deletion src/rust/cryptography-x509/src/csr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ pub struct Csr<'a> {
pub struct CertificationRequestInfo<'a> {
pub version: u8,
pub subject: name::Name<'a>,
pub spki: common::SubjectPublicKeyInfo<'a>,
pub spki: common::WithTlv<'a, common::SubjectPublicKeyInfo<'a>>,
#[implicit(0, required)]
pub attributes: Attributes<'a>,
}
Expand Down
2 changes: 1 addition & 1 deletion src/rust/src/x509/certificate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ impl Certificate {
// This makes an unnecessary copy. It'd be nice to get rid of it.
let serialized = pyo3::types::PyBytes::new(
py,
&asn1::write_single(&self.raw.borrow_dependent().tbs_cert.spki)?,
self.raw.borrow_dependent().tbs_cert.spki.tlv().full_data(),
);
Ok(types::LOAD_DER_PUBLIC_KEY.get(py)?.call1((serialized,))?)
}
Expand Down
2 changes: 1 addition & 1 deletion src/rust/src/x509/csr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ impl CertificateSigningRequest {
// This makes an unnecessary copy. It'd be nice to get rid of it.
let serialized = pyo3::types::PyBytes::new(
py,
&asn1::write_single(&self.raw.borrow_dependent().csr_info.spki)?,
self.raw.borrow_dependent().csr_info.spki.tlv().full_data(),
);
Ok(types::LOAD_DER_PUBLIC_KEY.get(py)?.call1((serialized,))?)
}
Expand Down
2 changes: 1 addition & 1 deletion src/rust/src/x509/verify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ impl CryptoOps for PyCryptoOps {
fn public_key(&self, cert: &Certificate<'_>) -> Result<Self::Key, Self::Err> {
pyo3::Python::with_gil(|py| -> Result<Self::Key, Self::Err> {
// This makes an unnecessary copy. It'd be nice to get rid of it.
let spki_der = pyo3::types::PyBytes::new(py, &asn1::write_single(&cert.tbs_cert.spki)?);
let spki_der = pyo3::types::PyBytes::new(py, cert.tbs_cert.spki.tlv().full_data());

Ok(types::LOAD_DER_PUBLIC_KEY
.get(py)?
Expand Down

0 comments on commit 201851a

Please sign in to comment.