diff options
author | Sam Saccone <samccone@google.com> | 2023-12-19 01:58:56 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2023-12-19 01:58:56 +0000 |
commit | f34a20e7b5b2cc181787960ffb2ace2ba9976420 (patch) | |
tree | 57e854596b4206fdfbbd03ea66612c1fece3d7ce | |
parent | 4a532ebdb7ff77c7a8fb2e09acb0f42753cb4ba5 (diff) | |
parent | 07fe575307fc1516488c98925599ef4be401ed32 (diff) | |
download | der-f34a20e7b5b2cc181787960ffb2ace2ba9976420.tar.gz |
Revert "Upgrade der to 0.7.8" am: 07fe575307
Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/der/+/2881631
Change-Id: I5f6950777a0e92080c7687b42c7ed110771099f2
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
61 files changed, 1401 insertions, 3278 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json index 2f03863..db1ff23 100644 --- a/.cargo_vcs_info.json +++ b/.cargo_vcs_info.json @@ -1,6 +1,6 @@ { "git": { - "sha1": "bb80aa06b3566b0828185e0349e11295ba1aa2f9" + "sha1": "14164f9dccd1ba4ceed9938f792b4e99874ef8e0" }, "path_in_vcs": "der" }
\ No newline at end of file @@ -35,11 +35,13 @@ rust_library_host { name: "libder", crate_name: "der", cargo_env_compat: true, - cargo_pkg_version: "0.7.8", + cargo_pkg_version: "0.6.1", srcs: ["src/lib.rs"], edition: "2021", features: [ "alloc", + "const-oid", + "der_derive", "derive", "flagset", "oid", @@ -57,11 +59,13 @@ rust_library_rlib { name: "libder_nostd", crate_name: "der", cargo_env_compat: true, - cargo_pkg_version: "0.7.8", + cargo_pkg_version: "0.6.1", srcs: ["src/lib.rs"], edition: "2021", features: [ "alloc", + "const-oid", + "der_derive", "derive", "flagset", "oid", diff --git a/CHANGELOG.md b/CHANGELOG.md index 0285152..0472850 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,124 +4,6 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## 0.7.8 (2023-08-07) -### Added -- `bytes` feature ([#1156]) -- impl `RefToOwned`/`OwnedToRef` for `&[u8]`/`Box<[u8]>` ([#1188]) -- `BmpString` ([#1164]) - -### Changed -- no-panic cleanup ([#1169]) -- Bump `der_derive` dependency to v0.7.2 ([#1192]) - -[#1156]: https://github.com/RustCrypto/formats/pull/1156 -[#1164]: https://github.com/RustCrypto/formats/pull/1164 -[#1169]: https://github.com/RustCrypto/formats/pull/1169 -[#1188]: https://github.com/RustCrypto/formats/pull/1188 -[#1192]: https://github.com/RustCrypto/formats/pull/1192 - -## 0.7.7 (2023-06-29) -### Added -- `TryFrom<String>` impl for strings based on `StrOwned` ([#1064]) - -[#1064]: https://github.com/RustCrypto/formats/pull/1064 - -## 0.7.6 (2023-05-16) -### Added -- `SetOfVec::{extend, from_iter}` methods ([#1065]) -- `SetOf(Vec)::{insert, insert_ordered}` methods ([#1067]) - -### Changed -- Deprecate `SetOf(Vec)::add` ([#1067]) - -### Fixed -- Off-by-one error in `BMPString` tag ([#1037]) -- Handling of non-unique items in `SetOf`(Vec) ([#1066]) - -[#1037]: https://github.com/RustCrypto/formats/pull/1037 -[#1065]: https://github.com/RustCrypto/formats/pull/1065 -[#1066]: https://github.com/RustCrypto/formats/pull/1066 -[#1067]: https://github.com/RustCrypto/formats/pull/1067 - -## 0.7.5 (2023-04-24) -### Added -- adds support for `DateTime::INFINITY` ([#1026]) - -[#1026]: https://github.com/RustCrypto/formats/pull/1026 - -## 0.7.4 (2023-04-19) -### Added -- `Decode` and `Encode` impls for `PhantomData` ([#1009]) -- `ValueOrd` and `DerOrd` impls for `PhantomData` ([#1012]) - -### Changed -- Bump `hex-literal` dependency to v0.4.1 ([#999]) -- Bump `der_derive` dependency to v0.7.1 ([#1016]) - -[#1009]: https://github.com/RustCrypto/formats/pull/1009 -[#1012]: https://github.com/RustCrypto/formats/pull/1012 -[#1016]: https://github.com/RustCrypto/formats/pull/1016 - -## 0.7.3 (2023-04-06) -### Added -- `UtcTime::MAX_YEAR` associated constant ([#989]) - -[#989]: https://github.com/RustCrypto/formats/pull/989 - -## 0.7.2 (2023-04-04) -### Added -- Expose `NestedReader ([#925]) -- `From<ObjectIdentifier>` impl for `Any` ([#965]) -- `Any::null` helper ([#969]) -- `Any::encode_from` ([#976]) - -[#925]: https://github.com/RustCrypto/formats/pull/925 -[#965]: https://github.com/RustCrypto/formats/pull/965 -[#969]: https://github.com/RustCrypto/formats/pull/969 -[#976]: https://github.com/RustCrypto/formats/pull/976 - -## 0.7.1 (2023-03-07) -### Changed -- Make `zeroize`'s `alloc` feature conditional ([#920]) - -[#920]: https://github.com/RustCrypto/formats/pull/920 - -## 0.7.0 (2023-02-26) [YANKED] -### Added -- `OwnedtoRef`/`RefToOwned` traits; MSRV 1.65 ([#797]) -- `OctetStringRef::decode_into` ([#817]) -- `Int` and `IntRef` types ([#823]) -- `IndefiniteLength` type ([#830]) -- `Any::value` accessor ([#833]) -- Buffered PEM reader ([#839]) -- `OctetString::into_bytes` ([#845]) -- Blanket impls on `Box<T>` for `DecodeValue`, `EncodeValue`, and `Sequence` ([#860]) - -### Changed -- Rename `UIntRef` => `UintRef` ([#786]) -- Replace use of `dyn Writer` with `impl Writer` ([#828]) -- Rename `AnyRef::decode_into` -> `::decode_as` ([#829]) -- Bump `pem-rfc7468` dependency to v0.7 ([#894]) -- Rename `Encode::to_vec` => `::to_der` ([#898]) - -### Removed -- `Sequence::fields` method ([#828]) -- Inherent `AnyRef` decoding methods ([#829]) - -[#786]: https://github.com/RustCrypto/formats/pull/786 -[#797]: https://github.com/RustCrypto/formats/pull/797 -[#817]: https://github.com/RustCrypto/formats/pull/817 -[#823]: https://github.com/RustCrypto/formats/pull/823 -[#828]: https://github.com/RustCrypto/formats/pull/828 -[#829]: https://github.com/RustCrypto/formats/pull/829 -[#830]: https://github.com/RustCrypto/formats/pull/830 -[#833]: https://github.com/RustCrypto/formats/pull/833 -[#839]: https://github.com/RustCrypto/formats/pull/839 -[#845]: https://github.com/RustCrypto/formats/pull/845 -[#860]: https://github.com/RustCrypto/formats/pull/860 -[#894]: https://github.com/RustCrypto/formats/pull/894 -[#898]: https://github.com/RustCrypto/formats/pull/898 - ## 0.6.1 (2022-12-05) ### Added - Rudimentary implementation of `TeletexString` and `VideotexString` ([#691]) @@ -11,9 +11,9 @@ [package] edition = "2021" -rust-version = "1.65" +rust-version = "1.57" name = "der" -version = "0.7.8" +version = "0.6.1" authors = ["RustCrypto Developers"] description = """ Pure Rust embedded-friendly implementation of the Distinguished Encoding Rules @@ -44,22 +44,12 @@ rustdoc-args = [ "docsrs", ] -[dependencies.arbitrary] -version = "1.3" -features = ["derive"] -optional = true - -[dependencies.bytes] -version = "1" -optional = true -default-features = false - [dependencies.const-oid] -version = "0.9.2" +version = "0.9" optional = true [dependencies.der_derive] -version = "0.7.2" +version = "0.6" optional = true [dependencies.flagset] @@ -67,8 +57,7 @@ version = "0.4.3" optional = true [dependencies.pem-rfc7468] -version = "0.7" -features = ["alloc"] +version = "0.6" optional = true [dependencies.time] @@ -78,31 +67,23 @@ default-features = false [dependencies.zeroize] version = "1.5" +features = ["alloc"] optional = true default-features = false [dev-dependencies.hex-literal] -version = "0.4.1" +version = "0.3.3" [dev-dependencies.proptest] version = "1" [features] -alloc = ["zeroize?/alloc"] -arbitrary = [ - "dep:arbitrary", - "const-oid?/arbitrary", - "std", -] -bytes = [ - "dep:bytes", - "alloc", -] -derive = ["dep:der_derive"] -oid = ["dep:const-oid"] +alloc = [] +derive = ["der_derive"] +oid = ["const-oid"] pem = [ - "dep:pem-rfc7468", "alloc", + "pem-rfc7468/alloc", "zeroize", ] real = [] diff --git a/Cargo.toml.orig b/Cargo.toml.orig index 49011a0..f8c7dfe 100644 --- a/Cargo.toml.orig +++ b/Cargo.toml.orig @@ -1,6 +1,6 @@ [package] name = "der" -version = "0.7.8" +version = "0.6.1" description = """ Pure Rust embedded-friendly implementation of the Distinguished Encoding Rules (DER) for Abstract Syntax Notation One (ASN.1) as described in ITU X.690 with @@ -13,32 +13,27 @@ categories = ["cryptography", "data-structures", "encoding", "no-std", "parser-i keywords = ["asn1", "crypto", "itu", "pkcs"] readme = "README.md" edition = "2021" -rust-version = "1.65" +rust-version = "1.57" [dependencies] -arbitrary = { version = "1.3", features = ["derive"], optional = true } -bytes = { version = "1", optional = true, default-features = false } -const-oid = { version = "0.9.2", optional = true } -der_derive = { version = "0.7.2", optional = true } +const-oid = { version = "0.9", optional = true, path = "../const-oid" } +der_derive = { version = "0.6", optional = true, path = "derive" } flagset = { version = "0.4.3", optional = true } -pem-rfc7468 = { version = "0.7", optional = true, features = ["alloc"] } +pem-rfc7468 = { version = "0.6", optional = true, path = "../pem-rfc7468" } time = { version = "0.3.4", optional = true, default-features = false } -zeroize = { version = "1.5", optional = true, default-features = false } +zeroize = { version = "1.5", optional = true, default-features = false, features = ["alloc"] } [dev-dependencies] -hex-literal = "0.4.1" +hex-literal = "0.3.3" proptest = "1" [features] -alloc = ["zeroize?/alloc"] -std = ["alloc"] - -arbitrary = ["dep:arbitrary", "const-oid?/arbitrary", "std"] -bytes = ["dep:bytes", "alloc"] -derive = ["dep:der_derive"] -oid = ["dep:const-oid"] -pem = ["dep:pem-rfc7468", "alloc", "zeroize"] +alloc = [] +derive = ["der_derive"] +oid = ["const-oid"] +pem = ["alloc", "pem-rfc7468/alloc", "zeroize"] real = [] +std = ["alloc"] [package.metadata.docs.rs] all-features = true diff --git a/LICENSE-MIT b/LICENSE-MIT index e0d0827..1b78809 100644 --- a/LICENSE-MIT +++ b/LICENSE-MIT @@ -1,4 +1,4 @@ -Copyright (c) 2020-2023 The RustCrypto Project Developers +Copyright (c) 2020-2022 The RustCrypto Project Developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated @@ -1,20 +1,23 @@ # This project was upgraded with external_updater. # Usage: tools/external_updater/updater.sh update rust/crates/der -# For more info, check https://cs.android.com/android/platform/superproject/+/main:tools/external_updater/README.md +# For more info, check https://cs.android.com/android/platform/superproject/+/master:tools/external_updater/README.md name: "der" description: "Pure Rust embedded-friendly implementation of the Distinguished Encoding Rules (DER) for Abstract Syntax Notation One (ASN.1) as described in ITU X.690." third_party { + url { + type: HOMEPAGE + value: "https://crates.io/crates/der" + } + url { + type: ARCHIVE + value: "https://static.crates.io/crates/der/der-0.6.1.crate" + } + version: "0.6.1" license_type: NOTICE last_upgrade_date { - year: 2023 + year: 2022 month: 12 - day: 15 - } - homepage: "https://crates.io/crates/der" - identifier { - type: "Archive" - value: "https://static.crates.io/crates/der/der-0.7.8.crate" - version: "0.7.8" + day: 9 } } @@ -49,7 +49,7 @@ encountered. There is currently no way to disable these checks. ## Minimum Supported Rust Version -This crate requires **Rust 1.65** at a minimum. +This crate requires **Rust 1.57** at a minimum. We may change the MSRV in the future, but it will be accompanied by a minor version bump. @@ -78,7 +78,7 @@ dual licensed as above, without any additional terms or conditions. [build-image]: https://github.com/RustCrypto/formats/actions/workflows/der.yml/badge.svg [build-link]: https://github.com/RustCrypto/formats/actions/workflows/der.yml [license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg -[rustc-image]: https://img.shields.io/badge/rustc-1.65+-blue.svg +[rustc-image]: https://img.shields.io/badge/rustc-1.57+-blue.svg [chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg [chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/300570-formats diff --git a/src/arrayvec.rs b/src/arrayvec.rs index 6ce608d..21f1341 100644 --- a/src/arrayvec.rs +++ b/src/arrayvec.rs @@ -23,11 +23,14 @@ impl<T, const N: usize> ArrayVec<T, N> { } } - /// Push an item into this [`ArrayVec`]. - pub fn push(&mut self, item: T) -> Result<()> { + /// Add an element to this [`ArrayVec`]. + /// + /// Items MUST be added in lexicographical order according to the `Ord` + /// impl on `T`. + pub fn add(&mut self, element: T) -> Result<()> { match self.length.checked_add(1) { Some(n) if n <= N => { - self.elements[self.length] = Some(item); + self.elements[self.length] = Some(element); self.length = n; Ok(()) } @@ -135,11 +138,11 @@ mod tests { #[test] fn add() { let mut vec = ArrayVec::<u8, 3>::new(); - vec.push(1).unwrap(); - vec.push(2).unwrap(); - vec.push(3).unwrap(); + vec.add(1).unwrap(); + vec.add(2).unwrap(); + vec.add(3).unwrap(); - assert_eq!(vec.push(4).err().unwrap(), ErrorKind::Overlength.into()); + assert_eq!(vec.add(4).err().unwrap(), ErrorKind::Overlength.into()); assert_eq!(vec.len(), 3); } } diff --git a/src/asn1.rs b/src/asn1.rs index b04b1b5..34d692b 100644 --- a/src/asn1.rs +++ b/src/asn1.rs @@ -1,13 +1,8 @@ //! Module containing all of the various ASN.1 built-in types supported by //! this library. -#[macro_use] -mod internal_macros; - mod any; mod bit_string; -#[cfg(feature = "alloc")] -mod bmp_string; mod boolean; mod choice; mod context_specific; @@ -37,7 +32,7 @@ pub use self::{ context_specific::{ContextSpecific, ContextSpecificRef}, generalized_time::GeneralizedTime, ia5_string::Ia5StringRef, - integer::{int::IntRef, uint::UintRef}, + integer::bigint::UIntRef, null::Null, octet_string::OctetStringRef, printable_string::PrintableStringRef, @@ -51,17 +46,9 @@ pub use self::{ }; #[cfg(feature = "alloc")] -pub use self::{ - any::Any, - bit_string::BitString, - bmp_string::BmpString, - ia5_string::Ia5String, - integer::{int::Int, uint::Uint}, - octet_string::OctetString, - printable_string::PrintableString, - set_of::SetOfVec, - teletex_string::TeletexString, -}; +#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] +pub use self::{any::Any, bit_string::BitString, octet_string::OctetString, set_of::SetOfVec}; #[cfg(feature = "oid")] +#[cfg_attr(docsrs, doc(cfg(feature = "oid")))] pub use const_oid::ObjectIdentifier; diff --git a/src/asn1/any.rs b/src/asn1/any.rs index 017a909..cb65f23 100644 --- a/src/asn1/any.rs +++ b/src/asn1/any.rs @@ -1,15 +1,16 @@ //! ASN.1 `ANY` type. -#![cfg_attr(feature = "arbitrary", allow(clippy::integer_arithmetic))] - use crate::{ - BytesRef, Choice, Decode, DecodeValue, DerOrd, EncodeValue, Error, ErrorKind, Header, Length, - Reader, Result, SliceReader, Tag, Tagged, ValueOrd, Writer, + asn1::*, ByteSlice, Choice, Decode, DecodeValue, DerOrd, EncodeValue, Error, ErrorKind, + FixedTag, Header, Length, Reader, Result, SliceReader, Tag, Tagged, ValueOrd, Writer, }; use core::cmp::Ordering; #[cfg(feature = "alloc")] -use crate::SliceWriter; +use alloc::vec::Vec; + +#[cfg(feature = "oid")] +use crate::asn1::ObjectIdentifier; /// ASN.1 `ANY`: represents any explicitly tagged ASN.1 value. /// @@ -22,31 +23,30 @@ use crate::SliceWriter; /// Nevertheless, this crate defines an `ANY` type as it remains a familiar /// and useful concept which is still extensively used in things like /// PKI-related RFCs. -#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub struct AnyRef<'a> { /// Tag representing the type of the encoded value. tag: Tag, /// Inner value encoded as bytes. - value: BytesRef<'a>, + value: ByteSlice<'a>, } impl<'a> AnyRef<'a> { /// [`AnyRef`] representation of the ASN.1 `NULL` type. pub const NULL: Self = Self { tag: Tag::Null, - value: BytesRef::EMPTY, + value: ByteSlice::EMPTY, }; /// Create a new [`AnyRef`] from the provided [`Tag`] and DER bytes. pub fn new(tag: Tag, bytes: &'a [u8]) -> Result<Self> { - let value = BytesRef::new(bytes).map_err(|_| ErrorKind::Length { tag })?; + let value = ByteSlice::new(bytes).map_err(|_| ErrorKind::Length { tag })?; Ok(Self { tag, value }) } - /// Infallible creation of an [`AnyRef`] from a [`BytesRef`]. - pub(crate) fn from_tag_and_value(tag: Tag, value: BytesRef<'a>) -> Self { + /// Infallible creation of an [`AnyRef`] from a [`ByteSlice`]. + pub(crate) fn from_tag_and_value(tag: Tag, value: ByteSlice<'a>) -> Self { Self { tag, value } } @@ -56,14 +56,11 @@ impl<'a> AnyRef<'a> { } /// Attempt to decode this [`AnyRef`] type into the inner value. - pub fn decode_as<T>(self) -> Result<T> + pub fn decode_into<T>(self) -> Result<T> where - T: Choice<'a> + DecodeValue<'a>, + T: DecodeValue<'a> + FixedTag, { - if !T::can_decode(self.tag) { - return Err(self.tag.unexpected_error(None)); - } - + self.tag.assert_eq(T::TAG)?; let header = Header { tag: self.tag, length: self.value.len(), @@ -79,6 +76,68 @@ impl<'a> AnyRef<'a> { self == Self::NULL } + /// Attempt to decode an ASN.1 `BIT STRING`. + pub fn bit_string(self) -> Result<BitStringRef<'a>> { + self.try_into() + } + + /// Attempt to decode an ASN.1 `CONTEXT-SPECIFIC` field. + pub fn context_specific<T>(self) -> Result<ContextSpecific<T>> + where + T: Decode<'a>, + { + self.try_into() + } + + /// Attempt to decode an ASN.1 `GeneralizedTime`. + pub fn generalized_time(self) -> Result<GeneralizedTime> { + self.try_into() + } + + /// Attempt to decode an ASN.1 `IA5String`. + pub fn ia5_string(self) -> Result<Ia5StringRef<'a>> { + self.try_into() + } + + /// Attempt to decode an ASN.1 `OCTET STRING`. + pub fn octet_string(self) -> Result<OctetStringRef<'a>> { + self.try_into() + } + + /// Attempt to decode an ASN.1 `OBJECT IDENTIFIER`. + #[cfg(feature = "oid")] + #[cfg_attr(docsrs, doc(cfg(feature = "oid")))] + pub fn oid(self) -> Result<ObjectIdentifier> { + self.try_into() + } + + /// Attempt to decode an ASN.1 `OPTIONAL` value. + pub fn optional<T>(self) -> Result<Option<T>> + where + T: Choice<'a> + TryFrom<Self, Error = Error>, + { + if T::can_decode(self.tag) { + T::try_from(self).map(Some) + } else { + Ok(None) + } + } + + /// Attempt to decode an ASN.1 `PrintableString`. + pub fn printable_string(self) -> Result<PrintableStringRef<'a>> { + self.try_into() + } + + /// Attempt to decode an ASN.1 `TeletexString`. + pub fn teletex_string(self) -> Result<TeletexStringRef<'a>> { + self.try_into() + } + + /// Attempt to decode an ASN.1 `VideotexString`. + pub fn videotex_string(self) -> Result<VideotexStringRef<'a>> { + 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<F, T>(self, f: F) -> Result<T> @@ -90,6 +149,16 @@ impl<'a> AnyRef<'a> { let result = f(&mut reader)?; reader.finish(result) } + + /// Attempt to decode an ASN.1 `UTCTime`. + pub fn utc_time(self) -> Result<UtcTime> { + self.try_into() + } + + /// Attempt to decode an ASN.1 `UTF8String`. + pub fn utf8_string(self) -> Result<Utf8StringRef<'a>> { + self.try_into() + } } impl<'a> Choice<'a> for AnyRef<'a> { @@ -101,15 +170,10 @@ impl<'a> Choice<'a> for AnyRef<'a> { impl<'a> Decode<'a> for AnyRef<'a> { fn decode<R: Reader<'a>>(reader: &mut R) -> Result<AnyRef<'a>> { let header = Header::decode(reader)?; - Self::decode_value(reader, header) - } -} -impl<'a> DecodeValue<'a> for AnyRef<'a> { - fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> { Ok(Self { tag: header.tag, - value: BytesRef::decode_value(reader, header)?, + value: ByteSlice::decode_value(reader, header)?, }) } } @@ -119,7 +183,7 @@ impl EncodeValue for AnyRef<'_> { Ok(self.value.len()) } - fn encode_value(&self, writer: &mut impl Writer) -> Result<()> { + fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> { writer.write(self.value()) } } @@ -136,8 +200,8 @@ impl ValueOrd for AnyRef<'_> { } } -impl<'a> From<AnyRef<'a>> for BytesRef<'a> { - fn from(any: AnyRef<'a>) -> BytesRef<'a> { +impl<'a> From<AnyRef<'a>> for ByteSlice<'a> { + fn from(any: AnyRef<'a>) -> ByteSlice<'a> { any.value } } @@ -150,166 +214,71 @@ impl<'a> TryFrom<&'a [u8]> for AnyRef<'a> { } } +/// ASN.1 `ANY`: represents any explicitly tagged ASN.1 value. +/// +/// This type provides the same functionality as [`AnyRef`] but owns the +/// backing data. #[cfg(feature = "alloc")] -pub use self::allocating::Any; - -#[cfg(feature = "alloc")] -mod allocating { - use super::*; - use crate::{referenced::*, BytesOwned}; - use alloc::boxed::Box; - - /// ASN.1 `ANY`: represents any explicitly tagged ASN.1 value. - /// - /// This type provides the same functionality as [`AnyRef`] but owns the - /// backing data. - #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] - #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] - pub struct Any { - /// Tag representing the type of the encoded value. - tag: Tag, - - /// Inner value encoded as bytes. - value: BytesOwned, - } - - impl Any { - /// Create a new [`Any`] from the provided [`Tag`] and DER bytes. - pub fn new(tag: Tag, bytes: impl Into<Box<[u8]>>) -> Result<Self> { - let value = BytesOwned::new(bytes)?; - - // Ensure the tag and value are a valid `AnyRef`. - AnyRef::new(tag, value.as_slice())?; - Ok(Self { tag, value }) - } - - /// Allow access to value - pub fn value(&self) -> &[u8] { - self.value.as_slice() - } - - /// Attempt to decode this [`Any`] type into the inner value. - pub fn decode_as<'a, T>(&'a self) -> Result<T> - where - T: Choice<'a> + DecodeValue<'a>, - { - AnyRef::from(self).decode_as() - } - - /// Encode the provided type as an [`Any`] value. - pub fn encode_from<T>(msg: &T) -> Result<Self> - where - T: Tagged + EncodeValue, - { - let encoded_len = usize::try_from(msg.value_len()?)?; - let mut buf = vec![0u8; encoded_len]; - let mut writer = SliceWriter::new(&mut buf); - msg.encode_value(&mut writer)?; - writer.finish()?; - Any::new(msg.tag(), buf) - } - - /// Attempt to decode this value an ASN.1 `SEQUENCE`, creating a new - /// nested reader and calling the provided argument with it. - pub fn sequence<'a, F, T>(&'a self, f: F) -> Result<T> - where - F: FnOnce(&mut SliceReader<'a>) -> Result<T>, - { - AnyRef::from(self).sequence(f) - } - - /// [`Any`] representation of the ASN.1 `NULL` type. - pub fn null() -> Self { - Self { - tag: Tag::Null, - value: BytesOwned::default(), - } - } - } - - impl Choice<'_> for Any { - fn can_decode(_: Tag) -> bool { - true - } - } - - impl<'a> Decode<'a> for Any { - fn decode<R: Reader<'a>>(reader: &mut R) -> Result<Self> { - let header = Header::decode(reader)?; - Self::decode_value(reader, header) - } - } - - impl<'a> DecodeValue<'a> for Any { - fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> { - let value = reader.read_vec(header.length)?; - Self::new(header.tag, value) - } - } +#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub struct Any { + /// Tag representing the type of the encoded value. + tag: Tag, - impl EncodeValue for Any { - fn value_len(&self) -> Result<Length> { - Ok(self.value.len()) - } + /// Inner value encoded as bytes. + value: Vec<u8>, +} - fn encode_value(&self, writer: &mut impl Writer) -> Result<()> { - writer.write(self.value.as_slice()) - } - } +#[cfg(feature = "alloc")] +impl Any { + /// Create a new [`Any`] from the provided [`Tag`] and DER bytes. + pub fn new(tag: Tag, bytes: impl Into<Vec<u8>>) -> Result<Self> { + let value = bytes.into(); - 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.as_slice()).expect("invalid ANY") - } + // Ensure the tag and value are a valid `AnyRef`. + AnyRef::new(tag, &value)?; + Ok(Self { tag, value }) } +} - impl Tagged for Any { - fn tag(&self) -> Tag { - self.tag - } +#[cfg(feature = "alloc")] +impl Choice<'_> for Any { + fn can_decode(_: Tag) -> bool { + true } +} - impl ValueOrd for Any { - fn value_cmp(&self, other: &Self) -> Result<Ordering> { - self.value.der_cmp(&other.value) - } +#[cfg(feature = "alloc")] +impl<'a> Decode<'a> for Any { + fn decode<R: Reader<'a>>(reader: &mut R) -> Result<Self> { + let header = Header::decode(reader)?; + let value = reader.read_vec(header.length)?; + Self::new(header.tag, value) } +} - impl<'a, T> From<T> for Any - where - T: Into<AnyRef<'a>>, - { - fn from(input: T) -> Any { - let anyref: AnyRef<'a> = input.into(); - Self { - tag: anyref.tag(), - value: BytesOwned::from(anyref.value), - } - } +#[cfg(feature = "alloc")] +impl EncodeValue for Any { + fn value_len(&self) -> Result<Length> { + self.value.len().try_into() } - impl<'a> RefToOwned<'a> for AnyRef<'a> { - type Owned = Any; - fn ref_to_owned(&self) -> Self::Owned { - Any { - tag: self.tag(), - value: BytesOwned::from(self.value), - } - } + fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> { + writer.write(&self.value) } +} - impl OwnedToRef for Any { - type Borrowed<'a> = AnyRef<'a>; - fn owned_to_ref(&self) -> Self::Borrowed<'_> { - self.into() - } +#[cfg(feature = "alloc")] +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") } +} - impl Any { - /// Is this value an ASN.1 `NULL` value? - pub fn is_null(&self) -> bool { - self.owned_to_ref() == AnyRef::NULL - } +#[cfg(feature = "alloc")] +impl Tagged for Any { + fn tag(&self) -> Tag { + self.tag } } diff --git a/src/asn1/bit_string.rs b/src/asn1/bit_string.rs index bf3371c..eed14e4 100644 --- a/src/asn1/bit_string.rs +++ b/src/asn1/bit_string.rs @@ -1,11 +1,14 @@ //! ASN.1 `BIT STRING` support. use crate::{ - BytesRef, DecodeValue, DerOrd, EncodeValue, Error, ErrorKind, FixedTag, Header, Length, Reader, - Result, Tag, ValueOrd, Writer, + asn1::AnyRef, ByteSlice, DecodeValue, DerOrd, EncodeValue, Error, ErrorKind, FixedTag, Header, + Length, Reader, Result, Tag, ValueOrd, Writer, }; use core::{cmp::Ordering, iter::FusedIterator}; +#[cfg(feature = "alloc")] +use alloc::vec::Vec; + /// ASN.1 `BIT STRING` type. /// /// This type contains a sequence of any number of bits, modeled internally as @@ -21,7 +24,7 @@ pub struct BitStringRef<'a> { bit_length: usize, /// Bitstring represented as a slice of bytes. - inner: BytesRef<'a>, + inner: ByteSlice<'a>, } impl<'a> BitStringRef<'a> { @@ -37,7 +40,7 @@ impl<'a> BitStringRef<'a> { return Err(Self::TAG.value_error()); } - let inner = BytesRef::new(bytes).map_err(|_| Self::TAG.length_error())?; + let inner = ByteSlice::new(bytes).map_err(|_| Self::TAG.length_error())?; let bit_length = usize::try_from(inner.len())? .checked_mul(8) @@ -117,8 +120,6 @@ impl<'a> BitStringRef<'a> { } } -impl_any_conversions!(BitStringRef<'a>, 'a); - impl<'a> DecodeValue<'a> for BitStringRef<'a> { fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> { let header = Header { @@ -127,7 +128,7 @@ impl<'a> DecodeValue<'a> for BitStringRef<'a> { }; let unused_bits = reader.read_byte()?; - let inner = BytesRef::decode_value(reader, header)?; + let inner = ByteSlice::decode_value(reader, header)?; Self::new(unused_bits, inner.as_slice()) } } @@ -137,7 +138,7 @@ impl EncodeValue for BitStringRef<'_> { self.byte_len() + Length::ONE } - fn encode_value(&self, writer: &mut impl Writer) -> Result<()> { + fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> { writer.write_byte(self.unused_bits)?; writer.write(self.raw_bytes()) } @@ -158,6 +159,14 @@ impl<'a> From<&BitStringRef<'a>> for BitStringRef<'a> { } } +impl<'a> TryFrom<AnyRef<'a>> for BitStringRef<'a> { + type Error = Error; + + fn try_from(any: AnyRef<'a>) -> Result<BitStringRef<'a>> { + any.decode_into() + } +} + impl<'a> TryFrom<&'a [u8]> for BitStringRef<'a> { type Error = Error; @@ -171,7 +180,7 @@ impl<'a> TryFrom<&&'a [u8]> for BitStringRef<'a> { type Error = Error; fn try_from(bytes: &&'a [u8]) -> Result<BitStringRef<'a>> { - BitStringRef::from_bytes(bytes) + BitStringRef::from_bytes(*bytes) } } @@ -189,198 +198,141 @@ impl<'a> FixedTag for BitStringRef<'a> { const TAG: Tag = Tag::BitString; } -// Implement by hand because the derive would create invalid values. -// Use the constructor to create a valid value. -#[cfg(feature = "arbitrary")] -impl<'a> arbitrary::Arbitrary<'a> for BitStringRef<'a> { - fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> { - Self::new( - u.int_in_range(0..=Self::MAX_UNUSED_BITS)?, - BytesRef::arbitrary(u)?.as_slice(), - ) - .map_err(|_| arbitrary::Error::IncorrectFormat) - } +/// Owned form of ASN.1 `BIT STRING` type. +/// +/// This type provides the same functionality as [`BitStringRef`] but owns the +/// backing data. +#[cfg(feature = "alloc")] +#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub struct BitString { + /// Number of unused bits in the final octet. + unused_bits: u8, - fn size_hint(depth: usize) -> (usize, Option<usize>) { - arbitrary::size_hint::and(u8::size_hint(depth), BytesRef::size_hint(depth)) - } -} + /// Length of this `BIT STRING` in bits. + bit_length: usize, -#[cfg(feature = "alloc")] -pub use self::allocating::BitString; + /// Bitstring represented as a slice of bytes. + inner: Vec<u8>, +} #[cfg(feature = "alloc")] -mod allocating { - use super::*; - use crate::referenced::*; - use alloc::vec::Vec; +impl BitString { + /// Maximum number of unused bits allowed. + pub const MAX_UNUSED_BITS: u8 = 7; - /// Owned form of ASN.1 `BIT STRING` type. + /// Create a new ASN.1 `BIT STRING` from a byte slice. /// - /// This type provides the same functionality as [`BitStringRef`] but owns the - /// backing data. - #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] - pub struct BitString { - /// Number of unused bits in the final octet. - unused_bits: u8, - - /// Length of this `BIT STRING` in bits. - bit_length: usize, - - /// Bitstring represented as a slice of bytes. - inner: Vec<u8>, - } - - impl BitString { - /// Maximum number of unused bits allowed. - pub const MAX_UNUSED_BITS: u8 = 7; - - /// Create a new ASN.1 `BIT STRING` from a byte slice. - /// - /// Accepts an optional number of "unused bits" (0-7) which are omitted - /// from the final octet. This number is 0 if the value is octet-aligned. - pub fn new(unused_bits: u8, bytes: impl Into<Vec<u8>>) -> Result<Self> { - let inner = bytes.into(); - - // Ensure parameters parse successfully as a `BitStringRef`. - let bit_length = BitStringRef::new(unused_bits, &inner)?.bit_length; - - Ok(BitString { - unused_bits, - bit_length, - inner, - }) - } - - /// Create a new ASN.1 `BIT STRING` from the given bytes. - /// - /// The "unused bits" are set to 0. - pub fn from_bytes(bytes: &[u8]) -> Result<Self> { - Self::new(0, bytes) - } - - /// Get the number of unused bits in the octet serialization of this - /// `BIT STRING`. - pub fn unused_bits(&self) -> u8 { - self.unused_bits - } - - /// Is the number of unused bits a value other than 0? - pub fn has_unused_bits(&self) -> bool { - self.unused_bits != 0 - } - - /// Get the length of this `BIT STRING` in bits. - pub fn bit_len(&self) -> usize { - self.bit_length - } + /// Accepts an optional number of "unused bits" (0-7) which are omitted + /// from the final octet. This number is 0 if the value is octet-aligned. + pub fn new(unused_bits: u8, bytes: impl Into<Vec<u8>>) -> Result<Self> { + let inner = bytes.into(); - /// Is the inner byte slice empty? - pub fn is_empty(&self) -> bool { - self.inner.is_empty() - } + // Ensure parameters parse successfully as a `BitStringRef`. + let bit_length = BitStringRef::new(unused_bits, &inner)?.bit_length; - /// Borrow the inner byte slice. - /// - /// Returns `None` if the number of unused bits is *not* equal to zero, - /// i.e. if the `BIT STRING` is not octet aligned. - /// - /// Use [`BitString::raw_bytes`] to obtain access to the raw value - /// regardless of the presence of unused bits. - pub fn as_bytes(&self) -> Option<&[u8]> { - if self.has_unused_bits() { - None - } else { - Some(self.raw_bytes()) - } - } + Ok(BitString { + unused_bits, + bit_length, + inner, + }) + } - /// Borrow the raw bytes of this `BIT STRING`. - pub fn raw_bytes(&self) -> &[u8] { - self.inner.as_slice() - } + /// Create a new ASN.1 `BIT STRING` from the given bytes. + /// + /// The "unused bits" are set to 0. + pub fn from_bytes(bytes: &[u8]) -> Result<Self> { + Self::new(0, bytes) + } - /// Iterator over the bits of this `BIT STRING`. - pub fn bits(&self) -> BitStringIter<'_> { - BitStringRef::from(self).bits() - } + /// Get the number of unused bits in the octet serialization of this + /// `BIT STRING`. + pub fn unused_bits(&self) -> u8 { + self.unused_bits } - impl_any_conversions!(BitString); + /// Is the number of unused bits a value other than 0? + pub fn has_unused_bits(&self) -> bool { + self.unused_bits != 0 + } - impl<'a> DecodeValue<'a> for BitString { - fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> { - let inner_len = (header.length - Length::ONE)?; - let unused_bits = reader.read_byte()?; - let inner = reader.read_vec(inner_len)?; - Self::new(unused_bits, inner) - } + /// Get the length of this `BIT STRING` in bits. + pub fn bit_len(&self) -> usize { + self.bit_length } - impl EncodeValue for BitString { - fn value_len(&self) -> Result<Length> { - Length::ONE + Length::try_from(self.inner.len())? - } + /// Is the inner byte slice empty? + pub fn is_empty(&self) -> bool { + self.inner.is_empty() + } - fn encode_value(&self, writer: &mut impl Writer) -> Result<()> { - writer.write_byte(self.unused_bits)?; - writer.write(&self.inner) + /// Borrow the inner byte slice. + /// + /// Returns `None` if the number of unused bits is *not* equal to zero, + /// i.e. if the `BIT STRING` is not octet aligned. + /// + /// Use [`BitString::raw_bytes`] to obtain access to the raw value + /// regardless of the presence of unused bits. + pub fn as_bytes(&self) -> Option<&[u8]> { + if self.has_unused_bits() { + None + } else { + Some(self.raw_bytes()) } } - impl FixedTag for BitString { - const TAG: Tag = Tag::BitString; + /// Borrow the raw bytes of this `BIT STRING`. + pub fn raw_bytes(&self) -> &[u8] { + self.inner.as_slice() } - impl<'a> From<&'a BitString> for BitStringRef<'a> { - fn from(bit_string: &'a BitString) -> BitStringRef<'a> { - // Ensured to parse successfully in constructor - BitStringRef::new(bit_string.unused_bits, &bit_string.inner) - .expect("invalid BIT STRING") - } + /// Iterator over the bits of this `BIT STRING`. + pub fn bits(&self) -> BitStringIter<'_> { + BitStringRef::from(self).bits() } +} - impl ValueOrd for BitString { - fn value_cmp(&self, other: &Self) -> Result<Ordering> { - match self.unused_bits.cmp(&other.unused_bits) { - Ordering::Equal => self.inner.der_cmp(&other.inner), - ordering => Ok(ordering), - } - } +#[cfg(feature = "alloc")] +impl<'a> DecodeValue<'a> for BitString { + fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> { + let inner_len = (header.length - Length::ONE)?; + let unused_bits = reader.read_byte()?; + let inner = reader.read_vec(inner_len)?; + Self::new(unused_bits, inner) } +} - // Implement by hand because the derive would create invalid values. - // Use the constructor to create a valid value. - #[cfg(feature = "arbitrary")] - impl<'a> arbitrary::Arbitrary<'a> for BitString { - fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> { - Self::new( - u.int_in_range(0..=Self::MAX_UNUSED_BITS)?, - BytesRef::arbitrary(u)?.as_slice(), - ) - .map_err(|_| arbitrary::Error::IncorrectFormat) - } +#[cfg(feature = "alloc")] +impl EncodeValue for BitString { + fn value_len(&self) -> Result<Length> { + Length::ONE + Length::try_from(self.inner.len())? + } - fn size_hint(depth: usize) -> (usize, Option<usize>) { - arbitrary::size_hint::and(u8::size_hint(depth), BytesRef::size_hint(depth)) - } + fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> { + writer.write_byte(self.unused_bits)?; + writer.write(&self.inner) } +} - impl<'a> RefToOwned<'a> for BitStringRef<'a> { - type Owned = BitString; - fn ref_to_owned(&self) -> Self::Owned { - BitString { - unused_bits: self.unused_bits, - bit_length: self.bit_length, - inner: Vec::from(self.inner.as_slice()), - } - } +#[cfg(feature = "alloc")] +impl FixedTag for BitString { + const TAG: Tag = Tag::BitString; +} + +#[cfg(feature = "alloc")] +impl<'a> From<&'a BitString> for BitStringRef<'a> { + fn from(bit_string: &'a BitString) -> BitStringRef<'a> { + // Ensured to parse successfully in constructor + BitStringRef::new(bit_string.unused_bits, &bit_string.inner).expect("invalid BIT STRING") } +} - impl OwnedToRef for BitString { - type Borrowed<'a> = BitStringRef<'a>; - fn owned_to_ref(&self) -> Self::Borrowed<'_> { - self.into() +#[cfg(feature = "alloc")] +impl ValueOrd for BitString { + fn value_cmp(&self, other: &Self) -> Result<Ordering> { + match self.unused_bits.cmp(&other.unused_bits) { + Ordering::Equal => self.inner.der_cmp(&other.inner), + ordering => Ok(ordering), } } } @@ -493,7 +445,7 @@ where BitStringRef::new((lead % 8) as u8, buff)?.value_len() } - fn encode_value(&self, writer: &mut impl Writer) -> Result<()> { + fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> { let (lead, buff) = encode_flagset(self); let buff = &buff[..buff.len() - lead / 8]; BitStringRef::new((lead % 8) as u8, buff)?.encode_value(writer) @@ -534,7 +486,7 @@ mod tests { assert_eq!(bits.len(), 18); for bit in [0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1] { - assert_eq!(u8::from(bits.next().unwrap()), bit) + assert_eq!(bits.next().unwrap() as u8, bit) } // Ensure `None` is returned on successive calls diff --git a/src/asn1/bmp_string.rs b/src/asn1/bmp_string.rs deleted file mode 100644 index b4135d5..0000000 --- a/src/asn1/bmp_string.rs +++ /dev/null @@ -1,164 +0,0 @@ -//! ASN.1 `BMPString` support. - -use crate::{ - BytesOwned, DecodeValue, EncodeValue, Error, FixedTag, Header, Length, Reader, Result, Tag, - Writer, -}; -use alloc::{boxed::Box, vec::Vec}; -use core::{fmt, str::FromStr}; - -/// ASN.1 `BMPString` type. -/// -/// Encodes Basic Multilingual Plane (BMP) subset of Unicode (ISO 10646), -/// a.k.a. UCS-2. -#[derive(Clone, Eq, PartialEq, PartialOrd, Ord)] -pub struct BmpString { - bytes: BytesOwned, -} - -impl BmpString { - /// Create a new [`BmpString`] from its UCS-2 encoding. - pub fn from_ucs2(bytes: impl Into<Box<[u8]>>) -> Result<Self> { - let bytes = bytes.into(); - - if bytes.len() % 2 != 0 { - return Err(Tag::BmpString.length_error()); - } - - let ret = Self { - bytes: bytes.try_into()?, - }; - - for maybe_char in char::decode_utf16(ret.codepoints()) { - match maybe_char { - // All surrogates paired and character is in the Basic Multilingual Plane - Ok(c) if (c as u64) < u64::from(u16::MAX) => (), - // Unpaired surrogates or characters outside Basic Multilingual Plane - _ => return Err(Tag::BmpString.value_error()), - } - } - - Ok(ret) - } - - /// Create a new [`BmpString`] from a UTF-8 string. - pub fn from_utf8(utf8: &str) -> Result<Self> { - let capacity = utf8 - .len() - .checked_mul(2) - .ok_or_else(|| Tag::BmpString.length_error())?; - - let mut bytes = Vec::with_capacity(capacity); - - for code_point in utf8.encode_utf16() { - bytes.extend(code_point.to_be_bytes()); - } - - Self::from_ucs2(bytes) - } - - /// Borrow the encoded UCS-2 as bytes. - pub fn as_bytes(&self) -> &[u8] { - self.bytes.as_ref() - } - - /// Obtain the inner bytes. - #[inline] - pub fn into_bytes(self) -> Box<[u8]> { - self.bytes.into() - } - - /// Get an iterator over characters in the string. - pub fn chars(&self) -> impl Iterator<Item = char> + '_ { - char::decode_utf16(self.codepoints()) - .map(|maybe_char| maybe_char.expect("unpaired surrogates checked in constructor")) - } - - /// Get an iterator over the `u16` codepoints. - pub fn codepoints(&self) -> impl Iterator<Item = u16> + '_ { - // TODO(tarcieri): use `array_chunks` - self.as_bytes() - .chunks_exact(2) - .map(|chunk| u16::from_be_bytes([chunk[0], chunk[1]])) - } -} - -impl AsRef<[u8]> for BmpString { - fn as_ref(&self) -> &[u8] { - self.as_bytes() - } -} - -impl<'a> DecodeValue<'a> for BmpString { - fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> { - Self::from_ucs2(reader.read_vec(header.length)?) - } -} - -impl EncodeValue for BmpString { - fn value_len(&self) -> Result<Length> { - Ok(self.bytes.len()) - } - - fn encode_value(&self, writer: &mut impl Writer) -> Result<()> { - writer.write(self.as_bytes()) - } -} - -impl FixedTag for BmpString { - const TAG: Tag = Tag::BmpString; -} - -impl FromStr for BmpString { - type Err = Error; - - fn from_str(s: &str) -> Result<Self> { - Self::from_utf8(s) - } -} - -impl fmt::Debug for BmpString { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "BmpString(\"{}\")", self) - } -} - -impl fmt::Display for BmpString { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - for c in self.chars() { - write!(f, "{}", c)?; - } - Ok(()) - } -} - -#[cfg(test)] -mod tests { - use super::BmpString; - use crate::{Decode, Encode}; - use alloc::string::ToString; - use hex_literal::hex; - - const EXAMPLE_BYTES: &[u8] = &hex!( - "1e 26 00 43 00 65 00 72 00 74" - " 00 69 00 66 00 69 00 63" - " 00 61 00 74 00 65 00 54" - " 00 65 00 6d 00 70 00 6c" - " 00 61 00 74 00 65" - ); - - const EXAMPLE_UTF8: &str = "CertificateTemplate"; - - #[test] - fn decode() { - let bmp_string = BmpString::from_der(EXAMPLE_BYTES).unwrap(); - assert_eq!(bmp_string.to_string(), EXAMPLE_UTF8); - } - - #[test] - fn encode() { - let bmp_string = BmpString::from_utf8(EXAMPLE_UTF8).unwrap(); - let encoded = bmp_string.to_der().unwrap(); - assert_eq!(encoded, EXAMPLE_BYTES); - } -} diff --git a/src/asn1/boolean.rs b/src/asn1/boolean.rs index 3eb0f2e..e032181 100644 --- a/src/asn1/boolean.rs +++ b/src/asn1/boolean.rs @@ -1,8 +1,8 @@ //! ASN.1 `BOOLEAN` support. use crate::{ - asn1::AnyRef, ord::OrdIsValueOrd, DecodeValue, EncodeValue, Error, ErrorKind, FixedTag, Header, - Length, Reader, Result, Tag, Writer, + asn1::AnyRef, ord::OrdIsValueOrd, ByteSlice, DecodeValue, EncodeValue, Error, ErrorKind, + FixedTag, Header, Length, Reader, Result, Tag, Writer, }; /// Byte used to encode `true` in ASN.1 DER. From X.690 Section 11.1: @@ -33,7 +33,7 @@ impl EncodeValue for bool { Ok(Length::ONE) } - fn encode_value(&self, writer: &mut impl Writer) -> Result<()> { + fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> { writer.write_byte(if *self { TRUE_OCTET } else { FALSE_OCTET }) } } @@ -44,6 +44,17 @@ impl FixedTag for bool { impl OrdIsValueOrd for bool {} +impl From<bool> for AnyRef<'static> { + fn from(value: bool) -> AnyRef<'static> { + let value = ByteSlice::from(match value { + false => &[FALSE_OCTET], + true => &[TRUE_OCTET], + }); + + AnyRef::from_tag_and_value(Tag::Boolean, value) + } +} + impl TryFrom<AnyRef<'_>> for bool { type Error = Error; diff --git a/src/asn1/context_specific.rs b/src/asn1/context_specific.rs index 101ddf0..311b5fe 100644 --- a/src/asn1/context_specific.rs +++ b/src/asn1/context_specific.rs @@ -145,7 +145,7 @@ where } } - fn encode_value(&self, writer: &mut impl Writer) -> Result<()> { + fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> { match self.tag_mode { TagMode::Explicit => self.value.encode(writer), TagMode::Implicit => self.value.encode_value(writer), @@ -239,7 +239,7 @@ where self.encoder().value_len() } - fn encode_value(&self, writer: &mut impl Writer) -> Result<()> { + fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> { self.encoder().encode_value(writer) } } diff --git a/src/asn1/generalized_time.rs b/src/asn1/generalized_time.rs index 8837917..9950e36 100644 --- a/src/asn1/generalized_time.rs +++ b/src/asn1/generalized_time.rs @@ -1,18 +1,16 @@ //! ASN.1 `GeneralizedTime` support. -#![cfg_attr(feature = "arbitrary", allow(clippy::integer_arithmetic))] use crate::{ + asn1::AnyRef, datetime::{self, DateTime}, ord::OrdIsValueOrd, - DecodeValue, EncodeValue, ErrorKind, FixedTag, Header, Length, Reader, Result, Tag, Writer, + DecodeValue, EncodeValue, Error, ErrorKind, FixedTag, Header, Length, Reader, Result, Tag, + Writer, }; use core::time::Duration; #[cfg(feature = "std")] -use { - crate::{asn1::AnyRef, Error}, - std::time::SystemTime, -}; +use std::time::SystemTime; #[cfg(feature = "time")] use time::PrimitiveDateTime; @@ -28,7 +26,6 @@ use time::PrimitiveDateTime; /// > is zero. GeneralizedTime values MUST NOT include fractional seconds. /// /// [1]: https://tools.ietf.org/html/rfc5280#section-4.1.2.5.2 -#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub struct GeneralizedTime(DateTime); @@ -37,7 +34,7 @@ impl GeneralizedTime { const LENGTH: usize = 15; /// Create a [`GeneralizedTime`] from a [`DateTime`]. - pub const fn from_date_time(datetime: DateTime) -> Self { + pub fn from_date_time(datetime: DateTime) -> Self { Self(datetime) } @@ -61,6 +58,7 @@ impl GeneralizedTime { /// Instantiate from [`SystemTime`]. #[cfg(feature = "std")] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub fn from_system_time(time: SystemTime) -> Result<Self> { DateTime::try_from(time) .map(Into::into) @@ -69,13 +67,12 @@ impl GeneralizedTime { /// Convert to [`SystemTime`]. #[cfg(feature = "std")] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub fn to_system_time(&self) -> SystemTime { self.0.to_system_time() } } -impl_any_conversions!(GeneralizedTime); - impl<'a> DecodeValue<'a> for GeneralizedTime { fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> { if Self::LENGTH != usize::try_from(header.length)? { @@ -114,7 +111,7 @@ impl EncodeValue for GeneralizedTime { Self::LENGTH.try_into() } - fn encode_value(&self, writer: &mut impl Writer) -> Result<()> { + fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> { let year_hi = u8::try_from(self.0.year() / 100)?; let year_lo = u8::try_from(self.0.year() % 100)?; @@ -165,6 +162,14 @@ impl From<&DateTime> for GeneralizedTime { } } +impl TryFrom<AnyRef<'_>> for GeneralizedTime { + type Error = Error; + + fn try_from(any: AnyRef<'_>) -> Result<GeneralizedTime> { + any.decode_into() + } +} + impl<'a> DecodeValue<'a> for DateTime { fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> { Ok(GeneralizedTime::decode_value(reader, header)?.into()) @@ -176,7 +181,7 @@ impl EncodeValue for DateTime { GeneralizedTime::from(self).value_len() } - fn encode_value(&self, writer: &mut impl Writer) -> Result<()> { + fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> { GeneralizedTime::from(self).encode_value(writer) } } @@ -188,6 +193,7 @@ impl FixedTag for DateTime { impl OrdIsValueOrd for DateTime {} #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl<'a> DecodeValue<'a> for SystemTime { fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> { Ok(GeneralizedTime::decode_value(reader, header)?.into()) @@ -195,17 +201,19 @@ impl<'a> DecodeValue<'a> for SystemTime { } #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl EncodeValue for SystemTime { fn value_len(&self) -> Result<Length> { GeneralizedTime::try_from(self)?.value_len() } - fn encode_value(&self, writer: &mut impl Writer) -> Result<()> { + fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> { GeneralizedTime::try_from(self)?.encode_value(writer) } } #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl From<GeneralizedTime> for SystemTime { fn from(time: GeneralizedTime) -> SystemTime { time.to_system_time() @@ -213,6 +221,7 @@ impl From<GeneralizedTime> for SystemTime { } #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl From<&GeneralizedTime> for SystemTime { fn from(time: &GeneralizedTime) -> SystemTime { time.to_system_time() @@ -220,6 +229,7 @@ impl From<&GeneralizedTime> for SystemTime { } #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl TryFrom<SystemTime> for GeneralizedTime { type Error = Error; @@ -229,6 +239,7 @@ impl TryFrom<SystemTime> for GeneralizedTime { } #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl TryFrom<&SystemTime> for GeneralizedTime { type Error = Error; @@ -238,6 +249,7 @@ impl TryFrom<&SystemTime> for GeneralizedTime { } #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl<'a> TryFrom<AnyRef<'a>> for SystemTime { type Error = Error; @@ -247,14 +259,17 @@ impl<'a> TryFrom<AnyRef<'a>> for SystemTime { } #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl FixedTag for SystemTime { const TAG: Tag = Tag::GeneralizedTime; } #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl OrdIsValueOrd for SystemTime {} #[cfg(feature = "time")] +#[cfg_attr(docsrs, doc(cfg(feature = "time")))] impl<'a> DecodeValue<'a> for PrimitiveDateTime { fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> { GeneralizedTime::decode_value(reader, header)?.try_into() @@ -262,25 +277,29 @@ impl<'a> DecodeValue<'a> for PrimitiveDateTime { } #[cfg(feature = "time")] +#[cfg_attr(docsrs, doc(cfg(feature = "time")))] impl EncodeValue for PrimitiveDateTime { fn value_len(&self) -> Result<Length> { GeneralizedTime::try_from(self)?.value_len() } - fn encode_value(&self, writer: &mut impl Writer) -> Result<()> { + fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> { GeneralizedTime::try_from(self)?.encode_value(writer) } } #[cfg(feature = "time")] +#[cfg_attr(docsrs, doc(cfg(feature = "time")))] impl FixedTag for PrimitiveDateTime { const TAG: Tag = Tag::GeneralizedTime; } #[cfg(feature = "time")] +#[cfg_attr(docsrs, doc(cfg(feature = "time")))] impl OrdIsValueOrd for PrimitiveDateTime {} #[cfg(feature = "time")] +#[cfg_attr(docsrs, doc(cfg(feature = "time")))] impl TryFrom<PrimitiveDateTime> for GeneralizedTime { type Error = Error; @@ -290,6 +309,7 @@ impl TryFrom<PrimitiveDateTime> for GeneralizedTime { } #[cfg(feature = "time")] +#[cfg_attr(docsrs, doc(cfg(feature = "time")))] impl TryFrom<&PrimitiveDateTime> for GeneralizedTime { type Error = Error; @@ -299,6 +319,7 @@ impl TryFrom<&PrimitiveDateTime> for GeneralizedTime { } #[cfg(feature = "time")] +#[cfg_attr(docsrs, doc(cfg(feature = "time")))] impl TryFrom<GeneralizedTime> for PrimitiveDateTime { type Error = Error; diff --git a/src/asn1/ia5_string.rs b/src/asn1/ia5_string.rs index 1b06dce..3971270 100644 --- a/src/asn1/ia5_string.rs +++ b/src/asn1/ia5_string.rs @@ -1,26 +1,10 @@ //! ASN.1 `IA5String` support. -use crate::{asn1::AnyRef, FixedTag, Result, StrRef, Tag}; -use core::{fmt, ops::Deref}; - -macro_rules! impl_ia5_string { - ($type: ty) => { - impl_ia5_string!($type,); - }; - ($type: ty, $($li: lifetime)?) => { - impl_string_type!($type, $($li),*); - - impl<$($li),*> FixedTag for $type { - const TAG: Tag = Tag::Ia5String; - } - - impl<$($li),*> fmt::Debug for $type { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "Ia5String({:?})", self.as_str()) - } - } - }; -} +use crate::{ + asn1::AnyRef, ord::OrdIsValueOrd, ByteSlice, DecodeValue, EncodeValue, Error, FixedTag, Header, + Length, Reader, Result, StrSlice, Tag, Writer, +}; +use core::{fmt, ops::Deref, str}; /// ASN.1 `IA5String` type. /// @@ -37,7 +21,7 @@ macro_rules! impl_ia5_string { #[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord)] pub struct Ia5StringRef<'a> { /// Inner value - inner: StrRef<'a>, + inner: StrSlice<'a>, } impl<'a> Ia5StringRef<'a> { @@ -53,130 +37,83 @@ impl<'a> Ia5StringRef<'a> { return Err(Self::TAG.value_error()); } - StrRef::from_bytes(input) + StrSlice::from_bytes(input) .map(|inner| Self { inner }) .map_err(|_| Self::TAG.value_error()) } } -impl_ia5_string!(Ia5StringRef<'a>, 'a); - impl<'a> Deref for Ia5StringRef<'a> { - type Target = StrRef<'a>; + type Target = StrSlice<'a>; fn deref(&self) -> &Self::Target { &self.inner } } -impl<'a> From<&Ia5StringRef<'a>> for Ia5StringRef<'a> { - fn from(value: &Ia5StringRef<'a>) -> Ia5StringRef<'a> { - *value +impl AsRef<str> for Ia5StringRef<'_> { + fn as_ref(&self) -> &str { + self.as_str() } } -impl<'a> From<Ia5StringRef<'a>> for AnyRef<'a> { - fn from(internationalized_string: Ia5StringRef<'a>) -> AnyRef<'a> { - AnyRef::from_tag_and_value(Tag::Ia5String, internationalized_string.inner.into()) +impl AsRef<[u8]> for Ia5StringRef<'_> { + fn as_ref(&self) -> &[u8] { + self.as_bytes() } } -#[cfg(feature = "alloc")] -pub use self::allocation::Ia5String; +impl<'a> DecodeValue<'a> for Ia5StringRef<'a> { + fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> { + Self::new(ByteSlice::decode_value(reader, header)?.as_slice()) + } +} -#[cfg(feature = "alloc")] -mod allocation { - use super::Ia5StringRef; - use crate::{ - asn1::AnyRef, - referenced::{OwnedToRef, RefToOwned}, - Error, FixedTag, Result, StrOwned, Tag, - }; - use alloc::string::String; - use core::{fmt, ops::Deref}; - - /// 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: StrOwned, +impl EncodeValue for Ia5StringRef<'_> { + fn value_len(&self) -> Result<Length> { + self.inner.value_len() } - impl Ia5String { - /// Create a new `IA5String`. - pub fn new<T>(input: &T) -> Result<Self> - where - T: AsRef<[u8]> + ?Sized, - { - let input = input.as_ref(); - Ia5StringRef::new(input)?; - - StrOwned::from_bytes(input) - .map(|inner| Self { inner }) - .map_err(|_| Self::TAG.value_error()) - } + fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> { + self.inner.encode_value(writer) } +} - impl_ia5_string!(Ia5String); +impl<'a> FixedTag for Ia5StringRef<'a> { + const TAG: Tag = Tag::Ia5String; +} - impl Deref for Ia5String { - type Target = StrOwned; +impl OrdIsValueOrd for Ia5StringRef<'_> {} - fn deref(&self) -> &Self::Target { - &self.inner - } +impl<'a> From<&Ia5StringRef<'a>> for Ia5StringRef<'a> { + fn from(value: &Ia5StringRef<'a>) -> Ia5StringRef<'a> { + *value } +} - impl<'a> From<Ia5StringRef<'a>> for Ia5String { - fn from(international_string: Ia5StringRef<'a>) -> Ia5String { - let inner = international_string.inner.into(); - Self { inner } - } - } +impl<'a> TryFrom<AnyRef<'a>> for Ia5StringRef<'a> { + type Error = Error; - 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()) - } + fn try_from(any: AnyRef<'a>) -> Result<Ia5StringRef<'a>> { + any.decode_into() } +} - impl<'a> RefToOwned<'a> for Ia5StringRef<'a> { - type Owned = Ia5String; - fn ref_to_owned(&self) -> Self::Owned { - Ia5String { - inner: self.inner.ref_to_owned(), - } - } +impl<'a> From<Ia5StringRef<'a>> for AnyRef<'a> { + fn from(printable_string: Ia5StringRef<'a>) -> AnyRef<'a> { + AnyRef::from_tag_and_value(Tag::Ia5String, printable_string.inner.into()) } +} - impl OwnedToRef for Ia5String { - type Borrowed<'a> = Ia5StringRef<'a>; - fn owned_to_ref(&self) -> Self::Borrowed<'_> { - Ia5StringRef { - inner: self.inner.owned_to_ref(), - } - } +impl<'a> fmt::Display for Ia5StringRef<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.as_str()) } +} - impl TryFrom<String> for Ia5String { - type Error = Error; - - fn try_from(input: String) -> Result<Self> { - Ia5StringRef::new(&input)?; - - StrOwned::new(input) - .map(|inner| Self { inner }) - .map_err(|_| Self::TAG.value_error()) - } +impl<'a> fmt::Debug for Ia5StringRef<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Ia5String({:?})", self.as_str()) } } @@ -189,7 +126,7 @@ mod tests { #[test] fn parse_bytes() { let example_bytes = hex!("16 0d 74 65 73 74 31 40 72 73 61 2e 63 6f 6d"); - let internationalized_string = Ia5StringRef::from_der(&example_bytes).unwrap(); - assert_eq!(internationalized_string.as_str(), "test1@rsa.com"); + let printable_string = Ia5StringRef::from_der(&example_bytes).unwrap(); + assert_eq!(printable_string.as_str(), "test1@rsa.com"); } } diff --git a/src/asn1/integer.rs b/src/asn1/integer.rs index a6e913d..20e2f01 100644 --- a/src/asn1/integer.rs +++ b/src/asn1/integer.rs @@ -1,17 +1,132 @@ //! ASN.1 `INTEGER` support. +pub(super) mod bigint; pub(super) mod int; pub(super) mod uint; +use crate::{ + asn1::AnyRef, ByteSlice, DecodeValue, EncodeValue, Error, FixedTag, Header, Length, Reader, + Result, SliceWriter, Tag, ValueOrd, Writer, +}; use core::{cmp::Ordering, mem}; -use crate::{EncodeValue, Result, SliceWriter}; +macro_rules! impl_int_encoding { + ($($int:ty => $uint:ty),+) => { + $( + impl<'a> DecodeValue<'a> for $int { + fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> { + let bytes = ByteSlice::decode_value(reader, header)?.as_slice(); -/// Is the highest bit of the first byte in the slice set to `1`? (if present) + let result = if is_highest_bit_set(bytes) { + <$uint>::from_be_bytes(int::decode_to_array(bytes)?) as $int + } else { + Self::from_be_bytes(uint::decode_to_array(bytes)?) + }; + + // Ensure we compute the same encoded length as the original any value + if header.length != result.value_len()? { + return Err(Self::TAG.non_canonical_error()); + } + + Ok(result) + } + } + + impl EncodeValue for $int { + fn value_len(&self) -> Result<Length> { + if *self < 0 { + int::encoded_len(&(*self as $uint).to_be_bytes()) + } else { + uint::encoded_len(&self.to_be_bytes()) + } + } + + fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> { + if *self < 0 { + int::encode_bytes(writer, &(*self as $uint).to_be_bytes()) + } else { + uint::encode_bytes(writer, &self.to_be_bytes()) + } + } + } + + impl FixedTag for $int { + const TAG: Tag = Tag::Integer; + } + + impl ValueOrd for $int { + fn value_cmp(&self, other: &Self) -> Result<Ordering> { + value_cmp(*self, *other) + } + } + + impl TryFrom<AnyRef<'_>> for $int { + type Error = Error; + + fn try_from(any: AnyRef<'_>) -> Result<Self> { + any.decode_into() + } + } + )+ + }; +} + +macro_rules! impl_uint_encoding { + ($($uint:ty),+) => { + $( + impl<'a> DecodeValue<'a> for $uint { + fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> { + let bytes = ByteSlice::decode_value(reader, header)?.as_slice(); + let result = Self::from_be_bytes(uint::decode_to_array(bytes)?); + + // Ensure we compute the same encoded length as the original any value + if header.length != result.value_len()? { + return Err(Self::TAG.non_canonical_error()); + } + + Ok(result) + } + } + + impl EncodeValue for $uint { + fn value_len(&self) -> Result<Length> { + uint::encoded_len(&self.to_be_bytes()) + } + + fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> { + uint::encode_bytes(writer, &self.to_be_bytes()) + } + } + + impl FixedTag for $uint { + const TAG: Tag = Tag::Integer; + } + + impl ValueOrd for $uint { + fn value_cmp(&self, other: &Self) -> Result<Ordering> { + value_cmp(*self, *other) + } + } + + impl TryFrom<AnyRef<'_>> for $uint { + type Error = Error; + + fn try_from(any: AnyRef<'_>) -> Result<Self> { + any.decode_into() + } + } + )+ + }; +} + +impl_int_encoding!(i8 => u8, i16 => u16, i32 => u32, i64 => u64, i128 => u128); +impl_uint_encoding!(u8, u16, u32, u64, u128); + +/// Is the highest bit of the first byte in the slice 1? (if present) #[inline] fn is_highest_bit_set(bytes: &[u8]) -> bool { bytes - .first() + .get(0) .map(|byte| byte & 0b10000000 != 0) .unwrap_or(false) } diff --git a/src/asn1/integer/bigint.rs b/src/asn1/integer/bigint.rs new file mode 100644 index 0000000..f896406 --- /dev/null +++ b/src/asn1/integer/bigint.rs @@ -0,0 +1,152 @@ +//! "Big" ASN.1 `INTEGER` types. + +use super::uint; +use crate::{ + asn1::AnyRef, ord::OrdIsValueOrd, ByteSlice, DecodeValue, EncodeValue, Error, ErrorKind, + FixedTag, Header, Length, Reader, Result, Tag, Writer, +}; + +/// "Big" unsigned ASN.1 `INTEGER` type. +/// +/// Provides direct access to the underlying 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(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub struct UIntRef<'a> { + /// Inner value + inner: ByteSlice<'a>, +} + +impl<'a> UIntRef<'a> { + /// Create a new [`UIntRef`] from a byte slice. + pub fn new(bytes: &'a [u8]) -> Result<Self> { + let inner = ByteSlice::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) -> &'a [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 UIntRef<'a> { + fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> { + let bytes = ByteSlice::decode_value(reader, header)?.as_slice(); + let result = Self::new(uint::decode_to_slice(bytes)?)?; + + // 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<'a> EncodeValue for UIntRef<'a> { + fn value_len(&self) -> Result<Length> { + 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 UIntRef<'a> { + fn from(value: &UIntRef<'a>) -> UIntRef<'a> { + *value + } +} + +impl<'a> TryFrom<AnyRef<'a>> for UIntRef<'a> { + type Error = Error; + + fn try_from(any: AnyRef<'a>) -> Result<UIntRef<'a>> { + any.decode_into() + } +} + +impl<'a> FixedTag for UIntRef<'a> { + const TAG: Tag = Tag::Integer; +} + +impl<'a> OrdIsValueOrd for UIntRef<'a> {} + +#[cfg(test)] +mod tests { + use super::UIntRef; + use crate::{ + asn1::{integer::tests::*, AnyRef}, + Decode, Encode, ErrorKind, SliceWriter, Tag, + }; + + #[test] + fn decode_uint_bytes() { + assert_eq!(&[0], UIntRef::from_der(I0_BYTES).unwrap().as_bytes()); + assert_eq!(&[127], UIntRef::from_der(I127_BYTES).unwrap().as_bytes()); + assert_eq!(&[128], UIntRef::from_der(I128_BYTES).unwrap().as_bytes()); + assert_eq!(&[255], UIntRef::from_der(I255_BYTES).unwrap().as_bytes()); + + assert_eq!( + &[0x01, 0x00], + UIntRef::from_der(I256_BYTES).unwrap().as_bytes() + ); + + assert_eq!( + &[0x7F, 0xFF], + UIntRef::from_der(I32767_BYTES).unwrap().as_bytes() + ); + } + + #[test] + fn encode_uint_bytes() { + for &example in &[ + I0_BYTES, + I127_BYTES, + I128_BYTES, + I255_BYTES, + I256_BYTES, + I32767_BYTES, + ] { + let uint = UIntRef::from_der(example).unwrap(); + + let mut buf = [0u8; 128]; + let mut encoder = SliceWriter::new(&mut buf); + uint.encode(&mut encoder).unwrap(); + + let result = encoder.finish().unwrap(); + assert_eq!(example, result); + } + } + + #[test] + fn reject_oversize_without_extra_zero() { + let err = UIntRef::try_from(AnyRef::new(Tag::Integer, &[0x81]).unwrap()) + .err() + .unwrap(); + + assert_eq!(err.kind(), ErrorKind::Value { tag: Tag::Integer }); + } +} diff --git a/src/asn1/integer/int.rs b/src/asn1/integer/int.rs index bccc521..a9fe438 100644 --- a/src/asn1/integer/int.rs +++ b/src/asn1/integer/int.rs @@ -1,310 +1,12 @@ -//! Support for encoding signed integers +//! Support for encoding negative integers -use super::{is_highest_bit_set, uint, value_cmp}; -use crate::{ - ord::OrdIsValueOrd, AnyRef, BytesRef, DecodeValue, EncodeValue, Error, ErrorKind, FixedTag, - Header, Length, Reader, Result, Tag, ValueOrd, Writer, -}; -use core::cmp::Ordering; +use super::is_highest_bit_set; +use crate::{ErrorKind, Length, Result, Writer}; -#[cfg(feature = "alloc")] -pub use allocating::Int; - -macro_rules! impl_encoding_traits { - ($($int:ty => $uint:ty),+) => { - $( - impl<'a> DecodeValue<'a> for $int { - fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> { - let mut buf = [0u8; Self::BITS as usize / 8]; - let max_length = u32::from(header.length) as usize; - - if max_length > buf.len() { - return Err(Self::TAG.non_canonical_error()); - } - - let bytes = reader.read_into(&mut buf[..max_length])?; - - let result = if is_highest_bit_set(bytes) { - <$uint>::from_be_bytes(decode_to_array(bytes)?) as $int - } else { - Self::from_be_bytes(uint::decode_to_array(bytes)?) - }; - - // Ensure we compute the same encoded length as the original any value - if header.length != result.value_len()? { - return Err(Self::TAG.non_canonical_error()); - } - - Ok(result) - } - } - - impl EncodeValue for $int { - fn value_len(&self) -> Result<Length> { - if *self < 0 { - negative_encoded_len(&(*self as $uint).to_be_bytes()) - } else { - uint::encoded_len(&self.to_be_bytes()) - } - } - - fn encode_value(&self, writer: &mut impl Writer) -> Result<()> { - if *self < 0 { - encode_bytes(writer, &(*self as $uint).to_be_bytes()) - } else { - uint::encode_bytes(writer, &self.to_be_bytes()) - } - } - } - - impl FixedTag for $int { - const TAG: Tag = Tag::Integer; - } - - impl ValueOrd for $int { - fn value_cmp(&self, other: &Self) -> Result<Ordering> { - value_cmp(*self, *other) - } - } - - impl TryFrom<AnyRef<'_>> for $int { - type Error = Error; - - fn try_from(any: AnyRef<'_>) -> Result<Self> { - any.decode_as() - } - } - )+ - }; -} - -impl_encoding_traits!(i8 => u8, i16 => u16, i32 => u32, i64 => u64, i128 => u128); - -/// Signed arbitrary precision ASN.1 `INTEGER` reference type. -/// -/// Provides direct access to the underlying big endian bytes which comprise -/// an signed integer value. -/// -/// Intended for use cases like very large integers that are used in -/// cryptographic applications (e.g. keys, signatures). -#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] -pub struct IntRef<'a> { - /// Inner value - inner: BytesRef<'a>, -} - -impl<'a> IntRef<'a> { - /// Create a new [`IntRef`] from a byte slice. - pub fn new(bytes: &'a [u8]) -> Result<Self> { - let inner = BytesRef::new(strip_leading_ones(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 ones stripped. - pub fn as_bytes(&self) -> &'a [u8] { - self.inner.as_slice() - } - - /// Get the length of this [`IntRef`] 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_any_conversions!(IntRef<'a>, 'a); - -impl<'a> DecodeValue<'a> for IntRef<'a> { - fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> { - let bytes = BytesRef::decode_value(reader, header)?; - validate_canonical(bytes.as_slice())?; - - let result = Self::new(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<'a> EncodeValue for IntRef<'a> { - fn value_len(&self) -> Result<Length> { - // Signed integers always hold their full encoded form. - Ok(self.inner.len()) - } - - fn encode_value(&self, writer: &mut impl Writer) -> Result<()> { - writer.write(self.as_bytes()) - } -} - -impl<'a> From<&IntRef<'a>> for IntRef<'a> { - fn from(value: &IntRef<'a>) -> IntRef<'a> { - *value - } -} - -impl<'a> FixedTag for IntRef<'a> { - const TAG: Tag = Tag::Integer; -} - -impl<'a> OrdIsValueOrd for IntRef<'a> {} - -#[cfg(feature = "alloc")] -mod allocating { - use super::{strip_leading_ones, validate_canonical, IntRef}; - use crate::{ - asn1::Uint, - ord::OrdIsValueOrd, - referenced::{OwnedToRef, RefToOwned}, - BytesOwned, DecodeValue, EncodeValue, ErrorKind, FixedTag, Header, Length, Reader, Result, - Tag, Writer, - }; - use alloc::vec::Vec; - - /// Signed arbitrary precision ASN.1 `INTEGER` type. - /// - /// Provides heap-allocated storage for big endian bytes which comprise an - /// signed 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 Int { - /// Inner value - inner: BytesOwned, - } - - impl Int { - /// Create a new [`Int`] from a byte slice. - pub fn new(bytes: &[u8]) -> Result<Self> { - let inner = BytesOwned::new(strip_leading_ones(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 ones stripped. - pub fn as_bytes(&self) -> &[u8] { - self.inner.as_slice() - } - - /// Get the length of this [`Int`] 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_any_conversions!(Int); - - impl<'a> DecodeValue<'a> for Int { - fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> { - let bytes = BytesOwned::decode_value(reader, header)?; - validate_canonical(bytes.as_slice())?; - - let result = Self::new(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 Int { - fn value_len(&self) -> Result<Length> { - // Signed integers always hold their full encoded form. - Ok(self.inner.len()) - } - - fn encode_value(&self, writer: &mut impl Writer) -> Result<()> { - writer.write(self.as_bytes()) - } - } - - impl<'a> From<&IntRef<'a>> for Int { - fn from(value: &IntRef<'a>) -> Int { - let inner = BytesOwned::new(value.as_bytes()).expect("Invalid Int"); - Int { inner } - } - } - - impl From<Uint> for Int { - fn from(value: Uint) -> Self { - let mut inner: Vec<u8> = Vec::new(); - - // Add leading `0x00` byte if required - if value.value_len().expect("invalid Uint") > value.len() { - inner.push(0x00); - } - - inner.extend_from_slice(value.as_bytes()); - let inner = BytesOwned::new(inner).expect("invalid Uint"); - - Int { inner } - } - } - - impl FixedTag for Int { - const TAG: Tag = Tag::Integer; - } - - impl OrdIsValueOrd for Int {} - - impl<'a> RefToOwned<'a> for IntRef<'a> { - type Owned = Int; - fn ref_to_owned(&self) -> Self::Owned { - let inner = self.inner.ref_to_owned(); - - Int { inner } - } - } - - impl OwnedToRef for Int { - type Borrowed<'a> = IntRef<'a>; - fn owned_to_ref(&self) -> Self::Borrowed<'_> { - let inner = self.inner.owned_to_ref(); - - IntRef { inner } - } - } -} - -/// Ensure `INTEGER` is canonically encoded. -fn validate_canonical(bytes: &[u8]) -> Result<()> { - // The `INTEGER` type always encodes a signed value and we're decoding - // as signed here, so we allow a zero extension or sign extension byte, - // but only as permitted under DER canonicalization. - match bytes { - [] => Err(Tag::Integer.non_canonical_error()), - [0x00, byte, ..] if *byte < 0x80 => Err(Tag::Integer.non_canonical_error()), - [0xFF, byte, ..] if *byte >= 0x80 => Err(Tag::Integer.non_canonical_error()), - _ => Ok(()), - } -} - -/// Decode an signed integer of the specified size. +/// Decode an unsigned integer of the specified size. /// /// Returns a byte array of the requested size containing a big endian integer. -fn decode_to_array<const N: usize>(bytes: &[u8]) -> Result<[u8; N]> { +pub(super) fn decode_to_array<const N: usize>(bytes: &[u8]) -> Result<[u8; N]> { match N.checked_sub(bytes.len()) { Some(offset) => { let mut output = [0xFFu8; N]; @@ -325,21 +27,21 @@ fn decode_to_array<const N: usize>(bytes: &[u8]) -> Result<[u8; N]> { } /// Encode the given big endian bytes representing an integer as ASN.1 DER. -fn encode_bytes<W>(writer: &mut W, bytes: &[u8]) -> Result<()> +pub(super) fn encode_bytes<W>(writer: &mut W, bytes: &[u8]) -> Result<()> where W: Writer + ?Sized, { writer.write(strip_leading_ones(bytes)) } -/// Get the encoded length for the given **negative** integer serialized as bytes. +/// Get the encoded length for the given unsigned integer serialized as bytes. #[inline] -fn negative_encoded_len(bytes: &[u8]) -> Result<Length> { +pub(super) fn encoded_len(bytes: &[u8]) -> Result<Length> { Length::try_from(strip_leading_ones(bytes).len()) } /// Strip the leading all-ones bytes from the given byte slice. -pub(crate) fn strip_leading_ones(mut bytes: &[u8]) -> &[u8] { +fn strip_leading_ones(mut bytes: &[u8]) -> &[u8] { while let Some((byte, rest)) = bytes.split_first() { if *byte == 0xFF && is_highest_bit_set(rest) { bytes = rest; @@ -351,92 +53,3 @@ pub(crate) fn strip_leading_ones(mut bytes: &[u8]) -> &[u8] { bytes } - -#[cfg(test)] -mod tests { - use super::{validate_canonical, IntRef}; - use crate::{asn1::integer::tests::*, Decode, Encode, SliceWriter}; - - #[test] - fn validate_canonical_ok() { - assert_eq!(validate_canonical(&[0x00]), Ok(())); - assert_eq!(validate_canonical(&[0x01]), Ok(())); - assert_eq!(validate_canonical(&[0x00, 0x80]), Ok(())); - assert_eq!(validate_canonical(&[0xFF, 0x00]), Ok(())); - } - - #[test] - fn validate_canonical_err() { - // Empty integers are always non-canonical. - assert!(validate_canonical(&[]).is_err()); - - // Positives with excessive zero extension are non-canonical. - assert!(validate_canonical(&[0x00, 0x00]).is_err()); - - // Negatives with excessive sign extension are non-canonical. - assert!(validate_canonical(&[0xFF, 0x80]).is_err()); - } - - #[test] - fn decode_intref() { - // Positive numbers decode, but have zero extensions as necessary - // (to distinguish them from negative representations). - assert_eq!(&[0], IntRef::from_der(I0_BYTES).unwrap().as_bytes()); - assert_eq!(&[127], IntRef::from_der(I127_BYTES).unwrap().as_bytes()); - assert_eq!(&[0, 128], IntRef::from_der(I128_BYTES).unwrap().as_bytes()); - assert_eq!(&[0, 255], IntRef::from_der(I255_BYTES).unwrap().as_bytes()); - - assert_eq!( - &[0x01, 0x00], - IntRef::from_der(I256_BYTES).unwrap().as_bytes() - ); - - assert_eq!( - &[0x7F, 0xFF], - IntRef::from_der(I32767_BYTES).unwrap().as_bytes() - ); - - // Negative integers decode. - assert_eq!(&[128], IntRef::from_der(INEG128_BYTES).unwrap().as_bytes()); - assert_eq!( - &[255, 127], - IntRef::from_der(INEG129_BYTES).unwrap().as_bytes() - ); - assert_eq!( - &[128, 0], - IntRef::from_der(INEG32768_BYTES).unwrap().as_bytes() - ); - } - - #[test] - fn encode_intref() { - for &example in &[ - I0_BYTES, - I127_BYTES, - I128_BYTES, - I255_BYTES, - I256_BYTES, - I32767_BYTES, - ] { - let uint = IntRef::from_der(example).unwrap(); - - let mut buf = [0u8; 128]; - let mut encoder = SliceWriter::new(&mut buf); - uint.encode(&mut encoder).unwrap(); - - let result = encoder.finish().unwrap(); - assert_eq!(example, result); - } - - for &example in &[INEG128_BYTES, INEG129_BYTES, INEG32768_BYTES] { - let uint = IntRef::from_der(example).unwrap(); - - let mut buf = [0u8; 128]; - let mut encoder = SliceWriter::new(&mut buf); - uint.encode(&mut encoder).unwrap(); - - let result = encoder.finish().unwrap(); - assert_eq!(example, result); - } - } -} diff --git a/src/asn1/integer/uint.rs b/src/asn1/integer/uint.rs index 95c6297..e45a72f 100644 --- a/src/asn1/integer/uint.rs +++ b/src/asn1/integer/uint.rs @@ -1,270 +1,6 @@ //! Unsigned integer decoders/encoders. -use super::value_cmp; -use crate::{ - ord::OrdIsValueOrd, AnyRef, BytesRef, DecodeValue, EncodeValue, Error, ErrorKind, FixedTag, - Header, Length, Reader, Result, Tag, ValueOrd, Writer, -}; -use core::cmp::Ordering; - -#[cfg(feature = "alloc")] -pub use allocating::Uint; - -macro_rules! impl_encoding_traits { - ($($uint:ty),+) => { - $( - impl<'a> DecodeValue<'a> for $uint { - fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> { - // Integers always encodes as a signed value, unsigned gets a leading 0x00 that - // needs to be stripped off. We need to provide room for it. - const UNSIGNED_HEADROOM: usize = 1; - - let mut buf = [0u8; (Self::BITS as usize / 8) + UNSIGNED_HEADROOM]; - let max_length = u32::from(header.length) as usize; - - if max_length > buf.len() { - return Err(Self::TAG.non_canonical_error()); - } - - let bytes = reader.read_into(&mut buf[..max_length])?; - - let result = Self::from_be_bytes(decode_to_array(bytes)?); - - // Ensure we compute the same encoded length as the original any value - if header.length != result.value_len()? { - return Err(Self::TAG.non_canonical_error()); - } - - Ok(result) - } - } - - impl EncodeValue for $uint { - fn value_len(&self) -> Result<Length> { - encoded_len(&self.to_be_bytes()) - } - - fn encode_value(&self, writer: &mut impl Writer) -> Result<()> { - encode_bytes(writer, &self.to_be_bytes()) - } - } - - impl FixedTag for $uint { - const TAG: Tag = Tag::Integer; - } - - impl ValueOrd for $uint { - fn value_cmp(&self, other: &Self) -> Result<Ordering> { - value_cmp(*self, *other) - } - } - - impl TryFrom<AnyRef<'_>> for $uint { - type Error = Error; - - fn try_from(any: AnyRef<'_>) -> Result<Self> { - any.decode_as() - } - } - )+ - }; -} - -impl_encoding_traits!(u8, u16, u32, u64, u128); - -/// Unsigned arbitrary precision ASN.1 `INTEGER` reference type. -/// -/// Provides direct access to the underlying 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(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] -pub struct UintRef<'a> { - /// Inner value - inner: BytesRef<'a>, -} - -impl<'a> UintRef<'a> { - /// Create a new [`UintRef`] from a byte slice. - pub fn new(bytes: &'a [u8]) -> Result<Self> { - let inner = BytesRef::new(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) -> &'a [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_any_conversions!(UintRef<'a>, 'a); - -impl<'a> DecodeValue<'a> for UintRef<'a> { - fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> { - let bytes = 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. - if result.value_len()? != header.length { - return Err(Self::TAG.non_canonical_error()); - } - - Ok(result) - } -} - -impl<'a> EncodeValue for UintRef<'a> { - fn value_len(&self) -> Result<Length> { - encoded_len(self.inner.as_slice()) - } - - fn encode_value(&self, writer: &mut impl 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 UintRef<'a> { - fn from(value: &UintRef<'a>) -> UintRef<'a> { - *value - } -} - -impl<'a> FixedTag for UintRef<'a> { - const TAG: Tag = Tag::Integer; -} - -impl<'a> OrdIsValueOrd for UintRef<'a> {} - -#[cfg(feature = "alloc")] -mod allocating { - use super::{decode_to_slice, encoded_len, strip_leading_zeroes, UintRef}; - use crate::{ - ord::OrdIsValueOrd, - referenced::{OwnedToRef, RefToOwned}, - BytesOwned, DecodeValue, EncodeValue, ErrorKind, FixedTag, Header, Length, Reader, Result, - Tag, Writer, - }; - - /// Unsigned arbitrary precision ASN.1 `INTEGER` type. - /// - /// Provides heap-allocated storage for 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: BytesOwned, - } - - impl Uint { - /// Create a new [`Uint`] from a byte slice. - pub fn new(bytes: &[u8]) -> Result<Self> { - let inner = BytesOwned::new(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 [`Uint`] 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_any_conversions!(Uint); - - impl<'a> DecodeValue<'a> for Uint { - fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> { - let bytes = BytesOwned::decode_value(reader, header)?; - let result = Self::new(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<Length> { - encoded_len(self.inner.as_slice()) - } - - fn encode_value(&self, writer: &mut impl 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 = BytesOwned::new(value.as_bytes()).expect("Invalid Uint"); - Uint { inner } - } - } - - impl FixedTag for Uint { - const TAG: Tag = Tag::Integer; - } - - impl OrdIsValueOrd for Uint {} - - impl<'a> RefToOwned<'a> for UintRef<'a> { - type Owned = Uint; - fn ref_to_owned(&self) -> Self::Owned { - let inner = self.inner.ref_to_owned(); - - Uint { inner } - } - } - - impl OwnedToRef for Uint { - type Borrowed<'a> = UintRef<'a>; - fn owned_to_ref(&self) -> Self::Borrowed<'_> { - let inner = self.inner.owned_to_ref(); - - UintRef { inner } - } - } -} +use crate::{Length, Result, Tag, Writer}; /// Decode an unsigned integer into a big endian byte slice with all leading /// zeroes removed. @@ -339,13 +75,13 @@ pub(crate) fn strip_leading_zeroes(mut bytes: &[u8]) -> &[u8] { /// Does the given integer need a leading zero? fn needs_leading_zero(bytes: &[u8]) -> bool { - matches!(bytes.first(), Some(byte) if *byte >= 0x80) + matches!(bytes.get(0), Some(byte) if *byte >= 0x80) } #[cfg(test)] mod tests { - use super::{decode_to_array, UintRef}; - use crate::{asn1::integer::tests::*, AnyRef, Decode, Encode, ErrorKind, SliceWriter, Tag}; + use super::decode_to_array; + use crate::{ErrorKind, Tag}; #[test] fn decode_to_array_no_leading_zero() { @@ -377,52 +113,4 @@ mod tests { let err = decode_to_array::<1>(&[1, 2, 3]).err().unwrap(); assert_eq!(err.kind(), ErrorKind::Length { tag: Tag::Integer }); } - - #[test] - fn decode_uintref() { - assert_eq!(&[0], UintRef::from_der(I0_BYTES).unwrap().as_bytes()); - assert_eq!(&[127], UintRef::from_der(I127_BYTES).unwrap().as_bytes()); - assert_eq!(&[128], UintRef::from_der(I128_BYTES).unwrap().as_bytes()); - assert_eq!(&[255], UintRef::from_der(I255_BYTES).unwrap().as_bytes()); - - assert_eq!( - &[0x01, 0x00], - UintRef::from_der(I256_BYTES).unwrap().as_bytes() - ); - - assert_eq!( - &[0x7F, 0xFF], - UintRef::from_der(I32767_BYTES).unwrap().as_bytes() - ); - } - - #[test] - fn encode_uintref() { - for &example in &[ - I0_BYTES, - I127_BYTES, - I128_BYTES, - I255_BYTES, - I256_BYTES, - I32767_BYTES, - ] { - let uint = UintRef::from_der(example).unwrap(); - - let mut buf = [0u8; 128]; - let mut encoder = SliceWriter::new(&mut buf); - uint.encode(&mut encoder).unwrap(); - - let result = encoder.finish().unwrap(); - assert_eq!(example, result); - } - } - - #[test] - fn reject_oversize_without_extra_zero() { - let err = UintRef::try_from(AnyRef::new(Tag::Integer, &[0x81]).unwrap()) - .err() - .unwrap(); - - assert_eq!(err.kind(), ErrorKind::Value { tag: Tag::Integer }); - } } diff --git a/src/asn1/internal_macros.rs b/src/asn1/internal_macros.rs deleted file mode 100644 index 10ad99d..0000000 --- a/src/asn1/internal_macros.rs +++ /dev/null @@ -1,75 +0,0 @@ -macro_rules! impl_any_conversions { - ($type: ty) => { - impl_any_conversions!($type, ); - }; - ($type: ty, $($li: lifetime)?) => { - impl<'__der: $($li),*, $($li),*> TryFrom<$crate::AnyRef<'__der>> for $type { - type Error = $crate::Error; - - fn try_from(any: $crate::AnyRef<'__der>) -> Result<$type> { - any.decode_as() - } - } - - #[cfg(feature = "alloc")] - impl<'__der: $($li),*, $($li),*> TryFrom<&'__der $crate::Any> for $type { - type Error = $crate::Error; - - fn try_from(any: &'__der $crate::Any) -> Result<$type> { - any.decode_as() - } - } - }; -} - -macro_rules! impl_string_type { - ($type: ty, $($li: lifetime)?) => { - impl_any_conversions!($type, $($li),*); - - mod __impl_string { - use super::*; - - use crate::{ - ord::OrdIsValueOrd, BytesRef, DecodeValue, EncodeValue, Header, Length, Reader, - Result, Writer, - }; - use core::{fmt, str}; - - impl<$($li),*> AsRef<str> for $type { - fn as_ref(&self) -> &str { - self.as_str() - } - } - - impl<$($li),*> AsRef<[u8]> for $type { - fn as_ref(&self) -> &[u8] { - self.as_bytes() - } - } - - impl<'__der: $($li),*, $($li),*> DecodeValue<'__der> for $type { - fn decode_value<R: Reader<'__der>>(reader: &mut R, header: Header) -> Result<Self> { - Self::new(BytesRef::decode_value(reader, header)?.as_slice()) - } - } - - impl<$($li),*> EncodeValue for $type { - fn value_len(&self) -> Result<Length> { - self.inner.value_len() - } - - fn encode_value(&self, writer: &mut impl Writer) -> Result<()> { - self.inner.encode_value(writer) - } - } - - impl<$($li),*> OrdIsValueOrd for $type {} - - impl<$($li),*> fmt::Display for $type { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(self.as_str()) - } - } - } - }; -} diff --git a/src/asn1/null.rs b/src/asn1/null.rs index 7c1e205..e87729f 100644 --- a/src/asn1/null.rs +++ b/src/asn1/null.rs @@ -1,7 +1,7 @@ //! ASN.1 `NULL` support. use crate::{ - asn1::AnyRef, ord::OrdIsValueOrd, BytesRef, DecodeValue, EncodeValue, Error, ErrorKind, + asn1::AnyRef, ord::OrdIsValueOrd, ByteSlice, DecodeValue, EncodeValue, Error, ErrorKind, FixedTag, Header, Length, Reader, Result, Tag, Writer, }; @@ -9,8 +9,6 @@ use crate::{ #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub struct Null; -impl_any_conversions!(Null); - impl<'a> DecodeValue<'a> for Null { fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> { if header.length.is_zero() { @@ -26,7 +24,7 @@ impl EncodeValue for Null { Ok(Length::ZERO) } - fn encode_value(&self, _writer: &mut impl Writer) -> Result<()> { + fn encode_value(&self, _writer: &mut dyn Writer) -> Result<()> { Ok(()) } } @@ -39,7 +37,15 @@ impl OrdIsValueOrd for Null {} impl<'a> From<Null> for AnyRef<'a> { fn from(_: Null) -> AnyRef<'a> { - AnyRef::from_tag_and_value(Tag::Null, BytesRef::default()) + AnyRef::from_tag_and_value(Tag::Null, ByteSlice::default()) + } +} + +impl TryFrom<AnyRef<'_>> for Null { + type Error = Error; + + fn try_from(any: AnyRef<'_>) -> Result<Null> { + any.decode_into() } } @@ -69,7 +75,7 @@ impl EncodeValue for () { Ok(Length::ZERO) } - fn encode_value(&self, _writer: &mut impl Writer) -> Result<()> { + fn encode_value(&self, _writer: &mut dyn Writer) -> Result<()> { Ok(()) } } diff --git a/src/asn1/octet_string.rs b/src/asn1/octet_string.rs index 53d8ecb..6f5b91a 100644 --- a/src/asn1/octet_string.rs +++ b/src/asn1/octet_string.rs @@ -1,10 +1,13 @@ //! ASN.1 `OCTET STRING` support. use crate::{ - asn1::AnyRef, ord::OrdIsValueOrd, BytesRef, Decode, DecodeValue, EncodeValue, ErrorKind, + asn1::AnyRef, ord::OrdIsValueOrd, ByteSlice, DecodeValue, EncodeValue, Error, ErrorKind, FixedTag, Header, Length, Reader, Result, Tag, Writer, }; +#[cfg(feature = "alloc")] +use alloc::vec::Vec; + /// ASN.1 `OCTET STRING` type: borrowed form. /// /// Octet strings represent contiguous sequences of octets, a.k.a. bytes. @@ -13,13 +16,13 @@ use crate::{ #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub struct OctetStringRef<'a> { /// Inner value - inner: BytesRef<'a>, + inner: ByteSlice<'a>, } impl<'a> OctetStringRef<'a> { /// Create a new ASN.1 `OCTET STRING` from a byte slice. pub fn new(slice: &'a [u8]) -> Result<Self> { - BytesRef::new(slice) + ByteSlice::new(slice) .map(|inner| Self { inner }) .map_err(|_| ErrorKind::Length { tag: Self::TAG }.into()) } @@ -38,15 +41,8 @@ impl<'a> OctetStringRef<'a> { pub fn is_empty(&self) -> bool { self.inner.is_empty() } - - /// Parse `T` from this `OCTET STRING`'s contents. - pub fn decode_into<T: Decode<'a>>(&self) -> Result<T> { - Decode::from_der(self.as_bytes()) - } } -impl_any_conversions!(OctetStringRef<'a>, 'a); - impl AsRef<[u8]> for OctetStringRef<'_> { fn as_ref(&self) -> &[u8] { self.as_bytes() @@ -55,7 +51,7 @@ impl AsRef<[u8]> for OctetStringRef<'_> { impl<'a> DecodeValue<'a> for OctetStringRef<'a> { fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> { - let inner = BytesRef::decode_value(reader, header)?; + let inner = ByteSlice::decode_value(reader, header)?; Ok(Self { inner }) } } @@ -65,7 +61,7 @@ impl EncodeValue for OctetStringRef<'_> { self.inner.value_len() } - fn encode_value(&self, writer: &mut impl Writer) -> Result<()> { + fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> { self.inner.encode_value(writer) } } @@ -82,6 +78,14 @@ impl<'a> From<&OctetStringRef<'a>> for OctetStringRef<'a> { } } +impl<'a> TryFrom<AnyRef<'a>> for OctetStringRef<'a> { + type Error = Error; + + fn try_from(any: AnyRef<'a>) -> Result<OctetStringRef<'a>> { + any.decode_into() + } +} + impl<'a> From<OctetStringRef<'a>> for AnyRef<'a> { fn from(octet_string: OctetStringRef<'a>) -> AnyRef<'a> { AnyRef::from_tag_and_value(Tag::OctetString, octet_string.inner) @@ -94,164 +98,85 @@ impl<'a> From<OctetStringRef<'a>> for &'a [u8] { } } +/// ASN.1 `OCTET STRING` type: owned form.. +/// +/// Octet strings represent contiguous sequences of octets, a.k.a. bytes. +/// +/// This type provides the same functionality as [`OctetStringRef`] but owns +/// the backing data. #[cfg(feature = "alloc")] -pub use self::allocating::OctetString; +#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub struct OctetString { + /// Bitstring represented as a slice of bytes. + inner: Vec<u8>, +} #[cfg(feature = "alloc")] -mod allocating { - use super::*; - use crate::referenced::*; - use alloc::vec::Vec; - - /// ASN.1 `OCTET STRING` type: owned form.. - /// - /// Octet strings represent contiguous sequences of octets, a.k.a. bytes. - /// - /// This type provides the same functionality as [`OctetStringRef`] but owns - /// the backing data. - #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] - pub struct OctetString { - /// Bitstring represented as a slice of bytes. - pub(super) inner: Vec<u8>, - } - - impl OctetString { - /// Create a new ASN.1 `OCTET STRING`. - pub fn new(bytes: impl Into<Vec<u8>>) -> Result<Self> { - let inner = bytes.into(); - - // Ensure the bytes parse successfully as an `OctetStringRef` - OctetStringRef::new(&inner)?; - - Ok(Self { inner }) - } - - /// Borrow the inner byte slice. - pub fn as_bytes(&self) -> &[u8] { - self.inner.as_slice() - } - - /// Take ownership of the octet string. - pub fn into_bytes(self) -> Vec<u8> { - self.inner - } - - /// Get the length of the inner byte slice. - pub fn len(&self) -> Length { - self.value_len().expect("invalid OCTET STRING length") - } - - /// Is the inner byte slice empty? - pub fn is_empty(&self) -> bool { - self.inner.is_empty() - } - } - - impl_any_conversions!(OctetString); - - impl AsRef<[u8]> for OctetString { - fn as_ref(&self) -> &[u8] { - self.as_bytes() - } - } - - impl<'a> DecodeValue<'a> for OctetString { - fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> { - Self::new(reader.read_vec(header.length)?) - } - } - - impl EncodeValue for OctetString { - fn value_len(&self) -> Result<Length> { - self.inner.len().try_into() - } +impl OctetString { + /// Create a new ASN.1 `OCTET STRING`. + pub fn new(bytes: impl Into<Vec<u8>>) -> Result<Self> { + let inner = bytes.into(); - fn encode_value(&self, writer: &mut impl Writer) -> Result<()> { - writer.write(&self.inner) - } - } + // Ensure the bytes parse successfully as an `OctetStringRef` + OctetStringRef::new(&inner)?; - impl FixedTag for OctetString { - const TAG: Tag = Tag::OctetString; + Ok(Self { inner }) } - impl<'a> From<&'a OctetString> for OctetStringRef<'a> { - fn from(octet_string: &'a OctetString) -> OctetStringRef<'a> { - // Ensured to parse successfully in constructor - OctetStringRef::new(&octet_string.inner).expect("invalid OCTET STRING") - } + /// Borrow the inner byte slice. + pub fn as_bytes(&self) -> &[u8] { + self.inner.as_slice() } - impl OrdIsValueOrd for OctetString {} - - impl<'a> RefToOwned<'a> for OctetStringRef<'a> { - type Owned = OctetString; - fn ref_to_owned(&self) -> Self::Owned { - OctetString { - inner: Vec::from(self.inner.as_slice()), - } - } + /// Get the length of the inner byte slice. + pub fn len(&self) -> Length { + self.value_len().expect("invalid OCTET STRING length") } - impl OwnedToRef for OctetString { - type Borrowed<'a> = OctetStringRef<'a>; - fn owned_to_ref(&self) -> Self::Borrowed<'_> { - self.into() - } + /// Is the inner byte slice empty? + pub fn is_empty(&self) -> bool { + self.inner.is_empty() } +} - // Implement by hand because the derive would create invalid values. - // Use the constructor to create a valid value. - #[cfg(feature = "arbitrary")] - impl<'a> arbitrary::Arbitrary<'a> for OctetString { - fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> { - Self::new(Vec::arbitrary(u)?).map_err(|_| arbitrary::Error::IncorrectFormat) - } - - fn size_hint(depth: usize) -> (usize, Option<usize>) { - arbitrary::size_hint::and(u8::size_hint(depth), Vec::<u8>::size_hint(depth)) - } +#[cfg(feature = "alloc")] +impl AsRef<[u8]> for OctetString { + fn as_ref(&self) -> &[u8] { + self.as_bytes() } } -#[cfg(feature = "bytes")] -mod bytes { - use super::OctetString; - use crate::{DecodeValue, EncodeValue, FixedTag, Header, Length, Reader, Result, Tag, Writer}; - use bytes::Bytes; - - impl<'a> DecodeValue<'a> for Bytes { - fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> { - OctetString::decode_value(reader, header).map(|octet_string| octet_string.inner.into()) - } +#[cfg(feature = "alloc")] +impl<'a> DecodeValue<'a> for OctetString { + fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> { + Self::new(reader.read_vec(header.length)?) } +} - impl EncodeValue for Bytes { - fn value_len(&self) -> Result<Length> { - self.len().try_into() - } - - fn encode_value(&self, writer: &mut impl Writer) -> Result<()> { - writer.write(self.as_ref()) - } +#[cfg(feature = "alloc")] +impl EncodeValue for OctetString { + fn value_len(&self) -> Result<Length> { + self.inner.len().try_into() } - impl FixedTag for Bytes { - const TAG: Tag = Tag::OctetString; + fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> { + writer.write(&self.inner) } } -#[cfg(test)] -mod tests { - use crate::asn1::{OctetStringRef, PrintableStringRef}; - - #[test] - fn octet_string_decode_into() { - // PrintableString "hi" - let der = b"\x13\x02\x68\x69"; - let oct = OctetStringRef::new(der).unwrap(); +#[cfg(feature = "alloc")] +impl FixedTag for OctetString { + const TAG: Tag = Tag::OctetString; +} - let res = oct.decode_into::<PrintableStringRef<'_>>().unwrap(); - assert_eq!(AsRef::<str>::as_ref(&res), "hi"); +#[cfg(feature = "alloc")] +impl<'a> From<&'a OctetString> for OctetStringRef<'a> { + fn from(octet_string: &'a OctetString) -> OctetStringRef<'a> { + // Ensured to parse successfully in constructor + OctetStringRef::new(&octet_string.inner).expect("invalid OCTET STRING") } } + +#[cfg(feature = "alloc")] +impl OrdIsValueOrd for OctetString {} diff --git a/src/asn1/oid.rs b/src/asn1/oid.rs index 3daa452..8b28718 100644 --- a/src/asn1/oid.rs +++ b/src/asn1/oid.rs @@ -6,9 +6,6 @@ use crate::{ }; use const_oid::ObjectIdentifier; -#[cfg(feature = "alloc")] -use super::Any; - impl<'a> DecodeValue<'a> for ObjectIdentifier { fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> { let mut buf = [0u8; ObjectIdentifier::MAX_SIZE]; @@ -27,7 +24,7 @@ impl EncodeValue for ObjectIdentifier { Length::try_from(self.as_bytes().len()) } - fn encode_value(&self, writer: &mut impl Writer) -> Result<()> { + fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> { writer.write(self.as_bytes()) } } @@ -53,13 +50,6 @@ impl<'a> From<&'a ObjectIdentifier> for AnyRef<'a> { } } -#[cfg(feature = "alloc")] -impl From<ObjectIdentifier> for Any { - fn from(oid: ObjectIdentifier) -> Any { - AnyRef::from(&oid).into() - } -} - impl TryFrom<AnyRef<'_>> for ObjectIdentifier { type Error = Error; diff --git a/src/asn1/optional.rs b/src/asn1/optional.rs index ecda4f8..a9b923c 100644 --- a/src/asn1/optional.rs +++ b/src/asn1/optional.rs @@ -41,7 +41,7 @@ where (&self).encoded_len() } - fn encode(&self, writer: &mut impl Writer) -> Result<()> { + fn encode(&self, writer: &mut dyn Writer) -> Result<()> { (&self).encode(writer) } } @@ -57,7 +57,7 @@ where } } - fn encode(&self, encoder: &mut impl Writer) -> Result<()> { + fn encode(&self, encoder: &mut dyn Writer) -> Result<()> { match self { Some(encodable) => encodable.encode(encoder), None => Ok(()), diff --git a/src/asn1/printable_string.rs b/src/asn1/printable_string.rs index d2e51d7..d48f90f 100644 --- a/src/asn1/printable_string.rs +++ b/src/asn1/printable_string.rs @@ -1,26 +1,10 @@ //! ASN.1 `PrintableString` support. -use crate::{asn1::AnyRef, FixedTag, Result, StrRef, Tag}; -use core::{fmt, ops::Deref}; - -macro_rules! impl_printable_string { - ($type: ty) => { - impl_printable_string!($type,); - }; - ($type: ty, $($li: lifetime)?) => { - impl_string_type!($type, $($li),*); - - impl<$($li),*> FixedTag for $type { - const TAG: Tag = Tag::PrintableString; - } - - impl<$($li),*> fmt::Debug for $type { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "PrintableString({:?})", self.as_str()) - } - } - }; -} +use crate::{ + asn1::AnyRef, ord::OrdIsValueOrd, ByteSlice, DecodeValue, EncodeValue, Error, FixedTag, Header, + Length, Reader, Result, StrSlice, Tag, Writer, +}; +use core::{fmt, ops::Deref, str}; /// ASN.1 `PrintableString` type. /// @@ -54,7 +38,7 @@ macro_rules! impl_printable_string { #[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord)] pub struct PrintableStringRef<'a> { /// Inner value - inner: StrRef<'a>, + inner: StrSlice<'a>, } impl<'a> PrintableStringRef<'a> { @@ -87,151 +71,83 @@ impl<'a> PrintableStringRef<'a> { } } - StrRef::from_bytes(input) + StrSlice::from_bytes(input) .map(|inner| Self { inner }) .map_err(|_| Self::TAG.value_error()) } } -impl_printable_string!(PrintableStringRef<'a>, 'a); - impl<'a> Deref for PrintableStringRef<'a> { - type Target = StrRef<'a>; + type Target = StrSlice<'a>; fn deref(&self) -> &Self::Target { &self.inner } } -impl<'a> From<&PrintableStringRef<'a>> for PrintableStringRef<'a> { - fn from(value: &PrintableStringRef<'a>) -> PrintableStringRef<'a> { - *value + +impl AsRef<str> for PrintableStringRef<'_> { + fn as_ref(&self) -> &str { + self.as_str() } } -impl<'a> From<PrintableStringRef<'a>> for AnyRef<'a> { - fn from(printable_string: PrintableStringRef<'a>) -> AnyRef<'a> { - AnyRef::from_tag_and_value(Tag::PrintableString, printable_string.inner.into()) +impl AsRef<[u8]> for PrintableStringRef<'_> { + fn as_ref(&self) -> &[u8] { + self.as_bytes() } } -#[cfg(feature = "alloc")] -pub use self::allocation::PrintableString; - -#[cfg(feature = "alloc")] -mod allocation { - use super::PrintableStringRef; +impl<'a> DecodeValue<'a> for PrintableStringRef<'a> { + fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> { + Self::new(ByteSlice::decode_value(reader, header)?.as_slice()) + } +} - use crate::{ - asn1::AnyRef, - referenced::{OwnedToRef, RefToOwned}, - BytesRef, Error, FixedTag, Result, StrOwned, Tag, - }; - use alloc::string::String; - use core::{fmt, ops::Deref}; - - /// 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: StrOwned, +impl<'a> EncodeValue for PrintableStringRef<'a> { + fn value_len(&self) -> Result<Length> { + self.inner.value_len() } - impl PrintableString { - /// Create a new ASN.1 `PrintableString`. - pub fn new<T>(input: &T) -> Result<Self> - where - T: AsRef<[u8]> + ?Sized, - { - let input = input.as_ref(); - PrintableStringRef::new(input)?; - - StrOwned::from_bytes(input) - .map(|inner| Self { inner }) - .map_err(|_| Self::TAG.value_error()) - } + fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> { + self.inner.encode_value(writer) } +} - impl_printable_string!(PrintableString); +impl FixedTag for PrintableStringRef<'_> { + const TAG: Tag = Tag::PrintableString; +} - impl Deref for PrintableString { - type Target = StrOwned; +impl OrdIsValueOrd for PrintableStringRef<'_> {} - fn deref(&self) -> &Self::Target { - &self.inner - } +impl<'a> From<&PrintableStringRef<'a>> for PrintableStringRef<'a> { + fn from(value: &PrintableStringRef<'a>) -> PrintableStringRef<'a> { + *value } +} - impl<'a> From<PrintableStringRef<'a>> for PrintableString { - fn from(value: PrintableStringRef<'a>) -> PrintableString { - let inner = - StrOwned::from_bytes(value.inner.as_bytes()).expect("Invalid PrintableString"); - Self { inner } - } - } +impl<'a> TryFrom<AnyRef<'a>> for PrintableStringRef<'a> { + type Error = Error; - impl<'a> From<&'a PrintableString> for AnyRef<'a> { - fn from(printable_string: &'a PrintableString) -> AnyRef<'a> { - AnyRef::from_tag_and_value( - Tag::PrintableString, - BytesRef::new(printable_string.inner.as_bytes()).expect("Invalid PrintableString"), - ) - } + fn try_from(any: AnyRef<'a>) -> Result<PrintableStringRef<'a>> { + any.decode_into() } +} - impl<'a> RefToOwned<'a> for PrintableStringRef<'a> { - type Owned = PrintableString; - fn ref_to_owned(&self) -> Self::Owned { - PrintableString { - inner: self.inner.ref_to_owned(), - } - } +impl<'a> From<PrintableStringRef<'a>> for AnyRef<'a> { + fn from(printable_string: PrintableStringRef<'a>) -> AnyRef<'a> { + AnyRef::from_tag_and_value(Tag::PrintableString, printable_string.inner.into()) } +} - impl OwnedToRef for PrintableString { - type Borrowed<'a> = PrintableStringRef<'a>; - fn owned_to_ref(&self) -> Self::Borrowed<'_> { - PrintableStringRef { - inner: self.inner.owned_to_ref(), - } - } +impl<'a> fmt::Display for PrintableStringRef<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.as_str()) } +} - impl TryFrom<String> for PrintableString { - type Error = Error; - - fn try_from(input: String) -> Result<Self> { - PrintableStringRef::new(&input)?; - - StrOwned::new(input) - .map(|inner| Self { inner }) - .map_err(|_| Self::TAG.value_error()) - } +impl<'a> fmt::Debug for PrintableStringRef<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "PrintableString({:?})", self.as_str()) } } diff --git a/src/asn1/real.rs b/src/asn1/real.rs index b9f2e67..f872d2d 100644 --- a/src/asn1/real.rs +++ b/src/asn1/real.rs @@ -8,21 +8,22 @@ )] use crate::{ - BytesRef, DecodeValue, EncodeValue, FixedTag, Header, Length, Reader, Result, StrRef, Tag, - Writer, + str_slice::StrSlice, ByteSlice, DecodeValue, EncodeValue, FixedTag, Header, Length, Reader, + Result, Tag, Writer, }; use super::integer::uint::strip_leading_zeroes; +#[cfg_attr(docsrs, doc(cfg(feature = "real")))] impl<'a> DecodeValue<'a> for f64 { fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> { - let bytes = BytesRef::decode_value(reader, header)?.as_slice(); + let bytes = ByteSlice::decode_value(reader, header)?.as_slice(); if header.length == Length::ZERO { Ok(0.0) } else if is_nth_bit_one::<7>(bytes) { // Binary encoding from section 8.5.7 applies - let sign: u64 = u64::from(is_nth_bit_one::<6>(bytes)); + let sign: u64 = if is_nth_bit_one::<6>(bytes) { 1 } else { 0 }; // Section 8.5.7.2: Check the base -- the DER specs say that only base 2 should be supported in DER let base = mnth_bits_to_u8::<5, 4>(bytes); @@ -73,7 +74,7 @@ impl<'a> DecodeValue<'a> for f64 { _ => Err(Tag::Real.value_error()), } } else { - let astr = StrRef::from_bytes(&bytes[1..])?; + let astr = StrSlice::from_bytes(&bytes[1..])?; match astr.inner.parse::<f64>() { Ok(val) => Ok(val), // Real related error: encoding not supported or malformed @@ -83,6 +84,7 @@ impl<'a> DecodeValue<'a> for f64 { } } +#[cfg_attr(docsrs, doc(cfg(feature = "real")))] impl EncodeValue for f64 { fn value_len(&self) -> Result<Length> { if self.is_sign_positive() && (*self) < f64::MIN_POSITIVE { @@ -118,7 +120,7 @@ impl EncodeValue for f64 { } } - fn encode_value(&self, writer: &mut impl Writer) -> Result<()> { + fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> { // Check if special value // Encode zero first, if it's zero // Special value from section 8.5.9 if non zero @@ -192,6 +194,7 @@ impl EncodeValue for f64 { } } +#[cfg_attr(docsrs, doc(cfg(feature = "real")))] impl FixedTag for f64 { const TAG: Tag = Tag::Real; } @@ -201,7 +204,7 @@ impl FixedTag for f64 { pub(crate) fn is_nth_bit_one<const N: usize>(bytes: &[u8]) -> bool { if N < 8 { bytes - .first() + .get(0) .map(|byte| byte & (1 << N) != 0) .unwrap_or(false) } else { diff --git a/src/asn1/sequence.rs b/src/asn1/sequence.rs index ad4a5d5..d2f6bc5 100644 --- a/src/asn1/sequence.rs +++ b/src/asn1/sequence.rs @@ -2,26 +2,55 @@ //! `SEQUENCE`s to Rust structs. use crate::{ - BytesRef, DecodeValue, EncodeValue, FixedTag, Header, Length, Reader, Result, Tag, Writer, + ByteSlice, Decode, DecodeValue, Encode, EncodeValue, FixedTag, Header, Length, Reader, Result, + Tag, Writer, }; -#[cfg(feature = "alloc")] -use alloc::boxed::Box; - -/// Marker trait for ASN.1 `SEQUENCE`s. +/// ASN.1 `SEQUENCE` trait. /// -/// This is mainly used for custom derive. -pub trait Sequence<'a>: DecodeValue<'a> + EncodeValue {} +/// Types which impl this trait receive blanket impls for the [`Decode`], +/// [`Encode`], and [`FixedTag`] traits. +pub trait Sequence<'a>: Decode<'a> { + /// Call the provided function with a slice of [`Encode`] trait objects + /// representing the fields of this `SEQUENCE`. + /// + /// This method uses a callback because structs with fields which aren't + /// directly [`Encode`] may need to construct temporary values from + /// their fields prior to encoding. + fn fields<F, T>(&self, f: F) -> Result<T> + where + F: FnOnce(&[&dyn Encode]) -> Result<T>; +} -impl<'a, S> FixedTag for S +impl<'a, M> EncodeValue for M where - S: Sequence<'a>, + M: Sequence<'a>, { - const TAG: Tag = Tag::Sequence; + fn value_len(&self) -> Result<Length> { + self.fields(|fields| { + fields + .iter() + .try_fold(Length::ZERO, |acc, field| acc + field.encoded_len()?) + }) + } + + fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> { + self.fields(|fields| { + for &field in fields { + field.encode(writer)?; + } + + Ok(()) + }) + } } -#[cfg(feature = "alloc")] -impl<'a, T> Sequence<'a> for Box<T> where T: Sequence<'a> {} +impl<'a, M> FixedTag for M +where + M: Sequence<'a>, +{ + const TAG: Tag = Tag::Sequence; +} /// The [`SequenceRef`] type provides raw access to the octets which comprise a /// DER-encoded `SEQUENCE`. @@ -29,13 +58,13 @@ impl<'a, T> Sequence<'a> for Box<T> 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: ByteSlice<'a>, } impl<'a> DecodeValue<'a> for SequenceRef<'a> { fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> { Ok(Self { - body: BytesRef::decode_value(reader, header)?, + body: ByteSlice::decode_value(reader, header)?, }) } } @@ -45,9 +74,11 @@ impl EncodeValue for SequenceRef<'_> { Ok(self.body.len()) } - fn encode_value(&self, writer: &mut impl Writer) -> Result<()> { + fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> { self.body.encode_value(writer) } } -impl<'a> Sequence<'a> for SequenceRef<'a> {} +impl<'a> FixedTag for SequenceRef<'a> { + const TAG: Tag = Tag::Sequence; +} diff --git a/src/asn1/sequence_of.rs b/src/asn1/sequence_of.rs index befb029..6334d71 100644 --- a/src/asn1/sequence_of.rs +++ b/src/asn1/sequence_of.rs @@ -30,7 +30,7 @@ impl<T, const N: usize> SequenceOf<T, N> { /// Add an element to this [`SequenceOf`]. pub fn add(&mut self, element: T) -> Result<()> { - self.inner.push(element) + self.inner.add(element) } /// Get an element of this [`SequenceOf`]. @@ -88,7 +88,7 @@ where .fold(Ok(Length::ZERO), |len, elem| len + elem.encoded_len()?) } - fn encode_value(&self, writer: &mut impl Writer) -> Result<()> { + fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> { for elem in self.iter() { elem.encode(writer)?; } @@ -155,7 +155,7 @@ where .fold(Ok(Length::ZERO), |len, elem| len + elem.encoded_len()?) } - fn encode_value(&self, writer: &mut impl Writer) -> Result<()> { + fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> { for elem in self { elem.encode(writer)?; } @@ -178,6 +178,7 @@ where } #[cfg(feature = "alloc")] +#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] impl<'a, T> DecodeValue<'a> for Vec<T> where T: Decode<'a>, @@ -196,6 +197,7 @@ where } #[cfg(feature = "alloc")] +#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] impl<T> EncodeValue for Vec<T> where T: Encode, @@ -205,7 +207,7 @@ where .fold(Ok(Length::ZERO), |len, elem| len + elem.encoded_len()?) } - fn encode_value(&self, writer: &mut impl Writer) -> Result<()> { + fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> { for elem in self { elem.encode(writer)?; } @@ -215,11 +217,13 @@ where } #[cfg(feature = "alloc")] +#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] impl<T> FixedTag for Vec<T> { const TAG: Tag = Tag::Sequence; } #[cfg(feature = "alloc")] +#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] impl<T> ValueOrd for Vec<T> where T: DerOrd, diff --git a/src/asn1/set_of.rs b/src/asn1/set_of.rs index ff01312..b8c4b0d 100644 --- a/src/asn1/set_of.rs +++ b/src/asn1/set_of.rs @@ -44,32 +44,19 @@ where } } - /// Add an item to this [`SetOf`]. + /// Add an element to this [`SetOf`]. /// /// Items MUST be added in lexicographical order according to the /// [`DerOrd`] impl on `T`. - #[deprecated(since = "0.7.6", note = "use `insert` or `insert_ordered` instead")] pub fn add(&mut self, new_elem: T) -> Result<()> { - self.insert_ordered(new_elem) - } - - /// Insert an item into this [`SetOf`]. - pub fn insert(&mut self, item: T) -> Result<()> { - self.inner.push(item)?; - der_sort(self.inner.as_mut()) - } - - /// Insert an item into this [`SetOf`]. - /// - /// Items MUST be added in lexicographical order according to the - /// [`DerOrd`] impl on `T`. - pub fn insert_ordered(&mut self, item: T) -> Result<()> { // Ensure set elements are lexicographically ordered - if let Some(last) = self.inner.last() { - check_der_ordering(last, &item)?; + if let Some(last_elem) = self.inner.last() { + if new_elem.der_cmp(last_elem)? != Ordering::Greater { + return Err(ErrorKind::SetOrdering.into()); + } } - self.inner.push(item) + self.inner.add(new_elem) } /// Get the nth element from this [`SetOf`]. @@ -113,7 +100,7 @@ where let mut result = Self::new(); while !reader.is_finished() { - result.inner.push(T::decode(reader)?)?; + result.inner.add(T::decode(reader)?)?; } der_sort(result.inner.as_mut())?; @@ -132,7 +119,7 @@ where .fold(Ok(Length::ZERO), |len, elem| len + elem.encoded_len()?) } - fn encode_value(&self, writer: &mut impl Writer) -> Result<()> { + fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> { for elem in self.iter() { elem.encode(writer)?; } @@ -160,7 +147,7 @@ where let mut result = SetOf::new(); for elem in arr { - result.insert_ordered(elem)?; + result.add(elem)?; } Ok(result) @@ -198,6 +185,7 @@ impl<'a, T> ExactSizeIterator for SetOfIter<'a, T> {} /// This type implements an append-only `SET OF` type which is heap-backed /// and depends on `alloc` support. #[cfg(feature = "alloc")] +#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub struct SetOfVec<T> where @@ -207,6 +195,7 @@ where } #[cfg(feature = "alloc")] +#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] impl<T: DerOrd> Default for SetOfVec<T> { fn default() -> Self { Self { @@ -216,6 +205,7 @@ impl<T: DerOrd> Default for SetOfVec<T> { } #[cfg(feature = "alloc")] +#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] impl<T> SetOfVec<T> where T: DerOrd, @@ -227,56 +217,19 @@ where } } - /// Create a new [`SetOfVec`] from the given iterator. - /// - /// Note: this is an inherent method instead of an impl of the - /// [`FromIterator`] trait in order to be fallible. - #[allow(clippy::should_implement_trait)] - pub fn from_iter<I>(iter: I) -> Result<Self> - where - I: IntoIterator<Item = T>, - { - Vec::from_iter(iter).try_into() - } - /// Add an element to this [`SetOfVec`]. /// /// Items MUST be added in lexicographical order according to the /// [`DerOrd`] impl on `T`. - #[deprecated(since = "0.7.6", note = "use `insert` or `insert_ordered` instead")] - pub fn add(&mut self, item: T) -> Result<()> { - self.insert_ordered(item) - } - - /// Extend a [`SetOfVec`] using an iterator. - /// - /// Note: this is an inherent method instead of an impl of the - /// [`Extend`] trait in order to be fallible. - pub fn extend<I>(&mut self, iter: I) -> Result<()> - where - I: IntoIterator<Item = T>, - { - self.inner.extend(iter); - der_sort(&mut self.inner) - } - - /// Insert an item into this [`SetOfVec`]. Must be unique. - pub fn insert(&mut self, item: T) -> Result<()> { - self.inner.push(item); - der_sort(&mut self.inner) - } - - /// Insert an item into this [`SetOfVec`]. Must be unique. - /// - /// Items MUST be added in lexicographical order according to the - /// [`DerOrd`] impl on `T`. - pub fn insert_ordered(&mut self, item: T) -> Result<()> { + pub fn add(&mut self, new_elem: T) -> Result<()> { // Ensure set elements are lexicographically ordered - if let Some(last) = self.inner.last() { - check_der_ordering(last, &item)?; + if let Some(last_elem) = self.inner.last() { + if new_elem.der_cmp(last_elem)? != Ordering::Greater { + return Err(ErrorKind::SetOrdering.into()); + } } - self.inner.push(item); + self.inner.push(new_elem); Ok(()) } @@ -312,6 +265,7 @@ where } #[cfg(feature = "alloc")] +#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] impl<T> AsRef<[T]> for SetOfVec<T> where T: DerOrd, @@ -322,6 +276,7 @@ where } #[cfg(feature = "alloc")] +#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] impl<'a, T> DecodeValue<'a> for SetOfVec<T> where T: Decode<'a> + DerOrd, @@ -342,6 +297,7 @@ where } #[cfg(feature = "alloc")] +#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] impl<'a, T> EncodeValue for SetOfVec<T> where T: 'a + Decode<'a> + Encode + DerOrd, @@ -351,7 +307,7 @@ where .fold(Ok(Length::ZERO), |len, elem| len + elem.encoded_len()?) } - fn encode_value(&self, writer: &mut impl Writer) -> Result<()> { + fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> { for elem in self.iter() { elem.encode(writer)?; } @@ -361,6 +317,7 @@ where } #[cfg(feature = "alloc")] +#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] impl<T> FixedTag for SetOfVec<T> where T: DerOrd, @@ -369,6 +326,7 @@ where } #[cfg(feature = "alloc")] +#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] impl<T> From<SetOfVec<T>> for Vec<T> where T: DerOrd, @@ -379,6 +337,7 @@ where } #[cfg(feature = "alloc")] +#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] impl<T> TryFrom<Vec<T>> for SetOfVec<T> where T: DerOrd, @@ -386,12 +345,14 @@ where type Error = Error; fn try_from(mut vec: Vec<T>) -> Result<SetOfVec<T>> { + // TODO(tarcieri): use `[T]::sort_by` here? der_sort(vec.as_mut_slice())?; Ok(SetOfVec { inner: vec }) } } #[cfg(feature = "alloc")] +#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] impl<T, const N: usize> TryFrom<[T; N]> for SetOfVec<T> where T: DerOrd, @@ -404,6 +365,7 @@ where } #[cfg(feature = "alloc")] +#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] impl<T> ValueOrd for SetOfVec<T> where T: DerOrd, @@ -413,35 +375,6 @@ where } } -// Implement by hand because the derive would create invalid values. -// Use the conversion from Vec to create a valid value. -#[cfg(feature = "arbitrary")] -impl<'a, T> arbitrary::Arbitrary<'a> for SetOfVec<T> -where - T: DerOrd + arbitrary::Arbitrary<'a>, -{ - fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> { - Self::try_from( - u.arbitrary_iter()? - .collect::<std::result::Result<Vec<_>, _>>()?, - ) - .map_err(|_| arbitrary::Error::IncorrectFormat) - } - - fn size_hint(_depth: usize) -> (usize, Option<usize>) { - (0, None) - } -} - -/// Ensure set elements are lexicographically ordered using [`DerOrd`]. -fn check_der_ordering<T: DerOrd>(a: &T, b: &T) -> Result<()> { - match a.der_cmp(b)? { - Ordering::Less => Ok(()), - Ordering::Equal => Err(ErrorKind::SetDuplicate.into()), - Ordering::Greater => Err(ErrorKind::SetOrdering.into()), - } -} - /// Sort a mut slice according to its [`DerOrd`], returning any errors which /// might occur during the comparison. /// @@ -456,15 +389,9 @@ fn der_sort<T: DerOrd>(slice: &mut [T]) -> Result<()> { for i in 0..slice.len() { let mut j = i; - while j > 0 { - match slice[j - 1].der_cmp(&slice[j])? { - Ordering::Less => break, - Ordering::Equal => return Err(ErrorKind::SetDuplicate.into()), - Ordering::Greater => { - slice.swap(j - 1, j); - j -= 1; - } - } + while j > 0 && slice[j - 1].der_cmp(&slice[j])? == Ordering::Greater { + slice.swap(j - 1, j); + j -= 1; } } @@ -492,29 +419,22 @@ fn validate<T: DerOrd>(slice: &[T]) -> Result<()> { Ok(()) } -#[cfg(test)] +#[cfg(all(test, feature = "alloc"))] mod tests { - use super::SetOf; - #[cfg(feature = "alloc")] - use super::SetOfVec; - use crate::ErrorKind; + use super::{SetOf, SetOfVec}; + use alloc::vec::Vec; #[test] fn setof_tryfrom_array() { let arr = [3u16, 2, 1, 65535, 0]; let set = SetOf::try_from(arr).unwrap(); - assert!(set.iter().copied().eq([0, 1, 2, 3, 65535])); + assert_eq!( + set.iter().cloned().collect::<Vec<u16>>(), + &[0, 1, 2, 3, 65535] + ); } #[test] - fn setof_tryfrom_array_reject_duplicates() { - let arr = [1u16, 1]; - let err = SetOf::try_from(arr).err().unwrap(); - assert_eq!(err.kind(), ErrorKind::SetDuplicate); - } - - #[cfg(feature = "alloc")] - #[test] fn setofvec_tryfrom_array() { let arr = [3u16, 2, 1, 65535, 0]; let set = SetOfVec::try_from(arr).unwrap(); @@ -528,12 +448,4 @@ mod tests { let set = SetOfVec::try_from(vec).unwrap(); assert_eq!(set.as_ref(), &[0, 1, 2, 3, 65535]); } - - #[cfg(feature = "alloc")] - #[test] - fn setofvec_tryfrom_vec_reject_duplicates() { - let vec = vec![1u16, 1]; - let err = SetOfVec::try_from(vec).err().unwrap(); - assert_eq!(err.kind(), ErrorKind::SetDuplicate); - } } diff --git a/src/asn1/teletex_string.rs b/src/asn1/teletex_string.rs index 337c071..7d6621d 100644 --- a/src/asn1/teletex_string.rs +++ b/src/asn1/teletex_string.rs @@ -1,26 +1,10 @@ //! ASN.1 `TeletexString` support. -//! -use crate::{asn1::AnyRef, FixedTag, Result, StrRef, Tag}; -use core::{fmt, ops::Deref}; - -macro_rules! impl_teletex_string { - ($type: ty) => { - impl_teletex_string!($type,); - }; - ($type: ty, $($li: lifetime)?) => { - impl_string_type!($type, $($li),*); - - impl<$($li),*> FixedTag for $type { - const TAG: Tag = Tag::TeletexString; - } - impl<$($li),*> fmt::Debug for $type { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "TeletexString({:?})", self.as_str()) - } - } - }; -} +use crate::{ + asn1::AnyRef, ord::OrdIsValueOrd, ByteSlice, DecodeValue, EncodeValue, Error, FixedTag, Header, + Length, Reader, Result, StrSlice, Tag, Writer, +}; +use core::{fmt, ops::Deref, str}; /// ASN.1 `TeletexString` type. /// @@ -41,7 +25,7 @@ macro_rules! impl_teletex_string { #[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord)] pub struct TeletexStringRef<'a> { /// Inner value - inner: StrRef<'a>, + inner: StrSlice<'a>, } impl<'a> TeletexStringRef<'a> { @@ -57,140 +41,83 @@ impl<'a> TeletexStringRef<'a> { return Err(Self::TAG.value_error()); } - StrRef::from_bytes(input) + StrSlice::from_bytes(input) .map(|inner| Self { inner }) .map_err(|_| Self::TAG.value_error()) } } -impl_teletex_string!(TeletexStringRef<'a>, 'a); - impl<'a> Deref for TeletexStringRef<'a> { - type Target = StrRef<'a>; + type Target = StrSlice<'a>; fn deref(&self) -> &Self::Target { &self.inner } } -impl<'a> From<&TeletexStringRef<'a>> for TeletexStringRef<'a> { - fn from(value: &TeletexStringRef<'a>) -> TeletexStringRef<'a> { - *value +impl AsRef<str> for TeletexStringRef<'_> { + fn as_ref(&self) -> &str { + self.as_str() } } -impl<'a> From<TeletexStringRef<'a>> for AnyRef<'a> { - fn from(teletex_string: TeletexStringRef<'a>) -> AnyRef<'a> { - AnyRef::from_tag_and_value(Tag::TeletexString, teletex_string.inner.into()) +impl AsRef<[u8]> for TeletexStringRef<'_> { + fn as_ref(&self) -> &[u8] { + self.as_bytes() } } -#[cfg(feature = "alloc")] -pub use self::allocation::TeletexString; - -#[cfg(feature = "alloc")] -mod allocation { - use super::TeletexStringRef; - - use crate::{ - asn1::AnyRef, - referenced::{OwnedToRef, RefToOwned}, - BytesRef, Error, FixedTag, Result, StrOwned, Tag, - }; - use alloc::string::String; - use core::{fmt, ops::Deref}; - - /// 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: StrOwned, +impl<'a> DecodeValue<'a> for TeletexStringRef<'a> { + fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> { + Self::new(ByteSlice::decode_value(reader, header)?.as_slice()) } +} - impl TeletexString { - /// Create a new ASN.1 `TeletexString`. - pub fn new<T>(input: &T) -> Result<Self> - where - T: AsRef<[u8]> + ?Sized, - { - let input = input.as_ref(); - - TeletexStringRef::new(input)?; +impl<'a> EncodeValue for TeletexStringRef<'a> { + fn value_len(&self) -> Result<Length> { + self.inner.value_len() + } - StrOwned::from_bytes(input) - .map(|inner| Self { inner }) - .map_err(|_| Self::TAG.value_error()) - } + fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> { + self.inner.encode_value(writer) } +} - impl_teletex_string!(TeletexString); +impl FixedTag for TeletexStringRef<'_> { + const TAG: Tag = Tag::TeletexString; +} - impl Deref for TeletexString { - type Target = StrOwned; +impl OrdIsValueOrd for TeletexStringRef<'_> {} - fn deref(&self) -> &Self::Target { - &self.inner - } +impl<'a> From<&TeletexStringRef<'a>> for TeletexStringRef<'a> { + fn from(value: &TeletexStringRef<'a>) -> TeletexStringRef<'a> { + *value } +} - impl<'a> From<TeletexStringRef<'a>> for TeletexString { - fn from(value: TeletexStringRef<'a>) -> TeletexString { - let inner = - StrOwned::from_bytes(value.inner.as_bytes()).expect("Invalid TeletexString"); - Self { inner } - } - } +impl<'a> TryFrom<AnyRef<'a>> for TeletexStringRef<'a> { + type Error = Error; - impl<'a> From<&'a TeletexString> for AnyRef<'a> { - fn from(teletex_string: &'a TeletexString) -> AnyRef<'a> { - AnyRef::from_tag_and_value( - Tag::TeletexString, - BytesRef::new(teletex_string.inner.as_bytes()).expect("Invalid TeletexString"), - ) - } + fn try_from(any: AnyRef<'a>) -> Result<TeletexStringRef<'a>> { + any.decode_into() } +} - impl<'a> RefToOwned<'a> for TeletexStringRef<'a> { - type Owned = TeletexString; - fn ref_to_owned(&self) -> Self::Owned { - TeletexString { - inner: self.inner.ref_to_owned(), - } - } +impl<'a> From<TeletexStringRef<'a>> for AnyRef<'a> { + fn from(teletex_string: TeletexStringRef<'a>) -> AnyRef<'a> { + AnyRef::from_tag_and_value(Tag::TeletexString, teletex_string.inner.into()) } +} - impl OwnedToRef for TeletexString { - type Borrowed<'a> = TeletexStringRef<'a>; - fn owned_to_ref(&self) -> Self::Borrowed<'_> { - TeletexStringRef { - inner: self.inner.owned_to_ref(), - } - } +impl<'a> fmt::Display for TeletexStringRef<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.as_str()) } +} - impl TryFrom<String> for TeletexString { - type Error = Error; - - fn try_from(input: String) -> Result<Self> { - TeletexStringRef::new(&input)?; - - StrOwned::new(input) - .map(|inner| Self { inner }) - .map_err(|_| Self::TAG.value_error()) - } +impl<'a> fmt::Debug for TeletexStringRef<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "TeletexString({:?})", self.as_str()) } } diff --git a/src/asn1/utc_time.rs b/src/asn1/utc_time.rs index 9f2f171..7c23811 100644 --- a/src/asn1/utc_time.rs +++ b/src/asn1/utc_time.rs @@ -1,6 +1,7 @@ //! ASN.1 `UTCTime` support. use crate::{ + asn1::AnyRef, datetime::{self, DateTime}, ord::OrdIsValueOrd, DecodeValue, EncodeValue, Error, ErrorKind, FixedTag, Header, Length, Reader, Result, Tag, @@ -11,6 +12,9 @@ use core::time::Duration; #[cfg(feature = "std")] use std::time::SystemTime; +/// Maximum year that can be represented as a `UTCTime`. +pub const MAX_YEAR: u16 = 2049; + /// ASN.1 `UTCTime` type. /// /// This type implements the validity requirements specified in @@ -25,9 +29,6 @@ use std::time::SystemTime; /// > interpreted as `19YY`; and /// > - Where `YY` is less than 50, the year SHALL be interpreted as `20YY`. /// -/// Note: Due to common operations working on `UNIX_EPOCH` [`UtcTime`]s are -/// only supported for the years 1970-2049. -/// /// [1]: https://tools.ietf.org/html/rfc5280#section-4.1.2.5.1 #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub struct UtcTime(DateTime); @@ -36,12 +37,9 @@ impl UtcTime { /// Length of an RFC 5280-flavored ASN.1 DER-encoded [`UtcTime`]. pub const LENGTH: usize = 13; - /// Maximum year that can be represented as a `UTCTime`. - pub const MAX_YEAR: u16 = 2049; - /// Create a [`UtcTime`] from a [`DateTime`]. pub fn from_date_time(datetime: DateTime) -> Result<Self> { - if datetime.year() <= UtcTime::MAX_YEAR { + if datetime.year() <= MAX_YEAR { Ok(Self(datetime)) } else { Err(Self::TAG.value_error()) @@ -66,6 +64,7 @@ impl UtcTime { /// Instantiate from [`SystemTime`]. #[cfg(feature = "std")] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub fn from_system_time(time: SystemTime) -> Result<Self> { DateTime::try_from(time) .map_err(|_| Self::TAG.value_error())? @@ -74,13 +73,12 @@ impl UtcTime { /// Convert to [`SystemTime`]. #[cfg(feature = "std")] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub fn to_system_time(&self) -> SystemTime { self.0.to_system_time() } } -impl_any_conversions!(UtcTime); - impl<'a> DecodeValue<'a> for UtcTime { fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> { if Self::LENGTH != usize::try_from(header.length)? { @@ -122,7 +120,7 @@ impl EncodeValue for UtcTime { Self::LENGTH.try_into() } - fn encode_value(&self, writer: &mut impl Writer) -> Result<()> { + fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> { let year = match self.0.year() { y @ 1950..=1999 => y.checked_sub(1900), y @ 2000..=2049 => y.checked_sub(2000), @@ -182,43 +180,18 @@ impl TryFrom<&DateTime> for UtcTime { } #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl From<UtcTime> for SystemTime { fn from(utc_time: UtcTime) -> SystemTime { utc_time.to_system_time() } } -// Implement by hand because the derive would create invalid values. -// Use the conversion from DateTime to create a valid value. -// The DateTime type has a way bigger range of valid years than UtcTime, -// so the DateTime year is mapped into a valid range to throw away less inputs. -#[cfg(feature = "arbitrary")] -impl<'a> arbitrary::Arbitrary<'a> for UtcTime { - fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> { - const MIN_YEAR: u16 = 1970; - const VALID_YEAR_COUNT: u16 = UtcTime::MAX_YEAR - MIN_YEAR + 1; - const AVERAGE_SECONDS_IN_YEAR: u64 = 31_556_952; - - let datetime = DateTime::arbitrary(u)?; - let year = datetime.year(); - let duration = datetime.unix_duration(); - - // Clamp the year into a valid range to not throw away too much input - let valid_year = (year.saturating_sub(MIN_YEAR)) - .rem_euclid(VALID_YEAR_COUNT) - .saturating_add(MIN_YEAR); - let year_to_remove = year.saturating_sub(valid_year); - let valid_duration = duration - - Duration::from_secs( - u64::from(year_to_remove).saturating_mul(AVERAGE_SECONDS_IN_YEAR), - ); - - Self::from_date_time(DateTime::from_unix_duration(valid_duration).expect("supported range")) - .map_err(|_| arbitrary::Error::IncorrectFormat) - } - - fn size_hint(depth: usize) -> (usize, Option<usize>) { - DateTime::size_hint(depth) +impl TryFrom<AnyRef<'_>> for UtcTime { + type Error = Error; + + fn try_from(any: AnyRef<'_>) -> Result<UtcTime> { + any.decode_into() } } diff --git a/src/asn1/utf8_string.rs b/src/asn1/utf8_string.rs index 6018750..1a06411 100644 --- a/src/asn1/utf8_string.rs +++ b/src/asn1/utf8_string.rs @@ -1,16 +1,13 @@ //! ASN.1 `UTF8String` support. use crate::{ - asn1::AnyRef, ord::OrdIsValueOrd, EncodeValue, Error, FixedTag, Length, Result, StrRef, Tag, - Writer, + asn1::AnyRef, ord::OrdIsValueOrd, ByteSlice, DecodeValue, EncodeValue, Error, FixedTag, Header, + Length, Reader, Result, StrSlice, Tag, Writer, }; use core::{fmt, ops::Deref, str}; #[cfg(feature = "alloc")] -use { - crate::{DecodeValue, Header, Reader}, - alloc::{borrow::ToOwned, string::String}, -}; +use alloc::{borrow::ToOwned, string::String}; /// ASN.1 `UTF8String` type. /// @@ -29,7 +26,7 @@ use { #[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord)] pub struct Utf8StringRef<'a> { /// Inner value - inner: StrRef<'a>, + inner: StrSlice<'a>, } impl<'a> Utf8StringRef<'a> { @@ -38,33 +35,75 @@ impl<'a> Utf8StringRef<'a> { where T: AsRef<[u8]> + ?Sized, { - StrRef::from_bytes(input.as_ref()).map(|inner| Self { inner }) + StrSlice::from_bytes(input.as_ref()).map(|inner| Self { inner }) } } -impl_string_type!(Utf8StringRef<'a>, 'a); - impl<'a> Deref for Utf8StringRef<'a> { - type Target = StrRef<'a>; + type Target = StrSlice<'a>; fn deref(&self) -> &Self::Target { &self.inner } } +impl AsRef<str> for Utf8StringRef<'_> { + fn as_ref(&self) -> &str { + self.as_str() + } +} + +impl AsRef<[u8]> for Utf8StringRef<'_> { + fn as_ref(&self) -> &[u8] { + self.as_bytes() + } +} + +impl<'a> DecodeValue<'a> for Utf8StringRef<'a> { + fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> { + Self::new(ByteSlice::decode_value(reader, header)?.as_slice()) + } +} + +impl EncodeValue for Utf8StringRef<'_> { + fn value_len(&self) -> Result<Length> { + self.inner.value_len() + } + + fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> { + self.inner.encode_value(writer) + } +} + impl FixedTag for Utf8StringRef<'_> { const TAG: Tag = Tag::Utf8String; } +impl OrdIsValueOrd for Utf8StringRef<'_> {} + impl<'a> From<&Utf8StringRef<'a>> for Utf8StringRef<'a> { fn from(value: &Utf8StringRef<'a>) -> Utf8StringRef<'a> { *value } } +impl<'a> TryFrom<AnyRef<'a>> for Utf8StringRef<'a> { + type Error = Error; + + fn try_from(any: AnyRef<'a>) -> Result<Utf8StringRef<'a>> { + any.decode_into() + } +} + impl<'a> From<Utf8StringRef<'a>> for AnyRef<'a> { - fn from(utf_string: Utf8StringRef<'a>) -> AnyRef<'a> { - AnyRef::from_tag_and_value(Tag::Utf8String, utf_string.inner.into()) + fn from(printable_string: Utf8StringRef<'a>) -> AnyRef<'a> { + AnyRef::from_tag_and_value(Tag::Utf8String, printable_string.inner.into()) + } +} + +impl<'a> fmt::Display for Utf8StringRef<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.as_str()) } } @@ -87,7 +126,7 @@ impl EncodeValue for str { Utf8StringRef::new(self)?.value_len() } - fn encode_value(&self, writer: &mut impl Writer) -> Result<()> { + fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> { Utf8StringRef::new(self)?.encode_value(writer) } } @@ -99,6 +138,7 @@ impl FixedTag for str { impl OrdIsValueOrd for str {} #[cfg(feature = "alloc")] +#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] impl<'a> From<Utf8StringRef<'a>> for String { fn from(s: Utf8StringRef<'a>) -> String { s.as_str().to_owned() @@ -106,6 +146,7 @@ impl<'a> From<Utf8StringRef<'a>> for String { } #[cfg(feature = "alloc")] +#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] impl<'a> TryFrom<AnyRef<'a>> for String { type Error = Error; @@ -115,6 +156,7 @@ impl<'a> TryFrom<AnyRef<'a>> for String { } #[cfg(feature = "alloc")] +#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] impl<'a> DecodeValue<'a> for String { fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> { Ok(String::from_utf8(reader.read_vec(header.length)?)?) @@ -122,22 +164,25 @@ impl<'a> DecodeValue<'a> for String { } #[cfg(feature = "alloc")] +#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] impl EncodeValue for String { fn value_len(&self) -> Result<Length> { Utf8StringRef::new(self)?.value_len() } - fn encode_value(&self, writer: &mut impl Writer) -> Result<()> { + fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> { Utf8StringRef::new(self)?.encode_value(writer) } } #[cfg(feature = "alloc")] +#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] impl FixedTag for String { const TAG: Tag = Tag::Utf8String; } #[cfg(feature = "alloc")] +#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] impl OrdIsValueOrd for String {} #[cfg(test)] diff --git a/src/asn1/videotex_string.rs b/src/asn1/videotex_string.rs index 55b1a49..b758a22 100644 --- a/src/asn1/videotex_string.rs +++ b/src/asn1/videotex_string.rs @@ -1,7 +1,10 @@ //! ASN.1 `VideotexString` support. -use crate::{asn1::AnyRef, FixedTag, Result, StrRef, Tag}; -use core::{fmt, ops::Deref}; +use crate::{ + asn1::AnyRef, ord::OrdIsValueOrd, ByteSlice, DecodeValue, EncodeValue, Error, FixedTag, Header, + Length, Reader, Result, StrSlice, Tag, Writer, +}; +use core::{fmt, ops::Deref, str}; /// ASN.1 `VideotexString` type. /// @@ -20,7 +23,7 @@ use core::{fmt, ops::Deref}; #[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord)] pub struct VideotexStringRef<'a> { /// Inner value - inner: StrRef<'a>, + inner: StrSlice<'a>, } impl<'a> VideotexStringRef<'a> { @@ -37,32 +40,68 @@ impl<'a> VideotexStringRef<'a> { return Err(Self::TAG.value_error()); } - StrRef::from_bytes(input) + StrSlice::from_bytes(input) .map(|inner| Self { inner }) .map_err(|_| Self::TAG.value_error()) } } -impl_string_type!(VideotexStringRef<'a>, 'a); - impl<'a> Deref for VideotexStringRef<'a> { - type Target = StrRef<'a>; + type Target = StrSlice<'a>; fn deref(&self) -> &Self::Target { &self.inner } } +impl AsRef<str> for VideotexStringRef<'_> { + fn as_ref(&self) -> &str { + self.as_str() + } +} + +impl AsRef<[u8]> for VideotexStringRef<'_> { + fn as_ref(&self) -> &[u8] { + self.as_bytes() + } +} + +impl<'a> DecodeValue<'a> for VideotexStringRef<'a> { + fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> { + Self::new(ByteSlice::decode_value(reader, header)?.as_slice()) + } +} + +impl<'a> EncodeValue for VideotexStringRef<'a> { + fn value_len(&self) -> Result<Length> { + self.inner.value_len() + } + + fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> { + self.inner.encode_value(writer) + } +} + impl FixedTag for VideotexStringRef<'_> { const TAG: Tag = Tag::VideotexString; } +impl OrdIsValueOrd for VideotexStringRef<'_> {} + impl<'a> From<&VideotexStringRef<'a>> for VideotexStringRef<'a> { fn from(value: &VideotexStringRef<'a>) -> VideotexStringRef<'a> { *value } } +impl<'a> TryFrom<AnyRef<'a>> for VideotexStringRef<'a> { + type Error = Error; + + fn try_from(any: AnyRef<'a>) -> Result<VideotexStringRef<'a>> { + any.decode_into() + } +} + impl<'a> From<VideotexStringRef<'a>> for AnyRef<'a> { fn from(printable_string: VideotexStringRef<'a>) -> AnyRef<'a> { AnyRef::from_tag_and_value(Tag::VideotexString, printable_string.inner.into()) @@ -75,6 +114,12 @@ impl<'a> From<VideotexStringRef<'a>> for &'a [u8] { } } +impl<'a> fmt::Display for VideotexStringRef<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.as_str()) + } +} + impl<'a> fmt::Debug for VideotexStringRef<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "VideotexString({:?})", self.as_str()) diff --git a/src/byte_slice.rs b/src/byte_slice.rs new file mode 100644 index 0000000..00d46d0 --- /dev/null +++ b/src/byte_slice.rs @@ -0,0 +1,116 @@ +//! Common handling for types backed by byte slices with enforcement of a +//! library-level length limitation i.e. `Length::max()`. + +use crate::{ + str_slice::StrSlice, DecodeValue, DerOrd, EncodeValue, Error, Header, Length, Reader, Result, + Writer, +}; +use core::cmp::Ordering; + +/// 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, + + /// Inner value + inner: &'a [u8], +} + +impl<'a> ByteSlice<'a> { + /// Constant value representing an empty byte slice. + pub const EMPTY: Self = Self { + length: Length::ZERO, + inner: &[], + }; + + /// Create a new [`ByteSlice`], ensuring that the provided `slice` value + /// is shorter than `Length::max()`. + pub fn new(slice: &'a [u8]) -> Result<Self> { + Ok(Self { + length: Length::try_from(slice.len())?, + inner: slice, + }) + } + + /// Borrow the inner byte slice + pub fn as_slice(&self) -> &'a [u8] { + self.inner + } + + /// Get the [`Length`] of this [`ByteSlice`] + pub fn len(self) -> Length { + self.length + } + + /// Is this [`ByteSlice`] empty? + pub fn is_empty(self) -> bool { + self.len() == Length::ZERO + } +} + +impl AsRef<[u8]> for ByteSlice<'_> { + fn as_ref(&self) -> &[u8] { + self.as_slice() + } +} + +impl<'a> DecodeValue<'a> for ByteSlice<'a> { + fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> { + reader.read_slice(header.length).and_then(Self::new) + } +} + +impl EncodeValue for ByteSlice<'_> { + fn value_len(&self) -> Result<Length> { + Ok(self.length) + } + + fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> { + writer.write(self.as_ref()) + } +} + +impl Default for ByteSlice<'_> { + fn default() -> Self { + Self { + length: Length::ZERO, + inner: &[], + } + } +} + +impl DerOrd for ByteSlice<'_> { + fn der_cmp(&self, other: &Self) -> Result<Ordering> { + Ok(self.as_slice().cmp(other.as_slice())) + } +} + +impl<'a> From<&'a [u8; 1]> for ByteSlice<'a> { + fn from(byte: &'a [u8; 1]) -> ByteSlice<'a> { + Self { + length: Length::ONE, + inner: byte, + } + } +} + +impl<'a> From<StrSlice<'a>> for ByteSlice<'a> { + fn from(s: StrSlice<'a>) -> 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; + + fn try_from(slice: &'a [u8]) -> Result<Self> { + Self::new(slice) + } +} diff --git a/src/bytes_owned.rs b/src/bytes_owned.rs deleted file mode 100644 index b5e928e..0000000 --- a/src/bytes_owned.rs +++ /dev/null @@ -1,162 +0,0 @@ -//! Common handling for types backed by byte allocation with enforcement of a -//! library-level length limitation i.e. `Length::max()`. - -use crate::{ - referenced::OwnedToRef, BytesRef, DecodeValue, DerOrd, EncodeValue, Error, Header, Length, - Reader, Result, StrRef, Writer, -}; -use alloc::{boxed::Box, vec::Vec}; -use core::cmp::Ordering; - -/// Byte slice newtype which respects the `Length::max()` limit. -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] -pub(crate) struct BytesOwned { - /// Precomputed `Length` (avoids possible panicking conversions) - length: Length, - - /// Inner value - inner: Box<[u8]>, -} - -impl BytesOwned { - /// Create a new [`BytesOwned`], ensuring that the provided `slice` value - /// is shorter than `Length::max()`. - pub fn new(data: impl Into<Box<[u8]>>) -> Result<Self> { - 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 [`BytesRef`] - pub fn len(&self) -> Length { - self.length - } - - /// Is this [`BytesOwned`] empty? - pub fn is_empty(&self) -> bool { - self.len() == Length::ZERO - } -} - -impl AsRef<[u8]> for BytesOwned { - fn as_ref(&self) -> &[u8] { - self.as_slice() - } -} - -impl<'a> DecodeValue<'a> for BytesOwned { - fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> { - reader.read_vec(header.length).and_then(Self::new) - } -} - -impl EncodeValue for BytesOwned { - fn value_len(&self) -> Result<Length> { - Ok(self.length) - } - - fn encode_value(&self, writer: &mut impl Writer) -> Result<()> { - writer.write(self.as_ref()) - } -} - -impl Default for BytesOwned { - fn default() -> Self { - Self { - length: Length::ZERO, - inner: Box::new([]), - } - } -} - -impl DerOrd for BytesOwned { - fn der_cmp(&self, other: &Self) -> Result<Ordering> { - Ok(self.as_slice().cmp(other.as_slice())) - } -} - -impl From<BytesOwned> for Box<[u8]> { - fn from(bytes: BytesOwned) -> Box<[u8]> { - bytes.inner - } -} - -impl From<StrRef<'_>> for BytesOwned { - fn from(s: StrRef<'_>) -> BytesOwned { - let bytes = s.as_bytes(); - debug_assert_eq!(bytes.len(), usize::try_from(s.length).expect("overflow")); - - 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 From<BytesRef<'_>> for BytesOwned { - fn from(s: BytesRef<'_>) -> BytesOwned { - BytesOwned { - length: s.length, - inner: Box::from(s.inner), - } - } -} - -impl TryFrom<&[u8]> for BytesOwned { - type Error = Error; - - fn try_from(bytes: &[u8]) -> Result<Self> { - Self::new(bytes) - } -} - -impl TryFrom<Box<[u8]>> for BytesOwned { - type Error = Error; - - fn try_from(bytes: Box<[u8]>) -> Result<Self> { - Self::new(bytes) - } -} - -impl TryFrom<Vec<u8>> for BytesOwned { - type Error = Error; - - fn try_from(bytes: Vec<u8>) -> Result<Self> { - Self::new(bytes) - } -} - -// Implement by hand because the derive would create invalid values. -// Make sure the length and the inner.len matches. -#[cfg(feature = "arbitrary")] -impl<'a> arbitrary::Arbitrary<'a> for BytesOwned { - fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> { - let length = u.arbitrary()?; - Ok(Self { - length, - inner: Box::from(u.bytes(u32::from(length) as usize)?), - }) - } - - fn size_hint(depth: usize) -> (usize, Option<usize>) { - arbitrary::size_hint::and(Length::size_hint(depth), (0, None)) - } -} diff --git a/src/bytes_ref.rs b/src/bytes_ref.rs deleted file mode 100644 index 2cee407..0000000 --- a/src/bytes_ref.rs +++ /dev/null @@ -1,152 +0,0 @@ -//! 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, Header, Length, Reader, Result, StrRef, Writer, -}; -use core::cmp::Ordering; - -#[cfg(feature = "alloc")] -use crate::StrOwned; - -/// Byte slice newtype which respects the `Length::max()` limit. -#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] -pub(crate) struct BytesRef<'a> { - /// Precomputed `Length` (avoids possible panicking conversions) - pub length: Length, - - /// Inner value - pub inner: &'a [u8], -} - -impl<'a> BytesRef<'a> { - /// Constant value representing an empty byte slice. - pub const EMPTY: Self = Self { - length: Length::ZERO, - inner: &[], - }; - - /// Create a new [`BytesRef`], ensuring that the provided `slice` value - /// is shorter than `Length::max()`. - pub fn new(slice: &'a [u8]) -> Result<Self> { - Ok(Self { - length: Length::try_from(slice.len())?, - inner: slice, - }) - } - - /// Borrow the inner byte slice - pub fn as_slice(&self) -> &'a [u8] { - self.inner - } - - /// Get the [`Length`] of this [`BytesRef`] - pub fn len(self) -> Length { - self.length - } - - /// Is this [`BytesRef`] empty? - pub fn is_empty(self) -> bool { - self.len() == Length::ZERO - } -} - -impl AsRef<[u8]> for BytesRef<'_> { - fn as_ref(&self) -> &[u8] { - self.as_slice() - } -} - -impl<'a> DecodeValue<'a> for BytesRef<'a> { - fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> { - reader.read_slice(header.length).and_then(Self::new) - } -} - -impl EncodeValue for BytesRef<'_> { - fn value_len(&self) -> Result<Length> { - Ok(self.length) - } - - fn encode_value(&self, writer: &mut impl Writer) -> Result<()> { - writer.write(self.as_ref()) - } -} - -impl Default for BytesRef<'_> { - fn default() -> Self { - Self { - length: Length::ZERO, - inner: &[], - } - } -} - -impl DerOrd for BytesRef<'_> { - fn der_cmp(&self, other: &Self) -> Result<Ordering> { - Ok(self.as_slice().cmp(other.as_slice())) - } -} - -impl<'a> From<StrRef<'a>> for BytesRef<'a> { - fn from(s: StrRef<'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 StrOwned> for BytesRef<'a> { - fn from(s: &'a StrOwned) -> 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> { - type Error = Error; - - fn try_from(slice: &'a [u8]) -> Result<Self> { - Self::new(slice) - } -} - -// Implement by hand because the derive would create invalid values. -// Make sure the length and the inner.len matches. -#[cfg(feature = "arbitrary")] -impl<'a> arbitrary::Arbitrary<'a> for BytesRef<'a> { - fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> { - let length = u.arbitrary()?; - Ok(Self { - length, - inner: u.bytes(u32::from(length) as usize)?, - }) - } - - fn size_hint(depth: usize) -> (usize, Option<usize>) { - arbitrary::size_hint::and(Length::size_hint(depth), (0, None)) - } -} - -#[cfg(feature = "alloc")] -mod allocating { - use super::BytesRef; - use crate::{referenced::RefToOwned, BytesOwned}; - - impl<'a> RefToOwned<'a> for BytesRef<'a> { - type Owned = BytesOwned; - fn ref_to_owned(&self) -> Self::Owned { - BytesOwned::from(*self) - } - } -} diff --git a/src/datetime.rs b/src/datetime.rs index fd09b68..2b4c504 100644 --- a/src/datetime.rs +++ b/src/datetime.rs @@ -56,18 +56,6 @@ pub struct DateTime { } impl DateTime { - /// This is the maximum date represented by the [`DateTime`] - /// This corresponds to: 9999-12-31T23:59:59Z - pub const INFINITY: DateTime = DateTime { - year: 9999, - month: 12, - day: 31, - hour: 23, - minutes: 59, - seconds: 59, - unix_duration: MAX_UNIX_DURATION, - }; - /// Create a new [`DateTime`] from the given UTC time components. // TODO(tarcieri): checked arithmetic #[allow(clippy::integer_arithmetic)] @@ -252,6 +240,7 @@ impl DateTime { /// Instantiate from [`SystemTime`]. #[cfg(feature = "std")] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub fn from_system_time(time: SystemTime) -> Result<Self> { time.duration_since(UNIX_EPOCH) .map_err(|_| ErrorKind::DateTime.into()) @@ -260,6 +249,7 @@ impl DateTime { /// Convert to [`SystemTime`]. #[cfg(feature = "std")] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub fn to_system_time(&self) -> SystemTime { UNIX_EPOCH + self.unix_duration() } @@ -268,12 +258,19 @@ impl DateTime { impl FromStr for DateTime { type Err = Error; + // TODO(tarcieri): checked arithmetic + #[allow(clippy::integer_arithmetic)] fn from_str(s: &str) -> Result<Self> { match *s.as_bytes() { [year1, year2, year3, year4, b'-', month1, month2, b'-', day1, day2, b'T', hour1, hour2, b':', min1, min2, b':', sec1, sec2, b'Z'] => { let tag = Tag::GeneralizedTime; - let year = decode_year(&[year1, year2, year3, year4])?; + let year = + u16::from(decode_decimal(tag, year1, year2).map_err(|_| ErrorKind::DateTime)?) + * 100 + + u16::from( + decode_decimal(tag, year3, year4).map_err(|_| ErrorKind::DateTime)?, + ); let month = decode_decimal(tag, month1, month2).map_err(|_| ErrorKind::DateTime)?; let day = decode_decimal(tag, day1, day2).map_err(|_| ErrorKind::DateTime)?; let hour = decode_decimal(tag, hour1, hour2).map_err(|_| ErrorKind::DateTime)?; @@ -297,6 +294,7 @@ impl fmt::Display for DateTime { } #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl From<DateTime> for SystemTime { fn from(time: DateTime) -> SystemTime { time.to_system_time() @@ -304,6 +302,7 @@ impl From<DateTime> for SystemTime { } #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl From<&DateTime> for SystemTime { fn from(time: &DateTime) -> SystemTime { time.to_system_time() @@ -311,6 +310,7 @@ impl From<&DateTime> for SystemTime { } #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl TryFrom<SystemTime> for DateTime { type Error = Error; @@ -320,6 +320,7 @@ impl TryFrom<SystemTime> for DateTime { } #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl TryFrom<&SystemTime> for DateTime { type Error = Error; @@ -329,11 +330,12 @@ impl TryFrom<&SystemTime> for DateTime { } #[cfg(feature = "time")] +#[cfg_attr(docsrs, doc(cfg(feature = "time")))] impl TryFrom<DateTime> for PrimitiveDateTime { type Error = Error; fn try_from(time: DateTime) -> Result<PrimitiveDateTime> { - let month = time.month().try_into()?; + let month = (time.month() as u8).try_into()?; let date = time::Date::from_calendar_date(i32::from(time.year()), month, time.day())?; let time = time::Time::from_hms(time.hour(), time.minutes(), time.seconds())?; @@ -342,6 +344,7 @@ impl TryFrom<DateTime> for PrimitiveDateTime { } #[cfg(feature = "time")] +#[cfg_attr(docsrs, doc(cfg(feature = "time")))] impl TryFrom<PrimitiveDateTime> for DateTime { type Error = Error; @@ -357,28 +360,11 @@ impl TryFrom<PrimitiveDateTime> for DateTime { } } -// Implement by hand because the derive would create invalid values. -// Use the conversion from Duration to create a valid value. -#[cfg(feature = "arbitrary")] -impl<'a> arbitrary::Arbitrary<'a> for DateTime { - fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> { - Self::from_unix_duration(Duration::new( - u.int_in_range(0..=MAX_UNIX_DURATION.as_secs().saturating_sub(1))?, - u.int_in_range(0..=999_999_999)?, - )) - .map_err(|_| arbitrary::Error::IncorrectFormat) - } - - fn size_hint(depth: usize) -> (usize, Option<usize>) { - arbitrary::size_hint::and(u64::size_hint(depth), u32::size_hint(depth)) - } -} - /// Decode 2-digit decimal value // TODO(tarcieri): checked arithmetic #[allow(clippy::integer_arithmetic)] pub(crate) fn decode_decimal(tag: Tag, hi: u8, lo: u8) -> Result<u8> { - if hi.is_ascii_digit() && lo.is_ascii_digit() { + if (b'0'..=b'9').contains(&hi) && (b'0'..=b'9').contains(&lo) { Ok((hi - b'0') * 10 + (lo - b'0')) } else { Err(tag.value_error()) @@ -400,16 +386,6 @@ where writer.write_byte(b'0'.checked_add(value % 10).ok_or(ErrorKind::Overflow)?) } -/// Decode 4-digit year. -// TODO(tarcieri): checked arithmetic -#[allow(clippy::integer_arithmetic)] -fn decode_year(year: &[u8; 4]) -> Result<u16> { - let tag = Tag::GeneralizedTime; - let hi = decode_decimal(tag, year[0], year[1]).map_err(|_| ErrorKind::DateTime)?; - let lo = decode_decimal(tag, year[2], year[3]).map_err(|_| ErrorKind::DateTime)?; - Ok(u16::from(hi) * 100 + u16::from(lo)) -} - #[cfg(test)] mod tests { use super::DateTime; diff --git a/src/decode.rs b/src/decode.rs index fe53341..1c63b32 100644 --- a/src/decode.rs +++ b/src/decode.rs @@ -1,7 +1,6 @@ //! Trait definition for [`Decode`]. use crate::{FixedTag, Header, Reader, Result, SliceReader}; -use core::marker::PhantomData; #[cfg(feature = "pem")] use crate::{pem::PemLabel, PemReader}; @@ -9,9 +8,6 @@ use crate::{pem::PemLabel, PemReader}; #[cfg(doc)] use crate::{Length, Tag}; -#[cfg(feature = "alloc")] -use alloc::boxed::Box; - /// Decoding trait. /// /// This trait provides the core abstraction upon which all decoding operations @@ -39,17 +35,6 @@ where } } -/// Dummy implementation for [`PhantomData`] which allows deriving -/// implementations on structs with phantom fields. -impl<'a, T> Decode<'a> for PhantomData<T> -where - T: ?Sized, -{ - fn decode<R: Reader<'a>>(_reader: &mut R) -> Result<PhantomData<T>> { - Ok(PhantomData) - } -} - /// Marker trait for data structures that can be decoded from DER without /// borrowing any data from the decoder. /// @@ -67,12 +52,14 @@ impl<T> DecodeOwned for T where T: for<'a> Decode<'a> {} /// This trait is automatically impl'd for any type which impls both /// [`DecodeOwned`] and [`PemLabel`]. #[cfg(feature = "pem")] +#[cfg_attr(docsrs, doc(cfg(feature = "pem")))] pub trait DecodePem: DecodeOwned + PemLabel { /// Try to decode this type from PEM. fn from_pem(pem: impl AsRef<[u8]>) -> Result<Self>; } #[cfg(feature = "pem")] +#[cfg_attr(docsrs, doc(cfg(feature = "pem")))] impl<T: DecodeOwned + PemLabel> DecodePem for T { fn from_pem(pem: impl AsRef<[u8]>) -> Result<Self> { let mut reader = PemReader::new(pem.as_ref())?; @@ -87,13 +74,3 @@ pub trait DecodeValue<'a>: Sized { /// Attempt to decode this message using the provided [`Reader`]. fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self>; } - -#[cfg(feature = "alloc")] -impl<'a, T> DecodeValue<'a> for Box<T> -where - T: DecodeValue<'a>, -{ - fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> { - Ok(Box::new(T::decode_value(reader, header)?)) - } -} diff --git a/src/document.rs b/src/document.rs index 78355a6..aa953cd 100644 --- a/src/document.rs +++ b/src/document.rs @@ -27,6 +27,7 @@ use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing}; /// /// The [`SecretDocument`] provides a wrapper for this type with additional /// hardening applied. +#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] #[derive(Clone, Eq, PartialEq)] pub struct Document { /// ASN.1 DER encoded bytes. @@ -44,6 +45,7 @@ impl Document { /// Convert to a [`SecretDocument`]. #[cfg(feature = "zeroize")] + #[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] pub fn into_secret(self) -> SecretDocument { SecretDocument(self) } @@ -72,13 +74,14 @@ impl Document { /// Encode the provided type as ASN.1 DER, storing the resulting encoded DER /// as a [`Document`]. pub fn encode_msg<T: Encode>(msg: &T) -> Result<Self> { - msg.to_der()?.try_into() + msg.to_vec()?.try_into() } /// Decode ASN.1 DER document from PEM. /// /// Returns the PEM label and decoded [`Document`] on success. #[cfg(feature = "pem")] + #[cfg_attr(docsrs, doc(cfg(feature = "pem")))] pub fn from_pem(pem: &str) -> Result<(&str, Self)> { let (label, der_bytes) = pem::decode_vec(pem.as_bytes())?; Ok((label, der_bytes.try_into()?)) @@ -87,30 +90,35 @@ impl Document { /// Encode ASN.1 DER document as a PEM string with encapsulation boundaries /// containing the provided PEM type `label` (e.g. `CERTIFICATE`). #[cfg(feature = "pem")] + #[cfg_attr(docsrs, doc(cfg(feature = "pem")))] pub fn to_pem(&self, label: &'static str, line_ending: pem::LineEnding) -> Result<String> { Ok(pem::encode_string(label, line_ending, self.as_bytes())?) } /// Read ASN.1 DER document from a file. #[cfg(feature = "std")] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub fn read_der_file(path: impl AsRef<Path>) -> Result<Self> { fs::read(path)?.try_into() } /// Write ASN.1 DER document to a file. #[cfg(feature = "std")] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub fn write_der_file(&self, path: impl AsRef<Path>) -> Result<()> { Ok(fs::write(path, self.as_bytes())?) } /// Read PEM-encoded ASN.1 DER document from a file. #[cfg(all(feature = "pem", feature = "std"))] + #[cfg_attr(docsrs, doc(cfg(all(feature = "pem", feature = "std"))))] pub fn read_pem_file(path: impl AsRef<Path>) -> Result<(String, Self)> { Self::from_pem(&fs::read_to_string(path)?).map(|(label, doc)| (label.to_owned(), doc)) } /// Write PEM-encoded ASN.1 DER document to a file. #[cfg(all(feature = "pem", feature = "std"))] + #[cfg_attr(docsrs, doc(cfg(all(feature = "pem", feature = "std"))))] pub fn write_pem_file( &self, path: impl AsRef<Path>, @@ -158,7 +166,7 @@ impl Encode for Document { Ok(self.len()) } - fn encode(&self, writer: &mut impl Writer) -> Result<()> { + fn encode(&self, writer: &mut dyn Writer) -> Result<()> { writer.write(self.as_bytes()) } } @@ -197,6 +205,7 @@ impl TryFrom<Vec<u8>> for Document { /// are zeroized-on-drop, and also using more restrictive file permissions when /// writing files to disk. #[cfg(feature = "zeroize")] +#[cfg_attr(docsrs, doc(cfg(all(feature = "alloc", feature = "zeroize"))))] #[derive(Clone)] pub struct SecretDocument(Document); @@ -229,12 +238,14 @@ impl SecretDocument { /// Decode ASN.1 DER document from PEM. #[cfg(feature = "pem")] + #[cfg_attr(docsrs, doc(cfg(feature = "pem")))] pub fn from_pem(pem: &str) -> Result<(&str, Self)> { Document::from_pem(pem).map(|(label, doc)| (label, Self(doc))) } /// Encode ASN.1 DER document as a PEM string. #[cfg(feature = "pem")] + #[cfg_attr(docsrs, doc(cfg(feature = "pem")))] pub fn to_pem( &self, label: &'static str, @@ -245,24 +256,28 @@ impl SecretDocument { /// Read ASN.1 DER document from a file. #[cfg(feature = "std")] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub fn read_der_file(path: impl AsRef<Path>) -> Result<Self> { Document::read_der_file(path).map(Self) } /// Write ASN.1 DER document to a file. #[cfg(feature = "std")] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub fn write_der_file(&self, path: impl AsRef<Path>) -> Result<()> { write_secret_file(path, self.as_bytes()) } /// Read PEM-encoded ASN.1 DER document from a file. #[cfg(all(feature = "pem", feature = "std"))] + #[cfg_attr(docsrs, doc(cfg(all(feature = "pem", feature = "std"))))] pub fn read_pem_file(path: impl AsRef<Path>) -> Result<(String, Self)> { Document::read_pem_file(path).map(|(label, doc)| (label, Self(doc))) } /// Write PEM-encoded ASN.1 DER document to a file. #[cfg(all(feature = "pem", feature = "std"))] + #[cfg_attr(docsrs, doc(cfg(all(feature = "pem", feature = "std"))))] pub fn write_pem_file( &self, path: impl AsRef<Path>, diff --git a/src/encode.rs b/src/encode.rs index 28d7cba..51fc13d 100644 --- a/src/encode.rs +++ b/src/encode.rs @@ -1,10 +1,9 @@ //! Trait definition for [`Encode`]. use crate::{Header, Length, Result, SliceWriter, Tagged, Writer}; -use core::marker::PhantomData; #[cfg(feature = "alloc")] -use {alloc::boxed::Box, alloc::vec::Vec, core::iter}; +use {alloc::vec::Vec, core::iter}; #[cfg(feature = "pem")] use { @@ -25,7 +24,7 @@ pub trait Encode { fn encoded_len(&self) -> Result<Length>; /// Encode this value as ASN.1 DER using the provided [`Writer`]. - fn encode(&self, encoder: &mut impl Writer) -> Result<()>; + fn encode(&self, encoder: &mut dyn Writer) -> Result<()>; /// Encode this value to the provided byte slice, returning a sub-slice /// containing the encoded message. @@ -38,6 +37,7 @@ pub trait Encode { /// Encode this message as ASN.1 DER, appending it to the provided /// byte vector. #[cfg(feature = "alloc")] + #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] fn encode_to_vec(&self, buf: &mut Vec<u8>) -> Result<Length> { let expected_len = usize::try_from(self.encoded_len()?)?; buf.reserve(expected_len); @@ -58,9 +58,10 @@ pub trait Encode { actual_len.try_into() } - /// Encode this type as DER, returning a byte vector. + /// Serialize this message as a byte vector. #[cfg(feature = "alloc")] - fn to_der(&self) -> Result<Vec<u8>> { + #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] + fn to_vec(&self) -> Result<Vec<u8>> { let mut buf = Vec::new(); self.encode_to_vec(&mut buf)?; Ok(buf) @@ -77,38 +78,25 @@ where } /// Encode this value as ASN.1 DER using the provided [`Writer`]. - fn encode(&self, writer: &mut impl Writer) -> Result<()> { + fn encode(&self, writer: &mut dyn Writer) -> Result<()> { self.header()?.encode(writer)?; self.encode_value(writer) } } -/// Dummy implementation for [`PhantomData`] which allows deriving -/// implementations on structs with phantom fields. -impl<T> Encode for PhantomData<T> -where - T: ?Sized, -{ - fn encoded_len(&self) -> Result<Length> { - Ok(Length::ZERO) - } - - fn encode(&self, _writer: &mut impl Writer) -> Result<()> { - Ok(()) - } -} - /// PEM encoding trait. /// /// This trait is automatically impl'd for any type which impls both /// [`Encode`] and [`PemLabel`]. #[cfg(feature = "pem")] +#[cfg_attr(docsrs, doc(cfg(feature = "pem")))] pub trait EncodePem: Encode + PemLabel { /// Try to encode this type as PEM. fn to_pem(&self, line_ending: LineEnding) -> Result<String>; } #[cfg(feature = "pem")] +#[cfg_attr(docsrs, doc(cfg(feature = "pem")))] impl<T: Encode + PemLabel> EncodePem for T { fn to_pem(&self, line_ending: LineEnding) -> Result<String> { let der_len = usize::try_from(self.encoded_len()?)?; @@ -141,18 +129,5 @@ pub trait EncodeValue { /// Encode value (sans [`Tag`]+[`Length`] header) as ASN.1 DER using the /// provided [`Writer`]. - fn encode_value(&self, encoder: &mut impl Writer) -> Result<()>; -} - -#[cfg(feature = "alloc")] -impl<T> EncodeValue for Box<T> -where - T: EncodeValue, -{ - fn value_len(&self) -> Result<Length> { - T::value_len(self) - } - fn encode_value(&self, writer: &mut impl Writer) -> Result<()> { - T::encode_value(self, writer) - } + fn encode_value(&self, encoder: &mut dyn Writer) -> Result<()>; } diff --git a/src/encode_ref.rs b/src/encode_ref.rs index 8a60a93..c1e4f03 100644 --- a/src/encode_ref.rs +++ b/src/encode_ref.rs @@ -22,7 +22,7 @@ where self.0.encoded_len() } - fn encode(&self, writer: &mut impl Writer) -> Result<()> { + fn encode(&self, writer: &mut dyn Writer) -> Result<()> { self.0.encode(writer) } } @@ -47,7 +47,7 @@ where self.0.value_len() } - fn encode_value(&self, writer: &mut impl Writer) -> Result<()> { + fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> { self.0.encode_value(writer) } } diff --git a/src/error.rs b/src/error.rs index 902863d..5e492a4 100644 --- a/src/error.rs +++ b/src/error.rs @@ -174,6 +174,7 @@ pub enum ErrorKind { /// File not found error. #[cfg(feature = "std")] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] FileNotFound, /// Message is incomplete and does not contain all of the expected data. @@ -193,11 +194,9 @@ pub enum ErrorKind { /// I/O errors. #[cfg(feature = "std")] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] Io(std::io::ErrorKind), - /// Indefinite length disallowed. - IndefiniteLength, - /// Incorrect length for a given field. Length { /// Tag of the value being decoded. @@ -222,14 +221,12 @@ pub enum ErrorKind { /// to determine which OID(s) are causing the error (and then potentially /// contribute upstream support for algorithms they care about). #[cfg(feature = "oid")] + #[cfg_attr(docsrs, doc(cfg(feature = "oid")))] OidUnknown { /// OID value that was unrecognized by a parser for a DER-based format. oid: ObjectIdentifier, }, - /// `SET` cannot contain duplicates. - SetDuplicate, - /// `SET` ordering error: items not in canonical order. SetOrdering, @@ -241,10 +238,12 @@ pub enum ErrorKind { /// PEM encoding errors. #[cfg(feature = "pem")] + #[cfg_attr(docsrs, doc(cfg(feature = "pem")))] Pem(pem::Error), /// Permission denied reading file. #[cfg(feature = "std")] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] PermissionDenied, /// Reader does not support the requested operation. @@ -322,7 +321,6 @@ impl fmt::Display for ErrorKind { ), #[cfg(feature = "std")] ErrorKind::Io(err) => write!(f, "I/O error: {:?}", err), - ErrorKind::IndefiniteLength => write!(f, "indefinite length disallowed"), ErrorKind::Length { tag } => write!(f, "incorrect length for {}", tag), ErrorKind::Noncanonical { tag } => { write!(f, "ASN.1 {} not canonically encoded as DER", tag) @@ -332,7 +330,6 @@ impl fmt::Display for ErrorKind { ErrorKind::OidUnknown { oid } => { write!(f, "unknown/unsupported OID: {}", oid) } - ErrorKind::SetDuplicate => write!(f, "SET OF contains duplicate"), ErrorKind::SetOrdering => write!(f, "SET OF ordering error"), ErrorKind::Overflow => write!(f, "integer overflow"), ErrorKind::Overlength => write!(f, "ASN.1 DER message is too long"), diff --git a/src/header.rs b/src/header.rs index ad30381..ddb484e 100644 --- a/src/header.rs +++ b/src/header.rs @@ -44,7 +44,7 @@ impl Encode for Header { self.tag.encoded_len()? + self.length.encoded_len()? } - fn encode(&self, writer: &mut impl Writer) -> Result<()> { + fn encode(&self, writer: &mut dyn Writer) -> Result<()> { self.tag.encode(writer)?; self.length.encode(writer) } diff --git a/src/length.rs b/src/length.rs index d183a69..76ee0e9 100644 --- a/src/length.rs +++ b/src/length.rs @@ -14,13 +14,6 @@ const MAX_DER_OCTETS: usize = 5; /// Maximum length as a `u32` (256 MiB). const MAX_U32: u32 = 0xfff_ffff; -/// Octet identifying an indefinite length as described in X.690 Section -/// 8.1.3.6.1: -/// -/// > The single octet shall have bit 8 set to one, and bits 7 to -/// > 1 set to zero. -const INDEFINITE_LENGTH_OCTET: u8 = 0b10000000; // 0x80 - /// ASN.1-encoded length. /// /// Maximum length is defined by the [`Length::MAX`] constant (256 MiB). @@ -211,8 +204,7 @@ impl<'a> Decode<'a> for Length { match reader.read_byte()? { // Note: per X.690 Section 8.1.3.6.1 the byte 0x80 encodes indefinite // lengths, which are not allowed in DER, so disallow that byte. - len if len < INDEFINITE_LENGTH_OCTET => Ok(len.into()), - INDEFINITE_LENGTH_OCTET => Err(ErrorKind::IndefiniteLength.into()), + len if len < 0x80 => Ok(len.into()), // 1-4 byte variable-sized length prefix tag @ 0x81..=0x84 => { let nbytes = tag.checked_sub(0x80).ok_or(ErrorKind::Overlength)? as usize; @@ -254,7 +246,7 @@ impl Encode for Length { } } - fn encode(&self, writer: &mut impl Writer) -> Result<()> { + fn encode(&self, writer: &mut dyn Writer) -> Result<()> { match self.initial_octet() { Some(tag_byte) => { writer.write_byte(tag_byte)?; @@ -294,126 +286,9 @@ impl fmt::Display for Length { } } -// Implement by hand because the derive would create invalid values. -// Generate a u32 with a valid range. -#[cfg(feature = "arbitrary")] -impl<'a> arbitrary::Arbitrary<'a> for Length { - fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> { - Ok(Self(u.int_in_range(0..=MAX_U32)?)) - } - - fn size_hint(depth: usize) -> (usize, Option<usize>) { - u32::size_hint(depth) - } -} - -/// Length type with support for indefinite lengths as used by ASN.1 BER, -/// as described in X.690 Section 8.1.3.6: -/// -/// > 8.1.3.6 For the indefinite form, the length octets indicate that the -/// > contents octets are terminated by end-of-contents -/// > octets (see 8.1.5), and shall consist of a single octet. -/// > -/// > 8.1.3.6.1 The single octet shall have bit 8 set to one, and bits 7 to -/// > 1 set to zero. -/// > -/// > 8.1.3.6.2 If this form of length is used, then end-of-contents octets -/// > (see 8.1.5) shall be present in the encoding following the contents -/// > octets. -/// -/// Indefinite lengths are non-canonical and therefore invalid DER, however -/// there are interoperability corner cases where we have little choice but to -/// tolerate some BER productions where this is helpful. -#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] -pub struct IndefiniteLength(Option<Length>); - -impl IndefiniteLength { - /// Length of `0`. - pub const ZERO: Self = Self(Some(Length::ZERO)); - - /// Length of `1`. - pub const ONE: Self = Self(Some(Length::ONE)); - - /// Indefinite length. - pub const INDEFINITE: Self = Self(None); -} - -impl IndefiniteLength { - /// Create a definite length from a type which can be converted into a - /// `Length`. - pub fn new(length: impl Into<Length>) -> Self { - Self(Some(length.into())) - } - - /// Is this length definite? - pub fn is_definite(self) -> bool { - self.0.is_some() - } - /// Is this length indefinite? - pub fn is_indefinite(self) -> bool { - self.0.is_none() - } -} - -impl<'a> Decode<'a> for IndefiniteLength { - fn decode<R: Reader<'a>>(reader: &mut R) -> Result<IndefiniteLength> { - if reader.peek_byte() == Some(INDEFINITE_LENGTH_OCTET) { - // Consume the byte we already peeked at. - let byte = reader.read_byte()?; - debug_assert_eq!(byte, INDEFINITE_LENGTH_OCTET); - - Ok(Self::INDEFINITE) - } else { - Length::decode(reader).map(Into::into) - } - } -} - -impl Encode for IndefiniteLength { - fn encoded_len(&self) -> Result<Length> { - match self.0 { - Some(length) => length.encoded_len(), - None => Ok(Length::ONE), - } - } - - fn encode(&self, writer: &mut impl Writer) -> Result<()> { - match self.0 { - Some(length) => length.encode(writer), - None => writer.write_byte(INDEFINITE_LENGTH_OCTET), - } - } -} - -impl From<Length> for IndefiniteLength { - fn from(length: Length) -> IndefiniteLength { - Self(Some(length)) - } -} - -impl From<Option<Length>> for IndefiniteLength { - fn from(length: Option<Length>) -> IndefiniteLength { - IndefiniteLength(length) - } -} - -impl From<IndefiniteLength> for Option<Length> { - fn from(length: IndefiniteLength) -> Option<Length> { - length.0 - } -} - -impl TryFrom<IndefiniteLength> for Length { - type Error = Error; - - fn try_from(length: IndefiniteLength) -> Result<Length> { - length.0.ok_or_else(|| ErrorKind::IndefiniteLength.into()) - } -} - #[cfg(test)] mod tests { - use super::{IndefiniteLength, Length}; + use super::Length; use crate::{Decode, DerOrd, Encode, ErrorKind}; use core::cmp::Ordering; @@ -480,22 +355,8 @@ mod tests { } #[test] - fn indefinite_lengths() { - // DER disallows indefinite lengths + fn reject_indefinite_lengths() { assert!(Length::from_der(&[0x80]).is_err()); - - // The `IndefiniteLength` type supports them - let indefinite_length = IndefiniteLength::from_der(&[0x80]).unwrap(); - assert!(indefinite_length.is_indefinite()); - assert_eq!(indefinite_length, IndefiniteLength::INDEFINITE); - - // It also supports definite lengths. - let length = IndefiniteLength::from_der(&[0x83, 0x01, 0x00, 0x00]).unwrap(); - assert!(length.is_definite()); - assert_eq!( - Length::try_from(0x10000u32).unwrap(), - length.try_into().unwrap() - ); } #[test] @@ -1,5 +1,5 @@ #![no_std] -#![cfg_attr(docsrs, feature(doc_auto_cfg))] +#![cfg_attr(docsrs, feature(doc_cfg))] #![doc = include_str!("../README.md")] #![doc( html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg", @@ -15,7 +15,6 @@ clippy::checked_conversions, clippy::implicit_saturating_sub, clippy::integer_arithmetic, - clippy::mod_module_files, clippy::panic, clippy::panic_in_result_fn, clippy::unwrap_used, @@ -57,7 +56,7 @@ //! - [`VideotexStringRef`]: ASN.1 `VideotexString`. //! - [`SequenceOf`]: ASN.1 `SEQUENCE OF`. //! - [`SetOf`], [`SetOfVec`]: ASN.1 `SET OF`. -//! - [`UintRef`]: ASN.1 unsigned `INTEGER` with raw access to encoded bytes. +//! - [`UIntRef`]: ASN.1 unsigned `INTEGER` with raw access to encoded bytes. //! - [`UtcTime`]: ASN.1 `UTCTime`. //! - [`Utf8StringRef`]: ASN.1 `UTF8String`. //! @@ -140,20 +139,33 @@ //! } //! } //! -//! impl<'a> ::der::EncodeValue for AlgorithmIdentifier<'a> { -//! fn value_len(&self) -> ::der::Result<::der::Length> { -//! self.algorithm.encoded_len()? + self.parameters.encoded_len()? -//! } -//! -//! fn encode_value(&self, writer: &mut impl ::der::Writer) -> ::der::Result<()> { -//! self.algorithm.encode(writer)?; -//! self.parameters.encode(writer)?; -//! Ok(()) +//! impl<'a> Sequence<'a> for AlgorithmIdentifier<'a> { +//! // The `Sequence::fields` method is used for encoding and functions as +//! // a visitor for all of the fields in a message. +//! // +//! // To implement it, you must define a slice containing `Encode` +//! // trait objects, then pass it to the provided `field_encoder` +//! // function, which is implemented by the `der` crate and handles +//! // message serialization. +//! // +//! // Trait objects are used because they allow for slices containing +//! // heterogeneous field types, and a callback is used to allow for the +//! // construction of temporary field encoder types. The latter means +//! // that the fields of your Rust struct don't necessarily need to +//! // impl the `Encode` trait, but if they don't you must construct +//! // a temporary wrapper value which does. +//! // +//! // Types which impl the `Sequence` trait receive blanket impls of both +//! // the `Encode` and `Tagged` traits (where the latter is impl'd as +//! // `Tagged::TAG = der::Tag::Sequence`. +//! fn fields<F, T>(&self, field_encoder: F) -> der::Result<T> +//! where +//! F: FnOnce(&[&dyn Encode]) -> der::Result<T>, +//! { +//! field_encoder(&[&self.algorithm, &self.parameters]) //! } //! } //! -//! impl<'a> Sequence<'a> for AlgorithmIdentifier<'a> {} -//! //! // Example parameters value: OID for the NIST P-256 elliptic curve. //! let parameters = "1.2.840.10045.3.1.7".parse::<ObjectIdentifier>().unwrap(); //! @@ -170,7 +182,7 @@ //! // If you would prefer to avoid allocations, you can create a byte array //! // as backing storage instead, pass that to `der::Encoder::new`, and then //! // encode the `parameters` value using `encoder.encode(parameters)`. -//! let der_encoded_parameters = parameters.to_der().unwrap(); +//! let der_encoded_parameters = parameters.to_vec().unwrap(); //! //! let algorithm_identifier = AlgorithmIdentifier { //! // OID for `id-ecPublicKey`, if you're curious @@ -189,7 +201,7 @@ //! // reference to it, then encode the message using //! // `encoder.encode(algorithm_identifier)`, then finally `encoder.finish()` //! // to obtain a byte slice containing the encoded message. -//! let der_encoded_algorithm_identifier = algorithm_identifier.to_der().unwrap(); +//! let der_encoded_algorithm_identifier = algorithm_identifier.to_vec().unwrap(); //! //! // Deserialize the `AlgorithmIdentifier` we just serialized from ASN.1 DER //! // using `der::Decode::from_bytes`. @@ -243,7 +255,7 @@ //! }; //! //! // Encode -//! let der_encoded_algorithm_identifier = algorithm_identifier.to_der().unwrap(); +//! let der_encoded_algorithm_identifier = algorithm_identifier.to_vec().unwrap(); //! //! // Decode //! let decoded_algorithm_identifier = AlgorithmIdentifier::from_der( @@ -316,7 +328,7 @@ //! [`SequenceOf`]: asn1::SequenceOf //! [`SetOf`]: asn1::SetOf //! [`SetOfVec`]: asn1::SetOfVec -//! [`UintRef`]: asn1::UintRef +//! [`UIntRef`]: asn1::UIntRef //! [`UtcTime`]: asn1::UtcTime //! [`Utf8StringRef`]: asn1::Utf8StringRef @@ -332,10 +344,9 @@ extern crate alloc; extern crate std; pub mod asn1; -pub mod referenced; pub(crate) mod arrayvec; -mod bytes_ref; +mod byte_slice; mod datetime; mod decode; mod encode; @@ -345,16 +356,12 @@ mod header; mod length; mod ord; mod reader; -mod str_ref; +mod str_slice; mod tag; mod writer; #[cfg(feature = "alloc")] -mod bytes_owned; -#[cfg(feature = "alloc")] mod document; -#[cfg(feature = "alloc")] -mod str_owned; pub use crate::{ asn1::{AnyRef, Choice, Sequence}, @@ -364,35 +371,37 @@ pub use crate::{ encode_ref::{EncodeRef, EncodeValueRef}, error::{Error, ErrorKind, Result}, header::Header, - length::{IndefiniteLength, Length}, + length::Length, ord::{DerOrd, ValueOrd}, - reader::{nested::NestedReader, slice::SliceReader, Reader}, + reader::{slice::SliceReader, Reader}, tag::{Class, FixedTag, Tag, TagMode, TagNumber, Tagged}, writer::{slice::SliceWriter, Writer}, }; #[cfg(feature = "alloc")] -pub use crate::{asn1::Any, document::Document}; +pub use crate::document::Document; #[cfg(feature = "bigint")] +#[cfg_attr(docsrs, doc(cfg(feature = "bigint")))] pub use crypto_bigint as bigint; #[cfg(feature = "derive")] +#[cfg_attr(docsrs, doc(cfg(feature = "derive")))] pub use der_derive::{Choice, Enumerated, Sequence, ValueOrd}; -#[cfg(feature = "flagset")] -pub use flagset; - #[cfg(feature = "oid")] +#[cfg_attr(docsrs, doc(cfg(feature = "oid")))] pub use const_oid as oid; #[cfg(feature = "pem")] +#[cfg_attr(docsrs, doc(cfg(feature = "pem")))] pub use { crate::{decode::DecodePem, encode::EncodePem, reader::pem::PemReader, writer::pem::PemWriter}, pem_rfc7468 as pem, }; #[cfg(feature = "time")] +#[cfg_attr(docsrs, doc(cfg(feature = "time")))] pub use time; #[cfg(feature = "zeroize")] @@ -401,6 +410,4 @@ pub use zeroize; #[cfg(all(feature = "alloc", feature = "zeroize"))] pub use crate::document::SecretDocument; -pub(crate) use crate::{arrayvec::ArrayVec, bytes_ref::BytesRef, str_ref::StrRef}; -#[cfg(feature = "alloc")] -pub(crate) use crate::{bytes_owned::BytesOwned, str_owned::StrOwned}; +pub(crate) use crate::{arrayvec::ArrayVec, byte_slice::ByteSlice, str_slice::StrSlice}; @@ -1,7 +1,7 @@ //! Ordering trait. use crate::{EncodeValue, Result, Tagged}; -use core::{cmp::Ordering, marker::PhantomData}; +use core::cmp::Ordering; /// DER ordering trait. /// @@ -69,17 +69,3 @@ where Ok(length_ord) } - -/// Provide a no-op implementation for PhantomData -impl<T> ValueOrd for PhantomData<T> { - fn value_cmp(&self, _other: &Self) -> Result<Ordering> { - Ok(Ordering::Equal) - } -} - -/// Provide a no-op implementation for PhantomData -impl<T> DerOrd for PhantomData<T> { - fn der_cmp(&self, _other: &Self) -> Result<Ordering> { - Ok(Ordering::Equal) - } -} diff --git a/src/reader.rs b/src/reader.rs index ea52f7b..b917323 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -1,6 +1,6 @@ //! Reader trait. -pub(crate) mod nested; +mod nested; #[cfg(feature = "pem")] pub(crate) mod pem; pub(crate) mod slice; @@ -135,6 +135,7 @@ pub trait Reader<'r>: Sized { /// Read a byte vector of the given length. #[cfg(feature = "alloc")] + #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] fn read_vec(&mut self, len: Length) -> Result<Vec<u8>> { let mut bytes = vec![0u8; usize::try_from(len)?]; self.read_into(&mut bytes)?; diff --git a/src/reader/pem.rs b/src/reader/pem.rs index f11341a..01bb4f2 100644 --- a/src/reader/pem.rs +++ b/src/reader/pem.rs @@ -1,134 +1,16 @@ //! Streaming PEM reader. use super::Reader; -use crate::{Decode, Error, ErrorKind, Header, Length, Result}; -use core::cell::RefCell; - -#[allow(clippy::integer_arithmetic)] -mod utils { - use crate::{Error, Length, Result}; - use pem_rfc7468::Decoder; - - #[derive(Clone)] - pub(super) struct BufReader<'i> { - /// Inner PEM decoder. - decoder: Decoder<'i>, - - /// Remaining after base64 decoding - remaining: usize, - - /// Read buffer - buf: [u8; BufReader::CAPACITY], - - /// Position of the head in the buffer, - pos: usize, - - /// Position of the tail in the buffer, - cap: usize, - } - - impl<'i> BufReader<'i> { - const CAPACITY: usize = 256; - - pub fn new(pem: &'i [u8]) -> Result<Self> { - let decoder = Decoder::new(pem)?; - let remaining = decoder.remaining_len(); - - Ok(Self { - decoder, - remaining, - buf: [0u8; 256], - pos: 0, - cap: 0, - }) - } - - pub fn remaining_len(&self) -> usize { - self.decoder.remaining_len() + self.cap - self.pos - } - - fn fill_buffer(&mut self) -> Result<()> { - debug_assert!(self.pos <= self.cap); - - if self.is_empty() { - self.pos = 0; - self.cap = 0; - } - - let end = (self.cap + self.remaining).min(Self::CAPACITY); - let writable_slice = &mut self.buf[self.cap..end]; - if writable_slice.is_empty() { - return Ok(()); - } - - let wrote = self.decoder.decode(writable_slice)?.len(); - if wrote == 0 { - return Err(Error::incomplete(Length::try_from(self.pos)?)); - } - - self.cap += wrote; - self.remaining -= wrote; - debug_assert!(self.cap <= Self::CAPACITY); - - Ok(()) - } - - /// Get the PEM label which will be used in the encapsulation boundaries - /// for this document. - pub fn type_label(&self) -> &'i str { - self.decoder.type_label() - } - - fn is_empty(&self) -> bool { - self.pos == self.cap - } - - fn as_slice(&self) -> &[u8] { - &self.buf[self.pos..self.cap] - } - } - - impl<'i> BufReader<'i> { - pub fn peek_byte(&self) -> Option<u8> { - let s = self.as_slice(); - s.first().copied() - } - - pub fn copy_to_slice<'o>(&mut self, buf: &'o mut [u8]) -> Result<&'o [u8]> { - let mut output_pos = 0; - - while output_pos < buf.len() { - if self.is_empty() { - self.fill_buffer()?; - } - - let available = &self.buf[self.pos..self.cap]; - let window_len = (buf.len() - output_pos).min(available.len()); - let window = &mut buf[output_pos..output_pos + window_len]; - - window.copy_from_slice(&available[..window_len]); - self.pos += window_len; - output_pos += window_len; - } - - // Don't leave the read buffer empty for peek_byte() - if self.is_empty() && self.decoder.remaining_len() != 0 { - self.fill_buffer()? - } - - debug_assert_eq!(output_pos, buf.len()); - - Ok(buf) - } - } -} +use crate::{ErrorKind, Header, Length, Result}; +use pem_rfc7468::Decoder; /// `Reader` type which decodes PEM on-the-fly. #[cfg(feature = "pem")] +#[cfg_attr(docsrs, doc(cfg(feature = "pem")))] #[derive(Clone)] pub struct PemReader<'i> { - /// Inner PEM decoder wrapped in a BufReader. - reader: RefCell<utils::BufReader<'i>>, + /// Inner PEM decoder. + decoder: Decoder<'i>, /// Input length (in bytes after Base64 decoding). input_len: Length, @@ -138,16 +20,17 @@ pub struct PemReader<'i> { } #[cfg(feature = "pem")] +#[cfg_attr(docsrs, doc(cfg(feature = "pem")))] impl<'i> PemReader<'i> { /// Create a new PEM reader which decodes data on-the-fly. /// /// Uses the default 64-character line wrapping. pub fn new(pem: &'i [u8]) -> Result<Self> { - let reader = utils::BufReader::new(pem)?; - let input_len = Length::try_from(reader.remaining_len())?; + let decoder = Decoder::new(pem)?; + let input_len = Length::try_from(decoder.remaining_len())?; Ok(Self { - reader: RefCell::new(reader), + decoder, input_len, position: Length::ZERO, }) @@ -156,30 +39,25 @@ impl<'i> PemReader<'i> { /// Get the PEM label which will be used in the encapsulation boundaries /// for this document. pub fn type_label(&self) -> &'i str { - self.reader.borrow().type_label() + self.decoder.type_label() } } #[cfg(feature = "pem")] +#[cfg_attr(docsrs, doc(cfg(feature = "pem")))] impl<'i> Reader<'i> for PemReader<'i> { fn input_len(&self) -> Length { self.input_len } fn peek_byte(&self) -> Option<u8> { - if self.is_finished() { - None - } else { - self.reader.borrow().peek_byte() - } + // TODO(tarcieri): lookahead buffer + None } fn peek_header(&self) -> Result<Header> { - if self.is_finished() { - Err(Error::incomplete(self.offset())) - } else { - Header::decode(&mut self.clone()) - } + // TODO(tarcieri): lookahead buffer + Err(ErrorKind::Reader.into()) } fn position(&self) -> Length { @@ -192,13 +70,12 @@ impl<'i> Reader<'i> for PemReader<'i> { } fn read_into<'o>(&mut self, buf: &'o mut [u8]) -> Result<&'o [u8]> { - let bytes = self.reader.borrow_mut().copy_to_slice(buf)?; - + let bytes = self.decoder.decode(buf)?; self.position = (self.position + bytes.len())?; debug_assert_eq!( self.position, - (self.input_len - Length::try_from(self.reader.borrow().remaining_len())?)? + (self.input_len - Length::try_from(self.decoder.remaining_len())?)? ); Ok(bytes) diff --git a/src/reader/slice.rs b/src/reader/slice.rs index e78468f..6bab091 100644 --- a/src/reader/slice.rs +++ b/src/reader/slice.rs @@ -1,12 +1,12 @@ //! Slice reader. -use crate::{BytesRef, Decode, Error, ErrorKind, Header, Length, Reader, Result, Tag}; +use crate::{ByteSlice, Decode, Error, ErrorKind, Header, Length, Reader, Result, Tag}; /// [`Reader`] which consumes an input byte slice. #[derive(Clone, Debug)] pub struct SliceReader<'a> { /// Byte slice being decoded. - bytes: BytesRef<'a>, + bytes: ByteSlice<'a>, /// Did the decoding operation fail? failed: bool, @@ -19,7 +19,7 @@ impl<'a> SliceReader<'a> { /// Create a new slice reader for the given byte slice. pub fn new(bytes: &'a [u8]) -> Result<Self> { Ok(Self { - bytes: BytesRef::new(bytes)?, + bytes: ByteSlice::new(bytes)?, failed: false, position: Length::ZERO, }) @@ -64,7 +64,7 @@ impl<'a> Reader<'a> for SliceReader<'a> { fn peek_byte(&self) -> Option<u8> { self.remaining() .ok() - .and_then(|bytes| bytes.first().cloned()) + .and_then(|bytes| bytes.get(0).cloned()) } fn peek_header(&self) -> Result<Header> { diff --git a/src/referenced.rs b/src/referenced.rs deleted file mode 100644 index b0c8f03..0000000 --- a/src/referenced.rs +++ /dev/null @@ -1,69 +0,0 @@ -//! A module for working with referenced data. - -/// A trait for borrowing data from an owned struct -pub trait OwnedToRef { - /// The resulting type referencing back to Self - type Borrowed<'a> - where - Self: 'a; - - /// Creates a new object referencing back to the self for storage - fn owned_to_ref(&self) -> Self::Borrowed<'_>; -} - -/// A trait for cloning a referenced structure and getting owned objects -/// -/// This is the pendant to [`OwnedToRef`] -pub trait RefToOwned<'a> { - /// The resulting type after obtaining ownership. - type Owned: OwnedToRef<Borrowed<'a> = Self> - where - Self: 'a; - - /// Creates a new object taking ownership of the data - fn ref_to_owned(&self) -> Self::Owned; -} - -impl<T> OwnedToRef for Option<T> -where - T: OwnedToRef, -{ - type Borrowed<'a> = Option<T::Borrowed<'a>> where T: 'a; - - fn owned_to_ref(&self) -> Self::Borrowed<'_> { - self.as_ref().map(|o| o.owned_to_ref()) - } -} - -impl<'a, T> RefToOwned<'a> for Option<T> -where - T: RefToOwned<'a> + 'a, - T::Owned: OwnedToRef, -{ - type Owned = Option<T::Owned>; - fn ref_to_owned(&self) -> Self::Owned { - self.as_ref().map(|o| o.ref_to_owned()) - } -} - -#[cfg(feature = "alloc")] -mod allocating { - use super::{OwnedToRef, RefToOwned}; - use alloc::boxed::Box; - - impl<'a> RefToOwned<'a> for &'a [u8] { - type Owned = Box<[u8]>; - - fn ref_to_owned(&self) -> Self::Owned { - Box::from(*self) - } - } - - impl OwnedToRef for Box<[u8]> { - type Borrowed<'a> = &'a [u8]; - - fn owned_to_ref(&self) -> Self::Borrowed<'_> { - self.as_ref() - } - } -} diff --git a/src/str_owned.rs b/src/str_owned.rs deleted file mode 100644 index 20bfea5..0000000 --- a/src/str_owned.rs +++ /dev/null @@ -1,104 +0,0 @@ -//! Common handling for types backed by `String` with enforcement of a -//! library-level length limitation i.e. `Length::max()`. - -use crate::{ - referenced::OwnedToRef, BytesRef, DecodeValue, EncodeValue, Header, Length, Reader, Result, - StrRef, Writer, -}; -use alloc::string::String; -use core::str; - -/// String newtype which respects the [`Length::max`] limit. -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] -pub struct StrOwned { - /// Inner value - pub(crate) inner: String, - - /// Precomputed `Length` (avoids possible panicking conversions) - pub(crate) length: Length, -} - -impl StrOwned { - /// Create a new [`StrOwned`], ensuring that the byte representation of - /// the provided `str` value is shorter than `Length::max()`. - pub fn new(s: String) -> Result<Self> { - let length = Length::try_from(s.as_bytes().len())?; - - Ok(Self { inner: s, length }) - } - - /// Parse a [`String`] from UTF-8 encoded bytes. - pub fn from_bytes(bytes: &[u8]) -> Result<Self> { - Ok(Self { - inner: String::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 [`StrOwned`] - pub fn len(&self) -> Length { - self.length - } - - /// Is this [`StrOwned`] empty? - pub fn is_empty(&self) -> bool { - self.len() == Length::ZERO - } -} - -impl AsRef<str> for StrOwned { - fn as_ref(&self) -> &str { - self.as_str() - } -} - -impl AsRef<[u8]> for StrOwned { - fn as_ref(&self) -> &[u8] { - self.as_bytes() - } -} - -impl<'a> DecodeValue<'a> for StrOwned { - fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> { - Self::from_bytes(BytesRef::decode_value(reader, header)?.as_slice()) - } -} - -impl EncodeValue for StrOwned { - fn value_len(&self) -> Result<Length> { - Ok(self.length) - } - - fn encode_value(&self, writer: &mut impl Writer) -> Result<()> { - writer.write(self.as_ref()) - } -} - -impl From<StrRef<'_>> for StrOwned { - fn from(s: StrRef<'_>) -> StrOwned { - Self { - inner: String::from(s.inner), - length: s.length, - } - } -} - -impl OwnedToRef for StrOwned { - type Borrowed<'a> = StrRef<'a>; - fn owned_to_ref(&self) -> Self::Borrowed<'_> { - StrRef { - length: self.length, - inner: self.inner.as_ref(), - } - } -} diff --git a/src/str_ref.rs b/src/str_slice.rs index 899c750..8c94528 100644 --- a/src/str_ref.rs +++ b/src/str_slice.rs @@ -1,12 +1,12 @@ //! Common handling for types backed by `str` slices with enforcement of a //! library-level length limitation i.e. `Length::max()`. -use crate::{BytesRef, DecodeValue, EncodeValue, Header, Length, Reader, Result, Writer}; +use crate::{ByteSlice, DecodeValue, EncodeValue, Header, Length, Reader, Result, Writer}; use core::str; /// String slice newtype which respects the [`Length::max`] limit. #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] -pub struct StrRef<'a> { +pub struct StrSlice<'a> { /// Inner value pub(crate) inner: &'a str, @@ -14,8 +14,8 @@ pub struct StrRef<'a> { pub(crate) length: Length, } -impl<'a> StrRef<'a> { - /// Create a new [`StrRef`], ensuring that the byte representation of +impl<'a> StrSlice<'a> { + /// Create a new [`StrSlice`], ensuring that the byte representation of /// the provided `str` value is shorter than `Length::max()`. pub fn new(s: &'a str) -> Result<Self> { Ok(Self { @@ -24,7 +24,7 @@ impl<'a> StrRef<'a> { }) } - /// Parse a [`StrRef`] from UTF-8 encoded bytes. + /// Parse a [`StrSlice`] from UTF-8 encoded bytes. pub fn from_bytes(bytes: &'a [u8]) -> Result<Self> { Self::new(str::from_utf8(bytes)?) } @@ -39,54 +39,41 @@ impl<'a> StrRef<'a> { self.inner.as_bytes() } - /// Get the [`Length`] of this [`StrRef`] + /// Get the [`Length`] of this [`StrSlice`] pub fn len(self) -> Length { self.length } - /// Is this [`StrRef`] empty? + /// Is this [`StrSlice`] empty? pub fn is_empty(self) -> bool { self.len() == Length::ZERO } } -impl AsRef<str> for StrRef<'_> { +impl AsRef<str> for StrSlice<'_> { fn as_ref(&self) -> &str { self.as_str() } } -impl AsRef<[u8]> for StrRef<'_> { +impl AsRef<[u8]> for StrSlice<'_> { fn as_ref(&self) -> &[u8] { self.as_bytes() } } -impl<'a> DecodeValue<'a> for StrRef<'a> { +impl<'a> DecodeValue<'a> for StrSlice<'a> { fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> { - Self::from_bytes(BytesRef::decode_value(reader, header)?.as_slice()) + Self::from_bytes(ByteSlice::decode_value(reader, header)?.as_slice()) } } -impl<'a> EncodeValue for StrRef<'a> { +impl<'a> EncodeValue for StrSlice<'a> { fn value_len(&self) -> Result<Length> { Ok(self.length) } - fn encode_value(&self, writer: &mut impl Writer) -> Result<()> { + fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> { writer.write(self.as_ref()) } } - -#[cfg(feature = "alloc")] -mod allocating { - use super::StrRef; - use crate::{referenced::RefToOwned, StrOwned}; - - impl<'a> RefToOwned<'a> for StrRef<'a> { - type Owned = StrOwned; - fn ref_to_owned(&self) -> Self::Owned { - StrOwned::from(*self) - } - } -} @@ -1,5 +1,4 @@ //! ASN.1 tags. -#![cfg_attr(feature = "arbitrary", allow(clippy::integer_arithmetic))] mod class; mod mode; @@ -47,7 +46,6 @@ impl<T: FixedTag> Tagged for T { /// - Bits 8/7: [`Class`] /// - Bit 6: primitive (0) or constructed (1) /// - Bits 5-1: tag number -#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord)] #[non_exhaustive] pub enum Tag { @@ -213,7 +211,7 @@ impl Tag { Tag::UtcTime => 0x17, Tag::GeneralizedTime => 0x18, Tag::VisibleString => 0x1A, - Tag::BmpString => 0x1E, + Tag::BmpString => 0x1D, Tag::Application { constructed, number, @@ -282,7 +280,7 @@ impl TryFrom<u8> for Tag { 0x17 => Ok(Tag::UtcTime), 0x18 => Ok(Tag::GeneralizedTime), 0x1A => Ok(Tag::VisibleString), - 0x1E => Ok(Tag::BmpString), + 0x1d => Ok(Tag::BmpString), 0x30 => Ok(Tag::Sequence), // constructed 0x31 => Ok(Tag::Set), // constructed 0x40..=0x7E => Ok(Tag::Application { @@ -325,7 +323,7 @@ impl Encode for Tag { Ok(Length::ONE) } - fn encode(&self, writer: &mut impl Writer) -> Result<()> { + fn encode(&self, writer: &mut dyn Writer) -> Result<()> { writer.write_byte(self.into()) } } diff --git a/src/tag/mode.rs b/src/tag/mode.rs index ecdaf02..892ce3d 100644 --- a/src/tag/mode.rs +++ b/src/tag/mode.rs @@ -4,12 +4,11 @@ use crate::{Error, ErrorKind, Result}; use core::{fmt, str::FromStr}; /// Tagging modes: `EXPLICIT` versus `IMPLICIT`. -#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, PartialOrd, Ord)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum TagMode { /// `EXPLICIT` tagging. /// /// Tag is added in addition to the inner tag of the type. - #[default] Explicit, /// `IMPLICIT` tagging. @@ -18,6 +17,12 @@ pub enum TagMode { Implicit, } +impl Default for TagMode { + fn default() -> TagMode { + TagMode::Explicit + } +} + impl FromStr for TagMode { type Err = Error; diff --git a/src/tag/number.rs b/src/tag/number.rs index 6a7eaae..1930e94 100644 --- a/src/tag/number.rs +++ b/src/tag/number.rs @@ -186,16 +186,3 @@ impl fmt::Display for TagNumber { write!(f, "{}", self.0) } } - -// Implement by hand because the derive would create invalid values. -// Use the constructor to create a valid value. -#[cfg(feature = "arbitrary")] -impl<'a> arbitrary::Arbitrary<'a> for TagNumber { - fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> { - Ok(Self::new(u.int_in_range(0..=Self::MAX)?)) - } - - fn size_hint(depth: usize) -> (usize, Option<usize>) { - u8::size_hint(depth) - } -} diff --git a/src/writer.rs b/src/writer.rs index 164b215..5e97744 100644 --- a/src/writer.rs +++ b/src/writer.rs @@ -21,6 +21,7 @@ pub trait Writer { } #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl<W: io::Write> Writer for W { fn write(&mut self, slice: &[u8]) -> Result<()> { <Self as io::Write>::write(self, slice)?; diff --git a/src/writer/pem.rs b/src/writer/pem.rs index 87a6f8f..c554632 100644 --- a/src/writer/pem.rs +++ b/src/writer/pem.rs @@ -5,6 +5,7 @@ use crate::Result; use pem_rfc7468::{Encoder, LineEnding}; /// `Writer` type which outputs PEM-encoded data. +#[cfg_attr(docsrs, doc(cfg(feature = "pem")))] pub struct PemWriter<'w>(Encoder<'static, 'w>); impl<'w> PemWriter<'w> { diff --git a/tests/derive.rs b/tests/derive.rs index a8c77fe..dac14f8 100644 --- a/tests/derive.rs +++ b/tests/derive.rs @@ -48,8 +48,9 @@ mod choice { } } - const UTC_TIMESTAMP_DER: &[u8] = &hex!("17 0d 39 31 30 35 30 36 32 33 34 35 34 30 5a"); - const GENERAL_TIMESTAMP_DER: &[u8] = + const UTC_TIMESTAMP_DER: &'static [u8] = + &hex!("17 0d 39 31 30 35 30 36 32 33 34 35 34 30 5a"); + const GENERAL_TIMESTAMP_DER: &'static [u8] = &hex!("18 0f 31 39 39 31 30 35 30 36 32 33 34 35 34 30 5a"); #[test] @@ -115,8 +116,8 @@ mod choice { } } - const BITSTRING_DER: &[u8] = &hex!("80 04 00 01 02 03"); - const TIME_DER: &[u8] = &hex!("81 0f 31 39 39 31 30 35 30 36 32 33 34 35 34 30 5a"); + const BITSTRING_DER: &'static [u8] = &hex!("80 04 00 01 02 03"); + const TIME_DER: &'static [u8] = &hex!("81 0f 31 39 39 31 30 35 30 36 32 33 34 35 34 30 5a"); #[test] fn decode() { @@ -200,7 +201,6 @@ mod enumerated { /// Custom derive test cases for the `Sequence` macro. #[cfg(feature = "oid")] mod sequence { - use core::marker::PhantomData; use der::{ asn1::{AnyRef, ObjectIdentifier, SetOf}, Decode, Encode, Sequence, ValueOrd, @@ -267,9 +267,6 @@ mod sequence { tag_mode = "IMPLICIT" )] pub only_contains_attribute_certs: bool, - - /// Test handling of PhantomData. - pub phantom: PhantomData<()>, } // Extension as defined in [RFC 5280 Section 4.1.2.9]. @@ -435,7 +432,8 @@ mod sequence { #[test] fn decode() { - let algorithm_identifier = AlgorithmIdentifier::from_der(ALGORITHM_IDENTIFIER_DER).unwrap(); + let algorithm_identifier = + AlgorithmIdentifier::from_der(&ALGORITHM_IDENTIFIER_DER).unwrap(); assert_eq!(ID_EC_PUBLIC_KEY_OID, algorithm_identifier.algorithm); assert_eq!( @@ -455,7 +453,7 @@ mod sequence { assert_eq!( ALGORITHM_IDENTIFIER_DER, - algorithm_identifier.to_der().unwrap() + algorithm_identifier.to_vec().unwrap() ); } } diff --git a/tests/set_of.rs b/tests/set_of.rs index d583991..ba43d80 100644 --- a/tests/set_of.rs +++ b/tests/set_of.rs @@ -4,21 +4,15 @@ use der::{asn1::SetOfVec, DerOrd}; use proptest::{prelude::*, string::*}; -use std::collections::BTreeSet; proptest! { #[test] fn sort_equiv(bytes in bytes_regex(".{0,64}").unwrap()) { - let mut uniq = BTreeSet::new(); + let mut expected = bytes.clone(); + expected.sort_by(|a, b| a.der_cmp(b).unwrap()); - // Ensure there are no duplicates - if bytes.iter().copied().all(move |x| uniq.insert(x)) { - let mut expected = bytes.clone(); - expected.sort_by(|a, b| a.der_cmp(b).unwrap()); - - let set = SetOfVec::try_from(bytes).unwrap(); - prop_assert_eq!(expected.as_slice(), set.as_slice()); - } + let set = SetOfVec::try_from(bytes).unwrap(); + prop_assert_eq!(expected.as_slice(), set.as_slice()); } } |