[go: up one dir, main page]

Crate asn1_rs

Source
Expand description

§BER/DER Parsers/Encoders

A set of parsers/encoders for Basic Encoding Rules (BER [X.690]) and Distinguished Encoding Rules(DER [X.690]) formats, implemented with the nom parser combinator framework.

It is written in pure Rust, fast, and makes extensive use of zero-copy. A lot of care is taken to ensure security and safety of this crate, including design (recursion limit, defensive programming), tests, and fuzzing. It also aims to be panic-free.

This crate is a rewrite of der-parser to propose a more data-oriented API, and add generalized support for serialization.

Many ideas were initially borrowed from the crypto/utils/der crate (like the Any/TryFrom/FromDer mechanism), adapted and merged into a generalized BER/DER crate. Credits (and many thanks) go to Tony Arcieri for writing the original crate.

§BER/DER parsers

BER stands for Basic Encoding Rules, and is defined in [X.690]. It defines a set of rules to encode and decode ASN.1 [X.680] objects in binary.

[X.690] also defines Distinguished Encoding Rules (DER), which is BER with added rules to ensure canonical and unequivocal binary representation of objects.

The choice of which one to use is usually guided by the specification of the data format based on BER or DER: for example, X.509 uses DER as encoding representation.

The main traits for parsing are the BerParser and DerParser traits. These traits provide methods to parse binary input wrapped in Input, and return either the remaining (unparsed) bytes and the parsed object, or an error. The Input types is a simple wrapper around &[u8] to keep information on data span. This is especially useful to print information or to debug parsing errors.

This crates also provides the FromBer and FromDer traits for parsing (working on slices).

The parsers follow the interface from nom, and the ParseResult object is a specialized version of nom::IResult. This means that most nom combinators (map, many0, etc.) can be used in combination to objects and methods from this crate. Reading the nom documentation may help understanding how to write and combine parsers and use the output.

Minimum Supported Rust Version: 1.65.0

no_std support: asn1-rs supports #[no_std] (with a requirement on alloc).

§Recipes

See doc::recipes and doc::derive for more examples and recipes.

See doc::debug for advice and tools to debug parsers.

§Examples

Parse 2 BER integers:

use asn1_rs::{Integer, FromBer};

let bytes = [ 0x02, 0x03, 0x01, 0x00, 0x01,
              0x02, 0x03, 0x01, 0x00, 0x00,
];

let (rem, obj1) = Integer::from_ber(&bytes).expect("parsing failed");
let (rem, obj2) = Integer::from_ber(&bytes).expect("parsing failed");

assert_eq!(obj1, Integer::from_u32(65537));

In the above example, the generic Integer type is used. This type can contain integers of any size, but do not provide a simple API to manipulate the numbers.

In most cases, the integer either has a limit, or is expected to fit into a primitive type. To get a simple value, just use the from_ber/from_der methods on the primitive types:

use asn1_rs::FromBer;

let bytes = [ 0x02, 0x03, 0x01, 0x00, 0x01,
              0x02, 0x03, 0x01, 0x00, 0x00,
];

let (rem, obj1) = u32::from_ber(&bytes).expect("parsing failed");
let (rem, obj2) = u32::from_ber(&rem).expect("parsing failed");

assert_eq!(obj1, 65537);
assert_eq!(obj2, 65536);

If the parsing succeeds, but the integer cannot fit into the expected type, the method will return an IntegerTooLarge error.

§BER/DER encoders

BER/DER encoding is symmetrical to decoding, using the traits ToBer and ToDer traits. These traits provide methods to write encoded content to objects with the io::Write trait, or return an allocated Vec<u8> with the encoded data. If the serialization fails, an error is returned.

§Examples

Writing 2 BER integers:

use asn1_rs::{Integer, ToDer};

let mut writer = Vec::new();

let obj1 = Integer::from_u32(65537);
let obj2 = Integer::from_u32(65536);

let _ = obj1.write_der(&mut writer).expect("serialization failed");
let _ = obj2.write_der(&mut writer).expect("serialization failed");

let bytes = &[ 0x02, 0x03, 0x01, 0x00, 0x01,
               0x02, 0x03, 0x01, 0x00, 0x00,
];
assert_eq!(&writer, bytes);

Similarly to BerParser/DerParser, serialization methods are also implemented for primitive types:

use asn1_rs::ToDer;

let mut writer = Vec::new();

let _ = 65537.write_der(&mut writer).expect("serialization failed");
let _ = 65536.write_der(&mut writer).expect("serialization failed");

let bytes = &[ 0x02, 0x03, 0x01, 0x00, 0x01,
               0x02, 0x03, 0x01, 0x00, 0x00,
];
assert_eq!(&writer, bytes);

