Repository: randombit/botan
Pull request #3225 (2023-01-25 22:31:59)

Make Key_Constraints an "enum class"-like class
#3211 


Patch:
diff --git a/doc/api_ref/x509.rst b/doc/api_ref/x509.rst
index a6c62a207..2641bb378 100644
--- a/doc/api_ref/x509.rst
+++ b/doc/api_ref/x509.rst
@@ -128,11 +128,17 @@ The major certificate format in use today is X.509v3, used for instance in the
 
   .. cpp:function:: Key_Constraints constraints() const
 
-      Returns either an enumeration listing key constraints (what the
-      associated key can be used for) or ``NO_CONSTRAINTS`` if the
-      relevant extension was not included. Example values are
-      ``DIGITAL_SIGNATURE`` and ``KEY_CERT_SIGN``. More than one value
-      might be specified.
+      Returns a basic list of constraints which govern usage of the
+      key embedded in this certificate.
+
+      The Key_Constraints is a class that behaves somewhat like an
+      ``enum``. The easiest way to use it is with its ``includes``
+      method. For example::
+
+        constraints().includes(Key_Constraints::DigitalSignature)
+
+      checks if the certificate key is valid for generating digital
+      signatures.
 
   .. cpp:function:: bool matches_dns_name(const std::string& name) const
 
@@ -776,13 +782,13 @@ Other constraints can be set by calling the member functions
 ``Key_Constraints`` value, and replaces any previously set value. If
 no value is set, then the certificate key is marked as being valid for
 any usage.  You can set it to any of the following (for more than one
-usage, OR them together): ``DIGITAL_SIGNATURE``, ``NON_REPUDIATION``,
-``KEY_ENCIPHERMENT``, ``DATA_ENCIPHERMENT``, ``KEY_AGREEMENT``,
-``KEY_CERT_SIGN``, ``CRL_SIGN``, ``ENCIPHER_ONLY``,
-``DECIPHER_ONLY``. Many of these have quite special semantics, so you
-should either consult the appropriate standards document (such as RFC
-5280), or just not call ``add_constraints``, in which case the
-appropriate values will be chosen for you.
+usage, OR them together): ``DigitalSignature``, ``NonRepudiation``,
+``KeyEncipherment``, ``DataEncipherment``, ``KeyAgreement``,
+``KeyCertSign``, ``CrlSign``, ``EncipherOnly``, or ``DecipherOnly``.
+Many of these have quite special semantics, so you should either
+consult the appropriate standards document (such as RFC 5280), or just
+not call ``add_constraints``, in which case the appropriate values
+will be chosen for you based on the key type.
 
 The second function, ``add_ex_constraints``, allows you to specify an
 OID that has some meaning with regards to restricting the key to
