Skip to content

Commit

Permalink
allow using raw public key in ./botan tls_client/tls_server
Browse files Browse the repository at this point in the history
  • Loading branch information
reneme committed Nov 1, 2023
1 parent 30b41c7 commit ecc08d9
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 23 deletions.
24 changes: 21 additions & 3 deletions src/cli/tls_client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ class Callbacks : public Botan::TLS::Callbacks {

std::ostream& output();
bool flag_set(const std::string& flag_name) const;
std::string get_arg(const std::string& arg_name) const;
void send(std::span<const uint8_t> buffer);

int peer_closed() const { return m_peer_closed; }
Expand Down Expand Up @@ -75,6 +76,16 @@ class Callbacks : public Botan::TLS::Callbacks {
}
}

void tls_verify_raw_public_key(const Botan::Public_Key& raw_public_key,
Botan::Usage_Type /* usage */,
std::string_view /* hostname */,
const Botan::TLS::Policy& /* policy */) override {
const auto fingerprint = raw_public_key.fingerprint_public("SHA-256");
const auto trusted = (fingerprint == get_arg("trusted-pubkey-sha256"));
output() << "Raw Public Key (" << fingerprint
<< ") validation status: " << (trusted ? "trusted" : "NOT trusted") << "\n";
}

void tls_session_activated() override { output() << "Handshake complete\n"; }