If the parsing succeeds, but the integer cannot fit into the expected type, the method will return an IntegerTooLarge error.

§Custom derive attributes

To simplify the code needed to declare common/usual ASN.1 objects, custom derive attributes are provided.

For example, to derive a MyType SEQUENCE { a INTEGER }, declare a struct and add the Sequence attribute:

#[derive(Sequence)]
pub struct MyType {
  a: u32,
}

See doc::derive for documentation and examples.

§ASN.1 types mapping

When using types-based parsing, the usual method is to map the ASN.1 definition of types into asn1-rs (Rust) types, and call the matching trait methods on these types. For generic (not types-based) parsing, other methods are available (but not shown in the following table).

The following table describes how to declare and use ASN.1 types in asn1-rs. Some types will have a lifetime, as they try to borrow the input and avoid useless copies.

ASN.1 type Example `asn1-rs` type(s)
BIT STRING
My-Type ::= BIT STRING
use asn1_rs::BitString;
type MyType = BitString;
BOOLEAN
My-Type ::= BOOLEAN
type MyType = bool;

or

use asn1_rs::Boolean;
type MyType = Boolean;
CHOICE
My-Type ::= CHOICE {
    text         OCTET STRING,
    codedNumeric INTEGER}
use asn1_rs::Choice;
/// MessageType ::= CHOICE
#[derive(Debug, PartialEq, Choice)]
pub enum MessageType<'a> {
    /// text OCTET STRING
    Text(&'a [u8]),
    /// codedNumeric INTEGER
    CodedNumeric(u32),
}
ENUMERATED
My-Type ::= ENUMERATED { a, b, c }

Generic values: Value is parsed as Enumerated, which contains the index of the value in the type definition.

use asn1_rs::Enumerated;
type MyType = Enumerated;

let val = Enumerated::new(2);

Derive attribute:

use asn1_rs::Enumerated;
 #[derive(Debug, Clone, Copy)]
// NOTE: enum must have an integer representation + Clone + Copy
#[derive(Enumerated)]
pub enum MyEnum {
    Zero = 0,
    Four = 4,
    Five,
}
INTEGER
UInt8 ::= INTEGER (0..255)
UInt32 ::= INTEGER (0..4294967295)
Int32 ::= INTEGER (-2147483648..2147483647)

If integer has known constraints (sign / max), native types can be used:

type UInt8 = u8;
type UInt32 = u32;
type Int32 = i32;
INTEGER
My-Type ::= INTEGER

Variable-length integer:

use asn1_rs::Integer;
type MyType<'a> = Integer<'a>;

let value = Integer::from(4);
NULL
My-Type ::= NULL
type MyType = ();

or

use asn1_rs::Null;
type MyType = Null;
OBJECT IDENTIFIER
My-Type ::= OBJECT IDENTIFIER

The Oid type is Copy-on-Write:

use asn1_rs::*;
type T1<'a> = Oid<'a>;
let oid = oid!(1.2.44.233);
OCTET STRING
My-Type ::= OCTET STRING

To use a shared reference (zero-copy) on data:

type T1<'a> = &'a [u8];

or, to use a Copy-on-Write (possible owned) type:

use asn1_rs::OctetString;
type MyType<'a> = OctetString<'a>;
REAL
My-Type ::= REAL
type MyType1 = f32;
type MyType2 = f64;

or

use asn1_rs::Real;
type MyType = Real;
RELATIVE-OID
My-Type ::= RELATIVE-OID

Relative object identifiers are also implemented using Oid (Copy-on-Write):

use asn1_rs::*;
type T1<'a> = Oid<'a>;
let oid = oid!(rel 44.233);
UTF8String
My-Type ::= UTF8String
type MyType1<'a> = &'a str; // zero-copy
type MyType2 = String; // owned version

or the Copy-on-Write version:

use asn1_rs::Utf8String;
type MyType<'a> = Utf8String<'a>;
Restricted Character Strings
String-N ::= NumericString
String-V ::= VisibleString
String-P ::= PrintableString
String-I ::= IA5String
String-G ::= GeneralString
String-Gr ::= GraphicString
String-T ::= TeletexString
String-U ::= TeletexString

String-B ::= BMPString
String-Un ::= UTF8String

Copy-on-Write versions:

use asn1_rs::*;
type StringN<'a> = NumericString<'a>;
type StringV<'a> = VisibleString<'a>;
type StringP<'a> = PrintableString<'a>;
type StringI<'a> = Ia5String<'a>;
type StringT<'a> = TeletexString<'a>;
type StringG<'a> = GeneralString<'a>;
type StringGr<'a> = GraphicString<'a>;
type StringU<'a> = Utf8String<'a>;

