blob: 7a1fed1b77adcbf2d11a7bb2e6ec922d328b2802 [file] [log] [blame]
//! ASN.1 tags.
#![cfg_attr(feature = "arbitrary", allow(clippy::integer_arithmetic))]
mod class;
mod mode;
mod number;
pub use self::{class::Class, mode::TagMode, number::TagNumber};
use crate::{Decode, DerOrd, Encode, Error, ErrorKind, Length, Reader, Result, Writer};
use core::{cmp::Ordering, fmt};
/// Indicator bit for constructed form encoding (i.e. vs primitive form)
const CONSTRUCTED_FLAG: u8 = 0b100000;
/// Types which have a constant ASN.1 [`Tag`].
pub trait FixedTag {
/// ASN.1 tag
const TAG: Tag;
}
/// Types which have an ASN.1 [`Tag`].
pub trait Tagged {
/// Get the ASN.1 tag that this type is encoded with.
fn tag(&self) -> Tag;
}
/// Types which are [`FixedTag`] always have a known [`Tag`] type.
impl<T: FixedTag> Tagged for T {
fn tag(&self) -> Tag {
T::TAG
}
}
/// ASN.1 tags.
///
/// Tags are the leading identifier octet of the Tag-Length-Value encoding
/// used by ASN.1 DER and identify the type of the subsequent value.
///
/// They are described in X.690 Section 8.1.2: Identifier octets, and
/// structured as follows:
///
/// ```text
/// | Class | P/C | Tag Number |
/// ```
///
/// - 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 {
/// `BOOLEAN` tag: `1`.
Boolean,
/// `INTEGER` tag: `2`.
Integer,
/// `BIT STRING` tag: `3`.
BitString,
/// `OCTET STRING` tag: `4`.
OctetString,
/// `NULL` tag: `5`.
Null,
/// `OBJECT IDENTIFIER` tag: `6`.
ObjectIdentifier,
/// `REAL` tag: `9`.
Real,
/// `ENUMERATED` tag: `10`.
Enumerated,
/// `UTF8String` tag: `12`.
Utf8String,
/// `SEQUENCE` tag: `16`.
Sequence,
/// `SET` and `SET OF` tag: `17`.
Set,
/// `NumericString` tag: `18`.
NumericString,
/// `PrintableString` tag: `19`.
PrintableString,
/// `TeletexString` tag: `20`.
TeletexString,
/// `VideotexString` tag: `21`.
VideotexString,
/// `IA5String` tag: `22`.
Ia5String,
/// `UTCTime` tag: `23`.
UtcTime,
/// `GeneralizedTime` tag: `24`.
GeneralizedTime,
/// `VisibleString` tag: `26`.
VisibleString,
/// `BMPString` tag: `30`.
BmpString,
/// Application tag.
Application {
/// Is this tag constructed? (vs primitive).
constructed: bool,
/// Tag number.
number: TagNumber,
},
/// Context-specific tag.
ContextSpecific {
/// Is this tag constructed? (vs primitive).
constructed: bool,
/// Tag number.
number: TagNumber,
},
/// Private tag number.
Private {
/// Is this tag constructed? (vs primitive).
constructed: bool,
/// Tag number.
number: TagNumber,
},
}
impl Tag {
/// Assert that this [`Tag`] matches the provided expected tag.
///
/// On mismatch, returns an [`Error`] with [`ErrorKind::TagUnexpected`].
pub fn assert_eq(self, expected: Tag) -> Result<Tag> {
if self == expected {
Ok(self)
} else {
Err(self.unexpected_error(Some(expected)))
}
}
/// Get the [`Class`] that corresponds to this [`Tag`].
pub fn class(self) -> Class {
match self {
Tag::Application { .. } => Class::Application,
Tag::ContextSpecific { .. } => Class::ContextSpecific,
Tag::Private { .. } => Class::Private,
_ => Class::Universal,
}
}
/// Get the [`TagNumber`] (lower 6-bits) for this tag.
pub fn number(self) -> TagNumber {
TagNumber(self.octet() & TagNumber::MASK)
}
/// Does this tag represent a constructed (as opposed to primitive) field?
pub fn is_constructed(self) -> bool {
self.octet() & CONSTRUCTED_FLAG != 0
}
/// Is this an application tag?
pub fn is_application(self) -> bool {
self.class() == Class::Application
}
/// Is this a context-specific tag?
pub fn is_context_specific(self) -> bool {
self.class() == Class::ContextSpecific
}
/// Is this a private tag?
pub fn is_private(self) -> bool {
self.class() == Class::Private
}
/// Is this a universal tag?
pub fn is_universal(self) -> bool {
self.class() == Class::Universal
}
/// Get the octet encoding for this [`Tag`].
pub fn octet(self) -> u8 {
match self {
Tag::Boolean => 0x01,
Tag::Integer => 0x02,
Tag::BitString => 0x03,
Tag::OctetString => 0x04,
Tag::Null => 0x05,
Tag::ObjectIdentifier => 0x06,
Tag::Real => 0x09,
Tag::Enumerated => 0x0A,
Tag::Utf8String => 0x0C,
Tag::Sequence => 0x10 | CONSTRUCTED_FLAG,
Tag::Set => 0x11 | CONSTRUCTED_FLAG,
Tag::NumericString => 0x12,
Tag::PrintableString => 0x13,
Tag::TeletexString => 0x14,
Tag::VideotexString => 0x15,
Tag::Ia5String => 0x16,
Tag::UtcTime => 0x17,
Tag::GeneralizedTime => 0x18,
Tag::VisibleString => 0x1A,
Tag::BmpString => 0x1E,
Tag::Application {
constructed,
number,
}
| Tag::ContextSpecific {
constructed,
number,
}
| Tag::Private {
constructed,
number,
} => self.class().octet(constructed, number),
}
}
/// Create an [`Error`] for an invalid [`Length`].
pub fn length_error(self) -> Error {
ErrorKind::Length { tag: self }.into()
}
/// Create an [`Error`] for an non-canonical value with the ASN.1 type
/// identified by this tag.
pub fn non_canonical_error(self) -> Error {
ErrorKind::Noncanonical { tag: self }.into()
}
/// Create an [`Error`] because the current tag was unexpected, with an
/// optional expected tag.
pub fn unexpected_error(self, expected: Option<Self>) -> Error {
ErrorKind::TagUnexpected {
expected,
actual: self,
}
.into()
}
/// Create an [`Error`] for an invalid value with the ASN.1 type identified
/// by this tag.
pub fn value_error(self) -> Error {
ErrorKind::Value { tag: self }.into()
}
}
impl TryFrom<u8> for Tag {
type Error = Error;
fn try_from(byte: u8) -> Result<Tag> {
let constructed = byte & CONSTRUCTED_FLAG != 0;
let number = TagNumber::try_from(byte & TagNumber::MASK)?;
match byte {
0x01 => Ok(Tag::Boolean),
0x02 => Ok(Tag::Integer),
0x03 => Ok(Tag::BitString),
0x04 => Ok(Tag::OctetString),
0x05 => Ok(Tag::Null),
0x06 => Ok(Tag::ObjectIdentifier),
0x09 => Ok(Tag::Real),
0x0A => Ok(Tag::Enumerated),
0x0C => Ok(Tag::Utf8String),
0x12 => Ok(Tag::NumericString),
0x13 => Ok(Tag::PrintableString),
0x14 => Ok(Tag::TeletexString),
0x15 => Ok(Tag::VideotexString),
0x16 => Ok(Tag::Ia5String),
0x17 => Ok(Tag::UtcTime),
0x18 => Ok(Tag::GeneralizedTime),
0x1A => Ok(Tag::VisibleString),
0x1E => Ok(Tag::BmpString),
0x30 => Ok(Tag::Sequence), // constructed
0x31 => Ok(Tag::Set), // constructed
0x40..=0x7E => Ok(Tag::Application {
constructed,
number,
}),
0x80..=0xBE => Ok(Tag::ContextSpecific {
constructed,
number,
}),
0xC0..=0xFE => Ok(Tag::Private {
constructed,
number,
}),
_ => Err(ErrorKind::TagUnknown { byte }.into()),
}
}
}
impl From<Tag> for u8 {
fn from(tag: Tag) -> u8 {
tag.octet()
}
}
impl From<&Tag> for u8 {
fn from(tag: &Tag) -> u8 {
u8::from(*tag)
}
}
impl<'a> Decode<'a> for Tag {
fn decode<R: Reader<'a>>(reader: &mut R) -> Result<Self> {
reader.read_byte().and_then(Self::try_from)
}
}
impl Encode for Tag {
fn encoded_len(&self) -> Result<Length> {
Ok(Length::ONE)
}
fn encode(&self, writer: &mut impl Writer) -> Result<()> {
writer.write_byte(self.into())
}
}
impl DerOrd for Tag {
fn der_cmp(&self, other: &Self) -> Result<Ordering> {
Ok(self.octet().cmp(&other.octet()))
}
}
impl fmt::Display for Tag {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
const FIELD_TYPE: [&str; 2] = ["primitive", "constructed"];
match *self {
Tag::Boolean => f.write_str("BOOLEAN"),
Tag::Integer => f.write_str("INTEGER"),
Tag::BitString => f.write_str("BIT STRING"),
Tag::OctetString => f.write_str("OCTET STRING"),
Tag::Null => f.write_str("NULL"),
Tag::ObjectIdentifier => f.write_str("OBJECT IDENTIFIER"),
Tag::Real => f.write_str("REAL"),
Tag::Enumerated => f.write_str("ENUMERATED"),
Tag::Utf8String => f.write_str("UTF8String"),
Tag::Set => f.write_str("SET"),
Tag::NumericString => f.write_str("NumericString"),
Tag::PrintableString => f.write_str("PrintableString"),
Tag::TeletexString => f.write_str("TeletexString"),
Tag::VideotexString => f.write_str("VideotexString"),
Tag::Ia5String => f.write_str("IA5String"),
Tag::UtcTime => f.write_str("UTCTime"),
Tag::GeneralizedTime => f.write_str("GeneralizedTime"),
Tag::VisibleString => f.write_str("VisibleString"),
Tag::BmpString => f.write_str("BMPString"),
Tag::Sequence => f.write_str("SEQUENCE"),
Tag::Application {
constructed,
number,
} => write!(
f,
"APPLICATION [{}] ({})",
number,
FIELD_TYPE[usize::from(constructed)]
),
Tag::ContextSpecific {
constructed,
number,
} => write!(
f,
"CONTEXT-SPECIFIC [{}] ({})",
number,
FIELD_TYPE[usize::from(constructed)]
),
Tag::Private {
constructed,
number,
} => write!(
f,
"PRIVATE [{}] ({})",
number,
FIELD_TYPE[usize::from(constructed)]
),
}
}
}
impl fmt::Debug for Tag {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Tag(0x{:02x}: {})", u8::from(*self), self)
}
}
#[cfg(test)]
mod tests {
use super::TagNumber;
use super::{Class, Tag};
#[test]
fn tag_class() {
assert_eq!(Tag::Boolean.class(), Class::Universal);
assert_eq!(Tag::Integer.class(), Class::Universal);
assert_eq!(Tag::BitString.class(), Class::Universal);
assert_eq!(Tag::OctetString.class(), Class::Universal);
assert_eq!(Tag::Null.class(), Class::Universal);
assert_eq!(Tag::ObjectIdentifier.class(), Class::Universal);
assert_eq!(Tag::Real.class(), Class::Universal);
assert_eq!(Tag::Enumerated.class(), Class::Universal);
assert_eq!(Tag::Utf8String.class(), Class::Universal);
assert_eq!(Tag::Set.class(), Class::Universal);
assert_eq!(Tag::NumericString.class(), Class::Universal);
assert_eq!(Tag::PrintableString.class(), Class::Universal);
assert_eq!(Tag::TeletexString.class(), Class::Universal);
assert_eq!(Tag::VideotexString.class(), Class::Universal);
assert_eq!(Tag::Ia5String.class(), Class::Universal);
assert_eq!(Tag::UtcTime.class(), Class::Universal);
assert_eq!(Tag::GeneralizedTime.class(), Class::Universal);
assert_eq!(Tag::Sequence.class(), Class::Universal);
for num in 0..=30 {
for &constructed in &[false, true] {
let number = TagNumber::new(num);
assert_eq!(
Tag::Application {
constructed,
number
}
.class(),
Class::Application
);
assert_eq!(
Tag::ContextSpecific {
constructed,
number
}
.class(),
Class::ContextSpecific
);
assert_eq!(
Tag::Private {
constructed,
number
}
.class(),
Class::Private
);
}
}
}
}