aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJack Lloyd <[email protected]>2017-04-04 16:46:16 -0400
committerJack Lloyd <[email protected]>2017-04-04 16:46:16 -0400
commita17bfd26d81d2182a5ecccb98f75ff05c7c999bd (patch)
treed977c7163cd50329141d32277c4e0a97b787a358
parent542c6cb7338530b4a8f7d93f2410567815d993ef (diff)
parentb0068f74fb15aa4ffcb9225a60c0aaff89209b62 (diff)
Merge GH #900 Add ability to search by X509 DN hash
-rw-r--r--src/lib/x509/certstor.cpp78
-rw-r--r--src/lib/x509/certstor.h12
-rw-r--r--src/lib/x509/certstor_sql/certstor_sql.cpp9
-rw-r--r--src/lib/x509/certstor_sql/certstor_sql.h3
-rw-r--r--src/lib/x509/x509_ca.cpp2
-rw-r--r--src/lib/x509/x509_ca.h2
-rw-r--r--src/lib/x509/x509cert.cpp14
-rw-r--r--src/lib/x509/x509cert.h14
-rw-r--r--src/tests/test_certstor.cpp374
-rw-r--r--src/tests/unit_x509.cpp147
10 files changed, 423 insertions, 232 deletions
diff --git a/src/lib/x509/certstor.cpp b/src/lib/x509/certstor.cpp
index 10178a526..df4fc3365 100644
--- a/src/lib/x509/certstor.cpp
+++ b/src/lib/x509/certstor.cpp
@@ -13,27 +13,23 @@ namespace Botan {
std::shared_ptr<const X509_CRL> Certificate_Store::find_crl_for(const X509_Certificate&) const
{
- return std::shared_ptr<const X509_CRL>();
+ return {};
}
void Certificate_Store_In_Memory::add_certificate(const X509_Certificate& cert)
{
- for(size_t i = 0; i != m_certs.size(); ++i)
- {
- if(*m_certs[i] == cert)
+ for(const auto& c : m_certs)
+ if(*c == cert)
return;
- }
m_certs.push_back(std::make_shared<const X509_Certificate>(cert));
}
void Certificate_Store_In_Memory::add_certificate(std::shared_ptr<const X509_Certificate> cert)
{
- for(size_t i = 0; i != m_certs.size(); ++i)
- {
- if(*m_certs[i] == *cert)
+ for(const auto& c : m_certs)
+ if(*c == *cert)
return;
- }
m_certs.push_back(cert);
}
@@ -41,8 +37,8 @@ void Certificate_Store_In_Memory::add_certificate(std::shared_ptr<const X509_Cer
std::vector<X509_DN> Certificate_Store_In_Memory::all_subjects() const
{
std::vector<X509_DN> subjects;
- for(size_t i = 0; i != m_certs.size(); ++i)
- subjects.push_back(m_certs[i]->subject_dn());
+ for(const auto& cert : m_certs)
+ subjects.push_back(cert->subject_dn());
return subjects;
}
@@ -50,22 +46,22 @@ std::shared_ptr<const X509_Certificate>
Certificate_Store_In_Memory::find_cert(const X509_DN& subject_dn,
const std::vector<uint8_t>& key_id) const
{
- for(size_t i = 0; i != m_certs.size(); ++i)
+ for(const auto& cert : m_certs)
{
// Only compare key ids if set in both call and in the cert
if(key_id.size())
{
- std::vector<uint8_t> skid = m_certs[i]->subject_key_id();
+ std::vector<uint8_t> skid = cert->subject_key_id();
if(skid.size() && skid != key_id) // no match
continue;
}
- if(m_certs[i]->subject_dn() == subject_dn)
- return m_certs[i];
+ if(cert->subject_dn() == subject_dn)
+ return cert;
}
- return std::shared_ptr<const X509_Certificate>();
+ return nullptr;
}
@@ -75,14 +71,30 @@ Certificate_Store_In_Memory::find_cert_by_pubkey_sha1(const std::vector<uint8_t>
if(key_hash.size() != 20)
throw Invalid_Argument("Certificate_Store_In_Memory::find_cert_by_pubkey_sha1 invalid hash");
- for(size_t i = 0; i != m_certs.size(); ++i)
- {
- const std::vector<uint8_t> hash_i = m_certs[i]->subject_public_key_bitstring_sha1();
- if(key_hash == hash_i)
- {
- return m_certs[i];
- }
- }
+ std::unique_ptr<HashFunction> hash(HashFunction::create("SHA-1"));
+
+ for(const auto& cert : m_certs){
+ hash->update(cert->subject_public_key_bitstring());
+ if(key_hash == hash->final_stdvec()) //final_stdvec also clears the hash to initial state
+ return cert;
+ }
+
+ return nullptr;
+ }
+
+std::shared_ptr<const X509_Certificate>
+Certificate_Store_In_Memory::find_cert_by_raw_subject_dn_sha256(const std::vector<uint8_t>& subject_hash) const
+ {
+ if(subject_hash.size() != 32)
+ throw Invalid_Argument("Certificate_Store_In_Memory::find_cert_by_raw_subject_dn_sha256 invalid hash");
+
+ std::unique_ptr<HashFunction> hash(HashFunction::create("SHA-256"));
+
+ for(const auto& cert : m_certs){
+ hash->update(cert->raw_subject_dn());
+ if(subject_hash == hash->final_stdvec()) //final_stdvec also clears the hash to initial state
+ return cert;
+ }
return nullptr;
}
@@ -97,13 +109,13 @@ void Certificate_Store_In_Memory::add_crl(std::shared_ptr<const X509_CRL> crl)
{
X509_DN crl_issuer = crl->issuer_dn();
- for(size_t i = 0; i != m_crls.size(); ++i)
+ for(auto& c : m_crls)
{
// Found an update of a previously existing one; replace it
- if(m_crls[i]->issuer_dn() == crl_issuer)
+ if(c->issuer_dn() == crl_issuer)
{
- if(m_crls[i]->this_update() <= crl->this_update())
- m_crls[i] = crl;
+ if(c->this_update() <= crl->this_update())
+ c = crl;
return;
}
}
@@ -116,22 +128,22 @@ std::shared_ptr<const X509_CRL> Certificate_Store_In_Memory::find_crl_for(const
{
const std::vector<uint8_t>& key_id = subject.authority_key_id();
- for(size_t i = 0; i != m_crls.size(); ++i)
+ for(const auto& c : m_crls)
{
// Only compare key ids if set in both call and in the CRL
if(key_id.size())
{
- std::vector<uint8_t> akid = m_crls[i]->authority_key_id();
+ std::vector<uint8_t> akid = c->authority_key_id();
if(akid.size() && akid != key_id) // no match
continue;
}
- if(m_crls[i]->issuer_dn() == subject.issuer_dn())
- return m_crls[i];
+ if(c->issuer_dn() == subject.issuer_dn())
+ return c;
}
- return std::shared_ptr<const X509_CRL>();
+ return {};
}
Certificate_Store_In_Memory::Certificate_Store_In_Memory(const X509_Certificate& cert)
diff --git a/src/lib/x509/certstor.h b/src/lib/x509/certstor.h
index 6f0dc9cb3..2b68147f0 100644
--- a/src/lib/x509/certstor.h
+++ b/src/lib/x509/certstor.h
@@ -40,6 +40,15 @@ class BOTAN_DLL Certificate_Store
find_cert_by_pubkey_sha1(const std::vector<uint8_t>& key_hash) const = 0;
/**
+ * Find a certificate by searching for one with a matching SHA-256 hash of
+ * raw subject name. Used for OCSP.
+ * @param subject_hash SHA-256 hash of the subject's raw name
+ * @return a matching certificate or nullptr otherwise
+ */
+ virtual std::shared_ptr<const X509_Certificate>
+ find_cert_by_raw_subject_dn_sha256(const std::vector<uint8_t>& subject_hash) const = 0;
+
+ /**
* Finds a CRL for the given certificate
* @param subject the subject certificate
* @return the CRL for subject or nullptr otherwise
@@ -120,6 +129,9 @@ class BOTAN_DLL Certificate_Store_In_Memory : public Certificate_Store
std::shared_ptr<const X509_Certificate>
find_cert_by_pubkey_sha1(const std::vector<uint8_t>& key_hash) const override;
+ std::shared_ptr<const X509_Certificate>
+ find_cert_by_raw_subject_dn_sha256(const std::vector<uint8_t>& subject_hash) const override;
+
/**
* Finds a CRL for the given certificate
*/
diff --git a/src/lib/x509/certstor_sql/certstor_sql.cpp b/src/lib/x509/certstor_sql/certstor_sql.cpp
index 06e0fda1b..fc8a05eb2 100644
--- a/src/lib/x509/certstor_sql/certstor_sql.cpp
+++ b/src/lib/x509/certstor_sql/certstor_sql.cpp
@@ -81,8 +81,13 @@ Certificate_Store_In_SQL::find_cert(const X509_DN& subject_dn, const std::vector
std::shared_ptr<const X509_Certificate>
Certificate_Store_In_SQL::find_cert_by_pubkey_sha1(const std::vector<uint8_t>& /*key_hash*/) const
{
- // TODO!
- return nullptr;
+ throw Not_Implemented("TODO!");
+ }
+
+std::shared_ptr<const X509_Certificate>
+Certificate_Store_In_SQL::find_cert_by_raw_subject_dn_sha256(const std::vector<uint8_t>& /*subject_hash*/) const
+ {
+ throw Not_Implemented("TODO!");
}
std::shared_ptr<const X509_CRL>
diff --git a/src/lib/x509/certstor_sql/certstor_sql.h b/src/lib/x509/certstor_sql/certstor_sql.h
index 91d8d5c00..f95aea9b4 100644
--- a/src/lib/x509/certstor_sql/certstor_sql.h
+++ b/src/lib/x509/certstor_sql/certstor_sql.h
@@ -44,6 +44,9 @@ class BOTAN_DLL Certificate_Store_In_SQL : public Certificate_Store
std::shared_ptr<const X509_Certificate>
find_cert_by_pubkey_sha1(const std::vector<uint8_t>& key_hash) const override;
+ std::shared_ptr<const X509_Certificate>
+ find_cert_by_raw_subject_dn_sha256(const std::vector<uint8_t>& subject_hash) const override;
+
/**
* Returns all subject DNs known to the store instance.
*/
diff --git a/src/lib/x509/x509_ca.cpp b/src/lib/x509/x509_ca.cpp
index 692f837ae..e1a8c3af7 100644
--- a/src/lib/x509/x509_ca.cpp
+++ b/src/lib/x509/x509_ca.cpp
@@ -49,7 +49,7 @@ X509_CA::~X509_CA()
X509_Certificate X509_CA::sign_request(const PKCS10_Request& req,
RandomNumberGenerator& rng,
const X509_Time& not_before,
- const X509_Time& not_after)
+ const X509_Time& not_after) const
{
Key_Constraints constraints;
if(req.is_CA())
diff --git a/src/lib/x509/x509_ca.h b/src/lib/x509/x509_ca.h
index 0448e109b..bc4b3a506 100644
--- a/src/lib/x509/x509_ca.h
+++ b/src/lib/x509/x509_ca.h
@@ -38,7 +38,7 @@ class BOTAN_DLL X509_CA
X509_Certificate sign_request(const PKCS10_Request& req,
RandomNumberGenerator& rng,
const X509_Time& not_before,
- const X509_Time& not_after);
+ const X509_Time& not_after) const;
/**
* Get the certificate of this CA.
diff --git a/src/lib/x509/x509cert.cpp b/src/lib/x509/x509cert.cpp
index b6e15a3e0..512e4aa63 100644
--- a/src/lib/x509/x509cert.cpp
+++ b/src/lib/x509/x509cert.cpp
@@ -439,6 +439,13 @@ std::vector<uint8_t> X509_Certificate::raw_issuer_dn() const
return m_issuer.get1_memvec("X509.Certificate.dn_bits");
}
+std::vector<uint8_t> X509_Certificate::raw_issuer_dn_sha256() const
+ {
+ std::unique_ptr<HashFunction> hash(HashFunction::create("SHA-256"));
+ hash->update(raw_issuer_dn());
+ return hash->final_stdvec();
+ }
+
X509_DN X509_Certificate::subject_dn() const
{
return create_dn(m_subject);
@@ -449,6 +456,13 @@ std::vector<uint8_t> X509_Certificate::raw_subject_dn() const
return m_subject.get1_memvec("X509.Certificate.dn_bits");
}
+std::vector<uint8_t> X509_Certificate::raw_subject_dn_sha256() const
+ {
+ std::unique_ptr<HashFunction> hash(HashFunction::create("SHA-256"));
+ hash->update(raw_subject_dn());
+ return hash->final_stdvec();
+ }
+
std::string X509_Certificate::fingerprint(const std::string& hash_name) const
{
std::unique_ptr<HashFunction> hash(HashFunction::create(hash_name));
diff --git a/src/lib/x509/x509cert.h b/src/lib/x509/x509cert.h
index ab943db4b..ceadf88c5 100644
--- a/src/lib/x509/x509cert.h
+++ b/src/lib/x509/x509cert.h
@@ -96,16 +96,26 @@ class BOTAN_DLL X509_Certificate : public X509_Object
std::vector<std::string> issuer_info(const std::string& name) const;
/**
- * Raw subject DN
+ * Raw issuer DN
*/
std::vector<uint8_t> raw_issuer_dn() const;
/**
- * Raw issuer DN
+ * SHA-256 of Raw issuer DN
+ */
+ std::vector<uint8_t> raw_issuer_dn_sha256() const;
+
+ /**
+ * Raw subject DN
*/
std::vector<uint8_t> raw_subject_dn() const;
/**
+ * SHA-256 of Raw subject DN
+ */
+ std::vector<uint8_t> raw_subject_dn_sha256() const;
+
+ /**
* Get the notBefore of the certificate.
* @return notBefore of the certificate
*/
diff --git a/src/tests/test_certstor.cpp b/src/tests/test_certstor.cpp
index cacaf8d0e..abe4a4ed0 100644
--- a/src/tests/test_certstor.cpp
+++ b/src/tests/test_certstor.cpp
@@ -6,178 +6,271 @@
#include "tests.h"
-#if defined(BOTAN_HAS_CERTSTOR_SQLITE3)
- #include <botan/certstor_sqlite.h>
- #include <botan/sqlite3.h>
+#if defined(BOTAN_HAS_CERTSTOR_SQL)
+ #include <botan/certstor.h>
#include <botan/internal/filesystem.h>
#include <botan/pkcs8.h>
#include <sstream>
+ #if defined(BOTAN_HAS_CERTSTOR_SQLITE3)
+ #include <botan/certstor_sqlite.h>
+ #include <botan/sqlite3.h>
+ #endif
#endif
-
namespace Botan_Tests {
namespace {
-#if defined(BOTAN_HAS_CERTSTOR_SQLITE3)
+#if defined(BOTAN_HAS_CERTSTOR_SQL)
+struct CertificateAndKey
+ {
+ const Botan::X509_Certificate certificate;
+ const std::shared_ptr<Botan::Private_Key> private_key;
+ bool operator!=(const CertificateAndKey& rhs) const
+ {
+ return std::tie(certificate, private_key) != std::tie(rhs.certificate, rhs.private_key);
+ }
+ };
-Test::Result test_certstor_insert_find_remove_test(
- const std::vector<std::pair<Botan::X509_Certificate,std::shared_ptr<Botan::Private_Key>>>& certs,
- Botan::Certificate_Store_In_SQL& store)
+#if defined(BOTAN_HAS_CERTSTOR_SQLITE3)
+Test::Result test_certstor_sqlite3_insert_find_remove_test(const std::vector<CertificateAndKey>& certsandkeys)
{
- Test::Result result("Certificate Store - Insert, Find, Remove");
+ Test::Result result("Certificate Store SQLITE3 - Insert, Find, Remove");
- for(auto cert_key: certs)
+ try
{
- auto cert = cert_key.first;
- auto key = cert_key.second;
- auto wo_keyid = store.find_cert(cert.subject_dn(),{});
- auto w_keyid = store.find_cert(cert.subject_dn(),cert.subject_key_id());
+ auto& rng = Test::rng();
+ const std::string passwd(reinterpret_cast<const char*>(rng.random_vec(8).data()), 8);
+ // Just create a database in memory for testing (https://sqlite.org/inmemorydb.html)
+ Botan::Certificate_Store_In_SQLite store(":memory:", passwd, rng);
- if(!wo_keyid || !w_keyid)
- {
- result.test_failure("Can't retrieve certificate");
- return result;
- }
+ for(const auto& a : certsandkeys)
+ store.insert_key(a.certificate, *a.private_key);
- auto priv = store.find_key(cert);
- if(!priv && (certs[1] != cert_key && certs[0] != cert_key))
+ for(const auto certandkey : certsandkeys)
{
- result.test_failure("Can't retrieve private key for " + cert.fingerprint("SHA1"));
- return result;
- }
+ const auto cert = certandkey.certificate;
+ const auto key = certandkey.private_key;
+ const auto wo_keyid = store.find_cert(cert.subject_dn(), {});
+ const auto w_keyid = store.find_cert(cert.subject_dn(), cert.subject_key_id());
- result.test_eq("Got wrong certificate",cert.fingerprint(),w_keyid->fingerprint());
+ if(!wo_keyid || !w_keyid)
+ {
+ result.test_failure("Can't retrieve certificate");
+ return result;
+ }
- if(priv)
- {
- result.test_eq("Got wrong private key",key->private_key_bits(),priv->private_key_bits());
+ const auto priv = store.find_key(cert);
+ if(!priv && (certsandkeys[1] != certandkey && certsandkeys[0] != certandkey))
+ {
+ result.test_failure("Can't retrieve private key for " + cert.fingerprint("SHA1"));
+ return result;
+ }
- auto rev_certs = store.find_certs_for_key(*priv);
+ result.test_eq("Got wrong certificate", cert.fingerprint(), w_keyid->fingerprint());
- if(rev_certs.empty())
+ if(priv)
{
- result.test_failure("No certificate");
+ result.test_eq("Got wrong private key", key->private_key_bits(), priv->private_key_bits());
+
+ const auto rev_certs = store.find_certs_for_key(*priv);
+
+ if(rev_certs.empty())
+ {
+ result.test_failure("No certificate");
+ }
+ else
+ {
+ const bool found = std::any_of(rev_certs.begin(),
+ rev_certs.end(), [&](std::shared_ptr<const Botan::X509_Certificate> c) { return c->fingerprint() == cert.fingerprint(); });
+
+ result.test_eq("Got wrong/no certificate", found, true);
+ }
}
- else
+
+ if(certsandkeys[4] != certandkey && certsandkeys[5] != certandkey)
{
- bool found = std::any_of(rev_certs.begin(),rev_certs.end(),[&](std::shared_ptr<const Botan::X509_Certificate> c)
- { return c->fingerprint() == cert.fingerprint(); });
+ result.test_eq("Got wrong certificate", cert.fingerprint(), wo_keyid->fingerprint());
+ }
- result.test_eq("Got wrong/no certificate",found,true);
+ result.test_eq("Can't remove certificate", store.remove_cert(cert), true);
+ result.test_eq("Can't remove certificate", !store.find_cert(cert.subject_dn(), cert.subject_key_id()), true);
+
+ if(priv)
+ {
+ store.remove_key(*key);
}
+
+ result.test_eq("Can't remove key", !store.find_key(cert), true);
}
- if(certs[4] != cert_key && certs[5] != cert_key)
+ return result;
+ }
+ catch(std::exception& e)
+ {
+ result.test_failure(e.what());
+ return result;
+ }
+ }
+
+Test::Result test_certstor_sqlite3_crl_test(const std::vector<CertificateAndKey>& certsandkeys)
+ {
+ Test::Result result("Certificate Store SQLITE3 - CRL");
+ try
+ {
+ auto& rng = Test::rng();
+ const std::string passwd(reinterpret_cast<const char*>(rng.random_vec(8).data()), 8);
+ // Just create a database in memory for testing (https://sqlite.org/inmemorydb.html)
+ Botan::Certificate_Store_In_SQLite store(":memory:", passwd, rng);
+
+ for(const auto& a : certsandkeys)
+ store.insert_cert(a.certificate);
+
+ store.revoke_cert(certsandkeys[0].certificate, Botan::CA_COMPROMISE);
+ store.revoke_cert(certsandkeys[3].certificate, Botan::CA_COMPROMISE);
+ store.revoke_cert(certsandkeys[3].certificate, Botan::CA_COMPROMISE);
+
{
- result.test_eq("Got wrong certificate",cert.fingerprint(),wo_keyid->fingerprint());
+ const auto crls = store.generate_crls();
+
+ result.test_eq("Can't revoke certificate", crls.size(), 2);
+ result.test_eq("Can't revoke certificate",
+ crls[0].is_revoked(certsandkeys[0].certificate) ^ crls[1].is_revoked(certsandkeys[0].certificate), true);
+ result.test_eq("Can't revoke certificate",
+ crls[0].is_revoked(certsandkeys[3].certificate) ^ crls[1].is_revoked(certsandkeys[3].certificate), true);
}
-
- result.test_eq("Can't remove certificate",store.remove_cert(cert),true);
- result.test_eq("Can't remove certificate",!store.find_cert(cert.subject_dn(),cert.subject_key_id()),true);
- if(priv)
+ store.affirm_cert(certsandkeys[3].certificate);
+
{
- store.remove_key(*key);
+ const auto crls = store.generate_crls();
+
+ result.test_eq("Can't revoke certificate, wrong crl size", crls.size(), 1);
+ result.test_eq("Can't revoke certificate, cert 0 not revoked", crls[0].is_revoked(certsandkeys[0].certificate), true);
}
- result.test_eq("Can't remove key",!store.find_key(cert),true);
- }
+ const auto cert0_crl = store.find_crl_for(certsandkeys[0].certificate);
- return result;
- }
+ result.test_eq("Can't revoke certificate, crl for cert 0", !cert0_crl, false);
+ result.test_eq("Can't revoke certificate, crl for cert 0 size check", cert0_crl->get_revoked().size(), 1);
+ result.test_eq("Can't revoke certificate, no crl for cert 0", cert0_crl->is_revoked(certsandkeys[0].certificate), true);
-Test::Result test_certstor_crl_test(
- const std::vector<std::pair<Botan::X509_Certificate,std::shared_ptr<Botan::Private_Key>>>& certs,
- Botan::Certificate_Store_In_SQL& store)
- {
- Test::Result result("Certificate Store - CRL");
+ const auto cert3_crl = store.find_crl_for(certsandkeys[3].certificate);
+
+ result.test_eq("Can't revoke certificate, crl for cert 3", !cert3_crl, true);
- store.revoke_cert(certs[0].first,Botan::CA_COMPROMISE);
- store.revoke_cert(certs[3].first,Botan::CA_COMPROMISE);
- store.revoke_cert(certs[3].first,Botan::CA_COMPROMISE);
+ return result;
+ }
+ catch(std::exception& e)
+ {
+ result.test_failure(e.what());
+ return result;
+ }
+ }
+Test::Result test_certstor_sqlite3_all_subjects_test(const std::vector<CertificateAndKey>& certsandkeys)
{
- auto crls = store.generate_crls();
+ Test::Result result("Certificate Store SQLITE3 - All subjects");
+ try
+ {
+ auto& rng = Test::rng();
+ const std::string passwd(reinterpret_cast<const char*>(rng.random_vec(8).data()), 8);
+ // Just create a database in memory for testing (https://sqlite.org/inmemorydb.html)
+ Botan::Certificate_Store_In_SQLite store(":memory:", passwd, rng);
- result.test_eq("Can't revoke certificate",crls.size(),2);
- result.test_eq("Can't revoke certificate",crls[0].is_revoked(certs[0].first) ^ crls[1].is_revoked(certs[0].first),true);
- result.test_eq("Can't revoke certificate",crls[0].is_revoked(certs[3].first) ^ crls[1].is_revoked(certs[3].first),true);
- }
+ for(const auto& a : certsandkeys)
+ store.insert_cert(a.certificate);
- store.affirm_cert(certs[3].first);
+ const auto subjects = store.all_subjects();
- {
- auto crls = store.generate_crls();
+ result.test_eq("Check subject list length", subjects.size(), 6);
- result.test_eq("Can't revoke certificate, wrong crl size",crls.size(),1);
- result.test_eq("Can't revoke certificate, cert 0 not revoked",crls[0].is_revoked(certs[0].first),true);
+ for(const auto sub : subjects)
+ {
+ std::stringstream ss;
+
+ ss << sub;
+ result.test_eq("Check subject " + ss.str(),
+ certsandkeys[0].certificate.subject_dn() == sub ||
+ certsandkeys[1].certificate.subject_dn() == sub ||
+ certsandkeys[2].certificate.subject_dn() == sub ||
+ certsandkeys[3].certificate.subject_dn() == sub ||
+ certsandkeys[4].certificate.subject_dn() == sub ||
+ certsandkeys[5].certificate.subject_dn() == sub,
+ true);
+ }
+ return result;
+ }
+ catch(std::exception& e)
+ {
+ result.test_failure(e.what());
+ return result;
+ }
}
- auto cert0_crl = store.find_crl_for(certs[0].first);
+#endif
- result.test_eq("Can't revoke certificate, crl for cert 0",!cert0_crl,false);
- result.test_eq("Can't revoke certificate, crl for cert 0 size check",cert0_crl->get_revoked().size(),1);
- result.test_eq("Can't revoke certificate, no crl for cert 0",cert0_crl->is_revoked(certs[0].first),true);
+Test::Result test_certstor_find_hash_subject(const std::vector<CertificateAndKey>& certsandkeys)
+ {
+ Test::Result result("Certificate Store - Find by subject hash");
- auto cert3_crl = store.find_crl_for(certs[3].first);
+ try
+ {
+ Botan::Certificate_Store_In_Memory store;
- result.test_eq("Can't revoke certificate, crl for cert 3",!cert3_crl,true);
+ for(const auto& a : certsandkeys)
+ store.add_certificate(a.certificate);
- return result;
- }
+ for(const auto certandkey : certsandkeys)
+ {
+ const auto cert = certandkey.certificate;
+ const auto hash = cert.raw_subject_dn_sha256();
-Test::Result test_certstor_all_subjects_test(
- const std::vector<std::pair<Botan::X509_Certificate,std::shared_ptr<Botan::Private_Key>>>& certs,
- Botan::Certificate_Store_In_SQL& store)
- {
- Test::Result result("Certificate Store - All subjects");
+ const auto found = store.find_cert_by_raw_subject_dn_sha256(hash);
+ if(!found)
+ {
+ result.test_failure("Can't retrieve certificate " + cert.fingerprint("SHA1"));
+ return result;
+ }
- auto subjects = store.all_subjects();
-
- result.test_eq("Check subject list length",subjects.size(),6);
+ result.test_eq("Got wrong certificate", hash, found->raw_subject_dn_sha256());
+ }
- for(auto sub: subjects)
+ const auto found = store.find_cert_by_raw_subject_dn_sha256(std::vector<uint8_t>(32,0));
+ if(found)
+ {
+ result.test_failure("Certificate found for dummy hash");
+ return result;
+ }
+
+ return result;
+ }
+ catch(std::exception& e)
{
- std::stringstream ss;
-
- ss << sub;
- result.test_eq("Check subject " + ss.str(),
- certs[0].first.subject_dn() == sub ||
- certs[1].first.subject_dn() == sub ||
- certs[2].first.subject_dn() == sub ||
- certs[3].first.subject_dn() == sub ||
- certs[4].first.subject_dn() == sub ||
- certs[5].first.subject_dn() == sub,true);
-
+ result.test_failure(e.what());
+ return result;
}
- return result;
}
class Certstor_Tests : public Test
{
public:
- std::vector<Test::Result> run() override
+ std::vector<Test::Result> run() override
{
const std::string test_dir = Test::data_dir() + "/certstor";
- const std::vector<std::pair<std::string,std::string>> test_data({
- std::make_pair("cert1.crt","key01.pem"),
- std::make_pair("cert2.crt","key01.pem"),
- std::make_pair("cert3.crt","key03.pem"),
- std::make_pair("cert4.crt","key04.pem"),
- std::make_pair("cert5a.crt","key05.pem"),
- std::make_pair("cert5b.crt","key06.pem")
- });
-
- std::vector<Test::Result> results;
- std::vector<std::pair<std::string,std::function<Test::Result(
- const std::vector<std::pair<Botan::X509_Certificate,std::shared_ptr<Botan::Private_Key>>>&,
- Botan::Certificate_Store_In_SQL&)>>>
- fns({
- std::make_pair("Certificate Store - Insert, Find, Remove",test_certstor_insert_find_remove_test),
- std::make_pair("Certificate Store - CRL",test_certstor_crl_test),
- std::make_pair("Certificate Store - All subjects",test_certstor_all_subjects_test)
- });
+ struct CertificateAndKeyFilenames
+ {
+ const std::string certificate;
+ const std::string private_key;
+ } const certsandkeys_filenames[]
+ {
+ {"cert1.crt", "key01.pem"},
+ {"cert2.crt", "key01.pem"},
+ {"cert3.crt", "key03.pem"},
+ {"cert4.crt", "key04.pem"},
+ {"cert5a.crt", "key05.pem"},
+ {"cert5b.crt", "key06.pem"},
+ };
try
{
@@ -200,53 +293,38 @@ class Certstor_Tests : public Test
return {result};
}
- for(auto fn: fns)
- {
- Test::Result result(fn.first);
-
- try
- {
- auto& rng = Test::rng();
- std::string passwd(reinterpret_cast<const char*>(rng.random_vec(8).data()),8);
-
- // Just create a database in memory for testing (https://sqlite.org/inmemorydb.html)
- Botan::Certificate_Store_In_SQLite store(":memory:", passwd, rng);
- std::vector<std::pair<Botan::X509_Certificate,std::shared_ptr<Botan::Private_Key>>> retrieve;
-
- for(auto&& cert_key_pair : test_data)
- {
- Botan::X509_Certificate cert(test_dir + "/" + cert_key_pair.first);
- std::shared_ptr<Botan::Private_Key> key(Botan::PKCS8::load_key(test_dir + "/" + cert_key_pair.second,rng));
-
- if(!key)
- {
- result.test_failure("Failed to load key from disk");
- results.push_back(fn.second(retrieve,store));
- continue;
- }
+ auto& rng = Test::rng();
+ std::vector<CertificateAndKey> certsandkeys;
+ for(const auto& certandkey_filenames : certsandkeys_filenames)
+ {
+ const Botan::X509_Certificate certificate(test_dir + "/" + certandkey_filenames.certificate);
+ std::shared_ptr<Botan::Private_Key> private_key(Botan::PKCS8::load_key(test_dir + "/" +
+ certandkey_filenames.private_key, rng));
- store.insert_cert(cert);
- store.insert_key(cert,*key);
- retrieve.push_back(std::make_pair(cert,key));
- }
-
- results.push_back(fn.second(retrieve,store));
- }
- catch(std::exception& e)
+ if(!private_key)
{
- results.push_back(Test::Result::Failure("Certstor test '" + fn.first + "'", e.what()));
+ Test::Result result("Certificate Store");
+ result.test_failure("Failed to load key from disk at path: " + test_dir + "/" + certandkey_filenames.private_key);
+ return {result};
}
+
+ certsandkeys.push_back({certificate, private_key});
}
+ std::vector<Test::Result> results;
+
+ results.push_back(test_certstor_find_hash_subject(certsandkeys));
+#if defined(BOTAN_HAS_CERTSTOR_SQLITE3)
+ results.push_back(test_certstor_sqlite3_insert_find_remove_test(certsandkeys));
+ results.push_back(test_certstor_sqlite3_crl_test(certsandkeys));
+ results.push_back(test_certstor_sqlite3_all_subjects_test(certsandkeys));
+#endif
return results;
}
};
BOTAN_REGISTER_TEST("certstor", Certstor_Tests);
-
#endif
-
}
-
}
diff --git a/src/tests/unit_x509.cpp b/src/tests/unit_x509.cpp
index 5f11523b5..981897ab0 100644
--- a/src/tests/unit_x509.cpp
+++ b/src/tests/unit_x509.cpp
@@ -87,15 +87,16 @@ Botan::X509_Cert_Options req_opts2()
std::unique_ptr<Botan::Private_Key> make_a_private_key(const std::string& algo)
{
- std::string params = ""; // default "" means choose acceptable algo-specific params
-
- // Here we override defaults as needed
- if(algo == "RSA")
- params = "1024";
- if(algo == "GOST-34.10")
- params = "gost_256A";
- if(algo == "ECKCDSA" || algo == "ECGDSA")
- params = "brainpool256r1";
+ const std::string params = [&]{
+ // Here we override defaults as needed
+ if(algo == "RSA")
+ return "1024";
+ if(algo == "GOST-34.10")
+ return "gost_256A";
+ if(algo == "ECKCDSA" || algo == "ECGDSA")
+ return "brainpool256r1";
+ return ""; // default "" means choose acceptable algo-specific params
+ }();
return Botan::create_private_key(algo, Test::rng(), params);
}
@@ -111,7 +112,7 @@ Test::Result test_cert_status_strings()
Botan::to_string(Botan::Certificate_Status_Code::OK),
Botan::to_string(Botan::Certificate_Status_Code::VERIFIED));
- const std::vector<Botan::Certificate_Status_Code> codes = {
+ const Botan::Certificate_Status_Code codes[]{
Botan::Certificate_Status_Code::OCSP_RESPONSE_GOOD,
Botan::Certificate_Status_Code::OCSP_SIGNATURE_OK,
Botan::Certificate_Status_Code::VALID_CRL_CHECKED,
@@ -151,9 +152,9 @@ Test::Result test_cert_status_strings()
Botan::Certificate_Status_Code::CERT_PUBKEY_INVALID,
};
- for(auto code : codes)
+ for(const auto code : codes)
{
- std::string s = Botan::to_string(code);
+ const std::string s = Botan::to_string(code);
result.confirm("String is long enough to be informative", s.size() > 12);
result.test_eq("No duplicates", seen.count(s), 0);
seen.insert(s);
@@ -166,7 +167,7 @@ Test::Result test_cert_status_strings()
Test::Result test_x509_dates()
{
- Test::Result result("X509_Time");
+ Test::Result result("X509 Time");
Botan::X509_Time time;
result.confirm("unset time not set", !time.time_is_set());
@@ -187,7 +188,7 @@ Test::Result test_x509_dates()
result.test_eq("GENERALIZED_TIME readable_string", time.readable_string(), "2020/03/05 10:03:50 UTC");
// Dates that are valid per X.500 but rejected as unsupported
- const std::vector<std::string> valid_but_unsup = {
+ const std::string valid_but_unsup[]{
"0802010000-0000",
"0802011724+0000",
"0406142334-0500",
@@ -204,7 +205,7 @@ Test::Result test_x509_dates()
};
// valid length 13
- const std::vector<std::string> valid_utc = {
+ const std::string valid_utc[]{
"080201000000Z",
"080201172412Z",
"040614233433Z",
@@ -212,7 +213,7 @@ Test::Result test_x509_dates()
"000614233455Z",
};
- const std::vector<std::string> invalid_utc = {
+ const std::string invalid_utc[]{
"",
" ",
"2008`02-01",
@@ -284,11 +285,11 @@ Test::Result test_x509_dates()
};
// valid length 15
- const std::vector<std::string> valid_generalized_time = {
+ const std::string valid_generalized_time[]{
"20000305100350Z",
};
- const std::vector<std::string> invalid_generalized = {
+ const std::string invalid_generalized[]{
// No trailing Z
"20000305100350",
@@ -314,27 +315,27 @@ Test::Result test_x509_dates()
"170217180154Z",
};
- for(auto&& v : valid_but_unsup)
+ for(const auto& v : valid_but_unsup)
{
result.test_throws("valid but unsupported", [v]() { Botan::X509_Time t(v, Botan::ASN1_Tag::UTC_TIME); });
}
- for(auto&& v : valid_utc)
+ for(const auto& v : valid_utc)
{
Botan::X509_Time t(v, Botan::ASN1_Tag::UTC_TIME);
}
- for(auto&& v : valid_generalized_time)
+ for(const auto& v : valid_generalized_time)
{
Botan::X509_Time t(v, Botan::ASN1_Tag::GENERALIZED_TIME);
}
- for(auto&& v : invalid_utc)
+ for(const auto& v : invalid_utc)
{
result.test_throws("invalid", [v]() { Botan::X509_Time t(v, Botan::ASN1_Tag::UTC_TIME); });
}
- for (auto&& v : invalid_generalized)
+ for (const auto& v : invalid_generalized)
{
result.test_throws("invalid", [v]() { Botan::X509_Time t(v, Botan::ASN1_Tag::GENERALIZED_TIME); });
}
@@ -357,7 +358,7 @@ Test::Result test_x509_cert(const std::string& sig_algo, const std::string& hash
}
/* Create the self-signed cert */
- Botan::X509_Certificate ca_cert =
+ const Botan::X509_Certificate ca_cert =
Botan::X509::create_self_signed_cert(ca_opts(),
*ca_key,
hash_fn,
@@ -429,7 +430,7 @@ Test::Result test_x509_cert(const std::string& sig_algo, const std::string& hash
result.test_eq("issuer info Orga", user1_cert.issuer_info("O").at(0), ca_opts().organization);
result.test_eq("issuer info OrgaUnit", user1_cert.issuer_info("OU").at(0), ca_opts().org_unit);
- Botan::X509_CRL crl1 = ca.new_crl(Test::rng());
+ const Botan::X509_CRL crl1 = ca.new_crl(Test::rng());
/* Verify the certs */
Botan::Path_Validation_Restrictions restrictions(false, 80);
@@ -474,7 +475,7 @@ Test::Result test_x509_cert(const std::string& sig_algo, const std::string& hash
revoked.push_back(Botan::CRL_Entry(user1_cert, Botan::CESSATION_OF_OPERATION));
revoked.push_back(user2_cert);
- Botan::X509_CRL crl2 = ca.update_crl(crl1, revoked, Test::rng());
+ const Botan::X509_CRL crl2 = ca.update_crl(crl1, revoked, Test::rng());
store.add_crl(crl2);
@@ -522,27 +523,27 @@ Test::Result test_usage(const std::string& sig_algo, const std::string& hash_fn
}
/* Create the self-signed cert */
- Botan::X509_Certificate ca_cert =
+ const Botan::X509_Certificate ca_cert =
Botan::X509::create_self_signed_cert(ca_opts(),
*ca_key,
hash_fn,
Test::rng());
/* Create the CA object */
- Botan::X509_CA ca(ca_cert, *ca_key, hash_fn, Test::rng());
+ const Botan::X509_CA ca(ca_cert, *ca_key, hash_fn, Test::rng());
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;
- Botan::PKCS10_Request user1_req =
+ const Botan::PKCS10_Request user1_req =
Botan::X509::create_cert_req(opts,
*user1_key,
hash_fn,
Test::rng());
- Botan::X509_Certificate user1_cert =
+ const Botan::X509_Certificate user1_cert =
ca.sign_request(user1_req, Test::rng(),
from_date(2008, 01, 01),
from_date(2033, 01, 01));
@@ -556,13 +557,13 @@ Test::Result test_usage(const std::string& sig_algo, const std::string& hash_fn
opts.constraints = Key_Constraints(Key_Constraints::DIGITAL_SIGNATURE | Key_Constraints::CRL_SIGN);
- Botan::PKCS10_Request mult_usage_req =
+ const Botan::PKCS10_Request mult_usage_req =
Botan::X509::create_cert_req(opts,
*user1_key,
hash_fn,
Test::rng());
- Botan::X509_Certificate mult_usage_cert =
+ const Botan::X509_Certificate mult_usage_cert =
ca.sign_request(mult_usage_req, Test::rng(),
from_date(2008, 01, 01),
from_date(2033, 01, 01));
@@ -575,13 +576,13 @@ Test::Result test_usage(const std::string& sig_algo, const std::string& hash_fn
opts.constraints = Key_Constraints::NO_CONSTRAINTS;
- Botan::PKCS10_Request no_usage_req =
+ const Botan::PKCS10_Request no_usage_req =
Botan::X509::create_cert_req(opts,
*user1_key,
hash_fn,
Test::rng());
- Botan::X509_Certificate no_usage_cert =
+ const Botan::X509_Certificate no_usage_cert =
ca.sign_request(no_usage_req, Test::rng(),
from_date(2008, 01, 01),
from_date(2033, 01, 01));
@@ -610,14 +611,14 @@ Test::Result test_self_issued(const std::string& sig_algo, const std::string& ha
}
// create the self-signed cert
- Botan::X509_Certificate ca_cert =
+ const Botan::X509_Certificate ca_cert =
Botan::X509::create_self_signed_cert(ca_opts(),
*ca_key,
hash_fn,
Test::rng());
/* Create the CA object */
- Botan::X509_CA ca(ca_cert, *ca_key, hash_fn, Test::rng());
+ const Botan::X509_CA ca(ca_cert, *ca_key, hash_fn, Test::rng());
std::unique_ptr<Botan::Private_Key> user_key(make_a_private_key(sig_algo));
@@ -626,23 +627,23 @@ Test::Result test_self_issued(const std::string& sig_algo, const std::string& ha
Botan::X509_Cert_Options opts = ca_opts();
opts.constraints = Key_Constraints::DIGITAL_SIGNATURE;
- Botan::PKCS10_Request self_issued_req =
+ const Botan::PKCS10_Request self_issued_req =
Botan::X509::create_cert_req(opts,
*user_key,
hash_fn,
Test::rng());
- Botan::X509_Certificate self_issued_cert =
+ const Botan::X509_Certificate self_issued_cert =
ca.sign_request(self_issued_req, Test::rng(),
from_date(2008, 01, 01),
from_date(2033, 01, 01));
// check that this chain can can be verified successfully
- Botan::Certificate_Store_In_Memory trusted(ca.ca_certificate());
+ const Botan::Certificate_Store_In_Memory trusted(ca.ca_certificate());
- Botan::Path_Validation_Restrictions restrictions(false, 80);
+ const Botan::Path_Validation_Restrictions restrictions(false, 80);
- Botan::Path_Validation_Result validation_result =
+ const Botan::Path_Validation_Result validation_result =
Botan::x509_path_validate(self_issued_cert,
restrictions,
trusted);
@@ -868,7 +869,7 @@ Test::Result test_x509_extensions(const std::string& sig_algo, const std::string
opts.extensions = req_extensions;
/* Create a self-signed certificate */
- Botan::X509_Certificate self_signed_cert = Botan::X509::create_self_signed_cert(opts, *user_key, hash_fn, Test::rng());
+ const Botan::X509_Certificate self_signed_cert = Botan::X509::create_self_signed_cert(opts, *user_key, hash_fn, Test::rng());
// check if known Key_Usage extension is present in self-signed cert
auto key_usage_ext = self_signed_cert.v3_extensions().get(Botan::OIDS::lookup("X509v3.KeyUsage"));
@@ -886,14 +887,14 @@ Test::Result test_x509_extensions(const std::string& sig_algo, const std::string
}
- Botan::PKCS10_Request user_req =
+ const Botan::PKCS10_Request user_req =
Botan::X509::create_cert_req(opts,
*user_key,
hash_fn,
Test::rng());
/* Create a CA-signed certificate */
- Botan::X509_Certificate user_cert =
+ const Botan::X509_Certificate user_cert =
ca.sign_request(user_req, Test::rng(),
from_date(2008, 01, 01),
from_date(2033, 01, 01));
@@ -916,6 +917,61 @@ Test::Result test_x509_extensions(const std::string& sig_algo, const std::string
return result;
}
+Test::Result test_hashes(const std::string &algo, const std::string &hash_fn = "SHA-256")
+ {
+ Test::Result result("X509 Hashes");
+
+ const std::unique_ptr<Botan::Private_Key> key(make_a_private_key(algo));
+
+ struct TestData {
+ const std::string issuer, subject, issuer_hash, subject_hash;
+ } const cases[]{
+ {"",
+ "",
+ "E4F60D0AA6D7F3D3B6A6494B1C861B99F649C6F9EC51ABAF201B20F297327C95",
+ "E4F60D0AA6D7F3D3B6A6494B1C861B99F649C6F9EC51ABAF201B20F297327C95"},
+ {"a",
+ "b",
+ "BC2E013472F39AC579964880E422737C82BA812CB8BC2FD17E013060D71E6E19",
+ "5E31CFAA3FAFB1A5BA296A0D2BAB9CA44D7936E9BF0BBC54637D0C53DBC4A432"},
+ {"A",
+ "B",
+ "4B3206201C4BC9B6CD6C36532A97687DF9238155D99ADB60C66BF2B2220643D8",
+ "FFF635A52A16618B4A0E9CD26B5E5A2FA573D343C051E6DE8B0811B1ACC89B86"},
+ {"Test Issuer/US/Botan Project/Testing",
+ "Test Subject/US/Botan Project/Testing",
+ "E2407027922619C0673E0AA59A9CD3673730C36A39F891BCE0806D1DD225A937",
+ "42A63CB4FCCA81AC6D14D5E209B3156E033B90FF1007216927EA9324BA4EF2DB"},
+ {"Test Subject/US/Botan Project/Testing",
+ "Test Issuer/US/Botan Project/Testing",
+ "42A63CB4FCCA81AC6D14D5E209B3156E033B90FF1007216927EA9324BA4EF2DB",
+ "E2407027922619C0673E0AA59A9CD3673730C36A39F891BCE0806D1DD225A937"}};
+
+ for (const auto& a : cases)
+ {
+ Botan::X509_Cert_Options opts{a.issuer};
+ opts.CA_key();
+
+ const Botan::X509_Certificate issuer_cert =
+ Botan::X509::create_self_signed_cert(opts, *key, hash_fn, Test::rng());
+
+ result.test_eq(a.issuer, Botan::hex_encode(issuer_cert.raw_issuer_dn_sha256()), a.issuer_hash);
+ result.test_eq(a.issuer, Botan::hex_encode(issuer_cert.raw_subject_dn_sha256()), a.issuer_hash);
+
+ const Botan::X509_CA ca(issuer_cert, *key, hash_fn, Test::rng());
+ const Botan::PKCS10_Request req =
+ Botan::X509::create_cert_req(a.subject, *key, hash_fn, Test::rng());
+ const Botan::X509_Certificate subject_cert =
+ ca.sign_request(req, Test::rng(),
+ from_date(2008, 01, 01),
+ from_date(2033, 01, 01));
+
+ result.test_eq(a.subject, Botan::hex_encode(subject_cert.raw_issuer_dn_sha256()), a.issuer_hash);
+ result.test_eq(a.subject, Botan::hex_encode(subject_cert.raw_subject_dn_sha256()), a.subject_hash);
+ }
+ return result;
+ }
+
class X509_Cert_Unit_Tests : public Test
{
public:
@@ -923,7 +979,7 @@ class X509_Cert_Unit_Tests : public Test
{
std::vector<Test::Result> results;
- const std::vector<std::string> sig_algos { "RSA", "DSA", "ECDSA", "ECGDSA", "ECKCDSA", "GOST-34.10" };
+ const std::string sig_algos[] { "RSA", "DSA", "ECDSA", "ECGDSA", "ECKCDSA", "GOST-34.10" };
Test::Result cert_result("X509 Unit");
Test::Result usage_result("X509 Usage");
Test::Result self_issued_result("X509 Self Issued");
@@ -981,6 +1037,7 @@ class X509_Cert_Unit_Tests : public Test
results.push_back(valid_constraints_result);
results.push_back(test_x509_dates());
results.push_back(test_cert_status_strings());
+ results.push_back(test_hashes("ECDSA"));
return results;
}