diff --git a/crates/asn1-parser/README.md b/crates/asn1-parser/README.md index 939174f..d9386ed 100644 --- a/crates/asn1-parser/README.md +++ b/crates/asn1-parser/README.md @@ -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 diff --git a/crates/asn1-parser/src/asn1.rs b/crates/asn1-parser/src/asn1.rs index dcd20b4..d66773c 100644 --- a/crates/asn1-parser/src/asn1.rs +++ b/crates/asn1-parser/src/asn1.rs @@ -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)] @@ -24,6 +24,7 @@ pub enum Asn1Type<'data> { ObjectIdentifier(ObjectIdentifier), ExplicitTag(ExplicitTag<'data>), + ImplicitTag(ImplicitTag<'data>), ApplicationTag(ApplicationTag<'data>), } @@ -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()), } @@ -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(), } } @@ -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) { @@ -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(), } @@ -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), } @@ -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(_) => {} } diff --git a/crates/asn1-parser/src/string/octet_string.rs b/crates/asn1-parser/src/string/octet_string.rs index 5c37e55..ec89230 100644 --- a/crates/asn1-parser/src/string/octet_string.rs +++ b/crates/asn1-parser/src/string/octet_string.rs @@ -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, diff --git a/crates/asn1-parser/src/tag.rs b/crates/asn1-parser/src/tag.rs index 424538a..3a065ee 100644 --- a/crates/asn1-parser/src/tag.rs +++ b/crates/asn1-parser/src/tag.rs @@ -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 for Tag { fn from(tag: u8) -> Self { Self(tag) diff --git a/crates/asn1-parser/src/tags/application.rs b/crates/asn1-parser/src/tags/application.rs index 9ef28c2..7ca91b7 100644 --- a/crates/asn1-parser/src/tags/application.rs +++ b/crates/asn1-parser/src/tags/application.rs @@ -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() } } diff --git a/crates/asn1-parser/src/tags/explicit.rs b/crates/asn1-parser/src/tags/explicit.rs index eae1245..7d27a4b 100644 --- a/crates/asn1-parser/src/tags/explicit.rs +++ b/crates/asn1-parser/src/tags/explicit.rs @@ -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() } } diff --git a/crates/asn1-parser/src/tags/implicit.rs b/crates/asn1-parser/src/tags/implicit.rs new file mode 100644 index 0000000..43a4785 --- /dev/null +++ b/crates/asn1-parser/src/tags/implicit.rs @@ -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>>, +} + +pub type OwnedImplicitTag = ImplicitTag<'static>; + +impl<'data> ImplicitTag<'data> { + pub fn new_owned(tag: u8, octets: Vec) -> 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 { + 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() + } + } +} diff --git a/crates/asn1-parser/src/tags/mod.rs b/crates/asn1-parser/src/tags/mod.rs index ed44f2c..ed14564 100644 --- a/crates/asn1-parser/src/tags/mod.rs +++ b/crates/asn1-parser/src/tags/mod.rs @@ -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};