aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSam Saccone <samccone@google.com>2023-12-19 01:58:56 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2023-12-19 01:58:56 +0000
commitf34a20e7b5b2cc181787960ffb2ace2ba9976420 (patch)
tree57e854596b4206fdfbbd03ea66612c1fece3d7ce
parent4a532ebdb7ff77c7a8fb2e09acb0f42753cb4ba5 (diff)
parent07fe575307fc1516488c98925599ef4be401ed32 (diff)
downloadder-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>
-rw-r--r--.cargo_vcs_info.json2
-rw-r--r--Android.bp8
-rw-r--r--CHANGELOG.md118
-rw-r--r--Cargo.toml41
-rw-r--r--Cargo.toml.orig29
-rw-r--r--LICENSE-MIT2
-rw-r--r--METADATA21
-rw-r--r--README.md4
-rw-r--r--src/arrayvec.rs17
-rw-r--r--src/asn1.rs21
-rw-r--r--src/asn1/any.rs311
-rw-r--r--src/asn1/bit_string.rs298
-rw-r--r--src/asn1/bmp_string.rs164
-rw-r--r--src/asn1/boolean.rs17
-rw-r--r--src/asn1/context_specific.rs4
-rw-r--r--src/asn1/generalized_time.rs49
-rw-r--r--src/asn1/ia5_string.rs165
-rw-r--r--src/asn1/integer.rs121
-rw-r--r--src/asn1/integer/bigint.rs152
-rw-r--r--src/asn1/integer/int.rs405
-rw-r--r--src/asn1/integer/uint.rs320
-rw-r--r--src/asn1/internal_macros.rs75
-rw-r--r--src/asn1/null.rs18
-rw-r--r--src/asn1/octet_string.rs219
-rw-r--r--src/asn1/oid.rs12
-rw-r--r--src/asn1/optional.rs4
-rw-r--r--src/asn1/printable_string.rs184
-rw-r--r--src/asn1/real.rs17
-rw-r--r--src/asn1/sequence.rs63
-rw-r--r--src/asn1/sequence_of.rs12
-rw-r--r--src/asn1/set_of.rs164
-rw-r--r--src/asn1/teletex_string.rs171
-rw-r--r--src/asn1/utc_time.rs55
-rw-r--r--src/asn1/utf8_string.rs75
-rw-r--r--src/asn1/videotex_string.rs59
-rw-r--r--src/byte_slice.rs116
-rw-r--r--src/bytes_owned.rs162
-rw-r--r--src/bytes_ref.rs152
-rw-r--r--src/datetime.rs60
-rw-r--r--src/decode.rs27
-rw-r--r--src/document.rs19
-rw-r--r--src/encode.rs45
-rw-r--r--src/encode_ref.rs4
-rw-r--r--src/error.rs13
-rw-r--r--src/header.rs2
-rw-r--r--src/length.rs147
-rw-r--r--src/lib.rs75
-rw-r--r--src/ord.rs16
-rw-r--r--src/reader.rs3
-rw-r--r--src/reader/pem.rs157
-rw-r--r--src/reader/slice.rs8
-rw-r--r--src/referenced.rs69
-rw-r--r--src/str_owned.rs104
-rw-r--r--src/str_slice.rs (renamed from src/str_ref.rs)39
-rw-r--r--src/tag.rs8
-rw-r--r--src/tag/mode.rs9
-rw-r--r--src/tag/number.rs13
-rw-r--r--src/writer.rs1
-rw-r--r--src/writer/pem.rs1
-rw-r--r--tests/derive.rs18
-rw-r--r--tests/set_of.rs14
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
diff --git a/Android.bp b/Android.bp
index 31a63ba..85389e1 100644
--- a/Android.bp
+++ b/Android.bp
@@ -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])
diff --git a/Cargo.toml b/Cargo.toml
index a1fcaf1..1296c9f 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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
diff --git a/METADATA b/METADATA
index 234bf10..3217d88 100644
--- a/METADATA
+++ b/METADATA
@@ -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
}
}
diff --git a/README.md b/README.md
index f13053f..f7bb015 100644
--- a/README.md
+++ b/README.md
@@ -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]
diff --git a/src/lib.rs b/src/lib.rs
index 0bb32c9..c8e7945 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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};
diff --git a/src/ord.rs b/src/ord.rs
index 42d4623..fd967bf 100644
--- a/src/ord.rs
+++ b/src/ord.rs
@@ -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)
- }
- }
-}
diff --git a/src/tag.rs b/src/tag.rs
index 7a1fed1..0e7bf20 100644
--- a/src/tag.rs
+++ b/src/tag.rs
@@ -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());
}
}