use std::{fmt, io};
use crate::decode;
#[derive(Clone, Copy, Eq, PartialEq)]
pub struct Tag([u8; 4]);
impl Tag {
const CLASS_MASK: u8 = 0xc0;
const CONSTRUCTED_MASK: u8 = 0x20;
const SINGLEBYTE_DATA_MASK: u8 = 0x1f;
const MULTIBYTE_DATA_MASK: u8 = 0x7f;
const LAST_OCTET_MASK: u8 = 0x80;
const MAX_VAL_SPAN_3_OCTETS: u32 = 0x001f_ffff;
const MAX_VAL_SPAN_2_OCTETS: u32 = 0x3fff;
const MAX_VAL_SPAN_1_OCTET: u32 = 0x7f;
const MAX_VAL_FOURTH_OCTET: u32 = 0x1e;
const UNIVERSAL: u8 = 0x00;
const APPLICATION: u8 = 0x40;
const CONTEXT_SPECIFIC: u8 = 0x80;
const PRIVATE: u8 = 0xc0;
pub const END_OF_VALUE: Self = Tag([0, 0, 0, 0]);
pub const BOOLEAN: Self = Tag([1, 0, 0, 0]);
pub const INTEGER: Self = Tag([2, 0, 0, 0]);
pub const BIT_STRING: Self = Tag([3, 0, 0, 0]);
pub const OCTET_STRING: Self = Tag([4, 0, 0, 0]);
pub const NULL: Self = Tag([5, 0, 0, 0]);
pub const OID: Self = Tag([6, 0, 0, 0]);
pub const OBJECT_DESCRIPTOR: Self = Tag([7, 0, 0, 0]);
pub const EXTERNAL: Self = Tag([8, 0, 0, 0]);
pub const REAL: Self = Tag([9, 0, 0, 0]);
pub const ENUMERATED: Self = Tag([10, 0, 0, 0]);
pub const EMBEDDED_PDV: Self = Tag([11, 0, 0, 0]);
pub const UTF8_STRING: Self = Tag([12, 0, 0, 0]);
pub const RELATIVE_OID: Self = Tag([13, 0, 0, 0]);
pub const SEQUENCE: Self = Tag([16, 0, 0, 0]);
pub const SET: Self = Tag([17, 0, 0, 0]);
pub const NUMERIC_STRING: Self = Tag([18, 0, 0, 0]);
pub const PRINTABLE_STRING: Self = Tag([19, 0, 0, 0]);
pub const TELETEX_STRING: Self = Tag([20, 0, 0, 0]);
pub const VIDEOTEX_STRING: Self = Tag([21, 0, 0, 0]);
pub const IA5_STRING: Self = Tag([22, 0, 0, 0]);
pub const UTC_TIME: Self = Tag([23, 0, 0, 0]);
pub const GENERALIZED_TIME: Self = Tag([24, 0, 0, 0]);
pub const GRAPHIC_STRING: Self = Tag([25, 0, 0, 0]);
pub const VISIBLE_STRING: Self = Tag([26, 0, 0, 0]);
pub const GENERAL_STRING: Self = Tag([27, 0, 0, 0]);
pub const UNIVERSAL_STRING: Self = Tag([28, 0, 0, 0]);
pub const BMP_STRING: Self = Tag([29, 0, 0, 0]);
pub const CTX_0: Self = Tag([Tag::CONTEXT_SPECIFIC, 0, 0, 0]);
pub const CTX_1: Self = Tag([Tag::CONTEXT_SPECIFIC | 1, 0, 0, 0]);
pub const CTX_2: Self = Tag([Tag::CONTEXT_SPECIFIC | 2, 0, 0, 0]);
pub const CTX_3: Self = Tag([Tag::CONTEXT_SPECIFIC | 3, 0, 0, 0]);
pub const CTX_4: Self = Tag([Tag::CONTEXT_SPECIFIC | 4, 0, 0, 0]);
pub const CTX_5: Self = Tag([Tag::CONTEXT_SPECIFIC | 5, 0, 0, 0]);
pub const CTX_6: Self = Tag([Tag::CONTEXT_SPECIFIC | 6, 0, 0, 0]);
}
impl Tag {
#[inline]
fn new(class_mask: u8, number: u32) -> Self {
assert!(number <= Tag::MAX_VAL_SPAN_3_OCTETS);
if number <= Tag::MAX_VAL_FOURTH_OCTET {
Tag([class_mask | number as u8, 0, 0, 0])
} else if number <= Tag::MAX_VAL_SPAN_1_OCTET {
let number = number as u8;
Tag([class_mask | Tag::SINGLEBYTE_DATA_MASK, number, 0, 0])
} else if number <= Tag::MAX_VAL_SPAN_2_OCTETS {
let first_part = {
Tag::MULTIBYTE_DATA_MASK & ((number >> 7) as u8)
| Tag::LAST_OCTET_MASK
};
let second_part = Tag::MULTIBYTE_DATA_MASK & (number as u8);
Tag([
class_mask | Tag::SINGLEBYTE_DATA_MASK, first_part,
second_part, 0
])
} else {
let first_part = {
Tag::MULTIBYTE_DATA_MASK & ((number >> 14) as u8)
| Tag::LAST_OCTET_MASK
};
let second_part = {
Tag::MULTIBYTE_DATA_MASK & ((number >> 7) as u8)
| Tag::LAST_OCTET_MASK
};
let third_part = Tag::MULTIBYTE_DATA_MASK & (number as u8);
Tag([
class_mask | Tag::SINGLEBYTE_DATA_MASK, first_part,
second_part, third_part
])
}
}
pub fn universal(number: u32) -> Self {
Tag::new(Tag::UNIVERSAL, number)
}
pub fn application(number: u32) -> Self {
Tag::new(Tag::APPLICATION, number)
}
pub fn ctx(number: u32) -> Self {
Tag::new(Tag::CONTEXT_SPECIFIC, number)
}
pub fn private(number: u32) -> Self {
Tag::new(Tag::PRIVATE, number)
}
pub fn is_universal(self) -> bool {
self.0[0] & Self::CLASS_MASK == Self::UNIVERSAL
}
pub fn is_application(self) -> bool {
self.0[0] & Self::CLASS_MASK == Self::APPLICATION
}
pub fn is_context_specific(self) -> bool {
self.0[0] & Self::CLASS_MASK == Self::CONTEXT_SPECIFIC
}
pub fn is_private(self) -> bool {
self.0[0] & Self::CLASS_MASK == Self::PRIVATE
}
pub fn number(self) -> u32 {
if (Tag::SINGLEBYTE_DATA_MASK & self.0[0]) != Tag::SINGLEBYTE_DATA_MASK {
u32::from(Tag::SINGLEBYTE_DATA_MASK & self.0[0])
} else if Tag::LAST_OCTET_MASK & self.0[1] == 0 {
u32::from(Tag::MULTIBYTE_DATA_MASK & self.0[1])
} else if Tag::LAST_OCTET_MASK & self.0[2] == 0 {
u32::from(Tag::MULTIBYTE_DATA_MASK & self.0[1]) << 7
| u32::from(Tag::MULTIBYTE_DATA_MASK & self.0[2])
} else {
u32::from(Tag::MULTIBYTE_DATA_MASK & self.0[1]) << 14
| u32::from(Tag::MULTIBYTE_DATA_MASK & self.0[2]) << 7
| u32::from(Tag::MULTIBYTE_DATA_MASK & self.0[3])
}
}
pub fn take_from<S: decode::Source>(
source: &mut S,
) -> Result<(Self, bool), S::Err> {
let byte = source.take_u8()?;
let mut data = [byte & !Tag::CONSTRUCTED_MASK, 0, 0, 0];
let constructed = byte & Tag::CONSTRUCTED_MASK != 0;
if (data[0] & Tag::SINGLEBYTE_DATA_MASK) == Tag::SINGLEBYTE_DATA_MASK {
for i in 1..=3 {
data[i] = source.take_u8()?;
if data[i] & Tag::LAST_OCTET_MASK == 0 {
return Ok((Tag(data), constructed));
}
}
} else {
return Ok((Tag(data), constructed));
}
xerr!(Err(decode::Error::Unimplemented.into()))
}
pub fn take_from_if<S: decode::Source>(
self,
source: &mut S,
) -> Result<Option<bool>, S::Err> {
if source.request(1)? == 0 {
return Ok(None)
}
let byte = source.slice()[0];
let mut data = [byte & !Tag::CONSTRUCTED_MASK, 0, 0, 0];
if (data[0] & Tag::SINGLEBYTE_DATA_MASK) == Tag::SINGLEBYTE_DATA_MASK {
let mut i = 1;
loop {
if source.request(i + 1)? == 0 {
xerr!(return Err(decode::Error::Malformed.into()))
}
data[i] = source.slice()[i];
if data[i] & Tag::LAST_OCTET_MASK == 0 {
break
}
if i == 3 {
xerr!(return Err(decode::Error::Unimplemented.into()))
}
i += 1;
}
}
let (tag, compressed) = (Tag(data), byte & Tag::CONSTRUCTED_MASK != 0);
if tag == self {
source.advance(tag.encoded_len())?;
Ok(Some(compressed))
}
else {
Ok(None)
}
}
#[allow(clippy::trivially_copy_pass_by_ref)]
pub fn encoded_len(&self) -> usize {
if (Tag::SINGLEBYTE_DATA_MASK & self.0[0]) != Tag::SINGLEBYTE_DATA_MASK {
1
} else if Tag::LAST_OCTET_MASK & self.0[1] == 0 {
2
} else if Tag::LAST_OCTET_MASK & self.0[2] == 0 {
3
} else {
4
}
}
#[allow(clippy::trivially_copy_pass_by_ref)]
pub fn write_encoded<W: io::Write>(
&self,
constructed: bool,
target: &mut W
) -> Result<(), io::Error> {
let mut buf = self.0;
if constructed {
buf[0] |= Tag::CONSTRUCTED_MASK
}
target.write_all(&buf[..self.encoded_len()])
}
}
impl fmt::Display for Tag {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Tag::BOOLEAN => write!(f, "BOOLEAN"),
Tag::INTEGER => write!(f, "INTEGER"),
Tag::BIT_STRING => write!(f, "BIT STRING"),
Tag::OCTET_STRING => write!(f, "OCTET STRING"),
Tag::NULL => write!(f, "NULL"),
Tag::OID => write!(f, "OBJECT IDENTIFIER"),
Tag::OBJECT_DESCRIPTOR => write!(f, "ObjectDescriptor"),
Tag::EXTERNAL => write!(f, "EXTERNAL"),
Tag::REAL => write!(f, "REAL"),
Tag::ENUMERATED => write!(f, "ENUMERATED"),
Tag::EMBEDDED_PDV => write!(f, "EMBEDDED PDV"),
Tag::UTF8_STRING => write!(f, "UTF8String"),
Tag::RELATIVE_OID => write!(f, "RELATIVE-OID"),
Tag::SEQUENCE => write!(f, "SEQUENCE"),
Tag::SET => write!(f, "SET"),
Tag::NUMERIC_STRING => write!(f, "NumericString"),
Tag::PRINTABLE_STRING => write!(f, "PrintableString"),
Tag::TELETEX_STRING => write!(f, "TeletexString"),
Tag::VIDEOTEX_STRING => write!(f, "VideotexString"),
Tag::IA5_STRING => write!(f, "IA5String"),
Tag::UTC_TIME => write!(f, "UTCTime"),
Tag::GENERALIZED_TIME => write!(f, "GeneralizedTime"),
Tag::GRAPHIC_STRING => write!(f, "GraphicString"),
Tag::VISIBLE_STRING => write!(f, "VisibleString"),
Tag::GENERAL_STRING => write!(f, "GeneralString"),
Tag::UNIVERSAL_STRING => write!(f, "UniversalString"),
Tag::BMP_STRING => write!(f, "BMPString"),
tag => {
match tag.0[0] & Tag::CLASS_MASK {
Tag::UNIVERSAL => write!(f, "[UNIVERSAL ")?,
Tag::APPLICATION => write!(f, "[APPLICATION ")?,
Tag::CONTEXT_SPECIFIC => write!(f, "[")?,
Tag::PRIVATE => write!(f, "[PRIVATE ")?,
_ => unreachable!()
}
write!(f, "{}]", tag.number())
}
}
}
}
impl fmt::Debug for Tag {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Tag({})", self)
}
}
#[cfg(test)]
mod test {
use super::*;
const TYPES: &[u8] = &[Tag::UNIVERSAL, Tag::APPLICATION, Tag::CONTEXT_SPECIFIC, Tag::PRIVATE];
#[test]
fn test_single_octet_tags() {
let range: Vec<u32> = (0..5).chain(
Tag::MAX_VAL_FOURTH_OCTET-5..Tag::MAX_VAL_FOURTH_OCTET
).collect();
for &typ in TYPES {
for i in range.clone() {
let tag = Tag::new(typ, i);
let expected = Tag([typ | i as u8, 0, 0, 0]);
let decoded = Tag::take_from(&mut &tag.0[..]).unwrap();
assert_eq!(tag.take_from_if(&mut &tag.0[..]), Ok(Some(false)));
assert_eq!(decoded.1, false);
assert_eq!(decoded.0, expected);
assert_eq!(tag.number(), i);
assert_eq!(tag, expected);
}
}
}
#[test]
fn test_double_octets_tags() {
let range: Vec<u32> = (
Tag::MAX_VAL_FOURTH_OCTET+1..Tag::MAX_VAL_FOURTH_OCTET+5
).chain(
Tag::MAX_VAL_SPAN_1_OCTET-5..Tag::MAX_VAL_SPAN_1_OCTET
).collect();
for &typ in TYPES {
for i in range.clone() {
let tag = Tag::new(typ, i);
let expected = Tag([
Tag::SINGLEBYTE_DATA_MASK | typ, i as u8, 0, 0
]);
let decoded = Tag::take_from(&mut &tag.0[..]).unwrap();
assert_eq!(tag.take_from_if(&mut &tag.0[..]), Ok(Some(false)));
assert_eq!(decoded.1, false);
assert_eq!(decoded.0, expected);
assert_eq!(tag.number(), i);
assert_eq!(tag, expected);
}
}
}
#[test]
fn test_three_octets_tags() {
let range: Vec<u32> = (
Tag::MAX_VAL_SPAN_1_OCTET+1..Tag::MAX_VAL_SPAN_1_OCTET + 5
).chain(
Tag::MAX_VAL_SPAN_2_OCTETS-5..Tag::MAX_VAL_SPAN_2_OCTETS
).collect();
for &typ in TYPES {
for i in range.clone() {
let tag = Tag::new(typ, i);
let expected = Tag([
Tag::SINGLEBYTE_DATA_MASK | typ,
(i >> 7) as u8 | Tag::LAST_OCTET_MASK,
i as u8 & !Tag::LAST_OCTET_MASK,
0
]);
let decoded = Tag::take_from(&mut &tag.0[..]).unwrap();
assert_eq!(tag.take_from_if(&mut &tag.0[..]), Ok(Some(false)));
assert_eq!(decoded.1, false);
assert_eq!(decoded.0, expected);
assert_eq!(tag.number(), i);
assert_eq!(tag, expected);
}
}
}
#[test]
fn test_four_octets_tags() {
let range: Vec<u32> = (
Tag::MAX_VAL_SPAN_2_OCTETS+1..Tag::MAX_VAL_SPAN_2_OCTETS + 5
).chain(
Tag::MAX_VAL_SPAN_3_OCTETS-5..Tag::MAX_VAL_SPAN_3_OCTETS
).collect();
for &typ in TYPES {
for i in range.clone() {
let tag = Tag::new(typ, i);
let expected = Tag([
Tag::SINGLEBYTE_DATA_MASK | typ,
(i >> 14) as u8 | Tag::LAST_OCTET_MASK,
(i >> 7) as u8 | Tag::LAST_OCTET_MASK,
i as u8 & !Tag::LAST_OCTET_MASK
]);
let decoded = Tag::take_from(&mut &tag.0[..]).unwrap();
assert_eq!(tag.take_from_if(&mut &tag.0[..]), Ok(Some(false)));
assert_eq!(decoded.1, false);
assert_eq!(decoded.0, expected);
assert_eq!(tag.number(), i);
assert_eq!(tag, expected);
}
}
}
#[test]
fn test_tags_failures() {
let large_tag = [
0b1111_1111, 0b1000_0000, 0b1000_0000, 0b1000_0000, 0b1000_0000
];
assert_eq!(
Tag::take_from(&mut &large_tag[..]),
Err(decode::Error::Unimplemented)
);
let short_tag = [0b1111_1111, 0b1000_0000];
assert_eq!(
Tag::take_from(&mut &short_tag[..]),
Err(decode::Error::Malformed)
);
}
}