#![no_std] #![cfg_attr(docsrs, feature(doc_auto_cfg))] #![doc = include_str!("../README.md")] #![doc( html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg", html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg" )] #![forbid(unsafe_code)] #![warn( clippy::cast_lossless, clippy::cast_possible_truncation, clippy::cast_possible_wrap, clippy::cast_precision_loss, clippy::cast_sign_loss, clippy::checked_conversions, clippy::implicit_saturating_sub, clippy::integer_arithmetic, clippy::mod_module_files, clippy::panic, clippy::panic_in_result_fn, clippy::unwrap_used, missing_docs, rust_2018_idioms, unused_lifetimes, unused_qualifications )] //! # Usage //! ## [`Decode`] and [`Encode`] traits //! The [`Decode`] and [`Encode`] traits provide the decoding/encoding API //! respectively, and are designed to work in conjunction with concrete ASN.1 //! types, including all types which impl the [`Sequence`] trait. //! //! The traits are impl'd for the following Rust core types: //! - `()`: ASN.1 `NULL`. See also [`Null`]. //! - [`bool`]: ASN.1 `BOOLEAN`. //! - [`i8`], [`i16`], [`i32`], [`i64`], [`i128`]: ASN.1 `INTEGER`. //! - [`u8`], [`u16`], [`u32`], [`u64`], [`u128`]: ASN.1 `INTEGER`. //! - [`f64`]: ASN.1 `REAL` (gated on `real` crate feature) //! - [`str`], [`String`][`alloc::string::String`]: ASN.1 `UTF8String`. //! `String` requires `alloc` feature. See also [`Utf8StringRef`]. //! - [`Option`]: ASN.1 `OPTIONAL`. //! - [`SystemTime`][`std::time::SystemTime`]: ASN.1 `GeneralizedTime`. Requires `std` feature. //! - [`Vec`][`alloc::vec::Vec`]: ASN.1 `SEQUENCE OF`. Requires `alloc` feature. //! - `[T; N]`: ASN.1 `SEQUENCE OF`. See also [`SequenceOf`]. //! //! The following ASN.1 types provided by this crate also impl these traits: //! - [`Any`], [`AnyRef`]: ASN.1 `ANY`. //! - [`BitString`], [`BitStringRef`]: ASN.1 `BIT STRING` //! - [`GeneralizedTime`]: ASN.1 `GeneralizedTime`. //! - [`Ia5StringRef`]: ASN.1 `IA5String`. //! - [`Null`]: ASN.1 `NULL`. //! - [`ObjectIdentifier`]: ASN.1 `OBJECT IDENTIFIER`. //! - [`OctetString`], [`OctetStringRef`]: ASN.1 `OCTET STRING`. //! - [`PrintableStringRef`]: ASN.1 `PrintableString` (ASCII subset). //! - [`TeletexStringRef`]: ASN.1 `TeletexString`. //! - [`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. //! - [`UtcTime`]: ASN.1 `UTCTime`. //! - [`Utf8StringRef`]: ASN.1 `UTF8String`. //! //! Context specific fields can be modeled using these generic types: //! - [`ContextSpecific`]: decoder/encoder for owned context-specific fields //! - [`ContextSpecificRef`]: encode-only type for references to context-specific fields //! //! ## Example //! The following example implements X.509's `AlgorithmIdentifier` message type //! as defined in [RFC 5280 Section 4.1.1.2]. //! //! The ASN.1 schema for this message type is as follows: //! //! ```text //! AlgorithmIdentifier ::= SEQUENCE { //! algorithm OBJECT IDENTIFIER, //! parameters ANY DEFINED BY algorithm OPTIONAL } //! ``` //! //! Structured ASN.1 messages are typically encoded as a `SEQUENCE`, which //! this crate maps to a Rust struct using the [`Sequence`] trait. This //! trait is bounded on the [`Decode`] trait and provides a blanket impl //! of the [`Encode`] trait, so any type which impls [`Sequence`] can be //! used for both decoding and encoding. //! //! The following code example shows how to define a struct which maps to the //! above schema, as well as impl the [`Sequence`] trait for that struct: //! //! ``` //! # #[cfg(all(feature = "alloc", feature = "oid"))] //! # { //! // Note: the following example does not require the `std` feature at all. //! // It does leverage the `alloc` feature, but also provides instructions for //! // "heapless" usage when the `alloc` feature is disabled. //! use der::{ //! asn1::{AnyRef, ObjectIdentifier}, //! DecodeValue, Decode, SliceReader, Encode, Header, Reader, Sequence //! }; //! //! /// X.509 `AlgorithmIdentifier`. //! #[derive(Copy, Clone, Debug, Eq, PartialEq)] //! pub struct AlgorithmIdentifier<'a> { //! /// This field contains an ASN.1 `OBJECT IDENTIFIER`, a.k.a. OID. //! pub algorithm: ObjectIdentifier, //! //! /// This field is `OPTIONAL` and contains the ASN.1 `ANY` type, which //! /// in this example allows arbitrary algorithm-defined parameters. //! pub parameters: Option> //! } //! //! impl<'a> DecodeValue<'a> for AlgorithmIdentifier<'a> { //! fn decode_value>(reader: &mut R, _header: Header) -> der::Result { //! // The `der::Decoder::Decode` method can be used to decode any //! // type which impls the `Decode` trait, which is impl'd for //! // all of the ASN.1 built-in types in the `der` crate. //! // //! // Note that if your struct's fields don't contain an ASN.1 //! // built-in type specifically, there are also helper methods //! // for all of the built-in types supported by this library //! // which can be used to select a specific type. //! // //! // For example, another way of decoding this particular field, //! // which contains an ASN.1 `OBJECT IDENTIFIER`, is by calling //! // `decoder.oid()`. Similar methods are defined for other //! // ASN.1 built-in types. //! let algorithm = reader.decode()?; //! //! // This field contains an ASN.1 `OPTIONAL` type. The `der` crate //! // maps this directly to Rust's `Option` type and provides //! // impls of the `Decode` and `Encode` traits for `Option`. //! // To explicitly request an `OPTIONAL` type be decoded, use the //! // `decoder.optional()` method. //! let parameters = reader.decode()?; //! //! // The value returned from the provided `FnOnce` will be //! // returned from the `any.sequence(...)` call above. //! // Note that the entire sequence body *MUST* be consumed //! // or an error will be returned. //! Ok(Self { algorithm, parameters }) //! } //! } //! //! 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> {} //! //! // Example parameters value: OID for the NIST P-256 elliptic curve. //! let parameters = "1.2.840.10045.3.1.7".parse::().unwrap(); //! //! // We need to convert `parameters` into an `Any<'a>` type, which wraps a //! // `&'a [u8]` byte slice. //! // //! // To do that, we need owned DER-encoded data so that we can have //! // `AnyRef` borrow a reference to it, so we have to serialize the OID. //! // //! // When the `alloc` feature of this crate is enabled, any type that impls //! // the `Encode` trait including all ASN.1 built-in types and any type //! // which impls `Sequence` can be serialized by calling `Encode::to_der()`. //! // //! // 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 algorithm_identifier = AlgorithmIdentifier { //! // OID for `id-ecPublicKey`, if you're curious //! algorithm: "1.2.840.10045.2.1".parse().unwrap(), //! //! // `Any<'a>` impls `TryFrom<&'a [u8]>`, which parses the provided //! // slice as an ASN.1 DER-encoded message. //! parameters: Some(der_encoded_parameters.as_slice().try_into().unwrap()) //! }; //! //! // Serialize the `AlgorithmIdentifier` created above as ASN.1 DER, //! // allocating a `Vec` for storage. //! // //! // As mentioned earlier, if you don't have the `alloc` feature enabled you //! // can create a fix-sized array instead, then call `Encoder::new` with a //! // 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(); //! //! // Deserialize the `AlgorithmIdentifier` we just serialized from ASN.1 DER //! // using `der::Decode::from_bytes`. //! let decoded_algorithm_identifier = AlgorithmIdentifier::from_der( //! &der_encoded_algorithm_identifier //! ).unwrap(); //! //! // Ensure the original `AlgorithmIdentifier` is the same as the one we just //! // decoded from ASN.1 DER. //! assert_eq!(algorithm_identifier, decoded_algorithm_identifier); //! # } //! ``` //! //! ## Custom derive support //! When the `derive` feature of this crate is enabled, the following custom //! derive macros are available: //! //! - [`Choice`]: derive for `CHOICE` enum (see [`der_derive::Choice`]) //! - [`Enumerated`]: derive for `ENUMERATED` enum (see [`der_derive::Enumerated`]) //! - [`Sequence`]: derive for `SEQUENCE` struct (see [`der_derive::Sequence`]) //! //! ### Derive [`Sequence`] for struct //! The following is a code example of how to use the [`Sequence`] custom derive: //! //! ``` //! # #[cfg(all(feature = "alloc", feature = "derive", feature = "oid"))] //! # { //! use der::{asn1::{AnyRef, ObjectIdentifier}, Encode, Decode, Sequence}; //! //! /// X.509 `AlgorithmIdentifier` (same as above) //! #[derive(Copy, Clone, Debug, Eq, PartialEq, Sequence)] // NOTE: added `Sequence` //! pub struct AlgorithmIdentifier<'a> { //! /// This field contains an ASN.1 `OBJECT IDENTIFIER`, a.k.a. OID. //! pub algorithm: ObjectIdentifier, //! //! /// This field is `OPTIONAL` and contains the ASN.1 `ANY` type, which //! /// in this example allows arbitrary algorithm-defined parameters. //! pub parameters: Option> //! } //! //! // Example parameters value: OID for the NIST P-256 elliptic curve. //! let parameters_oid = "1.2.840.10045.3.1.7".parse::().unwrap(); //! //! let algorithm_identifier = AlgorithmIdentifier { //! // OID for `id-ecPublicKey`, if you're curious //! algorithm: "1.2.840.10045.2.1".parse().unwrap(), //! //! // `Any<'a>` impls `From<&'a ObjectIdentifier>`, allowing OID constants to //! // be directly converted to an `AnyRef` type for this use case. //! parameters: Some(AnyRef::from(¶meters_oid)) //! }; //! //! // Encode //! let der_encoded_algorithm_identifier = algorithm_identifier.to_der().unwrap(); //! //! // Decode //! let decoded_algorithm_identifier = AlgorithmIdentifier::from_der( //! &der_encoded_algorithm_identifier //! ).unwrap(); //! //! assert_eq!(algorithm_identifier, decoded_algorithm_identifier); //! # } //! ``` //! //! For fields which don't directly impl [`Decode`] and [`Encode`], //! you can add annotations to convert to an intermediate ASN.1 type //! first, so long as that type impls `TryFrom` and `Into` for the //! ASN.1 type. //! //! For example, structs containing `&'a [u8]` fields may want them encoded //! as either a `BIT STRING` or `OCTET STRING`. By using the //! `#[asn1(type = "BIT STRING")]` annotation it's possible to select which //! ASN.1 type should be used. //! //! Building off the above example: //! //! ```rust //! # #[cfg(all(feature = "alloc", feature = "derive", feature = "oid"))] //! # { //! # use der::{asn1::{AnyRef, BitStringRef, ObjectIdentifier}, Sequence}; //! # //! # #[derive(Copy, Clone, Debug, Eq, PartialEq, Sequence)] //! # pub struct AlgorithmIdentifier<'a> { //! # pub algorithm: ObjectIdentifier, //! # pub parameters: Option> //! # } //! /// X.509 `SubjectPublicKeyInfo` (SPKI) //! #[derive(Copy, Clone, Debug, Eq, PartialEq, Sequence)] //! pub struct SubjectPublicKeyInfo<'a> { //! /// X.509 `AlgorithmIdentifier` //! pub algorithm: AlgorithmIdentifier<'a>, //! //! /// Public key data //! pub subject_public_key: BitStringRef<'a>, //! } //! # } //! ``` //! //! # See also //! For more information about ASN.1 DER we recommend the following guides: //! //! - [A Layman's Guide to a Subset of ASN.1, BER, and DER] (RSA Laboratories) //! - [A Warm Welcome to ASN.1 and DER] (Let's Encrypt) //! //! [RFC 5280 Section 4.1.1.2]: https://tools.ietf.org/html/rfc5280#section-4.1.1.2 //! [A Layman's Guide to a Subset of ASN.1, BER, and DER]: https://luca.ntop.org/Teaching/Appunti/asn1.html //! [A Warm Welcome to ASN.1 and DER]: https://letsencrypt.org/docs/a-warm-welcome-to-asn1-and-der/ //! //! [`Any`]: asn1::Any //! [`AnyRef`]: asn1::AnyRef //! [`ContextSpecific`]: asn1::ContextSpecific //! [`ContextSpecificRef`]: asn1::ContextSpecificRef //! [`BitString`]: asn1::BitString //! [`BitStringRef`]: asn1::BitStringRef //! [`GeneralizedTime`]: asn1::GeneralizedTime //! [`Ia5StringRef`]: asn1::Ia5StringRef //! [`Null`]: asn1::Null //! [`ObjectIdentifier`]: asn1::ObjectIdentifier //! [`OctetString`]: asn1::OctetString //! [`OctetStringRef`]: asn1::OctetStringRef //! [`PrintableStringRef`]: asn1::PrintableStringRef //! [`TeletexStringRef`]: asn1::TeletexStringRef //! [`VideotexStringRef`]: asn1::VideotexStringRef //! [`SequenceOf`]: asn1::SequenceOf //! [`SetOf`]: asn1::SetOf //! [`SetOfVec`]: asn1::SetOfVec //! [`UintRef`]: asn1::UintRef //! [`UtcTime`]: asn1::UtcTime //! [`Utf8StringRef`]: asn1::Utf8StringRef #[cfg(feature = "alloc")] #[allow(unused_imports)] #[macro_use] extern crate alloc; #[cfg(feature = "std")] extern crate std; pub mod asn1; pub mod referenced; pub(crate) mod arrayvec; mod bytes_ref; mod datetime; mod decode; mod encode; mod encode_ref; mod error; mod header; mod length; mod ord; mod reader; mod str_ref; 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}, datetime::DateTime, decode::{Decode, DecodeOwned, DecodeValue}, encode::{Encode, EncodeValue}, encode_ref::{EncodeRef, EncodeValueRef}, error::{Error, ErrorKind, Result}, header::Header, length::{IndefiniteLength, Length}, ord::{DerOrd, ValueOrd}, reader::{nested::NestedReader, slice::SliceReader, Reader}, tag::{Class, FixedTag, Tag, TagMode, TagNumber, Tagged}, writer::{slice::SliceWriter, Writer}, }; #[cfg(feature = "alloc")] pub use crate::{asn1::Any, document::Document}; #[cfg(feature = "bigint")] pub use crypto_bigint as bigint; #[cfg(feature = "derive")] pub use der_derive::{Choice, Enumerated, Sequence, ValueOrd}; #[cfg(feature = "flagset")] pub use flagset; #[cfg(feature = "oid")] pub use const_oid as oid; #[cfg(feature = "pem")] pub use { crate::{decode::DecodePem, encode::EncodePem, reader::pem::PemReader, writer::pem::PemWriter}, pem_rfc7468 as pem, }; #[cfg(feature = "time")] pub use time; #[cfg(feature = "zeroize")] 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};