Skip to content

Commit 4a63b69

Browse files
authored
der: fix X.680 tag order: compare class and number first (#1790)
1 parent fc5b028 commit 4a63b69

File tree

1 file changed

+95
-6
lines changed

1 file changed

+95
-6
lines changed

der/src/tag.rs

Lines changed: 95 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -440,11 +440,9 @@ impl Encode for Tag {
440440

441441
impl DerOrd for Tag {
442442
fn der_cmp(&self, other: &Self) -> Result<Ordering> {
443-
Ok(self
444-
.class()
445-
.cmp(&other.class())
446-
.then_with(|| self.is_constructed().cmp(&other.is_constructed()))
447-
.then_with(|| self.number().cmp(&other.number())))
443+
Ok((self.class().cmp(&other.class()))
444+
.then_with(|| self.number().cmp(&other.number()))
445+
.then_with(|| self.is_constructed().cmp(&other.is_constructed())))
448446
}
449447
}
450448

@@ -513,10 +511,12 @@ impl fmt::Debug for Tag {
513511

514512
#[cfg(test)]
515513
mod tests {
514+
use core::cmp::Ordering;
515+
516516
use hex_literal::hex;
517517

518518
use super::{Class, Tag, TagNumber};
519-
use crate::{Decode, ErrorKind, Length, Reader, SliceReader};
519+
use crate::{Decode, DerOrd, ErrorKind, Length, Reader, SliceReader};
520520

521521
#[test]
522522
fn tag_class() {
@@ -639,4 +639,93 @@ mod tests {
639639
assert_eq!(Tag::peek(&reader).unwrap(), Tag::Integer);
640640
assert_eq!(reader.position(), Length::ZERO); // Position unchanged
641641
}
642+
643+
#[test]
644+
fn tag_order() {
645+
// T-REC-X.680-202102
646+
// 8.6 The canonical order for tags is based on the outermost tag of each type and is defined as follows:
647+
// a) those elements or alternatives with universal class tags shall appear first, followed by those with
648+
// application class tags, followed by those with context-specific tags, followed by those with private class
649+
// tags;
650+
// b) within each class of tags, the elements or alternatives shall appear in ascending order of their tag
651+
// numbers.
652+
assert_eq!(Tag::Boolean.der_cmp(&Tag::Integer), Ok(Ordering::Less));
653+
assert_eq!(Tag::Integer.der_cmp(&Tag::Null), Ok(Ordering::Less));
654+
assert_eq!(Tag::Null.der_cmp(&Tag::Sequence), Ok(Ordering::Less));
655+
assert_eq!(Tag::Sequence.der_cmp(&Tag::Ia5String), Ok(Ordering::Less));
656+
assert_eq!(Tag::Ia5String.der_cmp(&Tag::BmpString), Ok(Ordering::Less));
657+
658+
// universal class, then application class
659+
assert_eq!(
660+
Tag::BmpString.der_cmp(&Tag::Application {
661+
constructed: true,
662+
number: TagNumber(0)
663+
}),
664+
Ok(Ordering::Less)
665+
);
666+
// ascending tag numbers
667+
assert_eq!(
668+
Tag::Application {
669+
constructed: true,
670+
number: TagNumber(0)
671+
}
672+
.der_cmp(&Tag::Application {
673+
constructed: true,
674+
number: TagNumber(1)
675+
}),
676+
Ok(Ordering::Less)
677+
);
678+
679+
// ignore constructed bit
680+
assert_eq!(
681+
Tag::Application {
682+
constructed: true,
683+
number: TagNumber(1)
684+
}
685+
.der_cmp(&Tag::Application {
686+
constructed: false,
687+
number: TagNumber(2)
688+
}),
689+
Ok(Ordering::Less)
690+
);
691+
692+
// for same tag numbers, order by constructed bit
693+
assert_eq!(
694+
Tag::Application {
695+
constructed: false,
696+
number: TagNumber(2)
697+
}
698+
.der_cmp(&Tag::Application {
699+
constructed: true,
700+
number: TagNumber(2)
701+
}),
702+
Ok(Ordering::Less)
703+
);
704+
705+
// application class is before context-specific class
706+
assert_eq!(
707+
Tag::Application {
708+
constructed: true,
709+
number: TagNumber(2)
710+
}
711+
.der_cmp(&Tag::ContextSpecific {
712+
constructed: true,
713+
number: TagNumber(0)
714+
}),
715+
Ok(Ordering::Less)
716+
);
717+
718+
// context-specific class is before private class
719+
assert_eq!(
720+
Tag::ContextSpecific {
721+
constructed: true,
722+
number: TagNumber(10)
723+
}
724+
.der_cmp(&Tag::Private {
725+
constructed: true,
726+
number: TagNumber(0)
727+
}),
728+
Ok(Ordering::Less)
729+
);
730+
}
642731
}

0 commit comments

Comments
 (0)