Repository: randombit/botan
Pull request #3326 (2023-02-25 21:05:05)

Add Asymmetric_Key::supports_operation
This allows removing algorithm-specific dispatch logic from key_constraint.cpp

One side effect here is we no longer accept key agreement usage bits for Kyber. I remember we were on the fence on if this should be allowed or not. (cc @reneme)


Patch:
diff --git a/src/lib/prov/pkcs11/p11_ecdh.h b/src/lib/prov/pkcs11/p11_ecdh.h
index 63a4d4640..95de6552f 100644
--- a/src/lib/prov/pkcs11/p11_ecdh.h
+++ b/src/lib/prov/pkcs11/p11_ecdh.h
@@ -50,6 +50,11 @@ class BOTAN_PUBLIC_API(2,0) PKCS11_ECDH_PublicKey : public PKCS11_EC_PublicKey
          return "ECDH";
          }
 
+      bool supports_operation(PublicKeyOperation op) const override
+         {
+         return (op == PublicKeyOperation::KeyAgreement);
+         }
+
       /// @return the exported ECDH public key
       ECDH_PublicKey export_key() const;
    };
@@ -105,6 +110,11 @@ class BOTAN_PUBLIC_API(2,0) PKCS11_ECDH_PrivateKey final : public virtual PKCS11
 
       secure_vector<uint8_t> private_key_bits() const override;
 
+      bool supports_operation(PublicKeyOperation op) const override
+         {
+         return (op == PublicKeyOperation::KeyAgreement);
+         }
+
       std::unique_ptr<PK_Ops::Key_Agreement>
          create_key_agreement_op(RandomNumberGenerator& rng,
                                  const std::string& params,
diff --git a/src/lib/prov/pkcs11/p11_ecdsa.h b/src/lib/prov/pkcs11/p11_ecdsa.h
index ccf07a513..057e1dfc0 100644
--- a/src/lib/prov/pkcs11/p11_ecdsa.h
+++ b/src/lib/prov/pkcs11/p11_ecdsa.h
@@ -97,6 +97,11 @@ class BOTAN_PUBLIC_API(2,0) PKCS11_ECDSA_PrivateKey final : public PKCS11_EC_Pri
          return "ECDSA";
          }
 
+      bool supports_operation(PublicKeyOperation op) const override
+         {
+         return (op == PublicKeyOperation::Signature);
+         }
+
       size_t message_parts() const override { return 2; }
 
       size_t message_part_size() const override
diff --git a/src/lib/prov/tpm/tpm.h b/src/lib/prov/tpm/tpm.h
index d5fe929c3..daa782f74 100644
--- a/src/lib/prov/tpm/tpm.h
+++ b/src/lib/prov/tpm/tpm.h
@@ -169,6 +169,11 @@ class BOTAN_PUBLIC_API(2,0) TPM_PrivateKey final : public Private_Key
 
       std::string algo_name() const override { return "RSA"; } // ???
 
