Skip to content

Commit

Permalink
der: add TagNumber type, make Tag structured (#464)
Browse files Browse the repository at this point in the history
Adds a `TagNumber` type for representing the lower 5 bits of a Tag,
where bits 8/7 identify the class and bit 6 identifies primitive vs
constructed forms.

Changes `Tag` from being `repr(u8)` to a structured representation with
`Application`, `ContextSpecific`, and `Private` variants, each of which
includes a `TagNumber`. This enables support for all of these classes
for tag numbers up to 30, which is the maximum value which can be
represented with a single leading identifier octet.

This change enables the `ContextSpecific` struct to have public members,
as the tag number invariant can now be maintained by the `TagNumber`
type instead.
  • Loading branch information
tarcieri authored Jun 3, 2021
1 parent 92fe26d commit 863f59d
Show file tree
Hide file tree
Showing 5 changed files with 243 additions and 260 deletions.
4 changes: 0 additions & 4 deletions der/derive/src/choice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,9 +224,6 @@ impl DeriveChoice {
/// ASN.1 `CHOICE` alternative: one of the ASN.1 types comprising the `CHOICE`
/// which maps to an enum variant.
struct Alternative {
/// ASN.1 type for this alternative.
pub asn1_type: Asn1Type,

/// [`Ident`] for the corresponding enum variant.
pub ident: Ident,

Expand All @@ -246,7 +243,6 @@ impl Alternative {
};

let alternative = Self {
asn1_type,
ident: variant.ident.clone(),
field_type,
};
Expand Down
77 changes: 21 additions & 56 deletions der/src/asn1/context_specific.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,61 +2,32 @@
use crate::{
Any, Choice, Decodable, Encodable, Encoder, Error, ErrorKind, Header, Length, Result, Tag,
TagNumber,
};
use core::convert::TryFrom;

/// Context-specific field.
///
/// This type encodes a field which is specific to a particular context,
/// and has a special "context-specific tag" (presently 0-15 supported).
/// and is identified by a [`TagNumber`].
///
/// Any context-specific field can be decoded/encoded with this type.
/// The intended use is to dynamically dispatch off of the context-specific
/// tag when decoding, which allows support for extensions, which are denoted
/// in an ASN.1 schema using the `...` ellipsis extension marker.
///
///
/// tag number when decoding, which allows support for extensions, which are
/// denoted in an ASN.1 schema using the `...` ellipsis extension marker.
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub struct ContextSpecific<'a> {
/// Context-specific tag value sans the leading `0b10000000` class
/// Context-specific tag number sans the leading `0b10000000` class
/// identifier bit and `0b100000` constructed flag.
pub(crate) tag: u8,
pub tag_number: TagNumber,

/// Value of the field.
pub(crate) value: Any<'a>,
}

impl<'a> ContextSpecific<'a> {
/// Create a new context-specific field.
///
/// The tag value includes only lower 6-bits of the context specific tag,
/// sans the leading `10` high bits identifying the context-specific tag
/// class as well as the constructed flag.
pub fn new(tag: u8, value: Any<'a>) -> Result<Self> {
// Ensure we consider the context-specific tag valid
Tag::context_specific(tag)?;

Ok(Self { tag, value })
}

/// Get the context-specific tag for this field.
///
/// The tag value includes only lower 6-bits of the context specific tag,
/// sans the leading `10` high bits identifying the context-specific tag
/// class as well as the constructed flag.
pub fn tag(self) -> u8 {
self.tag
}

/// Get the value of this context-specific tag.
pub fn value(self) -> Any<'a> {
self.value
}
pub value: Any<'a>,
}

impl<'a> Choice<'a> for ContextSpecific<'a> {
fn can_decode(tag: Tag) -> bool {
tag.is_context_specific()
matches!(tag, Tag::ContextSpecific(_))
}
}

Expand All @@ -66,7 +37,7 @@ impl<'a> Encodable for ContextSpecific<'a> {
}

fn encode(&self, encoder: &mut Encoder<'_>) -> Result<()> {
let tag = Tag::context_specific(self.tag)?;
let tag = Tag::ContextSpecific(self.tag_number);
Header::new(tag, self.value.encoded_len()?)?.encode(encoder)?;
self.value.encode(encoder)
}
Expand All @@ -82,21 +53,17 @@ impl<'a> TryFrom<Any<'a>> for ContextSpecific<'a> {
type Error = Error;

fn try_from(any: Any<'a>) -> Result<ContextSpecific<'a>> {
let tag = if any.tag().is_context_specific() {
(any.tag() as u8)
.checked_sub(0xA0)
.ok_or(ErrorKind::Overflow)?
} else {
return Err(ErrorKind::UnexpectedTag {
match any.tag() {
Tag::ContextSpecific(tag_number) => Ok(Self {
tag_number,
value: Any::from_der(any.as_bytes())?,
}),
actual => Err(ErrorKind::UnexpectedTag {
expected: None,
actual: any.tag(),
actual,
}
.into());
};

let value = Any::from_der(any.as_bytes())?;

Self::new(tag, value)
.into()),
}
}
}

Expand All @@ -113,11 +80,9 @@ mod tests {
#[test]
fn round_trip() {
let field = ContextSpecific::from_der(EXAMPLE_BYTES).unwrap();
assert_eq!(field.tag(), 1);

let value = field.value();
assert_eq!(value.tag(), Tag::BitString);
assert_eq!(value.as_bytes(), &EXAMPLE_BYTES[5..]);
assert_eq!(field.tag_number.value(), 1);
assert_eq!(field.value.tag(), Tag::BitString);
assert_eq!(field.value.as_bytes(), &EXAMPLE_BYTES[5..]);

let mut buf = [0u8; 128];
let encoded = field.encode_to_slice(&mut buf).unwrap();
Expand Down
6 changes: 4 additions & 2 deletions der/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
//!
//! This crate provides a `no_std`-friendly implementation of a subset of ASN.1
//! DER necessary for decoding/encoding various cryptography-related formats
//! implemented as part of the [RustCrypto] project, e.g. the [`pkcs8`] crate.
//! implemented as part of the [RustCrypto] project, e.g. the [`pkcs5`] and
//! [`pkcs8`] crates.
//!
//! The core implementation avoids any heap usage (with convenience methods
//! that allocate gated under the off-by-default `alloc` feature).
Expand Down Expand Up @@ -315,6 +316,7 @@
//!
//! [X.690]: https://www.itu.int/rec/T-REC-X.690/
//! [RustCrypto]: https://github.com/rustcrypto
//! [`pkcs5`]: https://docs.rs/pkcs5/
//! [`pkcs8`]: https://docs.rs/pkcs8/
//! [RustCrypto/utils#370]: https://github.com/RustCrypto/utils/issues/370
//! [RFC 5280 Section 4.1.1.2]: https://tools.ietf.org/html/rfc5280#section-4.1.1.2
Expand Down Expand Up @@ -376,7 +378,7 @@ pub use crate::{
header::Header,
length::Length,
message::Message,
tag::{Class, Tag, Tagged},
tag::{Class, Tag, TagNumber, Tagged},
};

pub(crate) use crate::byte_slice::ByteSlice;
Expand Down
Loading

0 comments on commit 863f59d

Please sign in to comment.