diff --git a/src/lib/tls/tls12/tls_client_impl_12.cpp b/src/lib/tls/tls12/tls_client_impl_12.cpp
index 5da8f88d0..0f1b133ce 100644
--- a/src/lib/tls/tls12/tls_client_impl_12.cpp
+++ b/src/lib/tls/tls12/tls_client_impl_12.cpp
@@ -197,16 +197,18 @@ namespace {
 bool key_usage_matches_ciphersuite(Key_Constraints usage,
                                    const Ciphersuite& suite)
    {
-   if(usage == NO_CONSTRAINTS)
+   if(usage == Key_Constraints::None)
       return true; // anything goes ...
 
    if(suite.kex_method() == Kex_Algo::STATIC_RSA)
       {
-      return (usage & KEY_ENCIPHERMENT) | (usage & DATA_ENCIPHERMENT);
+      return usage.includes_any(Key_Constraints::KeyEncipherment,
+                                Key_Constraints::DataEncipherment);
       }
    else
       {
-      return (usage & DIGITAL_SIGNATURE) | (usage & NON_REPUDIATION);
+      return usage.includes_any(Key_Constraints::DigitalSignature,
+                                Key_Constraints::NonRepudiation);
       }
    }
 
diff --git a/src/lib/tls/tls12/tls_server_impl_12.cpp b/src/lib/tls/tls12/tls_server_impl_12.cpp
index a3e2e3bd6..7e2876e49 100644
--- a/src/lib/tls/tls12/tls_server_impl_12.cpp
+++ b/src/lib/tls/tls12/tls_server_impl_12.cpp
@@ -576,7 +576,7 @@ void Server_Impl_12::process_certificate_verify_msg(Server_Handshake_State& pend
    if(client_certs.empty())
       throw TLS_Exception(Alert::DECODE_ERROR, "No client certificate sent");
 
-   if(!client_certs[0].allowed_usage(DIGITAL_SIGNATURE))
+   if(!client_certs[0].allowed_usage(Key_Constraints::DigitalSignature))
       throw TLS_Exception(Alert::BAD_CERTIFICATE, "Client certificate does not support signing");
 
    const bool sig_valid =
diff --git a/src/lib/tls/tls13/msg_certificate_13.cpp b/src/lib/tls/tls13/msg_certificate_13.cpp
index 4670af457..e844e08d0 100644
--- a/src/lib/tls/tls13/msg_certificate_13.cpp
+++ b/src/lib/tls/tls13/msg_certificate_13.cpp
@@ -29,10 +29,11 @@ namespace {
 bool certificate_allows_signing(const X509_Certificate& cert)
    {
    const auto constraints = cert.constraints();
-   if(constraints == NO_CONSTRAINTS)
+   if(constraints.empty())
       { return true; }
 
-   return constraints & DIGITAL_SIGNATURE || constraints & NON_REPUDIATION;
+   return constraints.includes_any(Key_Constraints::DigitalSignature,
+                                   Key_Constraints::NonRepudiation);
    }
 
 std::vector<std::string>
diff --git a/src/lib/x509/key_constraint.cpp b/src/lib/x509/key_constraint.cpp
index 957e4cdc3..769471801 100644
--- a/src/lib/x509/key_constraint.cpp
+++ b/src/lib/x509/key_constraint.cpp
@@ -1,49 +1,48 @@
 /*
-* KeyUsage
 * (C) 1999-2007,2016 Jack Lloyd
 * (C) 2016 René Korthaus, Rohde & Schwarz Cybersecurity
 *
 * Botan is released under the Simplified BSD License (see license.txt)
 */
 
-#include <botan/pkix_types.h>
+#include <botan/pkix_enums.h>
 #include <botan/pk_keys.h>
 #include <vector>
 
 namespace Botan {
 
-std::string key_constraints_to_string(Key_Constraints constraints)
+std::string Key_Constraints::to_string() const
    {
-   std::vector<std::string> str;
-
-   if(constraints == NO_CONSTRAINTS)
+   if(this->m_value == Key_Constraints::None)
       return "no_constraints";
 
-   if(constraints & DIGITAL_SIGNATURE)
+   std::vector<std::string> str;
+
+   if(this->m_value & Key_Constraints::DigitalSignature)
       str.push_back("digital_signature");
 
-   if(constraints & NON_REPUDIATION)
+   if(this->m_value & Key_Constraints::NonRepudiation)
       str.push_back("non_repudiation");
 
-   if(constraints & KEY_ENCIPHERMENT)
+   if(this->m_value & Key_Constraints::KeyEncipherment)
       str.push_back("key_encipherment");
 
-   if(constraints & DATA_ENCIPHERMENT)
+   if(this->m_value & Key_Constraints::DataEncipherment)
       str.push_back("data_encipherment");
 
-   if(constraints & KEY_AGREEMENT)
+   if(this->m_value & Key_Constraints::KeyAgreement)
       str.push_back("key_agreement");
 
-   if(constraints & KEY_CERT_SIGN)
+   if(this->m_value & Key_Constraints::KeyCertSign)
       str.push_back("key_cert_sign");
 
-   if(constraints & CRL_SIGN)
+   if(this->m_value & Key_Constraints::CrlSign)
       str.push_back("crl_sign");
 
-   if(constraints & ENCIPHER_ONLY)
+   if(this->m_value & Key_Constraints::EncipherOnly)
       str.push_back("encipher_only");
 
-   if(constraints & DECIPHER_ONLY)
+   if(this->m_value & Key_Constraints::DecipherOnly)
       str.push_back("decipher_only");
 
    // Not 0 (checked at start) but nothing matched above!
@@ -67,13 +66,10 @@ std::string key_constraints_to_string(Key_Constraints constraints)
 /*
 * Make sure the given key constraints are permitted for the given key type
 */
-void verify_cert_constraints_valid_for_key_type(const Public_Key& pub_key,
-                                                Key_Constraints constraints)
+bool Key_Constraints::compatible_with(const Public_Key& pub_key) const
    {
    const std::string name = pub_key.algo_name();
 
-   size_t permitted = 0;
-
    const bool can_agree = (name == "DH" || name == "ECDH" || name.starts_with("Kyber-"));
    const bool can_encrypt = (name == "RSA" || name == "ElGamal" || name.starts_with("Kyber-"));
 
@@ -83,25 +79,35 @@ void verify_cert_constraints_valid_for_key_type(const Public_Key& pub_key,
        name == "GOST-34.10" || name == "GOST-34.10-2012-256" || name == "GOST-34.10-2012-512" ||
        name.starts_with("Dilithium-"));
 
+   uint32_t permitted = 0;
+
    if(can_agree)
       {
-      permitted |= KEY_AGREEMENT | ENCIPHER_ONLY | DECIPHER_ONLY;
+      permitted |= Key_Constraints::KeyAgreement |
+         Key_Constraints::EncipherOnly |
+         Key_Constraints::DecipherOnly;
       }
 
    if(can_encrypt)
       {
-      permitted |= KEY_ENCIPHERMENT | DATA_ENCIPHERMENT;
+      permitted |= Key_Constraints::KeyEncipherment |
+         Key_Constraints::DataEncipherment;
       }
 
    if(can_sign)
       {
-      permitted |= DIGITAL_SIGNATURE | NON_REPUDIATION | KEY_CERT_SIGN | CRL_SIGN;
+      permitted |= Key_Constraints::DigitalSignature |
+         Key_Constraints::NonRepudiation |
+         Key_Constraints::KeyCertSign |
+         Key_Constraints::CrlSign;
       }
 
-   if(Key_Constraints(constraints & permitted) != constraints)
+   if((m_value & permitted) != m_value)
       {
-      throw Invalid_Argument("Invalid " + name + " constraints " + key_constraints_to_string(constraints));
+      return false;
       }
+
+   return true;
    }
 
 }
diff --git a/src/lib/x509/pkcs10.cpp b/src/lib/x509/pkcs10.cpp
index 1c8575fd6..cdaf50be1 100644
--- a/src/lib/x509/pkcs10.cpp
+++ b/src/lib/x509/pkcs10.cpp
@@ -256,7 +256,7 @@ Key_Constraints PKCS10_Request::constraints() const
       return dynamic_cast<Cert_Extension::Key_Usage&>(*ext).get_constraints();
       }
 
-   return NO_CONSTRAINTS;
+   return Key_Constraints::None;
    }
 
 /*
diff --git a/src/lib/x509/pkix_enums.h b/src/lib/x509/pkix_enums.h
index 8263abd3f..7d7668260 100644
--- a/src/lib/x509/pkix_enums.h
+++ b/src/lib/x509/pkix_enums.h
@@ -1,5 +1,5 @@
 /*
-* (C) 2013 Jack Lloyd
+* (C) 2013,2023 Jack Lloyd
 *
 * Botan is released under the Simplified BSD License (see license.txt)
 */
@@ -8,9 +8,11 @@
 #define BOTAN_X509_PKIX_ENUMS_H_
 
 #include <botan/types.h>
-
+#include <string>
 namespace Botan {
 
+class Public_Key;
+
 /**
 * Certificate validation status code
 */
@@ -100,18 +102,87 @@ BOTAN_PUBLIC_API(2,0) const char* to_string(Certificate_Status_Code code);
 * X.509v3 Key Constraints.
 * If updating update copy in ffi.h
 */
-enum Key_Constraints {
-   NO_CONSTRAINTS     = 0,
-   DIGITAL_SIGNATURE  = 1 << 15,
-   NON_REPUDIATION    = 1 << 14,
-   KEY_ENCIPHERMENT   = 1 << 13,
-   DATA_ENCIPHERMENT  = 1 << 12,
-   KEY_AGREEMENT      = 1 << 11,
-   KEY_CERT_SIGN      = 1 << 10,
-   CRL_SIGN           = 1 << 9,
-   ENCIPHER_ONLY      = 1 << 8,
-   DECIPHER_ONLY      = 1 << 7
-};
+class BOTAN_PUBLIC_API(3,0) Key_Constraints
+   {
+   public:
+      enum Bits : uint32_t {
+         None              = 0,
+         DigitalSignature  = 1 << 15,
+         NonRepudiation    = 1 << 14,
+         KeyEncipherment   = 1 << 13,
+         DataEncipherment  = 1 << 12,
+         KeyAgreement      = 1 << 11,
+         KeyCertSign       = 1 << 10,
+         CrlSign           = 1 << 9,
+         EncipherOnly      = 1 << 8,
+         DecipherOnly      = 1 << 7,
+
+         // Deprecated SHOUTING_CASE names for Key_Constraints
+         // will be removed in a future major release
+         NO_CONSTRAINTS    BOTAN_DEPRECATED("Use None")             = None,
+         DIGITAL_SIGNATURE BOTAN_DEPRECATED("Use DigitalSignature") = DigitalSignature,
+         NON_REPUDIATION   BOTAN_DEPRECATED("Use NonRepudiation")   = NonRepudiation,
+         KEY_ENCIPHERMENT  BOTAN_DEPRECATED("Use KeyEncipherment")  = KeyEncipherment,
+         DATA_ENCIPHERMENT BOTAN_DEPRECATED("Use DataEncipherment") = DataEncipherment,
+         KEY_AGREEMENT     BOTAN_DEPRECATED("Use KeyAgreement")     = KeyAgreement,
+         KEY_CERT_SIGN     BOTAN_DEPRECATED("Use KeyCertSign")      = KeyCertSign,
+         CRL_SIGN          BOTAN_DEPRECATED("Use CrlSign")          = CrlSign,
+         ENCIPHER_ONLY     BOTAN_DEPRECATED("Use EncipherOnly")     = EncipherOnly,
+         DECIPHER_ONLY     BOTAN_DEPRECATED("Use DecipherOnly")     = DecipherOnly,
+      };
+
+      Key_Constraints(const Key_Constraints& other) = default;
+      Key_Constraints(Key_Constraints&& other) = default;
+      Key_Constraints& operator=(const Key_Constraints& other) = default;
+      Key_Constraints& operator=(Key_Constraints&& other) = default;
+
+      Key_Constraints(Key_Constraints::Bits bits) : m_value(bits) {}
+
+      explicit Key_Constraints(uint32_t bits) : m_value(bits) {}
+
+      Key_Constraints() : m_value(0) {}
+
+      /**
+      * Return typical constraints for a CA certificate, namely
+      * KeyCertSign and CrlSign
+      */
+      static Key_Constraints ca_constraints()
+         {
+         return Key_Constraints(Key_Constraints::KeyCertSign | Key_Constraints::CrlSign);
+         }
+
+      bool operator==(const Key_Constraints&) const = default;
+
+      void operator|=(Key_Constraints::Bits other)
+         {
+         m_value |= other;
+         }
+
+      // Return true if all bits in mask are set
+      bool includes(Key_Constraints::Bits other) const { return (m_value & other) == other; }
+      bool includes(Key_Constraints other) const { return (m_value & other.m_value) == other.m_value; }
+
+      // Return true if any of the bits provided are set
+      bool includes_any(auto&& ...bits) const
+         {
+         return (m_value & (bits | ...)) > 0;
+         }
+
+      bool empty() const { return m_value == 0; }
+
+      uint32_t value() const { return m_value; }
+
+      std::string to_string() const;
+
+      /**
+      * Check that key constraints are permitted for a specific public key.
+      * @param key the public key on which the constraints shall be enforced on
+      * @return false if the constraints are not permitted for this key
+      */
+      bool compatible_with(const Public_Key& key) const;
+   private:
+      uint32_t m_value;
+   };
 
 /**
 * X.509v2 CRL Reason Code.
diff --git a/src/lib/x509/pkix_types.h b/src/lib/x509/pkix_types.h
index 93e829884..7f4215e02 100644
--- a/src/lib/x509/pkix_types.h
+++ b/src/lib/x509/pkix_types.h
@@ -24,16 +24,11 @@ namespace Botan {
 class X509_Certificate;
 class Public_Key;
 
-/**
-* Check that key constraints are permitted for a specific public key.
-* @param pub_key the public key on which the constraints shall be enforced on
-* @param constraints the constraints that shall be enforced on the key
-* @throw Invalid_Argument if the given constraints are not permitted for this key
-*/
-BOTAN_PUBLIC_API(2,0) void verify_cert_constraints_valid_for_key_type(const Public_Key& pub_key,
-                                                                      Key_Constraints constraints);
-
-std::string BOTAN_PUBLIC_API(2,0) key_constraints_to_string(Key_Constraints);
+BOTAN_DEPRECATED("Use Key_Constraints::to_string")
+inline std::string key_constraints_to_string(Key_Constraints c)
+   {
+   return c.to_string();
+   }
 
 /**
 * Distinguished Name
diff --git a/src/lib/x509/x509_ca.cpp b/src/lib/x509/x509_ca.cpp
index 096eb7805..d7bcb90f8 100644
--- a/src/lib/x509/x509_ca.cpp
+++ b/src/lib/x509/x509_ca.cpp
@@ -78,17 +78,12 @@ Extensions choose_extensions(const PKCS10_Request& req,
                              const X509_Certificate& ca_cert,
                              const std::string& hash_fn)
    {
-   Key_Constraints constraints;
-   if(req.is_CA())
-      {
-      constraints = Key_Constraints(KEY_CERT_SIGN | CRL_SIGN);
-      }
-   else
-      {
-      std::unique_ptr<Public_Key> key(req.subject_public_key());
-      verify_cert_constraints_valid_for_key_type(*key, req.constraints());
-      constraints = req.constraints();
-      }
+   const auto constraints =
+      req.is_CA() ? Key_Constraints::ca_constraints() : req.constraints();
+
+   std::unique_ptr<Public_Key> key(req.subject_public_key());
+   if(!constraints.compatible_with(*key))
+      throw Invalid_Argument("The requested key constraints are incompatible with the algorithm");
 
    Extensions extensions = req.extensions();
 
@@ -96,7 +91,7 @@ Extensions choose_extensions(const PKCS10_Request& req,
       std::make_unique<Cert_Extension::Basic_Constraints>(req.is_CA(), req.path_limit()),
       true);
 
-   if(constraints != NO_CONSTRAINTS)
+   if(!constraints.empty())
       {
       extensions.replace(std::make_unique<Cert_Extension::Key_Usage>(constraints), true);
       }
diff --git a/src/lib/x509/x509_ext.cpp b/src/lib/x509/x509_ext.cpp
index 1b82107fd..fc46c3968 100644
--- a/src/lib/x509/x509_ext.cpp
+++ b/src/lib/x509/x509_ext.cpp
@@ -337,18 +337,19 @@ void Basic_Constraints::decode_inner(const std::vector<uint8_t>& in)
 */
 std::vector<uint8_t> Key_Usage::encode_inner() const
    {
-   if(m_constraints == NO_CONSTRAINTS)
-      throw Encoding_Error("Cannot encode zero usage constraints");
+   if(m_constraints.empty())
+      throw Encoding_Error("Cannot encode empty PKIX key constraints");
 
-   const size_t unused_bits = ctz(static_cast<uint32_t>(m_constraints));
+   const size_t constraint_bits = m_constraints.value();
+   const size_t unused_bits = ctz(static_cast<uint32_t>(constraint_bits));
 
    std::vector<uint8_t> der;
    der.push_back(static_cast<uint8_t>(ASN1_Type::BitString));
    der.push_back(2 + ((unused_bits < 8) ? 1 : 0));
    der.push_back(unused_bits % 8);
-   der.push_back((m_constraints >> 8) & 0xFF);
-   if(m_constraints & 0xFF)
-      der.push_back(m_constraints & 0xFF);
+   der.push_back((constraint_bits >> 8) & 0xFF);
+   if(constraint_bits & 0xFF)
+      der.push_back(constraint_bits & 0xFF);
 
    return der;
    }
diff --git a/src/lib/x509/x509_ext.h b/src/lib/x509/x509_ext.h
index ffaa2a63a..d1d5e9fa1 100644
--- a/src/lib/x509/x509_ext.h
+++ b/src/lib/x509/x509_ext.h
@@ -59,7 +59,9 @@ class BOTAN_PUBLIC_API(2,0) Key_Usage final : public Certificate_Extension
          return std::make_unique<Key_Usage>(m_constraints);
          }
 
-      explicit Key_Usage(Key_Constraints c = NO_CONSTRAINTS) : m_constraints(c) {}
+      explicit Key_Usage(Key_Constraints c) : m_constraints(c) {}
+
+      explicit Key_Usage() : m_constraints(Key_Constraints::None) {}
 
       Key_Constraints get_constraints() const { return m_constraints; }
 
@@ -70,7 +72,7 @@ class BOTAN_PUBLIC_API(2,0) Key_Usage final : public Certificate_Extension
       std::string oid_name() const override { return "X509v3.KeyUsage"; }
 
       bool should_encode() const override
-         { return (m_constraints != NO_CONSTRAINTS); }
+         { return !m_constraints.empty(); }
       std::vector<uint8_t> encode_inner() const override;
       void decode_inner(const std::vector<uint8_t>&) override;
 
diff --git a/src/lib/x509/x509cert.cpp b/src/lib/x509/x509cert.cpp
index 1d636e287..aec2e897a 100644
--- a/src/lib/x509/x509cert.cpp
+++ b/src/lib/x509/x509cert.cpp
@@ -62,7 +62,7 @@ struct X509_Certificate_Data
 
    size_t m_version = 0;
    size_t m_path_len_constraint = 0;
-   Key_Constraints m_key_constraints = NO_CONSTRAINTS;
+   Key_Constraints m_key_constraints;
    bool m_self_signed = false;
    bool m_is_ca_certificate = false;
    bool m_serial_negative = false;
@@ -216,15 +216,11 @@ std::unique_ptr<X509_Certificate_Data> parse_x509_cert_body(const X509_Object& o
       RFC 5280: When the keyUsage extension appears in a certificate,
       at least one of the bits MUST be set to 1.
       */
-      if(data->m_key_constraints == NO_CONSTRAINTS)
+      if(data->m_key_constraints.empty())
          {
          throw Decoding_Error("Certificate has invalid encoding for KeyUsage");
          }
       }
-   else
-      {
-      data->m_key_constraints = NO_CONSTRAINTS;
-      }
 
    if(auto ext = data->m_v3_extensions.get_extension_object_as<Cert_Extension::Subject_Key_ID>())
       {
@@ -252,8 +248,8 @@ std::unique_ptr<X509_Certificate_Data> parse_x509_cert_body(const X509_Object& o
          * removing this entirely, or alternately adding a warning level
          * validation failure for it.
          */
-         if(data->m_key_constraints == NO_CONSTRAINTS ||
-            (data->m_key_constraints & KEY_CERT_SIGN))
+         if(data->m_key_constraints.empty() ||
+            data->m_key_constraints.includes(Key_Constraints::KeyCertSign))
             {
             data->m_is_ca_certificate = true;
             data->m_path_len_constraint = ext->get_path_limit();
@@ -518,11 +514,17 @@ const Extensions& X509_Certificate::v3_extensions() const
    return data().m_v3_extensions;
    }
 
+bool X509_Certificate::has_constraints(Key_Constraints usage) const
+   {
+   // Unlike allowed_usage, returns false if constraints was not set
+   return constraints().includes(usage);
+   }
+
 bool X509_Certificate::allowed_usage(Key_Constraints usage) const
    {
-   if(constraints() == NO_CONSTRAINTS)
+   if(constraints().empty())
       return true;
-   return ((constraints() & usage) == usage);
+   return constraints().includes(usage);
    }
 
 bool X509_Certificate::allowed_extended_usage(const std::string& usage) const
@@ -552,34 +554,32 @@ bool X509_Certificate::allowed_usage(Usage_Type usage) const
          return true;
 
       case Usage_Type::TLS_SERVER_AUTH:
-         return (allowed_usage(KEY_AGREEMENT) || allowed_usage(KEY_ENCIPHERMENT) || allowed_usage(DIGITAL_SIGNATURE)) && allowed_extended_usage("PKIX.ServerAuth");
+         return (allowed_usage(Key_Constraints::KeyAgreement) ||
+                 allowed_usage(Key_Constraints::KeyEncipherment) ||
+                 allowed_usage(Key_Constraints::DigitalSignature)) &&
+            allowed_extended_usage("PKIX.ServerAuth");
 
       case Usage_Type::TLS_CLIENT_AUTH:
-         return (allowed_usage(DIGITAL_SIGNATURE) || allowed_usage(KEY_AGREEMENT)) && allowed_extended_usage("PKIX.ClientAuth");
+         return (allowed_usage(Key_Constraints::DigitalSignature) ||
+                 allowed_usage(Key_Constraints::KeyAgreement)) &&
+            allowed_extended_usage("PKIX.ClientAuth");
 
       case Usage_Type::OCSP_RESPONDER:
-         return (allowed_usage(DIGITAL_SIGNATURE) || allowed_usage(NON_REPUDIATION)) && has_ex_constraint("PKIX.OCSPSigning");
+         return (allowed_usage(Key_Constraints::DigitalSignature) ||
+                 allowed_usage(Key_Constraints::NonRepudiation)) &&
+            has_ex_constraint("PKIX.OCSPSigning");
 
       case Usage_Type::CERTIFICATE_AUTHORITY:
          return is_CA_cert();
 
       case Usage_Type::ENCRYPTION:
-         return (allowed_usage(KEY_ENCIPHERMENT) || allowed_usage(DATA_ENCIPHERMENT));
+         return (allowed_usage(Key_Constraints::KeyEncipherment) ||
+                 allowed_usage(Key_Constraints::DataEncipherment));
       }
 
    return false;
    }
 
-bool X509_Certificate::has_constraints(Key_Constraints constraints) const
-   {
-   if(this->constraints() == NO_CONSTRAINTS)
-      {
-      return false;
-      }
-
-   return ((this->constraints() & constraints) != 0);
-   }
-
 bool X509_Certificate::has_ex_constraint(const std::string& ex_constraint) const
    {
    return has_ex_constraint(OID::from_string(ex_constraint));
@@ -775,27 +775,27 @@ std::string X509_Certificate::to_string() const
 
    out << "Constraints:\n";
    Key_Constraints constraints = this->constraints();
-   if(constraints == NO_CONSTRAINTS)
+   if(constraints.empty())
       out << " None\n";
    else
       {
-      if(constraints & DIGITAL_SIGNATURE)
+      if(constraints.includes(Key_Constraints::DigitalSignature))
          out << "   Digital Signature\n";
-      if(constraints & NON_REPUDIATION)
+      if(constraints.includes(Key_Constraints::NonRepudiation))
          out << "   Non-Repudiation\n";
-      if(constraints & KEY_ENCIPHERMENT)
+      if(constraints.includes(Key_Constraints::KeyEncipherment))
          out << "   Key Encipherment\n";
-      if(constraints & DATA_ENCIPHERMENT)
+      if(constraints.includes(Key_Constraints::DataEncipherment))
          out << "   Data Encipherment\n";
-      if(constraints & KEY_AGREEMENT)
+      if(constraints.includes(Key_Constraints::KeyAgreement))
          out << "   Key Agreement\n";
-      if(constraints & KEY_CERT_SIGN)
+      if(constraints.includes(Key_Constraints::KeyCertSign))
          out << "   Cert Sign\n";
-      if(constraints & CRL_SIGN)
+      if(constraints.includes(Key_Constraints::CrlSign))
          out << "   CRL Sign\n";
-      if(constraints & ENCIPHER_ONLY)
+      if(constraints.includes(Key_Constraints::EncipherOnly))
          out << "   Encipher Only\n";
-      if(constraints & DECIPHER_ONLY)
+      if(constraints.includes(Key_Constraints::DecipherOnly))
          out << "   Decipher Only\n";
       }
 
diff --git a/src/lib/x509/x509cert.h b/src/lib/x509/x509cert.h
index 94599d6a0..3109b0fa6 100644
--- a/src/lib/x509/x509cert.h
+++ b/src/lib/x509/x509cert.h
@@ -235,8 +235,10 @@ class BOTAN_PUBLIC_API(2,0) X509_Certificate : public X509_Object
       bool allowed_usage(Usage_Type usage) const;
 
       /**
-      * Returns true if the specified @param constraints are included in the key
-      * usage extension.
+      * Returns true if and only if the specified @param constraints are
+      * included in the key usage extension.
+      *
+      * Typically for applications you want allowed_usage instead.
       */
       bool has_constraints(Key_Constraints constraints) const;
 
diff --git a/src/lib/x509/x509opt.cpp b/src/lib/x509/x509opt.cpp
index b3b4354be..b804a4dfe 100644
--- a/src/lib/x509/x509opt.cpp
+++ b/src/lib/x509/x509opt.cpp
@@ -73,7 +73,6 @@ X509_Cert_Options::X509_Cert_Options(const std::string& initial_opts,
    {
    is_CA = false;
    path_limit = 0;
-   constraints = NO_CONSTRAINTS;
    // use default for chosen algorithm
    padding_scheme = "";
 
diff --git a/src/lib/x509/x509path.cpp b/src/lib/x509/x509path.cpp
index d4cc52a38..a6c01923c 100644
--- a/src/lib/x509/x509path.cpp
+++ b/src/lib/x509/x509path.cpp
@@ -55,8 +55,8 @@ PKIX::check_chain(const std::vector<X509_Certificate>& cert_path,
       cert_status[0].insert(Certificate_Status_Code::INVALID_USAGE);
       }
 
-   if(cert_path[0].is_CA_cert() == false &&
-      cert_path[0].has_constraints(KEY_CERT_SIGN))
+   if(cert_path[0].has_constraints(Key_Constraints::KeyCertSign) &&
+      cert_path[0].is_CA_cert() == false)
       {
       /*
       "If the keyCertSign bit is asserted, then the cA bit in the
@@ -358,7 +358,7 @@ PKIX::check_crl(const std::vector<X509_Certificate>& cert_path,
          const X509_Certificate& subject = cert_path.at(i);
          const X509_Certificate& ca = cert_path.at(i+1);
 
-         if(!ca.allowed_usage(CRL_SIGN))
+         if(!ca.allowed_usage(Key_Constraints::CrlSign))
             status.insert(Certificate_Status_Code::CA_CERT_NOT_FOR_CRL_ISSUER);
 
          if(validation_time < crls[i]->this_update())
diff --git a/src/lib/x509/x509self.cpp b/src/lib/x509/x509self.cpp
index 0cd77d367..1526c5b83 100644
--- a/src/lib/x509/x509self.cpp
+++ b/src/lib/x509/x509self.cpp
@@ -67,22 +67,17 @@ X509_Certificate create_self_signed_cert(const X509_Cert_Options& opts,
 
    Extensions extensions = opts.extensions;
 
-   Key_Constraints constraints;
-   if(opts.is_CA)
-      {
-      constraints = Key_Constraints(KEY_CERT_SIGN | CRL_SIGN);
-      }
-   else
-      {
-      verify_cert_constraints_valid_for_key_type(key, opts.constraints);
-      constraints = opts.constraints;
-      }
+   const auto constraints =
+      opts.is_CA ? Key_Constraints::ca_constraints() : opts.constraints;
+
+   if(!constraints.compatible_with(key))
+      throw Invalid_Argument("The requested key constraints are incompatible with the algorithm");
 
    extensions.add_new(
       std::make_unique<Cert_Extension::Basic_Constraints>(opts.is_CA, opts.path_limit),
       true);
 
-   if(constraints != NO_CONSTRAINTS)
+   if(!constraints.empty())
       {
       extensions.add_new(std::make_unique<Cert_Extension::Key_Usage>(constraints), true);
       }
@@ -116,25 +111,21 @@ PKCS10_Request create_cert_req(const X509_Cert_Options& opts,
    AlternativeName subject_alt;
    load_info(opts, subject_dn, subject_alt);
 
-   Key_Constraints constraints;
-   if(opts.is_CA)
-      {
-      constraints = Key_Constraints(KEY_CERT_SIGN | CRL_SIGN);
-      }
-   else
-      {
-      verify_cert_constraints_valid_for_key_type(key, opts.constraints);
-      constraints = opts.constraints;
-      }
+   const auto constraints =
+      opts.is_CA ? Key_Constraints::ca_constraints() : opts.constraints;
+
+   if(!constraints.compatible_with(key))
+      throw Invalid_Argument("The requested key constraints are incompatible with the algorithm");
 
    Extensions extensions = opts.extensions;
 
    extensions.add_new(std::make_unique<Cert_Extension::Basic_Constraints>(opts.is_CA, opts.path_limit));
 
-   if(constraints != NO_CONSTRAINTS)
+   if(!constraints.empty())
       {
       extensions.add_new(std::make_unique<Cert_Extension::Key_Usage>(constraints));
       }
+
    extensions.add_new(std::make_unique<Cert_Extension::Extended_Key_Usage>(opts.ex_constraints));
    extensions.add_new(std::make_unique<Cert_Extension::Subject_Alternative_Name>(subject_alt));
 


Test patch:
diff --git a/src/tests/unit_x509.cpp b/src/tests/unit_x509.cpp
index d741b8889..fa344430d 100644
--- a/src/tests/unit_x509.cpp
+++ b/src/tests/unit_x509.cpp
@@ -67,11 +67,11 @@ Botan::X509_Cert_Options req_opts1(const std::string& algo, const std::string& s
 
    if(algo == "RSA")
       {
-      opts.constraints = Botan::Key_Constraints(Botan::KEY_ENCIPHERMENT);
+      opts.constraints = Botan::Key_Constraints::KeyEncipherment;
       }
    else if(algo == "DSA" || algo == "ECDSA" || algo == "ECGDSA" || algo == "ECKCDSA")
       {
-      opts.constraints = Botan::Key_Constraints(Botan::DIGITAL_SIGNATURE);
+      opts.constraints = Botan::Key_Constraints::DigitalSignature;
       }
 
    return opts;
@@ -812,8 +812,10 @@ Test::Result test_x509_cert(const Botan::Private_Key& ca_key,
    const auto ca_cert = Botan::X509::create_self_signed_cert(ca_opts(sig_padding), ca_key, hash_fn, Test::rng());
 
       {
-      const auto constraints = Botan::Key_Constraints(Botan::KEY_CERT_SIGN | Botan::CRL_SIGN);
-      result.test_eq("ca key usage", (ca_cert.constraints() & constraints) == constraints, true);
+      result.confirm("ca key usage cert",
+                     ca_cert.constraints().includes(Botan::Key_Constraints::KeyCertSign));
+      result.confirm("ca key usage crl",
+                     ca_cert.constraints().includes(Botan::Key_Constraints::CrlSign));
       }
 
    /* Create user #1's key and cert request */
@@ -875,8 +877,8 @@ Test::Result test_x509_cert(const Botan::Private_Key& ca_key,
       Botan::X509::create_self_signed_cert(req_opts1(sig_algo, sig_padding), *user1_key, hash_fn, Test::rng());
 
       {
-      auto constrains = req_opts1(sig_algo).constraints;
-      result.test_eq("user1 key usage", (user1_cert.constraints() & constrains) == constrains, true);
+      auto constraints = req_opts1(sig_algo).constraints;
+      result.confirm("user1 key usage", user1_cert.constraints().includes(constraints));
       }
 
    /* Copy, assign and compare */
@@ -1013,7 +1015,7 @@ Test::Result test_usage(const Botan::Private_Key& ca_key,
    std::unique_ptr<Botan::Private_Key> user1_key(make_a_private_key(sig_algo));
 
    Botan::X509_Cert_Options opts("Test User 1/US/Botan Project/Testing");
-   opts.constraints = Key_Constraints::DIGITAL_SIGNATURE;
+   opts.constraints = Key_Constraints::DigitalSignature;
 
    const Botan::PKCS10_Request user1_req = Botan::X509::create_cert_req(
          opts,
@@ -1030,14 +1032,14 @@ Test::Result test_usage(const Botan::Private_Key& ca_key,
    // cert only allows digitalSignature, but we check for both digitalSignature and cRLSign
    result.test_eq("key usage cRLSign not allowed",
                   user1_cert.allowed_usage(
-                     Key_Constraints(Key_Constraints::DIGITAL_SIGNATURE | Key_Constraints::CRL_SIGN)), false);
+                     Key_Constraints(Key_Constraints::DigitalSignature | Key_Constraints::CrlSign)), false);
    result.test_eq("encryption is not allowed",
                   user1_cert.allowed_usage(Usage_Type::ENCRYPTION), false);
 
    // cert only allows digitalSignature, so checking for only that should be ok
-   result.confirm("key usage digitalSignature allowed", user1_cert.allowed_usage(Key_Constraints::DIGITAL_SIGNATURE));
+   result.confirm("key usage digitalSignature allowed", user1_cert.allowed_usage(Key_Constraints::DigitalSignature));
 
-   opts.constraints = Key_Constraints(Key_Constraints::DIGITAL_SIGNATURE | Key_Constraints::CRL_SIGN);
+   opts.constraints = Key_Constraints(Key_Constraints::DigitalSignature | Key_Constraints::CrlSign);
 
    const Botan::PKCS10_Request mult_usage_req = Botan::X509::create_cert_req(
             opts,
@@ -1053,15 +1055,15 @@ Test::Result test_usage(const Botan::Private_Key& ca_key,
 
    // cert allows multiple usages, so each one of them as well as both together should be allowed
    result.confirm("key usage multiple digitalSignature allowed",
-                  mult_usage_cert.allowed_usage(Key_Constraints::DIGITAL_SIGNATURE));
-   result.confirm("key usage multiple cRLSign allowed", mult_usage_cert.allowed_usage(Key_Constraints::CRL_SIGN));
+                  mult_usage_cert.allowed_usage(Key_Constraints::DigitalSignature));
+   result.confirm("key usage multiple cRLSign allowed", mult_usage_cert.allowed_usage(Key_Constraints::CrlSign));
    result.confirm("key usage multiple digitalSignature and cRLSign allowed", mult_usage_cert.allowed_usage(
-                     Key_Constraints(Key_Constraints::DIGITAL_SIGNATURE | Key_Constraints::CRL_SIGN)));
+                     Key_Constraints(Key_Constraints::DigitalSignature | Key_Constraints::CrlSign)));
    result.test_eq("encryption is not allowed",
                   mult_usage_cert.allowed_usage(Usage_Type::ENCRYPTION), false);
 
 
-   opts.constraints = Key_Constraints::NO_CONSTRAINTS;
+   opts.constraints = Key_Constraints();
 
    const Botan::PKCS10_Request no_usage_req = Botan::X509::create_cert_req(opts, *user1_key, hash_fn, Test::rng());
 
@@ -1071,14 +1073,14 @@ Test::Result test_usage(const Botan::Private_Key& ca_key,
                       from_date(2, 01, 01));
 
    // cert allows every usage
-   result.confirm("key usage digitalSignature allowed", no_usage_cert.allowed_usage(Key_Constraints::DIGITAL_SIGNATURE));
-   result.confirm("key usage cRLSign allowed", no_usage_cert.allowed_usage(Key_Constraints::CRL_SIGN));
+   result.confirm("key usage digitalSignature allowed", no_usage_cert.allowed_usage(Key_Constraints::DigitalSignature));
+   result.confirm("key usage cRLSign allowed", no_usage_cert.allowed_usage(Key_Constraints::CrlSign));
    result.confirm("key usage encryption allowed", no_usage_cert.allowed_usage(Usage_Type::ENCRYPTION));
 
    if (sig_algo == "RSA")
       {
       // cert allows data encryption
-       opts.constraints = Key_Constraints(Key_Constraints::KEY_ENCIPHERMENT | Key_Constraints::DATA_ENCIPHERMENT);
+       opts.constraints = Key_Constraints(Key_Constraints::KeyEncipherment | Key_Constraints::DataEncipherment);
 
       const Botan::PKCS10_Request enc_req = Botan::X509::create_cert_req(
                opts,
@@ -1120,7 +1122,7 @@ Test::Result test_self_issued(const Botan::Private_Key& ca_key,
    // create a self-issued certificate, that is, a certificate with subject dn == issuer dn,
    // but signed by a CA, not signed by it's own private key
    Botan::X509_Cert_Options opts = ca_opts();
-   opts.constraints = Key_Constraints::DIGITAL_SIGNATURE;
+   opts.constraints = Key_Constraints::DigitalSignature;
    opts.set_padding_scheme(sig_padding);
 
    const Botan::PKCS10_Request self_issued_req = Botan::X509::create_cert_req(opts, *user_key, hash_fn, Test::rng());
@@ -1159,233 +1161,123 @@ Test::Result test_x509_uninit()
    }
 
 
-using Botan::Key_Constraints;
-
-/**
-* @brief Some typical key usage scenarios (taken from RFC 5280, sec. 4.2.1.3)
-*/
-struct typical_usage_constraints
-   {
-   // ALL constraints are not typical at all, but we use them for a negative test
-   Key_Constraints all = Key_Constraints(
-                            Key_Constraints::DIGITAL_SIGNATURE |
-                            Key_Constraints::NON_REPUDIATION |
-                            Key_Constraints::KEY_ENCIPHERMENT |
-                            Key_Constraints::DATA_ENCIPHERMENT |
-                            Key_Constraints::KEY_AGREEMENT |
-                            Key_Constraints::KEY_CERT_SIGN |
-                            Key_Constraints::CRL_SIGN |
-                            Key_Constraints::ENCIPHER_ONLY |
-                            Key_Constraints::DECIPHER_ONLY);
-
-   Key_Constraints ca = Key_Constraints(Key_Constraints::KEY_CERT_SIGN);
-   Key_Constraints sign_data = Key_Constraints(Key_Constraints::DIGITAL_SIGNATURE);
-   Key_Constraints non_repudiation = Key_Constraints(
-                                        Key_Constraints::NON_REPUDIATION |
-                                        Key_Constraints::DIGITAL_SIGNATURE);
-   Key_Constraints key_encipherment = Key_Constraints(Key_Constraints::KEY_ENCIPHERMENT);
-   Key_Constraints data_encipherment = Key_Constraints(Key_Constraints::DATA_ENCIPHERMENT);
-   Key_Constraints key_agreement = Key_Constraints(Key_Constraints::KEY_AGREEMENT);
-   Key_Constraints key_agreement_encipher_only = Key_Constraints(
-            Key_Constraints::KEY_AGREEMENT |
-            Key_Constraints::ENCIPHER_ONLY);
-   Key_Constraints key_agreement_decipher_only = Key_Constraints(
-            Key_Constraints::KEY_AGREEMENT |
-            Key_Constraints::DECIPHER_ONLY);
-   Key_Constraints crl_sign = Key_Constraints::CRL_SIGN;
-   Key_Constraints sign_everything = Key_Constraints(
-                                        Key_Constraints::DIGITAL_SIGNATURE |
-                                        Key_Constraints::KEY_CERT_SIGN |
-                                        Key_Constraints::CRL_SIGN);
-   };
-
-
 Test::Result test_valid_constraints(const Botan::Private_Key& key,
                                     const std::string& pk_algo)
    {
-   Test::Result result("X509 Valid Constraints");
+   using Botan::Key_Constraints;
 
-   // should not throw on empty constraints
-   verify_cert_constraints_valid_for_key_type(key, Key_Constraints(Key_Constraints::NO_CONSTRAINTS));
+   Test::Result result("X509 Valid Constraints " + pk_algo);
 
-   // now check some typical usage scenarios for the given key type
-   typical_usage_constraints typical_usage;
+   result.confirm("empty constraints always acceptable",
+                  Key_Constraints().compatible_with(key));
+
+   // Now check some typical usage scenarios for the given key type
+   // Taken from RFC 5280, sec. 4.2.1.3
+   // ALL constraints are not typical at all, but we use them for a negative test
+   const auto all = Key_Constraints(
+      Key_Constraints::DigitalSignature |
+      Key_Constraints::NonRepudiation |
+      Key_Constraints::KeyEncipherment |
+      Key_Constraints::DataEncipherment |
+      Key_Constraints::KeyAgreement |
+      Key_Constraints::KeyCertSign |
+      Key_Constraints::CrlSign |
+      Key_Constraints::EncipherOnly |
+      Key_Constraints::DecipherOnly);
+
+   const auto ca = Key_Constraints(Key_Constraints::KeyCertSign);
+   const auto sign_data = Key_Constraints(Key_Constraints::DigitalSignature);
+   const auto non_repudiation = Key_Constraints(
+      Key_Constraints::NonRepudiation |
+      Key_Constraints::DigitalSignature);
+   const auto key_encipherment = Key_Constraints(Key_Constraints::KeyEncipherment);
+   const auto data_encipherment = Key_Constraints(Key_Constraints::DataEncipherment);
+   const auto key_agreement = Key_Constraints(Key_Constraints::KeyAgreement);
+   const auto key_agreement_encipher_only = Key_Constraints(
+      Key_Constraints::KeyAgreement |
+      Key_Constraints::EncipherOnly);
+   const auto key_agreement_decipher_only = Key_Constraints(
+      Key_Constraints::KeyAgreement |
+      Key_Constraints::DecipherOnly);
+   const auto crl_sign = Key_Constraints(Key_Constraints::CrlSign);
+   const auto sign_everything = Key_Constraints(
+      Key_Constraints::DigitalSignature |
+      Key_Constraints::KeyCertSign |
+      Key_Constraints::CrlSign);
 
    if(pk_algo == "DH" || pk_algo == "ECDH")
       {
       // DH and ECDH only for key agreement
-      result.test_throws("all constraints not permitted", [&key, &typical_usage]()
-         {
-         verify_cert_constraints_valid_for_key_type(key, typical_usage.all);
-         });
-      result.test_throws("cert sign not permitted", [&key, &typical_usage]()
-         {
-         verify_cert_constraints_valid_for_key_type(key, typical_usage.ca);
-         });
-      result.test_throws("signature not permitted", [&key, &typical_usage]()
-         {
-         verify_cert_constraints_valid_for_key_type(key, typical_usage.sign_data);
-         });
-      result.test_throws("non repudiation not permitted", [&key, &typical_usage]()
-         {
-         verify_cert_constraints_valid_for_key_type(key, typical_usage.non_repudiation);
-         });
-      result.test_throws("key encipherment not permitted", [&key, &typical_usage]()
-         {
-         verify_cert_constraints_valid_for_key_type(key, typical_usage.key_encipherment);
-         });
-      result.test_throws("data encipherment not permitted", [&key, &typical_usage]()
-         {
-         verify_cert_constraints_valid_for_key_type(key, typical_usage.data_encipherment);
-         });
-
-      verify_cert_constraints_valid_for_key_type(key, typical_usage.key_agreement);
-      verify_cert_constraints_valid_for_key_type(key, typical_usage.key_agreement_encipher_only);
-      verify_cert_constraints_valid_for_key_type(key, typical_usage.key_agreement_decipher_only);
-
-      result.test_throws("crl sign not permitted", [&key, &typical_usage]()
-         {
-         verify_cert_constraints_valid_for_key_type(key, typical_usage.crl_sign);
-         });
-      result.test_throws("sign, cert sign, crl sign not permitted", [&key, &typical_usage]()
-         {
-         verify_cert_constraints_valid_for_key_type(key, typical_usage.sign_everything);
-         });
+      result.test_eq("all constraints not permitted", all.compatible_with(key), false);
+      result.test_eq("cert sign not permitted", ca.compatible_with(key), false);
+      result.test_eq("signature not permitted", sign_data.compatible_with(key), false);
+      result.test_eq("non repudiation not permitted", non_repudiation.compatible_with(key), false);
+      result.test_eq("key encipherment not permitted", key_encipherment.compatible_with(key), false);
+      result.test_eq("data encipherment not permitted", data_encipherment.compatible_with(key), false);
+      result.test_eq("usage acceptable", key_agreement.compatible_with(key), true);
+      result.test_eq("usage acceptable", key_agreement_encipher_only.compatible_with(key), true);
+      result.test_eq("usage acceptable", key_agreement_decipher_only.compatible_with(key), true);
+      result.test_eq("crl sign not permitted", crl_sign.compatible_with(key), false);
+      result.test_eq("sign", sign_everything.compatible_with(key), false);
       }
    else if(pk_algo == "Kyber")
       {
       // Kyber can encrypt and agree
-      result.test_throws("all constraints not permitted", [&key, &typical_usage]()
-         {
-         verify_cert_constraints_valid_for_key_type(key, typical_usage.all);
-         });
-      result.test_throws("cert sign not permitted", [&key, &typical_usage]()
-         {
-         verify_cert_constraints_valid_for_key_type(key, typical_usage.ca);
-         });
-      result.test_throws("signature not permitted", [&key, &typical_usage]()
-         {
-         verify_cert_constraints_valid_for_key_type(key, typical_usage.sign_data);
-         });
-      result.test_throws("non repudiation not permitted", [&key, &typical_usage]()
-         {
-         verify_cert_constraints_valid_for_key_type(key, typical_usage.non_repudiation);
-         });
-      result.test_throws("crl sign not permitted", [&key, &typical_usage]()
-         {
-         verify_cert_constraints_valid_for_key_type(key, typical_usage.crl_sign);
-         });
-      result.test_throws("sign, cert sign, crl sign not permitted", [&key, &typical_usage]()
-         {
-         verify_cert_constraints_valid_for_key_type(key, typical_usage.sign_everything);
-         });
-
-      verify_cert_constraints_valid_for_key_type(key, typical_usage.key_agreement);
-      verify_cert_constraints_valid_for_key_type(key, typical_usage.data_encipherment);
-      verify_cert_constraints_valid_for_key_type(key, typical_usage.key_encipherment);
+      result.test_eq("all constraints not permitted", all.compatible_with(key), false);
+      result.test_eq("cert sign not permitted", ca.compatible_with(key), false);
+      result.test_eq("signature not permitted", sign_data.compatible_with(key), false);
+      result.test_eq("non repudiation not permitted", non_repudiation.compatible_with(key), false);
+      result.test_eq("crl sign not permitted", crl_sign.compatible_with(key), false);
+      result.test_eq("sign", sign_everything.compatible_with(key), false);
+      result.test_eq("usage acceptable", key_agreement.compatible_with(key), true);
+      result.test_eq("usage acceptable", data_encipherment.compatible_with(key), true);
+      result.test_eq("usage acceptable", key_encipherment.compatible_with(key), true);
       }
    else if(pk_algo == "RSA")
       {
       // RSA can do everything except key agreement
-      result.test_throws("all constraints not permitted", [&key, &typical_usage]()
-         {
-         verify_cert_constraints_valid_for_key_type(key, typical_usage.all);
-         });
-
-      verify_cert_constraints_valid_for_key_type(key, typical_usage.ca);
-      verify_cert_constraints_valid_for_key_type(key, typical_usage.sign_data);
-      verify_cert_constraints_valid_for_key_type(key, typical_usage.non_repudiation);
-      verify_cert_constraints_valid_for_key_type(key, typical_usage.key_encipherment);
-      verify_cert_constraints_valid_for_key_type(key, typical_usage.data_encipherment);
-
-      result.test_throws("key agreement not permitted", [&key, &typical_usage]()
-         {
-         verify_cert_constraints_valid_for_key_type(key, typical_usage.key_agreement);
-         });
-      result.test_throws("key agreement, encipher only not permitted", [&key, &typical_usage]()
-         {
-         verify_cert_constraints_valid_for_key_type(key, typical_usage.key_agreement_encipher_only);
-         });
-      result.test_throws("key agreement, decipher only not permitted", [&key, &typical_usage]()
-         {
-         verify_cert_constraints_valid_for_key_type(key, typical_usage.key_agreement_decipher_only);
-         });
-
-      verify_cert_constraints_valid_for_key_type(key, typical_usage.crl_sign);
-      verify_cert_constraints_valid_for_key_type(key, typical_usage.sign_everything);
+      result.test_eq("all constraints not permitted", all.compatible_with(key), false);
+
+      result.test_eq("usage acceptable", ca.compatible_with(key), true);
+      result.test_eq("usage acceptable", sign_data.compatible_with(key), true);
+      result.test_eq("usage acceptable", non_repudiation.compatible_with(key), true);
+      result.test_eq("usage acceptable", key_encipherment.compatible_with(key), true);
+      result.test_eq("usage acceptable", data_encipherment.compatible_with(key), true);
+      result.test_eq("key agreement not permitted", key_agreement.compatible_with(key), false);
+      result.test_eq("key agreement", key_agreement_encipher_only.compatible_with(key), false);
+      result.test_eq("key agreement", key_agreement_decipher_only.compatible_with(key), false);
+      result.test_eq("usage acceptable", crl_sign.compatible_with(key), true);
+      result.test_eq("usage acceptable", sign_everything.compatible_with(key), true);
       }
    else if(pk_algo == "ElGamal")
       {
       // only ElGamal encryption is currently implemented
-      result.test_throws("all constraints not permitted", [&key, &typical_usage]()
-         {
-         verify_cert_constraints_valid_for_key_type(key, typical_usage.all);
-         });
-      result.test_throws("cert sign not permitted", [&key, &typical_usage]()
-         {
-         verify_cert_constraints_valid_for_key_type(key, typical_usage.ca);
-         });
-
-      verify_cert_constraints_valid_for_key_type(key, typical_usage.data_encipherment);
-      verify_cert_constraints_valid_for_key_type(key, typical_usage.key_encipherment);
-
-      result.test_throws("key agreement not permitted", [&key, &typical_usage]()
-         {
-         verify_cert_constraints_valid_for_key_type(key, typical_usage.key_agreement);
-         });
-      result.test_throws("key agreement, encipher only not permitted", [&key, &typical_usage]()
-         {
-         verify_cert_constraints_valid_for_key_type(key, typical_usage.key_agreement_encipher_only);
-         });
-      result.test_throws("key agreement, decipher only not permitted", [&key, &typical_usage]()
-         {
-         verify_cert_constraints_valid_for_key_type(key, typical_usage.key_agreement_decipher_only);
-         });
-      result.test_throws("crl sign not permitted", [&key, &typical_usage]()
-         {
-         verify_cert_constraints_valid_for_key_type(key, typical_usage.crl_sign);
-         });
-      result.test_throws("sign, cert sign, crl sign not permitted not permitted", [&key, &typical_usage]()
-         {
-         verify_cert_constraints_valid_for_key_type(key, typical_usage.sign_everything);
-         });
+      result.test_eq("all constraints not permitted", all.compatible_with(key), false);
+      result.test_eq("cert sign not permitted", ca.compatible_with(key), false);
+      result.test_eq("data encipherment permitted", data_encipherment.compatible_with(key), true);
+      result.test_eq("key encipherment permitted", key_encipherment.compatible_with(key), true);
+      result.test_eq("key agreement not permitted", key_agreement.compatible_with(key), false);
+      result.test_eq("key agreement", key_agreement_encipher_only.compatible_with(key), false);
+      result.test_eq("key agreement", key_agreement_decipher_only.compatible_with(key), false);
+      result.test_eq("crl sign not permitted", crl_sign.compatible_with(key), false);
+      result.test_eq("sign", sign_everything.compatible_with(key), false);
       }
    else if(pk_algo == "DSA" || pk_algo == "ECDSA" || pk_algo == "ECGDSA" || pk_algo == "ECKCDSA" ||
            pk_algo == "GOST-34.10" || pk_algo == "Dilithium")
       {
       // these are signature algorithms only
-      result.test_throws("all constraints not permitted", [&key, &typical_usage]()
-         {
-         verify_cert_constraints_valid_for_key_type(key, typical_usage.all);
-         });
-
-      verify_cert_constraints_valid_for_key_type(key, typical_usage.ca);
-      verify_cert_constraints_valid_for_key_type(key, typical_usage.sign_data);
-      verify_cert_constraints_valid_for_key_type(key, typical_usage.non_repudiation);
-
-      result.test_throws("key encipherment not permitted", [&key, &typical_usage]()
-         {
-         verify_cert_constraints_valid_for_key_type(key, typical_usage.key_encipherment);
-         });
-      result.test_throws("data encipherment not permitted", [&key, &typical_usage]()
-         {
-         verify_cert_constraints_valid_for_key_type(key, typical_usage.data_encipherment);
-         });
-      result.test_throws("key agreement not permitted", [&key, &typical_usage]()
-         {
-         verify_cert_constraints_valid_for_key_type(key, typical_usage.key_agreement);
-         });
-      result.test_throws("key agreement, encipher only not permitted", [&key, &typical_usage]()
-         {
-         verify_cert_constraints_valid_for_key_type(key, typical_usage.key_agreement_encipher_only);
-         });
-      result.test_throws("key agreement, decipher only not permitted", [&key, &typical_usage]()
-         {
-         verify_cert_constraints_valid_for_key_type(key, typical_usage.key_agreement_decipher_only);
-         });
-
-      verify_cert_constraints_valid_for_key_type(key, typical_usage.crl_sign);
-      verify_cert_constraints_valid_for_key_type(key, typical_usage.sign_everything);
+      result.test_eq("all constraints not permitted", all.compatible_with(key), false);
+
+      result.test_eq("ca allowed", ca.compatible_with(key), true);
+      result.test_eq("sign allowed", sign_data.compatible_with(key), true);
+      result.test_eq("non-repudiation allowed", non_repudiation.compatible_with(key), true);
+      result.test_eq("key encipherment not permitted", key_encipherment.compatible_with(key), false);
+      result.test_eq("data encipherment not permitted", data_encipherment.compatible_with(key), false);
+      result.test_eq("key agreement not permitted", key_agreement.compatible_with(key), false);
+      result.test_eq("key agreement", key_agreement_encipher_only.compatible_with(key), false);
+      result.test_eq("key agreement", key_agreement_decipher_only.compatible_with(key), false);
+      result.test_eq("crl sign allowed", crl_sign.compatible_with(key), true);
+      result.test_eq("sign allowed", sign_everything.compatible_with(key), true);
       }
 
    return result;
@@ -1529,7 +1421,7 @@ Test::Result test_x509_extensions(const Botan::Private_Key& ca_key,
    std::unique_ptr<Botan::Private_Key> user_key(make_a_private_key(sig_algo));
 
    Botan::X509_Cert_Options opts("Test User 1/US/Botan Project/Testing");
-   opts.constraints = Key_Constraints::DIGITAL_SIGNATURE;
+   opts.constraints = Key_Constraints::DigitalSignature;
 
    // include a custom extension in the request
    Botan::Extensions req_extensions;
@@ -1575,8 +1467,9 @@ Test::Result test_x509_extensions(const Botan::Private_Key& ca_key,
    key_usage_ext = ca_signed_cert.v3_extensions().get(ku_oid);
    if(result.confirm("Key_Usage extension present in CA-signed certificate", key_usage_ext != nullptr))
       {
+      auto constraints = dynamic_cast<Botan::Cert_Extension::Key_Usage&>(*key_usage_ext).get_constraints();
       result.confirm("Key_Usage extension value matches in user certificate",
-                     dynamic_cast<Botan::Cert_Extension::Key_Usage&>(*key_usage_ext).get_constraints() == Botan::DIGITAL_SIGNATURE);
+                     constraints == Botan::Key_Constraints::DigitalSignature);
       }
 
    // check if custom extension is present in CA-signed cert