Skip to content

Commit

Permalink
feat(asn1-parser): implement implicit tagging
Browse files Browse the repository at this point in the history
  • Loading branch information
TheBestTvarynka committed Jan 19, 2024
1 parent 2328d9d commit 65b7512
Show file tree
Hide file tree
Showing 8 changed files with 141 additions and 7 deletions.
2 changes: 1 addition & 1 deletion crates/asn1-parser/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ This `asn1` parser is aimed to parse input bytes and return an AST as the result
---

- [X] [ExplicitTag](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/asn1-tags.html)
- [ ] [ImplicitTag](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/asn1-tags.html)
- [X] [ImplicitTag](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/asn1-tags.html)
- [X] [ApplicationTag](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/asn1-tags.html)

## Usage example
Expand Down
12 changes: 10 additions & 2 deletions crates/asn1-parser/src/asn1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ use core::ops::Range;
use crate::reader::Reader;
use crate::writer::Writer;
use crate::{
ApplicationTag, Asn1Encoder, Asn1Result, Asn1ValueDecoder, BitString, BmpString, Bool, Error, ExplicitTag, Integer,
MetaInfo, Null, ObjectIdentifier, OctetString, Sequence, Set, Tag, Taggable, Tlv, Utf8String,
ApplicationTag, Asn1Encoder, Asn1Result, Asn1ValueDecoder, BitString, BmpString, Bool, Error, ExplicitTag,
ImplicitTag, Integer, MetaInfo, Null, ObjectIdentifier, OctetString, Sequence, Set, Tag, Taggable, Tlv, Utf8String,
};

#[derive(Debug, Clone, PartialEq, Eq)]
Expand All @@ -24,6 +24,7 @@ pub enum Asn1Type<'data> {
ObjectIdentifier(ObjectIdentifier),

ExplicitTag(ExplicitTag<'data>),
ImplicitTag(ImplicitTag<'data>),
ApplicationTag(ApplicationTag<'data>),
}

Expand All @@ -45,6 +46,7 @@ impl Asn1Type<'_> {
Asn1Type::Integer(i) => Asn1Type::Integer(i.to_owned()),
Asn1Type::ObjectIdentifier(o) => Asn1Type::ObjectIdentifier(o.clone()),
Asn1Type::ExplicitTag(e) => Asn1Type::ExplicitTag(e.to_owned()),
Asn1Type::ImplicitTag(i) => Asn1Type::ImplicitTag(i.to_owned()),
Asn1Type::ApplicationTag(a) => Asn1Type::ApplicationTag(a.to_owned()),
Asn1Type::BmpString(b) => Asn1Type::BmpString(b.to_owned()),
}
Expand All @@ -65,6 +67,7 @@ impl Taggable for Asn1Type<'_> {
Asn1Type::Integer(i) => i.tag(),
Asn1Type::ObjectIdentifier(o) => o.tag(),
Asn1Type::ExplicitTag(e) => e.tag(),
Asn1Type::ImplicitTag(i) => i.tag(),
Asn1Type::ApplicationTag(a) => a.tag(),
}
}
Expand Down Expand Up @@ -92,6 +95,8 @@ impl<'data> Asn1ValueDecoder<'data> for Asn1Type<'data> {
Ok(Asn1Type::ObjectIdentifier(ObjectIdentifier::decode(tag, reader)?))
} else if ExplicitTag::compare_tags(tag) {
Ok(Asn1Type::ExplicitTag(ExplicitTag::decode(tag, reader)?))
} else if ImplicitTag::compare_tags(tag) {
Ok(Asn1Type::ImplicitTag(ImplicitTag::decode(tag, reader)?))
} else if ApplicationTag::compare_tags(tag) {
Ok(Asn1Type::ApplicationTag(ApplicationTag::decode(tag, reader)?))
} else if Null::compare_tags(tag) {
Expand Down Expand Up @@ -119,6 +124,7 @@ impl Asn1Encoder for Asn1Type<'_> {
Asn1Type::Integer(integer) => integer.needed_buf_size(),
Asn1Type::ObjectIdentifier(object_identifier) => object_identifier.needed_buf_size(),
Asn1Type::ExplicitTag(e) => e.needed_buf_size(),
Asn1Type::ImplicitTag(i) => i.needed_buf_size(),
Asn1Type::ApplicationTag(a) => a.needed_buf_size(),
Asn1Type::Null(n) => n.needed_buf_size(),
}
Expand All @@ -136,6 +142,7 @@ impl Asn1Encoder for Asn1Type<'_> {
Asn1Type::Integer(integer) => integer.encode(writer),
Asn1Type::ObjectIdentifier(object_identifier) => object_identifier.encode(writer),
Asn1Type::ExplicitTag(e) => e.encode(writer),
Asn1Type::ImplicitTag(i) => i.encode(writer),
Asn1Type::ApplicationTag(a) => a.encode(writer),
Asn1Type::Null(n) => n.encode(writer),
}
Expand All @@ -155,6 +162,7 @@ impl MetaInfo for Asn1Type<'_> {
Asn1Type::Integer(_) => {}
Asn1Type::ObjectIdentifier(_) => {}
Asn1Type::ExplicitTag(explicit_tag) => explicit_tag.clear_meta(),
Asn1Type::ImplicitTag(implicit_tag) => implicit_tag.clear_meta(),
Asn1Type::ApplicationTag(application_tag) => application_tag.clear_meta(),
Asn1Type::Null(_) => {}
}
Expand Down
1 change: 1 addition & 0 deletions crates/asn1-parser/src/string/octet_string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ impl OctetString<'_> {
asn1.clear_meta();
Box::new(asn1.to_owned_with_asn1(asn1.inner_asn1().to_owned()))
});

