diff --git a/der/derive/src/attributes.rs b/der/derive/src/attributes.rs index c765e190c..326ab78c8 100644 --- a/der/derive/src/attributes.rs +++ b/der/derive/src/attributes.rs @@ -1,11 +1,11 @@ //! Attribute-related types used by the proc macro use crate::{Asn1Type, Tag, TagMode, TagNumber}; -use proc_macro2::TokenStream; +use proc_macro2::{Span, TokenStream}; use proc_macro_error::{abort, abort_call_site}; use quote::quote; use std::{fmt::Debug, str::FromStr}; -use syn::{Attribute, Lit, LitStr, Meta, MetaList, MetaNameValue, NestedMeta, Path}; +use syn::{Attribute, Ident, Lit, LitStr, Meta, MetaList, MetaNameValue, NestedMeta, Path}; /// Attribute name. pub(crate) const ATTR_NAME: &str = "asn1"; @@ -21,12 +21,22 @@ pub(crate) struct TypeAttrs { /// /// The default value is `EXPLICIT`. pub tag_mode: TagMode, + + /// The type parameters that needs to implement `der::Choice<'_>` + pub choice: Vec, + /// The type parameters that needs to implement `der::Encode` + pub encode: Vec, + /// The type parameters that needs to implement `der::asn1::BitStringLike<'_>` + pub bitstringlike: Vec, } impl TypeAttrs { /// Parse attributes from a struct field or enum variant. pub fn parse(attrs: &[Attribute]) -> Self { let mut tag_mode = None; + let mut choice = Vec::new(); + let mut encode = Vec::new(); + let mut bitstringlike = Vec::new(); let mut parsed_attrs = Vec::new(); AttrNameValue::from_attributes(attrs, &mut parsed_attrs); @@ -39,6 +49,12 @@ impl TypeAttrs { } tag_mode = Some(mode); + } else if let Some(ident) = attr.parse_value::("choice") { + choice.push(Ident::new(&ident, Span::call_site())); + } else if let Some(ident) = attr.parse_value::("encode") { + encode.push(Ident::new(&ident, Span::call_site())); + } else if let Some(ident) = attr.parse_value::("bitstringlike") { + bitstringlike.push(Ident::new(&ident, Span::call_site())); } else { abort!( attr.name, @@ -49,6 +65,9 @@ impl TypeAttrs { Self { tag_mode: tag_mode.unwrap_or_default(), + bitstringlike, + encode, + choice, } } } diff --git a/der/derive/src/sequence.rs b/der/derive/src/sequence.rs index 3e4724691..c6990bd05 100644 --- a/der/derive/src/sequence.rs +++ b/der/derive/src/sequence.rs @@ -8,6 +8,7 @@ use field::SequenceField; use proc_macro2::TokenStream; use proc_macro_error::abort; use quote::quote; +use std::cmp::Ordering; use syn::{DeriveInput, Ident, Lifetime}; /// Derive the `Sequence` trait for a struct @@ -20,6 +21,12 @@ pub(crate) struct DeriveSequence { /// Fields of the struct. fields: Vec, + + /// Type parameters of the struct. + type_parameters: Vec, + + /// Type attributes of the struct. + type_attrs: TypeAttrs, } impl DeriveSequence { @@ -33,6 +40,12 @@ impl DeriveSequence { ), }; + let type_parameters = input + .generics + .type_params() + .map(|g| g.ident.clone()) + .collect::>(); + // TODO(tarcieri): properly handle multiple lifetimes let lifetime = input .generics @@ -52,6 +65,8 @@ impl DeriveSequence { ident: input.ident, lifetime, fields, + type_parameters, + type_attrs, } } @@ -82,8 +97,27 @@ impl DeriveSequence { encode_body.push(field.to_encode_tokens()); } + let mut type_parameters = Vec::new(); + let mut type_parameters_bounds = Vec::new(); + for param in &self.type_parameters { + type_parameters.push(param.clone()); + + if let Some(bound) = param.to_bind_tokens(&lifetime, &self.type_attrs) { + type_parameters_bounds.push(bound); + } + } + + let maybe_where_token = if !self.type_parameters.is_empty() { + quote! {where} + } else { + quote! {} + }; + quote! { - impl<#lifetime> ::der::DecodeValue<#lifetime> for #ident<#lt_params> { + impl<#lifetime, #(#type_parameters),*> ::der::DecodeValue<#lifetime> for #ident<#lt_params #(#type_parameters),*> + #maybe_where_token + #(#type_parameters_bounds),* + { fn decode_value>( reader: &mut R, header: ::der::Header, @@ -100,7 +134,10 @@ impl DeriveSequence { } } - impl<#lifetime> ::der::Sequence<#lifetime> for #ident<#lt_params> { + impl<#lifetime, #(#type_parameters),*> ::der::Sequence<#lifetime> for #ident<#lt_params #(#type_parameters),*> + #maybe_where_token + #(#type_parameters_bounds),* + { fn fields(&self, f: F) -> ::der::Result where F: FnOnce(&[&dyn der::Encode]) -> ::der::Result, @@ -114,6 +151,55 @@ impl DeriveSequence { } } +/// Trait for extending `syn::Ident` with a helper to generate the TokenStream for binding the type +trait BindToken { + fn to_bind_tokens(&self, lifetime: &TokenStream, attrs: &TypeAttrs) -> Option; +} + +impl BindToken for Ident { + fn to_bind_tokens(&self, lifetime: &TokenStream, attrs: &TypeAttrs) -> Option { + let ident = self; + + let mut bounds = Vec::new(); + + if attrs.choice.iter().any(|i| ident.ident_eq(i)) { + bounds.push(quote! { + ::der::Choice<#lifetime> + }); + } + if attrs.encode.iter().any(|i| ident.ident_eq(i)) { + bounds.push(quote! { + ::der::Encode + }); + } + if attrs.bitstringlike.iter().any(|i| ident.ident_eq(i)) { + bounds.push(quote! { + ::der::asn1::BitStringLike<#lifetime> + }); + } + + if !bounds.is_empty() { + Some(quote! { + #ident: #(#bounds)+* + }) + } else { + None + } + } +} + +/// A small helper for `Ident` to do comparison of `Ident` ignoring its callsite +trait IdentEq { + fn ident_eq(&self, ident: &Ident) -> bool; +} + +impl IdentEq for Ident { + fn ident_eq(&self, ident: &Ident) -> bool { + // Implementation detail, ordering comparison only compares the name + self.cmp(ident) == Ordering::Equal + } +} + #[cfg(test)] mod tests { use super::DeriveSequence; diff --git a/der/src/asn1.rs b/der/src/asn1.rs index 34d692b67..5cbdf34f6 100644 --- a/der/src/asn1.rs +++ b/der/src/asn1.rs @@ -27,7 +27,7 @@ mod videotex_string; pub use self::{ any::AnyRef, - bit_string::{BitStringIter, BitStringRef}, + bit_string::{BitStringIter, BitStringLike, BitStringRef}, choice::Choice, context_specific::{ContextSpecific, ContextSpecificRef}, generalized_time::GeneralizedTime, @@ -47,7 +47,11 @@ pub use self::{ #[cfg(feature = "alloc")] #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] -pub use self::{any::Any, bit_string::BitString, octet_string::OctetString, set_of::SetOfVec}; +pub use self::{ + any::Any, bit_string::BitString, ia5_string::Ia5String, integer::bigint::UInt, + octet_string::OctetString, printable_string::PrintableString, set_of::SetOfVec, + teletex_string::TeletexString, +}; #[cfg(feature = "oid")] #[cfg_attr(docsrs, doc(cfg(feature = "oid")))] 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/bit_string.rs b/der/src/asn1/bit_string.rs index eed14e456..dbd425912 100644 --- a/der/src/asn1/bit_string.rs +++ b/der/src/asn1/bit_string.rs @@ -1,8 +1,8 @@ //! ASN.1 `BIT STRING` support. use crate::{ - asn1::AnyRef, ByteSlice, DecodeValue, DerOrd, EncodeValue, Error, ErrorKind, FixedTag, Header, - Length, Reader, Result, Tag, ValueOrd, Writer, + asn1::AnyRef, ByteSlice, Decode, DecodeValue, DerOrd, Encode, EncodeValue, Error, ErrorKind, + FixedTag, Header, Length, Reader, Result, Tag, ValueOrd, Writer, }; use core::{cmp::Ordering, iter::FusedIterator}; @@ -153,8 +153,8 @@ impl ValueOrd for BitStringRef<'_> { } } -impl<'a> From<&BitStringRef<'a>> for BitStringRef<'a> { - fn from(value: &BitStringRef<'a>) -> BitStringRef<'a> { +impl<'a, 'k> From<&'k BitStringRef<'a>> for BitStringRef<'a> { + fn from(value: &'k BitStringRef<'a>) -> BitStringRef<'a> { *value } } @@ -452,6 +452,18 @@ where } } +/// Trait used as a marker to ensure the type can hold the content of a Bit String +pub trait BitStringLike<'der> +where + Self: Decode<'der> + Encode + FixedTag, +{ +} + +impl<'der> BitStringLike<'der> for BitStringRef<'der> {} + +#[cfg(feature = "alloc")] +impl<'a> BitStringLike<'a> for BitString {} + #[cfg(test)] mod tests { use super::{BitStringRef, Result, Tag}; diff --git a/der/src/asn1/ia5_string.rs b/der/src/asn1/ia5_string.rs index 3971270a8..9a71b0386 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()) @@ -117,6 +129,135 @@ impl<'a> fmt::Debug for Ia5StringRef<'a> { } } +#[cfg(feature = "alloc")] +pub use self::alloc::Ia5String; + +#[cfg(feature = "alloc")] +mod alloc { + use super::Ia5StringRef; + use crate::{ + asn1::{Any, AnyRef}, + ord::OrdIsValueOrd, + ByteSlice, DecodeValue, EncodeValue, Error, FixedTag, Header, Length, Reader, Result, + String, Tag, Writer, + }; + use core::{fmt, ops::Deref, str}; + + /// ASN.1 `IA5String` type. + /// + /// Supports the [International Alphabet No. 5 (IA5)] character encoding, i.e. + /// the lower 128 characters of the ASCII alphabet. (Note: IA5 is now + /// technically known as the International Reference Alphabet or IRA as + /// specified in the ITU-T's T.50 recommendation). + /// + /// For UTF-8, use [`String`][`alloc::string::String`]. + /// + /// [International Alphabet No. 5 (IA5)]: https://en.wikipedia.org/wiki/T.50_%28standard%29 + #[derive(Clone, Eq, PartialEq, PartialOrd, Ord)] + pub struct Ia5String { + /// Inner value + inner: String, + } + + impl Ia5String { + /// Create a new `IA5String`. + pub fn new(input: &T) -> Result + where + T: AsRef<[u8]> + ?Sized, + { + let input = input.as_ref(); + Ia5StringRef::new(input)?; + + String::from_bytes(input) + .map(|inner| Self { inner }) + .map_err(|_| Self::TAG.value_error()) + } + } + + impl Deref for Ia5String { + type Target = String; + + fn deref(&self) -> &Self::Target { + &self.inner + } + } + + impl AsRef for Ia5String { + fn as_ref(&self) -> &str { + self.as_str() + } + } + + impl AsRef<[u8]> for Ia5String { + fn as_ref(&self) -> &[u8] { + self.as_bytes() + } + } + + impl<'a> DecodeValue<'a> for Ia5String { + fn decode_value>(reader: &mut R, header: Header) -> Result { + Self::new(ByteSlice::decode_value(reader, header)?.as_slice()) + } + } + + impl EncodeValue for Ia5String { + fn value_len(&self) -> Result { + self.inner.value_len() + } + + fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> { + self.inner.encode_value(writer) + } + } + + impl FixedTag for Ia5String { + const TAG: Tag = Tag::Ia5String; + } + + impl OrdIsValueOrd for Ia5String {} + + impl<'a> TryFrom<&AnyRef<'a>> for Ia5String { + type Error = Error; + + fn try_from(any: &AnyRef<'a>) -> Result { + (*any).decode_into() + } + } + + impl<'a> TryFrom<&'a Any> for Ia5String { + type Error = Error; + + fn try_from(any: &'a Any) -> Result { + any.decode_into() + } + } + + impl<'a> From> for Ia5String { + fn from(international_string: Ia5StringRef<'a>) -> Ia5String { + let inner = international_string.inner.into(); + Self { inner } + } + } + + impl<'a> From<&'a Ia5String> for AnyRef<'a> { + fn from(international_string: &'a Ia5String) -> AnyRef<'a> { + AnyRef::from_tag_and_value(Tag::Ia5String, (&international_string.inner).into()) + } + } + + impl fmt::Display for Ia5String { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.as_str()) + } + } + + impl fmt::Debug for Ia5String { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Ia5String({:?})", self.as_str()) + } + } +} + #[cfg(test)] mod tests { use super::Ia5StringRef; diff --git a/der/src/asn1/integer/bigint.rs b/der/src/asn1/integer/bigint.rs index f896406a6..f4c8de026 100644 --- a/der/src/asn1/integer/bigint.rs +++ b/der/src/asn1/integer/bigint.rs @@ -94,6 +94,107 @@ impl<'a> FixedTag for UIntRef<'a> { impl<'a> OrdIsValueOrd for UIntRef<'a> {} +#[cfg(feature = "alloc")] +pub use self::alloc::UInt; + +#[cfg(feature = "alloc")] +mod alloc { + use super::{super::uint, UIntRef}; + use crate::{ + asn1::AnyRef, ord::OrdIsValueOrd, Bytes, DecodeValue, EncodeValue, Error, ErrorKind, + FixedTag, Header, Length, Reader, Result, Tag, Writer, + }; + + /// "Big" unsigned ASN.1 `INTEGER` type. + /// + /// Provides direct storage for the big endian bytes which comprise an + /// unsigned integer value. + /// + /// Intended for use cases like very large integers that are used in + /// cryptographic applications (e.g. keys, signatures). + #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] + pub struct UInt { + /// Inner value + inner: Bytes, + } + + impl UInt { + /// Create a new [`UIntRef`] from a byte slice. + pub fn new(bytes: &[u8]) -> Result { + let inner = Bytes::new(uint::strip_leading_zeroes(bytes)) + .map_err(|_| ErrorKind::Length { tag: Self::TAG })?; + + Ok(Self { inner }) + } + + /// Borrow the inner byte slice which contains the least significant bytes + /// of a big endian integer value with all leading zeros stripped. + pub fn as_bytes(&self) -> &[u8] { + self.inner.as_slice() + } + + /// Get the length of this [`UIntRef`] in bytes. + pub fn len(&self) -> Length { + self.inner.len() + } + + /// Is the inner byte slice empty? + pub fn is_empty(&self) -> bool { + self.inner.is_empty() + } + } + + impl<'a> DecodeValue<'a> for UInt { + fn decode_value>(reader: &mut R, header: Header) -> Result { + let bytes = Bytes::decode_value(reader, header)?; + let result = Self::new(uint::decode_to_slice(bytes.as_slice())?)?; + + // Ensure we compute the same encoded length as the original any value. + if result.value_len()? != header.length { + return Err(Self::TAG.non_canonical_error()); + } + + Ok(result) + } + } + + impl EncodeValue for UInt { + fn value_len(&self) -> Result { + uint::encoded_len(self.inner.as_slice()) + } + + fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> { + // Add leading `0x00` byte if required + if self.value_len()? > self.len() { + writer.write_byte(0)?; + } + + writer.write(self.as_bytes()) + } + } + + impl<'a> From<&UIntRef<'a>> for UInt { + fn from(value: &UIntRef<'a>) -> UInt { + let inner = Bytes::new(value.as_bytes()).expect("Invalid UInt"); + UInt { inner } + } + } + + impl<'a> TryFrom> for UInt { + type Error = Error; + + fn try_from(any: AnyRef<'a>) -> Result { + any.decode_into() + } + } + + impl FixedTag for UInt { + const TAG: Tag = Tag::Integer; + } + + impl OrdIsValueOrd for UInt {} +} + #[cfg(test)] mod tests { use super::UIntRef; diff --git a/der/src/asn1/printable_string.rs b/der/src/asn1/printable_string.rs index d48f90f09..8d5400069 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()) @@ -151,6 +163,157 @@ impl<'a> fmt::Debug for PrintableStringRef<'a> { } } +#[cfg(feature = "alloc")] +pub use self::alloc::PrintableString; + +#[cfg(feature = "alloc")] +mod alloc { + use super::PrintableStringRef; + + use crate::{ + asn1::{Any, AnyRef}, + ord::OrdIsValueOrd, + ByteSlice, DecodeValue, EncodeValue, Error, FixedTag, Header, Length, Reader, Result, + String, Tag, Writer, + }; + use core::{fmt, ops::Deref, str}; + + /// ASN.1 `PrintableString` type. + /// + /// Supports a subset the ASCII character set (described below). + /// + /// For UTF-8, use [`Utf8StringRef`][`crate::asn1::Utf8StringRef`] instead. + /// For the full ASCII character set, use + /// [`Ia5StringRef`][`crate::asn1::Ia5StringRef`]. + /// + /// # Supported characters + /// + /// The following ASCII characters/ranges are supported: + /// + /// - `A..Z` + /// - `a..z` + /// - `0..9` + /// - "` `" (i.e. space) + /// - `\` + /// - `(` + /// - `)` + /// - `+` + /// - `,` + /// - `-` + /// - `.` + /// - `/` + /// - `:` + /// - `=` + /// - `?` + #[derive(Clone, Eq, PartialEq, PartialOrd, Ord)] + pub struct PrintableString { + /// Inner value + inner: String, + } + + impl PrintableString { + /// Create a new ASN.1 `PrintableString`. + pub fn new(input: &T) -> Result + where + T: AsRef<[u8]> + ?Sized, + { + let input = input.as_ref(); + PrintableStringRef::new(input)?; + + String::from_bytes(input) + .map(|inner| Self { inner }) + .map_err(|_| Self::TAG.value_error()) + } + } + + impl Deref for PrintableString { + type Target = String; + + fn deref(&self) -> &Self::Target { + &self.inner + } + } + + impl AsRef for PrintableString { + fn as_ref(&self) -> &str { + self.as_str() + } + } + + impl AsRef<[u8]> for PrintableString { + fn as_ref(&self) -> &[u8] { + self.as_bytes() + } + } + + impl<'a> DecodeValue<'a> for PrintableString { + fn decode_value>(reader: &mut R, header: Header) -> Result { + Self::new(ByteSlice::decode_value(reader, header)?.as_slice()) + } + } + + impl EncodeValue for PrintableString { + fn value_len(&self) -> Result { + self.inner.value_len() + } + + fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> { + self.inner.encode_value(writer) + } + } + + impl FixedTag for PrintableString { + const TAG: Tag = Tag::PrintableString; + } + + impl OrdIsValueOrd for PrintableString {} + + impl<'a> From> for PrintableString { + fn from(value: PrintableStringRef<'a>) -> PrintableString { + let inner = + String::from_bytes(value.inner.as_bytes()).expect("Invalid PrintableString"); + Self { inner } + } + } + + impl<'a> TryFrom<&AnyRef<'a>> for PrintableString { + type Error = Error; + + fn try_from(any: &AnyRef<'a>) -> Result { + (*any).decode_into() + } + } + + impl<'a> TryFrom<&'a Any> for PrintableString { + type Error = Error; + + fn try_from(any: &'a Any) -> Result { + any.decode_into() + } + } + + impl<'a> From<&'a PrintableString> for AnyRef<'a> { + fn from(printable_string: &'a PrintableString) -> AnyRef<'a> { + AnyRef::from_tag_and_value( + Tag::PrintableString, + ByteSlice::new(printable_string.inner.as_bytes()).expect("Invalid PrintableString"), + ) + } + } + + impl fmt::Display for PrintableString { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.as_str()) + } + } + + impl fmt::Debug for PrintableString { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "PrintableString({:?})", self.as_str()) + } + } +} + #[cfg(test)] mod tests { use super::PrintableStringRef; diff --git a/der/src/asn1/teletex_string.rs b/der/src/asn1/teletex_string.rs index 7d6621d2d..1a2de2eaa 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()) @@ -121,6 +133,145 @@ impl<'a> fmt::Debug for TeletexStringRef<'a> { } } +#[cfg(feature = "alloc")] +pub use self::alloc::TeletexString; + +#[cfg(feature = "alloc")] +mod alloc { + use super::TeletexStringRef; + + use crate::{ + asn1::AnyRef, ord::OrdIsValueOrd, ByteSlice, DecodeValue, EncodeValue, Error, FixedTag, + Header, Length, Reader, Result, String, Tag, Writer, + }; + 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). + /// + /// For UTF-8, use [`Utf8StringRef`][`crate::asn1::Utf8StringRef`] instead. + /// For the full ASCII character set, use + /// [`Ia5StringRef`][`crate::asn1::Ia5StringRef`]. + /// + /// # Supported characters + /// + /// The standard defines a complex character set allowed in this type. However, quoting the ASN.1 + /// mailing list, "a sizable volume of software in the world treats TeletexString (T61String) as a + /// simple 8-bit string with mostly Windows Latin 1 (superset of iso-8859-1) encoding". + /// + #[derive(Clone, Eq, PartialEq, PartialOrd, Ord)] + pub struct TeletexString { + /// Inner value + inner: String, + } + + impl TeletexString { + /// Create a new ASN.1 `TeletexString`. + pub fn new(input: &T) -> Result + where + T: AsRef<[u8]> + ?Sized, + { + let input = input.as_ref(); + + TeletexStringRef::new(input)?; + + String::from_bytes(input) + .map(|inner| Self { inner }) + .map_err(|_| Self::TAG.value_error()) + } + } + + impl Deref for TeletexString { + type Target = String; + + fn deref(&self) -> &Self::Target { + &self.inner + } + } + + impl AsRef for TeletexString { + fn as_ref(&self) -> &str { + self.as_str() + } + } + + impl AsRef<[u8]> for TeletexString { + fn as_ref(&self) -> &[u8] { + self.as_bytes() + } + } + + impl<'a> DecodeValue<'a> for TeletexString { + fn decode_value>(reader: &mut R, header: Header) -> Result { + Self::new(ByteSlice::decode_value(reader, header)?.as_slice()) + } + } + + impl EncodeValue for TeletexString { + fn value_len(&self) -> Result { + self.inner.value_len() + } + + fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> { + self.inner.encode_value(writer) + } + } + + impl FixedTag for TeletexString { + const TAG: Tag = Tag::TeletexString; + } + + impl OrdIsValueOrd for TeletexString {} + + impl<'a> From> for TeletexString { + fn from(value: TeletexStringRef<'a>) -> TeletexString { + let inner = String::from_bytes(value.inner.as_bytes()).expect("Invalid TeletexString"); + Self { inner } + } + } + + impl<'a> TryFrom<&AnyRef<'a>> for TeletexString { + type Error = Error; + + fn try_from(any: &AnyRef<'a>) -> Result { + (*any).decode_into() + } + } + + impl<'a> TryFrom<&'a Any> for TeletexString { + type Error = Error; + + fn try_from(any: &'a Any) -> Result { + any.decode_into() + } + } + + impl<'a> From<&'a TeletexString> for AnyRef<'a> { + fn from(teletex_string: &'a TeletexString) -> AnyRef<'a> { + AnyRef::from_tag_and_value( + Tag::TeletexString, + ByteSlice::new(teletex_string.inner.as_bytes()).expect("Invalid TeletexString"), + ) + } + } + + impl fmt::Display for TeletexString { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.as_str()) + } + } + + impl fmt::Debug for TeletexString { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "TeletexString({:?})", self.as_str()) + } + } +} + #[cfg(test)] mod tests { use super::TeletexStringRef; 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..e96a41951 100644 --- a/der/src/byte_slice.rs +++ b/der/src/byte_slice.rs @@ -7,14 +7,17 @@ use crate::{ }; use core::cmp::Ordering; +#[cfg(feature = "alloc")] +use crate::string::String; + /// Byte slice newtype which respects the `Length::max()` limit. #[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> { @@ -107,6 +110,19 @@ impl<'a> From> for ByteSlice<'a> { } } +#[cfg(feature = "alloc")] +impl<'a> From<&'a String> for ByteSlice<'a> { + fn from(s: &'a String) -> ByteSlice<'a> { + let bytes = s.as_bytes(); + debug_assert_eq!(bytes.len(), usize::try_from(s.length).expect("overflow")); + + ByteSlice { + inner: bytes, + length: s.length, + } + } +} + impl<'a> TryFrom<&'a [u8]> for ByteSlice<'a> { type Error = Error; diff --git a/der/src/bytes.rs b/der/src/bytes.rs new file mode 100644 index 000000000..f8081b49b --- /dev/null +++ b/der/src/bytes.rs @@ -0,0 +1,122 @@ +//! 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 + } + + /// Is this [`Bytes`] empty? + pub fn is_empty(&self) -> bool { + self.len() == Length::ZERO + } +} + +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 413b2e4af..a8e9f0aee 100644 --- a/der/src/lib.rs +++ b/der/src/lib.rs @@ -356,8 +356,12 @@ mod str_slice; mod tag; mod writer; +#[cfg(feature = "alloc")] +mod bytes; #[cfg(feature = "alloc")] mod document; +#[cfg(feature = "alloc")] +mod string; pub use crate::{ asn1::{AnyRef, Choice, Sequence}, @@ -375,7 +379,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")))] @@ -407,3 +411,5 @@ pub use zeroize; pub use crate::document::SecretDocument; pub(crate) use crate::{arrayvec::ArrayVec, byte_slice::ByteSlice, str_slice::StrSlice}; +#[cfg(feature = "alloc")] +pub(crate) use crate::{bytes::Bytes, string::String}; diff --git a/der/src/string.rs b/der/src/string.rs new file mode 100644 index 000000000..d0ed12a8a --- /dev/null +++ b/der/src/string.rs @@ -0,0 +1,94 @@ +//! Common handling for types backed by `String` with enforcement of a +//! library-level length limitation i.e. `Length::max()`. + +use crate::{ + ByteSlice, DecodeValue, EncodeValue, Header, Length, Reader, Result, StrSlice, Writer, +}; +use alloc::string::String as StringA; +use core::str; + +/// String newtype which respects the [`Length::max`] limit. +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub struct String { + /// Inner value + pub(crate) inner: StringA, + + /// Precomputed `Length` (avoids possible panicking conversions) + pub(crate) length: Length, +} + +impl String { + /// Create a new [`StrSlice`], ensuring that the byte representation of + /// the provided `str` value is shorter than `Length::max()`. + pub fn new(s: &str) -> Result { + Ok(Self { + inner: StringA::from(s), + length: Length::try_from(s.as_bytes().len())?, + }) + } + + /// Parse a [`StrSlice`] from UTF-8 encoded bytes. + pub fn from_bytes(bytes: &[u8]) -> Result { + Ok(Self { + inner: StringA::from_utf8(bytes.to_vec())?, + length: Length::try_from(bytes.len())?, + }) + } + + /// Borrow the inner `str` + pub fn as_str(&self) -> &str { + &self.inner + } + + /// Borrow the inner byte slice + pub fn as_bytes(&self) -> &[u8] { + self.inner.as_bytes() + } + + /// Get the [`Length`] of this [`StrSlice`] + pub fn len(&self) -> Length { + self.length + } + + /// Is this [`StrSlice`] empty? + pub fn is_empty(&self) -> bool { + self.len() == Length::ZERO + } +} + +impl AsRef for String { + fn as_ref(&self) -> &str { + self.as_str() + } +} + +impl AsRef<[u8]> for String { + fn as_ref(&self) -> &[u8] { + self.as_bytes() + } +} + +impl<'a> DecodeValue<'a> for String { + fn decode_value>(reader: &mut R, header: Header) -> Result { + Self::from_bytes(ByteSlice::decode_value(reader, header)?.as_slice()) + } +} + +impl EncodeValue for String { + fn value_len(&self) -> Result { + Ok(self.length) + } + + fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> { + writer.write(self.as_ref()) + } +} + +impl From> for String { + fn from(s: StrSlice<'_>) -> String { + Self { + inner: StringA::from(s.inner), + length: s.length, + } + } +} diff --git a/pkcs1/src/lib.rs b/pkcs1/src/lib.rs index 84dc6ab44..cf18ef593 100644 --- a/pkcs1/src/lib.rs +++ b/pkcs1/src/lib.rs @@ -52,7 +52,7 @@ pub const ALGORITHM_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.84 /// `AlgorithmIdentifier` for RSA. #[cfg(feature = "pkcs8")] #[cfg_attr(docsrs, doc(cfg(feature = "pkcs8")))] -pub const ALGORITHM_ID: pkcs8::AlgorithmIdentifier<'static> = pkcs8::AlgorithmIdentifier { +pub const ALGORITHM_ID: pkcs8::AlgorithmIdentifierRef<'static> = pkcs8::AlgorithmIdentifierRef { oid: ALGORITHM_OID, parameters: Some(der::asn1::AnyRef::NULL), }; diff --git a/pkcs1/src/params.rs b/pkcs1/src/params.rs index e803473e0..b2080893f 100644 --- a/pkcs1/src/params.rs +++ b/pkcs1/src/params.rs @@ -6,7 +6,7 @@ use der::{ asn1::ContextSpecificRef, Decode, DecodeValue, Encode, EncodeValue, FixedTag, Reader, Sequence, Tag, TagMode, TagNumber, Writer, }; -use spki::AlgorithmIdentifier; +use spki::AlgorithmIdentifierRef; const OID_SHA_1: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.3.14.3.2.26"); const OID_MGF_1: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.8"); @@ -15,7 +15,7 @@ const OID_PSPECIFIED: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.1 // TODO(tarcieri): make `AlgorithmIdentifier` generic around params; use `OID_SHA_1` const SEQ_OID_SHA_1_DER: &[u8] = &[0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a]; -const SHA_1_AI: AlgorithmIdentifier<'_> = AlgorithmIdentifier { +const SHA_1_AI: AlgorithmIdentifierRef<'_> = AlgorithmIdentifierRef { oid: OID_SHA_1, parameters: None, }; @@ -81,10 +81,10 @@ impl FixedTag for TrailerField { #[derive(Clone, Debug, Eq, PartialEq)] pub struct RsaPssParams<'a> { /// Hash Algorithm - pub hash: AlgorithmIdentifier<'a>, + pub hash: AlgorithmIdentifierRef<'a>, /// Mask Generation Function (MGF) - pub mask_gen: AlgorithmIdentifier<'a>, + pub mask_gen: AlgorithmIdentifierRef<'a>, /// Salt length pub salt_len: u8, @@ -180,8 +180,8 @@ impl<'a> TryFrom<&'a [u8]> for RsaPssParams<'a> { } /// Default Mask Generation Function (MGF): SHA-1. -fn default_mgf1_sha1<'a>() -> AlgorithmIdentifier<'a> { - AlgorithmIdentifier { +fn default_mgf1_sha1<'a>() -> AlgorithmIdentifierRef<'a> { + AlgorithmIdentifierRef { oid: OID_MGF_1, parameters: Some( AnyRef::new(Tag::Sequence, SEQ_OID_SHA_1_DER) @@ -208,13 +208,13 @@ fn default_mgf1_sha1<'a>() -> AlgorithmIdentifier<'a> { #[derive(Clone, Debug, Eq, PartialEq)] pub struct RsaOaepParams<'a> { /// Hash Algorithm - pub hash: AlgorithmIdentifier<'a>, + pub hash: AlgorithmIdentifierRef<'a>, /// Mask Generation Function (MGF) - pub mask_gen: AlgorithmIdentifier<'a>, + pub mask_gen: AlgorithmIdentifierRef<'a>, /// The source (and possibly the value) of the label L - pub p_source: AlgorithmIdentifier<'a>, + pub p_source: AlgorithmIdentifierRef<'a>, } impl<'a> Default for RsaOaepParams<'a> { @@ -291,8 +291,8 @@ impl<'a> TryFrom<&'a [u8]> for RsaOaepParams<'a> { } /// Default Source Algorithm, empty string -fn default_pempty_string<'a>() -> AlgorithmIdentifier<'a> { - AlgorithmIdentifier { +fn default_pempty_string<'a>() -> AlgorithmIdentifierRef<'a> { + AlgorithmIdentifierRef { oid: OID_PSPECIFIED, parameters: Some( AnyRef::new(Tag::OctetString, &[]).expect("error creating default OAEP params"), diff --git a/pkcs1/src/traits.rs b/pkcs1/src/traits.rs index c70820cb7..f6cff96fa 100644 --- a/pkcs1/src/traits.rs +++ b/pkcs1/src/traits.rs @@ -16,7 +16,10 @@ use { }; #[cfg(feature = "pkcs8")] -use crate::{ALGORITHM_ID, ALGORITHM_OID}; +use { + crate::{ALGORITHM_ID, ALGORITHM_OID}, + der::asn1::BitStringRef, +}; #[cfg(feature = "std")] use std::path::Path; @@ -183,9 +186,9 @@ impl DecodeRsaPrivateKey for T { #[cfg_attr(docsrs, doc(cfg(feature = "pkcs8")))] impl DecodeRsaPublicKey for T { fn from_pkcs1_der(public_key: &[u8]) -> Result { - Ok(Self::try_from(pkcs8::SubjectPublicKeyInfo { + Ok(Self::try_from(pkcs8::SubjectPublicKeyInfoRef { algorithm: ALGORITHM_ID, - subject_public_key: public_key, + subject_public_key: BitStringRef::from_bytes(public_key)?, })?) } } @@ -206,8 +209,8 @@ impl EncodeRsaPrivateKey for T { impl EncodeRsaPublicKey for T { fn to_pkcs1_der(&self) -> Result { let doc = self.to_public_key_der()?; - let spki = pkcs8::SubjectPublicKeyInfo::from_der(doc.as_bytes())?; + let spki = pkcs8::SubjectPublicKeyInfoRef::from_der(doc.as_bytes())?; spki.algorithm.assert_algorithm_oid(ALGORITHM_OID)?; - RsaPublicKey::from_der(spki.subject_public_key)?.try_into() + RsaPublicKey::from_der(spki.subject_public_key.raw_bytes())?.try_into() } } diff --git a/pkcs5/src/lib.rs b/pkcs5/src/lib.rs index 7019ee439..f658307c3 100644 --- a/pkcs5/src/lib.rs +++ b/pkcs5/src/lib.rs @@ -12,7 +12,7 @@ //! //! The main API for this crate is the [`EncryptionScheme`] enum, which impls //! the [`Decode`] and [`Encode`] traits from the [`der`] crate, and can be -//! used for decoding/encoding PKCS#5 [`AlgorithmIdentifier`] fields. +//! used for decoding/encoding PKCS#5 `AlgorithmIdentifier` fields. //! //! [RFC 8018]: https://tools.ietf.org/html/rfc8018 @@ -26,7 +26,7 @@ pub mod pbes2; pub use crate::error::{Error, Result}; pub use der::{self, asn1::ObjectIdentifier}; -pub use spki::AlgorithmIdentifier; +pub use spki::AlgorithmIdentifierRef; use der::{Decode, DecodeValue, Encode, Header, Reader, Sequence, Tag}; @@ -136,7 +136,7 @@ impl<'a> EncryptionScheme<'a> { impl<'a> DecodeValue<'a> for EncryptionScheme<'a> { fn decode_value>(decoder: &mut R, header: Header) -> der::Result { - AlgorithmIdentifier::decode_value(decoder, header)?.try_into() + AlgorithmIdentifierRef::decode_value(decoder, header)?.try_into() } } @@ -164,10 +164,10 @@ impl<'a> From> for EncryptionScheme<'a> { } } -impl<'a> TryFrom> for EncryptionScheme<'a> { +impl<'a> TryFrom> for EncryptionScheme<'a> { type Error = der::Error; - fn try_from(alg: AlgorithmIdentifier<'a>) -> der::Result> { + fn try_from(alg: AlgorithmIdentifierRef<'a>) -> der::Result> { if alg.oid == pbes2::PBES2_OID { match alg.parameters { Some(params) => pbes2::Parameters::try_from(params).map(Into::into), @@ -183,6 +183,6 @@ impl<'a> TryFrom<&'a [u8]> for EncryptionScheme<'a> { type Error = der::Error; fn try_from(bytes: &'a [u8]) -> der::Result> { - AlgorithmIdentifier::from_der(bytes)?.try_into() + AlgorithmIdentifierRef::from_der(bytes)?.try_into() } } diff --git a/pkcs5/src/pbes1.rs b/pkcs5/src/pbes1.rs index 735070562..7a1165f8e 100644 --- a/pkcs5/src/pbes1.rs +++ b/pkcs5/src/pbes1.rs @@ -2,7 +2,7 @@ //! //! [RFC 8018 Section 6.1]: https://tools.ietf.org/html/rfc8018#section-6.1 -use crate::AlgorithmIdentifier; +use crate::AlgorithmIdentifierRef; use der::{ asn1::{AnyRef, ObjectIdentifier, OctetStringRef}, Decode, Encode, ErrorKind, Length, Reader, Sequence, Tag, Writer, @@ -68,7 +68,7 @@ impl Algorithm { impl<'a> Decode<'a> for Algorithm { fn decode>(decoder: &mut R) -> der::Result { - AlgorithmIdentifier::decode(decoder)?.try_into() + AlgorithmIdentifierRef::decode(decoder)?.try_into() } } @@ -81,10 +81,10 @@ impl Sequence<'_> for Algorithm { } } -impl<'a> TryFrom> for Algorithm { +impl<'a> TryFrom> for Algorithm { type Error = der::Error; - fn try_from(alg: AlgorithmIdentifier<'a>) -> der::Result { + fn try_from(alg: AlgorithmIdentifierRef<'a>) -> der::Result { // Ensure that we have a supported PBES1 algorithm identifier let encryption = EncryptionScheme::try_from(alg.oid) .map_err(|_| der::Tag::ObjectIdentifier.value_error())?; diff --git a/pkcs5/src/pbes2.rs b/pkcs5/src/pbes2.rs index 8d63f114b..288dee407 100644 --- a/pkcs5/src/pbes2.rs +++ b/pkcs5/src/pbes2.rs @@ -12,7 +12,7 @@ pub use self::kdf::{ PBKDF2_OID, SCRYPT_OID, }; -use crate::{AlgorithmIdentifier, Error, Result}; +use crate::{AlgorithmIdentifierRef, Error, Result}; use der::{ asn1::{AnyRef, ObjectIdentifier, OctetStringRef}, Decode, Encode, ErrorKind, Length, Reader, Sequence, Tag, Writer, @@ -220,8 +220,8 @@ impl<'a> TryFrom> for Parameters<'a> { fn try_from(any: AnyRef<'a>) -> der::Result { any.sequence(|params| { - let kdf = AlgorithmIdentifier::decode(params)?; - let encryption = AlgorithmIdentifier::decode(params)?; + let kdf = AlgorithmIdentifierRef::decode(params)?; + let encryption = AlgorithmIdentifierRef::decode(params)?; Ok(Self { kdf: kdf.try_into()?, @@ -305,14 +305,14 @@ impl<'a> EncryptionScheme<'a> { impl<'a> Decode<'a> for EncryptionScheme<'a> { fn decode>(reader: &mut R) -> der::Result { - AlgorithmIdentifier::decode(reader).and_then(TryInto::try_into) + AlgorithmIdentifierRef::decode(reader).and_then(TryInto::try_into) } } -impl<'a> TryFrom> for EncryptionScheme<'a> { +impl<'a> TryFrom> for EncryptionScheme<'a> { type Error = der::Error; - fn try_from(alg: AlgorithmIdentifier<'a>) -> der::Result { + fn try_from(alg: AlgorithmIdentifierRef<'a>) -> der::Result { // TODO(tarcieri): support for non-AES algorithms? let iv = match alg.parameters { Some(params) => params.octet_string()?.as_bytes(), @@ -352,7 +352,7 @@ impl<'a> TryFrom> for EncryptionScheme<'a> { } } -impl<'a> TryFrom> for AlgorithmIdentifier<'a> { +impl<'a> TryFrom> for AlgorithmIdentifierRef<'a> { type Error = der::Error; fn try_from(scheme: EncryptionScheme<'a>) -> der::Result { @@ -366,7 +366,7 @@ impl<'a> TryFrom> for AlgorithmIdentifier<'a> { EncryptionScheme::DesEde3Cbc { iv } => iv, })?; - Ok(AlgorithmIdentifier { + Ok(AlgorithmIdentifierRef { oid: scheme.oid(), parameters: Some(parameters.into()), }) @@ -375,10 +375,10 @@ impl<'a> TryFrom> for AlgorithmIdentifier<'a> { impl<'a> Encode for EncryptionScheme<'a> { fn encoded_len(&self) -> der::Result { - AlgorithmIdentifier::try_from(*self)?.encoded_len() + AlgorithmIdentifierRef::try_from(*self)?.encoded_len() } fn encode(&self, writer: &mut dyn Writer) -> der::Result<()> { - AlgorithmIdentifier::try_from(*self)?.encode(writer) + AlgorithmIdentifierRef::try_from(*self)?.encode(writer) } } diff --git a/pkcs5/src/pbes2/kdf.rs b/pkcs5/src/pbes2/kdf.rs index 3fc107488..eb389ff71 100644 --- a/pkcs5/src/pbes2/kdf.rs +++ b/pkcs5/src/pbes2/kdf.rs @@ -1,6 +1,6 @@ //! Key derivation functions. -use crate::{AlgorithmIdentifier, Error, Result}; +use crate::{AlgorithmIdentifierRef, Error, Result}; use der::{ asn1::{AnyRef, ObjectIdentifier, OctetStringRef}, Decode, Encode, ErrorKind, Length, Reader, Sequence, Tag, Tagged, Writer, @@ -100,7 +100,7 @@ impl<'a> Kdf<'a> { impl<'a> Decode<'a> for Kdf<'a> { fn decode>(reader: &mut R) -> der::Result { - AlgorithmIdentifier::decode(reader)?.try_into() + AlgorithmIdentifierRef::decode(reader)?.try_into() } } @@ -128,10 +128,10 @@ impl<'a> From> for Kdf<'a> { } } -impl<'a> TryFrom> for Kdf<'a> { +impl<'a> TryFrom> for Kdf<'a> { type Error = der::Error; - fn try_from(alg: AlgorithmIdentifier<'a>) -> der::Result { + fn try_from(alg: AlgorithmIdentifierRef<'a>) -> der::Result { if let Some(params) = alg.parameters { match alg.oid { PBKDF2_OID => params.try_into().map(Self::Pbkdf2), @@ -242,7 +242,7 @@ impl<'a> TryFrom> for Pbkdf2Params<'a> { salt: OctetStringRef::decode(reader)?.as_bytes(), iteration_count: reader.decode()?, key_length: reader.decode()?, - prf: Option::>::decode(reader)? + prf: Option::>::decode(reader)? .map(TryInto::try_into) .transpose()? .unwrap_or_default(), @@ -298,10 +298,10 @@ impl Default for Pbkdf2Prf { } } -impl<'a> TryFrom> for Pbkdf2Prf { +impl<'a> TryFrom> for Pbkdf2Prf { type Error = der::Error; - fn try_from(alg: AlgorithmIdentifier<'a>) -> der::Result { + fn try_from(alg: AlgorithmIdentifierRef<'a>) -> der::Result { if let Some(params) = alg.parameters { // TODO(tarcieri): support non-NULL parameters? if !params.is_null() { @@ -323,12 +323,12 @@ impl<'a> TryFrom> for Pbkdf2Prf { } } -impl<'a> From for AlgorithmIdentifier<'a> { +impl<'a> From for AlgorithmIdentifierRef<'a> { fn from(prf: Pbkdf2Prf) -> Self { // TODO(tarcieri): support non-NULL parameters? let parameters = der::asn1::Null; - AlgorithmIdentifier { + AlgorithmIdentifierRef { oid: prf.oid(), parameters: Some(parameters.into()), } @@ -337,11 +337,11 @@ impl<'a> From for AlgorithmIdentifier<'a> { impl Encode for Pbkdf2Prf { fn encoded_len(&self) -> der::Result { - AlgorithmIdentifier::try_from(*self)?.encoded_len() + AlgorithmIdentifierRef::try_from(*self)?.encoded_len() } fn encode(&self, writer: &mut dyn Writer) -> der::Result<()> { - AlgorithmIdentifier::try_from(*self)?.encode(writer) + AlgorithmIdentifierRef::try_from(*self)?.encode(writer) } } diff --git a/pkcs7/src/enveloped_data_content.rs b/pkcs7/src/enveloped_data_content.rs index 2efe404d6..02e4f053b 100644 --- a/pkcs7/src/enveloped_data_content.rs +++ b/pkcs7/src/enveloped_data_content.rs @@ -6,9 +6,9 @@ use der::{ asn1::{ContextSpecific, OctetStringRef}, DecodeValue, Encode, Header, Reader, Sequence, TagMode, TagNumber, }; -use spki::AlgorithmIdentifier; +use spki::AlgorithmIdentifierRef; -type ContentEncryptionAlgorithmIdentifier<'a> = AlgorithmIdentifier<'a>; +type ContentEncryptionAlgorithmIdentifier<'a> = AlgorithmIdentifierRef<'a>; const ENCRYPTED_CONTENT_TAG: TagNumber = TagNumber::new(0); diff --git a/pkcs7/tests/content_tests.rs b/pkcs7/tests/content_tests.rs index b542e8139..be8dca179 100644 --- a/pkcs7/tests/content_tests.rs +++ b/pkcs7/tests/content_tests.rs @@ -9,7 +9,7 @@ use pkcs7::{ encrypted_data_content::EncryptedDataContent, enveloped_data_content::EncryptedContentInfo, ContentInfo, ContentType, }; -use spki::AlgorithmIdentifier; +use spki::AlgorithmIdentifierRef; use std::fs; fn encode_content_info<'a>(content_info: &ContentInfo<'a>, buf: &'a mut [u8]) -> &'a [u8] { @@ -52,7 +52,7 @@ fn decode_encrypted_key_example() { EncryptedContentInfo { content_type: ContentType::Data, content_encryption_algorithm: - AlgorithmIdentifier { + AlgorithmIdentifierRef { oid, parameters: Some(any), }, diff --git a/pkcs8/src/error.rs b/pkcs8/src/error.rs index bc4c2eafe..70c60aedb 100644 --- a/pkcs8/src/error.rs +++ b/pkcs8/src/error.rs @@ -26,7 +26,7 @@ pub enum Error { /// or [`SubjectPublicKeyInfo::subject_public_key`][`crate::SubjectPublicKeyInfo::subject_public_key`]. KeyMalformed, - /// [`AlgorithmIdentifier::parameters`][`crate::AlgorithmIdentifier::parameters`] + /// [`AlgorithmIdentifier::parameters`][`crate::AlgorithmIdentifierRef::parameters`] /// is malformed or otherwise encoded in an unexpected manner. ParametersMalformed, diff --git a/pkcs8/src/lib.rs b/pkcs8/src/lib.rs index a7ea0b725..de71bc782 100644 --- a/pkcs8/src/lib.rs +++ b/pkcs8/src/lib.rs @@ -83,7 +83,9 @@ pub use crate::{ version::Version, }; pub use der::{self, asn1::ObjectIdentifier, oid::AssociatedOid}; -pub use spki::{self, AlgorithmIdentifier, DecodePublicKey, SubjectPublicKeyInfo}; +pub use spki::{ + self, AlgorithmIdentifierRef, DecodePublicKey, SubjectPublicKeyInfo, SubjectPublicKeyInfoRef, +}; #[cfg(feature = "alloc")] #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] diff --git a/pkcs8/src/private_key_info.rs b/pkcs8/src/private_key_info.rs index 52f0878d7..cf23cf228 100644 --- a/pkcs8/src/private_key_info.rs +++ b/pkcs8/src/private_key_info.rs @@ -1,6 +1,6 @@ //! PKCS#8 `PrivateKeyInfo`. -use crate::{AlgorithmIdentifier, Error, Result, Version}; +use crate::{AlgorithmIdentifierRef, Error, Result, Version}; use core::fmt; use der::{ asn1::{AnyRef, BitStringRef, ContextSpecific, OctetStringRef}, @@ -29,7 +29,7 @@ const PUBLIC_KEY_TAG: TagNumber = TagNumber::N1; /// PKCS#8 `PrivateKeyInfo`. /// -/// ASN.1 structure containing an [`AlgorithmIdentifier`], private key +/// ASN.1 structure containing an `AlgorithmIdentifier`, private key /// data in an algorithm specific format, and optional attributes /// (ignored by this implementation). /// @@ -90,8 +90,8 @@ const PUBLIC_KEY_TAG: TagNumber = TagNumber::N1; /// [RFC 5958 Section 2]: https://datatracker.ietf.org/doc/html/rfc5958#section-2 #[derive(Clone)] pub struct PrivateKeyInfo<'a> { - /// X.509 [`AlgorithmIdentifier`] for the private key type. - pub algorithm: AlgorithmIdentifier<'a>, + /// X.509 `AlgorithmIdentifier` for the private key type. + pub algorithm: AlgorithmIdentifierRef<'a>, /// Private key data. pub private_key: &'a [u8], @@ -105,7 +105,7 @@ impl<'a> PrivateKeyInfo<'a> { /// /// This is a helper method which initializes `attributes` and `public_key` /// to `None`, helpful if you aren't using those. - pub fn new(algorithm: AlgorithmIdentifier<'a>, private_key: &'a [u8]) -> Self { + pub fn new(algorithm: AlgorithmIdentifierRef<'a>, private_key: &'a [u8]) -> Self { Self { algorithm, private_key, diff --git a/sec1/src/traits.rs b/sec1/src/traits.rs index cf0d9711e..5bff2f03e 100644 --- a/sec1/src/traits.rs +++ b/sec1/src/traits.rs @@ -103,7 +103,7 @@ impl DecodeEcPrivateKey for T { .parameters .and_then(|params| params.named_curve()); - let algorithm = pkcs8::AlgorithmIdentifier { + let algorithm = pkcs8::AlgorithmIdentifierRef { oid: ALGORITHM_OID, parameters: params_oid.as_ref().map(Into::into), }; diff --git a/spki/src/algorithm.rs b/spki/src/algorithm.rs index 2a8b6c7f9..6f76d6d35 100644 --- a/spki/src/algorithm.rs +++ b/spki/src/algorithm.rs @@ -2,8 +2,10 @@ use crate::{Error, Result}; use core::cmp::Ordering; -use der::asn1::{AnyRef, ObjectIdentifier}; -use der::{Decode, DecodeValue, DerOrd, Encode, Header, Reader, Sequence, ValueOrd}; +use der::{ + asn1::{AnyRef, Choice, ObjectIdentifier}, + Decode, DecodeValue, DerOrd, Encode, Header, Reader, Sequence, ValueOrd, +}; /// X.509 `AlgorithmIdentifier` as defined in [RFC 5280 Section 4.1.1.2]. /// @@ -15,16 +17,68 @@ use der::{Decode, DecodeValue, DerOrd, Encode, Header, Reader, Sequence, ValueOr /// /// [RFC 5280 Section 4.1.1.2]: https://tools.ietf.org/html/rfc5280#section-4.1.1.2 #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] -pub struct AlgorithmIdentifier<'a> { +pub struct AlgorithmIdentifier { /// Algorithm OID, i.e. the `algorithm` field in the `AlgorithmIdentifier` /// ASN.1 schema. pub oid: ObjectIdentifier, /// Algorithm `parameters`. - pub parameters: Option>, + pub parameters: Option, } -impl<'a> AlgorithmIdentifier<'a> { +impl<'a, Params> DecodeValue<'a> for AlgorithmIdentifier +where + Params: Choice<'a>, +{ + fn decode_value>(reader: &mut R, header: Header) -> der::Result { + reader.read_nested(header.length, |reader| { + Ok(Self { + oid: reader.decode()?, + parameters: reader.decode()?, + }) + }) + } +} + +impl<'a, Params> Sequence<'a> for AlgorithmIdentifier +where + Params: Choice<'a> + Encode, +{ + fn fields(&self, f: F) -> der::Result + where + F: FnOnce(&[&dyn Encode]) -> der::Result, + { + f(&[&self.oid, &self.parameters]) + } +} + +impl<'a, Params> TryFrom<&'a [u8]> for AlgorithmIdentifier +where + Params: Choice<'a> + Encode, +{ + type Error = Error; + + fn try_from(bytes: &'a [u8]) -> Result { + Ok(Self::from_der(bytes)?) + } +} + +impl ValueOrd for AlgorithmIdentifier +where + Params: DerOrd, +{ + fn value_cmp(&self, other: &Self) -> der::Result { + match self.oid.der_cmp(&other.oid)? { + Ordering::Equal => self.parameters.der_cmp(&other.parameters), + other => Ok(other), + } + } +} + +/// `AlgorithmIdentifier` reference which has `AnyRef` parameters. +pub type AlgorithmIdentifierRef<'a> = AlgorithmIdentifier>; + +impl<'a> AlgorithmIdentifierRef<'a> { /// Assert the `algorithm` OID is an expected value. pub fn assert_algorithm_oid(&self, expected_oid: ObjectIdentifier) -> Result { if self.oid == expected_oid { @@ -93,40 +147,3 @@ impl<'a> AlgorithmIdentifier<'a> { )) } } - -impl<'a> DecodeValue<'a> for AlgorithmIdentifier<'a> { - fn decode_value>(reader: &mut R, header: Header) -> der::Result { - reader.read_nested(header.length, |reader| { - Ok(Self { - oid: reader.decode()?, - parameters: reader.decode()?, - }) - }) - } -} - -impl<'a> Sequence<'a> for AlgorithmIdentifier<'a> { - fn fields(&self, f: F) -> der::Result - where - F: FnOnce(&[&dyn Encode]) -> der::Result, - { - f(&[&self.oid, &self.parameters]) - } -} - -impl<'a> TryFrom<&'a [u8]> for AlgorithmIdentifier<'a> { - type Error = Error; - - fn try_from(bytes: &'a [u8]) -> Result { - Ok(Self::from_der(bytes)?) - } -} - -impl ValueOrd for AlgorithmIdentifier<'_> { - fn value_cmp(&self, other: &Self) -> der::Result { - match self.oid.der_cmp(&other.oid)? { - Ordering::Equal => self.parameters.der_cmp(&other.parameters), - other => Ok(other), - } - } -} diff --git a/spki/src/lib.rs b/spki/src/lib.rs index cd11da857..34f5b347e 100644 --- a/spki/src/lib.rs +++ b/spki/src/lib.rs @@ -15,14 +15,14 @@ //! Borrow the [`ObjectIdentifier`] first then use [`der::AnyRef::from`] or `.into()`: //! //! ``` -//! use spki::{AlgorithmIdentifier, ObjectIdentifier, der::AnyRef}; +//! use spki::{AlgorithmIdentifier, ObjectIdentifier}; //! //! let alg_oid = "1.2.840.10045.2.1".parse::().unwrap(); //! let params_oid = "1.2.840.10045.3.1.7".parse::().unwrap(); //! //! let alg_id = AlgorithmIdentifier { //! oid: alg_oid, -//! parameters: Some(AnyRef::from(¶ms_oid)) +//! parameters: Some(params_oid) //! }; //! ``` @@ -41,15 +41,18 @@ mod traits; mod fingerprint; pub use crate::{ - algorithm::AlgorithmIdentifier, + algorithm::{AlgorithmIdentifier, AlgorithmIdentifierRef}, error::{Error, Result}, - spki::SubjectPublicKeyInfo, + spki::{SubjectPublicKeyInfo, SubjectPublicKeyInfoRef}, traits::DecodePublicKey, }; pub use der::{self, asn1::ObjectIdentifier}; #[cfg(feature = "alloc")] -pub use {crate::traits::EncodePublicKey, der::Document}; +pub use { + crate::{spki::SubjectPublicKeyInfoOwned, traits::EncodePublicKey}, + der::Document, +}; #[cfg(feature = "fingerprint")] pub use crate::fingerprint::FingerprintBytes; diff --git a/spki/src/spki.rs b/spki/src/spki.rs index 9058e49c7..14b60ed81 100644 --- a/spki/src/spki.rs +++ b/spki/src/spki.rs @@ -3,11 +3,15 @@ use crate::{AlgorithmIdentifier, Error, Result}; use core::cmp::Ordering; use der::{ - asn1::BitStringRef, Decode, DecodeValue, DerOrd, Encode, Header, Reader, Sequence, ValueOrd, + asn1::{AnyRef, BitStringLike, BitStringRef}, + Choice, Decode, DecodeValue, DerOrd, Encode, Header, Reader, Sequence, ValueOrd, }; #[cfg(feature = "alloc")] -use der::Document; +use der::{ + asn1::{Any, BitString}, + Document, +}; #[cfg(feature = "fingerprint")] use crate::{fingerprint, FingerprintBytes}; @@ -21,6 +25,13 @@ use { #[cfg(feature = "pem")] use der::pem::PemLabel; +/// [`SubjectPublicKeyInfo`] with [`AnyRef`] algorithm parameters, and [`BitStringRef`] params. +pub type SubjectPublicKeyInfoRef<'a> = SubjectPublicKeyInfo, BitStringRef<'a>>; + +/// [`SubjectPublicKeyInfo`] with [`Any`] algorithm parameters, and [`BitString`] params. +#[cfg(feature = "alloc")] +pub type SubjectPublicKeyInfoOwned = SubjectPublicKeyInfo; + /// X.509 `SubjectPublicKeyInfo` (SPKI) as defined in [RFC 5280 § 4.1.2.7]. /// /// ASN.1 structure containing an [`AlgorithmIdentifier`] and public key @@ -33,16 +44,20 @@ use der::pem::PemLabel; /// ``` /// /// [RFC 5280 § 4.1.2.7]: https://tools.ietf.org/html/rfc5280#section-4.1.2.7 -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub struct SubjectPublicKeyInfo<'a> { +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct SubjectPublicKeyInfo { /// X.509 [`AlgorithmIdentifier`] for the public key type - pub algorithm: AlgorithmIdentifier<'a>, + pub algorithm: AlgorithmIdentifier, /// Public key data - pub subject_public_key: &'a [u8], + pub subject_public_key: Key, } -impl<'a> SubjectPublicKeyInfo<'a> { +impl<'a, Params, Key> SubjectPublicKeyInfo +where + Params: Choice<'a> + Encode, + Key: BitStringLike<'a>, +{ /// Calculate the SHA-256 fingerprint of this [`SubjectPublicKeyInfo`] and /// encode it as a Base64 string. /// @@ -68,36 +83,41 @@ impl<'a> SubjectPublicKeyInfo<'a> { self.encode(&mut builder)?; Ok(builder.finish()) } - - /// Get a [`BitString`] representing the `subject_public_key` - fn bitstring(&self) -> der::Result> { - BitStringRef::from_bytes(self.subject_public_key) - } } -impl<'a> DecodeValue<'a> for SubjectPublicKeyInfo<'a> { +impl<'a: 'k, 'k, Params, Key: 'k> DecodeValue<'a> for SubjectPublicKeyInfo +where + Params: Choice<'a> + Encode, + Key: Decode<'a>, +{ fn decode_value>(reader: &mut R, header: Header) -> der::Result { reader.read_nested(header.length, |reader| { Ok(Self { algorithm: reader.decode()?, - subject_public_key: BitStringRef::decode(reader)? - .as_bytes() - .ok_or_else(|| der::Tag::BitString.value_error())?, + subject_public_key: Key::decode(reader)?, }) }) } } -impl<'a> Sequence<'a> for SubjectPublicKeyInfo<'a> { +impl<'a, Params, Key> Sequence<'a> for SubjectPublicKeyInfo +where + Params: Choice<'a> + Encode, + Key: BitStringLike<'a>, +{ fn fields(&self, f: F) -> der::Result where F: FnOnce(&[&dyn Encode]) -> der::Result, { - f(&[&self.algorithm, &self.bitstring()?]) + f(&[&self.algorithm, &self.subject_public_key]) } } -impl<'a> TryFrom<&'a [u8]> for SubjectPublicKeyInfo<'a> { +impl<'a, Params, Key> TryFrom<&'a [u8]> for SubjectPublicKeyInfo +where + Params: Choice<'a> + Encode, + Key: BitStringLike<'a>, +{ type Error = Error; fn try_from(bytes: &'a [u8]) -> Result { @@ -105,10 +125,14 @@ impl<'a> TryFrom<&'a [u8]> for SubjectPublicKeyInfo<'a> { } } -impl ValueOrd for SubjectPublicKeyInfo<'_> { +impl<'a, Params, Key> ValueOrd for SubjectPublicKeyInfo +where + Params: Choice<'a> + DerOrd + Encode, + Key: ValueOrd, +{ fn value_cmp(&self, other: &Self) -> der::Result { match self.algorithm.der_cmp(&other.algorithm)? { - Ordering::Equal => self.bitstring()?.der_cmp(&other.bitstring()?), + Ordering::Equal => self.subject_public_key.value_cmp(&other.subject_public_key), other => Ok(other), } } @@ -116,26 +140,36 @@ impl ValueOrd for SubjectPublicKeyInfo<'_> { #[cfg(feature = "alloc")] #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] -impl TryFrom> for Document { +impl<'a: 'k, 'k, Params, Key: 'k> TryFrom> for Document +where + Params: Choice<'a> + Encode, + Key: BitStringLike<'a>, + BitStringRef<'a>: From<&'k Key>, +{ type Error = Error; - fn try_from(spki: SubjectPublicKeyInfo<'_>) -> Result { + fn try_from(spki: SubjectPublicKeyInfo) -> Result { Self::try_from(&spki) } } #[cfg(feature = "alloc")] #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] -impl TryFrom<&SubjectPublicKeyInfo<'_>> for Document { +impl<'a: 'k, 'k, Params, Key: 'k> TryFrom<&SubjectPublicKeyInfo> for Document +where + Params: Choice<'a> + Encode, + Key: BitStringLike<'a>, + BitStringRef<'a>: From<&'k Key>, +{ type Error = Error; - fn try_from(spki: &SubjectPublicKeyInfo<'_>) -> Result { + fn try_from(spki: &SubjectPublicKeyInfo) -> Result { Ok(Self::encode_msg(spki)?) } } #[cfg(feature = "pem")] #[cfg_attr(docsrs, doc(cfg(feature = "pem")))] -impl PemLabel for SubjectPublicKeyInfo<'_> { +impl PemLabel for SubjectPublicKeyInfo { const PEM_LABEL: &'static str = "PUBLIC KEY"; } diff --git a/spki/src/traits.rs b/spki/src/traits.rs index c16e3974d..c6272edea 100644 --- a/spki/src/traits.rs +++ b/spki/src/traits.rs @@ -1,6 +1,6 @@ //! Traits for encoding/decoding SPKI public keys. -use crate::{Error, Result, SubjectPublicKeyInfo}; +use crate::{Error, Result, SubjectPublicKeyInfoRef}; #[cfg(feature = "alloc")] use der::Document; @@ -16,15 +16,15 @@ use std::path::Path; /// Parse a public key object from an encoded SPKI document. pub trait DecodePublicKey: - for<'a> TryFrom, Error = Error> + Sized + for<'a> TryFrom, Error = Error> + Sized { - /// Deserialize object from ASN.1 DER-encoded [`SubjectPublicKeyInfo`] + /// Deserialize object from ASN.1 DER-encoded `SubjectPublicKeyInfo` /// (binary format). fn from_public_key_der(bytes: &[u8]) -> Result { - Self::try_from(SubjectPublicKeyInfo::try_from(bytes)?) + Self::try_from(SubjectPublicKeyInfoRef::try_from(bytes)?) } - /// Deserialize PEM-encoded [`SubjectPublicKeyInfo`]. + /// Deserialize PEM-encoded `SubjectPublicKeyInfo`. /// /// Keys in this format begin with the following delimiter: /// @@ -35,7 +35,7 @@ pub trait DecodePublicKey: #[cfg_attr(docsrs, doc(cfg(feature = "pem")))] fn from_public_key_pem(s: &str) -> Result { let (label, doc) = Document::from_pem(s)?; - SubjectPublicKeyInfo::validate_pem_label(label)?; + SubjectPublicKeyInfoRef::validate_pem_label(label)?; Self::from_public_key_der(doc.as_bytes()) } @@ -53,7 +53,7 @@ pub trait DecodePublicKey: #[cfg_attr(docsrs, doc(cfg(all(feature = "pem", feature = "std"))))] fn read_public_key_pem_file(path: impl AsRef) -> Result { let (label, doc) = Document::read_pem_file(path)?; - SubjectPublicKeyInfo::validate_pem_label(&label)?; + SubjectPublicKeyInfoRef::validate_pem_label(&label)?; Self::from_public_key_der(doc.as_bytes()) } } @@ -70,7 +70,7 @@ pub trait EncodePublicKey { #[cfg_attr(docsrs, doc(cfg(feature = "pem")))] fn to_public_key_pem(&self, line_ending: LineEnding) -> Result { let doc = self.to_public_key_der()?; - Ok(doc.to_pem(SubjectPublicKeyInfo::PEM_LABEL, line_ending)?) + Ok(doc.to_pem(SubjectPublicKeyInfoRef::PEM_LABEL, line_ending)?) } /// Write ASN.1 DER-encoded public key to the given path @@ -89,6 +89,6 @@ pub trait EncodePublicKey { line_ending: LineEnding, ) -> Result<()> { let doc = self.to_public_key_der()?; - Ok(doc.write_pem_file(path, SubjectPublicKeyInfo::PEM_LABEL, line_ending)?) + Ok(doc.write_pem_file(path, SubjectPublicKeyInfoRef::PEM_LABEL, line_ending)?) } } diff --git a/spki/tests/spki.rs b/spki/tests/spki.rs index 3d6b19f48..9ba19d1aa 100644 --- a/spki/tests/spki.rs +++ b/spki/tests/spki.rs @@ -1,7 +1,7 @@ //! `SubjectPublicKeyInfo` tests. use hex_literal::hex; -use spki::SubjectPublicKeyInfo; +use spki::SubjectPublicKeyInfoRef; #[cfg(feature = "alloc")] use der::Encode; @@ -46,7 +46,7 @@ const ED25519_SPKI_FINGERPRINT: &[u8] = #[test] fn decode_ec_p256_der() { - let spki = SubjectPublicKeyInfo::try_from(EC_P256_DER_EXAMPLE).unwrap(); + let spki = SubjectPublicKeyInfoRef::try_from(EC_P256_DER_EXAMPLE).unwrap(); assert_eq!(spki.algorithm.oid, "1.2.840.10045.2.1".parse().unwrap()); @@ -55,19 +55,19 @@ fn decode_ec_p256_der() { "1.2.840.10045.3.1.7".parse().unwrap() ); - assert_eq!(spki.subject_public_key, &hex!("041CACFFB55F2F2CEFD89D89EB374B2681152452802DEEA09916068137D839CF7FC481A44492304D7EF66AC117BEFE83A8D08F155F2B52F9F618DD447029048E0F")[..]); + assert_eq!(spki.subject_public_key.raw_bytes(), &hex!("041CACFFB55F2F2CEFD89D89EB374B2681152452802DEEA09916068137D839CF7FC481A44492304D7EF66AC117BEFE83A8D08F155F2B52F9F618DD447029048E0F")[..]); } #[test] #[cfg(feature = "fingerprint")] fn decode_ed25519_and_fingerprint_spki() { // Repeat the decode test from the pkcs8 crate - let spki = SubjectPublicKeyInfo::try_from(ED25519_DER_EXAMPLE).unwrap(); + let spki = SubjectPublicKeyInfoRef::try_from(ED25519_DER_EXAMPLE).unwrap(); assert_eq!(spki.algorithm.oid, "1.3.101.112".parse().unwrap()); assert_eq!(spki.algorithm.parameters, None); assert_eq!( - spki.subject_public_key, + spki.subject_public_key.raw_bytes(), &hex!("4D29167F3F1912A6F7ADFA293A051A15C05EC67B8F17267B1C5550DCE853BD0D")[..] ); @@ -82,12 +82,12 @@ fn decode_ed25519_and_fingerprint_spki() { #[cfg(all(feature = "fingerprint", feature = "alloc"))] fn decode_ed25519_and_fingerprint_base64() { // Repeat the decode test from the pkcs8 crate - let spki = SubjectPublicKeyInfo::try_from(ED25519_DER_EXAMPLE).unwrap(); + let spki = SubjectPublicKeyInfoRef::try_from(ED25519_DER_EXAMPLE).unwrap(); assert_eq!(spki.algorithm.oid, "1.3.101.112".parse().unwrap()); assert_eq!(spki.algorithm.parameters, None); assert_eq!( - spki.subject_public_key, + spki.subject_public_key.raw_bytes(), &hex!("4D29167F3F1912A6F7ADFA293A051A15C05EC67B8F17267B1C5550DCE853BD0D")[..] ); @@ -100,17 +100,17 @@ fn decode_ed25519_and_fingerprint_base64() { #[test] fn decode_rsa_2048_der() { - let spki = SubjectPublicKeyInfo::try_from(RSA_2048_DER_EXAMPLE).unwrap(); + let spki = SubjectPublicKeyInfoRef::try_from(RSA_2048_DER_EXAMPLE).unwrap(); assert_eq!(spki.algorithm.oid, "1.2.840.113549.1.1.1".parse().unwrap()); assert!(spki.algorithm.parameters.unwrap().is_null()); - assert_eq!(spki.subject_public_key, &hex!("3082010A0282010100B6C42C515F10A6AAF282C63EDBE24243A170F3FA2633BD4833637F47CA4F6F36E03A5D29EFC3191AC80F390D874B39E30F414FCEC1FCA0ED81E547EDC2CD382C76F61C9018973DB9FA537972A7C701F6B77E0982DFC15FC01927EE5E7CD94B4F599FF07013A7C8281BDF22DCBC9AD7CABB7C4311C982F58EDB7213AD4558B332266D743AED8192D1884CADB8B14739A8DADA66DC970806D9C7AC450CB13D0D7C575FB198534FC61BC41BC0F0574E0E0130C7BBBFBDFDC9F6A6E2E3E2AFF1CBEAC89BA57884528D55CFB08327A1E8C89F4E003CF2888E933241D9D695BCBBACDC90B44E3E095FA37058EA25B13F5E295CBEAC6DE838AB8C50AF61E298975B872F0203010001")[..]); + assert_eq!(spki.subject_public_key.raw_bytes(), &hex!("3082010A0282010100B6C42C515F10A6AAF282C63EDBE24243A170F3FA2633BD4833637F47CA4F6F36E03A5D29EFC3191AC80F390D874B39E30F414FCEC1FCA0ED81E547EDC2CD382C76F61C9018973DB9FA537972A7C701F6B77E0982DFC15FC01927EE5E7CD94B4F599FF07013A7C8281BDF22DCBC9AD7CABB7C4311C982F58EDB7213AD4558B332266D743AED8192D1884CADB8B14739A8DADA66DC970806D9C7AC450CB13D0D7C575FB198534FC61BC41BC0F0574E0E0130C7BBBFBDFDC9F6A6E2E3E2AFF1CBEAC89BA57884528D55CFB08327A1E8C89F4E003CF2888E933241D9D695BCBBACDC90B44E3E095FA37058EA25B13F5E295CBEAC6DE838AB8C50AF61E298975B872F0203010001")[..]); } #[test] #[cfg(feature = "alloc")] fn encode_ec_p256_der() { - let pk = SubjectPublicKeyInfo::try_from(EC_P256_DER_EXAMPLE).unwrap(); + let pk = SubjectPublicKeyInfoRef::try_from(EC_P256_DER_EXAMPLE).unwrap(); let pk_encoded = pk.to_vec().unwrap(); assert_eq!(EC_P256_DER_EXAMPLE, pk_encoded.as_slice()); } @@ -118,7 +118,7 @@ fn encode_ec_p256_der() { #[test] #[cfg(feature = "alloc")] fn encode_ed25519_der() { - let pk = SubjectPublicKeyInfo::try_from(ED25519_DER_EXAMPLE).unwrap(); + let pk = SubjectPublicKeyInfoRef::try_from(ED25519_DER_EXAMPLE).unwrap(); let pk_encoded = pk.to_vec().unwrap(); assert_eq!(ED25519_DER_EXAMPLE, pk_encoded.as_slice()); } @@ -126,7 +126,7 @@ fn encode_ed25519_der() { #[test] #[cfg(feature = "alloc")] fn encode_rsa_2048_der() { - let pk = SubjectPublicKeyInfo::try_from(RSA_2048_DER_EXAMPLE).unwrap(); + let pk = SubjectPublicKeyInfoRef::try_from(RSA_2048_DER_EXAMPLE).unwrap(); let pk_encoded = pk.to_vec().unwrap(); assert_eq!(RSA_2048_DER_EXAMPLE, pk_encoded.as_slice()); } @@ -134,7 +134,7 @@ fn encode_rsa_2048_der() { #[test] #[cfg(feature = "pem")] fn encode_ec_p256_pem() { - let pk = SubjectPublicKeyInfo::try_from(EC_P256_DER_EXAMPLE).unwrap(); + let pk = SubjectPublicKeyInfoRef::try_from(EC_P256_DER_EXAMPLE).unwrap(); let pk_encoded = pk.to_pem(LineEnding::LF).unwrap(); assert_eq!(EC_P256_PEM_EXAMPLE, pk_encoded); } @@ -142,7 +142,7 @@ fn encode_ec_p256_pem() { #[test] #[cfg(feature = "pem")] fn encode_ed25519_pem() { - let pk = SubjectPublicKeyInfo::try_from(ED25519_DER_EXAMPLE).unwrap(); + let pk = SubjectPublicKeyInfoRef::try_from(ED25519_DER_EXAMPLE).unwrap(); let pk_encoded = pk.to_pem(LineEnding::LF).unwrap(); assert_eq!(ED25519_PEM_EXAMPLE, pk_encoded); } @@ -150,7 +150,7 @@ fn encode_ed25519_pem() { #[test] #[cfg(feature = "pem")] fn encode_rsa_2048_pem() { - let pk = SubjectPublicKeyInfo::try_from(RSA_2048_DER_EXAMPLE).unwrap(); + let pk = SubjectPublicKeyInfoRef::try_from(RSA_2048_DER_EXAMPLE).unwrap(); let pk_encoded = pk.to_pem(LineEnding::LF).unwrap(); assert_eq!(RSA_2048_PEM_EXAMPLE, pk_encoded); } diff --git a/spki/tests/traits.rs b/spki/tests/traits.rs index 597399e44..e35ed93cb 100644 --- a/spki/tests/traits.rs +++ b/spki/tests/traits.rs @@ -3,7 +3,7 @@ #![cfg(any(feature = "pem", feature = "std"))] use der::{Decode, Encode}; -use spki::{DecodePublicKey, Document, EncodePublicKey, Error, Result, SubjectPublicKeyInfo}; +use spki::{DecodePublicKey, Document, EncodePublicKey, Error, Result, SubjectPublicKeyInfoRef}; #[cfg(feature = "pem")] use spki::der::pem::LineEnding; @@ -42,10 +42,10 @@ impl EncodePublicKey for MockKey { } } -impl TryFrom> for MockKey { +impl TryFrom> for MockKey { type Error = Error; - fn try_from(spki: SubjectPublicKeyInfo<'_>) -> Result { + fn try_from(spki: SubjectPublicKeyInfoRef<'_>) -> Result { Ok(MockKey(spki.to_vec()?)) } } diff --git a/x509-cert/src/anchor.rs b/x509-cert/src/anchor.rs index ad7ef08e1..8aeccad9c 100644 --- a/x509-cert/src/anchor.rs +++ b/x509-cert/src/anchor.rs @@ -2,12 +2,13 @@ use crate::ext::pkix::{certpolicy::CertificatePolicies, NameConstraints}; use crate::{ext::Extensions, name::Name}; -use crate::{Certificate, TbsCertificate}; +use crate::{CertificateRef, TbsCertificateRef}; -use der::asn1::{OctetStringRef, Utf8StringRef}; +use alloc::string::String; +use der::asn1::OctetString; use der::{Choice, Enumerated, Sequence}; use flagset::{flags, FlagSet}; -use spki::SubjectPublicKeyInfo; +use spki::SubjectPublicKeyInfoRef; /// Version identifier for TrustAnchorInfo #[derive(Clone, Debug, Copy, PartialEq, Eq, Enumerated)] @@ -45,21 +46,21 @@ pub struct TrustAnchorInfo<'a> { #[asn1(default = "Default::default")] pub version: Version, - pub pub_key: SubjectPublicKeyInfo<'a>, + pub pub_key: SubjectPublicKeyInfoRef<'a>, - pub key_id: OctetStringRef<'a>, + pub key_id: OctetString, #[asn1(optional = "true")] - pub ta_title: Option>, + pub ta_title: Option, #[asn1(optional = "true")] pub cert_path: Option>, #[asn1(context_specific = "1", tag_mode = "EXPLICIT", optional = "true")] - pub extensions: Option>, + pub extensions: Option, #[asn1(context_specific = "2", tag_mode = "IMPLICIT", optional = "true")] - pub ta_title_lang_tag: Option>, + pub ta_title_lang_tag: Option, } /// ```text @@ -75,19 +76,19 @@ 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>, + pub certificate: Option>, #[asn1(context_specific = "1", tag_mode = "IMPLICIT", optional = "true")] - pub policy_set: Option>, + pub policy_set: Option, #[asn1(context_specific = "2", tag_mode = "IMPLICIT", optional = "true")] pub policy_flags: Option, #[asn1(context_specific = "3", tag_mode = "IMPLICIT", optional = "true")] - pub name_constr: Option>, + pub name_constr: Option, #[asn1(context_specific = "4", tag_mode = "IMPLICIT", optional = "true")] pub path_len_constraint: Option, @@ -129,10 +130,10 @@ pub type CertPolicyFlags = FlagSet; #[allow(clippy::large_enum_variant)] #[allow(missing_docs)] pub enum TrustAnchorChoice<'a> { - Certificate(Certificate<'a>), + Certificate(CertificateRef<'a>), #[asn1(context_specific = "1", tag_mode = "EXPLICIT", constructed = "true")] - TbsCertificate(TbsCertificate<'a>), + TbsCertificate(TbsCertificateRef<'a>), #[asn1(context_specific = "2", tag_mode = "EXPLICIT", constructed = "true")] TaInfo(TrustAnchorInfo<'a>), diff --git a/x509-cert/src/attr.rs b/x509-cert/src/attr.rs index 2b4336ad0..802135760 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}; 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() } @@ -189,7 +192,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() } @@ -221,13 +224,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 cf719489c..c409a09aa 100644 --- a/x509-cert/src/certificate.rs +++ b/x509-cert/src/certificate.rs @@ -1,12 +1,12 @@ //! Certificate types -use crate::{name::Name, time::Validity}; +use crate::{name::Name, serial_number::SerialNumber, time::Validity}; use alloc::vec::Vec; use core::cmp::Ordering; use const_oid::AssociatedOid; -use der::asn1::{BitStringRef, UIntRef}; +use der::asn1::{AnyRef, BitString, BitStringRef}; use der::{Decode, Enumerated, Error, ErrorKind, Sequence, ValueOrd}; use spki::{AlgorithmIdentifier, SubjectPublicKeyInfo}; @@ -36,7 +36,7 @@ pub enum Version { impl ValueOrd for Version { fn value_cmp(&self, other: &Self) -> der::Result { - (&(*self as u8)).value_cmp(&(*other as u8)) + (*self as u8).value_cmp(&(*other as u8)) } } @@ -71,9 +71,16 @@ impl Default for Version { /// ``` /// /// [RFC 5280 Section 4.1]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.1 -#[derive(Clone, Debug, Eq, PartialEq, Sequence, ValueOrd)] +#[derive(Clone, Debug, Eq, PartialEq, Sequence)] // /*, ValueOrd*/)] #[allow(missing_docs)] -pub struct TbsCertificate<'a> { +#[asn1( + choice = "SignParams", + choice = "KeyParams", + encode = "SignParams", + encode = "KeyParams", + bitstringlike = "Key" +)] +pub struct TbsCertificate { /// The certificate version /// /// Note that this value defaults to Version 1 per the RFC. However, @@ -83,30 +90,30 @@ pub struct TbsCertificate<'a> { #[asn1(context_specific = "0", default = "Default::default")] pub version: Version, - pub serial_number: UIntRef<'a>, - pub signature: AlgorithmIdentifier<'a>, - pub issuer: Name<'a>, + pub serial_number: SerialNumber, + pub signature: AlgorithmIdentifier, + pub issuer: Name, pub validity: Validity, - pub subject: Name<'a>, - pub subject_public_key_info: SubjectPublicKeyInfo<'a>, + pub subject: Name, + pub subject_public_key_info: SubjectPublicKeyInfo, #[asn1(context_specific = "1", tag_mode = "IMPLICIT", optional = "true")] - pub issuer_unique_id: Option>, + pub issuer_unique_id: Option, #[asn1(context_specific = "2", tag_mode = "IMPLICIT", optional = "true")] - pub subject_unique_id: Option>, + pub subject_unique_id: Option, #[asn1(context_specific = "3", tag_mode = "EXPLICIT", optional = "true")] - pub extensions: Option>, + pub extensions: Option, } -impl<'a> TbsCertificate<'a> { +impl TbsCertificate { /// Decodes a single extension /// /// Returns an error if multiple of these extensions is present. Returns /// `Ok(None)` if the extension is not present. Returns a decoding error /// if decoding failed. Otherwise returns the extension. - pub fn get<'b: 'a, T: Decode<'a> + AssociatedOid>( + pub fn get<'a, 'b: 'a, T: Decode<'a> + AssociatedOid>( &'b self, ) -> Result, Error> { let mut iter = self.filter::().peekable(); @@ -122,7 +129,7 @@ impl<'a> TbsCertificate<'a> { /// Filters extensions by an associated OID /// /// Returns a filtered iterator over all the extensions with the OID. - pub fn filter<'b: 'a, T: Decode<'a> + AssociatedOid>( + pub fn filter<'a, 'b: 'a, T: Decode<'a> + AssociatedOid>( &'b self, ) -> impl 'b + Iterator> { self.extensions @@ -130,7 +137,7 @@ impl<'a> TbsCertificate<'a> { .unwrap_or(&[]) .iter() .filter(|e| e.extn_id == T::OID) - .map(|e| Ok((e.critical, T::from_der(e.extn_value)?))) + .map(|e| Ok((e.critical, T::from_der(e.extn_value.as_bytes())?))) } } @@ -145,17 +152,24 @@ impl<'a> TbsCertificate<'a> { /// ``` /// /// [RFC 5280 Section 4.1]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.1 -#[derive(Clone, Debug, Eq, PartialEq, Sequence, ValueOrd)] +#[derive(Clone, Debug, Eq, PartialEq, Sequence)] //, ValueOrd)] #[allow(missing_docs)] -pub struct Certificate<'a> { - pub tbs_certificate: TbsCertificate<'a>, - pub signature_algorithm: AlgorithmIdentifier<'a>, - pub signature: BitStringRef<'a>, +#[asn1( + choice = "SignParams", + choice = "KeyParams", + encode = "SignParams", + encode = "KeyParams", + bitstringlike = "Key" +)] +pub struct Certificate { + pub tbs_certificate: TbsCertificate, + pub signature_algorithm: AlgorithmIdentifier, + pub signature: BitString, } #[cfg(feature = "pem")] #[cfg_attr(docsrs, doc(cfg(feature = "pem")))] -impl PemLabel for Certificate<'_> { +impl PemLabel for Certificate { const PEM_LABEL: &'static str = "CERTIFICATE"; } @@ -170,4 +184,9 @@ impl PemLabel for Certificate<'_> { /// ``` /// /// [RFC 6066]: https://datatracker.ietf.org/doc/html/rfc6066#section-10.1 -pub type PkiPath<'a> = Vec>; +pub type PkiPath<'a> = Vec>; + +/// `Certificate` reference which has `AnyRef` and `BitStringRef` parameters. +pub type CertificateRef<'a> = Certificate, AnyRef<'a>, BitStringRef<'a>>; +/// `TbsCertificate` reference which has `AnyRef` and `BitStringRef` parameters. +pub type TbsCertificateRef<'a> = TbsCertificate, AnyRef<'a>, BitStringRef<'a>>; diff --git a/x509-cert/src/crl.rs b/x509-cert/src/crl.rs index 09256f22b..a75c504cb 100644 --- a/x509-cert/src/crl.rs +++ b/x509-cert/src/crl.rs @@ -2,14 +2,15 @@ use crate::ext::Extensions; use crate::name::Name; +use crate::serial_number::SerialNumber; use crate::time::Time; use crate::Version; use alloc::vec::Vec; -use der::asn1::{BitStringRef, UIntRef}; +use der::asn1::BitString; use der::{Sequence, ValueOrd}; -use spki::AlgorithmIdentifier; +use spki::AlgorithmIdentifierRef; /// `CertificateList` as defined in [RFC 5280 Section 5.1]. /// @@ -26,8 +27,8 @@ use spki::AlgorithmIdentifier; #[allow(missing_docs)] pub struct CertificateList<'a> { pub tbs_cert_list: TbsCertList<'a>, - pub signature_algorithm: AlgorithmIdentifier<'a>, - pub signature: BitStringRef<'a>, + pub signature_algorithm: AlgorithmIdentifierRef<'a>, + pub signature: BitString, } /// Implicit intermediate structure from the ASN.1 definition of `TBSCertList`. @@ -46,10 +47,10 @@ pub struct CertificateList<'a> { /// [RFC 5280 Section 5.1]: https://datatracker.ietf.org/doc/html/rfc5280#section-5.1 #[derive(Clone, Debug, Eq, PartialEq, Sequence, ValueOrd)] #[allow(missing_docs)] -pub struct RevokedCert<'a> { - pub serial_number: UIntRef<'a>, +pub struct RevokedCert { + pub serial_number: SerialNumber, pub revocation_date: Time, - pub crl_entry_extensions: Option>, + pub crl_entry_extensions: Option, } /// `TbsCertList` as defined in [RFC 5280 Section 5.1]. @@ -75,12 +76,12 @@ pub struct RevokedCert<'a> { #[allow(missing_docs)] pub struct TbsCertList<'a> { pub version: Version, - pub signature: AlgorithmIdentifier<'a>, - pub issuer: Name<'a>, + pub signature: AlgorithmIdentifierRef<'a>, + pub issuer: Name, pub this_update: Time, pub next_update: Option