From 4bf075658d00d1a7ddb2a3b309a40a4d2b24608c Mon Sep 17 00:00:00 2001
From: roblabla <unfiltered@roblab.la>
Date: Thu, 15 Sep 2022 12:32:46 +0200
Subject: [PATCH 1/5] Implement ValueOrd for FlagSet<T>

---
 der/src/asn1/bit_string.rs | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/der/src/asn1/bit_string.rs b/der/src/asn1/bit_string.rs
index 7eea5a596..eed14e456 100644
--- a/der/src/asn1/bit_string.rs
+++ b/der/src/asn1/bit_string.rs
@@ -375,6 +375,17 @@ impl<T: flagset::Flags> FixedTag for flagset::FlagSet<T> {
     const TAG: Tag = BitStringRef::TAG;
 }
 
+#[cfg(feature = "flagset")]
+impl<T> ValueOrd for flagset::FlagSet<T>
+where
+    T: flagset::Flags,
+    T::Type: Ord,
+{
+    fn value_cmp(&self, other: &Self) -> Result<Ordering> {
+        Ok(self.bits().cmp(&other.bits()))
+    }
+}
+
 #[cfg(feature = "flagset")]
 #[allow(clippy::integer_arithmetic)]
 impl<'a, T> DecodeValue<'a> for flagset::FlagSet<T>

From 20f40ab8b71ae08d89528bf051b29d414b8a6f50 Mon Sep 17 00:00:00 2001
From: roblabla <unfiltered@roblab.la>
Date: Thu, 15 Sep 2022 12:37:46 +0200
Subject: [PATCH 2/5] Implement ValueOrd for UIntRef

---
 der/src/asn1/integer/bigint.rs | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/der/src/asn1/integer/bigint.rs b/der/src/asn1/integer/bigint.rs
index 7a73c480a..f896406a6 100644
--- a/der/src/asn1/integer/bigint.rs
+++ b/der/src/asn1/integer/bigint.rs
@@ -2,8 +2,8 @@
 
 use super::uint;
 use crate::{
-    asn1::AnyRef, ByteSlice, DecodeValue, EncodeValue, Error, ErrorKind, FixedTag, Header, Length,
-    Reader, Result, Tag, Writer,
+    asn1::AnyRef, ord::OrdIsValueOrd, ByteSlice, DecodeValue, EncodeValue, Error, ErrorKind,
+    FixedTag, Header, Length, Reader, Result, Tag, Writer,
 };
 
 /// "Big" unsigned ASN.1 `INTEGER` type.