Owned Versions:

use asn1_rs::*;
type StringB<'a> = BmpString<'a>;
type StringUn<'a> = UniversalString<'a>;
Unrestricted Character Strings
My-Type ::= CHARACTER STRING

Not Supported

Time
Time-U ::= UTCTime
Time-G ::= GeneralizedTime

Owned versions:

use asn1_rs::*;
type TimeU = UtcTime;
type TimeG = GeneralizedTime;
Other Time representations
My-Type1 ::= TIME
My-Type2 ::= DATE
My-Type3 ::= TIME-OF-DAY
My-Type4 ::= DATE-TIME
My-Type5 ::= DURATION

Not Yet Supported

Sequence
My-Type ::= SEQUENCE {
    a BOOLEAN,
    b INTEGER,
}

Use custom derive:

use asn1_rs::*;

#[derive(Debug, PartialEq, Sequence)]
struct MyType {
    a: bool,
    b: u32,
}

Generic Versions:

use asn1_rs::*;
type MyType1<'a> = Sequence<'a>; // generic object with unparsed content
type MyType2<'a> = AnySequence<'a>; // generic ordered collection of any type

Fixed length version:

type MyType = (bool, u32); // BerParser etc. are implemented for (T1, T2, ...)

Note: when parsing a tuple, all subtypes parsers must return the same type of error

SequenceOf
My-Type ::= SEQUENCE OF INTEGER

Generic Version:

use asn1_rs::*;
type MyType = SequenceOf<u32>;

Native types version:

type MyType = Vec<u32>; // BerParser etc. are implemented for Vec<T>

Fixed length version:

type MyType = [u32; 7]; // BerParser etc. are implemented for [T; N]
Set
My-Type ::= SET {
    a BOOLEAN,
    b INTEGER,
}

Use custom derive:

use asn1_rs::*;

#[derive(Debug, PartialEq, Set)]
struct MyType {
    a: bool,
    b: u32,
}

Generic Versions:

use asn1_rs::*;
type MyType<'a> = Set<'a>; // generic object with unparsed content

Generic Versions (requires std):

use asn1_rs::*;
type MyType<'a> = AnySet<'a>; // generic ordered collection of any type
SetOf
My-Type ::= SET OF INTEGER

Generic Version:

use asn1_rs::*;
type MyType = SetOf<u32>;

Native types versions (requires std):

use std::collections::{BTreeSet, HashSet};
type MyType1 = BTreeSet<u32>; // T must implement Ord
type MyType2 = HashSet<u32>; // T must implement Hash + Eq
Optional and default fields
My-Type ::= SEQUENCE {
    a BOOLEAN DEFAULT TRUE,
    b INTEGER OPTIONAL,
    c INTEGER DEFAULT 1
}

When parsing a single type:

type TypeB = Option<u32>;

Using custom derive attribute for a struct:

use asn1_rs::*;

#[derive(Debug, PartialEq, Sequence)]
pub struct MyType {
    #[default(true)]
    a: bool,
    #[optional]
    b: Option<u16>,
    #[default(1)]
    c: u16,
}
Tagged Explicit
-- Explicit tags
My-Type1 ::= [1] BOOLEAN
My-Type2 ::= [APPLICATION 2] INTEGER

Using tagged types:

use asn1_rs::*;
type MyType1<'a> = TaggedExplicit<bool, BerError<Input<'a>>, 1>;
type MyType2<'a> = ApplicationExplicit<Integer<'a>, BerError<Input<'a>>, 2>;

let x = MyType1::explicit(true);
let y = MyType2::explicit(Integer::from(4));

Note: the error type has to be specified in the type declaration.

or using the generic TaggedValue type:

use asn1_rs::*;
type T1<'a> = TaggedValue<
	bool,
	BerError<Input<'a>>,
	Explicit,
	{Class::CONTEXT_SPECIFIC},
	1>;
let x = T1::explicit(true);

Note: TaggedValue is more flexible, but requires more type annotations.

Tagged Implicit
-- Implicit tags
My-Type1 ::= [1] BOOLEAN
My-Type2 ::= [APPLICATION 2] INTEGER

Using tagged types:

use asn1_rs::*;
type MyType1<'a> = TaggedImplicit<bool, BerError<Input<'a>>, 1>;
type MyType2<'a> = ApplicationImplicit<Integer<'a>, BerError<Input<'a>>, 2>;

let x = MyType1::implicit(true);
let y = MyType2::implicit(Integer::from(4));

