picky_asn1/
tag.rs

1use serde::de;
2use std::fmt;
3
4#[derive(Debug, Clone, Copy, Eq, PartialEq)]
5pub enum Encoding {
6    Primitive,
7    Constructed,
8}
9
10impl fmt::Display for Encoding {
11    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
12        match self {
13            Self::Primitive => write!(f, "PRIMITIVE"),
14            Self::Constructed => write!(f, "CONSTRUCTED"),
15        }
16    }
17}
18
19#[derive(Debug, Clone, Copy, Eq, PartialEq)]
20pub enum TagClass {
21    Universal,
22    Application,
23    ContextSpecific,
24    Private,
25}
26
27impl fmt::Display for TagClass {
28    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29        match self {
30            Self::Universal => write!(f, "UNIVERSAL"),
31            Self::Application => write!(f, "APPLICATION"),
32            Self::ContextSpecific => write!(f, "CONTEXT_SPECIFIC"),
33            Self::Private => write!(f, "PRIVATE"),
34        }
35    }
36}
37
38#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
39pub struct Tag(u8);
40
41impl Tag {
42    pub const BOOLEAN: Self = Tag(0x01);
43    pub const INTEGER: Self = Tag(0x02);
44    pub const BIT_STRING: Self = Tag(0x03);
45    pub const OCTET_STRING: Self = Tag(0x04);
46    pub const NULL: Self = Tag(0x05);
47    pub const OID: Self = Tag(0x06);
48    pub const REAL: Self = Tag(0x09);
49    pub const UTF8_STRING: Self = Tag(0x0C);
50    pub const RELATIVE_OID: Self = Tag(0xD);
51    pub const NUMERIC_STRING: Self = Tag(0x12);
52    pub const PRINTABLE_STRING: Self = Tag(0x13);
53    pub const TELETEX_STRING: Self = Tag(0x14);
54    pub const VIDEOTEX_STRING: Self = Tag(0x15);
55    pub const IA5_STRING: Self = Tag(0x16);
56    pub const BMP_STRING: Self = Tag(0x1E);
57    pub const UTC_TIME: Self = Tag(0x17);
58    pub const GENERALIZED_TIME: Self = Tag(0x18);
59    pub const SEQUENCE: Self = Tag(0x30);
60    pub const SET: Self = Tag(0x31);
61    pub const GENERAL_STRING: Self = Tag(0x1b);
62
63    #[inline]
64    pub const fn application_primitive(number: u8) -> Self {
65        Tag(number & 0x1F | 0x40)
66    }
67
68    #[inline]
69    pub const fn application_constructed(number: u8) -> Self {
70        Tag(number & 0x1F | 0x60)
71    }
72
73    #[inline]
74    pub const fn context_specific_primitive(number: u8) -> Self {
75        Tag(number & 0x1F | 0x80)
76    }
77
78    #[inline]
79    pub const fn context_specific_constructed(number: u8) -> Self {
80        Tag(number & 0x1F | 0xA0)
81    }
82
83    /// Identifier octets as u8
84    #[inline]
85    pub const fn inner(self) -> u8 {
86        self.0
87    }
88
89    /// Tag number of the ASN.1 value (filtering class bits and constructed bit with a mask)
90    #[inline]
91    pub const fn number(self) -> u8 {
92        self.0 & 0x1F
93    }
94
95    // TODO: need version bump to be made const
96    pub fn class(self) -> TagClass {
97        match self.0 & 0xC0 {
98            0x00 => TagClass::Universal,
99            0x40 => TagClass::Application,
100            0x80 => TagClass::ContextSpecific,
101            _ /* 0xC0 */ => TagClass::Private,
102        }
103    }
104
105    // TODO: need version bump to be made const
106    pub fn class_and_number(self) -> (TagClass, u8) {
107        (self.class(), self.number())
108    }
109
110    // TODO: need version bump to be made const
111    pub fn components(self) -> (TagClass, Encoding, u8) {
112        (self.class(), self.encoding(), self.number())
113    }
114
115    #[inline]
116    pub const fn is_application(self) -> bool {
117        self.0 & 0xC0 == 0x40
118    }
119
120    #[inline]
121    pub const fn is_context_specific(self) -> bool {
122        self.0 & 0xC0 == 0x80
123    }
124
125    #[inline]
126    pub const fn is_universal(self) -> bool {
127        self.0 & 0xC0 == 0x00
128    }
129
130    #[inline]
131    pub const fn is_private(self) -> bool {
132        self.0 & 0xC0 == 0xC0
133    }
134
135    #[inline]
136    pub const fn is_constructed(self) -> bool {
137        self.0 & 0x20 == 0x20
138    }
139
140    #[inline]
141    pub const fn is_primitive(self) -> bool {
142        !self.is_constructed()
143    }
144
145    // TODO: need version bump to be made const
146    #[inline]
147    pub fn encoding(self) -> Encoding {
148        if self.is_constructed() {
149            Encoding::Constructed
150        } else {
151            Encoding::Primitive
152        }
153    }
154}
155
156impl From<u8> for Tag {
157    fn from(tag: u8) -> Self {
158        Self(tag)
159    }
160}
161
162impl fmt::Display for Tag {
163    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
164        match *self {
165            Tag::BOOLEAN => write!(f, "BOOLEAN"),
166            Tag::INTEGER => write!(f, "INTEGER"),
167            Tag::BIT_STRING => write!(f, "BIT STRING"),
168            Tag::OCTET_STRING => write!(f, "OCTET STRING"),
169            Tag::NULL => write!(f, "NULL"),
170            Tag::OID => write!(f, "OBJECT IDENTIFIER"),
171            Tag::REAL => write!(f, "REAL"),
172            Tag::UTF8_STRING => write!(f, "UTF8String"),
173            Tag::RELATIVE_OID => write!(f, "RELATIVE-OID"),
174            Tag::NUMERIC_STRING => write!(f, "NumericString"),
175            Tag::PRINTABLE_STRING => write!(f, "PrintableString"),
176            Tag::TELETEX_STRING => write!(f, "TeletexString"),
177            Tag::VIDEOTEX_STRING => write!(f, "VideotexString"),
178            Tag::IA5_STRING => write!(f, "IA5String"),
179            Tag::BMP_STRING => write!(f, "BMPString"),
180            Tag::UTC_TIME => write!(f, "UTCTime"),
181            Tag::GENERALIZED_TIME => write!(f, "GeneralizedTime"),
182            Tag::SEQUENCE => write!(f, "SEQUENCE"),
183            Tag::SET => write!(f, "SET"),
184            Tag::GENERAL_STRING => write!(f, "GeneralString"),
185            other => write!(f, "{}({:02X}) {}", other.class(), other.number(), other.encoding()),
186        }
187    }
188}
189
190impl fmt::Debug for Tag {
191    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
192        write!(f, "Tag({}[{:02X}])", self, self.0)
193    }
194}
195
196/// Used to peek next tag by using `Deserializer::deserialize_identifier`.
197///
198/// Can be used to implement ASN.1 Choice.
199///
200/// # Examples
201/// ```
202/// use serde::de;
203/// use picky_asn1::{
204///     wrapper::{IntegerAsn1, Utf8StringAsn1},
205///     tag::{Tag, TagPeeker},
206/// };
207/// use std::fmt;
208///
209/// pub enum MyChoice {
210///     Integer(u32),
211///     Utf8String(String),
212/// }
213///
214/// impl<'de> de::Deserialize<'de> for MyChoice {
215///     fn deserialize<D>(deserializer: D) -> Result<Self, <D as de::Deserializer<'de>>::Error>
216///     where
217///         D: de::Deserializer<'de>,
218///     {
219///         struct Visitor;
220///
221///         impl<'de> de::Visitor<'de> for Visitor {
222///             type Value = MyChoice;
223///
224///             fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
225///                 formatter.write_str("a valid MyChoice")
226///             }
227///
228///             fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
229///             where
230///                 A: de::SeqAccess<'de>,
231///             {
232///                 match seq.next_element::<TagPeeker>()?.unwrap().next_tag {
233///                     Tag::INTEGER => {
234///                         let value = seq.next_element::<u32>()?.unwrap();
235///                         Ok(MyChoice::Integer(value))
236///                     }
237///                     Tag::UTF8_STRING => {
238///                         let value = seq.next_element::<String>()?.unwrap();
239///                         Ok(MyChoice::Utf8String(value))
240///                     }
241///                     _ => Err(de::Error::invalid_value(
242///                         de::Unexpected::Other(
243///                             "[MyChoice] unsupported or unknown choice value",
244///                         ),
245///                         &"a supported choice value",
246///                     ))
247///                 }
248///             }
249///         }
250///
251///         deserializer.deserialize_enum("MyChoice", &["Integer", "Utf8String"], Visitor)
252///     }
253/// }
254///
255/// let buffer = b"\x0C\x06\xE8\x8B\x97\xE5\xAD\x97";
256/// let my_choice: MyChoice = picky_asn1_der::from_bytes(buffer).unwrap();
257/// match my_choice {
258///     MyChoice::Integer(_) => panic!("wrong variant"),
259///     MyChoice::Utf8String(string) => assert_eq!(string, "苗字"),
260/// }
261/// ```
262#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
263pub struct TagPeeker {
264    pub next_tag: Tag,
265}
266
267impl<'de> de::Deserialize<'de> for TagPeeker {
268    fn deserialize<D>(deserializer: D) -> Result<TagPeeker, D::Error>
269    where
270        D: de::Deserializer<'de>,
271    {
272        struct Visitor;
273
274        impl<'de> de::Visitor<'de> for Visitor {
275            type Value = TagPeeker;
276
277            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
278                formatter.write_str("a valid ASN.1 tag")
279            }
280
281            fn visit_u8<E>(self, v: u8) -> Result<Self::Value, E>
282            where
283                E: de::Error,
284            {
285                Ok(TagPeeker { next_tag: v.into() })
286            }
287        }
288
289        deserializer.deserialize_identifier(Visitor)
290    }
291}