From 47908b70683d9d0789be4fd4168c7e1ec52307ea Mon Sep 17 00:00:00 2001 From: lloyd Date: Thu, 5 Apr 2012 01:30:24 +0000 Subject: Initial client-side support for SRP (finally!). Tested against OpenSSL 1.0.1, only the certificate versions tested currently as OpenSSL doesn't support anon SRP. --- src/tls/c_kex.cpp | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) (limited to 'src/tls/c_kex.cpp') diff --git a/src/tls/c_kex.cpp b/src/tls/c_kex.cpp index ed571852c..0a6339bd0 100644 --- a/src/tls/c_kex.cpp +++ b/src/tls/c_kex.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -50,6 +51,7 @@ Client_Key_Exchange::Client_Key_Exchange(Record_Writer& writer, Handshake_State* state, Credentials_Manager& creds, const std::vector& peer_certs, + const std::string& hostname, RandomNumberGenerator& rng) { const std::string kex_algo = state->suite.kex_algo(); @@ -171,6 +173,33 @@ Client_Key_Exchange::Client_Key_Exchange(Record_Writer& writer, append_tls_length_value(key_material, priv_key.public_value(), 1); } + else if(kex_algo == "SRP_SHA") + { + const BigInt N = BigInt::decode(reader.get_range(2, 1, 65535)); + const BigInt g = BigInt::decode(reader.get_range(2, 1, 65535)); + MemoryVector salt = reader.get_range(1, 1, 255); + const BigInt B = BigInt::decode(reader.get_range(2, 1, 65535)); + + const std::string srp_group = srp6_group_identifier(N, g); + + const std::string srp_identifier = + creds.srp_identifier("tls-client", hostname); + + const std::string srp_password = + creds.srp_password("tls-client", hostname, srp_identifier); + + std::pair srp_vals = + srp6_client_agree(srp_identifier, + srp_password, + srp_group, + "SHA-1", + salt, + B, + rng); + + append_tls_length_value(key_material, BigInt::encode(srp_vals.first), 2); + pre_master = srp_vals.second.bits_of(); + } else { throw Internal_Error("Client_Key_Exchange: Unknown kex " + -- cgit v1.2.3 From 682e7c46e83afbfa3983932be0838aa82b67eafa Mon Sep 17 00:00:00 2001 From: lloyd Date: Thu, 5 Apr 2012 13:45:22 +0000 Subject: Re-enable TLS (was disabled by trunk merge), and require the srp6 module Initial outline of server side SRP support. Need to figure out how to transfer the v, b, B params from the server key exchange message to the client key exchange. The DH variants do this by passing a Private_Key via server_kex_key call, but wrapping SRP params in a Private_Key really doesn't feel right. Not sure what to do here. Possibly both SRP and DH should return a Key_Exchange_Material* that a client key exchange knows how to dynamic cast on. --- src/tls/c_kex.cpp | 4 ++++ src/tls/info.txt | 3 ++- src/tls/s_kex.cpp | 38 ++++++++++++++++++++++++++++++++++++-- 3 files changed, 42 insertions(+), 3 deletions(-) (limited to 'src/tls/c_kex.cpp') diff --git a/src/tls/c_kex.cpp b/src/tls/c_kex.cpp index 0a6339bd0..16c02e2b8 100644 --- a/src/tls/c_kex.cpp +++ b/src/tls/c_kex.cpp @@ -330,6 +330,10 @@ Client_Key_Exchange::Client_Key_Exchange(const MemoryRegion& contents, append_tls_length_value(pre_master, zeros, 2); append_tls_length_value(pre_master, psk.bits_of(), 2); } + else if(kex_algo == "SRP_SHA") + { + throw Internal_Error("SRP_SHA server side not done"); + } else if(kex_algo == "DH" || kex_algo == "DHE_PSK" || kex_algo == "ECDH" || kex_algo == "ECDHE_PSK") { diff --git a/src/tls/info.txt b/src/tls/info.txt index ab329c342..229cf658f 100644 --- a/src/tls/info.txt +++ b/src/tls/info.txt @@ -1,6 +1,6 @@ define TLS -load_on request +load_on auto The TLS code is complex, new, and not yet reviewed, there may be @@ -85,6 +85,7 @@ prf_tls rng rsa seed +srp6 sha1 sha2_32 ssl3mac diff --git a/src/tls/s_kex.cpp b/src/tls/s_kex.cpp index a5c8ff8d7..24bc6ecaa 100644 --- a/src/tls/s_kex.cpp +++ b/src/tls/s_kex.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -34,13 +35,13 @@ Server_Key_Exchange::Server_Key_Exchange(Record_Writer& writer, const Private_Key* signing_key) : m_kex_key(0) { + const std::string hostname = state->client_hello->sni_hostname(); const std::string kex_algo = state->suite.kex_algo(); if(kex_algo == "PSK" || kex_algo == "DHE_PSK" || kex_algo == "ECDHE_PSK") { std::string identity_hint = - creds.psk_identity_hint("tls-server", - state->client_hello->sni_hostname()); + creds.psk_identity_hint("tls-server", hostname); append_tls_length_value(m_params, identity_hint, 2); } @@ -88,6 +89,39 @@ Server_Key_Exchange::Server_Key_Exchange(Record_Writer& writer, m_kex_key = ecdh.release(); } + else if(kex_algo == "SRP_SHA") + { + const std::string srp_identifier = state->client_hello->srp_identifier(); + + BigInt N, g, v; + MemoryVector salt; + + const bool found = creds.srp_verifier("tls-server", hostname, + srp_identifier, + N, g, v, salt, + policy.hide_unknown_users()); + + if(!found) + throw TLS_Exception(Alert::UNKNOWN_PSK_IDENTITY, + "Unknown SRP user " + srp_identifier); + +#if 0 + BigInt B = srp6_server_step1(v, srp6_group_identifier(N, g), + "SHA-1", rng); +#else + BigInt B = 0; +#endif + + append_tls_length_value(m_params, BigInt::encode(N), 2); + append_tls_length_value(m_params, BigInt::encode(g), 2); + append_tls_length_value(m_params, salt, 1); + append_tls_length_value(m_params, BigInt::encode(B), 2); + + /* + * To finish, client key exchange needs to know + * group_id, v, b, B + */ + } else if(kex_algo != "PSK") throw Internal_Error("Server_Key_Exchange: Unknown kex type " + kex_algo); -- cgit v1.2.3 From e91b91578a483a23bd491149d3dd21079c4a27d1 Mon Sep 17 00:00:00 2001 From: lloyd Date: Fri, 6 Apr 2012 16:43:24 +0000 Subject: Finish up server side SRP support, a little ugly but it works. Add SRP hooks in the examples Fix next protocol support in the tls_server example. --- doc/examples/asio_tls_server.cpp | 5 ++ doc/examples/credentials.h | 96 +++++++++++++++++++++++++++++++++ doc/examples/tls_server.cpp | 44 +++++++++------ doc/tls.txt | 16 +++--- src/credentials/credentials_manager.cpp | 38 +++++++++++-- src/credentials/credentials_manager.h | 36 ++++++++----- src/tls/c_kex.cpp | 4 +- src/tls/s_kex.cpp | 43 +++++++++------ src/tls/tls_handshake_state.cpp | 8 +++ src/tls/tls_handshake_state.h | 2 + src/tls/tls_messages.h | 7 ++- src/tls/tls_policy.cpp | 22 +++----- src/tls/tls_server.cpp | 25 +++++++-- 13 files changed, 267 insertions(+), 79 deletions(-) (limited to 'src/tls/c_kex.cpp') diff --git a/doc/examples/asio_tls_server.cpp b/doc/examples/asio_tls_server.cpp index 5c606a3f2..e721d0455 100644 --- a/doc/examples/asio_tls_server.cpp +++ b/doc/examples/asio_tls_server.cpp @@ -215,6 +215,11 @@ class Session_Manager_Locked : public Botan::TLS::Session_Manager m_session_manager.save(session); } + Botan::u32bit session_lifetime() const + { + return m_session_manager.session_lifetime(); + } + private: boost::mutex m_mutex; Botan::TLS::Session_Manager_In_Memory m_session_manager; diff --git a/doc/examples/credentials.h b/doc/examples/credentials.h index 0999b251d..047e42339 100644 --- a/doc/examples/credentials.h +++ b/doc/examples/credentials.h @@ -6,6 +6,8 @@ #include #include #include +#include +#include #include #include #include @@ -25,6 +27,94 @@ class Credentials_Manager_Simple : public Botan::Credentials_Manager public: Credentials_Manager_Simple(Botan::RandomNumberGenerator& rng) : rng(rng) {} + std::string srp_identifier(const std::string& type, + const std::string& hostname) + { + if(type == "tls-client" && hostname == "localhost") + return "user"; + return ""; + } + + bool attempt_srp(const std::string& type, + const std::string& hostname) + { + return true; + if(hostname == "localhost") + return true; + return false; + } + + std::vector + trusted_certificate_authorities(const std::string&, + const std::string&) + { + std::vector certs; + + Botan::X509_Certificate verisign("/usr/share/ca-certificates/mozilla/VeriSign_Class_3_Public_Primary_Certification_Authority_-_G5.crt"); + certs.push_back(verisign); + return certs; + } + + void verify_certificate_chain( + const std::string& type, + const std::string& purported_hostname, + const std::vector& cert_chain) + { + try + { + Botan::Credentials_Manager::verify_certificate_chain(type, + purported_hostname, + cert_chain); + } + catch(std::exception& e) + { + std::cout << "Certificate verification failed - " << e.what() << "\n"; + } + } + + std::string srp_password(const std::string& type, + const std::string& hostname, + const std::string& identifier) + { + if(type == "tls-client" && hostname == "localhost" && identifier == "user") + return "password"; + + return ""; + } + + bool srp_verifier(const std::string& type, + const std::string& context, + const std::string& identifier, + std::string& group_id, + Botan::BigInt& verifier, + Botan::MemoryRegion& salt, + bool generate_fake_on_unknown) + { + + std::string pass = srp_password("tls-client", context, identifier); + if(pass == "") + { + if(!generate_fake_on_unknown) + return false; + + pass.resize(16); + Botan::global_state().global_rng().randomize((Botan::byte*)&pass[0], pass.size()); + } + + group_id = "modp/srp/2048"; + + salt.resize(16); + Botan::global_state().global_rng().randomize(&salt[0], salt.size()); + + verifier = Botan::generate_srp6_verifier(identifier, + pass, + salt, + group_id, + "SHA-1"); + + return true; + } + std::string psk_identity_hint(const std::string&, const std::string&) { @@ -34,6 +124,7 @@ class Credentials_Manager_Simple : public Botan::Credentials_Manager std::string psk_identity(const std::string&, const std::string&, const std::string& identity_hint) { + //return "lloyd"; return "Client_identity"; } @@ -49,6 +140,8 @@ class Credentials_Manager_Simple : public Botan::Credentials_Manager if(identity == "Client_identity") return Botan::SymmetricKey("b5a72e1387552e6dc10766dc0eda12961f5b21e17f98ef4c41e6572e53bd7527"); + if(identity == "lloyd") + return Botan::SymmetricKey("85b3c1b7dc62b507636ac767999c9630"); throw Botan::Internal_Error("No PSK set for " + identity); } @@ -129,6 +222,9 @@ class Credentials_Manager_Simple : public Botan::Credentials_Manager { const std::string hostname = (context == "" ? "localhost" : context); + if(hostname == "nosuchname") + return std::vector(); + std::string key_name = ""; if(value_exists(cert_key_types, "RSA")) diff --git a/doc/examples/tls_server.cpp b/doc/examples/tls_server.cpp index a5f2c5d78..057584677 100644 --- a/doc/examples/tls_server.cpp +++ b/doc/examples/tls_server.cpp @@ -19,17 +19,6 @@ using namespace std::tr1::placeholders; #include #include -bool handshake_complete(const TLS::Session& session) - { - printf("Handshake complete, protocol=%04X ciphersuite=%s compression=%d\n", - session.version(), session.ciphersuite().to_string().c_str(), - session.compression_method()); - - printf("Session id = %s\n", hex_encode(session.session_id()).c_str()); - printf("Master secret = %s\n", hex_encode(session.master_secret()).c_str()); - return true; - } - class Blocking_TLS_Server { public: @@ -44,23 +33,40 @@ class Blocking_TLS_Server server( output_fn, std::tr1::bind(&Blocking_TLS_Server::reader_fn, std::tr1::ref(*this), _1, _2, _3), - handshake_complete, + std::tr1::bind(&Blocking_TLS_Server::handshake_complete, std::tr1::ref(*this), _1), sessions, creds, policy, - rng), + rng, + protocols), exit(false) { read_loop(); } + bool handshake_complete(const TLS::Session& session) + { + std::cout << "Handshake complete: " + << session.version().to_string() << " " + << session.ciphersuite().to_string() << " " + << "SessionID: " << hex_encode(session.session_id()) << "\n"; + + if(session.srp_identifier() != "") + std::cout << "SRP identifier: " << session.srp_identifier() << "\n"; + + if(server.next_protocol() != "") + std::cout << "Next protocol: " << server.next_protocol() << "\n"; + + return true; + } + size_t read(byte buf[], size_t buf_len) { size_t got = read_queue.read(buf, buf_len); while(!exit && !got) { - read_loop(5); // header size + read_loop(TLS::TLS_HEADER_SIZE); got = read_queue.read(buf, buf_len); } @@ -148,8 +154,14 @@ int main(int argc, char* argv[]) Credentials_Manager_Simple creds(rng); std::vector protocols; - protocols.push_back("spdy/2"); - protocols.push_back("http/1.0"); + + /* + * These are the protocols we advertise to the client, but the + * client will send back whatever it actually plans on talking, + * which may or may not take into account what we advertise. + */ + protocols.push_back("echo/1.0"); + protocols.push_back("echo/1.1"); while(true) { diff --git a/doc/tls.txt b/doc/tls.txt index dd4fb1270..267fb6e62 100644 --- a/doc/tls.txt +++ b/doc/tls.txt @@ -7,14 +7,14 @@ SSL and TLS .. versionadded:: 1.10.2 Botan supports both client and server implementations of the SSL/TLS -protocols, including SSL v3, TLS v1.0, and TLS v1.1 (the insecure and -obsolete SSL v2 protocol is not supported, beyond processing SSL v2 -client hellos which some implementations send for backwards -compatability). - -The implementation uses ``std::tr1::function``, so it may not have -been compiled into the version you are using; you can test for the -feature macro ``BOTAN_HAS_TLS`` to check. +protocols, including SSL v3, TLS v1.0, TLS v1.1, and TLS v1.2 (the +insecure and obsolete SSL v2 protocol is not supported, beyond +processing SSL v2 client hellos which some clients still send for +backwards compatability with ancient servers). + +The implementation uses ``std::tr1::function`` for callbacks, so it +may not have been compiled into the version you are using; you can +test for the feature macro ``BOTAN_HAS_TLS`` to check. General TLS Interface ---------------------------------------- diff --git a/src/credentials/credentials_manager.cpp b/src/credentials/credentials_manager.cpp index ef5d44819..ed737a08f 100644 --- a/src/credentials/credentials_manager.cpp +++ b/src/credentials/credentials_manager.cpp @@ -30,6 +30,12 @@ SymmetricKey Credentials_Manager::psk(const std::string&, throw Internal_Error("No PSK set for identity " + identity); } +bool Credentials_Manager::attempt_srp(const std::string&, + const std::string&) + { + return false; + } + std::string Credentials_Manager::srp_identifier(const std::string&, const std::string&) { @@ -46,8 +52,7 @@ std::string Credentials_Manager::srp_password(const std::string&, bool Credentials_Manager::srp_verifier(const std::string&, const std::string&, const std::string&, - BigInt&, - BigInt&, + std::string&, BigInt&, MemoryRegion&, bool) @@ -99,6 +104,7 @@ void Credentials_Manager::verify_certificate_chain( if(!cert_chain[0].matches_dns_name(purported_hostname)) throw std::runtime_error("Certificate did not match hostname"); +#if 1 std::vector CAs = trusted_certificate_authorities(type, purported_hostname); X509_Store store; @@ -110,11 +116,33 @@ void Credentials_Manager::verify_certificate_chain( X509_Code result = store.validate_cert(cert_chain[0], X509_Store::TLS_SERVER); - if(CAs.empty() && result == CERT_ISSUER_NOT_FOUND) - return; + if(CAs.empty()) + { + if(result == CERT_ISSUER_NOT_FOUND) + return; + if(result == CANNOT_ESTABLISH_TRUST) + return; + } if(result != VERIFIED) - throw std::runtime_error("Certificate did not validate"); + throw std::runtime_error("Certificate did not validate, code " + to_string(result)); +#else + + // New X.509 API + const Certificate_Store& CAs = + trusted_certificate_authorities(type, purported_hostname); + + Path_Validation_Result result = + x509_path_validate(cert_chain, + Path_Validation_Restrictions(), + store); + + if(!result.successful_validation()) + throw std::runtime_error("Certificate validation failure: " + result.as_string()); + + if(!CAs.certificate_known(result.trust_root()) + throw std::runtime_error("Certificate chain roots in unknown/untrusted CA"); +#endif } } diff --git a/src/credentials/credentials_manager.h b/src/credentials/credentials_manager.h index e1b4268e3..67da07eec 100644 --- a/src/credentials/credentials_manager.h +++ b/src/credentials/credentials_manager.h @@ -48,6 +48,12 @@ class BOTAN_DLL Credentials_Manager const std::string& context, const std::string& identity); + /** + * Return true if we should attempt SRP authentication + */ + virtual bool attempt_srp(const std::string& type, + const std::string& context); + /** * @return identifier for client-side SRP auth, if available for this type/context. Should return empty string @@ -73,20 +79,21 @@ class BOTAN_DLL Credentials_Manager virtual bool srp_verifier(const std::string& type, const std::string& context, const std::string& identifier, - BigInt& group_prime, - BigInt& group_generator, + std::string& group_name, BigInt& verifier, MemoryRegion& salt, bool generate_fake_on_unknown); /** - * Return a cert chain we can use, ordered from leaf to root. - * Assumed that we can get the private key of the leaf with - * private_key_for + * Return a cert chain we can use, ordered from leaf to root, + * or else an empty vector. * - * @param cert_key_type is a set string representing the allowed - * key type ("RSA", "DSA", "ECDSA", etc) or empty if no - * preference. + * It is assumed that the caller can get the private key of the + * leaf with private_key_for + * + * @param cert_key_types specifies the key types desired ("RSA", + * "DSA", "ECDSA", etc), or empty if there + * is no preference by the caller. */ virtual std::vector cert_chain( const std::vector& cert_key_types, @@ -94,13 +101,14 @@ class BOTAN_DLL Credentials_Manager const std::string& context); /** - * Return a cert chain we can use, ordered from leaf to root. - * Assumed that we can get the private key of the leaf with - * private_key_for + * Return a cert chain we can use, ordered from leaf to root, + * or else an empty vector. + * + * It is assumed that the caller can get the private key of the + * leaf with private_key_for * - * @param cert_key_type is a set string representing the allowed - * key type ("RSA", "DSA", "ECDSA", etc) or empty if no - * preference. + * @param cert_key_type specifies the type of key requested + * ("RSA", "DSA", "ECDSA", etc) */ std::vector cert_chain_single_type( const std::string& cert_key_type, diff --git a/src/tls/c_kex.cpp b/src/tls/c_kex.cpp index 16c02e2b8..13925a482 100644 --- a/src/tls/c_kex.cpp +++ b/src/tls/c_kex.cpp @@ -332,7 +332,9 @@ Client_Key_Exchange::Client_Key_Exchange(const MemoryRegion& contents, } else if(kex_algo == "SRP_SHA") { - throw Internal_Error("SRP_SHA server side not done"); + SRP6_Server_Session& srp = state->server_kex->server_srp_params(); + + pre_master = srp.step2(BigInt::decode(reader.get_range(2, 0, 65535))).bits_of(); } else if(kex_algo == "DH" || kex_algo == "DHE_PSK" || kex_algo == "ECDH" || kex_algo == "ECDHE_PSK") diff --git a/src/tls/s_kex.cpp b/src/tls/s_kex.cpp index 24bc6ecaa..68a5c16db 100644 --- a/src/tls/s_kex.cpp +++ b/src/tls/s_kex.cpp @@ -33,7 +33,7 @@ Server_Key_Exchange::Server_Key_Exchange(Record_Writer& writer, Credentials_Manager& creds, RandomNumberGenerator& rng, const Private_Key* signing_key) : - m_kex_key(0) + m_kex_key(0), m_srp_params(0) { const std::string hostname = state->client_hello->sni_hostname(); const std::string kex_algo = state->suite.kex_algo(); @@ -93,34 +93,30 @@ Server_Key_Exchange::Server_Key_Exchange(Record_Writer& writer, { const std::string srp_identifier = state->client_hello->srp_identifier(); - BigInt N, g, v; + std::string group_id; + BigInt v; MemoryVector salt; const bool found = creds.srp_verifier("tls-server", hostname, srp_identifier, - N, g, v, salt, + group_id, v, salt, policy.hide_unknown_users()); if(!found) throw TLS_Exception(Alert::UNKNOWN_PSK_IDENTITY, "Unknown SRP user " + srp_identifier); -#if 0 - BigInt B = srp6_server_step1(v, srp6_group_identifier(N, g), - "SHA-1", rng); -#else - BigInt B = 0; -#endif + m_srp_params = new SRP6_Server_Session; - append_tls_length_value(m_params, BigInt::encode(N), 2); - append_tls_length_value(m_params, BigInt::encode(g), 2); + BigInt B = m_srp_params->step1(v, group_id, + "SHA-1", rng); + + DL_Group group(group_id); + + append_tls_length_value(m_params, BigInt::encode(group.get_p()), 2); + append_tls_length_value(m_params, BigInt::encode(group.get_g()), 2); append_tls_length_value(m_params, salt, 1); append_tls_length_value(m_params, BigInt::encode(B), 2); - - /* - * To finish, client key exchange needs to know - * group_id, v, b, B - */ } else if(kex_algo != "PSK") throw Internal_Error("Server_Key_Exchange: Unknown kex type " + kex_algo); @@ -150,7 +146,7 @@ Server_Key_Exchange::Server_Key_Exchange(const MemoryRegion& buf, const std::string& kex_algo, const std::string& sig_algo, Protocol_Version version) : - m_kex_key(0) + m_kex_key(0), m_srp_params(0) { if(buf.size() < 6) throw Decoding_Error("Server_Key_Exchange: Packet corrupted"); @@ -230,6 +226,11 @@ Server_Key_Exchange::Server_Key_Exchange(const MemoryRegion& buf, } } +Server_Key_Exchange::~Server_Key_Exchange() + { + delete m_kex_key; + delete m_srp_params; + } /** * Serialize a Server Key Exchange message @@ -278,6 +279,14 @@ const Private_Key& Server_Key_Exchange::server_kex_key() const BOTAN_ASSERT(m_kex_key, "Key is non-NULL"); return *m_kex_key; } + +// Only valid for SRP negotiation +SRP6_Server_Session& Server_Key_Exchange::server_srp_params() + { + BOTAN_ASSERT(m_srp_params, "SRP params are non-NULL"); + return *m_srp_params; + } + } } diff --git a/src/tls/tls_handshake_state.cpp b/src/tls/tls_handshake_state.cpp index 48d9abbeb..8e9003108 100644 --- a/src/tls/tls_handshake_state.cpp +++ b/src/tls/tls_handshake_state.cpp @@ -143,6 +143,14 @@ bool Handshake_State::received_handshake_msg(Handshake_Type handshake_msg) const return (hand_received_mask & mask); } +std::string Handshake_State::srp_identifier() const + { + if(suite.valid() && suite.kex_algo() == "SRP_SHA") + return client_hello->srp_identifier(); + + return ""; + } + const MemoryRegion& Handshake_State::session_ticket() const { if(new_session_ticket && !new_session_ticket->ticket().empty()) diff --git a/src/tls/tls_handshake_state.h b/src/tls/tls_handshake_state.h index 2a78d1d1e..c347c4574 100644 --- a/src/tls/tls_handshake_state.h +++ b/src/tls/tls_handshake_state.h @@ -64,6 +64,8 @@ class Handshake_State std::string& sig_algo, bool for_client_auth); + std::string srp_identifier() const; + KDF* protocol_specific_prf(); Protocol_Version version() const { return m_version; } diff --git a/src/tls/tls_messages.h b/src/tls/tls_messages.h index ff6ebda4d..c8a9382d6 100644 --- a/src/tls/tls_messages.h +++ b/src/tls/tls_messages.h @@ -21,6 +21,7 @@ namespace Botan { class Credentials_Manager; +class SRP6_Server_Session; namespace TLS { @@ -396,6 +397,9 @@ class Server_Key_Exchange : public Handshake_Message // Only valid for certain kex types const Private_Key& server_kex_key() const; + // Only valid for SRP negotiation + SRP6_Server_Session& server_srp_params(); + Server_Key_Exchange(Record_Writer& writer, Handshake_State* state, const Policy& policy, @@ -408,11 +412,12 @@ class Server_Key_Exchange : public Handshake_Message const std::string& sig_alg, Protocol_Version version); - ~Server_Key_Exchange() { delete m_kex_key; } + ~Server_Key_Exchange(); private: MemoryVector serialize() const; Private_Key* m_kex_key; + SRP6_Server_Session* m_srp_params; MemoryVector m_params; diff --git a/src/tls/tls_policy.cpp b/src/tls/tls_policy.cpp index de3c6f674..c42a6904c 100644 --- a/src/tls/tls_policy.cpp +++ b/src/tls/tls_policy.cpp @@ -47,7 +47,7 @@ std::vector Policy::allowed_key_exchange_methods() const { std::vector allowed; - //allowed.push_back("SRP_SHA"); + allowed.push_back("SRP_SHA"); //allowed.push_back("ECDHE_PSK"); //allowed.push_back("DHE_PSK"); //allowed.push_back("PSK"); @@ -204,19 +204,10 @@ class Ciphersuite_Preference_Ordering std::vector ciphersuite_list(const Policy& policy, bool have_srp) { - std::vector ciphers = policy.allowed_ciphers(); - std::vector hashes = policy.allowed_hashes(); - std::vector kex = policy.allowed_key_exchange_methods(); - std::vector sigs = policy.allowed_signature_methods(); - - if(!have_srp) - { - std::vector::iterator i = - std::find(kex.begin(), kex.end(), "SRP_SHA"); - - if(i != kex.end()) - kex.erase(i); - } + const std::vector ciphers = policy.allowed_ciphers(); + const std::vector hashes = policy.allowed_hashes(); + const std::vector kex = policy.allowed_key_exchange_methods(); + const std::vector sigs = policy.allowed_signature_methods(); Ciphersuite_Preference_Ordering order(ciphers, hashes, kex, sigs); @@ -230,6 +221,9 @@ std::vector ciphersuite_list(const Policy& policy, if(!suite.valid()) continue; // not a ciphersuite we know, skip + if(!have_srp && suite.kex_algo() == "SRP_SHA") + continue; + if(!value_exists(kex, suite.kex_algo())) continue; // unsupported key exchange diff --git a/src/tls/tls_server.cpp b/src/tls/tls_server.cpp index f5b4efc30..10872a825 100644 --- a/src/tls/tls_server.cpp +++ b/src/tls/tls_server.cpp @@ -91,11 +91,18 @@ bool check_for_resume(Session& session_info, */ u16bit choose_ciphersuite( const Policy& policy, + Credentials_Manager& creds, const std::map >& cert_chains, const Client_Hello* client_hello) { + const bool have_srp = creds.attempt_srp("tls-server", + client_hello->sni_hostname()); + const std::vector client_suites = client_hello->ciphersuites(); - const std::vector server_suites = ciphersuite_list(policy, false); + const std::vector server_suites = ciphersuite_list(policy, have_srp); + + if(server_suites.empty()) + throw Internal_Error("Policy forbids us from negotiating any ciphersuite"); const bool have_shared_ecc_curve = (policy.choose_curve(client_hello->supported_ecc_curves()) != ""); @@ -116,6 +123,18 @@ u16bit choose_ciphersuite( if(cert_chains.count(suite.sig_algo()) == 0) continue; + /* + The client may offer SRP cipher suites in the hello message but + omit the SRP extension. If the server would like to select an + SRP cipher suite in this case, the server SHOULD return a fatal + "unknown_psk_identity" alert immediately after processing the + client hello message. + - RFC 5054 section 2.5.1.2 + */ + if(suite.kex_algo() == "SRP_SHA" && client_hello->srp_identifier() == "") + throw TLS_Exception(Alert::UNKNOWN_PSK_IDENTITY, + "Client wanted SRP but did not send username"); + return suite_id; } @@ -368,7 +387,7 @@ void Server::process_handshake_msg(Handshake_Type type, state->hash, rng.random_vec(32), // new session ID state->version(), - choose_ciphersuite(policy, cert_chains, state->client_hello), + choose_ciphersuite(policy, creds, cert_chains, state->client_hello), choose_compression(policy, state->client_hello->compression_methods()), state->client_hello->fragment_size(), secure_renegotiation.supported(), @@ -546,7 +565,7 @@ void Server::process_handshake_msg(Handshake_Type type, peer_certs, MemoryVector(), m_hostname, - "" + state->srp_identifier() ); if(handshake_fn(session_info)) -- cgit v1.2.3