Note: the error type has to be specified in the type declaration.

or using the generic TaggedValue type:

use asn1_rs::*;
type T1<'a> = TaggedValue<
	bool,
	BerError<Input<'a>>,
	Implicit,
	{Class::CONTEXT_SPECIFIC},
	1>;
let x = T1::implicit(true);

Note: TaggedValue is more flexible, but requires more type annotations.

ANY

Not strictly an ASN.1 type

use asn1_rs::Any;
type MyType<'a> = Any<'a>;

In all of the above examples, parsing and encoding functions can be called directly on the generated type. For example:

use asn1_rs::{BerParser, Boolean};
type MyType = Boolean;

let input = asn1_rs::Input::from(b"\x01\x01\xff");
let (rem, my_object) = MyType::parse_ber(input).unwrap();

// Note: you can also use Rust primitive types directly:
let input = asn1_rs::Input::from(b"\x01\x01\xff");
let (rem, my_object) = <bool>::parse_ber(input).unwrap();

§Changes

See CHANGELOG.md, and UPGRADING.md for instructions for upgrading major versions.

§References

  • [X.680] Abstract Syntax Notation One (ASN.1): Specification of basic notation.
  • [X.690] ASN.1 encoding rules: Specification of Basic Encoding Rules (BER), Canonical Encoding Rules (CER) and Distinguished Encoding Rules (DER).

Re-exports§

pub use crate::Error;
pub use nom;
pub use bitvec;
pub use num_bigint;

Modules§

doc
Additional documentation: recipes, specific use cases and examples, etc.

Macros§

impl_toder_from_tober
Helper macro to implement ToDer for types where implementation is the same as ToBer
int
Helper macro to declare integers at compile-time
oid
Helper macro to declare integers at compile-time

Structs§

ASN1DateTime
Any
The Any object is not strictly an ASN.1 type, but holds a generic description of any object that could be encoded.
AnyIterator
Iterator for constructed sub-objects contained in an ANY object
AnySequence
The SEQUENCE object is an ordered list of heteregeneous types.
AnySet
The SET object is an unordered list of heteregeneous types.
BerClassFromIntError
BerError
BerGenericEncoder
Encoder for generic objects
BigIntbigint
A big signed integer type.
BigUintbigint
A big unsigned integer type.
BitString
ASN.1 BITSTRING type
BmpString
ASN.1 BMPSTRING type
Boolean
ASN.1 BOOLEAN type
Constructed
Encoder for constructed objects, with Definite length
ConstructedIndefinite
Encoder for constructed objects, with Indefinite length
EmbeddedPdv
EMBEDDED PDV ASN.1 Object
EndOfContent
End-of-contents octets
Enumerated
ASN.1 ENUMERATED type
GeneralString
ASN.1 restricted character string type (GeneralString)
GeneralizedTime
GraphicString
ASN.1 restricted character string type (GraphicString)
Header
BER/DER object header (identifier and length)
Ia5String
ASN.1 restricted character string type (Ia5String)
IndefiniteVec
Wrapper for sequence, to force using Indefinite length when serializing
Input
BER/DER parser input type
Integer
ASN.1 INTEGER type
Null
ASN.1 NULL type
NumericString
ASN.1 restricted character string type (NumericString)
ObjectDescriptor
ASN.1 restricted character string type (ObjectDescriptor)
OctetString
ASN.1 OCTETSTRING type
Oid
Object ID (OID) representation which can be relative or non-relative.
OptTaggedParser
Helper object to parse TAGGED OPTIONAL types (explicit or implicit)
Primitive
PrintableString
ASN.1 restricted character string type (PrintableString)
Sequence
The SEQUENCE object is an ordered list of heteregeneous types.
SequenceIterator
An Iterator over binary data, parsing elements of type T
SequenceIteratorInput
SequenceOf
The SEQUENCE OF object is an ordered list of homogeneous types.
Set
The SET object is an unordered list of heteregeneous types.
SetOf
The SET OF object is an unordered list of homogeneous types.
Tag
BER/DER Tag as defined in X.680 section 8.4
TaggedParser
TaggedParserBuilder
A builder for parsing tagged values (IMPLICIT or EXPLICIT)
TaggedValue
Helper object for creating FromBer/FromDer types for TAGGED OPTIONAL types
TeletexString
ASN.1 restricted character string type (TeletexString)
UniversalString
ASN.1 UniversalString type
UtcTime
Utf8String
ASN.1 restricted character string type (Utf8String)
VideotexString
ASN.1 restricted character string type (VideotexString)
VisibleString
ASN.1 restricted character string type (VisibleString)

Enums§

