Skip to content

Commit

Permalink
WIP: client-side initial key exchange. NEXT STEP: implement remaining…
Browse files Browse the repository at this point in the history
… missing CH extensions
  • Loading branch information
reneme committed Jan 17, 2022
1 parent f6a908b commit 88a602b
Show file tree
Hide file tree
Showing 6 changed files with 128 additions and 74 deletions.
2 changes: 1 addition & 1 deletion src/lib/tls/tls13/tls_client_impl_13.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ void Client_Impl_13::process_handshake_msg(const Handshake_State* previous_state

BOTAN_ASSERT_NOMSG(state.client_hello()->extensions().has<Key_Share>());
auto my_keyshare = state.client_hello()->extensions().get<Key_Share>();
my_keyshare->exchange(rng(), policy(), sh->extensions().get<Key_Share>());
const auto shared_secret = my_keyshare->exchange(sh->extensions().get<Key_Share>(), policy(), callbacks(), rng());

state.set_expected_next(ENCRYPTED_EXTENSIONS); // TODO expect CCS (middlebox compat)

Expand Down
49 changes: 49 additions & 0 deletions src/lib/tls/tls_algos.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,14 @@ std::string hash_function_of_scheme(Signature_Scheme scheme)
case Signature_Scheme::EDDSA_448:
return "Pure";

case Signature_Scheme::RSA_PKCS1_SHA1:
case Signature_Scheme::ECDSA_SHA1:
case Signature_Scheme::DSA_SHA1:
case Signature_Scheme::DSA_SHA256:
case Signature_Scheme::DSA_SHA384:
case Signature_Scheme::DSA_SHA512:
throw Invalid_State("hash_function_of_scheme: Unsupported signature scheme");

case Signature_Scheme::NONE:
return "";
}
Expand Down Expand Up @@ -250,6 +258,16 @@ bool signature_scheme_is_known(Signature_Scheme scheme)
case Signature_Scheme::ECDSA_SHA512:
return true;

// those schemes are added solely to please the TLS 1.3
// integration tests based on RFC 8448
case Signature_Scheme::RSA_PKCS1_SHA1:
case Signature_Scheme::ECDSA_SHA1:
case Signature_Scheme::DSA_SHA1:
case Signature_Scheme::DSA_SHA256:
case Signature_Scheme::DSA_SHA384:
case Signature_Scheme::DSA_SHA512:
return false;

default:
return false;
}
Expand Down Expand Up @@ -279,6 +297,16 @@ std::string signature_algorithm_of_scheme(Signature_Scheme scheme)
case Signature_Scheme::EDDSA_448:
return "Ed448";

case Signature_Scheme::DSA_SHA256:
case Signature_Scheme::DSA_SHA384:
case Signature_Scheme::DSA_SHA512:
throw Invalid_State("signature_algorithm_of_scheme: DSA signature scheme not supported");

case Signature_Scheme::DSA_SHA1:
case Signature_Scheme::ECDSA_SHA1:
case Signature_Scheme::RSA_PKCS1_SHA1:
throw Invalid_State("signature_algorithm_of_scheme: SHA1-based signature scheme not considered safe");

