Repository: randombit/botan
Pull request #3665 (2023-08-09 21:00:00)

Extend EC_Group to track when a group used the explicit encoding
GH #3630 #3664


Patch:
diff --git a/src/lib/ffi/ffi.cpp b/src/lib/ffi/ffi.cpp
index 30d3a4c65..35f001cb3 100644
--- a/src/lib/ffi/ffi.cpp
+++ b/src/lib/ffi/ffi.cpp
@@ -220,6 +220,11 @@ uint32_t botan_ffi_api_version() {
 }
 
 int botan_ffi_supports_api(uint32_t api_version) {
+   // This is the API introduced in 3.2
+   if(api_version == 20231009) {
+      return BOTAN_FFI_SUCCESS;
+   }
+
    // This is the API introduced in 3.1
    if(api_version == 20230711) {
       return BOTAN_FFI_SUCCESS;
diff --git a/src/lib/ffi/ffi.h b/src/lib/ffi/ffi.h
index d67c66c28..02db89828 100644
--- a/src/lib/ffi/ffi.h
+++ b/src/lib/ffi/ffi.h
@@ -1445,6 +1445,9 @@ int botan_pubkey_view_kyber_raw_key(botan_pubkey_t key, botan_view_ctx ctx, bota
 /*
 * Algorithm specific key operations: ECDSA and ECDH
 */
+BOTAN_FFI_EXPORT(3, 2)
+int botan_pubkey_ecc_key_used_explicit_encoding(botan_pubkey_t key);
+
 BOTAN_FFI_EXPORT(2, 2)
 int botan_privkey_load_ecdsa(botan_privkey_t* key, botan_mp_t scalar, const char* curve_name);
 
diff --git a/src/lib/ffi/ffi_pkey_algs.cpp b/src/lib/ffi/ffi_pkey_algs.cpp
index e752a4b7a..b94953fe9 100644
--- a/src/lib/ffi/ffi_pkey_algs.cpp
+++ b/src/lib/ffi/ffi_pkey_algs.cpp
@@ -355,6 +355,24 @@ int botan_privkey_create_ecdsa(botan_privkey_t* key_obj, botan_rng_t rng_obj, co
 
 /* ECDSA specific operations */
 
+int botan_pubkey_ecc_key_used_explicit_encoding(botan_pubkey_t key) {
+#if defined(BOTAN_HAS_ECC_KEY)
+   return ffi_guard_thunk(__func__, [=]() -> int {
+      const Botan::Public_Key& pub_key = safe_get(key);
+      const Botan::EC_PublicKey* ec_key = dynamic_cast<const Botan::EC_PublicKey*>(&pub_key);
+
+      if(ec_key == nullptr) {
+         return BOTAN_FFI_ERROR_BAD_PARAMETER;
+      }
+
+      return ec_key->domain().used_explicit_encoding() ? 1 : 0;
+   });
+#else
+   BOTAN_UNUSED(key);
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
 int botan_pubkey_load_ecdsa(botan_pubkey_t* key,
                             const botan_mp_t public_x,
                             const botan_mp_t public_y,
diff --git a/src/lib/ffi/info.txt b/src/lib/ffi/info.txt
index 8f7fbac33..aac802892 100644
--- a/src/lib/ffi/info.txt
+++ b/src/lib/ffi/info.txt
@@ -1,5 +1,5 @@
 <defines>
-FFI -> 20230711
+FFI -> 20231009
 </defines>
 
 <module_info>
diff --git a/src/lib/pubkey/ec_group/ec_group.cpp b/src/lib/pubkey/ec_group/ec_group.cpp
index 46ab13c33..eb4ed90e2 100644
--- a/src/lib/pubkey/ec_group/ec_group.cpp
+++ b/src/lib/pubkey/ec_group/ec_group.cpp
@@ -322,17 +322,19 @@ std::shared_ptr<EC_Group_Data> EC_Group::load_EC_group_info(const char* p_str,
 }
 
 //static
-std::shared_ptr<EC_Group_Data> EC_Group::BER_decode_EC_group(const uint8_t bits[], size_t len, EC_Group_Source source) {
+std::pair<std::shared_ptr<EC_Group_Data>, bool> EC_Group::BER_decode_EC_group(const uint8_t bits[],
+                                                                              size_t len,
+                                                                              EC_Group_Source source) {
    BER_Decoder ber(bits, len);
    BER_Object obj = ber.get_next_object();
 
-   if(obj.type() == ASN1_Type::Null) [[unlikely]] {
-      throw Decoding_Error("Cannot handle ImplicitCA ECC parameters");
-   } else if(obj.type() == ASN1_Type::ObjectId) {
+   if(obj.type() == ASN1_Type::ObjectId) {
       OID dom_par_oid;
       BER_Decoder(bits, len).decode(dom_par_oid);
-      return ec_group_data().lookup(dom_par_oid);
-   } else if(obj.type() == ASN1_Type::Sequence) {
+      return std::make_pair(ec_group_data().lookup(dom_par_oid), false);
+   }
+
+   if(obj.type() == ASN1_Type::Sequence) {
       BigInt p, a, b, order, cofactor;
       std::vector<uint8_t> base_pt;
       std::vector<uint8_t> seed;
@@ -377,9 +379,15 @@ std::shared_ptr<EC_Group_Data> EC_Group::BER_decode_EC_group(const uint8_t bits[
 
       std::pair<BigInt, BigInt> base_xy = Botan::OS2ECP(base_pt.data(), base_pt.size(), p, a, b);
 
-      return ec_group_data().lookup_or_create(p, a, b, base_xy.first, base_xy.second, order, cofactor, OID(), source);
+      auto data =
+         ec_group_data().lookup_or_create(p, a, b, base_xy.first, base_xy.second, order, cofactor, OID(), source);
+      return std::make_pair(data, true);
+   }
+
+   if(obj.type() == ASN1_Type::Null) {
+      throw Decoding_Error("Cannot handle ImplicitCA ECC parameters");
    } else {
-      throw Decoding_Error("Unexpected tag while decoding ECC domain params");
+      throw Decoding_Error(fmt("Unexpected tag {} while decoding ECC domain params", asn1_tag_to_string(obj.type())));
    }
 }
 
@@ -410,7 +418,10 @@ EC_Group::EC_Group(std::string_view str) {
       if(str.size() > 30 && str.substr(0, 29) == "-----BEGIN EC PARAMETERS-----") {
          // OK try it as PEM ...
          secure_vector<uint8_t> ber = PEM_Code::decode_check_label(str, "EC PARAMETERS");
-         this->m_data = BER_decode_EC_group(ber.data(), ber.size(), EC_Group_Source::ExternalSource);
+
+         auto data = BER_decode_EC_group(ber.data(), ber.size(), EC_Group_Source::ExternalSource);
+         this->m_data = data.first;
+         this->m_explicit_encoding = data.second;
       }
    }
 
@@ -438,7 +449,9 @@ EC_Group::EC_Group(const BigInt& p,
 }
 
 EC_Group::EC_Group(const uint8_t ber[], size_t ber_len) {
-   m_data = BER_decode_EC_group(ber, ber_len, EC_Group_Source::ExternalSource);
+   auto data = BER_decode_EC_group(ber, ber_len, EC_Group_Source::ExternalSource);
+   m_data = data.first;
+   m_explicit_encoding = data.second;
 }
 
 const EC_Group_Data& EC_Group::data() const {
diff --git a/src/lib/pubkey/ec_group/ec_group.h b/src/lib/pubkey/ec_group/ec_group.h
index 7f61e6ac9..b5d71c622 100644
--- a/src/lib/pubkey/ec_group/ec_group.h
+++ b/src/lib/pubkey/ec_group/ec_group.h
@@ -350,6 +350,14 @@ class BOTAN_PUBLIC_API(2, 0) EC_Group final {
 
       EC_Group_Source source() const;
 
+      /**
+      * Return true if this EC_Group was derived from an explicit encoding
+      *
+      * Explicit encoding of groups is deprecated; when support for explicit curves
+      * is removed in a future major release, this function will also be removed.
+      */
+      bool used_explicit_encoding() const { return m_explicit_encoding; }
+
       /**
       * Return a set of known named EC groups
       */
@@ -373,9 +381,9 @@ class BOTAN_PUBLIC_API(2, 0) EC_Group final {
    private:
       static EC_Group_Data_Map& ec_group_data();
 
-      static std::shared_ptr<EC_Group_Data> BER_decode_EC_group(const uint8_t bits[],
-                                                                size_t len,
-                                                                EC_Group_Source source);
+      static std::pair<std::shared_ptr<EC_Group_Data>, bool> BER_decode_EC_group(const uint8_t bits[],
+                                                                                 size_t len,
+                                                                                 EC_Group_Source source);
 
       static std::shared_ptr<EC_Group_Data> load_EC_group_info(const char* p,
                                                                const char* a,
@@ -388,6 +396,7 @@ class BOTAN_PUBLIC_API(2, 0) EC_Group final {
       // Member data
       const EC_Group_Data& data() const;
       std::shared_ptr<EC_Group_Data> m_data;
+      bool m_explicit_encoding = false;
 };
 
 inline bool operator!=(const EC_Group& lhs, const EC_Group& rhs) {
diff --git a/src/lib/x509/x509path.cpp b/src/lib/x509/x509path.cpp
index a2331e841..a57aa069b 100644
--- a/src/lib/x509/x509path.cpp
+++ b/src/lib/x509/x509path.cpp
@@ -24,6 +24,10 @@
    #include <future>
 #endif
 
+#if defined(BOTAN_HAS_ECC_KEY)
+   #include <botan/ecc_key.h>
+#endif
+
 namespace Botan {
 
 /*


Test patch:
diff --git a/src/tests/test_ec_group.cpp b/src/tests/test_ec_group.cpp
index 59223dacd..1cc1877f3 100644
--- a/src/tests/test_ec_group.cpp
+++ b/src/tests/test_ec_group.cpp
@@ -245,6 +245,7 @@ class EC_Group_Tests : public Test {
 
             result.confirm("EC_Group is known", !group.get_curve_oid().empty());
             result.confirm("EC_Group is considered valid", group.verify_group(Test::rng(), true));
+            result.confirm("EC_Group is not considered explict encoding", !group.used_explicit_encoding());
 
             result.test_eq("EC_Group has correct bit size", group.get_p().bits(), group.get_p_bits());
             result.test_eq("EC_Group has byte size", group.get_p().bytes(), group.get_p_bytes());
@@ -264,6 +265,16 @@ class EC_Group_Tests : public Test {
 
             result.confirm("Same group is same even with copy", group == copy);
 
+            const auto group_der_oid = group.DER_encode(Botan::EC_Group_Encoding::NamedCurve);
+            const Botan::EC_Group group_via_oid(group_der_oid);
+            result.confirm("EC_Group via OID is not considered explict encoding",
+                           !group_via_oid.used_explicit_encoding());
+
+            const auto group_der_explicit = group.DER_encode(Botan::EC_Group_Encoding::Explicit);
+            const Botan::EC_Group group_via_explicit(group_der_explicit);
+            result.confirm("EC_Group via explicit DER is considered explict encoding",
+                           group_via_explicit.used_explicit_encoding());
+
             const auto pt_mult_by_order = group.get_base_point() * group.get_order();
             result.confirm("Multiplying point by the order results in zero point", pt_mult_by_order.is_zero());
 
diff --git a/src/tests/test_ffi.cpp b/src/tests/test_ffi.cpp
index 1554cf024..02618f89c 100644
--- a/src/tests/test_ffi.cpp
+++ b/src/tests/test_ffi.cpp
@@ -688,6 +688,7 @@ class FFI_ECDSA_Certificate_Test final : public FFI_Test {
 
             botan_pubkey_t pub;
             if(TEST_FFI_OK(botan_x509_cert_get_public_key, (cert, &pub))) {
+               TEST_FFI_RC(1, botan_pubkey_ecc_key_used_explicit_encoding, (pub));
                TEST_FFI_OK(botan_pubkey_destroy, (pub));
             }
 
diff --git a/src/tests/unit_ecdsa.cpp b/src/tests/unit_ecdsa.cpp
index fcf236aa1..aefdf77c9 100644
--- a/src/tests/unit_ecdsa.cpp
+++ b/src/tests/unit_ecdsa.cpp
@@ -312,6 +312,8 @@ Test::Result test_read_pkcs8() {
          throw Test_Error("Unable to load valid PKCS8 ECDSA key");
       }
 
+      result.confirm("EC_Group is marked as explicit encoding", ecdsa_nodp->domain().used_explicit_encoding());
+
       Botan::PK_Signer signer(*ecdsa_nodp, Test::rng(), "SHA-256");
       Botan::PK_Verifier verifier(*ecdsa_nodp, "SHA-256");