diff options
author | lloyd <[email protected]> | 2012-01-27 15:38:53 +0000 |
---|---|---|
committer | lloyd <[email protected]> | 2012-01-27 15:38:53 +0000 |
commit | 2d31f3fc1b1c88739e5babbd6a9e8cb3b80263de (patch) | |
tree | 59eeaeae4d678f3d251fb3bdf6b9d25aa79140b0 | |
parent | 3d3c7f45e64c2dab1b9558fc9da80cdd30175063 (diff) |
Add client-side support for PSK kex. Tested against OpenSSL.
-rw-r--r-- | src/tls/c_kex.cpp | 44 | ||||
-rw-r--r-- | src/tls/s_kex.cpp | 10 | ||||
-rw-r--r-- | src/tls/tls_ciphersuite.cpp | 12 | ||||
-rw-r--r-- | src/tls/tls_client.cpp | 14 | ||||
-rw-r--r-- | src/tls/tls_extensions.cpp | 3 | ||||
-rw-r--r-- | src/tls/tls_messages.h | 4 | ||||
-rw-r--r-- | src/tls/tls_policy.cpp | 12 | ||||
-rw-r--r-- | src/tls/tls_reader.h | 12 |
8 files changed, 97 insertions, 14 deletions
diff --git a/src/tls/c_kex.cpp b/src/tls/c_kex.cpp index 2156973f1..8dccb05c9 100644 --- a/src/tls/c_kex.cpp +++ b/src/tls/c_kex.cpp @@ -9,6 +9,7 @@ #include <botan/internal/tls_reader.h> #include <botan/internal/tls_extensions.h> #include <botan/internal/assert.h> +#include <botan/credentials_manager.h> #include <botan/pubkey.h> #include <botan/dh.h> #include <botan/ecdh.h> @@ -46,14 +47,39 @@ SecureVector<byte> strip_leading_zeros(const MemoryRegion<byte>& input) */ Client_Key_Exchange::Client_Key_Exchange(Record_Writer& writer, Handshake_State* state, + Credentials_Manager& creds, const std::vector<X509_Certificate>& peer_certs, RandomNumberGenerator& rng) { - if(state->server_kex) + const std::string kex_algo = state->suite.kex_algo(); + + if(kex_algo == "PSK") + { + std::string identity_hint = ""; + + if(state->server_kex) + { + TLS_Data_Reader reader(state->server_kex->params()); + identity_hint = reader.get_string(2, 0, 65535); + } + + std::pair<std::string, SymmetricKey> psk = + creds.psk("tls-client", + state->client_hello->sni_hostname(), + identity_hint); + + append_tls_length_value(key_material, psk.first, 2); + + MemoryVector<byte> zeros(psk.second.length()); + + append_tls_length_value(pre_master, zeros, 2); + append_tls_length_value(pre_master, psk.second.bits_of(), 2); + } + else if(state->server_kex) { TLS_Data_Reader reader(state->server_kex->params()); - if(state->suite.kex_algo() == "DH") + if(kex_algo == "DH") { BigInt p = BigInt::decode(reader.get_range<byte>(2, 1, 65535)); BigInt g = BigInt::decode(reader.get_range<byte>(2, 1, 65535)); @@ -80,7 +106,7 @@ Client_Key_Exchange::Client_Key_Exchange(Record_Writer& writer, append_tls_length_value(key_material, priv_key.public_value(), 2); } - else if(state->suite.kex_algo() == "ECDH") + else if(kex_algo == "ECDH") { const byte curve_type = reader.get_byte(); @@ -109,17 +135,23 @@ Client_Key_Exchange::Client_Key_Exchange(Record_Writer& writer, append_tls_length_value(key_material, priv_key.public_value(), 1); } else - throw Internal_Error("Unknown key exchange type " + state->suite.kex_algo()); + { + throw Internal_Error("Client_Key_Exchange: Unknown kex " + + kex_algo); + } } else { - // No server key exchange msg better mean a RSA key in the cert + // No server key exchange msg better mean RSA kex + RSA key in cert - std::auto_ptr<Public_Key> pub_key(peer_certs[0].subject_public_key()); + if(kex_algo != "RSA") + throw Unexpected_Message("No server kex but negotiated kex " + kex_algo); if(peer_certs.empty()) throw Internal_Error("No certificate and no server key exchange"); + std::auto_ptr<Public_Key> pub_key(peer_certs[0].subject_public_key()); + if(const RSA_PublicKey* rsa_pub = dynamic_cast<const RSA_PublicKey*>(pub_key.get())) { const Protocol_Version pref_version = state->client_hello->version(); diff --git a/src/tls/s_kex.cpp b/src/tls/s_kex.cpp index 3ebdc3027..b8aba344c 100644 --- a/src/tls/s_kex.cpp +++ b/src/tls/s_kex.cpp @@ -116,7 +116,12 @@ Server_Key_Exchange::Server_Key_Exchange(const MemoryRegion<byte>& buf, * to be able to parse the whole thing anyway. */ - if(kex_algo == "DH") + if(kex_algo == "PSK") + { + std::string identity_hint = reader.get_string(2, 1, 65535); + append_tls_length_value(m_params, identity_hint, 2); + } + else if(kex_algo == "DH") { // 3 bigints, DH p, g, Y @@ -149,8 +154,7 @@ Server_Key_Exchange::Server_Key_Exchange(const MemoryRegion<byte>& buf, append_tls_length_value(m_params, ecdh_key, 1); } else - throw Decoding_Error("Server_Key_Exchange: Unsupported server key exchange type " + - kex_algo); + throw Decoding_Error("Server_Key_Exchange: Unsupported kex type " + kex_algo); if(sig_algo != "") { diff --git a/src/tls/tls_ciphersuite.cpp b/src/tls/tls_ciphersuite.cpp index b81d4adc4..e459830bb 100644 --- a/src/tls/tls_ciphersuite.cpp +++ b/src/tls/tls_ciphersuite.cpp @@ -129,22 +129,32 @@ Ciphersuite Ciphersuite::lookup_ciphersuite(u16bit suite) case TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA: return Ciphersuite("ECDSA", "ECDH", "SHA-1", "3DES", 24); -#if 0 + // PSK ciphersuites + case TLS_PSK_WITH_RC4_128_SHA: return Ciphersuite("", "PSK", "SHA-1", "ARC4", 16); + case TLS_PSK_WITH_3DES_EDE_CBC_SHA: return Ciphersuite("", "PSK", "SHA-1", "3DES", 24); + case TLS_PSK_WITH_AES_128_CBC_SHA: return Ciphersuite("", "PSK", "SHA-1", "AES-128", 16); + case TLS_PSK_WITH_AES_256_CBC_SHA: return Ciphersuite("", "PSK", "SHA-1", "AES-256", 32); +#if 0 + // PSK+DH ciphersuites + case TLS_DHE_PSK_WITH_RC4_128_SHA: return Ciphersuite("", "DHE_PSK", "SHA-1", "ARC4", 16); + case TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA: return Ciphersuite("", "DHE_PSK", "SHA-1", "3DES", 24); + case TLS_DHE_PSK_WITH_AES_128_CBC_SHA: return Ciphersuite("", "DHE_PSK", "SHA-1", "AES-128", 16); + case TLS_DHE_PSK_WITH_AES_256_CBC_SHA: return Ciphersuite("", "DHE_PSK", "SHA-1", "AES-256", 32); #endif diff --git a/src/tls/tls_client.cpp b/src/tls/tls_client.cpp index 8df0c77a1..39b0d3e59 100644 --- a/src/tls/tls_client.cpp +++ b/src/tls/tls_client.cpp @@ -221,6 +221,19 @@ void Client::process_handshake_msg(Handshake_Type type, { state->set_expected_next(CERTIFICATE); } + else if(state->suite.kex_algo() == "PSK") + { + /* PSK is anonymous so no certificate/cert req message is + ever sent. The server may or may not send a server kex, + depending on if it has an identity hint for us. + + PSK_DHE always sends a server key exchange for the DH + exchange portion. + */ + + state->set_expected_next(SERVER_KEX); + state->set_expected_next(SERVER_HELLO_DONE); + } else if(state->suite.kex_algo() != "RSA") { state->set_expected_next(SERVER_KEX); @@ -315,6 +328,7 @@ void Client::process_handshake_msg(Handshake_Type type, state->client_kex = new Client_Key_Exchange(writer, state, + creds, peer_certs, rng); diff --git a/src/tls/tls_extensions.cpp b/src/tls/tls_extensions.cpp index 250f25035..5d345cc9b 100644 --- a/src/tls/tls_extensions.cpp +++ b/src/tls/tls_extensions.cpp @@ -465,6 +465,9 @@ MemoryVector<byte> Signature_Algorithms::serialize() const for(size_t i = 0; i != m_supported_algos.size(); ++i) { + if(m_supported_algos[i].second == "") + continue; + buf.push_back(hash_algo_code(m_supported_algos[i].first)); buf.push_back(sig_algo_code(m_supported_algos[i].second)); } diff --git a/src/tls/tls_messages.h b/src/tls/tls_messages.h index 7d4905a0e..41ab6ece4 100644 --- a/src/tls/tls_messages.h +++ b/src/tls/tls_messages.h @@ -20,6 +20,8 @@ namespace Botan { +class Credentials_Manager; + namespace TLS { class Record_Writer; @@ -216,6 +218,7 @@ class Client_Key_Exchange : public Handshake_Message Client_Key_Exchange(Record_Writer& output, Handshake_State* state, + Credentials_Manager& creds, const std::vector<X509_Certificate>& peer_certs, RandomNumberGenerator& rng); @@ -368,6 +371,7 @@ class Server_Key_Exchange : public Handshake_Message bool verify(const X509_Certificate& cert, Handshake_State* state) const; + // Only valid for certain kex types const Private_Key& server_kex_key() const; Server_Key_Exchange(Record_Writer& writer, diff --git a/src/tls/tls_policy.cpp b/src/tls/tls_policy.cpp index 870afc6d0..a547e8fd5 100644 --- a/src/tls/tls_policy.cpp +++ b/src/tls/tls_policy.cpp @@ -63,6 +63,7 @@ std::vector<std::string> Policy::allowed_signature_methods() const allowed.push_back("ECDSA"); allowed.push_back("RSA"); allowed.push_back("DSA"); + allowed.push_back(""); return allowed; } @@ -159,7 +160,8 @@ std::vector<u16bit> Policy::ciphersuite_list(bool have_srp) const if(!have_srp) { - std::vector<std::string>::iterator i = std::find(kex.begin(), kex.end(), "SRP"); + std::vector<std::string>::iterator i = + std::find(kex.begin(), kex.end(), "SRP"); if(i != kex.end()) kex.erase(i); @@ -167,14 +169,16 @@ std::vector<u16bit> Policy::ciphersuite_list(bool have_srp) const Ciphersuite_Preference_Ordering order(ciphers, hashes, kex, sigs); - std::map<Ciphersuite, u16bit, Ciphersuite_Preference_Ordering> ciphersuites(order); + std::map<Ciphersuite, u16bit, Ciphersuite_Preference_Ordering> + ciphersuites(order); // When in doubt use brute force :) for(u32bit i = 0; i != 65536; ++i) { Ciphersuite suite = Ciphersuite::lookup_ciphersuite(i); - if(suite.cipher_keylen() == 0) - continue; // not a ciphersuite we know + + if(!suite.valid()) + continue; // not a ciphersuite we know, skip if(value_exists(ciphers, suite.cipher_algo()) && value_exists(hashes, suite.mac_algo()) && diff --git a/src/tls/tls_reader.h b/src/tls/tls_reader.h index 09487c5f9..162f691aa 100644 --- a/src/tls/tls_reader.h +++ b/src/tls/tls_reader.h @@ -153,9 +153,11 @@ class TLS_Data_Reader void assert_at_least(size_t n) const { if(buf.size() - offset < n) + { throw Decoding_Error("TLS_Data_Reader: Expected " + to_string(n) + " bytes remaining, only " + to_string(buf.size()-offset) + " left"); + } } const MemoryRegion<byte>& buf; @@ -205,6 +207,16 @@ void append_tls_length_value(MemoryRegion<byte>& buf, append_tls_length_value(buf, &vals[0], vals.size(), tag_size); } +inline void append_tls_length_value(MemoryRegion<byte>& buf, + const std::string& str, + size_t tag_size) + { + append_tls_length_value(buf, + reinterpret_cast<const byte*>(&str[0]), + str.size(), + tag_size); + } + } } |