mod class;
mod number;
pub use self::{class::Class, number::TagNumber};
use crate::{Decodable, Decoder, Encodable, Encoder, Error, ErrorKind, Length, Result};
use core::{convert::TryFrom, fmt};
const CONSTRUCTED_FLAG: u8 = 0b100000;
pub trait Tagged {
const TAG: Tag;
}
#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord)]
#[non_exhaustive]
pub enum Tag {
Boolean,
Integer,
BitString,
OctetString,
Null,
ObjectIdentifier,
Utf8String,
Sequence,
Set,
PrintableString,
Ia5String,
UtcTime,
GeneralizedTime,
Application(TagNumber),
ContextSpecific(TagNumber),
Private(TagNumber),
}
impl Tag {
pub fn assert_eq(self, expected: Tag) -> Result<Tag> {
if self == expected {
Ok(self)
} else {
Err(self.unexpected_error(Some(expected)))
}
}
pub fn class(self) -> Class {
match self {
Tag::Application(_) => Class::Application,
Tag::ContextSpecific(_) => Class::ContextSpecific,
Tag::Private(_) => Class::Private,
_ => Class::Universal,
}
}
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::Utf8String => 0x0C,
Tag::Sequence => 0x10 | CONSTRUCTED_FLAG,
Tag::Set => 0x11 | CONSTRUCTED_FLAG,
Tag::PrintableString => 0x13,
Tag::Ia5String => 0x16,
Tag::UtcTime => 0x17,
Tag::GeneralizedTime => 0x18,
Tag::Application(number) | Tag::ContextSpecific(number) | Tag::Private(number) => {
self.class().octet(number, true)
}
}
}
pub fn non_canonical_error(self) -> Error {
ErrorKind::Value { tag: self }.into()
}
pub fn unexpected_error(self, expected: Option<Self>) -> Error {
ErrorKind::UnexpectedTag {
expected,
actual: self,
}
.into()
}
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> {
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),
0x0C => Ok(Tag::Utf8String),
0x13 => Ok(Tag::PrintableString),
0x16 => Ok(Tag::Ia5String),
0x17 => Ok(Tag::UtcTime),
0x18 => Ok(Tag::GeneralizedTime),
0x30 => Ok(Tag::Sequence), 0x31 => Ok(Tag::Set), 0x60..=0x7E => Ok(Tag::Application(TagNumber(byte & 0b11111))), 0xA0..=0xBE => Ok(Tag::ContextSpecific(TagNumber(byte & 0b11111))), 0xE0..=0xFE => Ok(Tag::Private(TagNumber(byte & 0b11111))), _ => Err(ErrorKind::UnknownTag { 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 Decodable<'_> for Tag {
fn decode(decoder: &mut Decoder<'_>) -> Result<Self> {
decoder.byte().and_then(Self::try_from)
}
}
impl Encodable for Tag {
fn encoded_len(&self) -> Result<Length> {
Ok(Length::ONE)
}
fn encode(&self, encoder: &mut Encoder<'_>) -> Result<()> {
encoder.byte(self.into())
}
}
impl fmt::Display for Tag {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
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::Utf8String => f.write_str("UTF8String"),
Tag::Set => f.write_str("SET"),
Tag::PrintableString => f.write_str("PrintableString"),
Tag::Ia5String => f.write_str("IA5String"),
Tag::UtcTime => f.write_str("UTCTime"),
Tag::GeneralizedTime => f.write_str("GeneralizedTime"),
Tag::Sequence => f.write_str("SEQUENCE"),
Tag::Application(n) => write!(f, "APPLICATION {}", n),
Tag::ContextSpecific(n) => write!(f, "CONTEXT-SPECIFIC {}", n),
Tag::Private(n) => write!(f, "PRIVATE {}", n),
}
}
}
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::Utf8String.class(), Class::Universal);
assert_eq!(Tag::Set.class(), Class::Universal);
assert_eq!(Tag::PrintableString.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 {
let tag_num = TagNumber::new(num);
assert_eq!(Tag::Application(tag_num).class(), Class::Application);
assert_eq!(
Tag::ContextSpecific(tag_num).class(),
Class::ContextSpecific
);
assert_eq!(Tag::Private(tag_num).class(), Class::Private);
}
}
}