case Signature_Scheme::NONE:
return "";
}
Expand All @@ -290,13 +318,17 @@ std::string sig_scheme_to_string(Signature_Scheme scheme)
{
switch(scheme)
{
case Signature_Scheme::RSA_PKCS1_SHA1:
return "RSA_PKCS1_SHA1";
case Signature_Scheme::RSA_PKCS1_SHA256:
return "RSA_PKCS1_SHA256";
case Signature_Scheme::RSA_PKCS1_SHA384:
return "RSA_PKCS1_SHA384";
case Signature_Scheme::RSA_PKCS1_SHA512:
return "RSA_PKCS1_SHA512";

case Signature_Scheme::ECDSA_SHA1:
return "ECDSA_SHA1";
case Signature_Scheme::ECDSA_SHA256:
return "ECDSA_SHA256";
case Signature_Scheme::ECDSA_SHA384:
Expand All @@ -316,6 +348,15 @@ std::string sig_scheme_to_string(Signature_Scheme scheme)
case Signature_Scheme::EDDSA_448:
return "EDDSA_448";

case Signature_Scheme::DSA_SHA1:
return "DSA_SHA1";
case Signature_Scheme::DSA_SHA256:
return "DSA_SHA256";
case Signature_Scheme::DSA_SHA384:
return "DSA_SHA384";
case Signature_Scheme::DSA_SHA512:
return "DSA_SHA512";

case Signature_Scheme::NONE:
return "";
}
Expand Down Expand Up @@ -353,6 +394,14 @@ std::string padding_string_for_scheme(Signature_Scheme scheme)
case Signature_Scheme::EDDSA_448:
return "Pure";

case Signature_Scheme::RSA_PKCS1_SHA1:
case Signature_Scheme::ECDSA_SHA1:
case Signature_Scheme::DSA_SHA1:
case Signature_Scheme::DSA_SHA256:
case Signature_Scheme::DSA_SHA384:
case Signature_Scheme::DSA_SHA512:
throw Invalid_State("padding_string_for_scheme: Unsupported signature scheme");

case Signature_Scheme::NONE:
return "";
}
Expand Down
10 changes: 10 additions & 0 deletions src/lib/tls/tls_algos.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,16 @@ enum class Signature_Scheme : uint16_t {

EDDSA_25519 = 0x0807,
EDDSA_448 = 0x0808,

// provided but not actually supported
// required for TLS 1.3 test based on RFC 8448
ECDSA_SHA1 = 0x0203,
RSA_PKCS1_SHA1 = 0x0201,

DSA_SHA1 = 0x0202,
DSA_SHA256 = 0x0402,
DSA_SHA384 = 0x0502,
DSA_SHA512 = 0x0602
};

BOTAN_UNSTABLE_API const std::vector<Signature_Scheme>& all_signature_schemes();
Expand Down
4 changes: 2 additions & 2 deletions src/lib/tls/tls_extensions.h
Original file line number Diff line number Diff line change
Expand Up @@ -512,7 +512,7 @@ class Key_Share_Content
virtual bool empty() const = 0;
virtual ~Key_Share_Content() = default;

virtual secure_vector<uint8_t> exchange(RandomNumberGenerator&, const Policy&, const Key_Share_Content*) const
virtual secure_vector<uint8_t> exchange(const Key_Share_Content*, const Policy&, Callbacks&, RandomNumberGenerator&) const
{
throw Invalid_Argument("exchange should only be called on Key_Share_ClientHello");
}
Expand All @@ -539,7 +539,7 @@ class BOTAN_UNSTABLE_API Key_Share final : public Extension
bool empty() const override;

// TODO: this will only work as client_keyshare.exchange(server_keyshare) for now
secure_vector<uint8_t> exchange(RandomNumberGenerator& rng, const Policy&, const Key_Share* other) const;
secure_vector<uint8_t> exchange(const Key_Share* peer_keyshare, const Policy& policy, Callbacks& cb, RandomNumberGenerator& rng) const;

