Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TLS 1.3] Extend CredentialsManager for upcoming TLS 1.3 PSK #3622

Merged
merged 3 commits into from
Jul 18, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 48 additions & 17 deletions src/bogo_shim/bogo_shim.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1228,10 +1228,18 @@ std::vector<uint16_t> Shim_Policy::ciphersuite_list(Botan::TLS::Protocol_Version
class Shim_Credentials final : public Botan::Credentials_Manager {
public:
Shim_Credentials(const Shim_Arguments& args) : m_args(args) {
m_psk_identity = m_args.get_string_opt_or_else("psk-identity", "");
const auto psk_identity = m_args.get_string_opt_or_else("psk-identity", "");
const auto psk_str = m_args.get_string_opt_or_else("psk", "");

const std::string psk_str = m_args.get_string_opt_or_else("psk", "");
m_psk = Botan::SymmetricKey(reinterpret_cast<const uint8_t*>(psk_str.data()), psk_str.size());
if(!psk_identity.empty() || !psk_str.empty()) {
// If the shim received a -psk param but no -psk-identity param,
// we have to initialize the identity as "empty string".
m_psk_identity = psk_identity;
}

if(!psk_str.empty()) {
m_psk = Botan::SymmetricKey(reinterpret_cast<const uint8_t*>(psk_str.data()), psk_str.size());
}

if(m_args.option_used("key-file") && m_args.option_used("cert-file")) {
Botan::DataSource_Stream key_stream(m_args.get_string_opt("key-file"));
Expand Down Expand Up @@ -1267,28 +1275,51 @@ class Shim_Credentials final : public Botan::Credentials_Manager {
std::string psk_identity(const std::string& /*type*/,
const std::string& /*context*/,
const std::string& /*identity_hint*/) override {
return m_psk_identity;
return m_psk_identity.value_or("");
}

std::string psk_identity_hint(const std::string& /*type*/, const std::string& /*context*/) override {
return m_psk_identity;
return m_psk_identity.value_or("");
}

Botan::SymmetricKey psk(const std::string& type,
const std::string& context,
const std::string& identity) override {
if(type == "tls-server" && context == "session-ticket") {
return Botan::SymmetricKey("ABCDEF0123456789");
}
Botan::secure_vector<uint8_t> session_ticket_key() override {
return Botan::hex_decode_locked("ABCDEF0123456789");
}

Botan::secure_vector<uint8_t> dtls_cookie_secret() override {
return Botan::hex_decode_locked("F00FB00FD00F100F700F");
}

if(type == "tls-server" && context == "dtls-cookie-secret") {
return Botan::SymmetricKey("F00FB00FD00F100F700F");
std::vector<Botan::TLS::ExternalPSK> find_preshared_keys(
std::string_view host,
Botan::TLS::Connection_Side whoami,
const std::vector<std::string>& identities = {},
const std::optional<std::string>& prf = std::nullopt) override {
if(!m_psk_identity.has_value()) {
return Botan::Credentials_Manager::find_preshared_keys(host, whoami, identities, prf);
}

if(identity != m_psk_identity) {
auto id_matches =
identities.empty() || std::find(identities.begin(), identities.end(), m_psk_identity) != identities.end();

if(!id_matches) {
throw Shim_Exception("Unexpected PSK identity");
}
return m_psk;

if(!m_psk.has_value()) {
throw Shim_Exception("PSK identified but not set");
}

std::vector<Botan::TLS::ExternalPSK> psks;

// Currently, BoGo tests PSK with TLS 1.2 only. In TLS 1.2 the PRF does not
// need to be specified for PSKs.
//
// TODO: Once BoGo has tests for TLS 1.3 with externally provided PSKs, this
// will need to be handled somehow.
const std::string psk_prf = "SHA-256";
psks.emplace_back(m_psk_identity.value(), psk_prf, m_psk->bits_of());
return psks;
}

std::vector<Botan::X509_Certificate> cert_chain(
Expand Down Expand Up @@ -1320,8 +1351,8 @@ class Shim_Credentials final : public Botan::Credentials_Manager {

private:
const Shim_Arguments& m_args;
Botan::SymmetricKey m_psk;
std::string m_psk_identity;
std::optional<Botan::SymmetricKey> m_psk;
std::optional<std::string> m_psk_identity;
std::shared_ptr<Botan::Private_Key> m_key;
std::vector<Botan::X509_Certificate> m_cert_chain;
};
Expand Down
6 changes: 3 additions & 3 deletions src/cli/tls_client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -215,18 +215,18 @@ class TLS_Client final : public Command {
const auto client_crt_path = get_arg_maybe("client-cert");
const auto client_key_path = get_arg_maybe("client-cert-key");

const auto psk = [this]() -> std::optional<Botan::SymmetricKey> {
auto psk = [this]() -> std::optional<Botan::secure_vector<uint8_t>> {
auto psk_hex = get_arg_maybe("psk");
if(psk_hex) {
return Botan::SymmetricKey(Botan::hex_decode_locked(psk_hex.value()));
return Botan::hex_decode_locked(psk_hex.value());
} else {
return {};
}
}();
const std::optional<std::string> psk_identity = get_arg_maybe("psk-identity");

auto creds = std::make_shared<Basic_Credentials_Manager>(
use_system_cert_store, trusted_CAs, client_crt_path, client_key_path, psk, psk_identity);
use_system_cert_store, trusted_CAs, client_crt_path, client_key_path, std::move(psk), psk_identity);

Botan::TLS::Client client(callbacks,
session_mgr,
Expand Down
33 changes: 23 additions & 10 deletions src/cli/tls_helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ class Basic_Credentials_Manager : public Botan::Credentials_Manager {
std::string ca_path,
std::optional<std::string> client_crt = std::nullopt,
std::optional<std::string> client_key = std::nullopt,
std::optional<Botan::SymmetricKey> psk = std::nullopt,
std::optional<Botan::secure_vector<uint8_t>> psk = std::nullopt,
std::optional<std::string> psk_identity = std::nullopt) :
m_psk(std::move(psk)), m_psk_identity(std::move(psk_identity)) {
if(ca_path.empty() == false) {
Expand All @@ -81,7 +81,7 @@ class Basic_Credentials_Manager : public Botan::Credentials_Manager {

Basic_Credentials_Manager(const std::string& server_crt,
const std::string& server_key,
std::optional<Botan::SymmetricKey> psk = std::nullopt,
std::optional<Botan::secure_vector<uint8_t>> psk = std::nullopt,
std::optional<std::string> psk_identity = std::nullopt) :
m_psk(std::move(psk)), m_psk_identity(std::move(psk_identity)) {
load_credentials(server_crt, server_key);
Expand Down Expand Up @@ -155,13 +155,25 @@ class Basic_Credentials_Manager : public Botan::Credentials_Manager {
: Botan::Credentials_Manager::psk_identity(type, context, identity_hint);
}

Botan::SymmetricKey psk(const std::string& type,
const std::string& context,
const std::string& identity) override {
return (m_psk && m_psk_identity && identity == m_psk_identity.value() &&
(type == "tls-client" || type == "tls-server"))
? m_psk.value()
: Botan::Credentials_Manager::psk(type, context, identity);
std::vector<Botan::TLS::ExternalPSK> find_preshared_keys(
std::string_view host,
Botan::TLS::Connection_Side peer_type,
const std::vector<std::string>& ids = {},
const std::optional<std::string>& prf = std::nullopt) override {
if(!m_psk.has_value() || !m_psk_identity.has_value()) {
return Botan::Credentials_Manager::find_preshared_keys(host, peer_type, ids, prf);
}

std::vector<Botan::TLS::ExternalPSK> psks;

const bool prf_matches = !prf.has_value() || m_psk_prf == prf.value();
const bool id_matches = ids.empty() || std::find(ids.begin(), ids.end(), m_psk_identity.value()) != ids.end();

if(prf_matches && id_matches) {
psks.emplace_back(m_psk_identity.value(), m_psk_prf, m_psk.value());
}

return psks;
}

private:
Expand All @@ -172,8 +184,9 @@ class Basic_Credentials_Manager : public Botan::Credentials_Manager {

std::vector<Certificate_Info> m_creds;
std::vector<std::shared_ptr<Botan::Certificate_Store>> m_certstores;
std::optional<Botan::SymmetricKey> m_psk;
std::optional<Botan::secure_vector<uint8_t>> m_psk;
std::optional<std::string> m_psk_identity;
std::string m_psk_prf;
};

class TLS_All_Policy final : public Botan::TLS::Policy {
Expand Down
6 changes: 3 additions & 3 deletions src/cli/tls_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,10 +127,10 @@ class TLS_Server final : public Command {
const size_t max_clients = get_arg_sz("max-clients");
const std::string transport = get_arg("type");
const std::string dump_traces_to = get_arg("dump-traces");
const auto psk = [this]() -> std::optional<Botan::SymmetricKey> {
auto psk = [this]() -> std::optional<Botan::secure_vector<uint8_t>> {
auto psk_hex = get_arg_maybe("psk");
if(psk_hex) {
return Botan::SymmetricKey(Botan::hex_decode_locked(psk_hex.value()));
return Botan::hex_decode_locked(psk_hex.value());
} else {
return {};
}
Expand All @@ -149,7 +149,7 @@ class TLS_Server final : public Command {
auto policy = load_tls_policy(get_arg("policy"));
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, psk, psk_identity);
auto creds = std::make_shared<Basic_Credentials_Manager>(server_crt, server_key, std::move(psk), psk_identity);
auto callbacks = std::make_shared<Callbacks>(*this);

output() << "Listening for new connections on " << transport << " port " << port << std::endl;
Expand Down
23 changes: 21 additions & 2 deletions src/fuzzer/tls_client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include "fuzzers.h"

#include <botan/hex.h>
#include <botan/tls_client.h>
#include <botan/tls_session_manager_noop.h>

Expand All @@ -15,8 +16,26 @@ class Fuzzer_TLS_Client_Creds : public Botan::Credentials_Manager {

std::string psk_identity(const std::string&, const std::string&, const std::string&) override { return "psk_id"; }

Botan::SymmetricKey psk(const std::string&, const std::string&, const std::string&) override {
return Botan::SymmetricKey("AABBCCDDEEFF00112233445566778899");
Botan::secure_vector<uint8_t> session_ticket_key() override {
return Botan::hex_decode_locked("AABBCCDDEEFF00112233445566778899");
}

Botan::secure_vector<uint8_t> dtls_cookie_secret() override {
return Botan::hex_decode_locked("AABBCCDDEEFF00112233445566778899");
}

std::vector<Botan::TLS::ExternalPSK> find_preshared_keys(
std::string_view host,
Botan::TLS::Connection_Side whoami,
const std::vector<std::string>& identities = {},
const std::optional<std::string>& prf = std::nullopt) override {
if(!identities.empty() && std::find(identities.begin(), identities.end(), "psk_id") == identities.end()) {
return Botan::Credentials_Manager::find_preshared_keys(host, whoami, identities, prf);
}

std::vector<Botan::TLS::ExternalPSK> psks;
psks.emplace_back("psk_id", "SHA-256", Botan::hex_decode_locked("AABBCCDDEEFF00112233445566778899"));
return psks;
reneme marked this conversation as resolved.
Show resolved Hide resolved
}
};

Expand Down
23 changes: 21 additions & 2 deletions src/fuzzer/tls_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "fuzzers.h"

#include <botan/data_src.h>
#include <botan/hex.h>
#include <botan/pkcs8.h>
#include <botan/tls_server.h>
#include <botan/tls_session_manager_noop.h>
Expand Down Expand Up @@ -103,12 +104,30 @@ class Fuzzer_TLS_Server_Creds : public Botan::Credentials_Manager {
return nullptr;
}

Botan::secure_vector<uint8_t> session_ticket_key() override {
return Botan::hex_decode_locked("AABBCCDDEEFF00112233445566778899");
}

Botan::secure_vector<uint8_t> dtls_cookie_secret() override {
return Botan::hex_decode_locked("AABBCCDDEEFF00112233445566778899");
}

std::string psk_identity_hint(const std::string&, const std::string&) override { return "psk_hint"; }

std::string psk_identity(const std::string&, const std::string&, const std::string&) override { return "psk_id"; }

Botan::SymmetricKey psk(const std::string&, const std::string&, const std::string&) override {
return Botan::SymmetricKey("AABBCCDDEEFF00112233445566778899");
std::vector<Botan::TLS::ExternalPSK> find_preshared_keys(
std::string_view host,
Botan::TLS::Connection_Side whoami,
const std::vector<std::string>& identities = {},
const std::optional<std::string>& prf = std::nullopt) override {
if(!identities.empty() && std::find(identities.begin(), identities.end(), "psk_id") == identities.end()) {
return Botan::Credentials_Manager::find_preshared_keys(host, whoami, identities, prf);
}

std::vector<Botan::TLS::ExternalPSK> psks;
psks.emplace_back("psk_id", "SHA-256", Botan::hex_decode_locked("AABBCCDDEEFF00112233445566778899"));
return psks;
reneme marked this conversation as resolved.
Show resolved Hide resolved
}

private:
Expand Down
65 changes: 62 additions & 3 deletions src/lib/tls/credentials_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <botan/credentials_manager.h>

#include <botan/pkix_types.h>
#include <botan/internal/fmt.h>

namespace Botan {

Expand All @@ -21,10 +22,60 @@ std::string Credentials_Manager::psk_identity(const std::string& /*unused*/,
return "";
}

SymmetricKey Credentials_Manager::psk(const std::string& /*unused*/,
const std::string& /*unused*/,
SymmetricKey Credentials_Manager::psk(const std::string& type,
const std::string& context,
const std::string& identity) {
throw Internal_Error("No PSK set for identity " + identity);
auto side = [&] {
if(type == "tls-client") {
return TLS::Connection_Side::Client;
} else if(type == "tls-server") {
return TLS::Connection_Side::Server;
} else {
throw Internal_Error(fmt("No PSK set for type {}", type));
}
}();

// New applications should use the appropriate credentials methods. This is a
// retrofit of the behaviour before Botan 3.2.0 and will be removed in a
// future major release.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should note this in doc/deprecated.rst

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done: c575c31

Please merge at will, if the text in doc/deprecated.rst is sufficient.

//
// TODO: deprecate `psk("...", "session-ticket" | "dtls-cookie-secret")`
if(side == TLS::Connection_Side::Server && context == "session-ticket") {
if(auto key = session_ticket_key(); !key.empty()) {
return SymmetricKey(std::move(key));
}
} else if(side == TLS::Connection_Side::Server && context == "dtls-cookie-secret") {
if(auto key = dtls_cookie_secret(); !key.empty()) {
return SymmetricKey(std::move(key));
}
} else /* context is a host name */ {
// Assuming that find_preshared_keys returns _exactly_ one or no keys when
// searching for a single specific identity.
if(auto psks = find_preshared_keys(context, side, {identity}); psks.size() == 1) {
return SymmetricKey(psks.front().extract_master_secret());
}
}

throw Internal_Error(fmt("No PSK set for identity {}", identity));
}

std::vector<TLS::ExternalPSK> Credentials_Manager::find_preshared_keys(std::string_view /* host */,
TLS::Connection_Side /* whoami */,
const std::vector<std::string>& /* identities */,
const std::optional<std::string>& /* prf */) {
return {};
}

std::optional<TLS::ExternalPSK> Credentials_Manager::choose_preshared_key(std::string_view host,
TLS::Connection_Side whoami,
const std::vector<std::string>& identities,
const std::optional<std::string>& prf) {
auto psks = find_preshared_keys(host, whoami, identities, prf);
if(psks.empty()) {
return std::nullopt;
} else {
return std::move(psks.front());
}
}

std::vector<X509_Certificate> Credentials_Manager::find_cert_chain(
Expand Down Expand Up @@ -57,6 +108,14 @@ std::shared_ptr<Private_Key> Credentials_Manager::private_key_for(const X509_Cer
return std::shared_ptr<Private_Key>();
}

secure_vector<uint8_t> Credentials_Manager::session_ticket_key() {
return {};
}

secure_vector<uint8_t> Credentials_Manager::dtls_cookie_secret() {
return {};
}

std::vector<Certificate_Store*> Credentials_Manager::trusted_certificate_authorities(const std::string& /*unused*/,
const std::string& /*unused*/) {
return std::vector<Certificate_Store*>();
Expand Down
Loading