From 7284d507146154bdee8d3fd01194a3d73a9dfe7e Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Mon, 7 Jul 2025 13:25:01 -0600 Subject: [PATCH] der: improved internal ref types Changes `BytesRef` and `StringRef` from types with an explicit lifetime which act as newtypes for inner `&'a [u8]` and `&'a str` types to being newtypes for `[u8]` and `str`. This requires a very small amount of `unsafe` casting, but makes it possible to implement reference conversions entirely in terms of standard traits like `AsRef`, `Borrow`, `Deref`, and `ToOwned` as opposed to using our custom `RefToOwned` and OwnedToRef` traits. It makes it possible to use e.g. `Cow` as well. This is the first step towards moving the entire crate to this pattern and eliminating the `OwnedToRef` and `RefToOwned` raits, replacing them instead with the standard `AsRef` and `ToOwned` traits. --- der/src/asn1/any.rs | 18 +-- der/src/asn1/bit_string.rs | 14 +- der/src/asn1/general_string.rs | 4 +- der/src/asn1/ia5_string.rs | 26 ++-- der/src/asn1/integer/int.rs | 10 +- der/src/asn1/integer/uint.rs | 9 +- der/src/asn1/internal_macros.rs | 2 +- der/src/asn1/null.rs | 2 +- der/src/asn1/octet_string.rs | 6 +- der/src/asn1/printable_string.rs | 19 ++- der/src/asn1/real.rs | 4 +- der/src/asn1/sequence.rs | 4 +- der/src/asn1/teletex_string.rs | 19 ++- der/src/asn1/utf8_string.rs | 13 +- der/src/asn1/videotex_string.rs | 18 +-- der/src/bytes.rs | 232 ++++++++++++------------------- der/src/lib.rs | 2 +- der/src/reader/slice.rs | 2 +- der/src/string.rs | 138 ++++++++++-------- spki/src/algorithm.rs | 11 +- 20 files changed, 270 insertions(+), 283 deletions(-) diff --git a/der/src/asn1/any.rs b/der/src/asn1/any.rs index 439240776..02eaf8e74 100644 --- a/der/src/asn1/any.rs +++ b/der/src/asn1/any.rs @@ -29,14 +29,14 @@ pub struct AnyRef<'a> { tag: Tag, /// Inner value encoded as bytes. - value: BytesRef<'a>, + value: &'a BytesRef, } impl<'a> AnyRef<'a> { /// [`AnyRef`] representation of the ASN.1 `NULL` type. pub const NULL: Self = Self { tag: Tag::Null, - value: BytesRef::EMPTY, + value: BytesRef::new_unchecked(&[]), }; /// Create a new [`AnyRef`] from the provided [`Tag`] and DER bytes. @@ -48,7 +48,7 @@ impl<'a> AnyRef<'a> { } /// Infallible creation of an [`AnyRef`] from a [`BytesRef`]. - pub(crate) fn from_tag_and_value(tag: Tag, value: BytesRef<'a>) -> Self { + pub(crate) fn from_tag_and_value(tag: Tag, value: &'a BytesRef) -> Self { Self { tag, value } } @@ -129,7 +129,7 @@ impl<'a> DecodeValue<'a> for AnyRef<'a> { fn decode_value>(reader: &mut R, header: Header) -> Result { Ok(Self { tag: header.tag, - value: BytesRef::decode_value(reader, header)?, + value: <&'a BytesRef>::decode_value(reader, header)?, }) } } @@ -152,12 +152,12 @@ impl Tagged for AnyRef<'_> { impl ValueOrd for AnyRef<'_> { fn value_cmp(&self, other: &Self) -> Result { - self.value.der_cmp(&other.value) + self.value.der_cmp(other.value) } } -impl<'a> From> for BytesRef<'a> { - fn from(any: AnyRef<'a>) -> BytesRef<'a> { +impl<'a> From> for &'a BytesRef { + fn from(any: AnyRef<'a>) -> &'a BytesRef { any.value } } @@ -256,10 +256,10 @@ mod allocating { } /// Create a new [`AnyRef`] from the provided [`Any`] owned tag and bytes. - pub const fn to_ref(&self) -> AnyRef<'_> { + pub fn to_ref(&self) -> AnyRef<'_> { AnyRef { tag: self.tag, - value: self.value.to_ref(), + value: self.value.as_ref(), } } } diff --git a/der/src/asn1/bit_string.rs b/der/src/asn1/bit_string.rs index 29d3d07ea..f171ba681 100644 --- a/der/src/asn1/bit_string.rs +++ b/der/src/asn1/bit_string.rs @@ -26,7 +26,7 @@ pub struct BitStringRef<'a> { bit_length: usize, /// Bitstring represented as a slice of bytes. - inner: BytesRef<'a>, + inner: &'a BytesRef, } impl<'a> BitStringRef<'a> { @@ -145,7 +145,7 @@ impl<'a> DecodeValue<'a> for BitStringRef<'a> { }; let unused_bits = reader.read_byte()?; - let inner = BytesRef::decode_value(reader, header)?; + let inner = <&'a BytesRef>::decode_value(reader, header)?; Self::new(unused_bits, inner.as_slice()) } } @@ -164,7 +164,7 @@ impl EncodeValue for BitStringRef<'_> { impl ValueOrd for BitStringRef<'_> { fn value_cmp(&self, other: &Self) -> Result { match self.unused_bits.cmp(&other.unused_bits) { - Ordering::Equal => self.inner.der_cmp(&other.inner), + Ordering::Equal => self.inner.der_cmp(other.inner), ordering => Ok(ordering), } } @@ -233,13 +233,13 @@ impl<'a> arbitrary::Arbitrary<'a> for BitStringRef<'a> { fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { Self::new( u.int_in_range(0..=Self::MAX_UNUSED_BITS)?, - BytesRef::arbitrary(u)?.as_slice(), + <&'a BytesRef>::arbitrary(u)?.as_slice(), ) .map_err(|_| arbitrary::Error::IncorrectFormat) } fn size_hint(depth: usize) -> (usize, Option) { - arbitrary::size_hint::and(u8::size_hint(depth), BytesRef::size_hint(depth)) + arbitrary::size_hint::and(u8::size_hint(depth), <&'a BytesRef>::size_hint(depth)) } } @@ -421,13 +421,13 @@ mod allocating { fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { Self::new( u.int_in_range(0..=Self::MAX_UNUSED_BITS)?, - BytesRef::arbitrary(u)?.as_slice(), + <&'a BytesRef>::arbitrary(u)?.as_slice(), ) .map_err(|_| arbitrary::Error::IncorrectFormat) } fn size_hint(depth: usize) -> (usize, Option) { - arbitrary::size_hint::and(u8::size_hint(depth), BytesRef::size_hint(depth)) + arbitrary::size_hint::and(u8::size_hint(depth), <&'a BytesRef>::size_hint(depth)) } } diff --git a/der/src/asn1/general_string.rs b/der/src/asn1/general_string.rs index 95b941503..97bc963ea 100644 --- a/der/src/asn1/general_string.rs +++ b/der/src/asn1/general_string.rs @@ -4,7 +4,7 @@ use crate::{BytesRef, DecodeValue, EncodeValue, FixedTag, Header, Length, Reader #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct GeneralStringRef<'a> { /// Raw contents, unchecked - inner: BytesRef<'a>, + inner: &'a BytesRef, } impl<'a> GeneralStringRef<'a> { /// This is currently `&[u8]` internally, as `GeneralString` is not fully implemented yet @@ -21,7 +21,7 @@ impl<'a> DecodeValue<'a> for GeneralStringRef<'a> { fn decode_value>(reader: &mut R, header: Header) -> Result { Ok(Self { - inner: BytesRef::decode_value(reader, header)?, + inner: <&'a BytesRef>::decode_value(reader, header)?, }) } } diff --git a/der/src/asn1/ia5_string.rs b/der/src/asn1/ia5_string.rs index 6412ece77..e562aac7c 100644 --- a/der/src/asn1/ia5_string.rs +++ b/der/src/asn1/ia5_string.rs @@ -37,7 +37,7 @@ macro_rules! impl_ia5_string { #[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord)] pub struct Ia5StringRef<'a> { /// Inner value - inner: StringRef<'a>, + inner: &'a StringRef, } impl<'a> Ia5StringRef<'a> { @@ -57,15 +57,20 @@ impl<'a> Ia5StringRef<'a> { .map(|inner| Self { inner }) .map_err(|_| Self::TAG.value_error().into()) } + + /// Borrow the inner `str`. + pub fn as_str(&self) -> &'a str { + self.inner.as_str() + } } impl_ia5_string!(Ia5StringRef<'a>, 'a); impl<'a> Deref for Ia5StringRef<'a> { - type Target = StringRef<'a>; + type Target = StringRef; fn deref(&self) -> &Self::Target { - &self.inner + self.inner } } @@ -77,7 +82,7 @@ impl<'a> From<&Ia5StringRef<'a>> for Ia5StringRef<'a> { impl<'a> From> for AnyRef<'a> { fn from(internationalized_string: Ia5StringRef<'a>) -> AnyRef<'a> { - AnyRef::from_tag_and_value(Tag::Ia5String, internationalized_string.inner.into()) + AnyRef::from_tag_and_value(Tag::Ia5String, internationalized_string.inner.as_ref()) } } @@ -92,7 +97,7 @@ mod allocation { asn1::AnyRef, referenced::{OwnedToRef, RefToOwned}, }; - use alloc::string::String; + use alloc::{borrow::ToOwned, string::String}; use core::{fmt, ops::Deref}; /// ASN.1 `IA5String` type. @@ -138,14 +143,15 @@ mod allocation { impl<'a> From> for Ia5String { fn from(ia5_string: Ia5StringRef<'a>) -> Ia5String { - let inner = ia5_string.inner.into(); - Self { inner } + Self { + inner: ia5_string.inner.to_owned(), + } } } impl<'a> From<&'a Ia5String> for AnyRef<'a> { fn from(ia5_string: &'a Ia5String) -> AnyRef<'a> { - AnyRef::from_tag_and_value(Tag::Ia5String, (&ia5_string.inner).into()) + AnyRef::from_tag_and_value(Tag::Ia5String, ia5_string.inner.as_ref()) } } @@ -159,7 +165,7 @@ mod allocation { type Owned = Ia5String; fn ref_to_owned(&self) -> Self::Owned { Ia5String { - inner: self.inner.ref_to_owned(), + inner: self.inner.to_owned(), } } } @@ -168,7 +174,7 @@ mod allocation { type Borrowed<'a> = Ia5StringRef<'a>; fn owned_to_ref(&self) -> Self::Borrowed<'_> { Ia5StringRef { - inner: self.inner.owned_to_ref(), + inner: self.inner.as_ref(), } } } diff --git a/der/src/asn1/integer/int.rs b/der/src/asn1/integer/int.rs index f95420bf3..d1692562f 100644 --- a/der/src/asn1/integer/int.rs +++ b/der/src/asn1/integer/int.rs @@ -102,7 +102,7 @@ impl_encoding_traits!(i8 => u8, i16 => u16, i32 => u32, i64 => u64, i128 => u128 #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub struct IntRef<'a> { /// Inner value - inner: BytesRef<'a>, + inner: &'a BytesRef, } impl<'a> IntRef<'a> { @@ -137,7 +137,7 @@ impl<'a> DecodeValue<'a> for IntRef<'a> { type Error = Error; fn decode_value>(reader: &mut R, header: Header) -> Result { - let bytes = BytesRef::decode_value(reader, header)?; + let bytes = <&'a BytesRef>::decode_value(reader, header)?; validate_canonical(bytes.as_slice())?; let result = Self::new(bytes.as_slice())?; @@ -184,7 +184,7 @@ mod allocating { ord::OrdIsValueOrd, referenced::{OwnedToRef, RefToOwned}, }; - use alloc::vec::Vec; + use alloc::{borrow::ToOwned, vec::Vec}; /// Signed arbitrary precision ASN.1 `INTEGER` type. /// @@ -288,7 +288,7 @@ mod allocating { impl<'a> RefToOwned<'a> for IntRef<'a> { type Owned = Int; fn ref_to_owned(&self) -> Self::Owned { - let inner = self.inner.ref_to_owned(); + let inner = self.inner.to_owned(); Int { inner } } @@ -297,7 +297,7 @@ mod allocating { impl OwnedToRef for Int { type Borrowed<'a> = IntRef<'a>; fn owned_to_ref(&self) -> Self::Borrowed<'_> { - let inner = self.inner.owned_to_ref(); + let inner = self.inner.as_ref(); IntRef { inner } } diff --git a/der/src/asn1/integer/uint.rs b/der/src/asn1/integer/uint.rs index 289dfd1ee..cccde514a 100644 --- a/der/src/asn1/integer/uint.rs +++ b/der/src/asn1/integer/uint.rs @@ -87,7 +87,7 @@ impl_encoding_traits!(u8, u16, u32, u64, u128); #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub struct UintRef<'a> { /// Inner value - inner: BytesRef<'a>, + inner: &'a BytesRef, } impl<'a> UintRef<'a> { @@ -122,7 +122,7 @@ impl<'a> DecodeValue<'a> for UintRef<'a> { type Error = Error; fn decode_value>(reader: &mut R, header: Header) -> Result { - let bytes = BytesRef::decode_value(reader, header)?.as_slice(); + let bytes = <&'a BytesRef>::decode_value(reader, header)?.as_slice(); let result = Self::new(decode_to_slice(bytes)?)?; // Ensure we compute the same encoded length as the original any value. @@ -170,6 +170,7 @@ mod allocating { ord::OrdIsValueOrd, referenced::{OwnedToRef, RefToOwned}, }; + use alloc::borrow::ToOwned; /// Unsigned arbitrary precision ASN.1 `INTEGER` type. /// @@ -260,7 +261,7 @@ mod allocating { impl<'a> RefToOwned<'a> for UintRef<'a> { type Owned = Uint; fn ref_to_owned(&self) -> Self::Owned { - let inner = self.inner.ref_to_owned(); + let inner = self.inner.to_owned(); Uint { inner } } @@ -269,7 +270,7 @@ mod allocating { impl OwnedToRef for Uint { type Borrowed<'a> = UintRef<'a>; fn owned_to_ref(&self) -> Self::Borrowed<'_> { - let inner = self.inner.owned_to_ref(); + let inner = self.inner.as_ref(); UintRef { inner } } diff --git a/der/src/asn1/internal_macros.rs b/der/src/asn1/internal_macros.rs index 8eb11165e..c064ccc30 100644 --- a/der/src/asn1/internal_macros.rs +++ b/der/src/asn1/internal_macros.rs @@ -51,7 +51,7 @@ macro_rules! impl_string_type { type Error = $crate::Error; fn decode_value>(reader: &mut R, header: Header) -> $crate::Result { - Self::new(BytesRef::decode_value(reader, header)?.as_slice()) + Self::new(<&'__der BytesRef>::decode_value(reader, header)?.as_slice()) } } diff --git a/der/src/asn1/null.rs b/der/src/asn1/null.rs index a0eff7310..b8da1b0b8 100644 --- a/der/src/asn1/null.rs +++ b/der/src/asn1/null.rs @@ -41,7 +41,7 @@ impl OrdIsValueOrd for Null {} impl<'a> From for AnyRef<'a> { fn from(_: Null) -> AnyRef<'a> { - AnyRef::from_tag_and_value(Tag::Null, BytesRef::default()) + AnyRef::from_tag_and_value(Tag::Null, BytesRef::EMPTY) } } diff --git a/der/src/asn1/octet_string.rs b/der/src/asn1/octet_string.rs index 125df229f..f16a9e12f 100644 --- a/der/src/asn1/octet_string.rs +++ b/der/src/asn1/octet_string.rs @@ -13,7 +13,7 @@ use crate::{ #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub struct OctetStringRef<'a> { /// Inner value - inner: BytesRef<'a>, + inner: &'a BytesRef, } impl<'a> OctetStringRef<'a> { @@ -57,7 +57,7 @@ impl<'a> DecodeValue<'a> for OctetStringRef<'a> { type Error = Error; fn decode_value>(reader: &mut R, header: Header) -> Result { - let inner = BytesRef::decode_value(reader, header)?; + let inner = <&'a BytesRef>::decode_value(reader, header)?; Ok(Self { inner }) } } @@ -240,7 +240,7 @@ mod allocating { impl<'a> From<&'a OctetString> for OctetStringRef<'a> { fn from(octet_string: &'a OctetString) -> OctetStringRef<'a> { OctetStringRef { - inner: octet_string.inner.owned_to_ref(), + inner: octet_string.inner.as_ref(), } } } diff --git a/der/src/asn1/printable_string.rs b/der/src/asn1/printable_string.rs index c42cdae24..a8e2a39ec 100644 --- a/der/src/asn1/printable_string.rs +++ b/der/src/asn1/printable_string.rs @@ -54,7 +54,7 @@ macro_rules! impl_printable_string { #[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord)] pub struct PrintableStringRef<'a> { /// Inner value - inner: StringRef<'a>, + inner: &'a StringRef, } impl<'a> PrintableStringRef<'a> { @@ -91,15 +91,20 @@ impl<'a> PrintableStringRef<'a> { .map(|inner| Self { inner }) .map_err(|_| Self::TAG.value_error().into()) } + + /// Borrow the inner `str`. + pub fn as_str(&self) -> &'a str { + self.inner.as_str() + } } impl_printable_string!(PrintableStringRef<'a>, 'a); impl<'a> Deref for PrintableStringRef<'a> { - type Target = StringRef<'a>; + type Target = StringRef; fn deref(&self) -> &Self::Target { - &self.inner + self.inner } } impl<'a> From<&PrintableStringRef<'a>> for PrintableStringRef<'a> { @@ -110,7 +115,7 @@ impl<'a> From<&PrintableStringRef<'a>> for PrintableStringRef<'a> { 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()) + AnyRef::from_tag_and_value(Tag::PrintableString, printable_string.inner.as_ref()) } } @@ -126,7 +131,7 @@ mod allocation { asn1::AnyRef, referenced::{OwnedToRef, RefToOwned}, }; - use alloc::string::String; + use alloc::{borrow::ToOwned, string::String}; use core::{fmt, ops::Deref}; /// ASN.1 `PrintableString` type. @@ -214,7 +219,7 @@ mod allocation { type Owned = PrintableString; fn ref_to_owned(&self) -> Self::Owned { PrintableString { - inner: self.inner.ref_to_owned(), + inner: self.inner.to_owned(), } } } @@ -223,7 +228,7 @@ mod allocation { type Borrowed<'a> = PrintableStringRef<'a>; fn owned_to_ref(&self) -> Self::Borrowed<'_> { PrintableStringRef { - inner: self.inner.owned_to_ref(), + inner: self.inner.as_ref(), } } } diff --git a/der/src/asn1/real.rs b/der/src/asn1/real.rs index 4c76d697b..cfbe16488 100644 --- a/der/src/asn1/real.rs +++ b/der/src/asn1/real.rs @@ -18,7 +18,7 @@ impl<'a> DecodeValue<'a> for f64 { type Error = Error; fn decode_value>(reader: &mut R, header: Header) -> Result { - let bytes = BytesRef::decode_value(reader, header)?.as_slice(); + let bytes = <&'a BytesRef>::decode_value(reader, header)?.as_slice(); if header.length == Length::ZERO { Ok(0.0) @@ -76,7 +76,7 @@ impl<'a> DecodeValue<'a> for f64 { } } else { let astr = StringRef::from_bytes(&bytes[1..])?; - match astr.inner.parse::() { + match astr.as_str().parse::() { Ok(val) => Ok(val), // Real related error: encoding not supported or malformed Err(_) => Err(reader.error(Tag::Real.value_error())), diff --git a/der/src/asn1/sequence.rs b/der/src/asn1/sequence.rs index 70d01b04b..9b7c8a70b 100644 --- a/der/src/asn1/sequence.rs +++ b/der/src/asn1/sequence.rs @@ -30,7 +30,7 @@ impl<'a, T> Sequence<'a> for Box where T: Sequence<'a> {} /// This is a zero-copy reference type which borrows from the input data. pub struct SequenceRef<'a> { /// Body of the `SEQUENCE`. - body: BytesRef<'a>, + body: &'a BytesRef, } impl<'a> SequenceRef<'a> { @@ -51,7 +51,7 @@ impl<'a> DecodeValue<'a> for SequenceRef<'a> { fn decode_value>(reader: &mut R, header: Header) -> Result { Ok(Self { - body: BytesRef::decode_value(reader, header)?, + body: <&'a BytesRef>::decode_value(reader, header)?, }) } } diff --git a/der/src/asn1/teletex_string.rs b/der/src/asn1/teletex_string.rs index f1d16fa94..57d3d0c8a 100644 --- a/der/src/asn1/teletex_string.rs +++ b/der/src/asn1/teletex_string.rs @@ -44,7 +44,7 @@ macro_rules! impl_teletex_string { #[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord)] pub struct TeletexStringRef<'a> { /// Inner value - inner: StringRef<'a>, + inner: &'a StringRef, } impl<'a> TeletexStringRef<'a> { @@ -64,15 +64,20 @@ impl<'a> TeletexStringRef<'a> { .map(|inner| Self { inner }) .map_err(|_| Self::TAG.value_error().into()) } + + /// Borrow the inner `str`. + pub fn as_str(&self) -> &'a str { + self.inner.as_str() + } } impl_teletex_string!(TeletexStringRef<'a>, 'a); impl<'a> Deref for TeletexStringRef<'a> { - type Target = StringRef<'a>; + type Target = StringRef; fn deref(&self) -> &Self::Target { - &self.inner + self.inner } } @@ -84,7 +89,7 @@ impl<'a> From<&TeletexStringRef<'a>> for TeletexStringRef<'a> { 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()) + AnyRef::from_tag_and_value(Tag::TeletexString, teletex_string.inner.as_ref()) } } @@ -100,7 +105,7 @@ mod allocation { asn1::AnyRef, referenced::{OwnedToRef, RefToOwned}, }; - use alloc::string::String; + use alloc::{borrow::ToOwned, string::String}; use core::{fmt, ops::Deref}; /// ASN.1 `TeletexString` type. @@ -176,7 +181,7 @@ mod allocation { type Owned = TeletexString; fn ref_to_owned(&self) -> Self::Owned { TeletexString { - inner: self.inner.ref_to_owned(), + inner: self.inner.to_owned(), } } } @@ -185,7 +190,7 @@ mod allocation { type Borrowed<'a> = TeletexStringRef<'a>; fn owned_to_ref(&self) -> Self::Borrowed<'_> { TeletexStringRef { - inner: self.inner.owned_to_ref(), + inner: self.inner.as_ref(), } } } diff --git a/der/src/asn1/utf8_string.rs b/der/src/asn1/utf8_string.rs index 5e39c1c7e..1f0a6a1c5 100644 --- a/der/src/asn1/utf8_string.rs +++ b/der/src/asn1/utf8_string.rs @@ -29,7 +29,7 @@ use { #[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord)] pub struct Utf8StringRef<'a> { /// Inner value - inner: StringRef<'a>, + inner: &'a StringRef, } impl<'a> Utf8StringRef<'a> { @@ -40,15 +40,20 @@ impl<'a> Utf8StringRef<'a> { { StringRef::from_bytes(input.as_ref()).map(|inner| Self { inner }) } + + /// Borrow the inner `str`. + pub fn as_str(&self) -> &'a str { + self.inner.as_str() + } } impl_string_type!(Utf8StringRef<'a>, 'a); impl<'a> Deref for Utf8StringRef<'a> { - type Target = StringRef<'a>; + type Target = StringRef; fn deref(&self) -> &Self::Target { - &self.inner + self.inner } } @@ -64,7 +69,7 @@ impl<'a> From<&Utf8StringRef<'a>> for Utf8StringRef<'a> { impl<'a> From> for AnyRef<'a> { fn from(utf_string: Utf8StringRef<'a>) -> AnyRef<'a> { - AnyRef::from_tag_and_value(Tag::Utf8String, utf_string.inner.into()) + AnyRef::from_tag_and_value(Tag::Utf8String, utf_string.inner.as_ref()) } } diff --git a/der/src/asn1/videotex_string.rs b/der/src/asn1/videotex_string.rs index 1e5239958..063db6a39 100644 --- a/der/src/asn1/videotex_string.rs +++ b/der/src/asn1/videotex_string.rs @@ -20,7 +20,7 @@ use core::{fmt, ops::Deref}; #[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord)] pub struct VideotexStringRef<'a> { /// Inner value - inner: StringRef<'a>, + inner: &'a StringRef, } impl<'a> VideotexStringRef<'a> { @@ -46,10 +46,10 @@ impl<'a> VideotexStringRef<'a> { impl_string_type!(VideotexStringRef<'a>, 'a); impl<'a> Deref for VideotexStringRef<'a> { - type Target = StringRef<'a>; + type Target = StringRef; fn deref(&self) -> &Self::Target { - &self.inner + self.inner } } @@ -64,14 +64,14 @@ impl<'a> From<&VideotexStringRef<'a>> for VideotexStringRef<'a> { } 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()) + fn from(videotex_string: VideotexStringRef<'a>) -> AnyRef<'a> { + AnyRef::from_tag_and_value(Tag::VideotexString, videotex_string.inner.as_ref()) } } impl<'a> From> for &'a [u8] { - fn from(printable_string: VideotexStringRef<'a>) -> &'a [u8] { - printable_string.as_bytes() + fn from(videotex_string: VideotexStringRef<'a>) -> &'a [u8] { + videotex_string.inner.as_bytes() } } @@ -93,7 +93,7 @@ mod tests { 0x15, 0x0b, 0x54, 0x65, 0x73, 0x74, 0x20, 0x55, 0x73, 0x65, 0x72, 0x20, 0x31, ]; - let printable_string = VideotexStringRef::from_der(example_bytes).unwrap(); - assert_eq!(printable_string.as_str(), "Test User 1"); + let videotex_string = VideotexStringRef::from_der(example_bytes).unwrap(); + assert_eq!(videotex_string.as_str(), "Test User 1"); } } diff --git a/der/src/bytes.rs b/der/src/bytes.rs index 0cb108cf5..6e0f12714 100644 --- a/der/src/bytes.rs +++ b/der/src/bytes.rs @@ -1,91 +1,82 @@ //! Common handling for types backed by byte slices with enforcement of a //! library-level length limitation i.e. `Length::max()`. -use crate::{ - DecodeValue, DerOrd, EncodeValue, Error, ErrorKind, Header, Length, Reader, Result, StringRef, - Writer, -}; +use crate::{DecodeValue, DerOrd, EncodeValue, Error, Header, Length, Reader, Result, Writer}; use core::cmp::Ordering; -#[cfg(feature = "alloc")] -use crate::StringOwned; - -/// Byte slice newtype which respects the `Length::max()` limit. -#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] -pub(crate) struct BytesRef<'a> { - /// Precomputed `Length` (avoids possible panicking conversions) - pub length: Length, - - /// Inner value - pub inner: &'a [u8], -} +/// Byte slice newtype which respects the `Length::MAX` limit. +#[derive(Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] +#[repr(transparent)] +pub(crate) struct BytesRef([u8]); -impl<'a> BytesRef<'a> { +impl BytesRef { /// Constant value representing an empty byte slice. - pub const EMPTY: Self = Self { - length: Length::ZERO, - inner: &[], - }; + pub const EMPTY: &'static Self = Self::new_unchecked(&[]); /// Create a new [`BytesRef`], ensuring that the provided `slice` value - /// is shorter than `Length::max()`. - pub const fn new(slice: &'a [u8]) -> Result { + /// is shorter than `Length::MAX`. + pub const fn new(slice: &[u8]) -> Result<&Self> { match Length::new_usize(slice.len()) { - Ok(length) => Ok(Self { - length, - inner: slice, - }), + Ok(_) => Ok(Self::new_unchecked(slice)), Err(err) => Err(err), } } + /// Perform a raw conversion of a byte slice to `Self` without first performing a length check. + pub(crate) const fn new_unchecked(slice: &[u8]) -> &Self { + // SAFETY: `Self` is a `repr(transparent)` newtype for `[u8]` + #[allow(unsafe_code)] + unsafe { + &*(slice as *const [u8] as *const Self) + } + } + /// Borrow the inner byte slice - pub fn as_slice(&self) -> &'a [u8] { - self.inner + pub const fn as_slice(&self) -> &[u8] { + &self.0 } - /// Get the [`Length`] of this [`BytesRef`] - pub fn len(self) -> Length { - self.length + /// Get the [`Length`] of this [`BytesRef`]. + pub fn len(&self) -> Length { + debug_assert!(u32::try_from(self.0.len()).is_ok()); + + #[allow(clippy::cast_possible_truncation)] // checked by constructors + Length::new(self.0.len() as u32) } /// Is this [`BytesRef`] empty? - pub fn is_empty(self) -> bool { - self.len() == Length::ZERO + pub const fn is_empty(&self) -> bool { + self.0.is_empty() } - /// Get a prefix of a [`BytesRef`] of the given length. - pub fn prefix(self, length: Length) -> Result { + /// Get a prefix of a [`crate::bytes_ref::BytesRef`] of the given length. + pub fn prefix(&self, length: Length) -> Result<&Self> { let inner = self .as_slice() .get(..usize::try_from(length)?) - .ok_or_else(|| Error::incomplete(self.length))?; + .ok_or_else(|| Error::incomplete(self.len()))?; - Ok(Self { length, inner }) + Ok(Self::new_unchecked(inner)) } } -impl AsRef<[u8]> for BytesRef<'_> { +impl AsRef<[u8]> for BytesRef { fn as_ref(&self) -> &[u8] { self.as_slice() } } -impl<'a> DecodeValue<'a> for BytesRef<'a> { +impl<'a> DecodeValue<'a> for &'a BytesRef { type Error = Error; fn decode_value>(reader: &mut R, header: Header) -> Result { - if header.length.is_indefinite() && !header.tag.is_constructed() { - return Err(reader.error(ErrorKind::IndefiniteLength)); - } - - reader.read_slice(header.length).and_then(Self::new) + BytesRef::new(reader.read_slice(header.length)?) } } -impl EncodeValue for BytesRef<'_> { +impl EncodeValue for BytesRef { fn value_len(&self) -> Result { - Ok(self.length) + Ok(self.len()) } fn encode_value(&self, writer: &mut impl Writer) -> Result<()> { @@ -93,64 +84,29 @@ impl EncodeValue for BytesRef<'_> { } } -impl Default for BytesRef<'_> { - fn default() -> Self { - Self { - length: Length::ZERO, - inner: &[], - } - } -} - -impl DerOrd for BytesRef<'_> { +impl DerOrd for BytesRef { fn der_cmp(&self, other: &Self) -> Result { Ok(self.as_slice().cmp(other.as_slice())) } } -impl<'a> From> for BytesRef<'a> { - fn from(s: StringRef<'a>) -> BytesRef<'a> { - let bytes = s.as_bytes(); - debug_assert_eq!(bytes.len(), usize::try_from(s.length).expect("overflow")); - - BytesRef { - inner: bytes, - length: s.length, - } - } -} - -#[cfg(feature = "alloc")] -impl<'a> From<&'a StringOwned> for BytesRef<'a> { - fn from(s: &'a StringOwned) -> BytesRef<'a> { - let bytes = s.as_bytes(); - debug_assert_eq!(bytes.len(), usize::try_from(s.length).expect("overflow")); - - BytesRef { - inner: bytes, - length: s.length, - } - } -} - -impl<'a> TryFrom<&'a [u8]> for BytesRef<'a> { +impl<'a> TryFrom<&'a [u8]> for &'a BytesRef { type Error = Error; fn try_from(slice: &'a [u8]) -> Result { - Self::new(slice) + BytesRef::new(slice) } } -// Implement by hand because the derive would create invalid values. -// Make sure the length and the inner.len matches. +/// Implemented by hand because the derive would create invalid values. +/// Makes sure the length and the inner.len matches. #[cfg(feature = "arbitrary")] -impl<'a> arbitrary::Arbitrary<'a> for BytesRef<'a> { +impl<'a> arbitrary::Arbitrary<'a> for &'a BytesRef { fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { - let length = u.arbitrary()?; - Ok(Self { - length, - inner: u.bytes(u32::from(length) as usize)?, - }) + let length: Length = u.arbitrary()?; + Ok(BytesRef::new_unchecked( + u.bytes(u32::from(length) as usize)?, + )) } fn size_hint(depth: usize) -> (usize, Option) { @@ -162,12 +118,11 @@ impl<'a> arbitrary::Arbitrary<'a> for BytesRef<'a> { pub(crate) mod allocating { use super::BytesRef; use crate::{ - DecodeValue, DerOrd, EncodeValue, Error, Header, Length, Reader, Result, StringRef, Writer, + DecodeValue, DerOrd, EncodeValue, Error, Header, Length, Reader, Result, Writer, length::indefinite::read_constructed_vec, - referenced::{OwnedToRef, RefToOwned}, }; - use alloc::{boxed::Box, vec::Vec}; - use core::cmp::Ordering; + use alloc::{borrow::ToOwned, boxed::Box, vec::Vec}; + use core::{borrow::Borrow, cmp::Ordering, ops::Deref}; /// Byte slice newtype which respects the `Length::max()` limit. #[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] @@ -190,34 +145,37 @@ pub(crate) mod allocating { inner, }) } + } - /// Borrow the inner byte slice - pub const fn as_slice(&self) -> &[u8] { + impl AsRef<[u8]> for BytesOwned { + fn as_ref(&self) -> &[u8] { &self.inner } + } - /// Get the [`Length`] of this [`BytesRef`] - pub const fn len(&self) -> Length { - self.length + impl AsRef for BytesOwned { + fn as_ref(&self) -> &BytesRef { + BytesRef::new_unchecked(&self.inner) } + } - /// Is this [`BytesOwned`] empty? - pub const fn is_empty(&self) -> bool { - self.len().is_zero() + impl Borrow<[u8]> for BytesOwned { + fn borrow(&self) -> &[u8] { + &self.inner } + } - /// Create [`BytesRef`] from allocated [`BytesOwned`]. - pub const fn to_ref(&self) -> BytesRef<'_> { - BytesRef { - length: self.length, - inner: &self.inner, - } + impl Borrow for BytesOwned { + fn borrow(&self) -> &BytesRef { + BytesRef::new_unchecked(&self.inner) } } - impl AsRef<[u8]> for BytesOwned { - fn as_ref(&self) -> &[u8] { - self.as_slice() + impl Deref for BytesOwned { + type Target = BytesRef; + + fn deref(&self) -> &BytesRef { + self.borrow() } } @@ -265,40 +223,11 @@ pub(crate) mod allocating { } } - impl From> for BytesOwned { - fn from(s: StringRef<'_>) -> BytesOwned { - let bytes = s.as_bytes(); - debug_assert_eq!(bytes.len(), usize::try_from(s.length).expect("overflow")); - + impl From<&BytesRef> for BytesOwned { + fn from(bytes: &BytesRef) -> BytesOwned { BytesOwned { - inner: Box::from(bytes), - length: s.length, - } - } - } - - impl OwnedToRef for BytesOwned { - type Borrowed<'a> = BytesRef<'a>; - fn owned_to_ref(&self) -> Self::Borrowed<'_> { - BytesRef { - length: self.length, - inner: self.inner.as_ref(), - } - } - } - - impl<'a> RefToOwned<'a> for BytesRef<'a> { - type Owned = BytesOwned; - fn ref_to_owned(&self) -> Self::Owned { - BytesOwned::from(*self) - } - } - - impl From> for BytesOwned { - fn from(s: BytesRef<'_>) -> BytesOwned { - BytesOwned { - length: s.length, - inner: Box::from(s.inner), + length: bytes.len(), + inner: bytes.as_slice().into(), } } } @@ -327,6 +256,17 @@ pub(crate) mod allocating { } } + impl ToOwned for BytesRef { + type Owned = BytesOwned; + + fn to_owned(&self) -> BytesOwned { + BytesOwned { + inner: self.as_slice().into(), + length: self.len(), + } + } + } + // Implement by hand because the derive would create invalid values. // Make sure the length and the inner.len matches. #[cfg(feature = "arbitrary")] diff --git a/der/src/lib.rs b/der/src/lib.rs index c772b1d29..d867ef928 100644 --- a/der/src/lib.rs +++ b/der/src/lib.rs @@ -5,7 +5,7 @@ html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg", html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg" )] -#![forbid(unsafe_code)] +#![deny(unsafe_code)] // only allowed for transmuting newtype references #![warn( // TODO: re-enable this lint and fix its warnings // clippy::arithmetic_side_effects, diff --git a/der/src/reader/slice.rs b/der/src/reader/slice.rs index 1d8d4fa25..115155eac 100644 --- a/der/src/reader/slice.rs +++ b/der/src/reader/slice.rs @@ -6,7 +6,7 @@ use crate::{BytesRef, Decode, EncodingRules, Error, ErrorKind, Length, Reader}; #[derive(Clone, Debug)] pub struct SliceReader<'a> { /// Byte slice being decoded. - bytes: BytesRef<'a>, + bytes: &'a BytesRef, /// Encoding rules to apply when decoding the input. encoding_rules: EncodingRules, diff --git a/der/src/string.rs b/der/src/string.rs index b379d0b2a..080d9f227 100644 --- a/der/src/string.rs +++ b/der/src/string.rs @@ -5,74 +5,87 @@ use crate::{BytesRef, DecodeValue, EncodeValue, Error, Header, Length, Reader, R use core::str; /// String slice newtype which respects the [`Length::max`] limit. -#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] -pub struct StringRef<'a> { - /// Inner value - pub(crate) inner: &'a str, +#[derive(Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] +#[repr(transparent)] +pub struct StringRef(str); - /// Precomputed `Length` (avoids possible panicking conversions) - pub(crate) length: Length, -} - -impl<'a> StringRef<'a> { +impl StringRef { /// Create a new [`StringRef`], ensuring that the byte representation of /// the provided `str` value is shorter than `Length::max()`. - pub fn new(s: &'a str) -> Result { - Ok(Self { - inner: s, - length: Length::try_from(s.len())?, - }) + pub const fn new(s: &str) -> Result<&Self> { + match Length::new_usize(s.len()) { + Ok(_) => Ok(Self::new_unchecked(s)), + Err(err) => Err(err), + } + } + + /// Perform a raw conversion of a `str` to `Self` without first performing a length check. + pub(crate) const fn new_unchecked(s: &str) -> &Self { + // SAFETY: `Self` is a `repr(transparent)` newtype for `str` + #[allow(unsafe_code)] + unsafe { + &*(s as *const str as *const Self) + } } /// Parse a [`StringRef`] from UTF-8 encoded bytes. - pub fn from_bytes(bytes: &'a [u8]) -> Result { + pub fn from_bytes(bytes: &[u8]) -> Result<&Self> { Self::new(str::from_utf8(bytes)?) } - /// Borrow the inner `str` - pub fn as_str(&self) -> &'a str { - self.inner + /// Borrow the inner `str`. + pub fn as_str(&self) -> &str { + &self.0 } - /// Borrow the inner byte slice - pub fn as_bytes(&self) -> &'a [u8] { - self.inner.as_bytes() + /// Borrow the inner byte slice. + pub fn as_bytes(&self) -> &[u8] { + self.0.as_bytes() } - /// Get the [`Length`] of this [`StringRef`] - pub fn len(self) -> Length { - self.length + /// Get the [`Length`] of this [`StringRef`]. + pub fn len(&self) -> Length { + debug_assert!(u32::try_from(self.0.len()).is_ok()); + + #[allow(clippy::cast_possible_truncation)] // checked by constructors + Length::new(self.0.len() as u32) } /// Is this [`StringRef`] empty? - pub fn is_empty(self) -> bool { - self.len() == Length::ZERO + pub fn is_empty(&self) -> bool { + self.0.is_empty() } } -impl AsRef for StringRef<'_> { +impl AsRef for StringRef { fn as_ref(&self) -> &str { self.as_str() } } -impl AsRef<[u8]> for StringRef<'_> { +impl AsRef<[u8]> for StringRef { fn as_ref(&self) -> &[u8] { self.as_bytes() } } -impl<'a> DecodeValue<'a> for StringRef<'a> { +impl AsRef for StringRef { + fn as_ref(&self) -> &BytesRef { + BytesRef::new_unchecked(self.as_bytes()) + } +} + +impl<'a> DecodeValue<'a> for &'a StringRef { type Error = Error; fn decode_value>(reader: &mut R, header: Header) -> Result { - Self::from_bytes(BytesRef::decode_value(reader, header)?.as_slice()) + StringRef::from_bytes(<&'a BytesRef>::decode_value(reader, header)?.as_slice()) } } -impl EncodeValue for StringRef<'_> { +impl EncodeValue for StringRef { fn value_len(&self) -> Result { - Ok(self.length) + Ok(self.len()) } fn encode_value(&self, writer: &mut impl Writer) -> Result<()> { @@ -83,13 +96,11 @@ impl EncodeValue for StringRef<'_> { #[cfg(feature = "alloc")] pub(crate) mod allocating { use super::StringRef; - use crate::referenced::RefToOwned; use crate::{ BytesRef, DecodeValue, EncodeValue, Error, Header, Length, Reader, Result, Writer, - referenced::OwnedToRef, }; - use alloc::string::String; - use core::str; + use alloc::{borrow::ToOwned, string::String}; + use core::{borrow::Borrow, ops::Deref, str}; /// String newtype which respects the [`Length::max`] limit. #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] @@ -151,11 +162,37 @@ pub(crate) mod allocating { } } + impl AsRef for StringOwned { + fn as_ref(&self) -> &BytesRef { + BytesRef::new_unchecked(self.as_bytes()) + } + } + + impl AsRef for StringOwned { + fn as_ref(&self) -> &StringRef { + StringRef::new_unchecked(&self.inner) + } + } + + impl Borrow for StringOwned { + fn borrow(&self) -> &StringRef { + StringRef::new_unchecked(&self.inner) + } + } + + impl Deref for StringOwned { + type Target = StringRef; + + fn deref(&self) -> &StringRef { + self.borrow() + } + } + impl<'a> DecodeValue<'a> for StringOwned { type Error = Error; fn decode_value>(reader: &mut R, header: Header) -> Result { - Self::from_bytes(BytesRef::decode_value(reader, header)?.as_slice()) + Self::from_bytes(<&'a BytesRef>::decode_value(reader, header)?.as_slice()) } } @@ -169,29 +206,14 @@ pub(crate) mod allocating { } } - impl From> for StringOwned { - fn from(s: StringRef<'_>) -> StringOwned { - Self { - inner: String::from(s.inner), - length: s.length, - } - } - } + impl ToOwned for StringRef { + type Owned = StringOwned; - impl OwnedToRef for StringOwned { - type Borrowed<'a> = StringRef<'a>; - fn owned_to_ref(&self) -> Self::Borrowed<'_> { - StringRef { - length: self.length, - inner: self.inner.as_ref(), + fn to_owned(&self) -> StringOwned { + StringOwned { + inner: self.as_str().into(), + length: self.len(), } } } - - impl<'a> RefToOwned<'a> for StringRef<'a> { - type Owned = StringOwned; - fn ref_to_owned(&self) -> Self::Owned { - StringOwned::from(*self) - } - } } diff --git a/spki/src/algorithm.rs b/spki/src/algorithm.rs index a9bd330bb..94241fb54 100644 --- a/spki/src/algorithm.rs +++ b/spki/src/algorithm.rs @@ -161,10 +161,13 @@ impl<'a> AlgorithmIdentifierRef<'a> { self.oid, match self.parameters { None => None, - Some(p) => match p { - AnyRef::NULL => None, - _ => Some(p.decode_as::()?), - }, + Some(p) => { + if p.is_null() { + None + } else { + Some(p.decode_as::()?) + } + } }, )) }