Skip to content

Commit e7cb4ea

Browse files
der: order FlagSet by its BIT STRING encoding (#2451)
`ValueOrd for FlagSet<T>` compared `bits()` as an integer. The DER encoding (a NamedBitList BIT STRING) puts flag 0 in the most significant bit of the first octet and drops trailing zero bits, so the integer order and the encoding order disagree, e.g. for a 9-flag set: {A} bits 0x001 -> 03 02 07 80 {B} bits 0x002 -> 03 02 06 40 (sorts first in DER) X.690 11.6 orders `SET OF` components by their encodings, so `SetOfVec` of a type containing a `FlagSet` (e.g. x509-cert's `DistributionPoint` with `reasons: Option<ReasonFlags>`) can encode out of DER order. Compare the encoded values instead (on a stack buffer).
1 parent 16a33d3 commit e7cb4ea

1 file changed

Lines changed: 43 additions & 2 deletions

File tree

‎der/src/asn1/bit_string.rs‎

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -626,14 +626,24 @@ impl<T: flagset::Flags> FixedTag for flagset::FlagSet<T> {
626626
const TAG: Tag = BitStringRef::TAG;
627627
}
628628

629+
/// Compares the BIT STRING encodings (X.690 11.6 orders `SET OF` components by their
630+
/// encodings). The encoding puts flag 0 in the most significant bit of the first octet and drops
631+
/// trailing zero bits, so this differs from comparing [`FlagSet::bits`][flagset::FlagSet::bits]
632+
/// as an integer.
629633
#[cfg(feature = "flagset")]
630634
impl<T> ValueOrd for flagset::FlagSet<T>
631635
where
632636
T: flagset::Flags,
633-
T::Type: Ord,
637+
T::Type: From<bool>,
638+
T::Type: core::ops::Shl<usize, Output = T::Type>,
639+
u128: From<T::Type>,
634640
{
635641
fn value_cmp(&self, other: &Self) -> Result<Ordering> {
636-
Ok(self.bits().cmp(&other.bits()))
642+
let mut buf1 = [0u8; 17];
643+
let mut buf2 = [0u8; 17];
644+
let a = crate::encode::encode_value_to_slice(&mut buf1, self)?;
645+
let b = crate::encode::encode_value_to_slice(&mut buf2, other)?;
646+
Ok(a.cmp(b))
637647
}
638648
}
639649

@@ -766,4 +776,35 @@ mod tests {
766776
let bs2 = parse_bitstring(&hex!("00010203")).unwrap();
767777
assert_eq!(bs1.der_cmp(&bs2), Ok(Ordering::Greater));
768778
}
779+
780+
#[cfg(feature = "flagset")]
781+
#[test]
782+
fn flagset_valueord_is_encoding_order() {
783+
use crate::{DerOrd, Encode, ValueOrd};
784+
785+
flagset::flags! {
786+
enum Flags: u16 {
787+
A, B, C, D, E, F, G, H, I,
788+
}
789+
}
790+
791+
// {A} encodes as `03 02 07 80`, {B} as `03 02 06 40`: {B} sorts first.
792+
let a = flagset::FlagSet::from(Flags::A);
793+
let b = flagset::FlagSet::from(Flags::B);
794+
assert_eq!(b.value_cmp(&a), Ok(Ordering::Less));
795+
assert_eq!(a.der_cmp(&b), Ok(Ordering::Greater));
796+
797+
// every pair agrees with the order of the encodings
798+
let all: [flagset::FlagSet<Flags>; 512] =
799+
core::array::from_fn(|i| flagset::FlagSet::new(u16::try_from(i).unwrap()).unwrap());
800+
for x in &all {
801+
for y in &all {
802+
let mut bx = [0u8; 8];
803+
let mut by = [0u8; 8];
804+
let ex = x.encode_to_slice(&mut bx).unwrap();
805+
let ey = y.encode_to_slice(&mut by).unwrap();
806+
assert_eq!(x.der_cmp(y), Ok(ex.cmp(ey)), "{x:?} vs {y:?}");
807+
}
808+
}
809+
}
769810
}

0 commit comments

Comments
 (0)