OwnedOctetString {
octets: Cow::Owned(octets),
inner,
Expand Down
18 changes: 18 additions & 0 deletions crates/asn1-parser/src/tag.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,24 @@
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct Tag(pub(crate) u8);

impl Tag {
pub fn is_context_specific(self) -> bool {
self.0 & 0xc0 == 0x80
}

pub fn is_application(self) -> bool {
self.0 & 0xc0 == 0x40
}

pub fn is_constructed(self) -> bool {
self.0 & 0x20 == 0x20
}

pub fn is_primitive(self) -> bool {
!self.is_constructed()
}
}

impl From<u8> for Tag {
fn from(tag: u8) -> Self {
Self(tag)
Expand Down
3 changes: 1 addition & 2 deletions crates/asn1-parser/src/tags/application.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,7 @@ impl<'data> Asn1ValueDecoder<'data> for ApplicationTag<'data> {
}

fn compare_tags(tag: Tag) -> bool {
let raw_tag = tag.0;
raw_tag & 0xc0 == 0x40 && raw_tag & 0x20 == 0x20
tag.is_application() && tag.is_constructed()
}
}

Expand Down
3 changes: 1 addition & 2 deletions crates/asn1-parser/src/tags/explicit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,7 @@ impl<'data> Asn1ValueDecoder<'data> for ExplicitTag<'data> {
}

fn compare_tags(tag: Tag) -> bool {
let raw_tag = tag.0;
raw_tag & 0xc0 == 0x80 && raw_tag & 0x20 == 0x20
tag.is_context_specific() && tag.is_constructed()
}
}

Expand Down
107 changes: 107 additions & 0 deletions crates/asn1-parser/src/tags/implicit.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
use alloc::borrow::Cow;
use alloc::boxed::Box;
use alloc::vec::Vec;

use crate::asn1::Asn1;
use crate::length::{len_size, write_len};
use crate::reader::Reader;
use crate::writer::Writer;
use crate::{Asn1Decoder, Asn1Encoder, Asn1Result, Asn1ValueDecoder, MetaInfo, Tag, Taggable};

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ImplicitTag<'data> {
tag: u8,
octets: Cow<'data, [u8]>,
inner: Option<Box<Asn1<'data>>>,
}

pub type OwnedImplicitTag = ImplicitTag<'static>;

impl<'data> ImplicitTag<'data> {
pub fn new_owned(tag: u8, octets: Vec<u8>) -> Self {
let inner = Asn1::decode_buff(&octets).ok().map(|mut asn1| {
asn1.clear_meta();
Box::new(asn1.to_owned_with_asn1(asn1.inner_asn1().to_owned()))
});

Self {
tag,
octets: Cow::Owned(octets),
inner,
}
}

pub fn tag_number(&self) -> u8 {
self.tag & 0x1f
}

pub fn inner_asn1(&self) -> Option<&Asn1<'data>> {
self.inner.as_ref().map(|asn1| asn1.as_ref())
}

pub fn to_owned(&self) -> OwnedImplicitTag {
OwnedImplicitTag {
tag: self.tag,
octets: self.octets.to_vec().into(),
inner: self
.inner
.as_ref()
.map(|inner| Box::new(inner.to_owned_with_asn1(inner.inner_asn1().to_owned()))),
}
}
}

impl Taggable for ImplicitTag<'_> {
fn tag(&self) -> Tag {
Tag(self.tag)
}
}

impl<'data> Asn1ValueDecoder<'data> for ImplicitTag<'data> {
fn decode(tag: Tag, reader: &mut Reader<'data>) -> Asn1Result<Self> {
let data = reader.read_remaining();

let mut inner_reader = Reader::new(data);
inner_reader.set_next_id(reader.next_id());
inner_reader.set_offset(reader.full_offset() - data.len());
let inner = Asn1::decode(&mut inner_reader).ok().map(Box::new);

if !inner_reader.empty() && inner.is_some() {
return Err("implicit tag inner data contains leftovers".into());
}

reader.set_next_id(inner_reader.next_id());

Ok(Self {
tag: tag.0,
octets: Cow::Borrowed(data),
inner,
})
}

fn compare_tags(tag: Tag) -> bool {
tag.is_context_specific() && tag.is_primitive()
}
}

impl Asn1Encoder for ImplicitTag<'_> {
fn needed_buf_size(&self) -> usize {
let data_len = self.octets.len();

1 /* tag */ + len_size(data_len) + data_len
}

fn encode(&self, writer: &mut Writer) -> Asn1Result<()> {
writer.write_byte(self.tag)?;
write_len(self.octets.len(), writer)?;
writer.write_slice(&self.octets)
}
}

impl MetaInfo for ImplicitTag<'_> {
fn clear_meta(&mut self) {
if let Some(asn1) = self.inner.as_mut() {
asn1.clear_meta()
}
}
}
2 changes: 2 additions & 0 deletions crates/asn1-parser/src/tags/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
mod application;
mod explicit;
mod implicit;

pub use application::{ApplicationTag, OwnedApplicationTag};
pub use explicit::{ExplicitTag, OwnedExplicitTag};
pub use implicit::{ImplicitTag, OwnedImplicitTag};

0 comments on commit 65b7512

Please sign in to comment.