diff options
author | Jack Lloyd <[email protected]> | 2017-04-04 16:46:16 -0400 |
---|---|---|
committer | Jack Lloyd <[email protected]> | 2017-04-04 16:46:16 -0400 |
commit | a17bfd26d81d2182a5ecccb98f75ff05c7c999bd (patch) | |
tree | d977c7163cd50329141d32277c4e0a97b787a358 | |
parent | 542c6cb7338530b4a8f7d93f2410567815d993ef (diff) | |
parent | b0068f74fb15aa4ffcb9225a60c0aaff89209b62 (diff) |
Merge GH #900 Add ability to search by X509 DN hash
-rw-r--r-- | src/lib/x509/certstor.cpp | 78 | ||||
-rw-r--r-- | src/lib/x509/certstor.h | 12 | ||||
-rw-r--r-- | src/lib/x509/certstor_sql/certstor_sql.cpp | 9 | ||||
-rw-r--r-- | src/lib/x509/certstor_sql/certstor_sql.h | 3 | ||||
-rw-r--r-- | src/lib/x509/x509_ca.cpp | 2 | ||||
-rw-r--r-- | src/lib/x509/x509_ca.h | 2 | ||||
-rw-r--r-- | src/lib/x509/x509cert.cpp | 14 | ||||
-rw-r--r-- | src/lib/x509/x509cert.h | 14 | ||||
-rw-r--r-- | src/tests/test_certstor.cpp | 374 | ||||
-rw-r--r-- | src/tests/unit_x509.cpp | 147 |
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; } |