@@ -13,7 +13,7 @@ use crate::{
 ///
 /// Intended for use cases like very large integers that are used in
 /// cryptographic applications (e.g. keys, signatures).
-#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd)]
+#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
 pub struct UIntRef<'a> {
     /// Inner value
     inner: ByteSlice<'a>,
@@ -92,6 +92,8 @@ impl<'a> FixedTag for UIntRef<'a> {
     const TAG: Tag = Tag::Integer;
 }
 
+impl<'a> OrdIsValueOrd for UIntRef<'a> {}
+
 #[cfg(test)]
 mod tests {
     use super::UIntRef;

From 67d606d5b4e6b858544638b8f65df721e109568b Mon Sep 17 00:00:00 2001
From: roblabla <unfiltered@roblab.la>
Date: Fri, 16 Sep 2022 11:55:58 +0200
Subject: [PATCH 3/5] Implement ValueOrd for x509-cert Version

---
 x509-cert/src/certificate.rs | 13 ++++++++++---
 1 file changed, 10 insertions(+), 3 deletions(-)

diff --git a/x509-cert/src/certificate.rs b/x509-cert/src/certificate.rs
index cca7f8391..b9932cdd3 100644
--- a/x509-cert/src/certificate.rs
+++ b/x509-cert/src/certificate.rs
@@ -3,10 +3,11 @@
 use crate::{name::Name, time::Validity};
 
 use alloc::vec::Vec;
+use core::cmp::Ordering;
 
 use const_oid::AssociatedOid;
 use der::asn1::{BitStringRef, UIntRef};
-use der::{Decode, Enumerated, Error, ErrorKind, Sequence};
+use der::{Decode, Enumerated, Error, ErrorKind, Sequence, ValueOrd};
 use spki::{AlgorithmIdentifier, SubjectPublicKeyInfo};
 
 /// Certificate `Version` as defined in [RFC 5280 Section 4.1].
@@ -30,6 +31,12 @@ pub enum Version {
     V3 = 2,
 }
 
+impl ValueOrd for Version {
+    fn value_cmp(&self, other: &Self) -> der::Result<Ordering> {
+        (&(*self as u8)).value_cmp(&(*other as u8))
+    }
+}
+
 impl Default for Version {
     fn default() -> Self {
         Self::V1
@@ -61,7 +68,7 @@ impl Default for Version {
 /// ```
 ///
 /// [RFC 5280 Section 4.1]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.1
-#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
+#[derive(Clone, Debug, Eq, PartialEq, Sequence, ValueOrd)]
 #[allow(missing_docs)]
 pub struct TbsCertificate<'a> {
     /// The certificate version
@@ -135,7 +142,7 @@ impl<'a> TbsCertificate<'a> {
 /// ```
 ///
 /// [RFC 5280 Section 4.1]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.1
-#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
+#[derive(Clone, Debug, Eq, PartialEq, Sequence, ValueOrd)]
 #[allow(missing_docs)]
 pub struct Certificate<'a> {
     pub tbs_certificate: TbsCertificate<'a>,

From 97262304b86e80be2cd131a063b82777e85691f0 Mon Sep 17 00:00:00 2001
From: roblabla <unfiltered@roblab.la>
Date: Fri, 16 Sep 2022 11:57:59 +0200
Subject: [PATCH 4/5] Allow deriving ValueOrd on Choice enums

---
 der/derive/src/value_ord.rs | 107 +++++++++++++++++++++++++-----------
 1 file changed, 74 insertions(+), 33 deletions(-)

diff --git a/der/derive/src/value_ord.rs b/der/derive/src/value_ord.rs
index 6b08f7ffd..f385058f5 100644
--- a/der/derive/src/value_ord.rs
+++ b/der/derive/src/value_ord.rs
@@ -21,6 +21,8 @@ pub(crate) struct DeriveValueOrd {
 
     /// Fields of structs or enum variants.
     fields: Vec<ValueField>,
+
+    is_enum: bool,
 }
 
 impl DeriveValueOrd {
@@ -36,17 +38,21 @@ impl DeriveValueOrd {
             .next()
             .map(|lt| lt.lifetime.clone());
 
-        let fields = match input.data {
-            syn::Data::Enum(data) => data
-                .variants
-                .into_iter()
-                .map(|variant| ValueField::new_enum(variant, &type_attrs))
-                .collect(),
-            syn::Data::Struct(data) => data
-                .fields
-                .into_iter()
-                .map(|field| ValueField::new_struct(field, &type_attrs))
-                .collect(),
+        let (fields, is_enum) = match input.data {
+            syn::Data::Enum(data) => (
+                data.variants
+                    .into_iter()
+                    .map(|variant| ValueField::new_enum(variant, &type_attrs))
+                    .collect(),
+                true,
+            ),
+            syn::Data::Struct(data) => (
+                data.fields
+                    .into_iter()
+                    .map(|field| ValueField::new_struct(field, &type_attrs))
+                    .collect(),
+                false,
+            ),
             _ => abort!(
                 ident,
                 "can't derive `ValueOrd` on this type: \
@@ -58,6 +64,7 @@ impl DeriveValueOrd {
             ident,
             lifetime,
             fields,
+            is_enum,
         }
     }
 
@@ -79,15 +86,30 @@ impl DeriveValueOrd {
             body.push(field.to_tokens());
         }
 
+        let body = if self.is_enum {
+            quote! {
+                #[allow(unused_imports)]
+                use ::der::ValueOrd;
+                match (self, other) {
+                    #(#body)*
+                    _ => unreachable!(),
+                }
+            }
+        } else {
+            quote! {
+                #[allow(unused_imports)]
+                use ::der::{DerOrd, ValueOrd};
+
+                #(#body)*
+
+                Ok(::core::cmp::Ordering::Equal)
+            }
+        };
+
         quote! {
             impl<#(#lt_params)*> ::der::ValueOrd for #ident<#(#lt_params)*> {
                 fn value_cmp(&self, other: &Self) -> ::der::Result<::core::cmp::Ordering> {
-                    #[allow(unused_imports)]
-                    use ::der::DerOrd;
-
-                    #(#body)*
-
-                    Ok(::core::cmp::Ordering::Equal)
+                    #body
                 }
             }
         }
@@ -100,15 +122,21 @@ struct ValueField {
 
     /// Field-level attributes.
     attrs: FieldAttrs,
+
+    is_enum: bool,
 }
 
 impl ValueField {
     /// Create from an `enum` variant.
-    fn new_enum(variant: Variant, _: &TypeAttrs) -> Self {
-        abort!(
-            variant,
-            "deriving `ValueOrd` only presently supported for structs"
-        );
+    fn new_enum(variant: Variant, type_attrs: &TypeAttrs) -> Self {
+        let ident = variant.ident;
+
+        let attrs = FieldAttrs::parse(&variant.attrs, type_attrs);
+        Self {
+            ident,
+            attrs,
+            is_enum: true,
+        }
     }
 
     /// Create from a `struct` field.
@@ -120,24 +148,37 @@ impl ValueField {
             .unwrap_or_else(|| abort!(&field, "tuple structs are not supported"));
 
         let attrs = FieldAttrs::parse(&field.attrs, type_attrs);
-        Self { ident, attrs }
+        Self {
+            ident,
+            attrs,
+            is_enum: false,
+        }
     }
 
     /// Lower to [`TokenStream`].
     fn to_tokens(&self) -> TokenStream {
         let ident = &self.ident;
-        let mut binding1 = quote!(self.#ident);
-        let mut binding2 = quote!(other.#ident);
 
-        if let Some(ty) = &self.attrs.asn1_type {
-            binding1 = ty.encoder(&binding1);
-            binding2 = ty.encoder(&binding2);
-        }
+        if self.is_enum {
+            let binding1 = quote!(Self::#ident(this));
+            let binding2 = quote!(Self::#ident(other));
+            quote! {
+                (#binding1, #binding2) => this.value_cmp(other),
+            }
+        } else {
+            let mut binding1 = quote!(self.#ident);
+            let mut binding2 = quote!(other.#ident);
 
-        quote! {
-            match #binding1.der_cmp(&#binding2)? {
-                ::core::cmp::Ordering::Equal => (),
-                other => return Ok(other),
+            if let Some(ty) = &self.attrs.asn1_type {
+                binding1 = ty.encoder(&binding1);
+                binding2 = ty.encoder(&binding2);
+            }
+
+            quote! {
+                match #binding1.der_cmp(&#binding2)? {
+                    ::core::cmp::Ordering::Equal => (),
+                    other => return Ok(other),
+                }
             }
         }
     }

From 3b01ae85d0d2f99c7bf744097ae54fc4b31d8aab Mon Sep 17 00:00:00 2001
From: roblabla <unfiltered@roblab.la>
Date: Fri, 16 Sep 2022 11:58:35 +0200
Subject: [PATCH 5/5] Derive ValueOrd for a bunch of structs

---
 x509-cert/src/crl.rs                    | 8 ++++----
 x509-cert/src/ext.rs                    | 4 ++--
 x509-cert/src/ext/pkix/access.rs        | 4 ++--
 x509-cert/src/ext/pkix/certpolicy.rs    | 6 +++---
 x509-cert/src/ext/pkix/crl/dp.rs        | 4 ++--
 x509-cert/src/ext/pkix/name/dirstr.rs   | 4 ++--
 x509-cert/src/ext/pkix/name/dp.rs       | 4 ++--
 x509-cert/src/ext/pkix/name/ediparty.rs | 4 ++--
 x509-cert/src/ext/pkix/name/general.rs  | 4 ++--
 x509-cert/src/ext/pkix/name/other.rs    | 4 ++--
 x509-cert/src/ext/pkix/policymap.rs     | 4 ++--
 x509-cert/src/macros.rs                 | 7 +++++++
 x509-cert/src/time.rs                   | 6 +++---
 13 files changed, 35 insertions(+), 28 deletions(-)

diff --git a/x509-cert/src/crl.rs b/x509-cert/src/crl.rs
index 3e630435b..55fba27af 100644
--- a/x509-cert/src/crl.rs
+++ b/x509-cert/src/crl.rs
@@ -8,7 +8,7 @@ use crate::Version;
 use alloc::vec::Vec;
 
 use der::asn1::{BitStringRef, UIntRef};
-use der::Sequence;
+use der::{Sequence, ValueOrd};
 use spki::AlgorithmIdentifier;
 
 /// `CertificateList` as defined in [RFC 5280 Section 5.1].
@@ -22,7 +22,7 @@ use spki::AlgorithmIdentifier;
 /// ```
 ///
 /// [RFC 5280 Section 5.1]: https://datatracker.ietf.org/doc/html/rfc5280#section-5.1
-#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
+#[derive(Clone, Debug, Eq, PartialEq, Sequence, ValueOrd)]
 #[allow(missing_docs)]
 pub struct CertificateList<'a> {
     pub tbs_cert_list: TbsCertList<'a>,
@@ -44,7 +44,7 @@ pub struct CertificateList<'a> {
 /// ```
 ///
 /// [RFC 5280 Section 5.1]: https://datatracker.ietf.org/doc/html/rfc5280#section-5.1
-#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
+#[derive(Clone, Debug, Eq, PartialEq, Sequence, ValueOrd)]
 #[allow(missing_docs)]
 pub struct RevokedCert<'a> {
     pub serial_number: UIntRef<'a>,
@@ -71,7 +71,7 @@ pub struct RevokedCert<'a> {
 /// ```
 ///
 /// [RFC 5280 Section 5.1]: https://datatracker.ietf.org/doc/html/rfc5280#section-5.1
-#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
+#[derive(Clone, Debug, Eq, PartialEq, Sequence, ValueOrd)]
 #[allow(missing_docs)]
 pub struct TbsCertList<'a> {
     pub version: Version,
diff --git a/x509-cert/src/ext.rs b/x509-cert/src/ext.rs
index d717a7c8a..eb6f6fbb8 100644
--- a/x509-cert/src/ext.rs
+++ b/x509-cert/src/ext.rs
@@ -1,6 +1,6 @@
 //! Standardized X.509 Certificate Extensions
 
-use der::Sequence;
+use der::{Sequence, ValueOrd};
 use spki::ObjectIdentifier;
 
 pub mod pkix;
@@ -22,7 +22,7 @@ pub mod pkix;
 /// ```
 ///
 /// [RFC 5280 Section 4.1.2.9]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.9
-#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
+#[derive(Clone, Debug, Eq, PartialEq, Sequence, ValueOrd)]
 #[allow(missing_docs)]
 pub struct Extension<'a> {
     pub extn_id: ObjectIdentifier,
diff --git a/x509-cert/src/ext/pkix/access.rs b/x509-cert/src/ext/pkix/access.rs
index 5b74b6f86..0390235aa 100644
--- a/x509-cert/src/ext/pkix/access.rs
+++ b/x509-cert/src/ext/pkix/access.rs
@@ -6,7 +6,7 @@ use const_oid::{
     db::rfc5280::{ID_PE_AUTHORITY_INFO_ACCESS, ID_PE_SUBJECT_INFO_ACCESS},
     AssociatedOid,
 };
-use der::{asn1::ObjectIdentifier, Sequence};
+use der::{asn1::ObjectIdentifier, Sequence, ValueOrd};
 
 /// AuthorityInfoAccessSyntax as defined in [RFC 5280 Section 4.2.2.1].
 ///
@@ -50,7 +50,7 @@ impl_newtype!(SubjectInfoAccessSyntax<'a>, Vec<AccessDescription<'a>>);
 /// ```
 ///
 /// [RFC 5280 Section 4.2.2.1]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.2.1
-#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
+#[derive(Clone, Debug, Eq, PartialEq, Sequence, ValueOrd)]
 #[allow(missing_docs)]
 pub struct AccessDescription<'a> {
     pub access_method: ObjectIdentifier,
diff --git a/x509-cert/src/ext/pkix/certpolicy.rs b/x509-cert/src/ext/pkix/certpolicy.rs
index 382142741..bb2211fba 100644
--- a/x509-cert/src/ext/pkix/certpolicy.rs
+++ b/x509-cert/src/ext/pkix/certpolicy.rs
@@ -5,7 +5,7 @@ use alloc::vec::Vec;
 use const_oid::db::rfc5912::ID_CE_CERTIFICATE_POLICIES;
 use const_oid::AssociatedOid;
 use der::asn1::{GeneralizedTime, Ia5StringRef, ObjectIdentifier, UIntRef, Utf8StringRef};
-use der::{AnyRef, Choice, Sequence};
+use der::{AnyRef, Choice, Sequence, ValueOrd};
 
 /// CertificatePolicies as defined in [RFC 5280 Section 4.2.1.4].
 ///
@@ -35,7 +35,7 @@ impl_newtype!(CertificatePolicies<'a>, Vec<PolicyInformation<'a>>);
 /// ```
 ///
 /// [RFC 5280 Section 4.2.1.4]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.4
-#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
+#[derive(Clone, Debug, Eq, PartialEq, Sequence, ValueOrd)]
 #[allow(missing_docs)]
 pub struct PolicyInformation<'a> {
     pub policy_identifier: ObjectIdentifier,
@@ -52,7 +52,7 @@ pub struct PolicyInformation<'a> {
 /// ```
 ///
 /// [RFC 5280 Section 4.2.1.4]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.4
-#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
+#[derive(Clone, Debug, Eq, PartialEq, Sequence, ValueOrd)]
 #[allow(missing_docs)]
 pub struct PolicyQualifierInfo<'a> {
     pub policy_qualifier_id: ObjectIdentifier,
diff --git a/x509-cert/src/ext/pkix/crl/dp.rs b/x509-cert/src/ext/pkix/crl/dp.rs
index 7aa8a2d2d..f7d1ab724 100644
--- a/x509-cert/src/ext/pkix/crl/dp.rs
+++ b/x509-cert/src/ext/pkix/crl/dp.rs
@@ -1,7 +1,7 @@
 //! PKIX distribution point types
 
 use const_oid::{db::rfc5280::ID_PE_SUBJECT_INFO_ACCESS, AssociatedOid, ObjectIdentifier};
-use der::Sequence;
+use der::{Sequence, ValueOrd};
 use flagset::{flags, FlagSet};
 
 use crate::ext::pkix::name::{DistributionPointName, GeneralNames};
@@ -74,7 +74,7 @@ impl<'a> AssociatedOid for IssuingDistributionPoint<'a> {
 /// ```
 ///
 /// [RFC 5280 Section 4.2.1.13]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.13
-#[derive(Clone, Debug, PartialEq, Eq, Sequence)]
+#[derive(Clone, Debug, PartialEq, Eq, Sequence, ValueOrd)]
 #[allow(missing_docs)]
 pub struct DistributionPoint<'a> {
     #[asn1(context_specific = "0", tag_mode = "EXPLICIT", optional = "true")]
diff --git a/x509-cert/src/ext/pkix/name/dirstr.rs b/x509-cert/src/ext/pkix/name/dirstr.rs
index 283b53f11..2aaa732bd 100644
--- a/x509-cert/src/ext/pkix/name/dirstr.rs
+++ b/x509-cert/src/ext/pkix/name/dirstr.rs
@@ -1,5 +1,5 @@
 use der::asn1::{PrintableStringRef, TeletexStringRef, Utf8StringRef};
-use der::Choice;
+use der::{Choice, ValueOrd};
 
 /// DirectoryString as defined in [RFC 5280 Section 4.2.1.4].
 ///
@@ -38,7 +38,7 @@ use der::Choice;
 /// the need arises, we only support `PrintableString` and `UTF8String`.
 ///
 /// [RFC 5280 Section 4.2.1.4]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.4
-#[derive(Clone, Debug, Eq, PartialEq, Choice)]
+#[derive(Clone, Debug, Eq, PartialEq, Choice, ValueOrd)]
 #[allow(missing_docs)]
 pub enum DirectoryString<'a> {
     #[asn1(type = "PrintableString")]
diff --git a/x509-cert/src/ext/pkix/name/dp.rs b/x509-cert/src/ext/pkix/name/dp.rs
index 3cfdf969a..e895f88a0 100644
--- a/x509-cert/src/ext/pkix/name/dp.rs
+++ b/x509-cert/src/ext/pkix/name/dp.rs
@@ -1,7 +1,7 @@
 use super::GeneralNames;
 use crate::name::RelativeDistinguishedName;
 
-use der::Choice;
+use der::{Choice, ValueOrd};
 
 /// DistributionPointName as defined in [RFC 5280 Section 4.2.1.13].
 ///
@@ -13,7 +13,7 @@ use der::Choice;
 /// ```
 ///
 /// [RFC 5280 Section 4.2.1.13]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.13
-#[derive(Clone, Debug, Eq, PartialEq, Choice)]
+#[derive(Clone, Debug, Eq, PartialEq, Choice, ValueOrd)]
 #[allow(missing_docs)]
 pub enum DistributionPointName<'a> {
     #[asn1(context_specific = "0", tag_mode = "IMPLICIT", constructed = "true")]
diff --git a/x509-cert/src/ext/pkix/name/ediparty.rs b/x509-cert/src/ext/pkix/name/ediparty.rs
index 06ee320b3..94af09b00 100644
--- a/x509-cert/src/ext/pkix/name/ediparty.rs
+++ b/x509-cert/src/ext/pkix/name/ediparty.rs
@@ -1,4 +1,4 @@
-use der::Sequence;
+use der::{Sequence, ValueOrd};
 
 use super::DirectoryString;
 
@@ -25,7 +25,7 @@ use super::DirectoryString;
 ///
 /// [this OpenSSL bug]: https://github.com/openssl/openssl/issues/6859
 /// [RFC 5280 Section 4.2.1.6]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.6
-#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
+#[derive(Clone, Debug, Eq, PartialEq, Sequence, ValueOrd)]
 #[allow(missing_docs)]
 pub struct EdiPartyName<'a> {
     #[asn1(context_specific = "0", tag_mode = "EXPLICIT", optional = "true")]
diff --git a/x509-cert/src/ext/pkix/name/general.rs b/x509-cert/src/ext/pkix/name/general.rs
index 0daa36846..f6861d4c7 100644
--- a/x509-cert/src/ext/pkix/name/general.rs
+++ b/x509-cert/src/ext/pkix/name/general.rs
@@ -4,7 +4,7 @@ use super::{EdiPartyName, OtherName};
 use crate::name::Name;
 
 use der::asn1::{Ia5StringRef, ObjectIdentifier, OctetStringRef};
-use der::Choice;
+use der::{Choice, ValueOrd};
 
 /// GeneralNames as defined in [RFC 5280 Section 4.2.1.6].
 ///
@@ -34,7 +34,7 @@ pub type GeneralNames<'a> = alloc::vec::Vec<GeneralName<'a>>;
 /// This implementation does not currently support the `x400Address` choice.
 ///
 /// [RFC 5280 Section 4.2.1.6]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.6
-#[derive(Clone, Debug, Eq, PartialEq, Choice)]
+#[derive(Clone, Debug, Eq, PartialEq, Choice, ValueOrd)]
 #[allow(missing_docs)]
 pub enum GeneralName<'a> {
     #[asn1(context_specific = "0", tag_mode = "IMPLICIT", constructed = "true")]
diff --git a/x509-cert/src/ext/pkix/name/other.rs b/x509-cert/src/ext/pkix/name/other.rs
index 4a250bb45..a33601c07 100644
--- a/x509-cert/src/ext/pkix/name/other.rs
+++ b/x509-cert/src/ext/pkix/name/other.rs
@@ -1,4 +1,4 @@
-use der::{asn1::ObjectIdentifier, AnyRef, Sequence};
+use der::{asn1::ObjectIdentifier, AnyRef, Sequence, ValueOrd};
 
 /// OtherName as defined in [RFC 5280 Section 4.2.1.6].
 ///
@@ -10,7 +10,7 @@ use der::{asn1::ObjectIdentifier, AnyRef, Sequence};
 /// ```
 ///
 /// [RFC 5280 Section 4.2.1.6]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.6
-#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
+#[derive(Clone, Debug, Eq, PartialEq, Sequence, ValueOrd)]
 #[allow(missing_docs)]
 pub struct OtherName<'a> {
     pub type_id: ObjectIdentifier,
diff --git a/x509-cert/src/ext/pkix/policymap.rs b/x509-cert/src/ext/pkix/policymap.rs
index 35b1d4987..e6b55bad5 100644
--- a/x509-cert/src/ext/pkix/policymap.rs
+++ b/x509-cert/src/ext/pkix/policymap.rs
@@ -3,7 +3,7 @@ use alloc::vec::Vec;
 use const_oid::db::rfc5280::ID_CE_POLICY_MAPPINGS;
 use const_oid::AssociatedOid;
 use der::asn1::ObjectIdentifier;
-use der::Sequence;
+use der::{Sequence, ValueOrd};
 
 /// PolicyMappings as defined in [RFC 5280 Section 4.2.1.5].
 ///
@@ -31,7 +31,7 @@ impl_newtype!(PolicyMappings, Vec<PolicyMapping>);
 /// ```
 ///
 /// [RFC 5280 Section 4.2.1.5]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.5
-#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
+#[derive(Clone, Debug, Eq, PartialEq, Sequence, ValueOrd)]
 #[allow(missing_docs)]
 pub struct PolicyMapping {
     pub issuer_domain_policy: ObjectIdentifier,
diff --git a/x509-cert/src/macros.rs b/x509-cert/src/macros.rs
index 3256c2425..638d51686 100644
--- a/x509-cert/src/macros.rs
+++ b/x509-cert/src/macros.rs
@@ -69,5 +69,12 @@ macro_rules! impl_newtype {
                 self.0.value_len()
             }
         }
+
+        #[allow(unused_lifetimes)]
+        impl<'a> ::der::ValueOrd for $newtype {
+            fn value_cmp(&self, other: &Self) -> ::der::Result<::core::cmp::Ordering> {
+                self.0.value_cmp(&other.0)
+            }
+        }
     };
 }
diff --git a/x509-cert/src/time.rs b/x509-cert/src/time.rs
index e938c13d0..d1f2ec5d5 100644
--- a/x509-cert/src/time.rs
+++ b/x509-cert/src/time.rs
@@ -3,7 +3,7 @@
 use core::fmt;
 use core::time::Duration;
 use der::asn1::{GeneralizedTime, UtcTime};
-use der::{Choice, DateTime, Decode, Error, Result, Sequence};
+use der::{Choice, DateTime, Decode, Error, Result, Sequence, ValueOrd};
 
 #[cfg(feature = "std")]
 use std::time::SystemTime;
@@ -21,7 +21,7 @@ use std::time::SystemTime;
 ///
 /// [RFC 5280 Section 4.1.2.5]: https://tools.ietf.org/html/rfc5280#section-4.1.2.5
 /// [RFC 5280 Appendix A]: https://tools.ietf.org/html/rfc5280#page-117
-#[derive(Choice, Copy, Clone, Debug, Eq, PartialEq)]
+#[derive(Choice, Copy, Clone, Debug, Eq, PartialEq, ValueOrd)]
 pub enum Time {
     /// Legacy UTC time (has 2-digit year, valid only through 2050).
     #[asn1(type = "UTCTime")]
@@ -113,7 +113,7 @@ impl TryFrom<SystemTime> for Time {
 /// }
 /// ```
 /// [RFC 5280 Section 4.1.2.5]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.5
-#[derive(Copy, Clone, Debug, Eq, PartialEq, Sequence)]
+#[derive(Copy, Clone, Debug, Eq, PartialEq, Sequence, ValueOrd)]
 pub struct Validity {
     /// notBefore value
     pub not_before: Time,