explicit Key_Share(TLS_Data_Reader& reader,
uint16_t extension_size,
Expand Down
74 changes: 35 additions & 39 deletions src/lib/tls/tls_extensions_key_share.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,53 +105,53 @@ class Key_Share_Entry
* The caller must ensure that both this and `received` have the same group.
* This method must not be called on Key_Share_Entries without a private key.
*/
SymmetricKey exchange(RandomNumberGenerator& rng, const Policy& policy, const Key_Share_Entry& received) const
secure_vector<uint8_t> exchange(const Key_Share_Entry& received, const Policy& policy, Callbacks& cb, RandomNumberGenerator &rng) const
{
BOTAN_ASSERT_NOMSG(m_private_key != nullptr);
BOTAN_ASSERT_NOMSG(m_group == received.m_group);

PK_Key_Agreement ka(m_private_key, rng, "Raw");
PK_Key_Agreement ka(*m_private_key, rng, "Raw");

if (is_ecdh(m_group))
{
const EC_Group ec_group(cb.tls_decode_group_param(m_group));
ECDH_PublicKey peer_key(ec_group, ec_group.OS2ECP(received.m_key_exchange));
policy.check_peer_key_acceptable(peer_key);
if (is_ecdh(m_group))
{
const EC_Group ec_group(cb.tls_decode_group_param(m_group));
ECDH_PublicKey peer_key(ec_group, ec_group.OS2ECP(received.m_key_exchange));
policy.check_peer_key_acceptable(peer_key);

return ka.derive_key(0, peer_key.public_value()).bits_of();
}
return ka.derive_key(0, peer_key.public_value()).bits_of();
}

if (is_dh(m_group))
{
const DL_Group dl_group(cb.tls_decode_group_param(m_group));
if (is_dh(m_group))
{
const DL_Group dl_group(cb.tls_decode_group_param(m_group));

if(!dl_group.verify_group(rng, false))
throw TLS_Exception(Alert::INSUFFICIENT_SECURITY, "DH group validation failed");
if(!dl_group.verify_group(rng, false))
throw TLS_Exception(Alert::INSUFFICIENT_SECURITY, "DH group validation failed");

DH_PublicKey peer_key(dl_group, BigInt::decode(received.m_key_exchange));
policy.check_peer_key_acceptable(peer_key);
DH_PublicKey peer_key(dl_group, BigInt::decode(received.m_key_exchange));
policy.check_peer_key_acceptable(peer_key);

// Note: in contrast to TLS 1.2, no leading zeros are stripped here
// cf. RFC 8446 7.4.1
return ka.derive_key(0, peer_key.public_value()).bits_of();
}
// Note: in contrast to TLS 1.2, no leading zeros are stripped here
// cf. RFC 8446 7.4.1
return ka.derive_key(0, peer_key.public_value()).bits_of();
}

#if defined(BOTAN_HAS_CURVE_25519)
if(is_x25519(m_group))
{
if(received.m_key_exchange.size() != 32)
if(is_x25519(m_group))
{
throw TLS_Exception(Alert::HANDSHAKE_FAILURE, "Invalid X25519 key size");
}
if(received.m_key_exchange.size() != 32)
{
throw TLS_Exception(Alert::HANDSHAKE_FAILURE, "Invalid X25519 key size");
}

Curve25519_PublicKey peer_key(received.m_key_exchange);
policy.check_peer_key_acceptable(peer_key);
Curve25519_PublicKey peer_key(received.m_key_exchange);
policy.check_peer_key_acceptable(peer_key);

return ka.derive_key(0, peer_key.public_value()).bits_of();
}
return ka.derive_key(0, peer_key.public_value()).bits_of();
}
#endif

BOTAN_ASSERT_NOMSG(false);
BOTAN_ASSERT_NOMSG(false);
}

private:
Expand Down Expand Up @@ -285,9 +285,9 @@ class Key_Share_ClientHello final : public Key_Share_Content
[](const Key_Share_Entry& key_share_entry) { return key_share_entry.empty(); });
}

secure_vector<uint8_t> exchange(RandomNumberGenerator& rng, const Policy &policy, const Key_Share_Content* other) const override
secure_vector<uint8_t> exchange(const Key_Share_Content* server_share, const Policy &policy, Callbacks&cb, RandomNumberGenerator &rng) const override
{
const auto& server_selected = other->get_singleton_entry();
const auto& server_selected = server_share->get_singleton_entry();

auto match = std::find_if(m_client_shares.cbegin(), m_client_shares.cend(),
[&server_selected](const auto& offered){ return offered.group() == server_selected.group(); });
Expand All @@ -304,11 +304,7 @@ class Key_Share_ClientHello final : public Key_Share_Content
throw TLS_Exception(Alert::ILLEGAL_PARAMETER, "Server selected an unexpected key exchange group.");
}

match->exchange(rng, policy, server_selected);


return{};

return match->exchange(server_selected, policy, cb, rng);
}

protected:
Expand Down Expand Up @@ -467,9 +463,9 @@ bool Key_Share::empty() const
return (m_content == nullptr) || m_content->empty();
}

secure_vector<uint8_t> Key_Share::exchange(RandomNumberGenerator& rng, const Policy &policy, const Key_Share* client_keyshare) const
secure_vector<uint8_t> Key_Share::exchange(const Key_Share* peer_keyshare, const Policy& policy, Callbacks& cb, RandomNumberGenerator& rng) const
{
return m_content->exchange(rng, policy, client_keyshare->m_content.get());
return m_content->exchange(peer_keyshare->m_content.get(), policy, cb, rng);
}

#endif
Expand Down
63 changes: 31 additions & 32 deletions src/tests/test_tls_rfc8448.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,33 +114,6 @@ decltype(auto) parse_extensions(const std::vector<uint8_t> &exts_buffer, Test::R
return exts;
}

