aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorlloyd <[email protected]>2012-01-27 15:38:53 +0000
committerlloyd <[email protected]>2012-01-27 15:38:53 +0000
commit2d31f3fc1b1c88739e5babbd6a9e8cb3b80263de (patch)
tree59eeaeae4d678f3d251fb3bdf6b9d25aa79140b0
parent3d3c7f45e64c2dab1b9558fc9da80cdd30175063 (diff)
Add client-side support for PSK kex. Tested against OpenSSL.
-rw-r--r--src/tls/c_kex.cpp44
-rw-r--r--src/tls/s_kex.cpp10
-rw-r--r--src/tls/tls_ciphersuite.cpp12
-rw-r--r--src/tls/tls_client.cpp14
-rw-r--r--src/tls/tls_extensions.cpp3
-rw-r--r--src/tls/tls_messages.h4
-rw-r--r--src/tls/tls_policy.cpp12
-rw-r--r--src/tls/tls_reader.h12
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);
+ }
+
}
}