From f6654c6c17f1efad5a2f0c8ae73bf9df04f9d1f5 Mon Sep 17 00:00:00 2001 From: Arthur Gautier Date: Sat, 26 Nov 2022 21:28:03 -0800 Subject: [PATCH 1/9] der: adds a ByteVec, owned alternative to ByteSlice --- der/src/byte_vec.rs | 106 ++++++++++++++++++++++++++++++++++++++++++++ der/src/lib.rs | 4 ++ 2 files changed, 110 insertions(+) create mode 100644 der/src/byte_vec.rs diff --git a/der/src/byte_vec.rs b/der/src/byte_vec.rs new file mode 100644 index 000000000..39015c2db --- /dev/null +++ b/der/src/byte_vec.rs @@ -0,0 +1,106 @@ +//! Common handling for types backed by byte slices with enforcement of a +//! library-level length limitation i.e. `Length::max()`. + +use crate::{ + str_slice::StrSlice, DecodeValue, DerOrd, EncodeValue, Error, Header, Length, Reader, Result, + Writer, +}; +use alloc::vec::Vec; +use core::cmp::Ordering; + +/// Byte slice newtype which respects the `Length::max()` limit. +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub(crate) struct ByteVec { + /// Precomputed `Length` (avoids possible panicking conversions) + length: Length, + + /// Inner value + inner: Vec, +} + +impl ByteVec { + /// Create a new [`ByteVec`], ensuring that the provided `slice` value + /// is shorter than `Length::max()`. + pub fn new(slice: &[u8]) -> Result { + Ok(Self { + length: Length::try_from(slice.len())?, + inner: Vec::from(slice), + }) + } + + /// Borrow the inner byte slice + pub fn as_slice(&self) -> &[u8] { + &self.inner + } + + /// Get the [`Length`] of this [`ByteSlice`] + pub fn len(&self) -> Length { + self.length + } +} + +impl AsRef<[u8]> for ByteVec { + fn as_ref(&self) -> &[u8] { + self.as_slice() + } +} + +impl<'a> DecodeValue<'a> for ByteVec { + fn decode_value>(reader: &mut R, header: Header) -> Result { + reader.read_slice(header.length).and_then(Self::new) + } +} + +impl EncodeValue for ByteVec { + fn value_len(&self) -> Result { + Ok(self.length) + } + + fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> { + writer.write(self.as_ref()) + } +} + +impl Default for ByteVec { + fn default() -> Self { + Self { + length: Length::ZERO, + inner: Vec::new(), + } + } +} + +impl DerOrd for ByteVec { + fn der_cmp(&self, other: &Self) -> Result { + Ok(self.as_slice().cmp(other.as_slice())) + } +} + +impl From<&[u8; 1]> for ByteVec { + fn from(byte: &[u8; 1]) -> ByteVec { + Self { + length: Length::ONE, + inner: vec![byte[0]], + } + } +} + +impl From> for ByteVec { + fn from(s: StrSlice<'_>) -> ByteVec { + let bytes = s.as_bytes(); + debug_assert_eq!(bytes.len(), usize::try_from(s.length).expect("overflow")); + + ByteVec { + inner: Vec::from(bytes), + length: s.length, + } + } +} + +impl TryFrom<&[u8]> for ByteVec { + type Error = Error; + + fn try_from(slice: &[u8]) -> Result { + Self::new(slice) + } +} diff --git a/der/src/lib.rs b/der/src/lib.rs index d8a50a0d7..0ef8a6c6e 100644 --- a/der/src/lib.rs +++ b/der/src/lib.rs @@ -356,6 +356,8 @@ mod str_slice; mod tag; mod writer; +#[cfg(feature = "alloc")] +mod byte_vec; #[cfg(feature = "alloc")] mod document; @@ -406,4 +408,6 @@ pub use zeroize; #[cfg(all(feature = "alloc", feature = "zeroize"))] pub use crate::document::SecretDocument; +#[cfg(feature = "alloc")] +pub(crate) use crate::byte_vec::ByteVec; pub(crate) use crate::{arrayvec::ArrayVec, byte_slice::ByteSlice, str_slice::StrSlice}; From ef80cbad5384bf7ba25078f1dd27a17f60e620f0 Mon Sep 17 00:00:00 2001 From: Arthur Gautier Date: Sat, 26 Nov 2022 21:28:54 -0800 Subject: [PATCH 2/9] der: Any now uses the new ByteVec --- der/src/asn1/any.rs | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/der/src/asn1/any.rs b/der/src/asn1/any.rs index cb65f2391..32ca79d1f 100644 --- a/der/src/asn1/any.rs +++ b/der/src/asn1/any.rs @@ -7,7 +7,7 @@ use crate::{ use core::cmp::Ordering; #[cfg(feature = "alloc")] -use alloc::vec::Vec; +use crate::ByteVec; #[cfg(feature = "oid")] use crate::asn1::ObjectIdentifier; @@ -226,19 +226,35 @@ pub struct Any { tag: Tag, /// Inner value encoded as bytes. - value: Vec, + value: ByteVec, } #[cfg(feature = "alloc")] impl Any { /// Create a new [`Any`] from the provided [`Tag`] and DER bytes. - pub fn new(tag: Tag, bytes: impl Into>) -> Result { - let value = bytes.into(); + pub fn new(tag: Tag, bytes: &[u8]) -> Result { + let value = ByteVec::new(bytes)?; // Ensure the tag and value are a valid `AnyRef`. - AnyRef::new(tag, &value)?; + AnyRef::new(tag, value.as_slice())?; Ok(Self { tag, value }) } + + /// Attempt to decode this [`Any`] type into the inner value. + pub fn decode_into<'a, T>(&'a self) -> Result + where + T: DecodeValue<'a> + FixedTag, + { + self.tag.assert_eq(T::TAG)?; + let header = Header { + tag: self.tag, + length: self.value.len(), + }; + + let mut decoder = SliceReader::new(self.value.as_slice())?; + let result = T::decode_value(&mut decoder, header)?; + decoder.finish(result) + } } #[cfg(feature = "alloc")] @@ -253,18 +269,18 @@ impl<'a> Decode<'a> for Any { fn decode>(reader: &mut R) -> Result { let header = Header::decode(reader)?; let value = reader.read_vec(header.length)?; - Self::new(header.tag, value) + Self::new(header.tag, &value) } } #[cfg(feature = "alloc")] impl EncodeValue for Any { fn value_len(&self) -> Result { - self.value.len().try_into() + Ok(self.value.len()) } fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> { - writer.write(&self.value) + writer.write(self.value.as_slice()) } } @@ -272,7 +288,7 @@ impl EncodeValue for Any { impl<'a> From<&'a Any> for AnyRef<'a> { fn from(any: &'a Any) -> AnyRef<'a> { // Ensured to parse successfully in constructor - AnyRef::new(any.tag, &any.value).expect("invalid ANY") + AnyRef::new(any.tag, any.value.as_slice()).expect("invalid ANY") } } From 9a193c6133bfb7a6614b4aef42d6ba41c351acbe Mon Sep 17 00:00:00 2001 From: Arthur Gautier Date: Sat, 26 Nov 2022 21:48:11 -0800 Subject: [PATCH 3/9] der: ensure Any is exposed --- der/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/der/src/lib.rs b/der/src/lib.rs index 0ef8a6c6e..628e3acbf 100644 --- a/der/src/lib.rs +++ b/der/src/lib.rs @@ -377,7 +377,7 @@ pub use crate::{ }; #[cfg(feature = "alloc")] -pub use crate::document::Document; +pub use crate::{asn1::Any, document::Document}; #[cfg(feature = "bigint")] #[cfg_attr(docsrs, doc(cfg(feature = "bigint")))] From 656fb99e3a3be4e61935268b599b0e440c858459 Mon Sep 17 00:00:00 2001 From: Arthur Gautier Date: Sat, 26 Nov 2022 21:29:40 -0800 Subject: [PATCH 4/9] der: ensure ValueOrd is implemented for Any --- der/src/asn1/any.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/der/src/asn1/any.rs b/der/src/asn1/any.rs index 32ca79d1f..56c2d1cfe 100644 --- a/der/src/asn1/any.rs +++ b/der/src/asn1/any.rs @@ -194,6 +194,13 @@ impl Tagged for AnyRef<'_> { } } +#[cfg(feature = "alloc")] +impl ValueOrd for Any { + fn value_cmp(&self, other: &Self) -> Result { + self.value.der_cmp(&other.value) + } +} + impl ValueOrd for AnyRef<'_> { fn value_cmp(&self, other: &Self) -> Result { self.value.der_cmp(&other.value) From d09fe833a0b3b33c23f13d61e54bac6768b9c7b9 Mon Sep 17 00:00:00 2001 From: Arthur Gautier Date: Sat, 26 Nov 2022 21:37:59 -0800 Subject: [PATCH 5/9] der: drop the _string helpers from `AnyRef` --- der/src/asn1/any.rs | 39 ++++----- der/src/asn1/ia5_string.rs | 12 +++ der/src/asn1/printable_string.rs | 12 +++ der/src/asn1/teletex_string.rs | 12 +++ der/src/asn1/utf8_string.rs | 18 +++- der/src/asn1/videotex_string.rs | 12 +++ x509-cert/src/attr.rs | 19 +++-- x509-cert/src/ext/pkix/name/other.rs | 4 +- x509-cert/tests/certificate.rs | 41 +++++++-- x509-cert/tests/certreq.rs | 9 +- x509-cert/tests/name.rs | 21 +++-- x509-cert/tests/pkix_extensions.rs | 36 ++++++-- x509-cert/tests/trust_anchor_format.rs | 110 +++++++++++++++++++------ 13 files changed, 260 insertions(+), 85 deletions(-) diff --git a/der/src/asn1/any.rs b/der/src/asn1/any.rs index 56c2d1cfe..883a28e48 100644 --- a/der/src/asn1/any.rs +++ b/der/src/asn1/any.rs @@ -94,11 +94,6 @@ impl<'a> AnyRef<'a> { self.try_into() } - /// Attempt to decode an ASN.1 `IA5String`. - pub fn ia5_string(self) -> Result> { - self.try_into() - } - /// Attempt to decode an ASN.1 `OCTET STRING`. pub fn octet_string(self) -> Result> { self.try_into() @@ -123,21 +118,6 @@ impl<'a> AnyRef<'a> { } } - /// Attempt to decode an ASN.1 `PrintableString`. - pub fn printable_string(self) -> Result> { - self.try_into() - } - - /// Attempt to decode an ASN.1 `TeletexString`. - pub fn teletex_string(self) -> Result> { - self.try_into() - } - - /// Attempt to decode an ASN.1 `VideotexString`. - pub fn videotex_string(self) -> Result> { - self.try_into() - } - /// Attempt to decode this value an ASN.1 `SEQUENCE`, creating a new /// nested reader and calling the provided argument with it. pub fn sequence(self, f: F) -> Result @@ -154,11 +134,6 @@ impl<'a> AnyRef<'a> { pub fn utc_time(self) -> Result { self.try_into() } - - /// Attempt to decode an ASN.1 `UTF8String`. - pub fn utf8_string(self) -> Result> { - self.try_into() - } } impl<'a> Choice<'a> for AnyRef<'a> { @@ -305,3 +280,17 @@ impl Tagged for Any { self.tag } } + +#[cfg(feature = "alloc")] +impl<'a, T> From for Any +where + T: Into>, +{ + fn from(input: T) -> Any { + let anyref: AnyRef<'a> = input.into(); + Self { + tag: anyref.tag(), + value: ByteVec::new(anyref.value()).expect("invalid ANY"), + } + } +} diff --git a/der/src/asn1/ia5_string.rs b/der/src/asn1/ia5_string.rs index 3971270a8..5dca812e8 100644 --- a/der/src/asn1/ia5_string.rs +++ b/der/src/asn1/ia5_string.rs @@ -6,6 +6,9 @@ use crate::{ }; use core::{fmt, ops::Deref, str}; +#[cfg(feature = "alloc")] +use crate::asn1::Any; + /// ASN.1 `IA5String` type. /// /// Supports the [International Alphabet No. 5 (IA5)] character encoding, i.e. @@ -99,6 +102,15 @@ impl<'a> TryFrom> for Ia5StringRef<'a> { } } +#[cfg(feature = "alloc")] +impl<'a> TryFrom<&'a Any> for Ia5StringRef<'a> { + type Error = Error; + + fn try_from(any: &'a Any) -> Result> { + any.decode_into() + } +} + impl<'a> From> for AnyRef<'a> { fn from(printable_string: Ia5StringRef<'a>) -> AnyRef<'a> { AnyRef::from_tag_and_value(Tag::Ia5String, printable_string.inner.into()) diff --git a/der/src/asn1/printable_string.rs b/der/src/asn1/printable_string.rs index d48f90f09..51360b50f 100644 --- a/der/src/asn1/printable_string.rs +++ b/der/src/asn1/printable_string.rs @@ -6,6 +6,9 @@ use crate::{ }; use core::{fmt, ops::Deref, str}; +#[cfg(feature = "alloc")] +use crate::asn1::Any; + /// ASN.1 `PrintableString` type. /// /// Supports a subset the ASCII character set (described below). @@ -133,6 +136,15 @@ impl<'a> TryFrom> for PrintableStringRef<'a> { } } +#[cfg(feature = "alloc")] +impl<'a> TryFrom<&'a Any> for PrintableStringRef<'a> { + type Error = Error; + + fn try_from(any: &'a Any) -> Result> { + any.decode_into() + } +} + impl<'a> From> for AnyRef<'a> { fn from(printable_string: PrintableStringRef<'a>) -> AnyRef<'a> { AnyRef::from_tag_and_value(Tag::PrintableString, printable_string.inner.into()) diff --git a/der/src/asn1/teletex_string.rs b/der/src/asn1/teletex_string.rs index 7d6621d2d..5113d42c7 100644 --- a/der/src/asn1/teletex_string.rs +++ b/der/src/asn1/teletex_string.rs @@ -6,6 +6,9 @@ use crate::{ }; use core::{fmt, ops::Deref, str}; +#[cfg(feature = "alloc")] +use crate::asn1::Any; + /// ASN.1 `TeletexString` type. /// /// Supports a subset the ASCII character set (described below). @@ -103,6 +106,15 @@ impl<'a> TryFrom> for TeletexStringRef<'a> { } } +#[cfg(feature = "alloc")] +impl<'a> TryFrom<&'a Any> for TeletexStringRef<'a> { + type Error = Error; + + fn try_from(any: &'a Any) -> Result> { + any.decode_into() + } +} + impl<'a> From> for AnyRef<'a> { fn from(teletex_string: TeletexStringRef<'a>) -> AnyRef<'a> { AnyRef::from_tag_and_value(Tag::TeletexString, teletex_string.inner.into()) diff --git a/der/src/asn1/utf8_string.rs b/der/src/asn1/utf8_string.rs index 1a0641172..c529feb1e 100644 --- a/der/src/asn1/utf8_string.rs +++ b/der/src/asn1/utf8_string.rs @@ -7,7 +7,10 @@ use crate::{ use core::{fmt, ops::Deref, str}; #[cfg(feature = "alloc")] -use alloc::{borrow::ToOwned, string::String}; +use { + crate::asn1::Any, + alloc::{borrow::ToOwned, string::String}, +}; /// ASN.1 `UTF8String` type. /// @@ -95,9 +98,18 @@ impl<'a> TryFrom> for Utf8StringRef<'a> { } } +#[cfg(feature = "alloc")] +impl<'a> TryFrom<&'a Any> for Utf8StringRef<'a> { + type Error = Error; + + fn try_from(any: &'a Any) -> Result> { + any.decode_into() + } +} + impl<'a> From> for AnyRef<'a> { - fn from(printable_string: Utf8StringRef<'a>) -> AnyRef<'a> { - AnyRef::from_tag_and_value(Tag::Utf8String, printable_string.inner.into()) + fn from(utf_string: Utf8StringRef<'a>) -> AnyRef<'a> { + AnyRef::from_tag_and_value(Tag::Utf8String, utf_string.inner.into()) } } diff --git a/der/src/asn1/videotex_string.rs b/der/src/asn1/videotex_string.rs index b758a22e6..f1c9ba3b4 100644 --- a/der/src/asn1/videotex_string.rs +++ b/der/src/asn1/videotex_string.rs @@ -6,6 +6,9 @@ use crate::{ }; use core::{fmt, ops::Deref, str}; +#[cfg(feature = "alloc")] +use crate::asn1::Any; + /// ASN.1 `VideotexString` type. /// /// Supports a subset the ASCII character set (described below). @@ -102,6 +105,15 @@ impl<'a> TryFrom> for VideotexStringRef<'a> { } } +#[cfg(feature = "alloc")] +impl<'a> TryFrom<&'a Any> for VideotexStringRef<'a> { + type Error = Error; + + fn try_from(any: &'a Any) -> Result> { + any.decode_into() + } +} + impl<'a> From> for AnyRef<'a> { fn from(printable_string: VideotexStringRef<'a>) -> AnyRef<'a> { AnyRef::from_tag_and_value(Tag::VideotexString, printable_string.inner.into()) diff --git a/x509-cert/src/attr.rs b/x509-cert/src/attr.rs index f4ad84dd0..d3d975e4e 100644 --- a/x509-cert/src/attr.rs +++ b/x509-cert/src/attr.rs @@ -5,7 +5,10 @@ use const_oid::db::rfc4519::{COUNTRY_NAME, DOMAIN_COMPONENT, SERIAL_NUMBER}; use core::fmt::{self, Write}; use const_oid::db::DB; -use der::asn1::{AnyRef, ObjectIdentifier, SetOfVec}; +use der::asn1::{ + AnyRef, Ia5StringRef, ObjectIdentifier, PrintableStringRef, SetOfVec, TeletexStringRef, + Utf8StringRef, +}; use der::{Decode, Encode, Error, ErrorKind, Sequence, Tag, Tagged, ValueOrd}; /// X.501 `AttributeType` as defined in [RFC 5280 Appendix A.1]. @@ -227,10 +230,16 @@ impl AttributeTypeAndValue<'_> { impl fmt::Display for AttributeTypeAndValue<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let val = match self.value.tag() { - Tag::PrintableString => self.value.printable_string().ok().map(|s| s.as_str()), - Tag::Utf8String => self.value.utf8_string().ok().map(|s| s.as_str()), - Tag::Ia5String => self.value.ia5_string().ok().map(|s| s.as_str()), - Tag::TeletexString => self.value.teletex_string().ok().map(|s| s.as_str()), + Tag::PrintableString => PrintableStringRef::try_from(&self.value) + .ok() + .map(|s| s.as_str()), + Tag::Utf8String => Utf8StringRef::try_from(&self.value) + .ok() + .map(|s| s.as_str()), + Tag::Ia5String => Ia5StringRef::try_from(&self.value).ok().map(|s| s.as_str()), + Tag::TeletexString => TeletexStringRef::try_from(&self.value) + .ok() + .map(|s| s.as_str()), _ => None, }; diff --git a/x509-cert/src/ext/pkix/name/other.rs b/x509-cert/src/ext/pkix/name/other.rs index a33601c07..7bb7b493d 100644 --- a/x509-cert/src/ext/pkix/name/other.rs +++ b/x509-cert/src/ext/pkix/name/other.rs @@ -23,13 +23,13 @@ pub struct OtherName<'a> { #[cfg(test)] fn test() { use alloc::string::ToString; - use der::{Decode, Encode}; + use der::{asn1::Utf8StringRef, Decode, Encode}; use hex_literal::hex; let input = hex!("3021060A2B060104018237140203A0130C1155706E5F323134393530313330406D696C"); let decoded = OtherName::from_der(&input).unwrap(); - let onval = decoded.value.utf8_string().unwrap(); + let onval = Utf8StringRef::try_from(decoded.value).unwrap(); assert_eq!(onval.to_string(), "Upn_214950130@mil"); let encoded = decoded.to_vec().unwrap(); diff --git a/x509-cert/tests/certificate.rs b/x509-cert/tests/certificate.rs index b2afd1b37..b1a819d3b 100644 --- a/x509-cert/tests/certificate.rs +++ b/x509-cert/tests/certificate.rs @@ -1,7 +1,9 @@ //! Certificate tests use der::{ - asn1::{BitStringRef, ContextSpecific, ObjectIdentifier, UintRef}, + asn1::{ + BitStringRef, ContextSpecific, ObjectIdentifier, PrintableStringRef, UIntRef, Utf8StringRef, + }, Decode, DecodeValue, Encode, FixedTag, Header, Reader, Tag, Tagged, }; use hex_literal::hex; @@ -229,20 +231,30 @@ fn decode_cert() { for atav in i1 { if 0 == counter { assert_eq!(atav.oid.to_string(), "2.5.4.6"); - assert_eq!(atav.value.printable_string().unwrap().to_string(), "US"); + assert_eq!( + PrintableStringRef::try_from(&atav.value) + .unwrap() + .to_string(), + "US" + ); } else if 1 == counter { assert_eq!(atav.oid.to_string(), "2.5.4.10"); - assert_eq!(atav.value.printable_string().unwrap().to_string(), "Mock"); + assert_eq!( + PrintableStringRef::try_from(&atav.value) + .unwrap() + .to_string(), + "Mock" + ); } else if 2 == counter { assert_eq!(atav.oid.to_string(), "2.5.4.10"); assert_eq!( - atav.value.utf8_string().unwrap().to_string(), + Utf8StringRef::try_from(&atav.value).unwrap().to_string(), "IdenTrust Services LLC" ); } else if 3 == counter { assert_eq!(atav.oid.to_string(), "2.5.4.3"); assert_eq!( - atav.value.utf8_string().unwrap().to_string(), + Utf8StringRef::try_from(&atav.value).unwrap().to_string(), "PTE IdenTrust Global Common Root CA 1" ); } @@ -276,24 +288,35 @@ fn decode_cert() { if 0 == counter { assert_eq!(atav.oid.to_string(), "2.5.4.3"); assert_eq!( - atav.value.printable_string().unwrap().to_string(), + PrintableStringRef::try_from(&atav.value) + .unwrap() + .to_string(), "Test Federal Bridge CA" ); } else if 1 == counter { assert_eq!(atav.oid.to_string(), "2.5.4.11"); assert_eq!( - atav.value.printable_string().unwrap().to_string(), + PrintableStringRef::try_from(&atav.value) + .unwrap() + .to_string(), "TestFPKI" ); } else if 2 == counter { assert_eq!(atav.oid.to_string(), "2.5.4.10"); assert_eq!( - atav.value.printable_string().unwrap().to_string(), + PrintableStringRef::try_from(&atav.value) + .unwrap() + .to_string(), "U.S. Government" ); } else if 3 == counter { assert_eq!(atav.oid.to_string(), "2.5.4.6"); - assert_eq!(atav.value.printable_string().unwrap().to_string(), "US"); + assert_eq!( + PrintableStringRef::try_from(&atav.value) + .unwrap() + .to_string(), + "US" + ); } counter += 1; } diff --git a/x509-cert/tests/certreq.rs b/x509-cert/tests/certreq.rs index a76c02866..9b1099e73 100644 --- a/x509-cert/tests/certreq.rs +++ b/x509-cert/tests/certreq.rs @@ -1,6 +1,9 @@ //! Certification request (`CertReq`) tests -use der::{Encode, Tag, Tagged}; +use der::{ + asn1::{PrintableStringRef, Utf8StringRef}, + Encode, Tag, Tagged, +}; use hex_literal::hex; use x509_cert::request::{CertReq, Version}; @@ -38,8 +41,8 @@ fn decode_rsa_2048_der() { for (name, (oid, val)) in cr.info.subject.0.iter().zip(NAMES) { let kind = name.0.get(0).unwrap(); let value = match kind.value.tag() { - Tag::Utf8String => kind.value.utf8_string().unwrap().as_str(), - Tag::PrintableString => kind.value.printable_string().unwrap().as_str(), + Tag::Utf8String => Utf8StringRef::try_from(&kind.value).unwrap().as_str(), + Tag::PrintableString => PrintableStringRef::try_from(&kind.value).unwrap().as_str(), _ => panic!("unexpected tag"), }; diff --git a/x509-cert/tests/name.rs b/x509-cert/tests/name.rs index e52b2c72c..3705e32cf 100644 --- a/x509-cert/tests/name.rs +++ b/x509-cert/tests/name.rs @@ -40,17 +40,26 @@ fn decode_name() { for atav in i1 { if 0 == counter { assert_eq!(atav.oid.to_string(), "2.5.4.6"); - assert_eq!(atav.value.printable_string().unwrap().to_string(), "US"); + assert_eq!( + PrintableStringRef::try_from(&atav.value) + .unwrap() + .to_string(), + "US" + ); } else if 1 == counter { assert_eq!(atav.oid.to_string(), "2.5.4.10"); assert_eq!( - atav.value.printable_string().unwrap().to_string(), + PrintableStringRef::try_from(&atav.value) + .unwrap() + .to_string(), "Test Certificates 2011" ); } else if 2 == counter { assert_eq!(atav.oid.to_string(), "2.5.4.3"); assert_eq!( - atav.value.printable_string().unwrap().to_string(), + PrintableStringRef::try_from(&atav.value) + .unwrap() + .to_string(), "Good CA" ); } @@ -75,7 +84,7 @@ fn decode_rdn() { assert_eq!(oid.to_string(), "2.5.4.6"); let value = atav.value; assert_eq!(value.tag(), Tag::PrintableString); - let ps = value.printable_string().unwrap(); + let ps = PrintableStringRef::try_from(value).unwrap(); assert_eq!(ps.to_string(), "US"); } @@ -101,7 +110,7 @@ fn decode_rdn() { assert_eq!(oid2.to_string(), "2.5.4.10"); let value2 = atav1a.value; assert_eq!(value2.tag(), Tag::Utf8String); - let utf8b = value2.utf8_string().unwrap(); + let utf8b = Utf8StringRef::try_from(value2).unwrap(); assert_eq!(utf8b.to_string(), "123"); let atav2a = i.next().unwrap(); @@ -109,7 +118,7 @@ fn decode_rdn() { assert_eq!(oid1.to_string(), "2.5.4.3"); let value1 = atav2a.value; assert_eq!(value1.tag(), Tag::Utf8String); - let utf8a = value1.utf8_string().unwrap(); + let utf8a = Utf8StringRef::try_from(value1).unwrap(); assert_eq!(utf8a.to_string(), "JOHN SMITH"); let mut from_scratch = RelativeDistinguishedName::default(); diff --git a/x509-cert/tests/pkix_extensions.rs b/x509-cert/tests/pkix_extensions.rs index 21b65ef21..5981ab455 100644 --- a/x509-cert/tests/pkix_extensions.rs +++ b/x509-cert/tests/pkix_extensions.rs @@ -1,6 +1,6 @@ //! Certificate tests use const_oid::AssociatedOid; -use der::asn1::UintRef; +use der::asn1::{Ia5StringRef, PrintableStringRef, UIntRef, Utf8StringRef}; use der::{Decode, Encode, ErrorKind, Length, Tag, Tagged}; use hex_literal::hex; use x509_cert::ext::pkix::crl::dp::{DistributionPoint, ReasonFlags, Reasons}; @@ -138,7 +138,7 @@ fn decode_general_name() { let bytes = hex!("A021060A2B060104018237140203A0130C1155706E5F323134393530313330406D696C"); match GeneralName::from_der(&bytes).unwrap() { GeneralName::OtherName(other_name) => { - let onval = other_name.value.utf8_string().unwrap(); + let onval = Utf8StringRef::try_from(other_name.value).unwrap(); assert_eq!(onval.to_string(), "Upn_214950130@mil"); } _ => panic!("Failed to parse OtherName from GeneralName"), @@ -285,7 +285,7 @@ fn decode_cert() { for pqi in pq.iter() { if 0 == counter_pq { assert_eq!("1.3.6.1.5.5.7.2.1", pqi.policy_qualifier_id.to_string()); - let cpsval = pqi.qualifier.unwrap().ia5_string().unwrap(); + let cpsval = Ia5StringRef::try_from(pqi.qualifier.unwrap()).unwrap(); assert_eq!( "https://secure.identrust.com/certificates/policy/IGC/index.html", cpsval.to_string() @@ -508,17 +508,26 @@ fn decode_cert() { for atav in i1 { if 0 == counter { assert_eq!(atav.oid.to_string(), "2.5.4.6"); - assert_eq!(atav.value.printable_string().unwrap().to_string(), "US"); + assert_eq!( + PrintableStringRef::try_from(&atav.value) + .unwrap() + .to_string(), + "US" + ); } else if 1 == counter { assert_eq!(atav.oid.to_string(), "2.5.4.10"); assert_eq!( - atav.value.printable_string().unwrap().to_string(), + PrintableStringRef::try_from(&atav.value) + .unwrap() + .to_string(), "Test Certificates 2011" ); } else if 2 == counter { assert_eq!(atav.oid.to_string(), "2.5.4.3"); assert_eq!( - atav.value.printable_string().unwrap().to_string(), + PrintableStringRef::try_from(&atav.value) + .unwrap() + .to_string(), "Trust Anchor" ); } @@ -550,17 +559,26 @@ fn decode_cert() { for atav in i1 { if 0 == counter { assert_eq!(atav.oid.to_string(), "2.5.4.6"); - assert_eq!(atav.value.printable_string().unwrap().to_string(), "US"); + assert_eq!( + PrintableStringRef::try_from(&atav.value) + .unwrap() + .to_string(), + "US" + ); } else if 1 == counter { assert_eq!(atav.oid.to_string(), "2.5.4.10"); assert_eq!( - atav.value.printable_string().unwrap().to_string(), + PrintableStringRef::try_from(&atav.value) + .unwrap() + .to_string(), "Test Certificates 2011" ); } else if 2 == counter { assert_eq!(atav.oid.to_string(), "2.5.4.3"); assert_eq!( - atav.value.printable_string().unwrap().to_string(), + PrintableStringRef::try_from(&atav.value) + .unwrap() + .to_string(), "Good CA" ); } diff --git a/x509-cert/tests/trust_anchor_format.rs b/x509-cert/tests/trust_anchor_format.rs index bcc3e6933..15039a8d4 100644 --- a/x509-cert/tests/trust_anchor_format.rs +++ b/x509-cert/tests/trust_anchor_format.rs @@ -1,4 +1,7 @@ -use der::{Decode, Encode, SliceReader}; +use der::{ + asn1::{Ia5StringRef, PrintableStringRef}, + Decode, Encode, SliceReader, +}; use hex_literal::hex; use x509_cert::anchor::{CertPolicies, TrustAnchorChoice}; use x509_cert::ext::pkix::name::GeneralName; @@ -90,20 +93,34 @@ fn decode_ta1() { for atav in i1 { if 0 == counter { assert_eq!(atav.oid.to_string(), "2.5.4.6"); - assert_eq!(atav.value.printable_string().unwrap().to_string(), "US"); + assert_eq!( + PrintableStringRef::try_from(&atav.value) + .unwrap() + .to_string(), + "US" + ); } else if 1 == counter { assert_eq!(atav.oid.to_string(), "2.5.4.10"); assert_eq!( - atav.value.printable_string().unwrap().to_string(), + PrintableStringRef::try_from(&atav.value) + .unwrap() + .to_string(), "U.S. Government" ); } else if 2 == counter { assert_eq!(atav.oid.to_string(), "2.5.4.11"); - assert_eq!(atav.value.printable_string().unwrap().to_string(), "ECA"); + assert_eq!( + PrintableStringRef::try_from(&atav.value) + .unwrap() + .to_string(), + "ECA" + ); } else if 3 == counter { assert_eq!(atav.oid.to_string(), "2.5.4.3"); assert_eq!( - atav.value.printable_string().unwrap().to_string(), + PrintableStringRef::try_from(&atav.value) + .unwrap() + .to_string(), "ECA Root CA 4" ); } @@ -153,23 +170,34 @@ fn decode_ta2() { for atav in i1 { if 0 == counter { assert_eq!(atav.oid.to_string(), "2.5.4.6"); - assert_eq!(atav.value.printable_string().unwrap().to_string(), "US"); + assert_eq!( + PrintableStringRef::try_from(&atav.value) + .unwrap() + .to_string(), + "US" + ); } else if 1 == counter { assert_eq!(atav.oid.to_string(), "2.5.4.10"); assert_eq!( - atav.value.printable_string().unwrap().to_string(), + PrintableStringRef::try_from(&atav.value) + .unwrap() + .to_string(), "Entrust" ); } else if 2 == counter { assert_eq!(atav.oid.to_string(), "2.5.4.11"); assert_eq!( - atav.value.printable_string().unwrap().to_string(), + PrintableStringRef::try_from(&atav.value) + .unwrap() + .to_string(), "Certification Authorities" ); } else if 3 == counter { assert_eq!(atav.oid.to_string(), "2.5.4.11"); assert_eq!( - atav.value.printable_string().unwrap().to_string(), + PrintableStringRef::try_from(&atav.value) + .unwrap() + .to_string(), "Entrust Managed Services NFI Root CA" ); } @@ -190,19 +218,25 @@ fn decode_ta2() { if 0 == counter { assert_eq!(atav.oid.to_string(), "2.5.4.6"); assert_eq!( - atav.value.printable_string().unwrap().to_string(), + PrintableStringRef::try_from(&atav.value) + .unwrap() + .to_string(), "US" ); } else if 1 == counter { assert_eq!(atav.oid.to_string(), "2.5.4.10"); assert_eq!( - atav.value.printable_string().unwrap().to_string(), + PrintableStringRef::try_from(&atav.value) + .unwrap() + .to_string(), "U.S. Government" ); } else if 2 == counter { assert_eq!(atav.oid.to_string(), "2.5.4.11"); assert_eq!( - atav.value.printable_string().unwrap().to_string(), + PrintableStringRef::try_from(&atav.value) + .unwrap() + .to_string(), "DoD" ); } @@ -263,23 +297,34 @@ fn decode_ta3() { for atav in i1 { if 0 == counter { assert_eq!(atav.oid.to_string(), "2.5.4.6"); - assert_eq!(atav.value.printable_string().unwrap().to_string(), "US"); + assert_eq!( + PrintableStringRef::try_from(&atav.value) + .unwrap() + .to_string(), + "US" + ); } else if 1 == counter { assert_eq!(atav.oid.to_string(), "2.5.4.10"); assert_eq!( - atav.value.printable_string().unwrap().to_string(), + PrintableStringRef::try_from(&atav.value) + .unwrap() + .to_string(), "Exostar LLC" ); } else if 2 == counter { assert_eq!(atav.oid.to_string(), "2.5.4.11"); assert_eq!( - atav.value.printable_string().unwrap().to_string(), + PrintableStringRef::try_from(&atav.value) + .unwrap() + .to_string(), "Certification Authorities" ); } else if 3 == counter { assert_eq!(atav.oid.to_string(), "2.5.4.3"); assert_eq!( - atav.value.printable_string().unwrap().to_string(), + PrintableStringRef::try_from(&atav.value) + .unwrap() + .to_string(), "Exostar Federated Identity Service Root CA 1" ); } @@ -300,19 +345,25 @@ fn decode_ta3() { if 0 == counter { assert_eq!(atav.oid.to_string(), "2.5.4.6"); assert_eq!( - atav.value.printable_string().unwrap().to_string(), + PrintableStringRef::try_from(&atav.value) + .unwrap() + .to_string(), "US" ); } else if 1 == counter { assert_eq!(atav.oid.to_string(), "2.5.4.10"); assert_eq!( - atav.value.printable_string().unwrap().to_string(), + PrintableStringRef::try_from(&atav.value) + .unwrap() + .to_string(), "U.S. Government" ); } else if 2 == counter { assert_eq!(atav.oid.to_string(), "2.5.4.11"); assert_eq!( - atav.value.printable_string().unwrap().to_string(), + PrintableStringRef::try_from(&atav.value) + .unwrap() + .to_string(), "DoD" ); } @@ -366,17 +417,30 @@ fn decode_ta4() { for atav in i1 { if 0 == counter { assert_eq!(atav.oid.to_string(), "0.9.2342.19200300.100.1.25"); - assert_eq!(atav.value.ia5_string().unwrap().to_string(), "com"); + assert_eq!( + Ia5StringRef::try_from(&atav.value).unwrap().to_string(), + "com" + ); } else if 1 == counter { assert_eq!(atav.oid.to_string(), "0.9.2342.19200300.100.1.25"); - assert_eq!(atav.value.ia5_string().unwrap().to_string(), "raytheon"); + assert_eq!( + Ia5StringRef::try_from(&atav.value).unwrap().to_string(), + "raytheon" + ); } else if 2 == counter { assert_eq!(atav.oid.to_string(), "2.5.4.10"); - assert_eq!(atav.value.printable_string().unwrap().to_string(), "CAs"); + assert_eq!( + PrintableStringRef::try_from(&atav.value) + .unwrap() + .to_string(), + "CAs" + ); } else if 3 == counter { assert_eq!(atav.oid.to_string(), "2.5.4.11"); assert_eq!( - atav.value.printable_string().unwrap().to_string(), + PrintableStringRef::try_from(&atav.value) + .unwrap() + .to_string(), "RaytheonRoot" ); } From bfe0ae6158741b0cba6896dc7c224d69913b6137 Mon Sep 17 00:00:00 2001 From: Arthur Gautier Date: Sat, 26 Nov 2022 20:27:24 -0800 Subject: [PATCH 6/9] x509-cert: make name an owned type --- x509-cert/src/anchor.rs | 2 +- x509-cert/src/attr.rs | 28 +++++++------- x509-cert/src/certificate.rs | 4 +- x509-cert/src/crl.rs | 2 +- x509-cert/src/ext/pkix.rs | 9 ++--- x509-cert/src/ext/pkix/name/dp.rs | 2 +- x509-cert/src/ext/pkix/name/general.rs | 2 +- x509-cert/src/name.rs | 23 +++++------- x509-cert/src/request.rs | 4 +- x509-cert/tests/certificate.rs | 2 +- x509-cert/tests/name.rs | 52 +++++++++++++------------- x509-cert/tests/pkix_extensions.rs | 2 +- x509-ocsp/src/lib.rs | 4 +- 13 files changed, 65 insertions(+), 71 deletions(-) diff --git a/x509-cert/src/anchor.rs b/x509-cert/src/anchor.rs index 2a329b7c6..de38807ad 100644 --- a/x509-cert/src/anchor.rs +++ b/x509-cert/src/anchor.rs @@ -75,7 +75,7 @@ pub struct TrustAnchorInfo<'a> { #[derive(Clone, Debug, Eq, PartialEq, Sequence)] #[allow(missing_docs)] pub struct CertPathControls<'a> { - pub ta_name: Name<'a>, + pub ta_name: Name, #[asn1(context_specific = "0", tag_mode = "IMPLICIT", optional = "true")] pub certificate: Option>, diff --git a/x509-cert/src/attr.rs b/x509-cert/src/attr.rs index d3d975e4e..20484ea00 100644 --- a/x509-cert/src/attr.rs +++ b/x509-cert/src/attr.rs @@ -6,7 +6,7 @@ use core::fmt::{self, Write}; use const_oid::db::DB; use der::asn1::{ - AnyRef, Ia5StringRef, ObjectIdentifier, PrintableStringRef, SetOfVec, TeletexStringRef, + Any, Ia5StringRef, ObjectIdentifier, PrintableStringRef, SetOfVec, TeletexStringRef, Utf8StringRef, }; use der::{Decode, Encode, Error, ErrorKind, Sequence, Tag, Tagged, ValueOrd}; @@ -27,7 +27,7 @@ pub type AttributeType = ObjectIdentifier; /// ``` /// /// [RFC 5280 Appendix A.1]: https://datatracker.ietf.org/doc/html/rfc5280#appendix-A.1 -pub type AttributeValue<'a> = AnyRef<'a>; +pub type AttributeValue = Any; /// X.501 `Attribute` as defined in [RFC 5280 Appendix A.1]. /// @@ -53,15 +53,15 @@ pub type AttributeValue<'a> = AnyRef<'a>; /// [RFC 5280 Appendix A.1]: https://datatracker.ietf.org/doc/html/rfc5280#appendix-A.1 #[derive(Clone, Debug, PartialEq, Eq, Sequence, ValueOrd)] #[allow(missing_docs)] -pub struct Attribute<'a> { +pub struct Attribute { pub oid: AttributeType, - pub values: SetOfVec>, + pub values: SetOfVec, } -impl<'a> TryFrom<&'a [u8]> for Attribute<'a> { +impl TryFrom<&[u8]> for Attribute { type Error = Error; - fn try_from(bytes: &'a [u8]) -> Result { + fn try_from(bytes: &[u8]) -> Result { Self::from_der(bytes) } } @@ -73,7 +73,7 @@ impl<'a> TryFrom<&'a [u8]> for Attribute<'a> { /// ``` /// /// [RFC 2986 Section 4]: https://datatracker.ietf.org/doc/html/rfc2986#section-4 -pub type Attributes<'a> = SetOfVec>; +pub type Attributes = SetOfVec; /// X.501 `AttributeTypeAndValue` as defined in [RFC 5280 Appendix A.1]. /// @@ -85,11 +85,11 @@ pub type Attributes<'a> = SetOfVec>; /// ``` /// /// [RFC 5280 Appendix A.1]: https://datatracker.ietf.org/doc/html/rfc5280#appendix-A.1 -#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Sequence, ValueOrd)] +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Sequence, ValueOrd)] #[allow(missing_docs)] -pub struct AttributeTypeAndValue<'a> { +pub struct AttributeTypeAndValue { pub oid: AttributeType, - pub value: AnyRef<'a>, + pub value: AttributeValue, } #[derive(Copy, Clone)] @@ -144,7 +144,7 @@ impl Parser { } } -impl AttributeTypeAndValue<'_> { +impl AttributeTypeAndValue { /// Parses the hex value in the `OID=#HEX` format. fn encode_hex(oid: ObjectIdentifier, val: &str) -> Result, Error> { // Ensure an even number of hex bytes. @@ -172,7 +172,7 @@ impl AttributeTypeAndValue<'_> { } // Serialize. - let value = AnyRef::from_der(&bytes)?; + let value = Any::from_der(&bytes)?; let atv = AttributeTypeAndValue { oid, value }; atv.to_vec() } @@ -195,7 +195,7 @@ impl AttributeTypeAndValue<'_> { }; // Serialize. - let value = AnyRef::new(tag, parser.as_bytes())?; + let value = Any::new(tag, parser.as_bytes())?; let atv = AttributeTypeAndValue { oid, value }; atv.to_vec() } @@ -227,7 +227,7 @@ impl AttributeTypeAndValue<'_> { /// Serializes the structure according to the rules in [RFC 4514]. /// /// [RFC 4514]: https://datatracker.ietf.org/doc/html/rfc4514 -impl fmt::Display for AttributeTypeAndValue<'_> { +impl fmt::Display for AttributeTypeAndValue { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let val = match self.value.tag() { Tag::PrintableString => PrintableStringRef::try_from(&self.value) diff --git a/x509-cert/src/certificate.rs b/x509-cert/src/certificate.rs index a1853c565..7cb19b9c1 100644 --- a/x509-cert/src/certificate.rs +++ b/x509-cert/src/certificate.rs @@ -85,9 +85,9 @@ pub struct TbsCertificate<'a> { pub serial_number: UintRef<'a>, pub signature: AlgorithmIdentifierRef<'a>, - pub issuer: Name<'a>, + pub issuer: Name, pub validity: Validity, - pub subject: Name<'a>, + pub subject: Name, pub subject_public_key_info: SubjectPublicKeyInfoRef<'a>, #[asn1(context_specific = "1", tag_mode = "IMPLICIT", optional = "true")] diff --git a/x509-cert/src/crl.rs b/x509-cert/src/crl.rs index 40853619c..98bef1c81 100644 --- a/x509-cert/src/crl.rs +++ b/x509-cert/src/crl.rs @@ -76,7 +76,7 @@ pub struct RevokedCert<'a> { pub struct TbsCertList<'a> { pub version: Version, pub signature: AlgorithmIdentifierRef<'a>, - pub issuer: Name<'a>, + pub issuer: Name, pub this_update: Time, pub next_update: Option