1#![cfg_attr(feature = "arbitrary", allow(clippy::arithmetic_side_effects))]
3
4mod class;
5mod mode;
6mod number;
7
8pub use self::{class::Class, mode::TagMode, number::TagNumber};
9
10use crate::{Decode, DerOrd, Encode, Error, ErrorKind, Length, Reader, Result, Writer};
11use core::{cmp::Ordering, fmt};
12
13#[cfg(feature = "alloc")]
14use alloc::borrow::{Cow, ToOwned};
15
16const CONSTRUCTED_FLAG: u8 = 0b100000;
18
19pub trait FixedTag {
32 const TAG: Tag;
34}
35
36#[cfg(feature = "alloc")]
37impl<'a, T> FixedTag for Cow<'a, T>
38where
39 T: ToOwned + ?Sized,
40 &'a T: FixedTag,
41{
42 const TAG: Tag = <&'a T>::TAG;
43}
44
45#[diagnostic::on_unimplemented(note = "Consider adding impl of `FixedTag` to `{Self}`")]
65pub trait Tagged {
66 fn tag(&self) -> Tag;
68}
69
70impl<T: FixedTag + ?Sized> Tagged for T {
72 fn tag(&self) -> Tag {
73 T::TAG
74 }
75}
76
77#[diagnostic::on_unimplemented(note = "Consider adding impl of `FixedTag` to `{Self}`")]
113pub trait IsConstructed {
114 const CONSTRUCTED: bool;
116}
117
118impl<T: FixedTag + ?Sized> IsConstructed for T {
120 const CONSTRUCTED: bool = T::TAG.is_constructed();
121}
122
123#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
167#[derive(Copy, Clone, Eq, Hash, PartialEq, PartialOrd, Ord)]
168#[non_exhaustive]
169pub enum Tag {
170 Boolean,
172
173 Integer,
175
176 BitString,
178
179 OctetString,
181
182 Null,
184
185 ObjectIdentifier,
187
188 Real,
190
191 Enumerated,
193
194 Utf8String,
196
197 RelativeOid,
199
200 Sequence,
202
203 Set,
205
206 NumericString,
208
209 PrintableString,
211
212 TeletexString,
214
215 VideotexString,
217
218 Ia5String,
220
221 UtcTime,
223
224 GeneralizedTime,
226
227 VisibleString,
229
230 GeneralString,
232
233 BmpString,
235
236 Application {
238 constructed: bool,
240
241 number: TagNumber,
243 },
244
245 ContextSpecific {
247 constructed: bool,
249
250 number: TagNumber,
252 },
253
254 Private {
256 constructed: bool,
258
259 number: TagNumber,
261 },
262}
263
264impl Tag {
265 pub(crate) const MAX_SIZE: usize = 6;
268
269 pub(crate) fn decode_with_constructed_bit<'a>(
271 reader: &mut impl Reader<'a>,
272 ) -> Result<(Self, bool)> {
273 let first_byte = reader.read_byte()?;
274 let is_constructed = first_byte & CONSTRUCTED_FLAG != 0;
275
276 let tag = match first_byte {
277 0x01 => Tag::Boolean,
278 0x02 => Tag::Integer,
279 0x03 => Tag::BitString,
280 0x04 => Tag::OctetString,
281 0x05 => Tag::Null,
282 0x06 => Tag::ObjectIdentifier,
283 0x09 => Tag::Real,
284 0x0A => Tag::Enumerated,
285 0x0C => Tag::Utf8String,
286 0x0D => Tag::RelativeOid,
287 0x12 => Tag::NumericString,
288 0x13 => Tag::PrintableString,
289 0x14 => Tag::TeletexString,
290 0x15 => Tag::VideotexString,
291 0x16 => Tag::Ia5String,
292 0x17 => Tag::UtcTime,
293 0x18 => Tag::GeneralizedTime,
294 0x1A => Tag::VisibleString,
295 0x1B => Tag::GeneralString,
296 0x1E => Tag::BmpString,
297 #[cfg(feature = "ber")]
298 0x24 if reader.encoding_rules().is_ber() => Tag::OctetString,
299 0x30 => Tag::Sequence, 0x31 => Tag::Set, 0x40..=0x7F => {
302 let (constructed, number) = parse_parts(first_byte, reader)?;
303
304 Tag::Application {
305 constructed,
306 number,
307 }
308 }
309 0x80..=0xBF => {
310 let (constructed, number) = parse_parts(first_byte, reader)?;
311
312 Tag::ContextSpecific {
313 constructed,
314 number,
315 }
316 }
317 0xC0..=0xFF => {
318 let (constructed, number) = parse_parts(first_byte, reader)?;
319
320 Tag::Private {
321 constructed,
322 number,
323 }
324 }
325 0x1F => return Err(reader.error(ErrorKind::TagNumberInvalid)),
327 byte => return Err(reader.error(ErrorKind::TagUnknown { byte })),
328 };
329
330 Ok((tag, is_constructed))
331 }
332
333 pub fn peek<'a>(reader: &impl Reader<'a>) -> Result<Self> {
340 Self::decode(&mut reader.clone())
341 }
342
343 pub(crate) fn peek_matches<'a, R: Reader<'a>>(
345 reader: &mut R,
346 expected_class: Class,
347 expected_tag_number: TagNumber,
348 ) -> Result<bool> {
349 if reader.is_finished() {
350 return Ok(false);
351 }
352
353 let tag = Self::peek(reader)?;
354 Ok(tag.class() == expected_class && tag.number() == expected_tag_number)
355 }
356
357 pub fn assert_eq(self, expected: Tag) -> Result<Tag> {
362 if self == expected {
363 Ok(self)
364 } else {
365 Err(self.unexpected_error(Some(expected)).into())
366 }
367 }
368
369 #[must_use]
371 pub const fn class(self) -> Class {
372 match self {
373 Tag::Application { .. } => Class::Application,
374 Tag::ContextSpecific { .. } => Class::ContextSpecific,
375 Tag::Private { .. } => Class::Private,
376 _ => Class::Universal,
377 }
378 }
379
380 #[must_use]
382 pub const fn number(self) -> TagNumber {
383 match self {
384 Tag::Boolean => TagNumber(1),
385 Tag::Integer => TagNumber(2),
386 Tag::BitString => TagNumber(3),
387 Tag::OctetString => TagNumber(4),
388 Tag::Null => TagNumber(5),
389 Tag::ObjectIdentifier => TagNumber(6),
390 Tag::Real => TagNumber(9),
391 Tag::Enumerated => TagNumber(10),
392 Tag::Utf8String => TagNumber(12),
393 Tag::RelativeOid => TagNumber(13),
394 Tag::Sequence => TagNumber(16),
395 Tag::Set => TagNumber(17),
396 Tag::NumericString => TagNumber(18),
397 Tag::PrintableString => TagNumber(19),
398 Tag::TeletexString => TagNumber(20),
399 Tag::VideotexString => TagNumber(21),
400 Tag::Ia5String => TagNumber(22),
401 Tag::UtcTime => TagNumber(23),
402 Tag::GeneralizedTime => TagNumber(24),
403 Tag::VisibleString => TagNumber(26),
404 Tag::GeneralString => TagNumber(27),
405 Tag::BmpString => TagNumber(30),
406 Tag::Application { number, .. } => number,
407 Tag::ContextSpecific { number, .. } => number,
408 Tag::Private { number, .. } => number,
409 }
410 }
411
412 #[must_use]
414 pub const fn is_constructed(self) -> bool {
415 match self {
416 Tag::Sequence | Tag::Set => true,
417 Tag::Application { constructed, .. }
418 | Tag::ContextSpecific { constructed, .. }
419 | Tag::Private { constructed, .. } => constructed,
420 _ => false,
421 }
422 }
423
424 #[must_use]
426 pub const fn is_application(self) -> bool {
427 matches!(self.class(), Class::Application)
428 }
429
430 #[must_use]
432 pub const fn is_context_specific(self) -> bool {
433 matches!(self.class(), Class::ContextSpecific)
434 }
435
436 #[must_use]
438 pub const fn is_private(self) -> bool {
439 matches!(self.class(), Class::Private)
440 }
441
442 #[must_use]
444 pub const fn is_universal(self) -> bool {
445 matches!(self.class(), Class::Universal)
446 }
447
448 #[must_use]
450 pub fn length_error(self) -> ErrorKind {
451 ErrorKind::Length { tag: self }
452 }
453
454 #[must_use]
457 pub fn non_canonical_error(self) -> ErrorKind {
458 ErrorKind::Noncanonical { tag: self }
459 }
460
461 #[must_use]
464 pub fn unexpected_error(self, expected: Option<Self>) -> ErrorKind {
465 ErrorKind::TagUnexpected {
466 expected,
467 actual: self,
468 }
469 }
470
471 #[must_use]
474 pub fn value_error(self) -> ErrorKind {
475 ErrorKind::Value { tag: self }
476 }
477}
478
479impl<'a> Decode<'a> for Tag {
480 type Error = Error;
481
482 fn decode<R: Reader<'a>>(reader: &mut R) -> Result<Self> {
483 Self::decode_with_constructed_bit(reader).map(|(tag, _)| tag)
484 }
485}
486
487fn parse_parts<'a, R: Reader<'a>>(first_byte: u8, reader: &mut R) -> Result<(bool, TagNumber)> {
488 let constructed = first_byte & CONSTRUCTED_FLAG != 0;
489 let first_number_part = first_byte & TagNumber::MASK;
490
491 if first_number_part != TagNumber::MASK {
492 return Ok((constructed, TagNumber(first_number_part.into())));
493 }
494
495 let mut multi_byte_tag_number = 0;
496
497 for i in 0..Tag::MAX_SIZE - 1 {
498 let byte = reader.read_byte()?;
499 multi_byte_tag_number |= u32::from(byte & 0x7F);
500
501 if byte & 0x80 == 0 {
502 if multi_byte_tag_number < u32::from(TagNumber::MASK) {
503 return Err(reader.error(ErrorKind::TagNumberInvalid));
504 }
505
506 return Ok((constructed, TagNumber(multi_byte_tag_number)));
507 } else if i == 0 && multi_byte_tag_number == 0 {
508 return Err(reader.error(ErrorKind::TagNumberInvalid));
510 }
511
512 if multi_byte_tag_number.leading_zeros() < 7 {
513 return Err(reader.error(ErrorKind::TagNumberInvalid));
514 }
515
516 multi_byte_tag_number <<= 7;
517 }
518
519 Err(reader.error(ErrorKind::TagNumberInvalid))
521}
522
523impl Encode for Tag {
524 #[allow(clippy::cast_possible_truncation)]
525 fn encoded_len(&self) -> Result<Length> {
526 let number = self.number().value();
527
528 let length = if number <= 30 {
529 Length::ONE
530 } else {
531 Length::new(number.ilog2() / 7 + 2)
532 };
533
534 Ok(length)
535 }
536
537 fn encode(&self, writer: &mut impl Writer) -> Result<()> {
538 let mut first_byte = (self.class() as u8) | (u8::from(self.is_constructed()) << 5);
539
540 let number = self.number().value();
541
542 if number < u32::from(TagNumber::MASK) {
543 first_byte |= (number & 0x1F) as u8;
544 writer.write_byte(first_byte)?;
545 } else {
546 first_byte |= TagNumber::MASK;
547 writer.write_byte(first_byte)?;
548
549 let extra_bytes = number.ilog2() / 7 + 1;
550
551 for shift in (0..extra_bytes).rev() {
552 let mut byte = ((number >> (shift * 7)) & 0x7f) as u8;
553
554 if shift != 0 {
555 byte |= 0x80;
556 }
557
558 writer.write_byte(byte)?;
559 }
560 }
561
562 Ok(())
563 }
564}
565
566impl DerOrd for Tag {
567 fn der_cmp(&self, other: &Self) -> Result<Ordering> {
568 Ok((self.class().cmp(&other.class()))
569 .then_with(|| self.number().cmp(&other.number()))
570 .then_with(|| self.is_constructed().cmp(&other.is_constructed())))
571 }
572}
573
574impl fmt::Display for Tag {
575 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
576 const FIELD_TYPE: [&str; 2] = ["primitive", "constructed"];
577
578 match *self {
579 Tag::Boolean => f.write_str("BOOLEAN"),
580 Tag::Integer => f.write_str("INTEGER"),
581 Tag::BitString => f.write_str("BIT STRING"),
582 Tag::OctetString => f.write_str("OCTET STRING"),
583 Tag::Null => f.write_str("NULL"),
584 Tag::ObjectIdentifier => f.write_str("OBJECT IDENTIFIER"),
585 Tag::Real => f.write_str("REAL"),
586 Tag::Enumerated => f.write_str("ENUMERATED"),
587 Tag::Utf8String => f.write_str("UTF8String"),
588 Tag::RelativeOid => f.write_str("RELATIVE OID"),
589 Tag::Set => f.write_str("SET"),
590 Tag::NumericString => f.write_str("NumericString"),
591 Tag::PrintableString => f.write_str("PrintableString"),
592 Tag::TeletexString => f.write_str("TeletexString"),
593 Tag::VideotexString => f.write_str("VideotexString"),
594 Tag::Ia5String => f.write_str("IA5String"),
595 Tag::UtcTime => f.write_str("UTCTime"),
596 Tag::GeneralizedTime => f.write_str("GeneralizedTime"),
597 Tag::VisibleString => f.write_str("VisibleString"),
598 Tag::GeneralString => f.write_str("GeneralString"),
599 Tag::BmpString => f.write_str("BMPString"),
600 Tag::Sequence => f.write_str("SEQUENCE"),
601 Tag::Application {
602 constructed,
603 number,
604 } => write!(
605 f,
606 "APPLICATION [{}] ({})",
607 number,
608 FIELD_TYPE[usize::from(constructed)]
609 ),
610 Tag::ContextSpecific {
611 constructed,
612 number,
613 } => write!(
614 f,
615 "CONTEXT-SPECIFIC [{}] ({})",
616 number,
617 FIELD_TYPE[usize::from(constructed)]
618 ),
619 Tag::Private {
620 constructed,
621 number,
622 } => write!(
623 f,
624 "PRIVATE [{}] ({})",
625 number,
626 FIELD_TYPE[usize::from(constructed)]
627 ),
628 }
629 }
630}
631
632impl fmt::Debug for Tag {
633 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
634 write!(f, "Tag(0x{:02x}: {})", self.number().value(), self)
635 }
636}
637
638#[cfg(test)]
639mod tests {
640 use core::cmp::Ordering;
641
642 use hex_literal::hex;
643
644 use super::{Class, Tag, TagNumber};
645 use crate::{Decode, DerOrd, ErrorKind, Length, Reader, SliceReader};
646
647 #[test]
648 fn tag_class() {
649 assert_eq!(Tag::Boolean.class(), Class::Universal);
650 assert_eq!(Tag::Integer.class(), Class::Universal);
651 assert_eq!(Tag::BitString.class(), Class::Universal);
652 assert_eq!(Tag::OctetString.class(), Class::Universal);
653 assert_eq!(Tag::Null.class(), Class::Universal);
654 assert_eq!(Tag::ObjectIdentifier.class(), Class::Universal);
655 assert_eq!(Tag::Real.class(), Class::Universal);
656 assert_eq!(Tag::Enumerated.class(), Class::Universal);
657 assert_eq!(Tag::Utf8String.class(), Class::Universal);
658 assert_eq!(Tag::RelativeOid.class(), Class::Universal);
659 assert_eq!(Tag::Set.class(), Class::Universal);
660 assert_eq!(Tag::NumericString.class(), Class::Universal);
661 assert_eq!(Tag::PrintableString.class(), Class::Universal);
662 assert_eq!(Tag::TeletexString.class(), Class::Universal);
663 assert_eq!(Tag::VideotexString.class(), Class::Universal);
664 assert_eq!(Tag::Ia5String.class(), Class::Universal);
665 assert_eq!(Tag::UtcTime.class(), Class::Universal);
666 assert_eq!(Tag::GeneralizedTime.class(), Class::Universal);
667 assert_eq!(Tag::Sequence.class(), Class::Universal);
668
669 for num in 0..=30 {
670 for &constructed in &[false, true] {
671 let number = TagNumber(num);
672
673 assert_eq!(
674 Tag::Application {
675 constructed,
676 number
677 }
678 .class(),
679 Class::Application
680 );
681
682 assert_eq!(
683 Tag::ContextSpecific {
684 constructed,
685 number
686 }
687 .class(),
688 Class::ContextSpecific
689 );
690
691 assert_eq!(
692 Tag::Private {
693 constructed,
694 number
695 }
696 .class(),
697 Class::Private
698 );
699 }
700 }
701 }
702
703 #[test]
704 fn decoding() {
705 assert_eq!(
706 Tag::Application {
707 constructed: false,
708 number: TagNumber(0x4001)
709 },
710 Tag::from_der(&hex!("5F818001")).expect("bits 7 to 1 are zero")
711 );
712 assert_eq!(
713 Tag::ContextSpecific {
714 constructed: false,
715 number: TagNumber(0x200001)
716 },
717 Tag::from_der(&hex!("9F81808001")).expect("bits 7 to 1 are zero two times")
718 );
719 assert_eq!(
720 Tag::Private {
721 constructed: false,
722 number: TagNumber(u32::MAX)
723 },
724 Tag::from_der(&hex!("DF8FFFFFFF7F")).expect("private tag 2^32-1")
725 );
726 assert_eq!(
727 ErrorKind::TagNumberInvalid,
728 Tag::from_der(&hex!("FF03"))
729 .expect_err("valid tag number but must be in short form")
730 .kind()
731 );
732 assert_eq!(
733 ErrorKind::TagNumberInvalid,
734 Tag::from_der(&hex!("1FFF"))
735 .expect_err("universal tag with long form")
736 .kind()
737 );
738 assert_eq!(
739 ErrorKind::TagNumberInvalid,
740 Tag::from_der(&hex!("5F8020"))
741 .expect_err("leading zeros in long form")
742 .kind()
743 );
744 assert_eq!(
745 ErrorKind::TagNumberInvalid,
746 Tag::from_der(&hex!("DF9F8F8F8F0F"))
747 .expect_err("tag number larger than 32 bits")
748 .kind()
749 );
750 assert_eq!(
751 ErrorKind::Incomplete {
752 expected_len: Length::new(3),
753 actual_len: Length::new(2)
754 },
755 Tag::from_der(&hex!("5F9E"))
756 .expect_err("incomplete tag in long form")
757 .kind()
758 );
759 }
760
761 #[test]
762 fn tag_order() {
763 assert_eq!(Tag::Boolean.der_cmp(&Tag::Integer), Ok(Ordering::Less));
771 assert_eq!(Tag::Integer.der_cmp(&Tag::Null), Ok(Ordering::Less));
772 assert_eq!(Tag::Null.der_cmp(&Tag::Sequence), Ok(Ordering::Less));
773 assert_eq!(Tag::Sequence.der_cmp(&Tag::Ia5String), Ok(Ordering::Less));
774 assert_eq!(Tag::Ia5String.der_cmp(&Tag::BmpString), Ok(Ordering::Less));
775
776 assert_eq!(
778 Tag::BmpString.der_cmp(&Tag::Application {
779 constructed: true,
780 number: TagNumber(0)
781 }),
782 Ok(Ordering::Less)
783 );
784 assert_eq!(
786 Tag::Application {
787 constructed: true,
788 number: TagNumber(0)
789 }
790 .der_cmp(&Tag::Application {
791 constructed: true,
792 number: TagNumber(1)
793 }),
794 Ok(Ordering::Less)
795 );
796
797 assert_eq!(
799 Tag::Application {
800 constructed: true,
801 number: TagNumber(1)
802 }
803 .der_cmp(&Tag::Application {
804 constructed: false,
805 number: TagNumber(2)
806 }),
807 Ok(Ordering::Less)
808 );
809
810 assert_eq!(
812 Tag::Application {
813 constructed: false,
814 number: TagNumber(2)
815 }
816 .der_cmp(&Tag::Application {
817 constructed: true,
818 number: TagNumber(2)
819 }),
820 Ok(Ordering::Less)
821 );
822
823 assert_eq!(
825 Tag::Application {
826 constructed: true,
827 number: TagNumber(2)
828 }
829 .der_cmp(&Tag::ContextSpecific {
830 constructed: true,
831 number: TagNumber(0)
832 }),
833 Ok(Ordering::Less)
834 );
835
836 assert_eq!(
838 Tag::ContextSpecific {
839 constructed: true,
840 number: TagNumber(10)
841 }
842 .der_cmp(&Tag::Private {
843 constructed: true,
844 number: TagNumber(0)
845 }),
846 Ok(Ordering::Less)
847 );
848 }
849
850 #[test]
851 fn peek() {
852 let reader = SliceReader::new(&[0x02]).expect("valid reader");
853 assert_eq!(reader.position(), Length::ZERO);
854 assert_eq!(Tag::peek(&reader).expect("peeked tag"), Tag::Integer);
855 assert_eq!(reader.position(), Length::ZERO); }
857
858 #[test]
859 fn peek_long_tags() {
860 let reader = SliceReader::new(&hex!("DF8FFFFFFF7F")).expect("valid reader");
861 let tag = Tag::peek(&reader).expect("peeked tag");
862 assert!(!tag.is_context_specific());
863 assert!(!tag.is_application());
864 assert!(tag.is_private());
865 assert_eq!(
866 tag,
867 Tag::Private {
868 constructed: false,
869 number: TagNumber(u32::MAX)
870 }
871 );
872 }
873
874 #[test]
875 fn negative_peek_long_tags() {
876 let reader = SliceReader::new(&hex!("DF8FFFFFFFFF")).expect("valid reader");
877 assert_eq!(
878 Tag::peek(&reader)
879 .expect_err("tag ends in 0xFF, so 7+ bytes needed")
880 .kind(),
881 ErrorKind::TagNumberInvalid,
882 );
883 }
884}