aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/lib/cert/x509/certstor.cpp59
-rw-r--r--src/lib/cert/x509/certstor.h21
-rw-r--r--src/lib/cert/x509/certstor_sql/certstor_sql.cpp320
-rw-r--r--src/lib/cert/x509/certstor_sql/certstor_sql.h93
-rw-r--r--src/lib/cert/x509/certstor_sql/info.txt5
-rw-r--r--src/lib/cert/x509/certstor_sqlite3/certstor_sqlite.cpp18
-rw-r--r--src/lib/cert/x509/certstor_sqlite3/certstor_sqlite.h23
-rw-r--r--src/lib/cert/x509/certstor_sqlite3/info.txt6
-rw-r--r--src/lib/cert/x509/ocsp.cpp4
-rw-r--r--src/lib/cert/x509/x509_crl.cpp9
-rw-r--r--src/lib/cert/x509/x509_crl.h3
-rw-r--r--src/lib/cert/x509/x509_ext.cpp8
-rw-r--r--src/lib/cert/x509/x509_ext.h6
-rw-r--r--src/lib/cert/x509/x509path.cpp76
-rw-r--r--src/lib/cert/x509/x509path.h6
-rw-r--r--src/lib/utils/database.h2
-rw-r--r--src/lib/utils/sqlite3/sqlite3.cpp7
-rw-r--r--src/lib/utils/sqlite3/sqlite3.h1
18 files changed, 585 insertions, 82 deletions
diff --git a/src/lib/cert/x509/certstor.cpp b/src/lib/cert/x509/certstor.cpp
index 26c9ce117..4ef0e5240 100644
--- a/src/lib/cert/x509/certstor.cpp
+++ b/src/lib/cert/x509/certstor.cpp
@@ -5,62 +5,73 @@
* Botan is released under the Simplified BSD License (see license.txt)
*/
+#include <cassert>
#include <botan/certstor.h>
+#include <botan/ber_dec.h>
+#include <botan/der_enc.h>
#include <botan/internal/filesystem.h>
+#include <botan/pkcs8.h>
+#include <botan/data_src.h>
+#include <botan/auto_rng.h>
+#include <botan/hash.h>
+#include <botan/hex.h>
+
+#include <iostream>
namespace Botan {
-const X509_CRL* Certificate_Store::find_crl_for(const X509_Certificate&) const
+std::shared_ptr<const X509_CRL> Certificate_Store::find_crl_for(const X509_Certificate&) const
{
- return nullptr;
+ return std::shared_ptr<const X509_CRL>();
}
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)
+ if(*m_certs[i] == cert)
return;
}
- m_certs.push_back(cert);
+ m_certs.push_back(std::make_shared<X509_Certificate>(cert));
}
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());
+ subjects.push_back(m_certs[i]->subject_dn());
return subjects;
}
namespace {
-const X509_Certificate*
+template<typename T>
+std::shared_ptr<const X509_Certificate>
cert_search(const X509_DN& subject_dn, const std::vector<byte>& key_id,
- const std::vector<X509_Certificate>& certs)
+ const std::vector<std::shared_ptr<T>>& certs)
{
for(size_t i = 0; i != certs.size(); ++i)
{
// Only compare key ids if set in both call and in the cert
if(key_id.size())
{
- std::vector<byte> skid = certs[i].subject_key_id();
+ std::vector<byte> skid = certs[i]->subject_key_id();
if(skid.size() && skid != key_id) // no match
continue;
}
- if(certs[i].subject_dn() == subject_dn)
- return &certs[i];
+ if(certs[i]->subject_dn() == subject_dn)
+ return certs[i];
}
- return nullptr;
+ return std::shared_ptr<const X509_Certificate>();
}
}
-const X509_Certificate*
+std::shared_ptr<const X509_Certificate>
Certificate_Store_In_Memory::find_cert(const X509_DN& subject_dn,
const std::vector<byte>& key_id) const
{
@@ -74,19 +85,19 @@ void Certificate_Store_In_Memory::add_crl(const X509_CRL& crl)
for(size_t i = 0; i != m_crls.size(); ++i)
{
// Found an update of a previously existing one; replace it
- if(m_crls[i].issuer_dn() == crl_issuer)
+ if(m_crls[i]->issuer_dn() == crl_issuer)
{
- if(m_crls[i].this_update() <= crl.this_update())
- m_crls[i] = crl;
+ if(m_crls[i]->this_update() <= crl.this_update())
+ m_crls[i] = std::make_shared<X509_CRL>(crl);
return;
}
}
// Totally new CRL, add to the list
- m_crls.push_back(crl);
+ m_crls.push_back(std::make_shared<X509_CRL>(crl));
}
-const X509_CRL* Certificate_Store_In_Memory::find_crl_for(const X509_Certificate& subject) const
+std::shared_ptr<const X509_CRL> Certificate_Store_In_Memory::find_crl_for(const X509_Certificate& subject) const
{
const std::vector<byte>& key_id = subject.authority_key_id();
@@ -95,17 +106,17 @@ const X509_CRL* Certificate_Store_In_Memory::find_crl_for(const X509_Certificate
// Only compare key ids if set in both call and in the CRL
if(key_id.size())
{
- std::vector<byte> akid = m_crls[i].authority_key_id();
+ std::vector<byte> akid = m_crls[i]->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(m_crls[i]->issuer_dn() == subject.issuer_dn())
+ return m_crls[i];
}
- return nullptr;
+ return std::shared_ptr<const X509_CRL>();
}
Certificate_Store_In_Memory::Certificate_Store_In_Memory(const X509_Certificate& cert)
@@ -123,7 +134,7 @@ Certificate_Store_In_Memory::Certificate_Store_In_Memory(const std::string& dir)
{
try
{
- m_certs.push_back(X509_Certificate(cert_file));
+ m_certs.push_back(std::make_shared<X509_Certificate>(cert_file));
}
catch(std::exception&)
{
@@ -131,7 +142,7 @@ Certificate_Store_In_Memory::Certificate_Store_In_Memory(const std::string& dir)
}
}
-const X509_Certificate*
+std::shared_ptr<const X509_Certificate>
Certificate_Store_Overlay::find_cert(const X509_DN& subject_dn,
const std::vector<byte>& key_id) const
{
@@ -142,7 +153,7 @@ std::vector<X509_DN> Certificate_Store_Overlay::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());
+ subjects.push_back(m_certs[i]->subject_dn());
return subjects;
}
diff --git a/src/lib/cert/x509/certstor.h b/src/lib/cert/x509/certstor.h
index 29948c709..c769bfe5c 100644
--- a/src/lib/cert/x509/certstor.h
+++ b/src/lib/cert/x509/certstor.h
@@ -10,6 +10,8 @@
#include <botan/x509cert.h>
#include <botan/x509_crl.h>
+#include <botan/database.h>
+#include <botan/pbkdf.h>
namespace Botan {
@@ -24,10 +26,10 @@ class BOTAN_DLL Certificate_Store
/**
* Subject DN and (optionally) key identifier
*/
- virtual const X509_Certificate*
+ virtual std::shared_ptr<const X509_Certificate>
find_cert(const X509_DN& subject_dn, const std::vector<byte>& key_id) const = 0;
- virtual const X509_CRL* find_crl_for(const X509_Certificate& subject) const;
+ virtual std::shared_ptr<const X509_CRL> find_crl_for(const X509_Certificate& subject) const;
bool certificate_known(const X509_Certificate& cert) const
{
@@ -60,32 +62,31 @@ class BOTAN_DLL Certificate_Store_In_Memory : public Certificate_Store
std::vector<X509_DN> all_subjects() const override;
- const X509_Certificate* find_cert(
+ std::shared_ptr<const X509_Certificate> find_cert(
const X509_DN& subject_dn,
const std::vector<byte>& key_id) const override;
- const X509_CRL* find_crl_for(const X509_Certificate& subject) const override;
+ std::shared_ptr<const X509_CRL> find_crl_for(const X509_Certificate& subject) const override;
private:
// TODO: Add indexing on the DN and key id to avoid linear search
- std::vector<X509_Certificate> m_certs;
- std::vector<X509_CRL> m_crls;
+ std::vector<std::shared_ptr<X509_Certificate>> m_certs;
+ std::vector<std::shared_ptr<X509_CRL>> m_crls;
};
class BOTAN_DLL Certificate_Store_Overlay : public Certificate_Store
{
public:
- explicit Certificate_Store_Overlay(const std::vector<X509_Certificate>& certs) :
+ explicit Certificate_Store_Overlay(const std::vector<std::shared_ptr<const X509_Certificate>>& certs) :
m_certs(certs) {}
std::vector<X509_DN> all_subjects() const override;
- const X509_Certificate* find_cert(
+ std::shared_ptr<const X509_Certificate> find_cert(
const X509_DN& subject_dn,
const std::vector<byte>& key_id) const override;
private:
- const std::vector<X509_Certificate>& m_certs;
+ const std::vector<std::shared_ptr<const X509_Certificate>>& m_certs;
};
}
-
#endif
diff --git a/src/lib/cert/x509/certstor_sql/certstor_sql.cpp b/src/lib/cert/x509/certstor_sql/certstor_sql.cpp
new file mode 100644
index 000000000..d8325fb87
--- /dev/null
+++ b/src/lib/cert/x509/certstor_sql/certstor_sql.cpp
@@ -0,0 +1,320 @@
+/*
+* Certificate Store in SQL
+* (C) 2016 Kai Michaelis, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/certstor_sql.h>
+#include <botan/ber_dec.h>
+#include <botan/der_enc.h>
+#include <botan/internal/filesystem.h>
+#include <botan/pkcs8.h>
+#include <botan/data_src.h>
+#include <botan/auto_rng.h>
+#include <botan/hash.h>
+#include <botan/hex.h>
+
+namespace Botan {
+
+Certificate_Store_In_SQL::Certificate_Store_In_SQL(std::shared_ptr<SQL_Database> db,
+ const std::string& passwd,
+ const std::string& table_prefix)
+: m_database(db), m_prefix(table_prefix), m_password(passwd)
+ {
+ m_database->create_table("CREATE TABLE IF NOT EXISTS certificates ( \
+ fingerprint BLOB PRIMARY KEY, \
+ subject_dn BLOB, \
+ key_id BLOB, \
+ priv_fingerprint BLOB, \
+ certificate BLOB UNIQUE NOT NULL\
+ )");
+ m_database->create_table("CREATE TABLE IF NOT EXISTS keys ( \
+ fingerprint BLOB PRIMARY KEY, \
+ key BLOB UNIQUE NOT NULL\
+ )");
+ m_database->create_table("CREATE TABLE IF NOT EXISTS revoked (\
+ fingerprint BLOB PRIMARY KEY, \
+ reason BLOB NOT NULL, \
+ time BLOB NOT NULL \
+ )");
+ }
+
+// Certificate handling
+std::shared_ptr<const X509_Certificate>
+Certificate_Store_In_SQL::find_cert(const X509_DN& subject_dn, const std::vector<byte>& key_id) const
+ {
+ DER_Encoder enc;
+ std::shared_ptr<SQL_Database::Statement> stmt;
+
+ subject_dn.encode_into(enc);
+
+ if(key_id.empty())
+ {
+ stmt = m_database->new_statement("SELECT certificate FROM certificates WHERE subject_dn == ?1");
+ stmt->bind(1,enc.get_contents_unlocked());
+ }
+ else
+ {
+ stmt = m_database->new_statement("SELECT certificate FROM certificates WHERE\
+ subject_dn == ?1 AND (key_id == NULL OR key_id == ?2)");
+ stmt->bind(1,enc.get_contents_unlocked());
+ stmt->bind(2,key_id);
+ }
+
+ std::shared_ptr<const X509_Certificate> cert;
+ while(stmt->step())
+ {
+ auto blob = stmt->get_blob(0);
+ cert = std::make_shared<X509_Certificate>(
+ std::vector<byte>(blob.first,blob.first + blob.second));
+
+ }
+
+ return cert;
+ }
+
+std::shared_ptr<const X509_CRL>
+Certificate_Store_In_SQL::find_crl_for(const X509_Certificate& subject) const
+ {
+ auto all_crls = generate_crls();
+
+ for(auto crl: all_crls)
+ {
+ if(!crl.get_revoked().empty() && crl.issuer_dn() == subject.issuer_dn())
+ return std::shared_ptr<X509_CRL>(new X509_CRL(crl));
+ }
+
+ return std::shared_ptr<X509_CRL>();
+ }
+
+std::vector<X509_DN> Certificate_Store_In_SQL::all_subjects() const
+ {
+ std::vector<X509_DN> ret;
+ auto stmt = m_database->new_statement("SELECT subject_dn FROM certificates");
+
+ while(stmt->step())
+ {
+ auto blob = stmt->get_blob(0);
+ BER_Decoder dec(blob.first,blob.second);
+ X509_DN dn;
+
+ dn.decode_from(dec);
+
+ ret.push_back(dn);
+ }
+
+ return ret;
+ }
+
+bool Certificate_Store_In_SQL::insert_cert(const X509_Certificate& cert)
+ {
+ if(find_cert(cert.subject_dn(),cert.subject_key_id()))
+ return false;
+
+ DER_Encoder enc;
+ auto stmt = m_database->new_statement("INSERT OR REPLACE INTO certificates (\
+ fingerprint, \
+ subject_dn, \
+ key_id, \
+ priv_fingerprint, \
+ certificate \
+ ) VALUES ( ?1, ?2, ?3, ?4, ?5 )");
+
+ stmt->bind(1,cert.fingerprint("SHA-256"));
+ cert.subject_dn().encode_into(enc);
+ stmt->bind(2,enc.get_contents_unlocked());
+ stmt->bind(3,cert.subject_key_id());
+ stmt->bind(4,std::vector<byte>());
+ enc = DER_Encoder();
+ cert.encode_into(enc);
+ stmt->bind(5,enc.get_contents_unlocked());
+ stmt->spin();
+
+ return true;
+ }
+
+
+bool Certificate_Store_In_SQL::remove_cert(const X509_Certificate& cert)
+ {
+ if(!find_cert(cert.subject_dn(),cert.subject_key_id()))
+ return false;
+
+ auto stmt = m_database->new_statement("DELETE FROM certificates WHERE fingerprint == ?1");
+
+ stmt->bind(1,cert.fingerprint("SHA-256"));
+ stmt->spin();
+
+ return true;
+ }
+
+// Private key handling
+std::shared_ptr<const Private_Key> Certificate_Store_In_SQL::find_key(const X509_Certificate& cert) const
+ {
+ auto stmt = m_database->new_statement("SELECT key FROM keys JOIN certificates ON keys.fingerprint == certificates.priv_fingerprint WHERE certificates.fingerprint == ?1");
+ stmt->bind(1,cert.fingerprint("SHA-256"));
+
+ std::shared_ptr<const Private_Key> key;
+ while(stmt->step())
+ {
+ auto blob = stmt->get_blob(0);
+ AutoSeeded_RNG rng;
+ DataSource_Memory src(blob.first,blob.second);
+ key.reset(PKCS8::load_key(src,rng,m_password));
+ }
+
+ return key;
+ }
+
+std::vector<std::shared_ptr<const X509_Certificate>>
+Certificate_Store_In_SQL::find_certs_for_key(const Private_Key& key) const
+ {
+ AutoSeeded_RNG rng;
+ auto fpr = fingerprint_key(key);
+ auto stmt = m_database->new_statement("SELECT certificate FROM certificates WHERE priv_fingerprint == ?1");
+
+ stmt->bind(1,fpr);
+
+ std::shared_ptr<const X509_Certificate> cert;
+ while(stmt->step())
+ {
+ auto blob = stmt->get_blob(0);
+ cert = std::make_shared<X509_Certificate>(
+ std::vector<byte>(blob.first,blob.first + blob.second));
+ }
+
+ return {cert};
+ }
+
+bool Certificate_Store_In_SQL::insert_key(const X509_Certificate& cert, const Private_Key& key) {
+ insert_cert(cert);
+
+ if(find_key(cert))
+ return false;
+
+ AutoSeeded_RNG rng;
+ auto pkcs8 = PKCS8::BER_encode(key,rng,m_password);
+ auto fpr = fingerprint_key(key);
+
+ //m_database->new_statement("BEGIN TRANSACTION")->spin();
+
+ auto stmt1 = m_database->new_statement(
+ "INSERT OR REPLACE INTO keys ( fingerprint, key ) VALUES ( ?1, ?2 )");
+
+ stmt1->bind(1,fpr);
+ stmt1->bind(2,pkcs8.data(),pkcs8.size());
+ stmt1->spin();
+
+ auto stmt2 = m_database->new_statement(
+ "UPDATE certificates SET priv_fingerprint = ?1 WHERE fingerprint == ?2");
+
+ stmt2->bind(1,fpr);
+ stmt2->bind(2,cert.fingerprint("SHA-256"));
+ stmt2->spin();
+
+ //m_database->new_statement("END TRANSACTION")->spin();
+
+ return true;
+ }
+
+std::string Certificate_Store_In_SQL::fingerprint_key(const Botan::Private_Key& key) const
+ {
+ secure_vector<byte> buf = key.pkcs8_private_key();
+ std::unique_ptr<HashFunction> hash(HashFunction::create("SHA-256"));
+ hash->update(buf);
+ const auto hex_print = hex_encode(hash->final());
+
+ std::string formatted_print;
+
+ for(size_t i = 0; i != hex_print.size(); i += 2)
+ {
+ formatted_print.push_back(hex_print[i]);
+ formatted_print.push_back(hex_print[i+1]);
+
+ if(i != hex_print.size() - 2)
+ formatted_print.push_back(':');
+ }
+
+ return formatted_print;
+ }
+
+void Certificate_Store_In_SQL::remove_key(const Private_Key& key)
+ {
+ AutoSeeded_RNG rng;
+ auto fpr = fingerprint_key(key);
+ auto stmt = m_database->new_statement("DELETE FROM keys WHERE fingerprint == ?1");
+
+ stmt->bind(1,fpr);
+ stmt->spin();
+ }
+
+// Revocation
+void Certificate_Store_In_SQL::revoke_cert(const X509_Certificate& cert, CRL_Code code, const X509_Time& time)
+ {
+ insert_cert(cert);
+
+ auto stmt1 = m_database->new_statement(
+ "INSERT OR REPLACE INTO revoked ( fingerprint, reason, time ) VALUES ( ?1, ?2, ?3 )");
+
+ stmt1->bind(1,cert.fingerprint("SHA-256"));
+ stmt1->bind(2,code);
+
+ if(time.time_is_set())
+ {
+ DER_Encoder der;
+ time.encode_into(der);
+ stmt1->bind(3,der.get_contents_unlocked());
+ }
+ else
+ {
+ stmt1->bind(3,-1);
+ }
+
+ stmt1->spin();
+ }
+
+void Certificate_Store_In_SQL::affirm_cert(const X509_Certificate& cert)
+ {
+ auto stmt = m_database->new_statement("DELETE FROM revoked WHERE fingerprint == ?1");
+
+ stmt->bind(1,cert.fingerprint("SHA-256"));
+ stmt->spin();
+ }
+
+std::vector<X509_CRL> Certificate_Store_In_SQL::generate_crls() const
+ {
+ auto stmt = m_database->new_statement(
+ "SELECT certificate,reason,time FROM revoked JOIN certificates ON certificates.fingerprint == revoked.fingerprint");
+
+ std::map<X509_DN,std::vector<CRL_Entry>> crls;
+ while(stmt->step())
+ {
+ auto blob = stmt->get_blob(0);
+ auto cert = X509_Certificate(
+ std::vector<byte>(blob.first,blob.first + blob.second));
+ auto code = static_cast<CRL_Code>(stmt->get_size_t(1));
+ auto ent = CRL_Entry(cert,code);
+
+ auto i = crls.find(cert.issuer_dn());
+ if(i == crls.end())
+ {
+ crls.insert(std::make_pair(cert.issuer_dn(),std::vector<CRL_Entry>({ent})));
+ }
+ else
+ {
+ i->second.push_back(ent);
+ }
+ }
+
+ std::vector<X509_CRL> ret;
+ X509_Time t(std::chrono::system_clock::now());
+
+ for(auto p: crls)
+ {
+ ret.push_back(X509_CRL(p.first,t,t,p.second));
+ }
+
+ return ret;
+ }
+
+}
diff --git a/src/lib/cert/x509/certstor_sql/certstor_sql.h b/src/lib/cert/x509/certstor_sql/certstor_sql.h
new file mode 100644
index 000000000..24db88cdd
--- /dev/null
+++ b/src/lib/cert/x509/certstor_sql/certstor_sql.h
@@ -0,0 +1,93 @@
+/*
+* Certificate Store in SQL
+* (C) 2016 Kai Michaelis, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CERT_STORE_SQL_H__
+#define BOTAN_CERT_STORE_SQL_H__
+
+#include <botan/certstor.h>
+#include <botan/x509cert.h>
+#include <botan/x509_crl.h>
+#include <botan/database.h>
+
+namespace Botan {
+
+/**
+ * Certificate and private key store backed an SQL database.
+ */
+class BOTAN_DLL Certificate_Store_In_SQL : public Certificate_Store
+ {
+ public:
+ /**
+ * Create/open a certificate store backed by "db".
+ * Inserted private keys are encrypted using "passwd".
+ */
+ explicit Certificate_Store_In_SQL(const std::shared_ptr<SQL_Database> db,
+ const std::string& passwd,
+ const std::string& table_prefix = "");
+
+ /// Returns the first certificate with matching subject DN and optional key ID.
+ virtual std::shared_ptr<const X509_Certificate>
+ find_cert(const X509_DN& subject_dn, const std::vector<byte>& key_id) const override;
+
+ /// Generates a CRL for all certificates issued by "subject"s issuer.
+ virtual std::shared_ptr<const X509_CRL>
+ find_crl_for(const X509_Certificate& subject) const override;
+
+ /// Returns all subject DNs known to the store instance,
+ virtual std::vector<X509_DN> all_subjects() const override;
+
+ /**
+ * Inserts "cert" into the store, returns false if the certificate is
+ * already known and true if insertion was successful.
+ */
+ bool insert_cert(const X509_Certificate& cert);
+
+ /**
+ * Removes "cert" from the store. Returns false if the certificate could not
+ * be found and true if removal was successful.
+ */
+ bool remove_cert(const X509_Certificate& cert);
+
+ /// Returns the private key for "cert" or an empty shared_ptr if none was found.
+ std::shared_ptr<const Private_Key> find_key(const X509_Certificate&) const;
+
+ /// Returns all certificates for private key "key".
+ std::vector<std::shared_ptr<const X509_Certificate>>
+ find_certs_for_key(const Private_Key& key) const;
+
+ /**
+ * Inserts "key" for "cert" into the store, returns false if the key is
+ * already known and true if insertion was successful.
+ */
+ bool insert_key(const X509_Certificate& cert, const Private_Key& key);
+
+ /// Removes "key" from the store.
+ void remove_key(const Private_Key& key);
+
+ /// Marks "cert" as revoked starting from "time".
+ void revoke_cert(const X509_Certificate&,CRL_Code,const X509_Time& time = X509_Time());
+
+ /// Reverses the revokation for "cert".
+ void affirm_cert(const X509_Certificate&);
+
+ /**
+ * Generates Certificate Revocation Lists for all certificates marked as revoked.
+ * A CRL is returned for each unique issuer DN.
+ */
+ std::vector<X509_CRL> generate_crls() const;
+
+ private:
+ std::string fingerprint_key(const Private_Key&) const;
+
+ std::shared_ptr<SQL_Database> m_database;
+ std::string m_prefix;
+ std::string m_password;
+ std::mutex m_mutex;
+ };
+
+}
+#endif
diff --git a/src/lib/cert/x509/certstor_sql/info.txt b/src/lib/cert/x509/certstor_sql/info.txt
new file mode 100644
index 000000000..cfdd521a2
--- /dev/null
+++ b/src/lib/cert/x509/certstor_sql/info.txt
@@ -0,0 +1,5 @@
+define CERTSTOR_SQL 20160818
+
+<requires>
+datastor
+</requires>
diff --git a/src/lib/cert/x509/certstor_sqlite3/certstor_sqlite.cpp b/src/lib/cert/x509/certstor_sqlite3/certstor_sqlite.cpp
new file mode 100644
index 000000000..89dfb3575
--- /dev/null
+++ b/src/lib/cert/x509/certstor_sqlite3/certstor_sqlite.cpp
@@ -0,0 +1,18 @@
+/*
+* Certificate Store in SQL
+* (C) 2016 Kai Michaelis, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/certstor_sqlite.h>
+#include <botan/sqlite3.h>
+
+namespace Botan {
+
+Certificate_Store_In_SQLite::Certificate_Store_In_SQLite(const std::string& db_path,
+ const std::string& passwd,
+ const std::string& table_prefix)
+: Certificate_Store_In_SQL(std::make_shared<Sqlite3_Database>(db_path), passwd, table_prefix)
+ {}
+}
diff --git a/src/lib/cert/x509/certstor_sqlite3/certstor_sqlite.h b/src/lib/cert/x509/certstor_sqlite3/certstor_sqlite.h
new file mode 100644
index 000000000..c7d686d89
--- /dev/null
+++ b/src/lib/cert/x509/certstor_sqlite3/certstor_sqlite.h
@@ -0,0 +1,23 @@
+/*
+* Certificate Store in SQL
+* (C) 2016 Kai Michaelis, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CERT_STORE_SQLITE_H__
+#define BOTAN_CERT_STORE_SQLITE_H__
+
+#include <botan/certstor_sql.h>
+
+namespace Botan {
+
+class BOTAN_DLL Certificate_Store_In_SQLite : public Certificate_Store_In_SQL
+ {
+ public:
+ Certificate_Store_In_SQLite(const std::string& db_path,
+ const std::string& passwd,
+ const std::string& table_prefix = "");
+ };
+}
+#endif
diff --git a/src/lib/cert/x509/certstor_sqlite3/info.txt b/src/lib/cert/x509/certstor_sqlite3/info.txt
new file mode 100644
index 000000000..d5e50fc95
--- /dev/null
+++ b/src/lib/cert/x509/certstor_sqlite3/info.txt
@@ -0,0 +1,6 @@
+define CERTSTOR_SQLITE3 20160818
+
+<requires>
+certstor_sql
+sqlite3
+</requires>
diff --git a/src/lib/cert/x509/ocsp.cpp b/src/lib/cert/x509/ocsp.cpp
index df8df3b39..761c5b436 100644
--- a/src/lib/cert/x509/ocsp.cpp
+++ b/src/lib/cert/x509/ocsp.cpp
@@ -92,9 +92,9 @@ void check_signature(const std::vector<byte>& tbs_response,
if(!trusted_roots.certificate_known(result.trust_root())) // not needed anymore?
throw Exception("Certificate chain roots in unknown/untrusted CA");
- const std::vector<X509_Certificate>& cert_path = result.cert_path();
+ const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path = result.cert_path();
- check_signature(tbs_response, sig_algo, signature, cert_path[0]);
+ check_signature(tbs_response, sig_algo, signature, *cert_path[0]);
}
}
diff --git a/src/lib/cert/x509/x509_crl.cpp b/src/lib/cert/x509/x509_crl.cpp
index 64cb1b308..3c75825c1 100644
--- a/src/lib/cert/x509/x509_crl.cpp
+++ b/src/lib/cert/x509/x509_crl.cpp
@@ -39,6 +39,15 @@ X509_CRL::X509_CRL(const std::vector<byte>& in, bool touc) :
do_decode();
}
+X509_CRL::X509_CRL(const X509_DN& issuer, const X509_Time& thisUpdate,
+ const X509_Time& nextUpdate, const std::vector<CRL_Entry>& revoked) :
+ X509_Object(), m_throw_on_unknown_critical(false), m_revoked(revoked)
+ {
+ m_info.add(issuer.contents());
+ m_info.add("X509.CRL.start", thisUpdate.to_string());
+ m_info.add("X509.CRL.end", nextUpdate.to_string());
+ }
+
/**
* Check if this particular certificate is listed in the CRL
*/
diff --git a/src/lib/cert/x509/x509_crl.h b/src/lib/cert/x509/x509_crl.h
index dab4d5153..2e05f98fb 100644
--- a/src/lib/cert/x509/x509_crl.h
+++ b/src/lib/cert/x509/x509_crl.h
@@ -100,6 +100,9 @@ class BOTAN_DLL X509_CRL final : public X509_Object
X509_CRL(const std::vector<byte>& vec,
bool throw_on_unknown_critical = false);
+ X509_CRL(const X509_DN& issuer, const X509_Time& thisUpdate,
+ const X509_Time& nextUpdate, const std::vector<CRL_Entry>& revoked);
+
private:
void force_decode() override;
diff --git a/src/lib/cert/x509/x509_ext.cpp b/src/lib/cert/x509/x509_ext.cpp
index 650c20d53..23340f784 100644
--- a/src/lib/cert/x509/x509_ext.cpp
+++ b/src/lib/cert/x509/x509_ext.cpp
@@ -83,7 +83,7 @@ OID Certificate_Extension::oid_of() const
* Validate the extension (the default implementation is a NOP)
*/
void Certificate_Extension::validate(const X509_Certificate&, const X509_Certificate&,
- const std::vector<X509_Certificate>&,
+ const std::vector<std::shared_ptr<const X509_Certificate>>&,
std::vector<std::set<Certificate_Status_Code>>&,
size_t)
{
@@ -525,7 +525,7 @@ void Name_Constraints::contents_to(Data_Store& subject, Data_Store&) const
}
void Name_Constraints::validate(const X509_Certificate& subject, const X509_Certificate& issuer,
- const std::vector<X509_Certificate>& cert_path,
+ const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
std::vector<std::set<Certificate_Status_Code>>& cert_status,
size_t pos)
{
@@ -547,7 +547,7 @@ void Name_Constraints::validate(const X509_Certificate& subject, const X509_Cert
for(auto c: m_name_constraints.permitted())
{
- switch(c.base().matches(cert_path.at(j)))
+ switch(c.base().matches(*cert_path.at(j)))
{
case GeneralName::MatchResult::NotFound:
case GeneralName::MatchResult::All:
@@ -564,7 +564,7 @@ void Name_Constraints::validate(const X509_Certificate& subject, const X509_Cert
for(auto c: m_name_constraints.excluded())
{
- switch(c.base().matches(cert_path.at(j)))
+ switch(c.base().matches(*cert_path.at(j)))
{
case GeneralName::MatchResult::All:
case GeneralName::MatchResult::Some:
diff --git a/src/lib/cert/x509/x509_ext.h b/src/lib/cert/x509/x509_ext.h
index 8ea2f2da6..b1984fa94 100644
--- a/src/lib/cert/x509/x509_ext.h
+++ b/src/lib/cert/x509/x509_ext.h
@@ -68,7 +68,7 @@ class BOTAN_DLL Certificate_Extension
* @param pos Position of subject certificate in cert_path
*/
virtual void validate(const X509_Certificate& subject, const X509_Certificate& issuer,
- const std::vector<X509_Certificate>& cert_path,
+ const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
std::vector<std::set<Certificate_Status_Code>>& cert_status,
size_t pos);
@@ -305,7 +305,7 @@ class BOTAN_DLL Name_Constraints : public Certificate_Extension
Name_Constraints(const NameConstraints &nc) : m_name_constraints(nc) {}
void validate(const X509_Certificate& subject, const X509_Certificate& issuer,
- const std::vector<X509_Certificate>& cert_path,
+ const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
std::vector<std::set<Certificate_Status_Code>>& cert_status,
size_t pos) override;
@@ -479,7 +479,7 @@ class BOTAN_DLL Unknown_Critical_Extension final : public Certificate_Extension
{ return m_oid; };
void validate(const X509_Certificate&, const X509_Certificate&,
- const std::vector<X509_Certificate>&,
+ const std::vector<std::shared_ptr<const X509_Certificate>>&,
std::vector<std::set<Certificate_Status_Code>>& cert_status,
size_t pos) override
{
diff --git a/src/lib/cert/x509/x509path.cpp b/src/lib/cert/x509/x509path.cpp
index c08b11d42..65ab3eac1 100644
--- a/src/lib/cert/x509/x509path.cpp
+++ b/src/lib/cert/x509/x509path.cpp
@@ -20,7 +20,7 @@ namespace Botan {
namespace {
-const X509_Certificate*
+std::shared_ptr<const X509_Certificate>
find_issuing_cert(const X509_Certificate& cert,
Certificate_Store& end_certs,
const std::vector<Certificate_Store*>& certstores)
@@ -28,25 +28,25 @@ find_issuing_cert(const X509_Certificate& cert,
const X509_DN issuer_dn = cert.issuer_dn();
const std::vector<byte> auth_key_id = cert.authority_key_id();
- const X509_Certificate* c = end_certs.find_cert(issuer_dn, auth_key_id);
+ std::shared_ptr<const X509_Certificate> c = end_certs.find_cert(issuer_dn, auth_key_id);
if(c && *c != cert)
return c;
for(size_t i = 0; i != certstores.size(); ++i)
{
- if(const X509_Certificate* c = certstores[i]->find_cert(issuer_dn, auth_key_id))
+ if(std::shared_ptr<const X509_Certificate> c = certstores[i]->find_cert(issuer_dn, auth_key_id))
return c;
}
return nullptr;
}
-const X509_CRL* find_crls_for(const X509_Certificate& cert,
+std::shared_ptr<const X509_CRL> find_crls_for(const X509_Certificate& cert,
const std::vector<Certificate_Store*>& certstores)
{
for(size_t i = 0; i != certstores.size(); ++i)
{
- if(const X509_CRL* crl = certstores[i]->find_crl_for(cert))
+ if(std::shared_ptr<const X509_CRL> crl = certstores[i]->find_crl_for(cert))
return crl;
}
@@ -72,7 +72,7 @@ const X509_CRL* find_crls_for(const X509_Certificate& cert,
}
std::vector<std::set<Certificate_Status_Code>>
-check_chain(const std::vector<X509_Certificate>& cert_path,
+check_chain(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
const Path_Validation_Restrictions& restrictions,
const std::vector<Certificate_Store*>& certstores)
{
@@ -92,9 +92,9 @@ check_chain(const std::vector<X509_Certificate>& cert_path,
const bool at_self_signed_root = (i == cert_path.size() - 1);
- const X509_Certificate& subject = cert_path[i];
+ std::shared_ptr<const X509_Certificate> subject = cert_path[i];
- const X509_Certificate& issuer = cert_path[at_self_signed_root ? (i) : (i + 1)];
+ std::shared_ptr<const X509_Certificate> issuer = cert_path[at_self_signed_root ? (i) : (i + 1)];
if(i == 0 || restrictions.ocsp_all_intermediates())
{
@@ -102,25 +102,25 @@ check_chain(const std::vector<X509_Certificate>& cert_path,
if(certstores.size() > 1)
ocsp_responses.push_back(
std::async(std::launch::async,
- OCSP::online_check, issuer, subject, certstores[0]));
+ OCSP::online_check, *issuer, *subject, certstores[0]));
}
// Check all certs for valid time range
- if(current_time < X509_Time(subject.start_time(), ASN1_Tag::UTC_OR_GENERALIZED_TIME))
+ if(current_time < X509_Time(subject->start_time(), ASN1_Tag::UTC_OR_GENERALIZED_TIME))
status.insert(Certificate_Status_Code::CERT_NOT_YET_VALID);
- if(current_time > X509_Time(subject.end_time(), ASN1_Tag::UTC_OR_GENERALIZED_TIME))
+ if(current_time > X509_Time(subject->end_time(), ASN1_Tag::UTC_OR_GENERALIZED_TIME))
status.insert(Certificate_Status_Code::CERT_HAS_EXPIRED);
// Check issuer constraints
- if(!issuer.is_CA_cert() && !self_signed_ee_cert)
+ if(!issuer->is_CA_cert() && !self_signed_ee_cert)
status.insert(Certificate_Status_Code::CA_CERT_NOT_FOR_CERT_ISSUER);
- if(issuer.path_limit() < i)
+ if(issuer->path_limit() < i)
status.insert(Certificate_Status_Code::CERT_CHAIN_TOO_LONG);
- std::unique_ptr<Public_Key> issuer_key(issuer.subject_public_key());
+ std::unique_ptr<Public_Key> issuer_key(issuer->subject_public_key());
if(!issuer_key)
{
@@ -128,7 +128,7 @@ check_chain(const std::vector<X509_Certificate>& cert_path,
}
else
{
- if(subject.check_signature(*issuer_key) == false)
+ if(subject->check_signature(*issuer_key) == false)
status.insert(Certificate_Status_Code::SIGNATURE_ERROR);
if(issuer_key->estimated_strength() < restrictions.minimum_key_strength())
@@ -138,15 +138,15 @@ check_chain(const std::vector<X509_Certificate>& cert_path,
// Allow untrusted hashes on self-signed roots
if(!trusted_hashes.empty() && !at_self_signed_root)
{
- if(!trusted_hashes.count(subject.hash_used_for_signature()))
+ if(!trusted_hashes.count(subject->hash_used_for_signature()))
status.insert(Certificate_Status_Code::UNTRUSTED_HASH);
}
// Check cert extensions
- Extensions extensions = subject.v3_extensions();
+ Extensions extensions = subject->v3_extensions();
for(auto& extension : extensions.extensions())
{
- extension.first->validate(subject, issuer, cert_path, cert_status, i);
+ extension.first->validate(*subject, *issuer, cert_path, cert_status, i);
}
}
@@ -154,8 +154,8 @@ check_chain(const std::vector<X509_Certificate>& cert_path,
{
std::set<Certificate_Status_Code>& status = cert_status.at(i);
- const X509_Certificate& subject = cert_path.at(i);
- const X509_Certificate& ca = cert_path.at(i+1);
+ std::shared_ptr<const X509_Certificate> subject = cert_path.at(i);
+ std::shared_ptr<const X509_Certificate> ca = cert_path.at(i+1);
if(i < ocsp_responses.size())
{
@@ -163,7 +163,7 @@ check_chain(const std::vector<X509_Certificate>& cert_path,
{
OCSP::Response ocsp = ocsp_responses[i].get();
- auto ocsp_status = ocsp.status_for(ca, subject);
+ auto ocsp_status = ocsp.status_for(*ca, *subject);
status.insert(ocsp_status);
@@ -181,7 +181,7 @@ check_chain(const std::vector<X509_Certificate>& cert_path,
}
}
- const X509_CRL* crl_p = find_crls_for(subject, certstores);
+ std::shared_ptr<const X509_CRL> crl_p = find_crls_for(*subject, certstores);
if(!crl_p)
{
@@ -192,7 +192,7 @@ check_chain(const std::vector<X509_Certificate>& cert_path,
const X509_CRL& crl = *crl_p;
- if(!ca.allowed_usage(CRL_SIGN))
+ if(!ca->allowed_usage(CRL_SIGN))
status.insert(Certificate_Status_Code::CA_CERT_NOT_FOR_CRL_ISSUER);
if(current_time < X509_Time(crl.this_update()))
@@ -201,10 +201,10 @@ check_chain(const std::vector<X509_Certificate>& cert_path,
if(current_time > X509_Time(crl.next_update()))
status.insert(Certificate_Status_Code::CRL_HAS_EXPIRED);
- if(crl.check_signature(ca.subject_public_key()) == false)
+ if(crl.check_signature(ca->subject_public_key()) == false)
status.insert(Certificate_Status_Code::CRL_BAD_SIGNATURE);
- if(crl.is_revoked(subject))
+ if(crl.is_revoked(*subject))
status.insert(Certificate_Status_Code::CERT_IS_REVOKED);
}
@@ -226,8 +226,12 @@ Path_Validation_Result x509_path_validate(
if(end_certs.empty())
throw Invalid_Argument("x509_path_validate called with no subjects");
- std::vector<X509_Certificate> cert_path;
- cert_path.push_back(end_certs[0]);
+ std::vector<std::shared_ptr<const X509_Certificate>> cert_path;
+ std::vector<std::shared_ptr<const X509_Certificate>> end_certs_sharedptr;
+ cert_path.push_back(std::make_shared<X509_Certificate>(end_certs[0]));
+
+ for(auto c: end_certs)
+ end_certs_sharedptr.push_back(std::make_shared<const X509_Certificate>(c));
/*
* This is an inelegant but functional way of preventing path loops
@@ -236,12 +240,12 @@ Path_Validation_Result x509_path_validate(
*/
std::set<std::string> certs_seen;
- Certificate_Store_Overlay extra(end_certs);
+ Certificate_Store_Overlay extra(end_certs_sharedptr);
// iterate until we reach a root or cannot find the issuer
- while(!cert_path.back().is_self_signed())
+ while(!cert_path.back()->is_self_signed())
{
- const X509_Certificate* cert = find_issuing_cert(cert_path.back(), extra, certstores);
+ std::shared_ptr<const X509_Certificate> cert = find_issuing_cert(*cert_path.back(), extra, certstores);
if(!cert)
return Path_Validation_Result(Certificate_Status_Code::CERT_ISSUER_NOT_FOUND);
@@ -249,15 +253,15 @@ Path_Validation_Result x509_path_validate(
if(certs_seen.count(fprint) > 0)
return Path_Validation_Result(Certificate_Status_Code::CERT_CHAIN_LOOP);
certs_seen.insert(fprint);
- cert_path.push_back(*cert);
+ cert_path.push_back(cert);
}
std::vector<std::set<Certificate_Status_Code>> res = check_chain(cert_path, restrictions, certstores);
- if(!hostname.empty() && !cert_path[0].matches_dns_name(hostname))
+ if(!hostname.empty() && !cert_path[0]->matches_dns_name(hostname))
res[0].insert(Certificate_Status_Code::CERT_NAME_NOMATCH);
- if(!cert_path[0].allowed_usage(usage))
+ if(!cert_path[0]->allowed_usage(usage))
res[0].insert(Certificate_Status_Code::INVALID_USAGE);
return Path_Validation_Result(res, std::move(cert_path));
@@ -321,7 +325,7 @@ Path_Validation_Restrictions::Path_Validation_Restrictions(bool require_rev,
}
Path_Validation_Result::Path_Validation_Result(std::vector<std::set<Certificate_Status_Code>> status,
- std::vector<X509_Certificate>&& cert_chain) :
+ std::vector<std::shared_ptr<const X509_Certificate>>&& cert_chain) :
m_overall(Certificate_Status_Code::VERIFIED),
m_all_status(status),
m_cert_path(cert_chain)
@@ -346,14 +350,14 @@ const X509_Certificate& Path_Validation_Result::trust_root() const
if(result() != Certificate_Status_Code::VERIFIED)
throw Exception("Path_Validation_Result::trust_root meaningless with invalid status");
- return m_cert_path[m_cert_path.size()-1];
+ return *m_cert_path[m_cert_path.size()-1];
}
std::set<std::string> Path_Validation_Result::trusted_hashes() const
{
std::set<std::string> hashes;
for(size_t i = 0; i != m_cert_path.size(); ++i)
- hashes.insert(m_cert_path[i].hash_used_for_signature());
+ hashes.insert(m_cert_path[i]->hash_used_for_signature());
return hashes;
}
diff --git a/src/lib/cert/x509/x509path.h b/src/lib/cert/x509/x509path.h
index b7061685a..60b7fa1a2 100644
--- a/src/lib/cert/x509/x509path.h
+++ b/src/lib/cert/x509/x509path.h
@@ -92,7 +92,7 @@ class BOTAN_DLL Path_Validation_Result
/**
* @return the full path from subject to trust root
*/
- const std::vector<X509_Certificate>& cert_path() const { return m_cert_path; }
+ const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path() const { return m_cert_path; }
/**
* @return true iff the validation was successful
@@ -118,7 +118,7 @@ class BOTAN_DLL Path_Validation_Result
static const char* status_string(Certificate_Status_Code code);
Path_Validation_Result(std::vector<std::set<Certificate_Status_Code>> status,
- std::vector<X509_Certificate>&& cert_chain);
+ std::vector<std::shared_ptr<const X509_Certificate>>&& cert_chain);
explicit Path_Validation_Result(Certificate_Status_Code status) : m_overall(status) {}
@@ -130,7 +130,7 @@ class BOTAN_DLL Path_Validation_Result
Certificate_Status_Code m_overall;
std::vector<std::set<Certificate_Status_Code>> m_all_status;
- std::vector<X509_Certificate> m_cert_path;
+ std::vector<std::shared_ptr<const X509_Certificate>> m_cert_path;
};
diff --git a/src/lib/utils/database.h b/src/lib/utils/database.h
index 4cc0989b1..0cd45dac0 100644
--- a/src/lib/utils/database.h
+++ b/src/lib/utils/database.h
@@ -38,6 +38,8 @@ class BOTAN_DLL SQL_Database
virtual void bind(int column, const std::vector<byte>& blob) = 0;
+ virtual void bind(int column, const byte* data, size_t len) = 0;
+
/* Get output */
virtual std::pair<const byte*, size_t> get_blob(int column) = 0;
diff --git a/src/lib/utils/sqlite3/sqlite3.cpp b/src/lib/utils/sqlite3/sqlite3.cpp
index 77b4c0d10..251cbcdf5 100644
--- a/src/lib/utils/sqlite3/sqlite3.cpp
+++ b/src/lib/utils/sqlite3/sqlite3.cpp
@@ -99,6 +99,13 @@ void Sqlite3_Database::Sqlite3_Statement::bind(int column, const std::vector<byt
throw SQL_DB_Error("sqlite3_bind_text failed, code " + std::to_string(rc));
}
+void Sqlite3_Database::Sqlite3_Statement::bind(int column, const byte* p, size_t len)
+ {
+ int rc = ::sqlite3_bind_blob(m_stmt, column, p, len, SQLITE_TRANSIENT);
+ if(rc != SQLITE_OK)
+ throw SQL_DB_Error("sqlite3_bind_text failed, code " + std::to_string(rc));
+ }
+
std::pair<const byte*, size_t> Sqlite3_Database::Sqlite3_Statement::get_blob(int column)
{
BOTAN_ASSERT(::sqlite3_column_type(m_stmt, 0) == SQLITE_BLOB,
diff --git a/src/lib/utils/sqlite3/sqlite3.h b/src/lib/utils/sqlite3/sqlite3.h
index 067b94e85..659e1c487 100644
--- a/src/lib/utils/sqlite3/sqlite3.h
+++ b/src/lib/utils/sqlite3/sqlite3.h
@@ -35,6 +35,7 @@ class BOTAN_DLL Sqlite3_Database : public SQL_Database
void bind(int column, size_t val) override;
void bind(int column, std::chrono::system_clock::time_point time) override;
void bind(int column, const std::vector<byte>& val) override;
+ void bind(int column, const byte* data, size_t len) override;
std::pair<const byte*, size_t> get_blob(int column) override;
size_t get_size_t(int column) override;