+      bool supports_operation(PublicKeyOperation op) const override
+         {
+         return (op == PublicKeyOperation::Signature);
+         }
+
       std::unique_ptr<PK_Ops::Signature>
          create_signature_op(RandomNumberGenerator& rng,
                              const std::string& params,
diff --git a/src/lib/pubkey/curve25519/curve25519.h b/src/lib/pubkey/curve25519/curve25519.h
index 99860630e..c36955d72 100644
--- a/src/lib/pubkey/curve25519/curve25519.h
+++ b/src/lib/pubkey/curve25519/curve25519.h
@@ -29,6 +29,11 @@ class BOTAN_PUBLIC_API(2,0) Curve25519_PublicKey : public virtual Public_Key
 
       std::vector<uint8_t> public_value() const { return m_public; }
 
+      bool supports_operation(PublicKeyOperation op) const override
+         {
+         return (op == PublicKeyOperation::KeyAgreement);
+         }
+
       /**
       * Create a Curve25519 Public Key.
       * @param alg_id the X.509 algorithm identifier
diff --git a/src/lib/pubkey/dh/dh.h b/src/lib/pubkey/dh/dh.h
index e4c9c937d..b2be40ab1 100644
--- a/src/lib/pubkey/dh/dh.h
+++ b/src/lib/pubkey/dh/dh.h
@@ -24,6 +24,11 @@ class BOTAN_PUBLIC_API(2,0) DH_PublicKey : public virtual DL_Scheme_PublicKey
 
       DL_Group_Format group_format() const override { return DL_Group_Format::ANSI_X9_42; }
 
+      bool supports_operation(PublicKeyOperation op) const override
+         {
+         return (op == PublicKeyOperation::KeyAgreement);
+         }
+
       /**
       * Create a public key.
       * @param alg_id the X.509 algorithm identifier
diff --git a/src/lib/pubkey/dilithium/dilithium_common/dilithium.h b/src/lib/pubkey/dilithium/dilithium_common/dilithium.h
index bc3ddc014..9d7b444c5 100644
--- a/src/lib/pubkey/dilithium/dilithium_common/dilithium.h
+++ b/src/lib/pubkey/dilithium/dilithium_common/dilithium.h
@@ -85,6 +85,11 @@ class BOTAN_PUBLIC_API(3, 0) Dilithium_PublicKey : public virtual Public_Key
 
       bool check_key(RandomNumberGenerator&, bool) const override;
 
+      bool supports_operation(PublicKeyOperation op) const override
+         {
+         return (op == PublicKeyOperation::Signature);
+         }
+
       std::unique_ptr<PK_Ops::Verification>
       create_verification_op(const std::string& params,
                              const std::string& provider) const override;
diff --git a/src/lib/pubkey/dsa/dsa.h b/src/lib/pubkey/dsa/dsa.h
index f710a8094..7a6f625d8 100644
--- a/src/lib/pubkey/dsa/dsa.h
+++ b/src/lib/pubkey/dsa/dsa.h
@@ -24,6 +24,11 @@ class BOTAN_PUBLIC_API(2,0) DSA_PublicKey : public virtual DL_Scheme_PublicKey
       size_t message_parts() const override { return 2; }
       size_t message_part_size() const override { return group_q().bytes(); }
 
+      bool supports_operation(PublicKeyOperation op) const override
+         {
+         return (op == PublicKeyOperation::Signature);
+         }
+
       /**
       * Load a public key.
       * @param alg_id the X.509 algorithm identifier
diff --git a/src/lib/pubkey/ecdh/ecdh.h b/src/lib/pubkey/ecdh/ecdh.h
index d4fff33f3..55ee88852 100644
--- a/src/lib/pubkey/ecdh/ecdh.h
+++ b/src/lib/pubkey/ecdh/ecdh.h
@@ -56,6 +56,11 @@ class BOTAN_PUBLIC_API(2,0) ECDH_PublicKey : public virtual EC_PublicKey
       std::vector<uint8_t> public_value(EC_Point_Format format) const
          { return public_point().encode(format); }
 
+      bool supports_operation(PublicKeyOperation op) const override
+         {
+         return (op == PublicKeyOperation::KeyAgreement);
+         }
+
    protected:
       ECDH_PublicKey() = default;
    };
diff --git a/src/lib/pubkey/ecdsa/ecdsa.h b/src/lib/pubkey/ecdsa/ecdsa.h
index c5eeb0704..d3f9f01d3 100644
--- a/src/lib/pubkey/ecdsa/ecdsa.h
+++ b/src/lib/pubkey/ecdsa/ecdsa.h
@@ -65,6 +65,11 @@ class BOTAN_PUBLIC_API(2,0) ECDSA_PublicKey : public virtual EC_PublicKey
       size_t message_part_size() const override
          { return domain().get_order().bytes(); }
 
+      bool supports_operation(PublicKeyOperation op) const override
+         {
+         return (op == PublicKeyOperation::Signature);
+         }
+
       uint8_t recovery_param(const std::vector<uint8_t>& msg,
                              const BigInt& r,
                              const BigInt& s) const;
diff --git a/src/lib/pubkey/ecgdsa/ecgdsa.h b/src/lib/pubkey/ecgdsa/ecgdsa.h
index 98eb80ab9..cc387c8ed 100644
--- a/src/lib/pubkey/ecgdsa/ecgdsa.h
+++ b/src/lib/pubkey/ecgdsa/ecgdsa.h
@@ -48,6 +48,11 @@ class BOTAN_PUBLIC_API(2,0) ECGDSA_PublicKey : public virtual EC_PublicKey
       size_t message_part_size() const override
          { return domain().get_order().bytes(); }
 
+      bool supports_operation(PublicKeyOperation op) const override
+         {
+         return (op == PublicKeyOperation::Signature);
+         }
+
       std::unique_ptr<PK_Ops::Verification>
          create_verification_op(const std::string& params,
                                 const std::string& provider) const override;
diff --git a/src/lib/pubkey/ecies/ecies.cpp b/src/lib/pubkey/ecies/ecies.cpp
index eeb8f1a9b..7cfbf4747 100644
--- a/src/lib/pubkey/ecies/ecies.cpp
+++ b/src/lib/pubkey/ecies/ecies.cpp
@@ -45,6 +45,11 @@ class ECIES_PrivateKey final : public EC_PrivateKey, public PK_Key_Agreement_Key
          return m_key.public_key();
          }
 
+      bool supports_operation(PublicKeyOperation op) const override
+         {
+         return (op == PublicKeyOperation::KeyAgreement);
+         }
+
       std::unique_ptr<PK_Ops::Key_Agreement>
          create_key_agreement_op(RandomNumberGenerator& rng,
                                  const std::string& params,
diff --git a/src/lib/pubkey/eckcdsa/eckcdsa.h b/src/lib/pubkey/eckcdsa/eckcdsa.h
index f731567cd..48038c4f2 100644
--- a/src/lib/pubkey/eckcdsa/eckcdsa.h
+++ b/src/lib/pubkey/eckcdsa/eckcdsa.h
@@ -47,6 +47,11 @@ class BOTAN_PUBLIC_API(2,0) ECKCDSA_PublicKey : public virtual EC_PublicKey
       size_t message_part_size() const override
          { return domain().get_order().bytes(); }
 
+      bool supports_operation(PublicKeyOperation op) const override
+         {
+         return (op == PublicKeyOperation::Signature);
+         }
+
       std::unique_ptr<PK_Ops::Verification>
          create_verification_op(const std::string& params,
                                 const std::string& provider) const override;
diff --git a/src/lib/pubkey/ed25519/ed25519.h b/src/lib/pubkey/ed25519/ed25519.h
index 65bee1353..7d2cff7b4 100644
--- a/src/lib/pubkey/ed25519/ed25519.h
+++ b/src/lib/pubkey/ed25519/ed25519.h
@@ -30,6 +30,11 @@ class BOTAN_PUBLIC_API(2,2) Ed25519_PublicKey : public virtual Public_Key
 
       std::vector<uint8_t> public_key_bits() const override;
 
+      bool supports_operation(PublicKeyOperation op) const override
+         {
+         return (op == PublicKeyOperation::Signature);
+         }
+
       /**
       * Create a Ed25519 Public Key.
       * @param alg_id the X.509 algorithm identifier
diff --git a/src/lib/pubkey/elgamal/elgamal.h b/src/lib/pubkey/elgamal/elgamal.h
index a316d9c50..430ff3591 100644
--- a/src/lib/pubkey/elgamal/elgamal.h
+++ b/src/lib/pubkey/elgamal/elgamal.h
@@ -21,6 +21,11 @@ class BOTAN_PUBLIC_API(2,0) ElGamal_PublicKey : public virtual DL_Scheme_PublicK
       std::string algo_name() const override { return "ElGamal"; }
       DL_Group_Format group_format() const override { return DL_Group_Format::ANSI_X9_42; }
 
+      bool supports_operation(PublicKeyOperation op) const override
+         {
+         return (op == PublicKeyOperation::Encryption);
+         }
+
       /**
       * Load a public key.
       * @param alg_id the X.509 algorithm identifier
diff --git a/src/lib/pubkey/gost_3410/gost_3410.h b/src/lib/pubkey/gost_3410/gost_3410.h
index 0e3b8ce47..7c39a9480 100644
--- a/src/lib/pubkey/gost_3410/gost_3410.h
+++ b/src/lib/pubkey/gost_3410/gost_3410.h
@@ -56,6 +56,11 @@ class BOTAN_PUBLIC_API(2,0) GOST_3410_PublicKey : public virtual EC_PublicKey
       Signature_Format default_x509_signature_format() const override
          { return Signature_Format::Standard; }
 
+      bool supports_operation(PublicKeyOperation op) const override
+         {
+         return (op == PublicKeyOperation::Signature);
+         }
+
       std::unique_ptr<PK_Ops::Verification>
          create_verification_op(const std::string& params,
                                 const std::string& provider) const override;
diff --git a/src/lib/pubkey/kyber/kyber_common/kyber.h b/src/lib/pubkey/kyber/kyber_common/kyber.h
index 11cbc1b4e..893a890df 100644
--- a/src/lib/pubkey/kyber/kyber_common/kyber.h
+++ b/src/lib/pubkey/kyber/kyber_common/kyber.h
@@ -95,6 +95,11 @@ class BOTAN_PUBLIC_API(3, 0) Kyber_PublicKey : public virtual Public_Key
 
       bool check_key(RandomNumberGenerator&, bool) const override;
 
+      bool supports_operation(PublicKeyOperation op) const override
+         {
+         return (op == PublicKeyOperation::KeyEncapsulation);
+         }
+
       std::unique_ptr<PK_Ops::KEM_Encryption> create_kem_encryption_op(RandomNumberGenerator& rng,
             const std::string& params,
             const std::string& provider) const override;
diff --git a/src/lib/pubkey/mce/mceliece.h b/src/lib/pubkey/mce/mceliece.h
index cb023a228..a79e0f5e7 100644
--- a/src/lib/pubkey/mce/mceliece.h
+++ b/src/lib/pubkey/mce/mceliece.h
@@ -56,6 +56,11 @@ class BOTAN_PUBLIC_API(2,0) McEliece_PublicKey : public virtual Public_Key
       bool operator==(const McEliece_PublicKey& other) const;
       bool operator!=(const McEliece_PublicKey& other) const { return !(*this == other); }
 
+      bool supports_operation(PublicKeyOperation op) const override
+         {
+         return (op == PublicKeyOperation::KeyEncapsulation);
+         }
+
       std::unique_ptr<PK_Ops::KEM_Encryption>
          create_kem_encryption_op(RandomNumberGenerator& rng,
                                   const std::string& params,
diff --git a/src/lib/pubkey/pk_keys.h b/src/lib/pubkey/pk_keys.h
index f5ca35325..ddba4f070 100644
--- a/src/lib/pubkey/pk_keys.h
+++ b/src/lib/pubkey/pk_keys.h
@@ -19,7 +19,10 @@ class BigInt;
 class RandomNumberGenerator;
 
 /**
-* The two types of signature format supported by Botan.
+* Enumeration specifying the signature format.
+*
+* This is mostly used for requesting DER encoding of ECDSA signatures;
+* most other algorithms only support "standard".
 */
 enum class Signature_Format {
    Standard,
@@ -29,6 +32,19 @@ enum class Signature_Format {
    DER_SEQUENCE BOTAN_DEPRECATED("Use DerSequence") = DerSequence,
 };
 
+/**
+* Enumeration of possible operations a public key could be used for.
+*
+* It is possible to query if a key supports a particular operation
+* type using Asymmetric_Key::supports_operation()
+*/
+enum class PublicKeyOperation {
+   Encryption,
+   Signature,
+   KeyEncapsulation,
+   KeyAgreement,
+};
+
 /**
 * An interface for objects that are keys in public key algorithms
 *
@@ -76,6 +92,12 @@ class BOTAN_PUBLIC_API(3,0) Asymmetric_Key
       * and botan_privkey_get_field functions.
       */
       virtual const BigInt& get_int_field(const std::string& field) const;
+
+      /**
+      * Return true if this key could be used for the specified type
+      * of operation.
+      */
+      virtual bool supports_operation(PublicKeyOperation op) const = 0;
    };
 
 /*
diff --git a/src/lib/pubkey/rsa/rsa.cpp b/src/lib/pubkey/rsa/rsa.cpp
index 1305fd282..75f0550ab 100644
--- a/src/lib/pubkey/rsa/rsa.cpp
+++ b/src/lib/pubkey/rsa/rsa.cpp
@@ -136,6 +136,13 @@ RSA_PublicKey::RSA_PublicKey(const AlgorithmIdentifier& /*unused*/,
    init(std::move(n), std::move(e));
    }
 
