diff options
| author | Paul Kehrer <paul.l.kehrer@gmail.com> | 2023-04-17 05:45:25 +0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-04-16 17:45:25 -0400 |
| commit | 3e40017b5fff43b2bc3be774eca47c0643e97649 (patch) | |
| tree | f5dc6fc955a9d885d81c0f279f8ccae7a2b0bf60 /src/rust | |
| parent | 9c09a67204223578896026035ce877d7ea2ffdf4 (diff) | |
| download | cryptography-3e40017b5fff43b2bc3be774eca47c0643e97649.tar.gz | |
begin separation of x509 crate from cryptography crate (#8740)
* begin separation of x509 crate from cryptography crate
this will not be a published crate for now and the separation is
incomplete.
* no more rawcertificate, no more re-exporting
* rename RawCsr
* rename rawcrl
* port ocsprequest and rename
* more raw renaming
* switch to a workspace, rename
* remove unneeded imports
* add license headers, remove more unneeded imports
* coverage
* this should actually be possible iwth just --all
* merge all the coverage files
* path fix
* one last guess
* coverage
* remove extra definition
Diffstat (limited to 'src/rust')
25 files changed, 1093 insertions, 962 deletions
diff --git a/src/rust/Cargo.lock b/src/rust/Cargo.lock index c16a7fcec..1fc04cecd 100644 --- a/src/rust/Cargo.lock +++ b/src/rust/Cargo.lock @@ -70,6 +70,7 @@ version = "0.1.0" dependencies = [ "asn1", "cc", + "cryptography-x509", "foreign-types-shared", "once_cell", "openssl", @@ -80,6 +81,13 @@ dependencies = [ ] [[package]] +name = "cryptography-x509" +version = "0.1.0" +dependencies = [ + "asn1", +] + +[[package]] name = "foreign-types" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/src/rust/Cargo.toml b/src/rust/Cargo.toml index 3175cd12b..e96b1fc2b 100644 --- a/src/rust/Cargo.toml +++ b/src/rust/Cargo.toml @@ -11,6 +11,7 @@ rust-version = "1.56.0" once_cell = "1" pyo3 = { version = "0.18" } asn1 = { version = "0.14.0", default-features = false } +cryptography-x509 = { path = "cryptography-x509" } pem = "1.1" ouroboros = "0.15" openssl = "0.10.50" @@ -31,3 +32,6 @@ crate-type = ["cdylib"] [profile.release] lto = "thin" overflow-checks = true + +[workspace] +members = ["cryptography-x509"] diff --git a/src/rust/cryptography-x509/Cargo.toml b/src/rust/cryptography-x509/Cargo.toml new file mode 100644 index 000000000..b062ddbbf --- /dev/null +++ b/src/rust/cryptography-x509/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "cryptography-x509" +version = "0.1.0" +authors = ["The cryptography developers <cryptography-dev@python.org>"] +edition = "2021" +publish = false +# This specifies the MSRV +rust-version = "1.56.0" + +[dependencies] +asn1 = { version = "0.14.0", default-features = false } diff --git a/src/rust/cryptography-x509/src/certificate.rs b/src/rust/cryptography-x509/src/certificate.rs new file mode 100644 index 000000000..bb9a666f5 --- /dev/null +++ b/src/rust/cryptography-x509/src/certificate.rs @@ -0,0 +1,41 @@ +// This file is dual licensed under the terms of the Apache License, Version +// 2.0, and the BSD License. See the LICENSE file in the root of this repository +// for complete details. + +use crate::common; +use crate::extensions; +use crate::name; + +#[derive(asn1::Asn1Read, asn1::Asn1Write, Hash, PartialEq, Clone)] +pub struct Certificate<'a> { + pub tbs_cert: TbsCertificate<'a>, + pub signature_alg: common::AlgorithmIdentifier<'a>, + pub signature: asn1::BitString<'a>, +} + +#[derive(asn1::Asn1Read, asn1::Asn1Write, Hash, PartialEq, Clone)] +pub struct TbsCertificate<'a> { + #[explicit(0)] + #[default(0)] + pub version: u8, + pub serial: asn1::BigInt<'a>, + pub signature_alg: common::AlgorithmIdentifier<'a>, + + pub issuer: name::Name<'a>, + pub validity: Validity, + pub subject: name::Name<'a>, + + pub spki: common::SubjectPublicKeyInfo<'a>, + #[implicit(1)] + pub issuer_unique_id: Option<asn1::BitString<'a>>, + #[implicit(2)] + pub subject_unique_id: Option<asn1::BitString<'a>>, + #[explicit(3)] + pub extensions: Option<extensions::Extensions<'a>>, +} + +#[derive(asn1::Asn1Read, asn1::Asn1Write, Hash, PartialEq, Clone)] +pub struct Validity { + pub not_before: common::Time, + pub not_after: common::Time, +} diff --git a/src/rust/cryptography-x509/src/common.rs b/src/rust/cryptography-x509/src/common.rs new file mode 100644 index 000000000..13fcb3368 --- /dev/null +++ b/src/rust/cryptography-x509/src/common.rs @@ -0,0 +1,142 @@ +// This file is dual licensed under the terms of the Apache License, Version +// 2.0, and the BSD License. See the LICENSE file in the root of this repository +// for complete details. + +use std::marker::PhantomData; + +#[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Hash, Clone)] +pub struct AlgorithmIdentifier<'a> { + pub oid: asn1::ObjectIdentifier, + pub params: Option<asn1::Tlv<'a>>, +} + +#[derive(asn1::Asn1Read, asn1::Asn1Write, Hash, PartialEq, Clone)] +pub struct SubjectPublicKeyInfo<'a> { + _algorithm: AlgorithmIdentifier<'a>, + pub subject_public_key: asn1::BitString<'a>, +} + +#[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Eq, Hash, Clone)] +pub struct AttributeTypeValue<'a> { + pub type_id: asn1::ObjectIdentifier, + pub value: RawTlv<'a>, +} + +// Like `asn1::Tlv` but doesn't store `full_data` so it can be constructed from +// an un-encoded tag and value. +#[derive(Hash, PartialEq, Eq, Clone)] +pub struct RawTlv<'a> { + tag: asn1::Tag, + value: &'a [u8], +} + +impl<'a> RawTlv<'a> { + pub fn new(tag: asn1::Tag, value: &'a [u8]) -> Self { + RawTlv { tag, value } + } + + pub fn tag(&self) -> asn1::Tag { + self.tag + } + pub fn data(&self) -> &'a [u8] { + self.value + } +} +impl<'a> asn1::Asn1Readable<'a> for RawTlv<'a> { + fn parse(parser: &mut asn1::Parser<'a>) -> asn1::ParseResult<Self> { + let tlv = parser.read_element::<asn1::Tlv<'a>>()?; + Ok(RawTlv::new(tlv.tag(), tlv.data())) + } + + fn can_parse(_tag: asn1::Tag) -> bool { + true + } +} +impl<'a> asn1::Asn1Writable for RawTlv<'a> { + fn write(&self, w: &mut asn1::Writer<'_>) -> asn1::WriteResult { + w.write_tlv(self.tag, move |dest| dest.push_slice(self.value)) + } +} + +#[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Hash, Clone)] +pub enum Time { + UtcTime(asn1::UtcTime), + GeneralizedTime(asn1::GeneralizedTime), +} + +impl Time { + pub fn as_datetime(&self) -> &asn1::DateTime { + match self { + Time::UtcTime(data) => data.as_datetime(), + Time::GeneralizedTime(data) => data.as_datetime(), + } + } +} + +#[derive(Hash, PartialEq, Clone)] +pub enum Asn1ReadableOrWritable<'a, T, U> { + Read(T, PhantomData<&'a ()>), + Write(U, PhantomData<&'a ()>), +} + +impl<'a, T, U> Asn1ReadableOrWritable<'a, T, U> { + pub fn new_read(v: T) -> Self { + Asn1ReadableOrWritable::Read(v, PhantomData) + } + + pub fn new_write(v: U) -> Self { + Asn1ReadableOrWritable::Write(v, PhantomData) + } + + pub fn unwrap_read(&self) -> &T { + match self { + Asn1ReadableOrWritable::Read(v, _) => v, + Asn1ReadableOrWritable::Write(_, _) => panic!("unwrap_read called on a Write value"), + } + } +} + +impl<'a, T: asn1::SimpleAsn1Readable<'a>, U> asn1::SimpleAsn1Readable<'a> + for Asn1ReadableOrWritable<'a, T, U> +{ + const TAG: asn1::Tag = T::TAG; + fn parse_data(data: &'a [u8]) -> asn1::ParseResult<Self> { + Ok(Self::new_read(T::parse_data(data)?)) + } +} + +impl<'a, T: asn1::SimpleAsn1Writable, U: asn1::SimpleAsn1Writable> asn1::SimpleAsn1Writable + for Asn1ReadableOrWritable<'a, T, U> +{ + const TAG: asn1::Tag = U::TAG; + fn write_data(&self, w: &mut asn1::WriteBuf) -> asn1::WriteResult { + match self { + Asn1ReadableOrWritable::Read(v, _) => T::write_data(v, w), + Asn1ReadableOrWritable::Write(v, _) => U::write_data(v, w), + } + } +} + +#[cfg(test)] +mod tests { + use super::{Asn1ReadableOrWritable, RawTlv}; + use asn1::Asn1Readable; + + #[test] + #[should_panic] + fn test_asn1_readable_or_writable_unwrap_read() { + Asn1ReadableOrWritable::<u32, u32>::new_write(17).unwrap_read(); + } + + #[test] + fn test_asn1_readable_or_writable_write_read_data() { + let v = Asn1ReadableOrWritable::<u32, u32>::new_read(17); + assert_eq!(&asn1::write_single(&v).unwrap(), b"\x02\x01\x11"); + } + + #[test] + fn test_raw_tlv_can_parse() { + let t = asn1::Tag::from_bytes(&[0]).unwrap().0; + assert!(RawTlv::can_parse(t)); + } +} diff --git a/src/rust/cryptography-x509/src/crl.rs b/src/rust/cryptography-x509/src/crl.rs new file mode 100644 index 000000000..3a47e0a37 --- /dev/null +++ b/src/rust/cryptography-x509/src/crl.rs @@ -0,0 +1,69 @@ +// This file is dual licensed under the terms of the Apache License, Version +// 2.0, and the BSD License. See the LICENSE file in the root of this repository +// for complete details. + +use crate::{common, extensions, name}; + +pub type ReasonFlags<'a> = + Option<common::Asn1ReadableOrWritable<'a, asn1::BitString<'a>, asn1::OwnedBitString>>; + +#[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Hash)] +pub struct CertificateRevocationList<'a> { + pub tbs_cert_list: TBSCertList<'a>, + pub signature_algorithm: common::AlgorithmIdentifier<'a>, + pub signature_value: asn1::BitString<'a>, +} + +pub type RevokedCertificates<'a> = Option< + common::Asn1ReadableOrWritable< + 'a, + asn1::SequenceOf<'a, RevokedCertificate<'a>>, + asn1::SequenceOfWriter<'a, RevokedCertificate<'a>, Vec<RevokedCertificate<'a>>>, + >, +>; + +#[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Hash)] +pub struct TBSCertList<'a> { + pub version: Option<u8>, + pub signature: common::AlgorithmIdentifier<'a>, + pub issuer: name::Name<'a>, + pub this_update: common::Time, + pub next_update: Option<common::Time>, + pub revoked_certificates: RevokedCertificates<'a>, + #[explicit(0)] + pub crl_extensions: Option<extensions::Extensions<'a>>, +} + +#[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Hash, Clone)] +pub struct RevokedCertificate<'a> { + pub user_certificate: asn1::BigUint<'a>, + pub revocation_date: common::Time, + pub crl_entry_extensions: Option<extensions::Extensions<'a>>, +} + +#[derive(asn1::Asn1Read, asn1::Asn1Write)] +pub struct IssuingDistributionPoint<'a> { + #[explicit(0)] + pub distribution_point: Option<extensions::DistributionPointName<'a>>, + + #[implicit(1)] + #[default(false)] + pub only_contains_user_certs: bool, + + #[implicit(2)] + #[default(false)] + pub only_contains_ca_certs: bool, + + #[implicit(3)] + pub only_some_reasons: ReasonFlags<'a>, + + #[implicit(4)] + #[default(false)] + pub indirect_crl: bool, + + #[implicit(5)] + #[default(false)] + pub only_contains_attribute_certs: bool, +} + +pub type CRLReason = asn1::Enumerated; diff --git a/src/rust/cryptography-x509/src/csr.rs b/src/rust/cryptography-x509/src/csr.rs new file mode 100644 index 000000000..c23d22d0f --- /dev/null +++ b/src/rust/cryptography-x509/src/csr.rs @@ -0,0 +1,70 @@ +// This file is dual licensed under the terms of the Apache License, Version +// 2.0, and the BSD License. See the LICENSE file in the root of this repository +// for complete details. + +use crate::common; +use crate::extensions; +use crate::name; +use crate::oid; + +#[derive(asn1::Asn1Read, asn1::Asn1Write)] +pub struct Csr<'a> { + pub csr_info: CertificationRequestInfo<'a>, + pub signature_alg: common::AlgorithmIdentifier<'a>, + pub signature: asn1::BitString<'a>, +} + +#[derive(asn1::Asn1Read, asn1::Asn1Write)] +pub struct CertificationRequestInfo<'a> { + pub version: u8, + pub subject: name::Name<'a>, + pub spki: common::SubjectPublicKeyInfo<'a>, + #[implicit(0, required)] + pub attributes: Attributes<'a>, +} + +impl CertificationRequestInfo<'_> { + pub fn get_extension_attribute( + &self, + ) -> Result<Option<extensions::Extensions<'_>>, asn1::ParseError> { + for attribute in self.attributes.unwrap_read().clone() { + if attribute.type_id == oid::EXTENSION_REQUEST + || attribute.type_id == oid::MS_EXTENSION_REQUEST + { + check_attribute_length(attribute.values.unwrap_read().clone())?; + let val = attribute.values.unwrap_read().clone().next().unwrap(); + let exts = asn1::parse_single(val.full_data())?; + return Ok(Some(exts)); + } + } + Ok(None) + } +} + +pub fn check_attribute_length<'a>( + values: asn1::SetOf<'a, asn1::Tlv<'a>>, +) -> Result<(), asn1::ParseError> { + if values.count() > 1 { + // TODO: We should raise a more specific error here + // Only single-valued attributes are supported + Err(asn1::ParseError::new(asn1::ParseErrorKind::InvalidValue)) + } else { + Ok(()) + } +} + +pub type Attributes<'a> = common::Asn1ReadableOrWritable< + 'a, + asn1::SetOf<'a, Attribute<'a>>, + asn1::SetOfWriter<'a, Attribute<'a>, Vec<Attribute<'a>>>, +>; + +#[derive(asn1::Asn1Read, asn1::Asn1Write)] +pub struct Attribute<'a> { + pub type_id: asn1::ObjectIdentifier, + pub values: common::Asn1ReadableOrWritable< + 'a, + asn1::SetOf<'a, asn1::Tlv<'a>>, + asn1::SetOfWriter<'a, common::RawTlv<'a>, [common::RawTlv<'a>; 1]>, + >, +} diff --git a/src/rust/cryptography-x509/src/extensions.rs b/src/rust/cryptography-x509/src/extensions.rs new file mode 100644 index 000000000..11c6e54a4 --- /dev/null +++ b/src/rust/cryptography-x509/src/extensions.rs @@ -0,0 +1,175 @@ +// This file is dual licensed under the terms of the Apache License, Version +// 2.0, and the BSD License. See the LICENSE file in the root of this repository +// for complete details. + +use crate::common; +use crate::crl; +use crate::name; + +pub type Extensions<'a> = common::Asn1ReadableOrWritable< + 'a, + asn1::SequenceOf<'a, Extension<'a>>, + asn1::SequenceOfWriter<'a, Extension<'a>, Vec<Extension<'a>>>, +>; + +#[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Eq, Hash, Clone)] +pub struct Extension<'a> { + pub extn_id: asn1::ObjectIdentifier, + #[default(false)] + pub critical: bool, + pub extn_value: &'a [u8], +} + +#[derive(asn1::Asn1Read, asn1::Asn1Write)] +pub struct PolicyConstraints { + #[implicit(0)] + pub require_explicit_policy: Option<u64>, + #[implicit(1)] + pub inhibit_policy_mapping: Option<u64>, +} + +#[derive(asn1::Asn1Read, asn1::Asn1Write)] +pub struct AccessDescription<'a> { + pub access_method: asn1::ObjectIdentifier, + pub access_location: name::GeneralName<'a>, +} + +pub type SequenceOfAccessDescriptions<'a> = common::Asn1ReadableOrWritable< + 'a, + asn1::SequenceOf<'a, AccessDescription<'a>>, + asn1::SequenceOfWriter<'a, AccessDescription<'a>, Vec<AccessDescription<'a>>>, +>; + +// Needed due to clippy type complexity warning. +type SequenceOfPolicyQualifiers<'a> = common::Asn1ReadableOrWritable< + 'a, + asn1::SequenceOf<'a, PolicyQualifierInfo<'a>>, + asn1::SequenceOfWriter<'a, PolicyQualifierInfo<'a>, Vec<PolicyQualifierInfo<'a>>>, +>; + +#[derive(asn1::Asn1Read, asn1::Asn1Write)] +pub struct PolicyInformation<'a> { + pub policy_identifier: asn1::ObjectIdentifier, + pub policy_qualifiers: Option<SequenceOfPolicyQualifiers<'a>>, +} + +#[derive(asn1::Asn1Read, asn1::Asn1Write)] +pub struct PolicyQualifierInfo<'a> { + pub policy_qualifier_id: asn1::ObjectIdentifier, + pub qualifier: Qualifier<'a>, +} + +#[derive(asn1::Asn1Read, asn1::Asn1Write)] +pub enum Qualifier<'a> { + CpsUri(asn1::IA5String<'a>), + UserNotice(UserNotice<'a>), +} + +#[derive(asn1::Asn1Read, asn1::Asn1Write)] +pub struct UserNotice<'a> { + pub notice_ref: Option<NoticeReference<'a>>, + pub explicit_text: Option<DisplayText<'a>>, +} + +#[derive(asn1::Asn1Read, asn1::Asn1Write)] +pub struct NoticeReference<'a> { + pub organization: DisplayText<'a>, + pub notice_numbers: common::Asn1ReadableOrWritable< + 'a, + asn1::SequenceOf<'a, asn1::BigUint<'a>>, + asn1::SequenceOfWriter<'a, asn1::BigUint<'a>, Vec<asn1::BigUint<'a>>>, + >, +} + +// DisplayText also allows BMPString, which we currently do not support. +#[allow(clippy::enum_variant_names)] +#[derive(asn1::Asn1Read, asn1::Asn1Write)] +pub enum DisplayText<'a> { + IA5String(asn1::IA5String<'a>), + Utf8String(asn1::Utf8String<'a>), + VisibleString(asn1::VisibleString<'a>), + BmpString(asn1::BMPString<'a>), +} + +// Needed due to clippy type complexity warning. +pub type SequenceOfSubtrees<'a> = common::Asn1ReadableOrWritable< + 'a, + asn1::SequenceOf<'a, GeneralSubtree<'a>>, + asn1::SequenceOfWriter<'a, GeneralSubtree<'a>, Vec<GeneralSubtree<'a>>>, +>; + +#[derive(asn1::Asn1Read, asn1::Asn1Write)] +pub struct NameConstraints<'a> { + #[implicit(0)] + pub permitted_subtrees: Option<SequenceOfSubtrees<'a>>, + + #[implicit(1)] + pub excluded_subtrees: Option<SequenceOfSubtrees<'a>>, +} + +#[derive(asn1::Asn1Read, asn1::Asn1Write)] +pub struct GeneralSubtree<'a> { + pub base: name::GeneralName<'a>, + + #[implicit(0)] + #[default(0u64)] + pub minimum: u64, + + #[implicit(1)] + pub maximum: Option<u64>, +} + +#[derive(asn1::Asn1Read, asn1::Asn1Write)] +pub struct MSCertificateTemplate { + pub template_id: asn1::ObjectIdentifier, + pub major_version: Option<u32>, + pub minor_version: Option<u32>, +} + +#[derive(asn1::Asn1Read, asn1::Asn1Write)] +pub struct DistributionPoint<'a> { + #[explicit(0)] + pub distribution_point: Option<DistributionPointName<'a>>, + + #[implicit(1)] + pub reasons: crl::ReasonFlags<'a>, + + #[implicit(2)] + pub crl_issuer: Option<name::SequenceOfGeneralName<'a>>, +} + +#[derive(asn1::Asn1Read, asn1::Asn1Write)] +pub enum DistributionPointName<'a> { + #[implicit(0)] + FullName(name::SequenceOfGeneralName<'a>), + + #[implicit(1)] + NameRelativeToCRLIssuer( + common::Asn1ReadableOrWritable< + 'a, + asn1::SetOf<'a, common::AttributeTypeValue<'a>>, + asn1::SetOfWriter< + 'a, + common::AttributeTypeValue<'a>, + Vec<common::AttributeTypeValue<'a>>, + >, + >, + ), +} + +#[derive(asn1::Asn1Read, asn1::Asn1Write)] +pub struct AuthorityKeyIdentifier<'a> { + #[implicit(0)] + pub key_identifier: Option<&'a [u8]>, + #[implicit(1)] + pub authority_cert_issuer: Option<name::SequenceOfGeneralName<'a>>, + #[implicit(2)] + pub authority_cert_serial_number: Option<asn1::BigUint<'a>>, +} + +#[derive(asn1::Asn1Read, asn1::Asn1Write)] +pub struct BasicConstraints { + #[default(false)] + pub ca: bool, + pub path_length: Option<u64>, +} diff --git a/src/rust/cryptography-x509/src/lib.rs b/src/rust/cryptography-x509/src/lib.rs new file mode 100644 index 000000000..3f8878772 --- /dev/null +++ b/src/rust/cryptography-x509/src/lib.rs @@ -0,0 +1,14 @@ +// This file is dual licensed under the terms of the Apache License, Version +// 2.0, and the BSD License. See the LICENSE file in the root of this repository +// for complete details. + +#![forbid(unsafe_code)] + +pub mod certificate; +pub mod common; +pub mod crl; +pub mod csr; +pub mod extensions; +pub mod name; +pub mod ocsp_req; +pub mod oid; diff --git a/src/rust/cryptography-x509/src/name.rs b/src/rust/cryptography-x509/src/name.rs new file mode 100644 index 000000000..f53e342cb --- /dev/null +++ b/src/rust/cryptography-x509/src/name.rs @@ -0,0 +1,88 @@ +// This file is dual licensed under the terms of the Apache License, Version +// 2.0, and the BSD License. See the LICENSE file in the root of this repository +// for complete details. + +use crate::common; + +pub type Name<'a> = common::Asn1ReadableOrWritable< + 'a, + asn1::SequenceOf<'a, asn1::SetOf<'a, common::AttributeTypeValue<'a>>>, + asn1::SequenceOfWriter< + 'a, + asn1::SetOfWriter<'a, common::AttributeTypeValue<'a>, Vec<common::AttributeTypeValue<'a>>>, + Vec< + asn1::SetOfWriter< + 'a, + common::AttributeTypeValue<'a>, + Vec<common::AttributeTypeValue<'a>>, + >, + >, + >, +>; + +/// An IA5String ASN.1 element whose contents is not validated as meeting the +/// requirements (ASCII characters only), and instead is only known to be +/// valid UTF-8. +pub struct UnvalidatedIA5String<'a>(pub &'a str); + +impl<'a> asn1::SimpleAsn1Readable<'a> for UnvalidatedIA5String<'a> { + const TAG: asn1::Tag = asn1::IA5String::TAG; + fn parse_data(data: &'a [u8]) -> asn1::ParseResult<Self> { + Ok(UnvalidatedIA5String(std::str::from_utf8(data).map_err( + |_| asn1::ParseError::new(asn1::ParseErrorKind::InvalidValue), + )?)) + } +} + +impl<'a> asn1::SimpleAsn1Writable for UnvalidatedIA5String<'a> { + const TAG: asn1::Tag = asn1::IA5String::TAG; + fn write_data(&self, dest: &mut asn1::WriteBuf) -> asn1::WriteResult { + dest.push_slice(self.0.as_bytes()) + } +} + +#[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Hash)] +pub struct OtherName<'a> { + pub type_id: asn1::ObjectIdentifier, + #[explicit(0, required)] + pub value: asn1::Tlv<'a>, +} + +#[derive(asn1::Asn1Read, asn1::Asn1Write)] +pub enum GeneralName<'a> { + #[implicit(0)] + OtherName(OtherName<'a>), + + #[implicit(1)] + RFC822Name(UnvalidatedIA5String<'a>), + + #[implicit(2)] + DNSName(UnvalidatedIA5String<'a>), + + #[implicit(3)] + // unsupported + X400Address(asn1::Sequence<'a>), + + // Name is explicit per RFC 5280 Appendix A.1. + #[explicit(4)] + DirectoryName(Name<'a>), + + #[implicit(5)] + // unsupported + EDIPartyName(asn1::Sequence<'a>), + + #[implicit(6)] + UniformResourceIdentifier(UnvalidatedIA5String<'a>), + + #[implicit(7)] + IPAddress(&'a [u8]), + + #[implicit(8)] + RegisteredID(asn1::ObjectIdentifier), +} + +pub(crate) type SequenceOfGeneralName<'a> = common::Asn1ReadableOrWritable< + 'a, + asn1::SequenceOf<'a, GeneralName<'a>>, + asn1::SequenceOfWriter<'a, GeneralName<'a>, Vec<GeneralName<'a>>>, +>; diff --git a/src/rust/cryptography-x509/src/ocsp_req.rs b/src/rust/cryptography-x509/src/ocsp_req.rs new file mode 100644 index 000000000..1e096e71f --- /dev/null +++ b/src/rust/cryptography-x509/src/ocsp_req.rs @@ -0,0 +1,46 @@ +// This file is dual licensed under the terms of the Apache License, Version +// 2.0, and the BSD License. See the LICENSE file in the root of this repository +// for complete details. + +use crate::{common, extensions, name}; + +#[derive(asn1::Asn1Read, asn1::Asn1Write)] +pub struct TBSRequest<'a> { + #[explicit(0)] + #[default(0)] + pub version: u8, + #[explicit(1)] + pub requestor_name: Option<name::GeneralName<'a>>, + pub request_list: common::Asn1ReadableOrWritable< + 'a, + asn1::SequenceOf<'a, Request<'a>>, + asn1::SequenceOfWriter<'a, Request<'a>>, + >, + #[explicit(2)] + pub request_extensions: Option<extensions::Extensions<'a>>, +} + +#[derive(asn1::Asn1Read, asn1::Asn1Write)] +pub struct Request<'a> { + pub req_cert: CertID<'a>, + #[explicit(0)] + pub single_request_extensions: Option<extensions::Extensions<'a>>, +} + +#[derive(asn1::Asn1Read, asn1::Asn1Write)] +pub struct CertID<'a> { + pub hash_algorithm: common::AlgorithmIdentifier<'a>, + pub issuer_name_hash: &'a [u8], + pub issuer_key_hash: &'a [u8], + pub serial_number: asn1::BigInt<'a>, +} + +#[derive(asn1::Asn1Read, asn1::Asn1Write)] +pub struct OCSPRequest<'a> { + pub tbs_request: TBSRequest<'a>, + // Parsing out the full structure, which includes the entirety of a + // certificate is more trouble than it's worth, since it's not in the + // Python API. + #[explicit(0)] + pub optional_signature: Option<asn1::Sequence<'a>>, +} diff --git a/src/rust/cryptography-x509/src/oid.rs b/src/rust/cryptography-x509/src/oid.rs new file mode 100644 index 000000000..b2d22ebdd --- /dev/null +++ b/src/rust/cryptography-x509/src/oid.rs @@ -0,0 +1,86 @@ +// This file is dual licensed under the terms of the Apache License, Version +// 2.0, and the BSD License. See the LICENSE file in the root of this repository +// for complete details. + +pub const EXTENSION_REQUEST: asn1::ObjectIdentifier = asn1::oid!(1, 2, 840, 113549, 1, 9, 14); +pub const MS_EXTENSION_REQUEST: asn1::ObjectIdentifier = + asn1::oid!(1, 3, 6, 1, 4, 1, 311, 2, 1, 14); +pub const MS_CERTIFICATE_TEMPLATE: asn1::ObjectIdentifier = + asn1::oid!(1, 3, 6, 1, 4, 1, 311, 21, 7); +pub const PRECERT_SIGNED_CERTIFICATE_TIMESTAMPS_OID: asn1::ObjectIdentifier = + asn1::oid!(1, 3, 6, 1, 4, 1, 11129, 2, 4, 2); +pub const PRECERT_POISON_OID: asn1::ObjectIdentifier = asn1::oid!(1, 3, 6, 1, 4, 1, 11129, 2, 4, 3); +pub const SIGNED_CERTIFICATE_TIMESTAMPS_OID: asn1::ObjectIdentifier = + asn1::oid!(1, 3, 6, 1, 4, 1, 11129, 2, 4, 5); +pub const AUTHORITY_INFORMATION_ACCESS_OID: asn1::ObjectIdentifier = + asn1::oid!(1, 3, 6, 1, 5, 5, 7, 1, 1); +pub const SUBJECT_INFORMATION_ACCESS_OID: asn1::ObjectIdentifier = + asn1::oid!(1, 3, 6, 1, 5, 5, 7, 1, 11); +pub const TLS_FEATURE_OID: asn1::ObjectIdentifier = asn1::oid!(1, 3, 6, 1, 5, 5, 7, 1, 24); +pub const CP_CPS_URI_OID: asn1::ObjectIdentifier = asn1::oid!(1, 3, 6, 1, 5, 5, 7, 2, 1); +pub const CP_USER_NOTICE_OID: asn1::ObjectIdentifier = asn1::oid!(1, 3, 6, 1, 5, 5, 7, 2, 2); +pub const NONCE_OID: asn1::ObjectIdentifier = asn1::oid!(1, 3, 6, 1, 5, 5, 7, 48, 1, 2); +pub const OCSP_NO_CHECK_OID: asn1::ObjectIdentifier = asn1::oid!(1, 3, 6, 1, 5, 5, 7, 48, 1, 5); +pub const SUBJECT_KEY_IDENTIFIER_OID: asn1::ObjectIdentifier = asn1::oid!(2, 5, 29, 14); +pub const KEY_USAGE_OID: asn1::ObjectIdentifier = asn1::oid!(2, 5, 29, 15); +pub const SUBJECT_ALTERNATIVE_NAME_OID: asn1::ObjectIdentifier = asn1::oid!(2, 5, 29, 17); +pub const ISSUER_ALTERNATIVE_NAME_OID: asn1::ObjectIdentifier = asn1::oid!(2, 5, 29, 18); +pub const BASIC_CONSTRAINTS_OID: asn1::ObjectIdentifier = asn1::oid!(2, 5, 29, 19); +pub const CRL_NUMBER_OID: asn1::ObjectIdentifier = asn1::oid!(2, 5, 29, 20); +pub const CRL_REASON_OID: asn1::ObjectIdentifier = asn1::oid!(2, 5, 29, 21); +pub const INVALIDITY_DATE_OID: asn1::ObjectIdentifier = asn1::oid!(2, 5, 29, 24); +pub const DELTA_CRL_INDICATOR_OID: asn1::ObjectIdentifier = asn1::oid!(2, 5, 29, 27); +pub const ISSUING_DISTRIBUTION_POINT_OID: asn1::ObjectIdentifier = asn1::oid!(2, 5, 29, 28); +pub const CERTIFICATE_ISSUER_OID: asn1::ObjectIdentifier = asn1::oid!(2, 5, 29, 29); +pub const NAME_CONSTRAINTS_OID: asn1::ObjectIdentifier = asn1::oid!(2, 5, 29, 30); +pub const CRL_DISTRIBUTION_POINTS_OID: asn1::ObjectIdentifier = asn1::oid!(2, 5, 29, 31); +pub const CERTIFICATE_POLICIES_OID: asn1::ObjectIdentifier = asn1::oid!(2, 5, 29, 32); +pub const AUTHORITY_KEY_IDENTIFIER_OID: asn1::ObjectIdentifier = asn1::oid!(2, 5, 29, 35); +pub const POLICY_CONSTRAINTS_OID: asn1::ObjectIdentifier = asn1::oid!(2, 5, 29, 36); +pub const EXTENDED_KEY_USAGE_OID: asn1::ObjectIdentifier = asn1::oid!(2, 5, 29, 37); +pub const FRESHEST_CRL_OID: asn1::ObjectIdentifier = asn1::oid!(2, 5, 29, 46); +pub const INHIBIT_ANY_POLICY_OID: asn1::ObjectIdentifier = asn1::oid!(2, 5, 29, 54); +pub const ACCEPTABLE_RESPONSES_OID: asn1::ObjectIdentifier = + asn1::oid!(1, 3, 6, 1, 5, 5, 7, 48, 1, 4); + +// Signing methods +pub const ECDSA_WITH_SHA224_OID: asn1::ObjectIdentifier = asn1::oid!(1, 2, 840, 10045, 4, 3, 1); +pub const ECDSA_WITH_SHA256_OID: asn1::ObjectIdentifier = asn1::oid!(1, 2, 840, 10045, 4, 3, 2); +pub const ECDSA_WITH_SHA384_OID: asn1::ObjectIdentifier = asn1::oid!(1, 2, 840, 10045, 4, 3, 3); +pub const ECDSA_WITH_SHA512_OID: asn1::ObjectIdentifier = asn1::oid!(1, 2, 840, 10045, 4, 3, 4); +pub const ECDSA_WITH_SHA3_224_OID: asn1::ObjectIdentifier = + asn1::oid!(2, 16, 840, 1, 101, 3, 4, 3, 9); +pub const ECDSA_WITH_SHA3_256_OID: asn1::ObjectIdentifier = + asn1::oid!(2, 16, 840, 1, 101, 3, 4, 3, 10); +pub const ECDSA_WITH_SHA3_384_OID: asn1::ObjectIdentifier = + asn1::oid!(2, 16, 840, 1, 101, 3, 4, 3, 11); +pub const ECDSA_WITH_SHA3_512_OID: asn1::ObjectIdentifier = + asn1::oid!(2, 16, 840, 1, 101, 3, 4, 3, 12); + +pub const RSA_WITH_SHA224_OID: asn1::ObjectIdentifier = asn1::oid!(1, 2, 840, 113549, 1, 1, 14); +pub const RSA_WITH_SHA256_OID: asn1::ObjectIdentifier = asn1::oid!(1, 2, 840, 113549, 1, 1, 11); +pub const RSA_WITH_SHA384_OID: asn1::ObjectIdentifier = asn1::oid!(1, 2, 840, 113549, 1, 1, 12); +pub const RSA_WITH_SHA512_OID: asn1::ObjectIdentifier = asn1::oid!(1, 2, 840, 113549, 1, 1, 13); +pub const RSA_WITH_SHA3_224_OID: asn1::ObjectIdentifier = + asn1::oid!(2, 16, 840, 1, 101, 3, 4, 3, 13); +pub const RSA_WITH_SHA3_256_OID: asn1::ObjectIdentifier = + asn1::oid!(2, 16, 840, 1, 101, 3, 4, 3, 14); +pub const RSA_WITH_SHA3_384_OID: asn1::ObjectIdentifier = + asn1::oid!(2, 16, 840, 1, 101, 3, 4, 3, 15); +pub const RSA_WITH_SHA3_512_OID: asn1::ObjectIdentifier = + asn1::oid!(2, 16, 840, 1, 101, 3, 4, 3, 16); + +pub const DSA_WITH_SHA224_OID: asn1::ObjectIdentifier = asn1::oid!(2, 16, 840, 1, 101, 3, 4, 3, 1); +pub const DSA_WITH_SHA256_OID: asn1::ObjectIdentifier = asn1::oid!(2, 16, 840, 1, 101, 3, 4, 3, 2); +pub const DSA_WITH_SHA384_OID: asn1::ObjectIdentifier = asn1::oid!(2, 16, 840, 1, 101, 3, 4, 3, 3); +pub const DSA_WITH_SHA512_OID: asn1::ObjectIdentifier = asn1::oid!(2, 16, 840, 1, 101, 3, 4, 3, 4); + +pub const ED25519_OID: asn1::ObjectIdentifier = asn1::oid!(1, 3, 101, 112); +pub const ED448_OID: asn1::ObjectIdentifier = asn1::oid!(1, 3, 101, 113); + +// Hashes +pub const SHA1_OID: asn1::ObjectIdentifier = asn1::oid!(1, 3, 14, 3, 2, 26); +pub const SHA224_OID: asn1::ObjectIdentifier = asn1::oid!(2, 16, 840, 1, 101, 3, 4, 2, 4); +pub const SHA256_OID: asn1::ObjectIdentifier = asn1::oid!(2, 16, 840, 1, 101, 3, 4, 2, 1); +pub const SHA384_OID: asn1::ObjectIdentifier = asn1::oid!(2, 16, 840, 1, 101, 3, 4, 2, 2); +pub const SHA512_OID: asn1::ObjectIdentifier = asn1::oid!(2, 16, 840, 1, 101, 3, 4, 2, 3); diff --git a/src/rust/src/asn1.rs b/src/rust/src/asn1.rs index 53981ddac..cad48a73f 100644 --- a/src/rust/src/asn1.rs +++ b/src/rust/src/asn1.rs @@ -3,7 +3,7 @@ // for complete details. use crate::error::{CryptographyError, CryptographyResult}; -use crate::x509::Name; +use cryptography_x509::name::Name; use pyo3::basic::CompareOp; use pyo3::types::IntoPyDict; use pyo3::ToPyObject; diff --git a/src/rust/src/pkcs7.rs b/src/rust/src/pkcs7.rs index bb5161434..2fdb610e3 100644 --- a/src/rust/src/pkcs7.rs +++ b/src/rust/src/pkcs7.rs @@ -6,6 +6,8 @@ use crate::asn1::encode_der_data; use crate::buf::CffiBuf; use crate::error::CryptographyResult; use crate::x509; +use cryptography_x509::csr::{Attribute, Attributes}; +use cryptography_x509::{common, name, oid}; use once_cell::sync::Lazy; use std::borrow::Cow; @@ -26,10 +28,10 @@ const AES_128_CBC_OID: asn1::ObjectIdentifier = asn1::oid!(2, 16, 840, 1, 101, 3 static OIDS_TO_MIC_NAME: Lazy<HashMap<&asn1::ObjectIdentifier, &str>> = Lazy::new(|| { let mut h = HashMap::new(); - h.insert(&x509::oid::SHA224_OID, "sha-224"); - h.insert(&x509::oid::SHA256_OID, "sha-256"); - h.insert(&x509::oid::SHA384_OID, "sha-384"); - h.insert(&x509::oid::SHA512_OID, "sha-512"); + h.insert(&oid::SHA224_OID, "sha-224"); + h.insert(&oid::SHA256_OID, "sha-256"); + h.insert(&oid::SHA384_OID, "sha-384"); + h.insert(&oid::SHA512_OID, "sha-512"); h }); @@ -52,10 +54,11 @@ enum Content<'a> { #[derive(asn1::Asn1Write)] struct SignedData<'a> { version: u8, - digest_algorithms: asn1::SetOfWriter<'a, x509::AlgorithmIdentifier<'a>>, + digest_algorithms: asn1::SetOfWriter<'a, common::AlgorithmIdentifier<'a>>, content_info: ContentInfo<'a>, #[implicit(0)] - certificates: Option<asn1::SetOfWriter<'a, &'a x509::certificate::RawCertificate<'a>>>, + certificates: + Option<asn1::SetOfWriter<'a, &'a cryptography_x509::certificate::Certificate<'a>>>, // We don't ever supply any of these, so for now, don't fill out the fields. #[implicit(1)] @@ -68,27 +71,27 @@ struct SignedData<'a> { struct SignerInfo<'a> { version: u8, issuer_and_serial_number: IssuerAndSerialNumber<'a>, - digest_algorithm: x509::AlgorithmIdentifier<'a>, + digest_algorithm: common::AlgorithmIdentifier<'a>, #[implicit(0)] - authenticated_attributes: Option<x509::csr::Attributes<'a>>, + authenticated_attributes: Option<Attributes<'a>>, - digest_encryption_algorithm: x509::AlgorithmIdentifier<'a>, + digest_encryption_algorithm: common::AlgorithmIdentifier<'a>, encrypted_digest: &'a [u8], #[implicit(1)] - unauthenticated_attributes: Option<x509::csr::Attributes<'a>>, + unauthenticated_attributes: Option<Attributes<'a>>, } #[derive(asn1::Asn1Write)] struct IssuerAndSerialNumber<'a> { - issuer: x509::Name<'a>, + issuer: name::Name<'a>, serial_number: asn1::BigInt<'a>, } #[pyo3::prelude::pyfunction] fn serialize_certificates<'p>( py: pyo3::Python<'p>, - py_certs: Vec<pyo3::PyRef<'p, x509::Certificate>>, + py_certs: Vec<pyo3::PyRef<'p, x509::certificate::Certificate>>, encoding: &'p pyo3::PyAny, ) -> CryptographyResult<&'p pyo3::types::PyBytes> { if py_certs.is_empty() { @@ -163,12 +166,12 @@ fn sign_and_serialize<'p>( ]))?; let py_signers: Vec<( - pyo3::PyRef<'p, x509::Certificate>, + pyo3::PyRef<'p, x509::certificate::Certificate>, &pyo3::PyAny, &pyo3::PyAny, )> = builder.getattr(pyo3::intern!(py, "_signers"))?.extract()?; - let py_certs: Vec<pyo3::PyRef<'p, x509::Certificate>> = builder + let py_certs: Vec<pyo3::PyRef<'p, x509::certificate::Certificate>> = builder .getattr(pyo3::intern!(py, "_additional_certs"))? .extract()?; @@ -189,15 +192,15 @@ fn sign_and_serialize<'p>( } else { let mut authenticated_attrs = vec![]; - authenticated_attrs.push(x509::csr::Attribute { + authenticated_attrs.push(Attribute { type_id: PKCS7_CONTENT_TYPE_OID, - values: x509::Asn1ReadableOrWritable::new_write(asn1::SetOfWriter::new([ + values: common::Asn1ReadableOrWritable::new_write(asn1::SetOfWriter::new([ asn1::parse_single(&content_type_bytes).unwrap(), ])), }); - authenticated_attrs.push(x509::csr::Attribute { + authenticated_attrs.push(Attribute { type_id: PKCS7_SIGNING_TIME_OID, - values: x509::Asn1ReadableOrWritable::new_write(asn1::SetOfWriter::new([ + values: common::Asn1ReadableOrWritable::new_write(asn1::SetOfWriter::new([ asn1::parse_single(&signing_time_bytes).unwrap(), ])), }); @@ -206,17 +209,17 @@ fn sign_and_serialize<'p>( asn1::write_single(&x509::ocsp::hash_data(py, py_hash_alg, &data_with_header)?)?; // Gross hack: copy to PyBytes to extend the lifetime to 'p let digest_bytes = pyo3::types::PyBytes::new(py, &digest); - authenticated_attrs.push(x509::csr::Attribute { + authenticated_attrs.push(Attribute { type_id: PKCS7_MESSAGE_DIGEST_OID, - values: x509::Asn1ReadableOrWritable::new_write(asn1::SetOfWriter::new([ + values: common::Asn1ReadableOrWritable::new_write(asn1::SetOfWriter::new([ asn1::parse_single(digest_bytes.as_bytes()).unwrap(), ])), }); if !options.contains(pkcs7_options.getattr(pyo3::intern!(py, "NoCapabilities"))?)? { - authenticated_attrs.push(x509::csr::Attribute { + authenticated_attrs.push(Attribute { type_id: PKCS7_SMIME_CAP_OID, - values: x509::Asn1ReadableOrWritable::new_write(asn1::SetOfWriter::new([ + values: common::Asn1ReadableOrWritable::new_write(asn1::SetOfWriter::new([ asn1::parse_single(&smime_cap_bytes).unwrap(), ])), }); @@ -226,14 +229,14 @@ fn sign_and_serialize<'p>( asn1::write_single(&asn1::SetOfWriter::new(authenticated_attrs.as_slice()))?; ( - Some(x509::Asn1ReadableOrWritable::new_write( + Some(common::Asn1ReadableOrWritable::new_write( asn1::SetOfWriter::new(authenticated_attrs), )), x509::sign::sign_data(py, py_private_key, py_hash_alg, &signed_data)?, ) }; - let digest_alg = x509::AlgorithmIdentifier { + let digest_alg = common::AlgorithmIdentifier { oid: x509::ocsp::HASH_NAME_TO_OIDS[py_hash_alg .getattr(pyo3::intern!(py, "name"))? .extract::<&str>()?] diff --git a/src/rust/src/x509/certificate.rs b/src/rust/src/x509/certificate.rs index 6ccde6542..a20e3fe5f 100644 --- a/src/rust/src/x509/certificate.rs +++ b/src/rust/src/x509/certificate.rs @@ -7,79 +7,49 @@ use crate::asn1::{ }; use crate::error::{CryptographyError, CryptographyResult}; use crate::x509; -use crate::x509::{crl, extensions, oid, sct, sign, Asn1ReadableOrWritable}; +use crate::x509::{extensions, sct, sign}; +use cryptography_x509::common::Asn1ReadableOrWritable; +use cryptography_x509::extensions::{ + AuthorityKeyIdentifier, BasicConstraints, DisplayText, DistributionPoint, + DistributionPointName, MSCertificateTemplate, NameConstraints, PolicyConstraints, + PolicyInformation, PolicyQualifierInfo, Qualifier, SequenceOfAccessDescriptions, + SequenceOfSubtrees, UserNotice, +}; +use cryptography_x509::extensions::{Extension, Extensions}; +use cryptography_x509::{common, name, oid}; use pyo3::{IntoPy, ToPyObject}; use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher}; -#[derive(asn1::Asn1Read, asn1::Asn1Write, Hash, PartialEq, Clone)] -pub(crate) struct RawCertificate<'a> { - pub(crate) tbs_cert: TbsCertificate<'a>, - signature_alg: x509::AlgorithmIdentifier<'a>, - signature: asn1::BitString<'a>, -} - -#[derive(asn1::Asn1Read, asn1::Asn1Write, Hash, PartialEq, Clone)] -pub(crate) struct TbsCertificate<'a> { - #[explicit(0)] - #[default(0)] - version: u8, - pub(crate) serial: asn1::BigInt<'a>, - signature_alg: x509::AlgorithmIdentifier<'a>, - - pub(crate) issuer: x509::Name<'a>, - validity: Validity, - pub(crate) subject: x509::Name<'a>, - - pub(crate) spki: SubjectPublicKeyInfo<'a>, - #[implicit(1)] - issuer_unique_id: Option<asn1::BitString<'a>>, - #[implicit(2)] - subject_unique_id: Option<asn1::BitString<'a>>, - #[explicit(3)] - extensions: Option<x509::Extensions<'a>>, -} - -#[derive(asn1::Asn1Read, asn1::Asn1Write, Hash, PartialEq, Clone)] -pub(crate) struct Validity { - not_before: x509::Time, - not_after: x509::Time, -} - -#[derive(asn1::Asn1Read, asn1::Asn1Write, Hash, PartialEq, Clone)] -pub(crate) struct SubjectPublicKeyInfo<'a> { - _algorithm: x509::AlgorithmIdentifier<'a>, - pub(crate) subject_public_key: asn1::BitString<'a>, -} - #[ouroboros::self_referencing] -pub(crate) struct OwnedRawCertificate { +pub(crate) struct OwnedCertificate { data: pyo3::Py<pyo3::types::PyBytes>, #[borrows(data)] #[covariant] - value: RawCertificate<'this>, + value: cryptography_x509::certificate::Certificate<'this>, } -impl OwnedRawCertificate { +impl OwnedCertificate { // Re-expose ::new with `pub(crate)` visibility. pub(crate) fn new_public( data: pyo3::Py<pyo3::types::PyBytes>, value_ref_builder: impl for<'this> FnOnce( &'this pyo3::Py<pyo3::types::PyBytes>, - ) -> RawCertificate<'this>, - ) -> OwnedRawCertificate { - OwnedRawCertificate::new(data, value_ref_builder) + ) + -> cryptography_x509::certificate::Certificate<'this>, + ) -> OwnedCertificate { + OwnedCertificate::new(data, value_ref_builder) } - pub(crate) fn borrow_value_public(&self) -> &RawCertificate<'_> { + pub(crate) fn borrow_value_public(&self) -> &cryptography_x509::certificate::Certificate<'_> { self.borrow_value() } } #[pyo3::prelude::pyclass(module = "cryptography.hazmat.bindings._rust.x509")] pub(crate) struct Certificate { - pub(crate) raw: OwnedRawCertificate, + pub(crate) raw: OwnedCertificate, pub(crate) cached_extensions: Option<pyo3::PyObject>, } @@ -209,7 +179,7 @@ impl Certificate { Some(extensions) => { let readable_extensions = extensions.unwrap_read().clone(); let ext_count = readable_extensions.len(); - let filtered_extensions: Vec<x509::common::Extension<'_>> = readable_extensions + let filtered_extensions: Vec<Extension<'_>> = readable_extensions .filter(|x| x.extn_id != oid::PRECERT_SIGNED_CERTIFICATE_TIMESTAMPS_OID) .collect(); if filtered_extensions.len() == ext_count { @@ -219,7 +189,7 @@ impl Certificate { ), )); } - let filtered_extensions: x509::Extensions<'_> = Asn1ReadableOrWritable::new_write( + let filtered_extensions: Extensions<'_> = Asn1ReadableOrWritable::new_write( asn1::SequenceOfWriter::new(filtered_extensions), ); tbs_precert.extensions = Some(filtered_extensions); @@ -409,7 +379,7 @@ fn load_der_x509_certificate( py: pyo3::Python<'_>, data: pyo3::Py<pyo3::types::PyBytes>, ) -> CryptographyResult<Certificate> { - let raw = OwnedRawCertificate::try_new(data, |data| asn1::parse_single(data.as_bytes(py)))?; + let raw = OwnedCertificate::try_new(data, |data| asn1::parse_single(data.as_bytes(py)))?; // Parse cert version immediately so we can raise error on parse if it is invalid. cert_version(py, raw.borrow_value().tbs_cert.version)?; // determine if the serial is negative and raise a warning if it is. We want to drop support @@ -437,57 +407,6 @@ fn warn_if_negative_serial(py: pyo3::Python<'_>, bytes: &'_ [u8]) -> pyo3::PyRes Ok(()) } -// Needed due to clippy type complexity warning. -type SequenceOfPolicyQualifiers<'a> = x509::Asn1ReadableOrWritable< - 'a, - asn1::SequenceOf<'a, PolicyQualifierInfo<'a>>, - asn1::SequenceOfWriter<'a, PolicyQualifierInfo<'a>, Vec<PolicyQualifierInfo<'a>>>, ->; - -#[derive(asn1::Asn1Read, asn1::Asn1Write)] -pub(crate) struct PolicyInformation<'a> { - pub policy_identifier: asn1::ObjectIdentifier, - pub policy_qualifiers: Option<SequenceOfPolicyQualifiers<'a>>, -} - -#[derive(asn1::Asn1Read, asn1::Asn1Write)] -pub(crate) struct PolicyQualifierInfo<'a> { - pub policy_qualifier_id: asn1::ObjectIdentifier, - pub qualifier: Qualifier<'a>, -} - -#[derive(asn1::Asn1Read, asn1::Asn1Write)] -pub(crate) enum Qualifier<'a> { - CpsUri(asn1::IA5String<'a>), - UserNotice(UserNotice<'a>), -} - -#[derive(asn1::Asn1Read, asn1::Asn1Write)] -pub(crate) struct UserNotice<'a> { - pub notice_ref: Option<NoticeReference<'a>>, - pub explicit_text: Option<DisplayText<'a>>, -} - -#[derive(asn1::Asn1Read, asn1::Asn1Write)] -pub(crate) struct NoticeReference<'a> { - pub organization: DisplayText<'a>, - pub notice_numbers: x509::Asn1ReadableOrWritable< - 'a, - asn1::SequenceOf<'a, asn1::BigUint<'a>>, - asn1::SequenceOfWriter<'a, asn1::BigUint<'a>, Vec<asn1::BigUint<'a>>>, - >, -} - -// DisplayText also allows BMPString, which we currently do not support. -#[allow(clippy::enum_variant_names)] -#[derive(asn1::Asn1Read, asn1::Asn1Write)] -pub(crate) enum DisplayText<'a> { - IA5String(asn1::IA5String<'a>), - Utf8String(asn1::Utf8String<'a>), - VisibleString(asn1::VisibleString<'a>), - BmpString(asn1::BMPString<'a>), -} - fn parse_display_text( py: pyo3::Python<'_>, text: DisplayText<'_>, @@ -592,41 +511,6 @@ fn parse_cp(py: pyo3::Python<'_>, ext_data: &[u8]) -> Result<pyo3::PyObject, Cry Ok(certificate_policies.to_object(py)) } -// Needed due to clippy type complexity warning. -pub(crate) type SequenceOfSubtrees<'a> = x509::Asn1ReadableOrWritable< - 'a, - asn1::SequenceOf<'a, GeneralSubtree<'a>>, - asn1::SequenceOfWriter<'a, GeneralSubtree<'a>, Vec<GeneralSubtree<'a>>>, ->; - -#[derive(asn1::Asn1Read, asn1::Asn1Write)] -pub(crate) struct NameConstraints<'a> { - #[implicit(0)] - pub permitted_subtrees: Option<SequenceOfSubtrees<'a>>, - - #[implicit(1)] - pub excluded_subtrees: Option<SequenceOfSubtrees<'a>>, -} - -#[derive(asn1::Asn1Read, asn1::Asn1Write)] -pub(crate) struct GeneralSubtree<'a> { - pub base: x509::GeneralName<'a>, - - #[implicit(0)] - #[default(0u64)] - pub minimum: u64, - - #[implicit(1)] - pub maximum: Option<u64>, -} - -#[derive(asn1::Asn1Read, asn1::Asn1Write)] -pub(crate) struct MSCertificateTemplate { - pub template_id: asn1::ObjectIdentifier, - pub major_version: Option<u32>, - pub minor_version: Option<u32>, -} - fn parse_general_subtrees( py: pyo3::Python<'_>, subtrees: SequenceOfSubtrees<'_>, @@ -638,43 +522,6 @@ fn parse_general_subtrees( Ok(gns.to_object(py)) } -#[derive(asn1::Asn1Read, asn1::Asn1Write)] -pub(crate) struct DistributionPoint<'a> { - #[explicit(0)] - pub distribution_point: Option<DistributionPointName<'a>>, - - #[implicit(1)] - pub reasons: crl::ReasonFlags<'a>, - - #[implicit(2)] - pub crl_issuer: Option<x509::common::SequenceOfGeneralName<'a>>, -} - -#[derive(asn1::Asn1Read, asn1::Asn1Write)] -pub(crate) enum DistributionPointName<'a> { - #[implicit(0)] - FullName(x509::common::SequenceOfGeneralName<'a>), - - #[implicit(1)] - NameRelativeToCRLIssuer( - x509::Asn1ReadableOrWritable< - 'a, - asn1::SetOf<'a, x509::AttributeTypeValue<'a>>, - asn1::SetOfWriter<'a, x509::AttributeTypeValue<'a>, Vec<x509::AttributeTypeValue<'a>>>, - >, - ), -} - -#[derive(asn1::Asn1Read, asn1::Asn1Write)] -pub(crate) struct AuthorityKeyIdentifier<'a> { - #[implicit(0)] - pub key_identifier: Option<&'a [u8]>, - #[implicit(1)] - pub authority_cert_issuer: Option<x509::common::SequenceOfGeneralName<'a>>, - #[implicit(2)] - pub authority_cert_serial_number: Option<asn1::BigUint<'a>>, -} - pub(crate) fn parse_distribution_point_name( py: pyo3::Python<'_>, dp: DistributionPointName<'_>, @@ -767,21 +614,6 @@ pub(crate) fn encode_distribution_point_reasons( Ok(asn1::OwnedBitString::new(bits, unused_bits).unwrap()) } -#[derive(asn1::Asn1Read, asn1::Asn1Write, pyo3::prelude::FromPyObject)] -pub(crate) struct BasicConstraints { - #[default(false)] - pub ca: bool, - pub path_length: Option<u64>, -} - -#[derive(asn1::Asn1Read, asn1::Asn1Write)] -pub(crate) struct PolicyConstraints { - #[implicit(0)] - pub require_explicit_policy: Option<u64>, - #[implicit(1)] - pub inhibit_policy_mapping: Option<u64>, -} - pub(crate) fn parse_authority_key_identifier<'p>( py: pyo3::Python<'p>, ext_data: &[u8], @@ -807,7 +639,7 @@ pub(crate) fn parse_access_descriptions( ) -> Result<pyo3::PyObject, CryptographyError> { let x509_module = py.import(pyo3::intern!(py, "cryptography.x509"))?; let ads = pyo3::types::PyList::empty(py); - let parsed = asn1::parse_single::<x509::common::SequenceOfAccessDescriptions<'_>>(ext_data)?; + let parsed = asn1::parse_single::<SequenceOfAccessDescriptions<'_>>(ext_data)?; for access in parsed.unwrap_read().clone() { let py_oid = oid_to_py_oid(py, &access.access_method)?.to_object(py); let gn = x509::parse_general_name(py, access.access_location)?; @@ -829,7 +661,7 @@ pub fn parse_cert_ext<'p>( match oid { oid::SUBJECT_ALTERNATIVE_NAME_OID => { let gn_seq = - asn1::parse_single::<asn1::SequenceOf<'_, x509::GeneralName<'_>>>(ext_data)?; + asn1::parse_single::<asn1::SequenceOf<'_, name::GeneralName<'_>>>(ext_data)?; let sans = x509::parse_general_names(py, &gn_seq)?; Ok(Some( x509_module @@ -839,7 +671,7 @@ pub fn parse_cert_ext<'p>( } oid::ISSUER_ALTERNATIVE_NAME_OID => { let gn_seq = - asn1::parse_single::<asn1::SequenceOf<'_, x509::GeneralName<'_>>>(ext_data)?; + asn1::parse_single::<asn1::SequenceOf<'_, name::GeneralName<'_>>>(ext_data)?; let ians = x509::parse_general_names(py, &gn_seq)?; Ok(Some( x509_module @@ -1016,16 +848,18 @@ pub fn parse_cert_ext<'p>( pub(crate) fn time_from_py( py: pyo3::Python<'_>, val: &pyo3::PyAny, -) -> CryptographyResult<x509::Time> { +) -> CryptographyResult<common::Time> { let dt = x509::py_to_datetime(py, val)?; time_from_datetime(dt) } -pub(crate) fn time_from_datetime(dt: asn1::DateTime) -> CryptographyResult<x509::Time> { +pub(crate) fn time_from_datetime(dt: asn1::DateTime) -> CryptographyResult<common::Time> { if dt.year() >= 2050 { - Ok(x509::Time::GeneralizedTime(asn1::GeneralizedTime::new(dt)?)) + Ok(common::Time::GeneralizedTime(asn1::GeneralizedTime::new( + dt, + )?)) } else { - Ok(x509::Time::UtcTime(asn1::UtcTime::new(dt).unwrap())) + Ok(common::Time::UtcTime(asn1::UtcTime::new(dt).unwrap())) } } @@ -1065,7 +899,7 @@ fn create_x509_certificate( let py_not_before = builder.getattr(pyo3::intern!(py, "_not_valid_before"))?; let py_not_after = builder.getattr(pyo3::intern!(py, "_not_valid_after"))?; - let tbs_cert = TbsCertificate { + let tbs_cert = cryptography_x509::certificate::TbsCertificate { version: builder .getattr(pyo3::intern!(py, "_version"))? .getattr(pyo3::intern!(py, "value"))? @@ -1073,7 +907,7 @@ fn create_x509_certificate( serial: asn1::BigInt::new(py_uint_to_big_endian_bytes(py, py_serial)?).unwrap(), signature_alg: sigalg.clone(), issuer: x509::common::encode_name(py, py_issuer_name)?, - validity: Validity { + validity: cryptography_x509::certificate::Validity { not_before: time_from_py(py, py_not_before)?, not_after: time_from_py(py, py_not_after)?, }, @@ -1090,7 +924,7 @@ fn create_x509_certificate( let tbs_bytes = asn1::write_single(&tbs_cert)?; let signature = x509::sign::sign_data(py, private_key, hash_algorithm, &tbs_bytes)?; - let data = asn1::write_single(&RawCertificate { + let data = asn1::write_single(&cryptography_x509::certificate::Certificate { tbs_cert, signature_alg: sigalg, signature: asn1::BitString::new(signature, 0).unwrap(), diff --git a/src/rust/src/x509/common.rs b/src/rust/src/x509/common.rs index 3d4aec39c..4d977a921 100644 --- a/src/rust/src/x509/common.rs +++ b/src/rust/src/x509/common.rs @@ -5,10 +5,14 @@ use crate::asn1::{oid_to_py_oid, py_oid_to_oid}; use crate::error::{CryptographyError, CryptographyResult}; use crate::x509; +use cryptography_x509::common::{Asn1ReadableOrWritable, AttributeTypeValue, RawTlv}; +use cryptography_x509::extensions::{ + AccessDescription, Extension, Extensions, SequenceOfAccessDescriptions, +}; +use cryptography_x509::name::{GeneralName, Name, OtherName, UnvalidatedIA5String}; use pyo3::types::IntoPyDict; use pyo3::ToPyObject; use std::collections::HashSet; -use std::marker::PhantomData; /// Parse all sections in a PEM file and return the first matching section. /// If no matching sections are found, return an error. @@ -26,58 +30,6 @@ pub(crate) fn find_in_pem( }) } -pub(crate) type Name<'a> = Asn1ReadableOrWritable< - 'a, - asn1::SequenceOf<'a, asn1::SetOf<'a, AttributeTypeValue<'a>>>, - asn1::SequenceOfWriter< - 'a, - asn1::SetOfWriter<'a, AttributeTypeValue<'a>, Vec<AttributeTypeValue<'a>>>, - Vec<asn1::SetOfWriter<'a, AttributeTypeValue<'a>, Vec<AttributeTypeValue<'a>>>>, - >, ->; - -#[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Eq, Hash, Clone)] -pub(crate) struct AttributeTypeValue<'a> { - pub(crate) type_id: asn1::ObjectIdentifier, - pub(crate) value: RawTlv<'a>, -} - -// Like `asn1::Tlv` but doesn't store `full_data` so it can be constructed from -// an un-encoded tag and value. -#[derive(Hash, PartialEq, Eq, Clone)] -pub(crate) struct RawTlv<'a> { - tag: asn1::Tag, - value: &'a [u8], -} - -impl<'a> RawTlv<'a> { - pub(crate) fn new(tag: asn1::Tag, value: &'a [u8]) -> Self { - RawTlv { tag, value } - } - - pub(crate) fn tag(&self) -> asn1::Tag { - self.tag - } - pub(crate) fn data(&self) -> &'a [u8] { - self.value - } -} -impl<'a> asn1::Asn1Readable<'a> for RawTlv<'a> { - fn parse(parser: &mut asn1::Parser<'a>) -> asn1::ParseResult<Self> { - let tlv = parser.read_element::<asn1::Tlv<'a>>()?; - Ok(RawTlv::new(tlv.tag(), tlv.data())) - } - - fn can_parse(_tag: asn1::Tag) -> bool { - true - } -} -impl<'a> asn1::Asn1Writable for RawTlv<'a> { - fn write(&self, w: &mut asn1::Writer<'_>) -> asn1::WriteResult { - w.write_tlv(self.tag, move |dest| dest.push_slice(self.value)) - } -} - pub(crate) fn encode_name<'p>( py: pyo3::Python<'p>, py_name: &'p pyo3::PyAny, @@ -145,73 +97,6 @@ fn encode_name_bytes<'p>( Ok(pyo3::types::PyBytes::new(py, &result)) } -/// An IA5String ASN.1 element whose contents is not validated as meeting the -/// requirements (ASCII characters only), and instead is only known to be -/// valid UTF-8. -pub(crate) struct UnvalidatedIA5String<'a>(pub(crate) &'a str); - -impl<'a> asn1::SimpleAsn1Readable<'a> for UnvalidatedIA5String<'a> { - const TAG: asn1::Tag = asn1::IA5String::TAG; - fn parse_data(data: &'a [u8]) -> asn1::ParseResult<Self> { - Ok(UnvalidatedIA5String(std::str::from_utf8(data).map_err( - |_| asn1::ParseError::new(asn1::ParseErrorKind::InvalidValue), - )?)) - } -} - -impl<'a> asn1::SimpleAsn1Writable for UnvalidatedIA5String<'a> { - const TAG: asn1::Tag = asn1::IA5String::TAG; - fn write_data(&self, dest: &mut asn1::WriteBuf) -> asn1::WriteResult { - dest.push_slice(self.0.as_bytes()) - } -} - -#[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Hash)] -pub(crate) struct OtherName<'a> { - pub(crate) type_id: asn1::ObjectIdentifier, - #[explicit(0, required)] - pub(crate) value: asn1::Tlv<'a>, -} - -#[derive(asn1::Asn1Read, asn1::Asn1Write)] -pub(crate) enum GeneralName<'a> { - #[implicit(0)] - OtherName(OtherName<'a>), - - #[implicit(1)] - RFC822Name(UnvalidatedIA5String<'a>), - - #[implicit(2)] - DNSName(UnvalidatedIA5String<'a>), - - #[implicit(3)] - // unsupported - X400Address(asn1::Sequence<'a>), - - // Name is explicit per RFC 5280 Appendix A.1. - #[explicit(4)] - DirectoryName(Name<'a>), - - #[implicit(5)] - // unsupported - EDIPartyName(asn1::Sequence<'a>), - - #[implicit(6)] - UniformResourceIdentifier(UnvalidatedIA5String<'a>), - - #[implicit(7)] - IPAddress(&'a [u8]), - - #[implicit(8)] - RegisteredID(asn1::ObjectIdentifier), -} - -pub(crate) type SequenceOfGeneralName<'a> = Asn1ReadableOrWritable< - 'a, - asn1::SequenceOf<'a, GeneralName<'a>>, - asn1::SequenceOfWriter<'a, GeneralName<'a>, Vec<GeneralName<'a>>>, ->; - pub(crate) fn encode_general_names<'a>( py: pyo3::Python<'a>, py_gns: &'a pyo3::PyAny, @@ -271,18 +156,6 @@ pub(crate) fn encode_general_name<'a>( } } -#[derive(asn1::Asn1Read, asn1::Asn1Write)] -pub(crate) struct AccessDescription<'a> { - pub(crate) access_method: asn1::ObjectIdentifier, - pub(crate) access_location: GeneralName<'a>, -} - -pub(crate) type SequenceOfAccessDescriptions<'a> = Asn1ReadableOrWritable< - 'a, - asn1::SequenceOf<'a, AccessDescription<'a>>, - asn1::SequenceOfWriter<'a, AccessDescription<'a>, Vec<AccessDescription<'a>>>, ->; - pub(crate) fn encode_access_descriptions<'a>( py: pyo3::Python<'a>, py_ads: &'a pyo3::PyAny, @@ -303,41 +176,6 @@ pub(crate) fn encode_access_descriptions<'a>( )) } -#[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Hash, Clone)] -pub(crate) enum Time { - UtcTime(asn1::UtcTime), - GeneralizedTime(asn1::GeneralizedTime), -} - -impl Time { - pub(crate) fn as_datetime(&self) -> &asn1::DateTime { - match self { - Time::UtcTime(data) => data.as_datetime(), - Time::GeneralizedTime(data) => data.as_datetime(), - } - } -} - -pub(crate) type Extensions<'a> = Asn1ReadableOrWritable< - 'a, - asn1::SequenceOf<'a, Extension<'a>>, - asn1::SequenceOfWriter<'a, Extension<'a>, Vec<Extension<'a>>>, ->; - -#[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Hash, Clone)] -pub(crate) struct AlgorithmIdentifier<'a> { - pub(crate) oid: asn1::ObjectIdentifier, - pub(crate) params: Option<asn1::Tlv<'a>>, -} - -#[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Eq, Hash, Clone)] -pub(crate) struct Extension<'a> { - pub(crate) extn_id: asn1::ObjectIdentifier, - #[default(false)] - pub(crate) critical: bool, - pub(crate) extn_value: &'a [u8], -} - pub(crate) fn parse_name<'p>( py: pyo3::Python<'p>, name: &Name<'_>, @@ -723,77 +561,9 @@ pub(crate) fn datetime_now(py: pyo3::Python<'_>) -> pyo3::PyResult<asn1::DateTim ) } -#[derive(Hash, PartialEq, Clone)] -pub(crate) enum Asn1ReadableOrWritable<'a, T, U> { - Read(T, PhantomData<&'a ()>), - Write(U, PhantomData<&'a ()>), -} - -impl<'a, T, U> Asn1ReadableOrWritable<'a, T, U> { - pub(crate) fn new_read(v: T) -> Self { - Asn1ReadableOrWritable::Read(v, PhantomData) - } - - pub(crate) fn new_write(v: U) -> Self { - Asn1ReadableOrWritable::Write(v, PhantomData) - } - - pub(crate) fn unwrap_read(&self) -> &T { - match self { - Asn1ReadableOrWritable::Read(v, _) => v, - Asn1ReadableOrWritable::Write(_, _) => panic!("unwrap_read called on a Write value"), - } - } -} - -impl<'a, T: asn1::SimpleAsn1Readable<'a>, U> asn1::SimpleAsn1Readable<'a> - for Asn1ReadableOrWritable<'a, T, U> -{ - const TAG: asn1::Tag = T::TAG; - fn parse_data(data: &'a [u8]) -> asn1::ParseResult<Self> { - Ok(Self::new_read(T::parse_data(data)?)) - } -} - -impl<'a, T: asn1::SimpleAsn1Writable, U: asn1::SimpleAsn1Writable> asn1::SimpleAsn1Writable - for Asn1ReadableOrWritable<'a, T, U> -{ - const TAG: asn1::Tag = U::TAG; - fn write_data(&self, w: &mut asn1::WriteBuf) -> asn1::WriteResult { - match self { - Asn1ReadableOrWritable::Read(v, _) => T::write_data(v, w), - Asn1ReadableOrWritable::Write(v, _) => U::write_data(v, w), - } - } -} - pub(crate) fn add_to_module(module: &pyo3::prelude::PyModule) -> pyo3::PyResult<()> { module.add_wrapped(pyo3::wrap_pyfunction!(encode_extension_value))?; module.add_wrapped(pyo3::wrap_pyfunction!(encode_name_bytes))?; Ok(()) } - -#[cfg(test)] -mod tests { - use super::{Asn1ReadableOrWritable, RawTlv}; - use asn1::Asn1Readable; - - #[test] - #[should_panic] - fn test_asn1_readable_or_writable_unwrap_read() { - Asn1ReadableOrWritable::<u32, u32>::new_write(17).unwrap_read(); - } - - #[test] - fn test_asn1_readable_or_writable_write_read_data() { - let v = Asn1ReadableOrWritable::<u32, u32>::new_read(17); - assert_eq!(&asn1::write_single(&v).unwrap(), b"\x02\x01\x11"); - } - - #[test] - fn test_raw_tlv_can_parse() { - let t = asn1::Tag::from_bytes(&[0]).unwrap().0; - assert!(RawTlv::can_parse(t)); - } -} diff --git a/src/rust/src/x509/crl.rs b/src/rust/src/x509/crl.rs index f5ab1b0c0..ea04bb984 100644 --- a/src/rust/src/x509/crl.rs +++ b/src/rust/src/x509/crl.rs @@ -7,7 +7,8 @@ use crate::asn1::{ }; use crate::error::{CryptographyError, CryptographyResult}; use crate::x509; -use crate::x509::{certificate, extensions, oid, sign}; +use crate::x509::{certificate, extensions, sign}; +use cryptography_x509::{common, crl, name, oid}; use pyo3::{IntoPy, ToPyObject}; use std::sync::Arc; @@ -16,13 +17,13 @@ fn load_der_x509_crl( py: pyo3::Python<'_>, data: pyo3::Py<pyo3::types::PyBytes>, ) -> Result<CertificateRevocationList, CryptographyError> { - let raw = OwnedRawCertificateRevocationList::try_new( + let owned = OwnedCertificateRevocationList::try_new( data, |data| asn1::parse_single(data.as_bytes(py)), |_| Ok(pyo3::once_cell::GILOnceCell::new()), )?; - let version = raw.borrow_value().tbs_cert_list.version.unwrap_or(1); + let version = owned.borrow_value().tbs_cert_list.version.unwrap_or(1); if version != 1 { let x509_module = py.import(pyo3::intern!(py, "cryptography.x509"))?; return Err(CryptographyError::from(pyo3::PyErr::from_value( @@ -33,7 +34,7 @@ fn load_der_x509_crl( } Ok(CertificateRevocationList { - raw: Arc::new(raw), + owned: Arc::new(owned), cached_extensions: None, }) } @@ -55,41 +56,41 @@ fn load_pem_x509_crl( } #[ouroboros::self_referencing] -struct OwnedRawCertificateRevocationList { +struct OwnedCertificateRevocationList { data: pyo3::Py<pyo3::types::PyBytes>, #[borrows(data)] #[covariant] - value: RawCertificateRevocationList<'this>, + value: crl::CertificateRevocationList<'this>, #[borrows(data)] #[not_covariant] - revoked_certs: pyo3::once_cell::GILOnceCell<Vec<RawRevokedCertificate<'this>>>, + revoked_certs: pyo3::once_cell::GILOnceCell<Vec<crl::RevokedCertificate<'this>>>, } #[pyo3::prelude::pyclass(module = "cryptography.hazmat.bindings._rust.x509")] struct CertificateRevocationList { - raw: Arc<OwnedRawCertificateRevocationList>, + owned: Arc<OwnedCertificateRevocationList>, cached_extensions: Option<pyo3::PyObject>, } impl CertificateRevocationList { fn public_bytes_der(&self) -> CryptographyResult<Vec<u8>> { - Ok(asn1::write_single(self.raw.borrow_value())?) + Ok(asn1::write_single(self.owned.borrow_value())?) } fn revoked_cert(&self, py: pyo3::Python<'_>, idx: usize) -> pyo3::PyResult<RevokedCertificate> { - let raw = try_map_arc_data_crl(&self.raw, |_crl, revoked_certs| { + let owned = try_map_arc_data_crl(&self.owned, |_crl, revoked_certs| { let revoked_certs = revoked_certs.get(py).unwrap(); Ok::<_, pyo3::PyErr>(revoked_certs[idx].clone()) })?; Ok(RevokedCertificate { - raw, + owned, cached_extensions: None, }) } fn len(&self) -> usize { - self.raw + self.owned .borrow_value() .tbs_cert_list .revoked_certificates @@ -106,8 +107,12 @@ impl CertificateRevocationList { op: pyo3::basic::CompareOp, ) -> pyo3::PyResult<bool> { match op { - pyo3::basic::CompareOp::Eq => Ok(self.raw.borrow_value() == other.raw.borrow_value()), - pyo3::basic::CompareOp::Ne => Ok(self.raw.borrow_value() != other.raw.borrow_value()), + pyo3::basic::CompareOp::Eq => { + Ok(self.owned.borrow_value() == other.owned.borrow_value()) + } + pyo3::basic::CompareOp::Ne => { + Ok(self.owned.borrow_value() != other.owned.borrow_value()) + } _ => Err(pyo3::exceptions::PyTypeError::new_err( "CRLs cannot be ordered", )), @@ -120,7 +125,7 @@ impl CertificateRevocationList { fn __iter__(&self) -> CRLIterator { CRLIterator { - contents: OwnedCRLIteratorData::try_new(Arc::clone(&self.raw), |v| { + contents: OwnedCRLIteratorData::try_new(Arc::clone(&self.owned), |v| { Ok::<_, ()>( v.borrow_value() .tbs_cert_list @@ -138,7 +143,7 @@ impl CertificateRevocationList { py: pyo3::Python<'_>, idx: &pyo3::PyAny, ) -> pyo3::PyResult<pyo3::PyObject> { - self.raw.with(|val| { + self.owned.with(|val| { val.revoked_certs.get_or_init(py, || { match &val.value.tbs_cert_list.revoked_certificates { Some(c) => c.unwrap_read().clone().collect(), @@ -186,7 +191,7 @@ impl CertificateRevocationList { #[getter] fn signature_algorithm_oid<'p>(&self, py: pyo3::Python<'p>) -> pyo3::PyResult<&'p pyo3::PyAny> { - oid_to_py_oid(py, &self.raw.borrow_value().signature_algorithm.oid) + oid_to_py_oid(py, &self.owned.borrow_value().signature_algorithm.oid) } #[getter] @@ -206,7 +211,7 @@ impl CertificateRevocationList { "UnsupportedAlgorithm", (format!( "Signature algorithm OID:{} not recognized", - self.raw.borrow_value().signature_algorithm.oid + self.owned.borrow_value().signature_algorithm.oid ),), )?)), } @@ -214,7 +219,7 @@ impl CertificateRevocationList { #[getter] fn signature(&self) -> &[u8] { - self.raw.borrow_value().signature_value.as_bytes() + self.owned.borrow_value().signature_value.as_bytes() } #[getter] @@ -222,7 +227,7 @@ impl CertificateRevocationList { &self, py: pyo3::Python<'p>, ) -> CryptographyResult<&'p pyo3::types::PyBytes> { - let b = asn1::write_single(&self.raw.borrow_value().tbs_cert_list)?; + let b = asn1::write_single(&self.owned.borrow_value().tbs_cert_list)?; Ok(pyo3::types::PyBytes::new(py, &b)) } @@ -231,7 +236,7 @@ impl CertificateRevocationList { py: pyo3::Python<'p>, encoding: &'p pyo3::PyAny, ) -> CryptographyResult<&'p pyo3::types::PyBytes> { - let result = asn1::write_single(self.raw.borrow_value())?; + let result = asn1::write_single(self.owned.borrow_value())?; encode_der_data(py, "X509 CRL".to_string(), result, encoding) } @@ -240,13 +245,13 @@ impl CertificateRevocationList { fn issuer<'p>(&self, py: pyo3::Python<'p>) -> pyo3::PyResult<&'p pyo3::PyAny> { Ok(x509::parse_name( py, - &self.raw.borrow_value().tbs_cert_list.issuer, + &self.owned.borrow_value().tbs_cert_list.issuer, )?) } #[getter] fn next_update<'p>(&self, py: pyo3::Python<'p>) -> pyo3::PyResult<&'p pyo3::PyAny> { - match &self.raw.borrow_value().tbs_cert_list.next_update { + match &self.owned.borrow_value().tbs_cert_list.next_update { Some(t) => x509::datetime_to_py(py, t.as_datetime()), None => Ok(py.None().into_ref(py)), } @@ -256,7 +261,7 @@ impl CertificateRevocationList { fn last_update<'p>(&self, py: pyo3::Python<'p>) -> pyo3::PyResult<&'p pyo3::PyAny> { x509::datetime_to_py( py, - self.raw + self.owned .borrow_value() .tbs_cert_list .this_update @@ -270,7 +275,7 @@ impl CertificateRevocationList { x509::parse_and_cache_extensions( py, &mut self.cached_extensions, - &self.raw.borrow_value().tbs_cert_list.crl_extensions, + &self.owned.borrow_value().tbs_cert_list.crl_extensions, |oid, ext_data| match *oid { oid::CRL_NUMBER_OID => { let bignum = asn1::parse_single::<asn1::BigUint<'_>>(ext_data)?; @@ -291,7 +296,7 @@ impl CertificateRevocationList { )) } oid::ISSUER_ALTERNATIVE_NAME_OID => { - let gn_seq = asn1::parse_single::<asn1::SequenceOf<'_, x509::GeneralName<'_>>>( + let gn_seq = asn1::parse_single::<asn1::SequenceOf<'_, name::GeneralName<'_>>>( ext_data, )?; let ians = x509::parse_general_names(py, &gn_seq)?; @@ -313,7 +318,7 @@ impl CertificateRevocationList { certificate::parse_authority_key_identifier(py, ext_data)?, )), oid::ISSUING_DISTRIBUTION_POINT_OID => { - let idp = asn1::parse_single::<IssuingDistributionPoint<'_>>(ext_data)?; + let idp = asn1::parse_single::<crl::IssuingDistributionPoint<'_>>(ext_data)?; let (full_name, relative_name) = match idp.distribution_point { Some(data) => certificate::parse_distribution_point_name(py, data)?, None => (py.None(), py.None()), @@ -359,7 +364,7 @@ impl CertificateRevocationList { serial: &pyo3::types::PyLong, ) -> pyo3::PyResult<Option<RevokedCertificate>> { let serial_bytes = py_uint_to_big_endian_bytes(py, serial)?; - let owned = OwnedRawRevokedCertificate::try_new(Arc::clone(&self.raw), |v| { + let owned = OwnedRevokedCertificate::try_new(Arc::clone(&self.owned), |v| { let certs = match &v.borrow_value().tbs_cert_list.revoked_certificates { Some(certs) => certs.unwrap_read().clone(), None => return Err(()), @@ -375,7 +380,7 @@ impl CertificateRevocationList { }); match owned { Ok(o) => Ok(Some(RevokedCertificate { - raw: o, + owned: o, cached_extensions: None, })), Err(()) => Ok(None), @@ -387,8 +392,8 @@ impl CertificateRevocationList { py: pyo3::Python<'p>, public_key: &'p pyo3::PyAny, ) -> CryptographyResult<bool> { - if slf.raw.borrow_value().tbs_cert_list.signature - != slf.raw.borrow_value().signature_algorithm + if slf.owned.borrow_value().tbs_cert_list.signature + != slf.owned.borrow_value().signature_algorithm { return Ok(false); }; @@ -400,9 +405,9 @@ impl CertificateRevocationList { Ok(sign::verify_signature_with_oid( py, public_key, - &slf.raw.borrow_value().signature_algorithm.oid, - slf.raw.borrow_value().signature_value.as_bytes(), - &asn1::write_single(&slf.raw.borrow_value().tbs_cert_list)?, + &slf.owned.borrow_value().signature_algorithm.oid, + slf.owned.borrow_value().signature_value.as_bytes(), + &asn1::write_single(&slf.owned.borrow_value().tbs_cert_list)?, ) .is_ok()) } @@ -410,10 +415,10 @@ impl CertificateRevocationList { #[ouroboros::self_referencing] struct OwnedCRLIteratorData { - data: Arc<OwnedRawCertificateRevocationList>, + data: Arc<OwnedCertificateRevocationList>, #[borrows(data)] #[covariant] - value: Option<asn1::SequenceOf<'this, RawRevokedCertificate<'this>>>, + value: Option<asn1::SequenceOf<'this, crl::RevokedCertificate<'this>>>, } #[pyo3::prelude::pyclass(module = "cryptography.hazmat.bindings._rust.x509")] @@ -424,13 +429,13 @@ struct CRLIterator { // Open-coded implementation of the API discussed in // https://github.com/joshua-maros/ouroboros/issues/38 fn try_map_arc_data_crl<E>( - crl: &Arc<OwnedRawCertificateRevocationList>, + crl: &Arc<OwnedCertificateRevocationList>, f: impl for<'this> FnOnce( - &'this OwnedRawCertificateRevocationList, - &pyo3::once_cell::GILOnceCell<Vec<RawRevokedCertificate<'this>>>, - ) -> Result<RawRevokedCertificate<'this>, E>, -) -> Result<OwnedRawRevokedCertificate, E> { - OwnedRawRevokedCertificate::try_new(Arc::clone(crl), |inner_crl| { + &'this OwnedCertificateRevocationList, + &pyo3::once_cell::GILOnceCell<Vec<crl::RevokedCertificate<'this>>>, + ) -> Result<crl::RevokedCertificate<'this>, E>, +) -> Result<OwnedRevokedCertificate, E> { + OwnedRevokedCertificate::try_new(Arc::clone(crl), |inner_crl| { crl.with(|value| { f(inner_crl, unsafe { std::mem::transmute(value.revoked_certs) @@ -441,11 +446,11 @@ fn try_map_arc_data_crl<E>( fn try_map_arc_data_mut_crl_iterator<E>( it: &mut OwnedCRLIteratorData, f: impl for<'this> FnOnce( - &'this OwnedRawCertificateRevocationList, - &mut Option<asn1::SequenceOf<'this, RawRevokedCertificate<'this>>>, - ) -> Result<RawRevokedCertificate<'this>, E>, -) -> Result<OwnedRawRevokedCertificate, E> { - OwnedRawRevokedCertificate::try_new(Arc::clone(it.borrow_data()), |inner_it| { + &'this OwnedCertificateRevocationList, + &mut Option<asn1::SequenceOf<'this, crl::RevokedCertificate<'this>>>, + ) -> Result<crl::RevokedCertificate<'this>, E>, +) -> Result<OwnedRevokedCertificate, E> { + OwnedRevokedCertificate::try_new(Arc::clone(it.borrow_data()), |inner_it| { it.with_value_mut(|value| f(inner_it, unsafe { std::mem::transmute(value) })) }) } @@ -470,57 +475,23 @@ impl CRLIterator { }) .ok()?; Some(RevokedCertificate { - raw: revoked, + owned: revoked, cached_extensions: None, }) } } -#[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Hash)] -struct RawCertificateRevocationList<'a> { - tbs_cert_list: TBSCertList<'a>, - signature_algorithm: x509::AlgorithmIdentifier<'a>, - signature_value: asn1::BitString<'a>, -} - -type RevokedCertificates<'a> = Option< - x509::Asn1ReadableOrWritable< - 'a, - asn1::SequenceOf<'a, RawRevokedCertificate<'a>>, - asn1::SequenceOfWriter<'a, RawRevokedCertificate<'a>, Vec<RawRevokedCertificate<'a>>>, - >, ->; - -#[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Hash)] -struct TBSCertList<'a> { - version: Option<u8>, - signature: x509::AlgorithmIdentifier<'a>, - issuer: x509::Name<'a>, - this_update: x509::Time, - next_update: Option<x509::Time>, - revoked_certificates: RevokedCertificates<'a>, - #[explicit(0)] - crl_extensions: Option<x509::Extensions<'a>>, -} - -#[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Hash, Clone)] -struct RawRevokedCertificate<'a> { - user_certificate: asn1::BigUint<'a>, - revocation_date: x509::Time, - crl_entry_extensions: Option<x509::Extensions<'a>>, -} - #[ouroboros::self_referencing] -struct OwnedRawRevokedCertificate { - data: Arc<OwnedRawCertificateRevocationList>, +struct OwnedRevokedCertificate { + data: Arc<OwnedCertificateRevocationList>, #[borrows(data)] #[covariant] - value: RawRevokedCertificate<'this>, + value: crl::RevokedCertificate<'this>, } #[pyo3::prelude::pyclass(module = "cryptography.hazmat.bindings._rust.x509")] struct RevokedCertificate { - raw: OwnedRawRevokedCertificate, + owned: OwnedRevokedCertificate, cached_extensions: Option<pyo3::PyObject>, } @@ -528,12 +499,12 @@ struct RevokedCertificate { impl RevokedCertificate { #[getter] fn serial_number<'p>(&self, py: pyo3::Python<'p>) -> pyo3::PyResult<&'p pyo3::PyAny> { - big_byte_slice_to_py_int(py, self.raw.borrow_value().user_certificate.as_bytes()) + big_byte_slice_to_py_int(py, self.owned.borrow_value().user_certificate.as_bytes()) } #[getter] fn revocation_date<'p>(&self, py: pyo3::Python<'p>) -> pyo3::PyResult<&'p pyo3::PyAny> { - x509::datetime_to_py(py, self.raw.borrow_value().revocation_date.as_datetime()) + x509::datetime_to_py(py, self.owned.borrow_value().revocation_date.as_datetime()) } #[getter] @@ -541,45 +512,15 @@ impl RevokedCertificate { x509::parse_and_cache_extensions( py, &mut self.cached_extensions, - &self.raw.borrow_value().crl_entry_extensions, + &self.owned.borrow_value().crl_entry_extensions, |oid, ext_data| parse_crl_entry_ext(py, oid.clone(), ext_data), ) } } -pub(crate) type ReasonFlags<'a> = - Option<x509::Asn1ReadableOrWritable<'a, asn1::BitString<'a>, asn1::OwnedBitString>>; - -#[derive(asn1::Asn1Read, asn1::Asn1Write)] -pub(crate) struct IssuingDistributionPoint<'a> { - #[explicit(0)] - pub distribution_point: Option<certificate::DistributionPointName<'a>>, - - #[implicit(1)] - #[default(false)] - pub only_contains_user_certs: bool, - - #[implicit(2)] - #[default(false)] - pub only_contains_ca_certs: bool, - - #[implicit(3)] - pub only_some_reasons: ReasonFlags<'a>, - - #[implicit(4)] - #[default(false)] - pub indirect_crl: bool, - - #[implicit(5)] - #[default(false)] - pub only_contains_attribute_certs: bool, -} - -pub(crate) type CRLReason = asn1::Enumerated; - pub(crate) fn parse_crl_reason_flags<'p>( py: pyo3::Python<'p>, - reason: &CRLReason, + reason: &crl::CRLReason, ) -> CryptographyResult<&'p pyo3::PyAny> { let x509_module = py.import(pyo3::intern!(py, "cryptography.x509"))?; let flag_name = match reason.value() { @@ -615,7 +556,7 @@ pub fn parse_crl_entry_ext<'p>( let x509_module = py.import(pyo3::intern!(py, "cryptography.x509"))?; match oid { oid::CRL_REASON_OID => { - let flags = parse_crl_reason_flags(py, &asn1::parse_single::<CRLReason>(data)?)?; + let flags = parse_crl_reason_flags(py, &asn1::parse_single::<crl::CRLReason>(data)?)?; Ok(Some( x509_module .getattr(pyo3::intern!(py, "CRLReason"))? @@ -623,7 +564,7 @@ pub fn parse_crl_entry_ext<'p>( )) } oid::CERTIFICATE_ISSUER_OID => { - let gn_seq = asn1::parse_single::<asn1::SequenceOf<'_, x509::GeneralName<'_>>>(data)?; + let gn_seq = asn1::parse_single::<asn1::SequenceOf<'_, name::GeneralName<'_>>>(data)?; let gns = x509::parse_general_names(py, &gn_seq)?; Ok(Some( x509_module @@ -663,7 +604,7 @@ fn create_x509_crl( .getattr(pyo3::intern!(py, "serial_number"))? .extract()?; let py_revocation_date = py_revoked_cert.getattr(pyo3::intern!(py, "revocation_date"))?; - revoked_certs.push(RawRevokedCertificate { + revoked_certs.push(crl::RevokedCertificate { user_certificate: asn1::BigUint::new(py_uint_to_big_endian_bytes(py, serial_number)?) .unwrap(), revocation_date: x509::certificate::time_from_py(py, py_revocation_date)?, @@ -678,7 +619,7 @@ fn create_x509_crl( let py_issuer_name = builder.getattr(pyo3::intern!(py, "_issuer_name"))?; let py_this_update = builder.getattr(pyo3::intern!(py, "_last_update"))?; let py_next_update = builder.getattr(pyo3::intern!(py, "_next_update"))?; - let tbs_cert_list = TBSCertList { + let tbs_cert_list = crl::TBSCertList { version: Some(1), signature: sigalg.clone(), issuer: x509::common::encode_name(py, py_issuer_name)?, @@ -687,7 +628,7 @@ fn create_x509_crl( revoked_certificates: if revoked_certs.is_empty() { None } else { - Some(x509::Asn1ReadableOrWritable::new_write( + Some(common::Asn1ReadableOrWritable::new_write( asn1::SequenceOfWriter::new(revoked_certs), )) }, @@ -700,7 +641,7 @@ fn create_x509_crl( let tbs_bytes = asn1::write_single(&tbs_cert_list)?; let signature = x509::sign::sign_data(py, private_key, hash_algorithm, &tbs_bytes)?; - let data = asn1::write_single(&RawCertificateRevocationList { + let data = asn1::write_single(&crl::CertificateRevocationList { tbs_cert_list, signature_algorithm: sigalg, signature_value: asn1::BitString::new(signature, 0).unwrap(), diff --git a/src/rust/src/x509/csr.rs b/src/rust/src/x509/csr.rs index 2122018e0..6de3667ae 100644 --- a/src/rust/src/x509/csr.rs +++ b/src/rust/src/x509/csr.rs @@ -5,83 +5,25 @@ use crate::asn1::{encode_der_data, oid_to_py_oid, py_oid_to_oid}; use crate::error::{CryptographyError, CryptographyResult}; use crate::x509; -use crate::x509::{certificate, oid, sign}; +use crate::x509::{certificate, sign}; use asn1::SimpleAsn1Readable; +use cryptography_x509::csr::{check_attribute_length, Attribute, CertificationRequestInfo, Csr}; +use cryptography_x509::{common, oid}; use pyo3::IntoPy; use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher}; -#[derive(asn1::Asn1Read, asn1::Asn1Write)] -struct RawCsr<'a> { - csr_info: CertificationRequestInfo<'a>, - signature_alg: x509::AlgorithmIdentifier<'a>, - signature: asn1::BitString<'a>, -} - -#[derive(asn1::Asn1Read, asn1::Asn1Write)] -struct CertificationRequestInfo<'a> { - version: u8, - subject: x509::Name<'a>, - spki: certificate::SubjectPublicKeyInfo<'a>, - #[implicit(0, required)] - attributes: Attributes<'a>, -} - -pub(crate) type Attributes<'a> = x509::Asn1ReadableOrWritable< - 'a, - asn1::SetOf<'a, Attribute<'a>>, - asn1::SetOfWriter<'a, Attribute<'a>, Vec<Attribute<'a>>>, ->; - -#[derive(asn1::Asn1Read, asn1::Asn1Write)] -pub(crate) struct Attribute<'a> { - pub(crate) type_id: asn1::ObjectIdentifier, - pub(crate) values: x509::Asn1ReadableOrWritable< - 'a, - asn1::SetOf<'a, asn1::Tlv<'a>>, - asn1::SetOfWriter<'a, x509::common::RawTlv<'a>, [x509::common::RawTlv<'a>; 1]>, - >, -} - -fn check_attribute_length<'a>( - values: asn1::SetOf<'a, asn1::Tlv<'a>>, -) -> Result<(), CryptographyError> { - if values.count() > 1 { - Err(CryptographyError::from( - pyo3::exceptions::PyValueError::new_err("Only single-valued attributes are supported"), - )) - } else { - Ok(()) - } -} - -impl CertificationRequestInfo<'_> { - fn get_extension_attribute(&self) -> Result<Option<x509::Extensions<'_>>, CryptographyError> { - for attribute in self.attributes.unwrap_read().clone() { - if attribute.type_id == oid::EXTENSION_REQUEST - || attribute.type_id == oid::MS_EXTENSION_REQUEST - { - check_attribute_length(attribute.values.unwrap_read().clone())?; - let val = attribute.values.unwrap_read().clone().next().unwrap(); - let exts = asn1::parse_single(val.full_data())?; - return Ok(Some(exts)); - } - } - Ok(None) - } -} - #[ouroboros::self_referencing] -struct OwnedRawCsr { +struct OwnedCsr { data: pyo3::Py<pyo3::types::PyBytes>, #[borrows(data)] #[covariant] - value: RawCsr<'this>, + value: Csr<'this>, } #[pyo3::prelude::pyclass(module = "cryptography.hazmat.bindings._rust.x509")] struct CertificateSigningRequest { - raw: OwnedRawCsr, + raw: OwnedCsr, cached_extensions: Option<pyo3::PyObject>, } @@ -212,7 +154,11 @@ impl CertificateSigningRequest { .clone() { if rust_oid == attribute.type_id { - check_attribute_length(attribute.values.unwrap_read().clone())?; + check_attribute_length(attribute.values.unwrap_read().clone()).map_err(|_| { + pyo3::exceptions::PyValueError::new_err( + "Only single-valued attributes are supported", + ) + })?; let val = attribute.values.unwrap_read().clone().next().unwrap(); // We allow utf8string, printablestring, and ia5string at this time if val.tag() == asn1::Utf8String::TAG @@ -248,7 +194,11 @@ impl CertificateSigningRequest { .unwrap_read() .clone() { - check_attribute_length(attribute.values.unwrap_read().clone())?; + check_attribute_length(attribute.values.unwrap_read().clone()).map_err(|_| { + pyo3::exceptions::PyValueError::new_err( + "Only single-valued attributes are supported", + ) + })?; let oid = oid_to_py_oid(py, &attribute.type_id)?; let val = attribute.values.unwrap_read().clone().next().unwrap(); let serialized = pyo3::types::PyBytes::new(py, val.data()); @@ -268,7 +218,16 @@ impl CertificateSigningRequest { #[getter] fn extensions(&mut self, py: pyo3::Python<'_>) -> pyo3::PyResult<pyo3::PyObject> { - let exts = self.raw.borrow_value().csr_info.get_extension_attribute()?; + let exts = self + .raw + .borrow_value() + .csr_info + .get_extension_attribute() + .map_err(|_| { + pyo3::exceptions::PyValueError::new_err( + "Only single-valued attributes are supported", + ) + })?; x509::parse_and_cache_extensions(py, &mut self.cached_extensions, &exts, |oid, ext_data| { certificate::parse_cert_ext(py, oid.clone(), ext_data) @@ -314,7 +273,7 @@ fn load_der_x509_csr( py: pyo3::Python<'_>, data: pyo3::Py<pyo3::types::PyBytes>, ) -> CryptographyResult<CertificateSigningRequest> { - let raw = OwnedRawCsr::try_new(data, |data| asn1::parse_single(data.as_bytes(py)))?; + let raw = OwnedCsr::try_new(data, |data| asn1::parse_single(data.as_bytes(py)))?; let version = raw.borrow_value().csr_info.version; if version != 0 { @@ -369,7 +328,7 @@ fn create_x509_csr( ext_bytes = asn1::write_single(&exts)?; attrs.push(Attribute { type_id: (oid::EXTENSION_REQUEST).clone(), - values: x509::Asn1ReadableOrWritable::new_write(asn1::SetOfWriter::new([ + values: common::Asn1ReadableOrWritable::new_write(asn1::SetOfWriter::new([ asn1::parse_single(&ext_bytes)?, ])), }) @@ -393,8 +352,8 @@ fn create_x509_csr( attrs.push(Attribute { type_id: oid, - values: x509::Asn1ReadableOrWritable::new_write(asn1::SetOfWriter::new([ - x509::common::RawTlv::new(tag, value), + values: common::Asn1ReadableOrWritable::new_write(asn1::SetOfWriter::new([ + common::RawTlv::new(tag, value), ])), }) } @@ -405,12 +364,12 @@ fn create_x509_csr( version: 0, subject: x509::common::encode_name(py, py_subject_name)?, spki: asn1::parse_single(spki_bytes)?, - attributes: x509::Asn1ReadableOrWritable::new_write(asn1::SetOfWriter::new(attrs)), + attributes: common::Asn1ReadableOrWritable::new_write(asn1::SetOfWriter::new(attrs)), }; let tbs_bytes = asn1::write_single(&csr_info)?; let signature = x509::sign::sign_data(py, private_key, hash_algorithm, &tbs_bytes)?; - let data = asn1::write_single(&RawCsr { + let data = asn1::write_single(&Csr { csr_info, signature_alg: sigalg, signature: asn1::BitString::new(signature, 0).unwrap(), diff --git a/src/rust/src/x509/extensions.rs b/src/rust/src/x509/extensions.rs index 84009b0c7..08e112cbb 100644 --- a/src/rust/src/x509/extensions.rs +++ b/src/rust/src/x509/extensions.rs @@ -5,25 +5,26 @@ use crate::asn1::{py_oid_to_oid, py_uint_to_big_endian_bytes}; use crate::error::{CryptographyError, CryptographyResult}; use crate::x509; -use crate::x509::{certificate, crl, oid, sct}; +use crate::x509::{certificate, sct}; +use cryptography_x509::{common, crl, extensions, oid}; fn encode_general_subtrees<'a>( py: pyo3::Python<'a>, subtrees: &'a pyo3::PyAny, -) -> Result<Option<certificate::SequenceOfSubtrees<'a>>, CryptographyError> { +) -> Result<Option<extensions::SequenceOfSubtrees<'a>>, CryptographyError> { if subtrees.is_none() { Ok(None) } else { let mut subtree_seq = vec![]; for name in subtrees.iter()? { let gn = x509::common::encode_general_name(py, name?)?; - subtree_seq.push(certificate::GeneralSubtree { + subtree_seq.push(extensions::GeneralSubtree { base: gn, minimum: 0, maximum: None, }); } - Ok(Some(x509::Asn1ReadableOrWritable::new_write( + Ok(Some(common::Asn1ReadableOrWritable::new_write( asn1::SequenceOfWriter::new(subtree_seq), ))) } @@ -32,7 +33,7 @@ fn encode_general_subtrees<'a>( pub(crate) fn encode_authority_key_identifier<'a>( py: pyo3::Python<'a>, py_aki: &'a pyo3::PyAny, -) -> pyo3::PyResult<certificate::AuthorityKeyIdentifier<'a>> { +) -> pyo3::PyResult<extensions::AuthorityKeyIdentifier<'a>> { #[derive(pyo3::prelude::FromPyObject)] struct PyAuthorityKeyIdentifier<'a> { key_identifier: Option<&'a [u8]>, @@ -42,7 +43,7 @@ pub(crate) fn encode_authority_key_identifier<'a>( let aki = py_aki.extract::<PyAuthorityKeyIdentifier<'_>>()?; let authority_cert_issuer = if let Some(authority_cert_issuer) = aki.authority_cert_issuer { let gns = x509::common::encode_general_names(py, authority_cert_issuer)?; - Some(x509::Asn1ReadableOrWritable::new_write( + Some(common::Asn1ReadableOrWritable::new_write( asn1::SequenceOfWriter::new(gns), )) } else { @@ -55,7 +56,7 @@ pub(crate) fn encode_authority_key_identifier<'a>( } else { None }; - Ok(certificate::AuthorityKeyIdentifier { + Ok(extensions::AuthorityKeyIdentifier { authority_cert_issuer, authority_cert_serial_number, key_identifier: aki.key_identifier, @@ -65,7 +66,7 @@ pub(crate) fn encode_authority_key_identifier<'a>( pub(crate) fn encode_distribution_points<'p>( py: pyo3::Python<'p>, py_dps: &'p pyo3::PyAny, -) -> pyo3::PyResult<Vec<certificate::DistributionPoint<'p>>> { +) -> pyo3::PyResult<Vec<extensions::DistributionPoint<'p>>> { #[derive(pyo3::prelude::FromPyObject)] struct PyDistributionPoint<'a> { crl_issuer: Option<&'a pyo3::PyAny>, @@ -80,7 +81,7 @@ pub(crate) fn encode_distribution_points<'p>( let crl_issuer = if let Some(py_crl_issuer) = py_dp.crl_issuer { let gns = x509::common::encode_general_names(py, py_crl_issuer)?; - Some(x509::Asn1ReadableOrWritable::new_write( + Some(common::Asn1ReadableOrWritable::new_write( asn1::SequenceOfWriter::new(gns), )) } else { @@ -88,27 +89,27 @@ pub(crate) fn encode_distribution_points<'p>( }; let distribution_point = if let Some(py_full_name) = py_dp.full_name { let gns = x509::common::encode_general_names(py, py_full_name)?; - Some(certificate::DistributionPointName::FullName( - x509::Asn1ReadableOrWritable::new_write(asn1::SequenceOfWriter::new(gns)), + Some(extensions::DistributionPointName::FullName( + common::Asn1ReadableOrWritable::new_write(asn1::SequenceOfWriter::new(gns)), )) } else if let Some(py_relative_name) = py_dp.relative_name { let mut name_entries = vec![]; for py_name_entry in py_relative_name.iter()? { name_entries.push(x509::common::encode_name_entry(py, py_name_entry?)?); } - Some(certificate::DistributionPointName::NameRelativeToCRLIssuer( - x509::Asn1ReadableOrWritable::new_write(asn1::SetOfWriter::new(name_entries)), + Some(extensions::DistributionPointName::NameRelativeToCRLIssuer( + common::Asn1ReadableOrWritable::new_write(asn1::SetOfWriter::new(name_entries)), )) } else { None }; let reasons = if let Some(py_reasons) = py_dp.reasons { let reasons = certificate::encode_distribution_point_reasons(py, py_reasons)?; - Some(x509::Asn1ReadableOrWritable::new_write(reasons)) + Some(common::Asn1ReadableOrWritable::new_write(reasons)) } else { None }; - dps.push(certificate::DistributionPoint { + dps.push(extensions::DistributionPoint { crl_issuer, distribution_point, reasons, @@ -124,7 +125,16 @@ pub(crate) fn encode_extension( ) -> CryptographyResult<Option<Vec<u8>>> { match oid { &oid::BASIC_CONSTRAINTS_OID => { - let bc = ext.extract::<certificate::BasicConstraints>()?; + #[derive(pyo3::prelude::FromPyObject)] + struct PyBasicConstraints { + ca: bool, + path_length: Option<u64>, + } + let pybc = ext.extract::<PyBasicConstraints>()?; + let bc = extensions::BasicConstraints { + ca: pybc.ca, + path_length: pybc.path_length, + }; Ok(Some(asn1::write_single(&bc)?)) } &oid::SUBJECT_KEY_IDENTIFIER_OID => { @@ -232,9 +242,9 @@ pub(crate) fn encode_extension( .into()) } }; - certificate::PolicyQualifierInfo { + extensions::PolicyQualifierInfo { policy_qualifier_id: (oid::CP_CPS_URI_OID).clone(), - qualifier: certificate::Qualifier::CpsUri(cps_uri), + qualifier: extensions::Qualifier::CpsUri(cps_uri), } } else { let py_notice = @@ -250,15 +260,15 @@ pub(crate) fn encode_extension( notice_numbers.push(asn1::BigUint::new(bytes).unwrap()); } - Some(certificate::NoticeReference { - organization: certificate::DisplayText::Utf8String( + Some(extensions::NoticeReference { + organization: extensions::DisplayText::Utf8String( asn1::Utf8String::new( py_notice .getattr(pyo3::intern!(py, "organization"))? .extract()?, ), ), - notice_numbers: x509::Asn1ReadableOrWritable::new_write( + notice_numbers: common::Asn1ReadableOrWritable::new_write( asn1::SequenceOfWriter::new(notice_numbers), ), }) @@ -268,17 +278,17 @@ pub(crate) fn encode_extension( let py_explicit_text = py_qualifier.getattr(pyo3::intern!(py, "explicit_text"))?; let explicit_text = if py_explicit_text.is_true()? { - Some(certificate::DisplayText::Utf8String(asn1::Utf8String::new( + Some(extensions::DisplayText::Utf8String(asn1::Utf8String::new( py_explicit_text.extract()?, ))) } else { None }; - certificate::PolicyQualifierInfo { + extensions::PolicyQualifierInfo { policy_qualifier_id: (oid::CP_USER_NOTICE_OID).clone(), - qualifier: certificate::Qualifier::UserNotice( - certificate::UserNotice { + qualifier: extensions::Qualifier::UserNotice( + extensions::UserNotice { notice_ref, explicit_text, }, @@ -287,7 +297,7 @@ pub(crate) fn encode_extension( }; qualifiers.push(qualifier); } - Some(x509::Asn1ReadableOrWritable::new_write( + Some(common::Asn1ReadableOrWritable::new_write( asn1::SequenceOfWriter::new(qualifiers), )) } else { @@ -295,7 +305,7 @@ pub(crate) fn encode_extension( }; let py_policy_id = py_policy_info.getattr(pyo3::intern!(py, "policy_identifier"))?; - policy_informations.push(certificate::PolicyInformation { + policy_informations.push(extensions::PolicyInformation { policy_identifier: py_oid_to_oid(py_policy_id)?, policy_qualifiers: qualifiers, }); @@ -305,7 +315,7 @@ pub(crate) fn encode_extension( ))?)) } &oid::POLICY_CONSTRAINTS_OID => { - let pc = certificate::PolicyConstraints { + let pc = extensions::PolicyConstraints { require_explicit_policy: ext .getattr(pyo3::intern!(py, "require_explicit_policy"))? .extract()?, @@ -318,7 +328,7 @@ pub(crate) fn encode_extension( &oid::NAME_CONSTRAINTS_OID => { let permitted = ext.getattr(pyo3::intern!(py, "permitted_subtrees"))?; let excluded = ext.getattr(pyo3::intern!(py, "excluded_subtrees"))?; - let nc = certificate::NameConstraints { + let nc = extensions::NameConstraints { permitted_subtrees: encode_general_subtrees(ext.py(), permitted)?, excluded_subtrees: encode_general_subtrees(ext.py(), excluded)?, }; @@ -412,23 +422,23 @@ pub(crate) fn encode_extension( { let py_reasons = ext.getattr(pyo3::intern!(py, "only_some_reasons"))?; let reasons = certificate::encode_distribution_point_reasons(ext.py(), py_reasons)?; - Some(x509::Asn1ReadableOrWritable::new_write(reasons)) + Some(common::Asn1ReadableOrWritable::new_write(reasons)) } else { None }; let distribution_point = if ext.getattr(pyo3::intern!(py, "full_name"))?.is_true()? { let py_full_name = ext.getattr(pyo3::intern!(py, "full_name"))?; let gns = x509::common::encode_general_names(ext.py(), py_full_name)?; - Some(certificate::DistributionPointName::FullName( - x509::Asn1ReadableOrWritable::new_write(asn1::SequenceOfWriter::new(gns)), + Some(extensions::DistributionPointName::FullName( + common::Asn1ReadableOrWritable::new_write(asn1::SequenceOfWriter::new(gns)), )) } else if ext.getattr(pyo3::intern!(py, "relative_name"))?.is_true()? { let mut name_entries = vec![]; for py_name_entry in ext.getattr(pyo3::intern!(py, "relative_name"))?.iter()? { name_entries.push(x509::common::encode_name_entry(ext.py(), py_name_entry?)?); } - Some(certificate::DistributionPointName::NameRelativeToCRLIssuer( - x509::Asn1ReadableOrWritable::new_write(asn1::SetOfWriter::new(name_entries)), + Some(extensions::DistributionPointName::NameRelativeToCRLIssuer( + common::Asn1ReadableOrWritable::new_write(asn1::SetOfWriter::new(name_entries)), )) } else { None @@ -458,7 +468,7 @@ pub(crate) fn encode_extension( } &oid::MS_CERTIFICATE_TEMPLATE => { let py_template_id = ext.getattr(pyo3::intern!(py, "template_id"))?; - let mstpl = certificate::MSCertificateTemplate { + let mstpl = extensions::MSCertificateTemplate { template_id: py_oid_to_oid(py_template_id)?, major_version: ext.getattr(pyo3::intern!(py, "major_version"))?.extract()?, minor_version: ext.getattr(pyo3::intern!(py, "minor_version"))?.extract()?, diff --git a/src/rust/src/x509/mod.rs b/src/rust/src/x509/mod.rs index 2ad15c6e6..c43bf9023 100644 --- a/src/rust/src/x509/mod.rs +++ b/src/rust/src/x509/mod.rs @@ -10,13 +10,10 @@ pub(crate) mod extensions; pub(crate) mod ocsp; pub(crate) mod ocsp_req; pub(crate) mod ocsp_resp; -pub(crate) mod oid; pub(crate) mod sct; pub(crate) mod sign; -pub(crate) use certificate::Certificate; pub(crate) use common::{ datetime_to_py, find_in_pem, parse_and_cache_extensions, parse_general_name, - parse_general_names, parse_name, parse_rdn, py_to_datetime, AlgorithmIdentifier, - Asn1ReadableOrWritable, AttributeTypeValue, Extensions, GeneralName, Name, Time, + parse_general_names, parse_name, parse_rdn, py_to_datetime, }; diff --git a/src/rust/src/x509/ocsp.rs b/src/rust/src/x509/ocsp.rs index e3568ca9d..b362ef326 100644 --- a/src/rust/src/x509/ocsp.rs +++ b/src/rust/src/x509/ocsp.rs @@ -4,7 +4,9 @@ use crate::error::CryptographyResult; use crate::x509; -use crate::x509::oid; +use crate::x509::certificate::Certificate; +use cryptography_x509::ocsp_req::CertID; +use cryptography_x509::{common, oid}; use once_cell::sync::Lazy; use std::collections::HashMap; @@ -28,69 +30,59 @@ pub(crate) static HASH_NAME_TO_OIDS: Lazy<HashMap<&str, &asn1::ObjectIdentifier> h }); -#[derive(asn1::Asn1Read, asn1::Asn1Write)] -pub(crate) struct CertID<'a> { - pub(crate) hash_algorithm: x509::AlgorithmIdentifier<'a>, - pub(crate) issuer_name_hash: &'a [u8], - pub(crate) issuer_key_hash: &'a [u8], - pub(crate) serial_number: asn1::BigInt<'a>, -} - -impl CertID<'_> { - pub(crate) fn new<'p>( - py: pyo3::Python<'p>, - cert: &'p x509::Certificate, - issuer: &'p x509::Certificate, - hash_algorithm: &'p pyo3::PyAny, - ) -> CryptographyResult<CertID<'p>> { - let issuer_der = asn1::write_single(&cert.raw.borrow_value_public().tbs_cert.issuer)?; - let issuer_name_hash = hash_data(py, hash_algorithm, &issuer_der)?; - let issuer_key_hash = hash_data( - py, - hash_algorithm, - issuer - .raw - .borrow_value_public() - .tbs_cert - .spki - .subject_public_key - .as_bytes(), - )?; +pub(crate) fn certid_new<'p>( + py: pyo3::Python<'p>, + cert: &'p Certificate, + issuer: &'p Certificate, + hash_algorithm: &'p pyo3::PyAny, +) -> CryptographyResult<CertID<'p>> { + let issuer_der = asn1::write_single(&cert.raw.borrow_value_public().tbs_cert.issuer)?; + let issuer_name_hash = hash_data(py, hash_algorithm, &issuer_der)?; + let issuer_key_hash = hash_data( + py, + hash_algorithm, + issuer + .raw + .borrow_value_public() + .tbs_cert + .spki + .subject_public_key + .as_bytes(), + )?; - Ok(CertID { - hash_algorithm: x509::AlgorithmIdentifier { - oid: HASH_NAME_TO_OIDS[hash_algorithm - .getattr(pyo3::intern!(py, "name"))? - .extract::<&str>()?] - .clone(), - params: Some(*x509::sign::NULL_TLV), - }, - issuer_name_hash, - issuer_key_hash, - serial_number: cert.raw.borrow_value_public().tbs_cert.serial, - }) - } + Ok(CertID { + hash_algorithm: common::AlgorithmIdentifier { + oid: HASH_NAME_TO_OIDS[hash_algorithm + .getattr(pyo3::intern!(py, "name"))? + .extract::<&str>()?] + .clone(), + params: Some(*x509::sign::NULL_TLV), + }, + issuer_name_hash, + issuer_key_hash, + serial_number: cert.raw.borrow_value_public().tbs_cert.serial, + }) +} - pub(crate) fn new_from_hash<'p>( - py: pyo3::Python<'p>, - issuer_name_hash: &'p [u8], - issuer_key_hash: &'p [u8], - serial_number: asn1::BigInt<'p>, - hash_algorithm: &'p pyo3::PyAny, - ) -> CryptographyResult<CertID<'p>> { - Ok(CertID { - hash_algorithm: x509::AlgorithmIdentifier { - oid: HASH_NAME_TO_OIDS[hash_algorithm - .getattr(pyo3::intern!(py, "name"))? - .extract::<&str>()?] - .clone(), - params: Some(*x509::sign::NULL_TLV), - }, - issuer_name_hash, - issuer_key_hash, - serial_number, - }) - } +pub(crate) fn certid_new_from_hash<'p>( + py: pyo3::Python<'p>, + issuer_name_hash: &'p [u8], + issuer_key_hash: &'p [u8], + serial_number: asn1::BigInt<'p>, + hash_algorithm: &'p pyo3::PyAny, +) -> CryptographyResult<CertID<'p>> { + Ok(CertID { + hash_algorithm: common::AlgorithmIdentifier { + oid: HASH_NAME_TO_OIDS[hash_algorithm + .getattr(pyo3::intern!(py, "name"))? + .extract::<&str>()?] + .clone(), + params: Some(*x509::sign::NULL_TLV), + }, + issuer_name_hash, + issuer_key_hash, + serial_number, + }) } pub(crate) fn hash_data<'p>( diff --git a/src/rust/src/x509/ocsp_req.rs b/src/rust/src/x509/ocsp_req.rs index afd939d47..856c60c93 100644 --- a/src/rust/src/x509/ocsp_req.rs +++ b/src/rust/src/x509/ocsp_req.rs @@ -5,15 +5,16 @@ use crate::asn1::{big_byte_slice_to_py_int, oid_to_py_oid, py_uint_to_big_endian_bytes}; use crate::error::{CryptographyError, CryptographyResult}; use crate::x509; -use crate::x509::{extensions, ocsp, oid}; +use crate::x509::{extensions, ocsp}; +use cryptography_x509::{common, ocsp_req, oid}; use pyo3::IntoPy; #[ouroboros::self_referencing] -struct OwnedRawOCSPRequest { +struct OwnedOCSPRequest { data: pyo3::Py<pyo3::types::PyBytes>, #[borrows(data)] #[covariant] - value: RawOCSPRequest<'this>, + value: ocsp_req::OCSPRequest<'this>, } #[pyo3::prelude::pyfunction] @@ -21,7 +22,7 @@ fn load_der_ocsp_request( py: pyo3::Python<'_>, data: pyo3::Py<pyo3::types::PyBytes>, ) -> CryptographyResult<OCSPRequest> { - let raw = OwnedRawOCSPRequest::try_new(data, |data| asn1::parse_single(data.as_bytes(py)))?; + let raw = OwnedOCSPRequest::try_new(data, |data| asn1::parse_single(data.as_bytes(py)))?; if raw .borrow_value() @@ -46,13 +47,13 @@ fn load_der_ocsp_request( #[pyo3::prelude::pyclass(module = "cryptography.hazmat.bindings._rust.ocsp")] struct OCSPRequest { - raw: OwnedRawOCSPRequest, + raw: OwnedOCSPRequest, cached_extensions: Option<pyo3::PyObject>, } impl OCSPRequest { - fn cert_id(&self) -> ocsp::CertID<'_> { + fn cert_id(&self) -> ocsp_req::CertID<'_> { self.raw .borrow_value() .tbs_request @@ -174,39 +175,6 @@ impl OCSPRequest { } } -#[derive(asn1::Asn1Read, asn1::Asn1Write)] -struct RawOCSPRequest<'a> { - tbs_request: TBSRequest<'a>, - // Parsing out the full structure, which includes the entirety of a - // certificate is more trouble than it's worth, since it's not in the - // Python API. - #[explicit(0)] - optional_signature: Option<asn1::Sequence<'a>>, -} - -#[derive(asn1::Asn1Read, asn1::Asn1Write)] -struct TBSRequest<'a> { - #[explicit(0)] - #[default(0)] - version: u8, - #[explicit(1)] - requestor_name: Option<x509::GeneralName<'a>>, - request_list: x509::Asn1ReadableOrWritable< - 'a, - asn1::SequenceOf<'a, Request<'a>>, - asn1::SequenceOfWriter<'a, Request<'a>>, - >, - #[explicit(2)] - request_extensions: Option<x509::Extensions<'a>>, -} - -#[derive(asn1::Asn1Read, asn1::Asn1Write)] -struct Request<'a> { - req_cert: ocsp::CertID<'a>, - #[explicit(0)] - single_request_extensions: Option<x509::Extensions<'a>>, -} - #[pyo3::prelude::pyfunction] fn create_ocsp_request( py: pyo3::Python<'_>, @@ -216,20 +184,20 @@ fn create_ocsp_request( // Declare outside the if-block so the lifetimes are right. let (py_cert, py_issuer, py_hash): ( - pyo3::PyRef<'_, x509::Certificate>, - pyo3::PyRef<'_, x509::Certificate>, + pyo3::PyRef<'_, x509::certificate::Certificate>, + pyo3::PyRef<'_, x509::certificate::Certificate>, &pyo3::PyAny, ); let req_cert = if !builder_request.is_none() { let tuple = builder_request.extract::<( - pyo3::PyRef<'_, x509::Certificate>, - pyo3::PyRef<'_, x509::Certificate>, + pyo3::PyRef<'_, x509::certificate::Certificate>, + pyo3::PyRef<'_, x509::certificate::Certificate>, &pyo3::PyAny, )>()?; py_cert = tuple.0; py_issuer = tuple.1; py_hash = tuple.2; - ocsp::CertID::new(py, &py_cert, &py_issuer, py_hash)? + ocsp::certid_new(py, &py_cert, &py_issuer, py_hash)? } else { let (issuer_name_hash, issuer_key_hash, py_serial, py_hash): ( &[u8], @@ -240,7 +208,7 @@ fn create_ocsp_request( .getattr(pyo3::intern!(py, "_request_hash"))? .extract()?; let serial_number = asn1::BigInt::new(py_uint_to_big_endian_bytes(py, py_serial)?).unwrap(); - ocsp::CertID::new_from_hash( + ocsp::certid_new_from_hash( py, issuer_name_hash, issuer_key_hash, @@ -254,15 +222,15 @@ fn create_ocsp_request( builder.getattr(pyo3::intern!(py, "_extensions"))?, extensions::encode_extension, )?; - let reqs = [Request { + let reqs = [ocsp_req::Request { req_cert, single_request_extensions: None, }]; - let ocsp_req = RawOCSPRequest { - tbs_request: TBSRequest { + let ocsp_req = ocsp_req::OCSPRequest { + tbs_request: ocsp_req::TBSRequest { version: 0, requestor_name: None, - request_list: x509::Asn1ReadableOrWritable::new_write(asn1::SequenceOfWriter::new( + request_list: common::Asn1ReadableOrWritable::new_write(asn1::SequenceOfWriter::new( &reqs, )), request_extensions: extensions, diff --git a/src/rust/src/x509/ocsp_resp.rs b/src/rust/src/x509/ocsp_resp.rs index 0b2cab5f0..ffbf9c88a 100644 --- a/src/rust/src/x509/ocsp_resp.rs +++ b/src/rust/src/x509/ocsp_resp.rs @@ -5,7 +5,10 @@ use crate::asn1::{big_byte_slice_to_py_int, oid_to_py_oid}; use crate::error::{CryptographyError, CryptographyResult}; use crate::x509; -use crate::x509::{certificate, crl, extensions, ocsp, oid, py_to_datetime, sct}; +use crate::x509::{certificate, crl, extensions, ocsp, py_to_datetime, sct}; +use cryptography_x509::crl::CRLReason; +use cryptography_x509::extensions::Extensions; +use cryptography_x509::{common, name, ocsp_req, oid}; use pyo3::IntoPy; use std::sync::Arc; @@ -233,7 +236,7 @@ impl OCSPResponse { }); py_certs.append(pyo3::PyCell::new( py, - x509::Certificate { + x509::certificate::Certificate { raw: raw_cert, cached_extensions: None, }, @@ -407,9 +410,9 @@ fn map_arc_data_ocsp_response( f: impl for<'this> FnOnce( &'this [u8], &RawOCSPResponse<'this>, - ) -> certificate::RawCertificate<'this>, -) -> certificate::OwnedRawCertificate { - certificate::OwnedRawCertificate::new_public(it.borrow_data().clone_ref(py), |inner_it| { + ) -> cryptography_x509::certificate::Certificate<'this>, +) -> certificate::OwnedCertificate { + certificate::OwnedCertificate::new_public(it.borrow_data().clone_ref(py), |inner_it| { it.with(|value| { f(inner_it.as_bytes(py), unsafe { std::mem::transmute(value.value) @@ -443,13 +446,13 @@ struct ResponseBytes<'a> { } type OCSPCerts<'a> = Option< - x509::Asn1ReadableOrWritable< + common::Asn1ReadableOrWritable< 'a, - asn1::SequenceOf<'a, certificate::RawCertificate<'a>>, + asn1::SequenceOf<'a, cryptography_x509::certificate::Certificate<'a>>, asn1::SequenceOfWriter< 'a, - certificate::RawCertificate<'a>, - Vec<certificate::RawCertificate<'a>>, + cryptography_x509::certificate::Certificate<'a>, + Vec<cryptography_x509::certificate::Certificate<'a>>, >, >, >; @@ -457,7 +460,7 @@ type OCSPCerts<'a> = Option< #[derive(asn1::Asn1Read, asn1::Asn1Write)] struct BasicOCSPResponse<'a> { tbs_response_data: ResponseData<'a>, - signature_algorithm: x509::AlgorithmIdentifier<'a>, + signature_algorithm: common::AlgorithmIdentifier<'a>, signature: asn1::BitString<'a>, #[explicit(0)] certs: OCSPCerts<'a>, @@ -488,32 +491,32 @@ struct ResponseData<'a> { version: u8, responder_id: ResponderId<'a>, produced_at: asn1::GeneralizedTime, - responses: x509::Asn1ReadableOrWritable< + responses: common::Asn1ReadableOrWritable< 'a, asn1::SequenceOf<'a, SingleResponse<'a>>, asn1::SequenceOfWriter<'a, SingleResponse<'a>, Vec<SingleResponse<'a>>>, >, #[explicit(1)] - response_extensions: Option<x509::Extensions<'a>>, + response_extensions: Option<Extensions<'a>>, } #[derive(asn1::Asn1Read, asn1::Asn1Write)] enum ResponderId<'a> { #[explicit(1)] - ByName(x509::Name<'a>), + ByName(name::Name<'a>), #[explicit(2)] ByKey(&'a [u8]), } #[derive(asn1::Asn1Read, asn1::Asn1Write)] struct SingleResponse<'a> { - cert_id: ocsp::CertID<'a>, + cert_id: ocsp_req::CertID<'a>, cert_status: CertStatus, this_update: asn1::GeneralizedTime, #[explicit(0)] next_update: Option<asn1::GeneralizedTime>, #[explicit(1)] - single_extensions: Option<x509::Extensions<'a>>, + single_extensions: Option<Extensions<'a>>, } impl SingleResponse<'_> { @@ -601,7 +604,7 @@ enum CertStatus { struct RevokedInfo { revocation_time: asn1::GeneralizedTime, #[explicit(0)] - revocation_reason: Option<crl::CRLReason>, + revocation_reason: Option<CRLReason>, } #[pyo3::prelude::pyfunction] @@ -616,10 +619,10 @@ fn create_ocsp_response( .getattr(pyo3::intern!(py, "value"))? .extract::<u32>()?; - let py_cert: pyo3::PyRef<'_, x509::Certificate>; - let py_issuer: pyo3::PyRef<'_, x509::Certificate>; + let py_cert: pyo3::PyRef<'_, x509::certificate::Certificate>; + let py_issuer: pyo3::PyRef<'_, x509::certificate::Certificate>; let borrowed_cert; - let py_certs: Option<Vec<pyo3::PyRef<'_, x509::Certificate>>>; + let py_certs: Option<Vec<pyo3::PyRef<'_, x509::certificate::Certificate>>>; let response_bytes = if response_status == SUCCESSFUL_RESPONSE { let ocsp_mod = py.import(pyo3::intern!(py, "cryptography.x509.ocsp"))?; @@ -631,10 +634,12 @@ fn create_ocsp_response( .getattr(pyo3::intern!(py, "_issuer"))? .extract()?; let py_cert_hash_algorithm = py_single_resp.getattr(pyo3::intern!(py, "_algorithm"))?; - let (responder_cert, responder_encoding): (&pyo3::PyCell<x509::Certificate>, &pyo3::PyAny) = - builder - .getattr(pyo3::intern!(py, "_responder_id"))? - .extract()?; + let (responder_cert, responder_encoding): ( + &pyo3::PyCell<x509::certificate::Certificate>, + &pyo3::PyAny, + ) = builder + .getattr(pyo3::intern!(py, "_responder_id"))? + .extract()?; let py_cert_status = py_single_resp.getattr(pyo3::intern!(py, "_cert_status"))?; let cert_status = if py_cert_status.is(ocsp_mod @@ -690,7 +695,7 @@ fn create_ocsp_response( let this_update = asn1::GeneralizedTime::new(py_to_datetime(py, py_this_update)?)?; let responses = vec![SingleResponse { - cert_id: ocsp::CertID::new(py, &py_cert, &py_issuer, py_cert_hash_algorithm)?, + cert_id: ocsp::certid_new(py, &py_cert, &py_issuer, py_cert_hash_algorithm)?, cert_status, next_update, this_update, @@ -732,7 +737,7 @@ fn create_ocsp_response( version: 0, produced_at: asn1::GeneralizedTime::new(x509::common::datetime_now(py)?)?, responder_id, - responses: x509::Asn1ReadableOrWritable::new_write(asn1::SequenceOfWriter::new( + responses: common::Asn1ReadableOrWritable::new_write(asn1::SequenceOfWriter::new( responses, )), response_extensions: x509::common::encode_extensions( @@ -759,7 +764,7 @@ fn create_ocsp_response( py_certs = builder.getattr(pyo3::intern!(py, "_certs"))?.extract()?; let certs = py_certs.as_ref().map(|py_certs| { - x509::Asn1ReadableOrWritable::new_write(asn1::SequenceOfWriter::new( + common::Asn1ReadableOrWritable::new_write(asn1::SequenceOfWriter::new( py_certs .iter() .map(|c| c.raw.borrow_value_public().clone()) diff --git a/src/rust/src/x509/oid.rs b/src/rust/src/x509/oid.rs deleted file mode 100644 index b2e3a36ac..000000000 --- a/src/rust/src/x509/oid.rs +++ /dev/null @@ -1,101 +0,0 @@ -// This file is dual licensed under the terms of the Apache License, Version -// 2.0, and the BSD License. See the LICENSE file in the root of this repository -// for complete details. - -pub(crate) const EXTENSION_REQUEST: asn1::ObjectIdentifier = - asn1::oid!(1, 2, 840, 113549, 1, 9, 14); -pub(crate) const MS_EXTENSION_REQUEST: asn1::ObjectIdentifier = - asn1::oid!(1, 3, 6, 1, 4, 1, 311, 2, 1, 14); -pub(crate) const MS_CERTIFICATE_TEMPLATE: asn1::ObjectIdentifier = - asn1::oid!(1, 3, 6, 1, 4, 1, 311, 21, 7); -pub(crate) const PRECERT_SIGNED_CERTIFICATE_TIMESTAMPS_OID: asn1::ObjectIdentifier = - asn1::oid!(1, 3, 6, 1, 4, 1, 11129, 2, 4, 2); -pub(crate) const PRECERT_POISON_OID: asn1::ObjectIdentifier = - asn1::oid!(1, 3, 6, 1, 4, 1, 11129, 2, 4, 3); -pub(crate) const SIGNED_CERTIFICATE_TIMESTAMPS_OID: asn1::ObjectIdentifier = - asn1::oid!(1, 3, 6, 1, 4, 1, 11129, 2, 4, 5); -pub(crate) const AUTHORITY_INFORMATION_ACCESS_OID: asn1::ObjectIdentifier = - asn1::oid!(1, 3, 6, 1, 5, 5, 7, 1, 1); -pub(crate) const SUBJECT_INFORMATION_ACCESS_OID: asn1::ObjectIdentifier = - asn1::oid!(1, 3, 6, 1, 5, 5, 7, 1, 11); -pub(crate) const TLS_FEATURE_OID: asn1::ObjectIdentifier = asn1::oid!(1, 3, 6, 1, 5, 5, 7, 1, 24); -pub(crate) const CP_CPS_URI_OID: asn1::ObjectIdentifier = asn1::oid!(1, 3, 6, 1, 5, 5, 7, 2, 1); -pub(crate) const CP_USER_NOTICE_OID: asn1::ObjectIdentifier = asn1::oid!(1, 3, 6, 1, 5, 5, 7, 2, 2); -pub(crate) const NONCE_OID: asn1::ObjectIdentifier = asn1::oid!(1, 3, 6, 1, 5, 5, 7, 48, 1, 2); -pub(crate) const OCSP_NO_CHECK_OID: asn1::ObjectIdentifier = - asn1::oid!(1, 3, 6, 1, 5, 5, 7, 48, 1, 5); -pub(crate) const SUBJECT_KEY_IDENTIFIER_OID: asn1::ObjectIdentifier = asn1::oid!(2, 5, 29, 14); -pub(crate) const KEY_USAGE_OID: asn1::ObjectIdentifier = asn1::oid!(2, 5, 29, 15); -pub(crate) const SUBJECT_ALTERNATIVE_NAME_OID: asn1::ObjectIdentifier = asn1::oid!(2, 5, 29, 17); -pub(crate) const ISSUER_ALTERNATIVE_NAME_OID: asn1::ObjectIdentifier = asn1::oid!(2, 5, 29, 18); -pub(crate) const BASIC_CONSTRAINTS_OID: asn1::ObjectIdentifier = asn1::oid!(2, 5, 29, 19); -pub(crate) const CRL_NUMBER_OID: asn1::ObjectIdentifier = asn1::oid!(2, 5, 29, 20); -pub(crate) const CRL_REASON_OID: asn1::ObjectIdentifier = asn1::oid!(2, 5, 29, 21); -pub(crate) const INVALIDITY_DATE_OID: asn1::ObjectIdentifier = asn1::oid!(2, 5, 29, 24); -pub(crate) const DELTA_CRL_INDICATOR_OID: asn1::ObjectIdentifier = asn1::oid!(2, 5, 29, 27); -pub(crate) const ISSUING_DISTRIBUTION_POINT_OID: asn1::ObjectIdentifier = asn1::oid!(2, 5, 29, 28); -pub(crate) const CERTIFICATE_ISSUER_OID: asn1::ObjectIdentifier = asn1::oid!(2, 5, 29, 29); -pub(crate) const NAME_CONSTRAINTS_OID: asn1::ObjectIdentifier = asn1::oid!(2, 5, 29, 30); -pub(crate) const CRL_DISTRIBUTION_POINTS_OID: asn1::ObjectIdentifier = asn1::oid!(2, 5, 29, 31); -pub(crate) const CERTIFICATE_POLICIES_OID: asn1::ObjectIdentifier = asn1::oid!(2, 5, 29, 32); -pub(crate) const AUTHORITY_KEY_IDENTIFIER_OID: asn1::ObjectIdentifier = asn1::oid!(2, 5, 29, 35); -pub(crate) const POLICY_CONSTRAINTS_OID: asn1::ObjectIdentifier = asn1::oid!(2, 5, 29, 36); -pub(crate) const EXTENDED_KEY_USAGE_OID: asn1::ObjectIdentifier = asn1::oid!(2, 5, 29, 37); -pub(crate) const FRESHEST_CRL_OID: asn1::ObjectIdentifier = asn1::oid!(2, 5, 29, 46); -pub(crate) const INHIBIT_ANY_POLICY_OID: asn1::ObjectIdentifier = asn1::oid!(2, 5, 29, 54); -pub(crate) const ACCEPTABLE_RESPONSES_OID: asn1::ObjectIdentifier = - asn1::oid!(1, 3, 6, 1, 5, 5, 7, 48, 1, 4); - -// Signing methods -pub(crate) const ECDSA_WITH_SHA224_OID: asn1::ObjectIdentifier = - asn1::oid!(1, 2, 840, 10045, 4, 3, 1); -pub(crate) const ECDSA_WITH_SHA256_OID: asn1::ObjectIdentifier = - asn1::oid!(1, 2, 840, 10045, 4, 3, 2); -pub(crate) const ECDSA_WITH_SHA384_OID: asn1::ObjectIdentifier = - asn1::oid!(1, 2, 840, 10045, 4, 3, 3); -pub(crate) const ECDSA_WITH_SHA512_OID: asn1::ObjectIdentifier = - asn1::oid!(1, 2, 840, 10045, 4, 3, 4); -pub(crate) const ECDSA_WITH_SHA3_224_OID: asn1::ObjectIdentifier = - asn1::oid!(2, 16, 840, 1, 101, 3, 4, 3, 9); -pub(crate) const ECDSA_WITH_SHA3_256_OID: asn1::ObjectIdentifier = - asn1::oid!(2, 16, 840, 1, 101, 3, 4, 3, 10); -pub(crate) const ECDSA_WITH_SHA3_384_OID: asn1::ObjectIdentifier = - asn1::oid!(2, 16, 840, 1, 101, 3, 4, 3, 11); -pub(crate) const ECDSA_WITH_SHA3_512_OID: asn1::ObjectIdentifier = - asn1::oid!(2, 16, 840, 1, 101, 3, 4, 3, 12); - -pub(crate) const RSA_WITH_SHA224_OID: asn1::ObjectIdentifier = - asn1::oid!(1, 2, 840, 113549, 1, 1, 14); -pub(crate) const RSA_WITH_SHA256_OID: asn1::ObjectIdentifier = - asn1::oid!(1, 2, 840, 113549, 1, 1, 11); -pub(crate) const RSA_WITH_SHA384_OID: asn1::ObjectIdentifier = - asn1::oid!(1, 2, 840, 113549, 1, 1, 12); -pub(crate) const RSA_WITH_SHA512_OID: asn1::ObjectIdentifier = - asn1::oid!(1, 2, 840, 113549, 1, 1, 13); -pub(crate) const RSA_WITH_SHA3_224_OID: asn1::ObjectIdentifier = - asn1::oid!(2, 16, 840, 1, 101, 3, 4, 3, 13); -pub(crate) const RSA_WITH_SHA3_256_OID: asn1::ObjectIdentifier = - asn1::oid!(2, 16, 840, 1, 101, 3, 4, 3, 14); -pub(crate) const RSA_WITH_SHA3_384_OID: asn1::ObjectIdentifier = - asn1::oid!(2, 16, 840, 1, 101, 3, 4, 3, 15); -pub(crate) const RSA_WITH_SHA3_512_OID: asn1::ObjectIdentifier = - asn1::oid!(2, 16, 840, 1, 101, 3, 4, 3, 16); - -pub(crate) const DSA_WITH_SHA224_OID: asn1::ObjectIdentifier = - asn1::oid!(2, 16, 840, 1, 101, 3, 4, 3, 1); -pub(crate) const DSA_WITH_SHA256_OID: asn1::ObjectIdentifier = - asn1::oid!(2, 16, 840, 1, 101, 3, 4, 3, 2); -pub(crate) const DSA_WITH_SHA384_OID: asn1::ObjectIdentifier = - asn1::oid!(2, 16, 840, 1, 101, 3, 4, 3, 3); -pub(crate) const DSA_WITH_SHA512_OID: asn1::ObjectIdentifier = - asn1::oid!(2, 16, 840, 1, 101, 3, 4, 3, 4); - -pub(crate) const ED25519_OID: asn1::ObjectIdentifier = asn1::oid!(1, 3, 101, 112); -pub(crate) const ED448_OID: asn1::ObjectIdentifier = asn1::oid!(1, 3, 101, 113); - -// Hashes -pub(crate) const SHA1_OID: asn1::ObjectIdentifier = asn1::oid!(1, 3, 14, 3, 2, 26); -pub(crate) const SHA224_OID: asn1::ObjectIdentifier = asn1::oid!(2, 16, 840, 1, 101, 3, 4, 2, 4); -pub(crate) const SHA256_OID: asn1::ObjectIdentifier = asn1::oid!(2, 16, 840, 1, 101, 3, 4, 2, 1); -pub(crate) const SHA384_OID: asn1::ObjectIdentifier = asn1::oid!(2, 16, 840, 1, 101, 3, 4, 2, 2); -pub(crate) const SHA512_OID: asn1::ObjectIdentifier = asn1::oid!(2, 16, 840, 1, 101, 3, 4, 2, 3); diff --git a/src/rust/src/x509/sign.rs b/src/rust/src/x509/sign.rs index 4be023bb2..12579b35e 100644 --- a/src/rust/src/x509/sign.rs +++ b/src/rust/src/x509/sign.rs @@ -3,8 +3,7 @@ // for complete details. use crate::error::{CryptographyError, CryptographyResult}; -use crate::x509; -use crate::x509::oid; +use cryptography_x509::{common, oid}; use once_cell::sync::Lazy; @@ -138,16 +137,16 @@ pub(crate) fn compute_signature_algorithm<'p>( py: pyo3::Python<'p>, private_key: &'p pyo3::PyAny, hash_algorithm: &'p pyo3::PyAny, -) -> pyo3::PyResult<x509::AlgorithmIdentifier<'static>> { +) -> pyo3::PyResult<common::AlgorithmIdentifier<'static>> { let key_type = identify_key_type(py, private_key)?; let hash_type = identify_hash_type(py, hash_algorithm)?; match (key_type, hash_type) { - (KeyType::Ed25519, HashType::None) => Ok(x509::AlgorithmIdentifier { + (KeyType::Ed25519, HashType::None) => Ok(common::AlgorithmIdentifier { oid: (oid::ED25519_OID).clone(), params: None, }), - (KeyType::Ed448, HashType::None) => Ok(x509::AlgorithmIdentifier { + (KeyType::Ed448, HashType::None) => Ok(common::AlgorithmIdentifier { oid: (oid::ED448_OID).clone(), params: None, }), @@ -155,85 +154,85 @@ pub(crate) fn compute_signature_algorithm<'p>( "Algorithm must be None when signing via ed25519 or ed448", )), - (KeyType::Ec, HashType::Sha224) => Ok(x509::AlgorithmIdentifier { + (KeyType::Ec, HashType::Sha224) => Ok(common::AlgorithmIdentifier { oid: (oid::ECDSA_WITH_SHA224_OID).clone(), params: None, }), - (KeyType::Ec, HashType::Sha256) => Ok(x509::AlgorithmIdentifier { + (KeyType::Ec, HashType::Sha256) => Ok(common::AlgorithmIdentifier { oid: (oid::ECDSA_WITH_SHA256_OID).clone(), params: None, }), - (KeyType::Ec, HashType::Sha384) => Ok(x509::AlgorithmIdentifier { + (KeyType::Ec, HashType::Sha384) => Ok(common::AlgorithmIdentifier { oid: (oid::ECDSA_WITH_SHA384_OID).clone(), params: None, }), - (KeyType::Ec, HashType::Sha512) => Ok(x509::AlgorithmIdentifier { + (KeyType::Ec, HashType::Sha512) => Ok(common::AlgorithmIdentifier { oid: (oid::ECDSA_WITH_SHA512_OID).clone(), params: None, }), - (KeyType::Ec, HashType::Sha3_224) => Ok(x509::AlgorithmIdentifier { + (KeyType::Ec, HashType::Sha3_224) => Ok(common::AlgorithmIdentifier { oid: (oid::ECDSA_WITH_SHA3_224_OID).clone(), params: None, }), - (KeyType::Ec, HashType::Sha3_256) => Ok(x509::AlgorithmIdentifier { + (KeyType::Ec, HashType::Sha3_256) => Ok(common::AlgorithmIdentifier { oid: (oid::ECDSA_WITH_SHA3_256_OID).clone(), params: None, }), - (KeyType::Ec, HashType::Sha3_384) => Ok(x509::AlgorithmIdentifier { + (KeyType::Ec, HashType::Sha3_384) => Ok(common::AlgorithmIdentifier { oid: (oid::ECDSA_WITH_SHA3_384_OID).clone(), params: None, }), - (KeyType::Ec, HashType::Sha3_512) => Ok(x509::AlgorithmIdentifier { + (KeyType::Ec, HashType::Sha3_512) => Ok(common::AlgorithmIdentifier { oid: (oid::ECDSA_WITH_SHA3_512_OID).clone(), params: None, }), - (KeyType::Rsa, HashType::Sha224) => Ok(x509::AlgorithmIdentifier { + (KeyType::Rsa, HashType::Sha224) => Ok(common::AlgorithmIdentifier { oid: (oid::RSA_WITH_SHA224_OID).clone(), params: Some(*NULL_TLV), }), - (KeyType::Rsa, HashType::Sha256) => Ok(x509::AlgorithmIdentifier { + (KeyType::Rsa, HashType::Sha256) => Ok(common::AlgorithmIdentifier { oid: (oid::RSA_WITH_SHA256_OID).clone(), params: Some(*NULL_TLV), }), - (KeyType::Rsa, HashType::Sha384) => Ok(x509::AlgorithmIdentifier { + (KeyType::Rsa, HashType::Sha384) => Ok(common::AlgorithmIdentifier { oid: (oid::RSA_WITH_SHA384_OID).clone(), params: Some(*NULL_TLV), }), - (KeyType::Rsa, HashType::Sha512) => Ok(x509::AlgorithmIdentifier { + (KeyType::Rsa, HashType::Sha512) => Ok(common::AlgorithmIdentifier { oid: (oid::RSA_WITH_SHA512_OID).clone(), params: Some(*NULL_TLV), }), - (KeyType::Rsa, HashType::Sha3_224) => Ok(x509::AlgorithmIdentifier { + (KeyType::Rsa, HashType::Sha3_224) => Ok(common::AlgorithmIdentifier { oid: (oid::RSA_WITH_SHA3_224_OID).clone(), params: Some(*NULL_TLV), }), - (KeyType::Rsa, HashType::Sha3_256) => Ok(x509::AlgorithmIdentifier { + (KeyType::Rsa, HashType::Sha3_256) => Ok(common::AlgorithmIdentifier { oid: (oid::RSA_WITH_SHA3_256_OID).clone(), params: Some(*NULL_TLV), }), - (KeyType::Rsa, HashType::Sha3_384) => Ok(x509::AlgorithmIdentifier { + (KeyType::Rsa, HashType::Sha3_384) => Ok(common::AlgorithmIdentifier { oid: (oid::RSA_WITH_SHA3_384_OID).clone(), params: Some(*NULL_TLV), }), - (KeyType::Rsa, HashType::Sha3_512) => Ok(x509::AlgorithmIdentifier { + (KeyType::Rsa, HashType::Sha3_512) => Ok(common::AlgorithmIdentifier { oid: (oid::RSA_WITH_SHA3_512_OID).clone(), params: Some(*NULL_TLV), }), - (KeyType::Dsa, HashType::Sha224) => Ok(x509::AlgorithmIdentifier { + (KeyType::Dsa, HashType::Sha224) => Ok(common::AlgorithmIdentifier { oid: (oid::DSA_WITH_SHA224_OID).clone(), params: None, }), - (KeyType::Dsa, HashType::Sha256) => Ok(x509::AlgorithmIdentifier { + (KeyType::Dsa, HashType::Sha256) => Ok(common::AlgorithmIdentifier { oid: (oid::DSA_WITH_SHA256_OID).clone(), params: None, }), - (KeyType::Dsa, HashType::Sha384) => Ok(x509::AlgorithmIdentifier { + (KeyType::Dsa, HashType::Sha384) => Ok(common::AlgorithmIdentifier { oid: (oid::DSA_WITH_SHA384_OID).clone(), params: None, }), - (KeyType::Dsa, HashType::Sha512) => Ok(x509::AlgorithmIdentifier { + (KeyType::Dsa, HashType::Sha512) => Ok(common::AlgorithmIdentifier { oid: (oid::DSA_WITH_SHA512_OID).clone(), params: None, }), @@ -456,7 +455,7 @@ fn identify_key_hash_type_for_oid( #[cfg(test)] mod tests { use super::{identify_key_hash_type_for_oid, py_hash_name_from_hash_type, HashType, KeyType}; - use crate::x509::oid; + use cryptography_x509::oid; #[test] fn test_identify_key_hash_type_for_oid() { |
