diff options
author | Harry Reimann <[email protected]> | 2017-11-30 15:22:11 +0100 |
---|---|---|
committer | Harry Reimann <[email protected]> | 2017-12-04 10:54:14 +0100 |
commit | 6299685d6c118bd2125fd532e6f5d2258efd9f0d (patch) | |
tree | b86b96abaddb826487f277c38680f63a25638572 | |
parent | 805bb27dff20e491e76142db2b5fe1bd586d4788 (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.cpp | 11 | ||||
-rw-r--r-- | src/lib/tls/msg_client_kex.cpp | 94 | ||||
-rw-r--r-- | src/lib/tls/msg_server_kex.cpp | 23 | ||||
-rw-r--r-- | src/lib/tls/tls_callbacks.cpp | 116 | ||||
-rw-r--r-- | src/lib/tls/tls_callbacks.h | 85 | ||||
-rw-r--r-- | src/lib/tls/tls_handshake_state.cpp | 1 | ||||
-rw-r--r-- | src/lib/tls/tls_handshake_state.h | 3 |
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); |