+bool RSA_PublicKey::supports_operation(PublicKeyOperation op) const
+   {
+   return op == PublicKeyOperation::Signature ||
+      op == PublicKeyOperation::Encryption ||
+      op == PublicKeyOperation::KeyEncapsulation;
+   }
+
 RSA_PublicKey::RSA_PublicKey(const BigInt& modulus, const BigInt& exponent)
    {
    BigInt n = modulus;
diff --git a/src/lib/pubkey/rsa/rsa.h b/src/lib/pubkey/rsa/rsa.h
index 9a9ed92b1..64cacc704 100644
--- a/src/lib/pubkey/rsa/rsa.h
+++ b/src/lib/pubkey/rsa/rsa.h
@@ -63,6 +63,8 @@ class BOTAN_PUBLIC_API(2,0) RSA_PublicKey : public virtual Public_Key
 
       const BigInt& get_int_field(const std::string& field) const override;
 
+      bool supports_operation(PublicKeyOperation op) const override;
+
       // internal functions:
       std::shared_ptr<const RSA_Public_Data> public_data() const;
 
diff --git a/src/lib/pubkey/sm2/sm2.h b/src/lib/pubkey/sm2/sm2.h
index 937f675e2..f8c2ab7b4 100644
--- a/src/lib/pubkey/sm2/sm2.h
+++ b/src/lib/pubkey/sm2/sm2.h
@@ -45,6 +45,12 @@ class BOTAN_PUBLIC_API(2,2) SM2_PublicKey : public virtual EC_PublicKey
 
       size_t message_parts() const override { return 2; }
 
+      bool supports_operation(PublicKeyOperation op) const override
+         {
+         return (op == PublicKeyOperation::Signature ||
+                 op == PublicKeyOperation::Encryption);
+         }
+
       size_t message_part_size() const override
          { return domain().get_order().bytes(); }
 
diff --git a/src/lib/pubkey/xmss/xmss.h b/src/lib/pubkey/xmss/xmss.h
index 934ede2df..5ffd9fe6d 100644
--- a/src/lib/pubkey/xmss/xmss.h
+++ b/src/lib/pubkey/xmss/xmss.h
@@ -119,6 +119,11 @@ class BOTAN_PUBLIC_API(2,0) XMSS_PublicKey : public virtual Public_Key
        **/
       std::vector<uint8_t> raw_public_key() const;
 
+      bool supports_operation(PublicKeyOperation op) const override
+         {
+         return (op == PublicKeyOperation::Signature);
+         }
+
    protected:
       friend class XMSS_Verification_Operation;
 
diff --git a/src/lib/x509/key_constraint.cpp b/src/lib/x509/key_constraint.cpp
index 9bcc140b7..52199fbfd 100644
--- a/src/lib/x509/key_constraint.cpp
+++ b/src/lib/x509/key_constraint.cpp
@@ -58,33 +58,23 @@ std::string Key_Constraints::to_string() const
 */
 bool Key_Constraints::compatible_with(const Public_Key& pub_key) const
    {
-   const std::string name = pub_key.algo_name();
-
-   const bool can_agree = (name == "DH" || name == "ECDH" || name.starts_with("Kyber-"));
-   const bool can_encrypt = (name == "RSA" || name == "ElGamal" || name.starts_with("Kyber-"));
-
-   const bool can_sign =
-      (name == "RSA" || name == "DSA" ||
-       name == "ECDSA" || name == "ECGDSA" || name == "ECKCDSA" || name == "Ed25519" ||
-       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)
+   if(pub_key.supports_operation(PublicKeyOperation::KeyAgreement))
       {
       permitted |= Key_Constraints::KeyAgreement |
          Key_Constraints::EncipherOnly |
          Key_Constraints::DecipherOnly;
       }
 
-   if(can_encrypt)
+   if(pub_key.supports_operation(PublicKeyOperation::Encryption) ||
+      pub_key.supports_operation(PublicKeyOperation::KeyEncapsulation))
       {
       permitted |= Key_Constraints::KeyEncipherment |
          Key_Constraints::DataEncipherment;
       }
 
-   if(can_sign)
+   if(pub_key.supports_operation(PublicKeyOperation::Signature))
       {
       permitted |= Key_Constraints::DigitalSignature |
          Key_Constraints::NonRepudiation |


Test patch:
diff --git a/src/tests/test_pubkey.cpp b/src/tests/test_pubkey.cpp
index 33656d88e..ddf08c238 100644
--- a/src/tests/test_pubkey.cpp
+++ b/src/tests/test_pubkey.cpp
@@ -126,8 +126,14 @@ PK_Signature_Generation_Test::run_one_test(const std::string& pad_hdr, const Var
       return result;
       }
 
+   result.confirm("private key claims to support signatures",
+                  privkey->supports_operation(Botan::PublicKeyOperation::Signature));
+
    std::unique_ptr<Botan::Public_Key> pubkey(Botan::X509::load_key(Botan::X509::BER_encode(*privkey)));
 
+   result.confirm("public key claims to support signatures",
+                  privkey->supports_operation(Botan::PublicKeyOperation::Signature));
+
    std::vector<std::unique_ptr<Botan::PK_Verifier>> verifiers;
 
    for(auto const& verify_provider : possible_providers(algo_name()))
@@ -218,6 +224,9 @@ PK_Signature_Verification_Test::run_one_test(const std::string& pad_hdr, const V
 
    Test::Result result(algo_name() + "/" + padding + " signature verification");
 
+   result.confirm("public key claims to support signatures",
+                  pubkey->supports_operation(Botan::PublicKeyOperation::Signature));
+
    for(auto const& verify_provider : possible_providers(algo_name()))
       {
       std::unique_ptr<Botan::PK_Verifier> verifier;
@@ -358,6 +367,9 @@ PK_Encryption_Decryption_Test::run_one_test(const std::string& pad_hdr, const Va
 
    std::unique_ptr<Botan::Private_Key> privkey = load_private_key(vars);
 
+   result.confirm("private key claims to support encryption",
+                  privkey->supports_operation(Botan::PublicKeyOperation::Encryption));
+
    // instead slice the private key to work around elgamal test inputs
    //std::unique_ptr<Botan::Public_Key> pubkey(Botan::X509::load_key(Botan::X509::BER_encode(*privkey)));
    Botan::Public_Key* pubkey = privkey.get();
@@ -508,6 +520,9 @@ Test::Result PK_KEM_Test::run_one_test(const std::string& /*header*/, const VarM
 
    std::unique_ptr<Botan::Private_Key> privkey = load_private_key(vars);
 
+   result.confirm("private key claims to support KEM",
+                  privkey->supports_operation(Botan::PublicKeyOperation::KeyEncapsulation));
+
    const Botan::Public_Key& pubkey = *privkey;
 
    const size_t desired_key_len = K.size();
@@ -567,6 +582,10 @@ Test::Result PK_Key_Agreement_Test::run_one_test(const std::string& header, cons
                        " key agreement");
 
    std::unique_ptr<Botan::Private_Key> privkey = load_our_key(header, vars);
+
+   result.confirm("private key claims to support key agreement",
+                  privkey->supports_operation(Botan::PublicKeyOperation::KeyAgreement));
+
    const std::vector<uint8_t> pubkey = load_their_key(header, vars);
 
    const size_t key_len = vars.get_opt_sz("OutLen", 0);
diff --git a/src/tests/unit_x509.cpp b/src/tests/unit_x509.cpp
index e1b2f80e1..5e21f6330 100644
--- a/src/tests/unit_x509.cpp
+++ b/src/tests/unit_x509.cpp
@@ -1218,7 +1218,7 @@ Test::Result test_valid_constraints(const Botan::Private_Key& key,
       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("key agreement not permitted", key_agreement.compatible_with(key), false);
       result.test_eq("usage acceptable", data_encipherment.compatible_with(key), true);
       result.test_eq("usage acceptable", key_encipherment.compatible_with(key), true);
       }