void compare_signature_scheme_extensions(const std::vector<uint8_t> &produced_schemes, const std::vector<uint8_t> &expected_schemes, Test::Result &result)
{
auto preader = Botan::TLS::TLS_Data_Reader("Produced Signature_Algorithms", produced_schemes);
auto ps = Botan::TLS::Signature_Algorithms(preader, produced_schemes.size()).supported_schemes();

auto ereader = Botan::TLS::TLS_Data_Reader("Expected Signature_Algorithms", expected_schemes);
auto es = Botan::TLS::Signature_Algorithms(ereader, expected_schemes.size()).supported_schemes();

for (const auto& scheme : es)
{
if (!Botan::TLS::signature_scheme_is_known(scheme))
// do not check for schemes Botan doesn't support
continue;

if (!result.confirm("expected scheme is present", Botan::value_exists(ps, scheme)))
result.test_note(std::string("did not produce expected signature scheme: ") + Botan::TLS::sig_scheme_to_string(scheme));
}

for (const auto& scheme : ps)
{
if (!result.confirm("produced scheme was expected", Botan::value_exists(es, scheme)))
result.test_note(std::string("produced unexpected signature scheme: ") + Botan::TLS::sig_scheme_to_string(scheme));
}

// TODO: the order of schemes is not checked
}

void compare_extensions(const std::vector<uint8_t> &exts_buffer, const std::vector<uint8_t> &exp_exts_buffer, Test::Result &result)
{
const auto prod = parse_extensions(exts_buffer, result);
Expand Down Expand Up @@ -171,10 +144,7 @@ void compare_extensions(const std::vector<uint8_t> &exts_buffer, const std::vect
continue;
}

if (pext.first == Botan::TLS::Handshake_Extension_Type::TLSEXT_SIGNATURE_ALGORITHMS)
compare_signature_scheme_extensions(pext.second, eext->second, result);
else
result.test_eq(std::string("content of extension type ") + std::to_string(pext.first), pext.second, eext->second);
result.test_eq(std::string("content of extension type ") + std::to_string(pext.first), pext.second, eext->second);
}
}

Expand Down Expand Up @@ -303,6 +273,35 @@ class Test_Server_Credentials : public Botan::Credentials_Manager
Botan::RSA_PrivateKey m_key;
};

class RFC8448_Text_Policy : public Botan::TLS::Text_Policy
{
public:
RFC8448_Text_Policy(const Botan::TLS::Text_Policy& other)
: Text_Policy(other) {}

std::vector<Botan::TLS::Signature_Scheme> allowed_signature_schemes() const override
{
return
{
Botan::TLS::Signature_Scheme::ECDSA_SHA256,
Botan::TLS::Signature_Scheme::ECDSA_SHA384,
Botan::TLS::Signature_Scheme::ECDSA_SHA512,
Botan::TLS::Signature_Scheme::ECDSA_SHA1, // not actually supported
Botan::TLS::Signature_Scheme::RSA_PSS_SHA256,
Botan::TLS::Signature_Scheme::RSA_PSS_SHA384,
Botan::TLS::Signature_Scheme::RSA_PSS_SHA512,
Botan::TLS::Signature_Scheme::RSA_PKCS1_SHA256,
Botan::TLS::Signature_Scheme::RSA_PKCS1_SHA384,
Botan::TLS::Signature_Scheme::RSA_PKCS1_SHA512,
Botan::TLS::Signature_Scheme::RSA_PKCS1_SHA1, // not actually supported
Botan::TLS::Signature_Scheme::DSA_SHA256, // not actually supported
Botan::TLS::Signature_Scheme::DSA_SHA384, // not actually supported
Botan::TLS::Signature_Scheme::DSA_SHA512, // not actually supported
Botan::TLS::Signature_Scheme::DSA_SHA1 // not actually supported
};
}
};

class TLS_Context
{
protected:
Expand All @@ -323,7 +322,7 @@ class TLS_Context

std::unique_ptr<Botan::RandomNumberGenerator> rng;
Botan::TLS::Session_Manager_In_Memory session_mgr;
Botan::TLS::Text_Policy policy;
RFC8448_Text_Policy policy;
};

class Server_Context : public TLS_Context
Expand Down

0 comments on commit 88a602b

Please sign in to comment.