ASN1TimeZone
Class
BER Object class of tag
DerConstraint
Error types for DER constraints
Err
The Err enum indicates the parser was not successful
Error
The error type for operations of the FromBer, FromDer, and associated traits.
Explicit
A type parameter for EXPLICIT tagged values.
Implicit
A type parameter for IMPLICIT tagged values.
InnerError
The error type for operations of the FromBer, FromDer, and associated traits.
Length
BER Object Length
Needed
Contains information on needed data if a parser returned Incomplete
OidParseError
An error for OID parsing functions.
PdvIdentification
Real
ASN.1 REAL type
SerializeError
The error type for serialization operations of the ToDer trait.
Signbigint
A Sign is a BigInt’s composing element.

Constants§

MAX_RECURSION
Default maximum recursion limit

Traits§

Appendable
AsTaggedExplicit
Helper trait for creating tagged EXPLICIT values
AsTaggedImplicit
Helper trait for creating tagged IMPLICIT values
BerChoice
BerEncoder
Common trait for BER encoders
BerParser
Base trait for BER object parsers
CheckDerConstraints
Verification of DER constraints
Choice
DerAutoDerive
Trait to automatically derive FromDer
DerChoice
DerParser
Base trait for DER object parsers
DynTagged
Common trait for all tagged objects
FromBer
Base trait for BER object parsers
FromDer
Base trait for DER object parsers
GetObjectContent
TagKind
A type parameter for tagged values either Explicit or Implicit.
Tagged
TestValidCharset
Base trait for BER string objects and character set validation
ToBer
Common trait for BER encoding functions
ToDer
Common trait for DER encoding functions
ToStatic
Common trait for objects that can be transformed to a 'static version of self

Functions§

ber_get_content
Read the content bytes matching length defined in header (BER)
ber_header_length
Returns the length (in bytes) required for the full header (tag+length)
ber_length_constructed_items
Return the length (in bytes) required for a set of objects (BER)
ber_length_length
Returns the length (in bytes) required for the given length
ber_tag_length
Returns the length (in bytes) required for the given tag
ber_total_length
Returns the total length (header+content) required for an object, given the input parameters
der_get_content
Read the content bytes matching length defined in header (BER)
der_length_constructed_items
Return the length (in bytes) required for a set of objects (DER)
from_nom_bererror
Flatten all nom::Err variants error into a single error type
from_nom_error
Flatten all nom::Err variants error into a single error type
parse_der_tagged_explicit
parse_der_tagged_explicit_g
parse_der_tagged_implicit
parse_der_tagged_implicit_g

Type Aliases§

ApplicationExplicit
A helper object to parse [APPLICATION n] EXPLICIT T
ApplicationImplicit
A helper object to parse [APPLICATION n] IMPLICIT T
IResult
Holds the result of parsing functions
OptTaggedExplicit
A helper object to parse [ n ] EXPLICIT T OPTIONAL
OptTaggedImplicit
A helper object to parse [ n ] IMPLICIT T OPTIONAL
ParseResult
Holds the result of BER/DER serialization functions
PrivateExplicit
A helper object to parse [PRIVATE n] EXPLICIT T
PrivateImplicit
A helper object to parse [PRIVATE n] IMPLICIT T
Result
A specialized Result type for all operations from this crate.
SerializeResult
Holds the result of BER/DER encoding functions
SetIterator
An Iterator over binary data, parsing elements of type T
TaggedExplicit
A helper object to parse [ n ] EXPLICIT T
TaggedImplicit
A helper object to parse [ n ] IMPLICIT T

Derive Macros§

Alias
Create an ASN.1 type alias
BerAlias
BerAlias custom derive
BerParserAlias
BerParserAlias custom derive
BerParserSequence
BerParserSequence custom derive
BerParserSet
BerParserSet custom derive
BerSequence
BerSequence custom derive
BerSet
BerSet custom derive
Choice
Derive parsers and encoders for an enum representing a CHOICE
DerAlias
DerAlias custom derive
DerParserAlias
DerParserAlias custom derive
DerParserSequence
DerParserSequence custom derive
DerParserSet
DerParserSet custom derive
DerSequence
DerSequence custom derive
DerSet
DerSet custom derive
Enumerated
Derive parsers and encoders for an enum representing an ENUMERATED
Sequence
Derive parsers and encoders for a struct representing a SEQUENCE
Set
Derive parsers and encoders for a struct representing a SET
ToBerSequence
ToBerSequence custom derive
ToBerSet
ToBerSet custom derive
ToDerSequence
ToDerSequence custom derive
ToDerSet
ToDerSet custom derive
ToStatic
ToStatic custom derive