diff --git a/der/src/asn1/any.rs b/der/src/asn1/any.rs index cb65f2391..3c086c558 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::Bytes; #[cfg(feature = "oid")] use crate::asn1::ObjectIdentifier; @@ -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> { @@ -194,6 +169,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) @@ -226,19 +208,35 @@ pub struct Any { tag: Tag, /// Inner value encoded as bytes. - value: Vec, + value: Bytes, } #[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 = Bytes::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 +251,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 +270,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") } } @@ -282,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: Bytes::from(anyref.value), + } + } +} 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/der/src/byte_slice.rs b/der/src/byte_slice.rs index 00d46d0f1..595f47a79 100644 --- a/der/src/byte_slice.rs +++ b/der/src/byte_slice.rs @@ -11,10 +11,10 @@ use core::cmp::Ordering; #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub(crate) struct ByteSlice<'a> { /// Precomputed `Length` (avoids possible panicking conversions) - length: Length, + pub length: Length, /// Inner value - inner: &'a [u8], + pub inner: &'a [u8], } impl<'a> ByteSlice<'a> { diff --git a/der/src/bytes.rs b/der/src/bytes.rs new file mode 100644 index 000000000..0ad0c48f2 --- /dev/null +++ b/der/src/bytes.rs @@ -0,0 +1,117 @@ +//! Common handling for types backed by byte allocation with enforcement of a +//! library-level length limitation i.e. `Length::max()`. + +use crate::{ + str_slice::StrSlice, ByteSlice, DecodeValue, DerOrd, EncodeValue, Error, Header, Length, + Reader, Result, Writer, +}; +use alloc::boxed::Box; +use core::cmp::Ordering; + +/// Byte slice newtype which respects the `Length::max()` limit. +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub(crate) struct Bytes { + /// Precomputed `Length` (avoids possible panicking conversions) + length: Length, + + /// Inner value + inner: Box<[u8]>, +} + +impl Bytes { + /// Create a new [`Bytes`], ensuring that the provided `slice` value + /// is shorter than `Length::max()`. + pub fn new(data: impl Into>) -> Result { + let inner: Box<[u8]> = data.into(); + + Ok(Self { + length: Length::try_from(inner.len())?, + inner, + }) + } + + /// 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 Bytes { + fn as_ref(&self) -> &[u8] { + self.as_slice() + } +} + +impl<'a> DecodeValue<'a> for Bytes { + fn decode_value>(reader: &mut R, header: Header) -> Result { + reader.read_vec(header.length).and_then(Self::new) + } +} + +impl EncodeValue for Bytes { + 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 Bytes { + fn default() -> Self { + Self { + length: Length::ZERO, + inner: Box::new([]), + } + } +} + +impl DerOrd for Bytes { + fn der_cmp(&self, other: &Self) -> Result { + Ok(self.as_slice().cmp(other.as_slice())) + } +} + +impl From<&[u8; 1]> for Bytes { + fn from(byte: &[u8; 1]) -> Bytes { + Self { + length: Length::ONE, + inner: Box::new([byte[0]]), + } + } +} + +impl From> for Bytes { + fn from(s: StrSlice<'_>) -> Bytes { + let bytes = s.as_bytes(); + debug_assert_eq!(bytes.len(), usize::try_from(s.length).expect("overflow")); + + Bytes { + inner: Box::from(bytes), + length: s.length, + } + } +} + +impl From> for Bytes { + fn from(s: ByteSlice<'_>) -> Bytes { + Bytes { + length: s.length, + inner: Box::from(s.inner), + } + } +} + +impl TryFrom<&[u8]> for Bytes { + 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..4657e132c 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 bytes; #[cfg(feature = "alloc")] mod document; @@ -375,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")))] @@ -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::bytes::Bytes; pub(crate) use crate::{arrayvec::ArrayVec, byte_slice::ByteSlice, str_slice::StrSlice}; 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 f4ad84dd0..20484ea00 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::{ + Any, 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]. @@ -24,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]. /// @@ -50,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) } } @@ -70,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]. /// @@ -82,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)] @@ -141,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. @@ -169,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() } @@ -192,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() } @@ -224,13 +227,19 @@ 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 => 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/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