void tls_session_established(const Botan::TLS::Session_Summary& session) override {
Expand Down Expand Up @@ -148,9 +159,10 @@ class TLS_Client final : public Command {
TLS_Client() :
Command(
"tls_client host --port=443 --print-certs --policy=default "
"--skip-system-cert-store --trusted-cas= --tls-version=default "
"--session-db= --session-db-pass= --next-protocols= --type=tcp "
"--client-cert= --client-cert-key= --psk= --psk-identity= --psk-prf=SHA-256 --debug") {
"--skip-system-cert-store --trusted-cas= --trusted-pubkey-sha256= "
"--tls-version=default --session-db= --session-db-pass= "
"--next-protocols= --type=tcp --client-cert= --client-cert-key= "
"--psk= --psk-identity= --psk-prf=SHA-256 --debug") {
init_sockets();
}

Expand All @@ -177,6 +189,7 @@ class TLS_Client final : public Command {
const std::string next_protos = get_arg("next-protocols");
const bool use_system_cert_store = flag_set("skip-system-cert-store") == false;
const std::string trusted_CAs = get_arg("trusted-cas");
const std::string trusted_pubkey_sha256 = get_arg("trusted-pubkey-sha256");
const auto tls_version = get_arg("tls-version");

if(!sessions_db.empty()) {
Expand Down Expand Up @@ -343,6 +356,7 @@ class TLS_Client final : public Command {

public:
using Command::flag_set;
using Command::get_arg;
using Command::output;

void send(std::span<const uint8_t> buf) const {
Expand Down Expand Up @@ -421,6 +435,10 @@ bool Callbacks::flag_set(const std::string& flag_name) const {
return m_client_command.flag_set(flag_name);
}

std::string Callbacks::get_arg(const std::string& arg_name) const {
return m_client_command.get_arg(arg_name);
}

void Callbacks::send(std::span<const uint8_t> buffer) {
m_client_command.send(buffer);
}
Expand Down
66 changes: 50 additions & 16 deletions src/cli/tls_helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <botan/hex.h>
#include <botan/pkcs8.h>
#include <botan/tls_policy.h>
#include <botan/x509_key.h>
#include <botan/x509self.h>
#include <fstream>
#include <memory>
Expand Down Expand Up @@ -42,13 +43,23 @@ inline std::string maybe_hex_encode(std::string_view v) {

class Basic_Credentials_Manager : public Botan::Credentials_Manager {
protected:
void load_credentials(const std::string& crt, const std::string& key) {
Certificate_Info cert;

void load_credentials(const std::string& cred, const std::string& key) {
Botan::DataSource_Stream key_in(key);
cert.key = Botan::PKCS8::load_key(key_in);
auto privkey = Botan::PKCS8::load_key(key_in);

Botan::DataSource_Stream in(crt);
// first try to read @p cred as a public key
try {
auto pubkey = Botan::X509::load_key(cred);
m_raw_pubkey = {std::exchange(privkey, {}), std::move(pubkey)};
return;
} catch(const Botan::Decoding_Error&) {}

// ... then try again assuming that @p cred contains a certificate chain
BOTAN_ASSERT_NONNULL(privkey);
Certificate_Info cert;
cert.key = std::move(privkey);

Botan::DataSource_Stream in(cred);
while(!in.end_of_data()) {
try {
cert.certs.push_back(Botan::X509_Certificate(in));
Expand All @@ -59,13 +70,13 @@ class Basic_Credentials_Manager : public Botan::Credentials_Manager {

// TODO: attempt to validate chain ourselves

m_creds.push_back(cert);
m_certs.push_back(cert);
}

public:
Basic_Credentials_Manager(bool use_system_store,
const std::string& ca_path,
std::optional<std::string> client_crt = std::nullopt,
std::optional<std::string> client_cred = std::nullopt,
std::optional<std::string> client_key = std::nullopt,
std::optional<Botan::secure_vector<uint8_t>> psk = std::nullopt,
std::optional<std::string> psk_identity = std::nullopt,
Expand All @@ -81,11 +92,11 @@ class Basic_Credentials_Manager : public Botan::Credentials_Manager {
m_certstores.push_back(std::make_shared<Botan::Certificate_Store_In_Memory>(ca_path));
}

BOTAN_ARG_CHECK(client_crt.has_value() == client_key.has_value(),
BOTAN_ARG_CHECK(client_cred.has_value() == client_key.has_value(),
"either provide both client certificate and key or neither");

if(client_crt.has_value() && client_key.has_value()) {
load_credentials(client_crt.value(), client_key.value());
if(client_cred.has_value() && client_key.has_value()) {
load_credentials(client_cred.value(), client_key.value());
}

#if defined(BOTAN_HAS_CERTSTOR_SYSTEM)
Expand All @@ -97,7 +108,7 @@ class Basic_Credentials_Manager : public Botan::Credentials_Manager {
#endif
}

Basic_Credentials_Manager(const std::string& server_crt,
Basic_Credentials_Manager(const std::string& server_cred,
const std::string& server_key,
std::optional<Botan::secure_vector<uint8_t>> psk = std::nullopt,
std::optional<std::string> psk_identity = std::nullopt,
Expand All @@ -109,7 +120,7 @@ class Basic_Credentials_Manager : public Botan::Credentials_Manager {
// the Hash algorithm MUST be set when the PSK is established or
// default to SHA-256 if no such algorithm is defined.
m_psk_prf(psk_prf.value_or("SHA-256")) {
load_credentials(server_crt, server_key);
load_credentials(server_cred, server_key);
}

std::vector<Botan::Certificate_Store*> trusted_certificate_authorities(const std::string& type,
Expand Down Expand Up @@ -138,14 +149,14 @@ class Basic_Credentials_Manager : public Botan::Credentials_Manager {

if(type == "tls-client") {
for(const auto& dn : acceptable_cas) {
for(const auto& cred : m_creds) {
for(const auto& cred : m_certs) {
if(dn == cred.certs[0].issuer_dn()) {
return cred.certs;
}
}
}
} else if(type == "tls-server") {
for(const auto& i : m_creds) {
for(const auto& i : m_certs) {
if(std::find(algos.begin(), algos.end(), i.key->algo_name()) == algos.end()) {
continue;
}
Expand All @@ -161,10 +172,18 @@ class Basic_Credentials_Manager : public Botan::Credentials_Manager {
return {};
}

std::shared_ptr<Botan::Public_Key> find_raw_public_key(const std::vector<std::string>& algos,
std::string_view type,
std::string_view hostname) override {
BOTAN_UNUSED(type, hostname);
return (algos.empty() || value_exists(algos, m_raw_pubkey.public_key->algo_name())) ? m_raw_pubkey.public_key
: nullptr;
}

std::shared_ptr<Botan::Private_Key> private_key_for(const Botan::X509_Certificate& cert,
const std::string& /*type*/,
const std::string& /*context*/) override {
for(const auto& i : m_creds) {
for(const auto& i : m_certs) {
if(cert == i.certs[0]) {
return i.key;
}
Expand All @@ -173,6 +192,14 @@ class Basic_Credentials_Manager : public Botan::Credentials_Manager {
return nullptr;
}

std::shared_ptr<Botan::Private_Key> private_key_for(const Botan::Public_Key& raw_public_key,
const std::string& /*type*/,
const std::string& /*context*/) override {
return (m_raw_pubkey.public_key->fingerprint_public() == raw_public_key.fingerprint_public())
? m_raw_pubkey.private_key
: nullptr;
}

std::string psk_identity(const std::string& type,
const std::string& context,
const std::string& identity_hint) override {
Expand Down Expand Up @@ -208,7 +235,14 @@ class Basic_Credentials_Manager : public Botan::Credentials_Manager {
std::shared_ptr<Botan::Private_Key> key;
};

std::vector<Certificate_Info> m_creds;
struct RawPublicKey_Info {
std::shared_ptr<Botan::Private_Key> private_key;
std::shared_ptr<Botan::Public_Key> public_key;
};

std::vector<Certificate_Info> m_certs;
RawPublicKey_Info m_raw_pubkey;

std::vector<std::shared_ptr<Botan::Certificate_Store>> m_certstores;
std::optional<Botan::secure_vector<uint8_t>> m_psk;
std::optional<std::string> m_psk_identity;
Expand Down
16 changes: 12 additions & 4 deletions src/cli/tls_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,14 @@ class Callbacks : public Botan::TLS::Callbacks {
return "echo/0.1";
}

void tls_verify_raw_public_key(const Botan::Public_Key& raw_public_key,
Botan::Usage_Type /* usage */,
std::string_view /* hostname */,
const Botan::TLS::Policy& /* policy */) override {
const auto fingerprint = raw_public_key.fingerprint_public("SHA-256");
output() << "received Raw Public Key (" << fingerprint << ")\n";
}

private:
TLS_Server& m_server_command;
std::string m_line_buf;
Expand All @@ -105,11 +113,11 @@ class TLS_Server final : public Command {
#if defined(BOTAN_SO_SOCKETID)
TLS_Server() :
Command(
"tls_server cert key --port=443 --psk= --psk-identity= --psk-prf=SHA-256 --type=tcp --policy=default --dump-traces= --max-clients=0 --socket-id=0")
"tls_server cert-or-pubkey key --port=443 --psk= --psk-identity= --psk-prf=SHA-256 --type=tcp --policy=default --dump-traces= --max-clients=0 --socket-id=0")
#else
TLS_Server() :
Command(
"tls_server cert key --port=443 --psk= --psk-identity= --psk-prf=SHA-256 --type=tcp --policy=default --dump-traces= --max-clients=0")
"tls_server cert-or-pubkey key --port=443 --psk= --psk-identity= --psk-prf=SHA-256 --type=tcp --policy=default --dump-traces= --max-clients=0")
#endif
{
init_sockets();
Expand All @@ -127,7 +135,7 @@ class TLS_Server final : public Command {
std::string description() const override { return "Accept TLS/DTLS connections from TLS/DTLS clients"; }

void go() override {
const std::string server_crt = get_arg("cert");
const std::string server_cred = get_arg("cert-or-pubkey");
const std::string server_key = get_arg("key");
const uint16_t port = get_arg_u16("port");
const size_t max_clients = get_arg_sz("max-clients");
Expand Down Expand Up @@ -158,7 +166,7 @@ class TLS_Server final : public Command {
auto session_manager =
std::make_shared<Botan::TLS::Session_Manager_In_Memory>(rng_as_shared()); // TODO sqlite3
auto creds =
std::make_shared<Basic_Credentials_Manager>(server_crt, server_key, std::move(psk), psk_identity, psk_prf);
std::make_shared<Basic_Credentials_Manager>(server_cred, server_key, std::move(psk), psk_identity, psk_prf);
auto callbacks = std::make_shared<Callbacks>(*this);

output() << "Listening for new connections on " << transport << " port " << port << std::endl;
Expand Down

0 comments on commit ecc08d9

Please sign in to comment.