Repository: randombit/botan
Pull request #2101 (2019-09-10 13:33:11)

Add Extensions::remove
From #2073


Patch:
diff --git a/src/lib/x509/x509_ext.cpp b/src/lib/x509/x509_ext.cpp
index 55e3c434e..7809b5617 100644
--- a/src/lib/x509/x509_ext.cpp
+++ b/src/lib/x509/x509_ext.cpp
@@ -124,7 +124,11 @@ void Extensions::add(Certificate_Extension* extn, bool critical)
    {
    // sanity check: we don't want to have the same extension more than once
    if(m_extension_info.count(extn->oid_of()) > 0)
-      throw Invalid_Argument(extn->oid_name() + " extension already present in Extensions::add");
+      {
+      const std::string name = extn->oid_name();
+      delete extn;
+      throw Invalid_Argument("Extension " + name + " already present in Extensions::add");
+      }
 
    const OID oid = extn->oid_of();
    Extensions_Info info(critical, extn);
@@ -147,10 +151,22 @@ bool Extensions::add_new(Certificate_Extension* extn, bool critical)
    return true;
    }
 
+bool Extensions::remove(const OID& oid)
+   {
+   const bool erased = m_extension_info.erase(oid) > 0;
+
+   if(erased)
+      {
+      m_extension_oids.erase(std::find(m_extension_oids.begin(), m_extension_oids.end(), oid));
+      }
+
+   return erased;
+   }
+
 void Extensions::replace(Certificate_Extension* extn, bool critical)
    {
    // Remove it if it existed
-   m_extension_info.erase(extn->oid_of());
+   remove(extn->oid_of());
 
    const OID oid = extn->oid_of();
    Extensions_Info info(critical, extn);
@@ -171,6 +187,15 @@ bool Extensions::critical_extension_set(const OID& oid) const
    return false;
    }
 
+std::vector<uint8_t> Extensions::get_extension_bits(const OID& oid) const
+   {
+   auto i = m_extension_info.find(oid);
+   if(i == m_extension_info.end())
+      throw Invalid_Argument("Extensions::get_extension_bits no such extension set");
+
+   return i->second.bits();
+   }
+
 const Certificate_Extension* Extensions::get_extension_object(const OID& oid) const
    {
    auto extn = m_extension_info.find(oid);
diff --git a/src/lib/x509/x509_ext.h b/src/lib/x509/x509_ext.h
index 2f818af63..59ece4c22 100644
--- a/src/lib/x509/x509_ext.h
+++ b/src/lib/x509/x509_ext.h
@@ -176,6 +176,12 @@ class BOTAN_PUBLIC_API(2,0) Extensions final : public ASN1_Object
       */
       void replace(Certificate_Extension* extn, bool critical = false);
 
+      /**
+      * Remove an extension from the list. Returns true if the
+      * extension had been set, false otherwise.
+      */
+      bool remove(const OID& oid);
+
       /**
       * Searches for an extension by OID and returns the result.
       * Only the known extensions types declared in this header


Test patch:
diff --git a/src/tests/unit_x509.cpp b/src/tests/unit_x509.cpp
index 2b4b3f5de..482b26531 100644
--- a/src/tests/unit_x509.cpp
+++ b/src/tests/unit_x509.cpp
@@ -1,5 +1,5 @@
 /*
-* (C) 2009 Jack Lloyd
+* (C) 2009,2019 Jack Lloyd
 * (C) 2016 René Korthaus, Rohde & Schwarz Cybersecurity
 *
 * Botan is released under the Simplified BSD License (see license.txt)
@@ -91,7 +91,7 @@ Botan::X509_Cert_Options req_opts2(const std::string& sig_padding = "")
    }
 
 Botan::X509_Cert_Options req_opts3(const std::string& sig_padding = "")
-{
+   {
    Botan::X509_Cert_Options opts("Test User 2/US/Botan Project/Testing");
 
    opts.uri = "https://botan.randombit.net";
@@ -104,7 +104,7 @@ Botan::X509_Cert_Options req_opts3(const std::string& sig_padding = "")
    opts.more_dns.push_back("www.botan.randombit.net");
 
    return opts;
-}
+   }
 
 std::unique_ptr<Botan::Private_Key> make_a_private_key(const std::string& algo)
    {
@@ -129,7 +129,6 @@ std::unique_ptr<Botan::Private_Key> make_a_private_key(const std::string& algo)
    return Botan::create_private_key(algo, Test::rng(), params);
    }
 
-
 Test::Result test_cert_status_strings()
    {
    Test::Result result("Certificate_Status_Code to_string");
@@ -202,6 +201,50 @@ Test::Result test_cert_status_strings()
 
    }
 
+Test::Result test_x509_extension()
+   {
+   Test::Result result("X509 Extensions API");
+
+   Botan::Extensions extn;
+
+   const auto oid_bc = Botan::OID::from_string("X509v3.BasicConstraints");
+   const auto oid_skid = Botan::OID::from_string("X509v3.SubjectKeyIdentifier");
+
+   extn.add(new Botan::Cert_Extension::Basic_Constraints(true), true);
+
+   result.confirm("Basic constraints is set", extn.extension_set(oid_bc));
+   result.confirm("Basic constraints is critical", extn.critical_extension_set(oid_bc));
+   result.confirm("SKID is not set", !extn.extension_set(oid_skid));
+   result.confirm("SKID is not critical", !extn.critical_extension_set(oid_skid));
+
+   result.test_eq("Extension::get_extension_bits",
+                  extn.get_extension_bits(oid_bc), "30060101FF020100");
+
+   result.test_throws("Extension::get_extension_bits throws if not set",
+                      [&]() { extn.get_extension_bits(oid_skid); });
+
+   result.test_throws("Extension::add throws on second add",
+                      [&]() { extn.add(new Botan::Cert_Extension::Basic_Constraints(false), false); });
+
+   result.test_eq("Extension::get_extension_bits",
+                  extn.get_extension_bits(oid_bc), "30060101FF020100");
+
+   result.confirm("Returns false since extension already existed",
+                  !extn.add_new(new Botan::Cert_Extension::Basic_Constraints(false), false));
+
+   result.confirm("Basic constraints is still critical", extn.critical_extension_set(oid_bc));
+
+   extn.replace(new Botan::Cert_Extension::Basic_Constraints(false), false);
+   result.confirm("Replaced basic constraints is not critical", !extn.critical_extension_set(oid_bc));
+   result.test_eq("Extension::get_extension_bits", extn.get_extension_bits(oid_bc), "3000");
+
+   result.confirm("Delete returns false if extn not set", !extn.remove(oid_skid));
+   result.confirm("Delete returns true if extn was set", extn.remove(oid_bc));
+   result.confirm("Basic constraints is not set", !extn.extension_set(oid_bc));
+   result.confirm("Basic constraints is not critical", !extn.critical_extension_set(oid_bc));
+
+   return result;
+   }
 
 Test::Result test_x509_dates()
    {
@@ -1645,6 +1688,7 @@ class X509_Cert_Unit_Tests final : public Test
          results.push_back(test_verify_gost2012_cert());
 #endif
 
+         results.push_back(test_x509_extension());
          results.push_back(test_x509_dates());
          results.push_back(test_cert_status_strings());
          results.push_back(test_x509_uninit());