aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHarry Reimann <[email protected]>2017-11-30 15:22:11 +0100
committerHarry Reimann <[email protected]>2017-12-04 10:54:14 +0100
commit6299685d6c118bd2125fd532e6f5d2258efd9f0d (patch)
treeb86b96abaddb826487f277c38680f63a25638572
parent805bb27dff20e491e76142db2b5fe1bd586d4788 (diff)
Move TLS signature and key exchange code into callbacks
Give applications using an external crypto device for signature generation and/or verification and/or (ec)dh key exchange while establishing a TLS session hooks to implement the corresponding functionality.
-rw-r--r--src/lib/tls/msg_cert_verify.cpp11
-rw-r--r--src/lib/tls/msg_client_kex.cpp94
-rw-r--r--src/lib/tls/msg_server_kex.cpp23
-rw-r--r--src/lib/tls/tls_callbacks.cpp116
-rw-r--r--src/lib/tls/tls_callbacks.h85
-rw-r--r--src/lib/tls/tls_handshake_state.cpp1
-rw-r--r--src/lib/tls/tls_handshake_state.h3
7 files changed, 237 insertions, 96 deletions
diff --git a/src/lib/tls/msg_cert_verify.cpp b/src/lib/tls/msg_cert_verify.cpp
index 4b7ba5662..e4b764b29 100644
--- a/src/lib/tls/msg_cert_verify.cpp
+++ b/src/lib/tls/msg_cert_verify.cpp
@@ -29,9 +29,9 @@ Certificate_Verify::Certificate_Verify(Handshake_IO& io,
std::pair<std::string, Signature_Format> format =
state.choose_sig_format(*priv_key, m_hash_algo, m_sig_algo, true, policy);
- PK_Signer signer(*priv_key, rng, format.first, format.second);
-
- m_signature = signer.sign_message(state.hash().get_contents(), rng);
+ m_signature =
+ state.callbacks().tls_sign_message(*priv_key, rng, format.first, format.second,
+ state.hash().get_contents());
state.hash().update(io.send(*this));
}
@@ -89,10 +89,9 @@ bool Certificate_Verify::verify(const X509_Certificate& cert,
state.parse_sig_format(*key.get(), m_hash_algo, m_sig_algo,
true, policy);
- PK_Verifier verifier(*key, format.first, format.second);
-
const bool signature_valid =
- verifier.verify_message(state.hash().get_contents(), m_signature);
+ state.callbacks().tls_verify_message(*key, format.first, format.second,
+ state.hash().get_contents(), m_signature);
#if defined(BOTAN_UNSAFE_FUZZER_MODE)
return true;
diff --git a/src/lib/tls/msg_client_kex.cpp b/src/lib/tls/msg_client_kex.cpp
index 51040e479..64e39a840 100644
--- a/src/lib/tls/msg_client_kex.cpp
+++ b/src/lib/tls/msg_client_kex.cpp
@@ -7,7 +7,6 @@
#include <botan/tls_messages.h>
#include <botan/tls_extensions.h>
-#include <botan/oids.h>
#include <botan/rng.h>
#include <botan/internal/tls_reader.h>
@@ -17,16 +16,8 @@
#include <botan/credentials_manager.h>
#include <botan/internal/ct_utils.h>
-#include <botan/pubkey.h>
-
-#include <botan/dh.h>
-#include <botan/ecdh.h>
#include <botan/rsa.h>
-#if defined(BOTAN_HAS_CURVE_25519)
- #include <botan/curve25519.h>
-#endif
-
#if defined(BOTAN_HAS_CECPQ1)
#include <botan/cecpq1.h>
#endif
@@ -94,49 +85,25 @@ Client_Key_Exchange::Client_Key_Exchange(Handshake_IO& io,
if(kex_algo == "DH" || kex_algo == "DHE_PSK")
{
- BigInt p = BigInt::decode(reader.get_range<uint8_t>(2, 1, 65535));
- BigInt g = BigInt::decode(reader.get_range<uint8_t>(2, 1, 65535));
- BigInt Y = BigInt::decode(reader.get_range<uint8_t>(2, 1, 65535));
+ const std::vector<uint8_t> modulus = reader.get_range<uint8_t>(2, 1, 65535);
+ const std::vector<uint8_t> generator = reader.get_range<uint8_t>(2, 1, 65535);
+ const std::vector<uint8_t> peer_public_value = reader.get_range<uint8_t>(2, 1, 65535);
if(reader.remaining_bytes())
throw Decoding_Error("Bad params size for DH key exchange");
- /*
- * A basic check for key validity. As we do not know q here we
- * cannot check that Y is in the right subgroup. However since
- * our key is ephemeral there does not seem to be any
- * advantage to bogus keys anyway.
- */
- if(Y <= 1 || Y >= p - 1)
- throw TLS_Exception(Alert::INSUFFICIENT_SECURITY,
- "Server sent bad DH key for DHE exchange");
-
- DL_Group group(p, g);
-
- if(!group.verify_group(rng, false))
- throw TLS_Exception(Alert::INSUFFICIENT_SECURITY,
- "DH group validation failed");
-
- DH_PublicKey counterparty_key(group, Y);
-
- policy.check_peer_key_acceptable(counterparty_key);
-
- DH_PrivateKey priv_key(rng, group);
-
- PK_Key_Agreement ka(priv_key, rng, "Raw");
-
- secure_vector<uint8_t> dh_secret = CT::strip_leading_zeros(
- ka.derive_key(0, counterparty_key.public_value()).bits_of());
+ const std::pair<secure_vector<uint8_t>, std::vector<uint8_t>> dh_result =
+ state.callbacks().tls_dh_agree(modulus, generator, peer_public_value, policy, rng);
if(kex_algo == "DH")
- m_pre_master = dh_secret;
+ m_pre_master = dh_result.first;
else
{
- append_tls_length_value(m_pre_master, dh_secret, 2);
+ append_tls_length_value(m_pre_master, dh_result.first, 2);
append_tls_length_value(m_pre_master, psk.bits_of(), 2);
}
- append_tls_length_value(m_key_material, priv_key.public_value(), 2);
+ append_tls_length_value(m_key_material, dh_result.second, 2);
}
else if(kex_algo == "ECDH" || kex_algo == "ECDHE_PSK")
{
@@ -158,51 +125,20 @@ Client_Key_Exchange::Client_Key_Exchange(Handshake_IO& io,
"Server sent ECC curve prohibited by policy");
}
- const std::vector<uint8_t> ecdh_key = reader.get_range<uint8_t>(1, 1, 255);
- std::vector<uint8_t> our_ecdh_public;
- secure_vector<uint8_t> ecdh_secret;
-
- if(curve_name == "x25519")
- {
-#if defined(BOTAN_HAS_CURVE_25519)
- if(ecdh_key.size() != 32)
- throw TLS_Exception(Alert::HANDSHAKE_FAILURE, "Invalid X25519 key size");
-
- Curve25519_PublicKey counterparty_key(ecdh_key);
- policy.check_peer_key_acceptable(counterparty_key);
- Curve25519_PrivateKey priv_key(rng);
- PK_Key_Agreement ka(priv_key, rng, "Raw");
- ecdh_secret = ka.derive_key(0, counterparty_key.public_value()).bits_of();
-
- // X25519 is always compressed but sent as "uncompressed" in TLS
- our_ecdh_public = priv_key.public_value();
-#else
- throw Internal_Error("Negotiated X25519 somehow, but it is disabled");
-#endif
- }
- else
- {
- EC_Group group(OIDS::lookup(curve_name));
- ECDH_PublicKey counterparty_key(group, OS2ECP(ecdh_key, group.get_curve()));
- policy.check_peer_key_acceptable(counterparty_key);
- ECDH_PrivateKey priv_key(rng, group);
- PK_Key_Agreement ka(priv_key, rng, "Raw");
- ecdh_secret = ka.derive_key(0, counterparty_key.public_value()).bits_of();
-
- // follow server's preference for point compression
- our_ecdh_public = priv_key.public_value(
- state.server_hello()->prefers_compressed_ec_points() ? PointGFp::COMPRESSED : PointGFp::UNCOMPRESSED);
- }
+ const std::vector<uint8_t> peer_public_value = reader.get_range<uint8_t>(1, 1, 255);
+ const std::pair<secure_vector<uint8_t>, std::vector<uint8_t>> ecdh_result =
+ state.callbacks().tls_ecdh_agree(curve_name, peer_public_value, policy, rng,
+ state.server_hello()->prefers_compressed_ec_points());
if(kex_algo == "ECDH")
- m_pre_master = ecdh_secret;
+ m_pre_master = ecdh_result.first;
else
{
- append_tls_length_value(m_pre_master, ecdh_secret, 2);
+ append_tls_length_value(m_pre_master, ecdh_result.first, 2);
append_tls_length_value(m_pre_master, psk.bits_of(), 2);
}
- append_tls_length_value(m_key_material, our_ecdh_public, 1);
+ append_tls_length_value(m_key_material, ecdh_result.second, 1);
}
#if defined(BOTAN_HAS_SRP6)
else if(kex_algo == "SRP_SHA")
diff --git a/src/lib/tls/msg_server_kex.cpp b/src/lib/tls/msg_server_kex.cpp
index ab75d3a9b..4f90cc8b3 100644
--- a/src/lib/tls/msg_server_kex.cpp
+++ b/src/lib/tls/msg_server_kex.cpp
@@ -185,12 +185,14 @@ Server_Key_Exchange::Server_Key_Exchange(Handshake_IO& io,
std::pair<std::string, Signature_Format> format =
state.choose_sig_format(*signing_key, m_hash_algo, m_sig_algo, false, policy);
- PK_Signer signer(*signing_key, rng, format.first, format.second);
+ std::vector<uint8_t> buf = state.client_hello()->random();
- signer.update(state.client_hello()->random());
- signer.update(state.server_hello()->random());
- signer.update(params());
- m_signature = signer.signature(rng);
+ buf += state.server_hello()->random();
+ buf += params();
+
+ m_signature =
+ state.callbacks().tls_sign_message(*signing_key, rng,
+ format.first, format.second, buf);
}
state.hash().update(io.send(*this));
@@ -300,13 +302,14 @@ bool Server_Key_Exchange::verify(const Public_Key& server_key,
state.parse_sig_format(server_key, m_hash_algo, m_sig_algo,
false, policy);
- PK_Verifier verifier(server_key, format.first, format.second);
+ std::vector<uint8_t> buf = state.client_hello()->random();
- verifier.update(state.client_hello()->random());
- verifier.update(state.server_hello()->random());
- verifier.update(params());
+ buf += state.server_hello()->random();
+ buf += params();
- const bool signature_valid = verifier.check_signature(m_signature);
+ const bool signature_valid =
+ state.callbacks().tls_verify_message(server_key, format.first, format.second,
+ buf, m_signature);
#if defined(BOTAN_UNSAFE_FUZZER_MODE)
return true;
diff --git a/src/lib/tls/tls_callbacks.cpp b/src/lib/tls/tls_callbacks.cpp
index 59620aec2..b4ffc81a5 100644
--- a/src/lib/tls/tls_callbacks.cpp
+++ b/src/lib/tls/tls_callbacks.cpp
@@ -9,6 +9,15 @@
#include <botan/tls_policy.h>
#include <botan/x509path.h>
#include <botan/ocsp.h>
+#include <botan/dh.h>
+#include <botan/ecdh.h>
+#include <botan/oids.h>
+#include <botan/tls_exceptn.h>
+#include <botan/internal/ct_utils.h>
+
+#if defined(BOTAN_HAS_CURVE_25519)
+ #include <botan/curve25519.h>
+#endif
namespace Botan {
@@ -50,4 +59,111 @@ void TLS::Callbacks::tls_verify_cert_chain(
throw Exception("Certificate validation failure: " + result.result_string());
}
+std::vector<uint8_t> TLS::Callbacks::tls_sign_message(
+ const Private_Key& key,
+ RandomNumberGenerator& rng,
+ const std::string& emsa,
+ Signature_Format format,
+ const std::vector<uint8_t>& msg)
+ {
+ PK_Signer signer(key, rng, emsa, format);
+
+ return signer.sign_message(msg, rng);
+ }
+
+bool TLS::Callbacks::tls_verify_message(
+ const Public_Key& key,
+ const std::string& emsa,
+ Signature_Format format,
+ const std::vector<uint8_t>& msg,
+ const std::vector<uint8_t>& sig)
+ {
+ PK_Verifier verifier(key, emsa, format);
+
+ return verifier.verify_message(msg, sig);
+ }
+
+std::pair<secure_vector<uint8_t>, std::vector<uint8_t>> TLS::Callbacks::tls_dh_agree(
+ const std::vector<uint8_t>& modulus,
+ const std::vector<uint8_t>& generator,
+ const std::vector<uint8_t>& peer_public_value,
+ const Policy& policy,
+ RandomNumberGenerator& rng)
+ {
+ BigInt p = BigInt::decode(modulus);
+ BigInt g = BigInt::decode(generator);
+ BigInt Y = BigInt::decode(peer_public_value);
+
+ /*
+ * A basic check for key validity. As we do not know q here we
+ * cannot check that Y is in the right subgroup. However since
+ * our key is ephemeral there does not seem to be any
+ * advantage to bogus keys anyway.
+ */
+ if(Y <= 1 || Y >= p - 1)
+ throw TLS_Exception(Alert::INSUFFICIENT_SECURITY,
+ "Server sent bad DH key for DHE exchange");
+
+ DL_Group group(p, g);
+
+ if(!group.verify_group(rng, false))
+ throw TLS_Exception(Alert::INSUFFICIENT_SECURITY,
+ "DH group validation failed");
+
+ DH_PublicKey peer_key(group, Y);
+
+ policy.check_peer_key_acceptable(peer_key);
+
+ DH_PrivateKey priv_key(rng, group);
+ PK_Key_Agreement ka(priv_key, rng, "Raw");
+ secure_vector<uint8_t> dh_secret = CT::strip_leading_zeros(
+ ka.derive_key(0, peer_key.public_value()).bits_of());
+
+ return std::make_pair(dh_secret, priv_key.public_value());
+ }
+
+std::pair<secure_vector<uint8_t>, std::vector<uint8_t>> TLS::Callbacks::tls_ecdh_agree(
+ const std::string& curve_name,
+ const std::vector<uint8_t>& peer_public_value,
+ const Policy& policy,
+ RandomNumberGenerator& rng,
+ bool compressed)
+ {
+ secure_vector<uint8_t> ecdh_secret;
+ std::vector<uint8_t> our_public_value;
+
+ if(curve_name == "x25519")
+ {
+#if defined(BOTAN_HAS_CURVE_25519)
+ if(peer_public_value.size() != 32)
+ {
+ throw TLS_Exception(Alert::HANDSHAKE_FAILURE, "Invalid X25519 key size");
+ }
+
+ Curve25519_PublicKey peer_key(peer_public_value);
+ policy.check_peer_key_acceptable(peer_key);
+ Curve25519_PrivateKey priv_key(rng);
+ PK_Key_Agreement ka(priv_key, rng, "Raw");
+ ecdh_secret = ka.derive_key(0, peer_key.public_value()).bits_of();
+
+ // X25519 is always compressed but sent as "uncompressed" in TLS
+ our_public_value = priv_key.public_value();
+#else
+ throw Internal_Error("Negotiated X25519 somehow, but it is disabled");
+#endif
+ }
+ else
+ {
+ EC_Group group(OIDS::lookup(curve_name));
+ ECDH_PublicKey peer_key(group, OS2ECP(peer_public_value, group.get_curve()));
+ policy.check_peer_key_acceptable(peer_key);
+ ECDH_PrivateKey priv_key(rng, group);
+ PK_Key_Agreement ka(priv_key, rng, "Raw");
+ ecdh_secret = ka.derive_key(0, peer_key.public_value()).bits_of();
+ our_public_value = priv_key.public_value(compressed ? PointGFp::COMPRESSED : PointGFp::UNCOMPRESSED);
+ }
+
+ return std::make_pair(ecdh_secret, our_public_value);
+ }
+
}
diff --git a/src/lib/tls/tls_callbacks.h b/src/lib/tls/tls_callbacks.h
index 962bb2489..cbd514050 100644
--- a/src/lib/tls/tls_callbacks.h
+++ b/src/lib/tls/tls_callbacks.h
@@ -11,6 +11,7 @@
#include <botan/tls_session.h>
#include <botan/tls_alert.h>
+#include <botan/pubkey.h>
#include <functional>
namespace Botan {
@@ -140,6 +141,90 @@ class BOTAN_PUBLIC_API(2,0) Callbacks
}
/**
+ * Optional callback with default impl: sign a message
+ *
+ * Default implementation uses PK_Signer::sign_message().
+ * Override to provide a different approach, e.g. using an external device.
+ *
+ * @param key the private key of the signer
+ * @param rng a random number generator
+ * @param emsa the encoding method to be applied to the message
+ * @param format the signature format
+ * @param msg the input data for the signature
+ *
+ * @return the signature
+ */
+ virtual std::vector<uint8_t> tls_sign_message(
+ const Private_Key& key,
+ RandomNumberGenerator& rng,
+ const std::string& emsa,
+ Signature_Format format,
+ const std::vector<uint8_t>& msg);
+
+ /**
+ * Optional callback with default impl: verify a message signature
+ *
+ * Default implementation uses PK_Verifier::verify_message().
+ * Override to provide a different approach, e.g. using an external device.
+ *
+ * @param key the public key of the signer
+ * @param emsa the encoding method to be applied to the message
+ * @param format the signature format
+ * @param msg the input data for the signature
+ * @param sig the signature to be checked
+ *
+ * @return true if the signature is valid, false otherwise
+ */
+ virtual bool tls_verify_message(
+ const Public_Key& key,
+ const std::string& emsa,
+ Signature_Format format,
+ const std::vector<uint8_t>& msg,
+ const std::vector<uint8_t>& sig);
+
+ /**
+ * Optional callback with default impl: client side DH agreement
+ *
+ * Default implementation uses PK_Key_Agreement::derive_key().
+ * Override to provide a different approach, e.g. using an external device.
+ *
+ * @param modulus the modulus p of the discrete logarithm group
+ * @param generator the generator of the DH subgroup
+ * @param peer_public_value the public value of the peer
+ * @param policy the TLS policy associated with the session being established
+ * @param rng a random number generator
+ *
+ * @return a pair consisting of the agreed raw secret and our public value
+ */
+ virtual std::pair<secure_vector<uint8_t>, std::vector<uint8_t>> tls_dh_agree(
+ const std::vector<uint8_t>& modulus,
+ const std::vector<uint8_t>& generator,
+ const std::vector<uint8_t>& peer_public_value,
+ const Policy& policy,
+ RandomNumberGenerator& rng);
+
+ /**
+ * Optional callback with default impl: client side ECDH agreement
+ *
+ * Default implementation uses PK_Key_Agreement::derive_key().
+ * Override to provide a different approach, e.g. using an external device.
+ *
+ * @param curve_name the name of the elliptic curve
+ * @param peer_public_value the public value of the peer
+ * @param policy the TLS policy associated with the session being established
+ * @param rng a random number generator
+ * @param compressed the compression preference for our public value
+ *
+ * @return a pair consisting of the agreed raw secret and our public value
+ */
+ virtual std::pair<secure_vector<uint8_t>, std::vector<uint8_t>> tls_ecdh_agree(
+ const std::string& curve_name,
+ const std::vector<uint8_t>& peer_public_value,
+ const Policy& policy,
+ RandomNumberGenerator& rng,
+ bool compressed);
+
+ /**
* Optional callback: inspect handshake message
* Throw an exception to abort the handshake.
* Default simply ignores the message.
diff --git a/src/lib/tls/tls_handshake_state.cpp b/src/lib/tls/tls_handshake_state.cpp
index 442d499d1..8cf16b1fc 100644
--- a/src/lib/tls/tls_handshake_state.cpp
+++ b/src/lib/tls/tls_handshake_state.cpp
@@ -8,7 +8,6 @@
#include <botan/internal/tls_handshake_state.h>
#include <botan/internal/tls_record.h>
#include <botan/tls_messages.h>
-#include <botan/tls_callbacks.h>
#include <botan/kdf.h>
#include <sstream>
diff --git a/src/lib/tls/tls_handshake_state.h b/src/lib/tls/tls_handshake_state.h
index e15e3a92f..2b03670f9 100644
--- a/src/lib/tls/tls_handshake_state.h
+++ b/src/lib/tls/tls_handshake_state.h
@@ -14,6 +14,7 @@
#include <botan/tls_ciphersuite.h>
#include <botan/tls_exceptn.h>
#include <botan/tls_handshake_msg.h>
+#include <botan/tls_callbacks.h>
#include <botan/pk_keys.h>
#include <botan/pubkey.h>
#include <functional>
@@ -160,6 +161,8 @@ class Handshake_State
const Session_Keys& session_keys() const { return m_session_keys; }
+ Callbacks& callbacks() const { return m_callbacks; }
+
void compute_session_keys();
void compute_session_keys(const secure_vector<uint8_t>& resume_master_secret);