diff options
author | lloyd <[email protected]> | 2011-12-29 17:18:56 +0000 |
---|---|---|
committer | lloyd <[email protected]> | 2011-12-29 17:18:56 +0000 |
commit | b500ef1a58f977f5ce5869e7160615f7b44876e7 (patch) | |
tree | 025f3a6ee8b7da533178c2f7b3a351cb94750211 | |
parent | caa9dfa12cf69bb4ab88c399e61e856fedb24900 (diff) |
Add support for sending server name indicator in client hello
Add support for sending and reading the SRP identifier extension.
Add some helper classes for managing TLS extensions
Add ciphersuite codes for SRP key exchange.
-rw-r--r-- | doc/examples/tls_client.cpp | 4 | ||||
-rw-r--r-- | doc/examples/tls_server.cpp | 9 | ||||
-rw-r--r-- | src/tls/hello.cpp | 91 | ||||
-rw-r--r-- | src/tls/info.txt | 2 | ||||
-rw-r--r-- | src/tls/tls_client.cpp | 20 | ||||
-rw-r--r-- | src/tls/tls_client.h | 4 | ||||
-rw-r--r-- | src/tls/tls_extensions.cpp | 150 | ||||
-rw-r--r-- | src/tls/tls_extensions.h | 109 | ||||
-rw-r--r-- | src/tls/tls_magic.h | 11 | ||||
-rw-r--r-- | src/tls/tls_messages.h | 6 | ||||
-rw-r--r-- | src/tls/tls_reader.h | 16 | ||||
-rw-r--r-- | src/tls/tls_session_key.h | 2 | ||||
-rw-r--r-- | src/tls/tls_suites.cpp | 38 |
13 files changed, 387 insertions, 75 deletions
diff --git a/doc/examples/tls_client.cpp b/doc/examples/tls_client.cpp index b44dbf7d5..1d62bbc20 100644 --- a/doc/examples/tls_client.cpp +++ b/doc/examples/tls_client.cpp @@ -35,7 +35,9 @@ class HTTPS_Client std::tr1::bind(&HTTPS_Client::proc_data, std::tr1::ref(*this), _1, _2, _3), sessions, policy, - rng) + rng, + host, + host + "_username") { SecureVector<byte> socket_buf(1024); size_t desired = 0; diff --git a/doc/examples/tls_server.cpp b/doc/examples/tls_server.cpp index e63565fc0..f43af1dcf 100644 --- a/doc/examples/tls_server.cpp +++ b/doc/examples/tls_server.cpp @@ -77,6 +77,7 @@ class Blocking_TLS_Server if(socket_got == 0) // eof? { close(); + printf("got eof on socket\n"); exit = true; } @@ -88,8 +89,8 @@ class Blocking_TLS_Server { if(buf_len == 0 && alert_code != NULL_ALERT) { - printf("Alert: %d, quitting\n", alert_code); - exit = true; + printf("Alert: %d\n", alert_code); + //exit = true; } printf("Got %d bytes: ", (int)buf_len); @@ -141,8 +142,8 @@ int main(int argc, char* argv[]) AutoSeeded_RNG rng; - //RSA_PrivateKey key(rng, 1024); - DSA_PrivateKey key(rng, DL_Group("dsa/jce/1024")); + RSA_PrivateKey key(rng, 1024); + //DSA_PrivateKey key(rng, DL_Group("dsa/jce/1024")); X509_Cert_Options options( "localhost/US/Syn Ack Labs/Mathematical Munitions Dept"); diff --git a/src/tls/hello.cpp b/src/tls/hello.cpp index 0343b649e..59e7c68d4 100644 --- a/src/tls/hello.cpp +++ b/src/tls/hello.cpp @@ -1,6 +1,6 @@ /* * TLS Hello Messages -* (C) 2004-2010 Jack Lloyd +* (C) 2004-2011 Jack Lloyd * * Released under the terms of the Botan license */ @@ -8,8 +8,11 @@ #include <botan/internal/tls_messages.h> #include <botan/internal/tls_reader.h> #include <botan/internal/tls_session_key.h> +#include <botan/internal/tls_extensions.h> #include <botan/tls_record.h> +#include <stdio.h> + namespace Botan { /* @@ -66,14 +69,16 @@ void Hello_Request::deserialize(const MemoryRegion<byte>& buf) Client_Hello::Client_Hello(Record_Writer& writer, TLS_Handshake_Hash& hash, const TLS_Policy& policy, - RandomNumberGenerator& rng) + RandomNumberGenerator& rng, + const std::string& hostname, + const std::string& srp_identifier) : + c_version(policy.pref_version()), + c_random(rng.random_vec(32)), + suites(policy.ciphersuites()), + comp_methods(policy.compression()), + requested_hostname(hostname), + requested_srp_id(srp_identifier) { - c_random = rng.random_vec(32); - - suites = policy.ciphersuites(); - comp_methods = policy.compression(); - c_version = policy.pref_version(); - send(writer, hash); } @@ -92,6 +97,13 @@ MemoryVector<byte> Client_Hello::serialize() const append_tls_length_value(buf, suites, 2); append_tls_length_value(buf, comp_methods, 1); + printf("Requesting hostname '%s'\n", requested_hostname.c_str()); + + TLS_Extensions extensions; + extensions.push_back(new Server_Name_Indicator(requested_hostname)); + extensions.push_back(new SRP_Identifier(requested_srp_id)); + buf += extensions.serialize(); + return buf; } @@ -152,59 +164,20 @@ void Client_Hello::deserialize(const MemoryRegion<byte>& buf) comp_methods = reader.get_range_vector<byte>(1, 1, 255); - if(reader.has_remaining()) + TLS_Extensions extensions(reader); + + for(size_t i = 0; i != extensions.count(); ++i) { - const u16bit all_extn_size = reader.get_u16bit(); - - if(reader.remaining_bytes() != all_extn_size) - throw Decoding_Error("Client_Hello: Bad extension size"); - - while(reader.has_remaining()) - { - const u16bit extension_code = reader.get_u16bit(); - const u16bit extension_size = reader.get_u16bit(); - - if(extension_code == TLSEXT_SERVER_NAME_INDICATION) - { - u16bit name_bytes = reader.get_u16bit(); - - while(name_bytes) - { - byte name_type = reader.get_byte(); - name_bytes--; - - if(name_type == 0) // DNS - { - std::vector<byte> name = - reader.get_range_vector<byte>(2, 1, 65535); - - requested_hostname.assign( - reinterpret_cast<const char*>(&name[0]), - name.size()); - - name_bytes -= (2 + name.size()); - } - else - { - reader.discard_next(name_bytes); - name_bytes = 0; - } - } - } - else if(extension_code == TLSEXT_SRP_IDENTIFIER) - { - std::vector<byte> name = reader.get_range_vector<byte>(1, 1, 255); - - requested_srp_id.assign( - reinterpret_cast<char*>(&name[0]), - name.size()); - } - else - { - reader.discard_next(extension_size); - } - } + TLS_Extension* extn = extensions.at(i); + + if(Server_Name_Indicator* sni = dynamic_cast<Server_Name_Indicator*>(extn)) + requested_hostname = sni->host_name(); + else if(SRP_Identifier* srp = dynamic_cast<SRP_Identifier*>(extn)) + requested_srp_id = srp->identifier(); } + + printf("hostname %s srp id %s\n", requested_hostname.c_str(), + requested_srp_id.c_str()); } /* diff --git a/src/tls/info.txt b/src/tls/info.txt index 43bf244c6..857a5bc2a 100644 --- a/src/tls/info.txt +++ b/src/tls/info.txt @@ -22,6 +22,7 @@ tls_suites.h <header:internal> tls_alerts.h tls_handshake_hash.h +tls_extensions.h tls_messages.h tls_reader.h tls_session_key.h @@ -34,6 +35,7 @@ cert_req.cpp cert_ver.cpp finished.cpp tls_handshake_hash.cpp +tls_extensions.cpp hello.cpp rec_read.cpp rec_wri.cpp diff --git a/src/tls/tls_client.cpp b/src/tls/tls_client.cpp index 19ae44ace..3fe93b846 100644 --- a/src/tls/tls_client.cpp +++ b/src/tls/tls_client.cpp @@ -21,7 +21,9 @@ TLS_Client::TLS_Client(std::tr1::function<void (const byte[], size_t)> output_fn std::tr1::function<void (const byte[], size_t, u16bit)> proc_fn, TLS_Session_Manager& session_manager, const TLS_Policy& policy, - RandomNumberGenerator& rng) : + RandomNumberGenerator& rng, + const std::string& hostname, + const std::string& srp_username) : TLS_Channel(output_fn, proc_fn), policy(policy), rng(rng), @@ -31,7 +33,12 @@ TLS_Client::TLS_Client(std::tr1::function<void (const byte[], size_t)> output_fn state = new Handshake_State; state->set_expected_next(SERVER_HELLO); - state->client_hello = new Client_Hello(writer, state->hash, policy, rng); + state->client_hello = new Client_Hello(writer, + state->hash, + policy, + rng, + hostname, + srp_username); } void TLS_Client::add_client_cert(const X509_Certificate& cert, @@ -74,7 +81,7 @@ void TLS_Client::process_handshake_msg(Handshake_Type type, if(type == HELLO_REQUEST) { Hello_Request hello_request(contents); - state->client_hello = new Client_Hello( writer, state->hash, policy, rng); + state->client_hello = new Client_Hello(writer, state->hash, policy, rng); } else if(type == SERVER_HELLO) { @@ -205,10 +212,10 @@ void TLS_Client::process_handshake_msg(Handshake_Type type, state->server_hello_done = new Server_Hello_Done(contents); + std::vector<X509_Certificate> send_certs; + if(state->received_handshake_msg(CERTIFICATE_REQUEST)) { - std::vector<X509_Certificate> send_certs; - std::vector<Certificate_Type> types = state->cert_req->acceptable_types(); @@ -223,7 +230,8 @@ void TLS_Client::process_handshake_msg(Handshake_Type type, state->kex_pub, state->version, state->client_hello->version()); - if(state->received_handshake_msg(CERTIFICATE_REQUEST)) + if(state->received_handshake_msg(CERTIFICATE_REQUEST) && + !send_certs.empty()) { Private_Key* key_matching_cert = 0; // FIXME state->client_verify = new Certificate_Verify(writer, state->hash, diff --git a/src/tls/tls_client.h b/src/tls/tls_client.h index 295eb0364..e80051460 100644 --- a/src/tls/tls_client.h +++ b/src/tls/tls_client.h @@ -27,7 +27,9 @@ class BOTAN_DLL TLS_Client : public TLS_Channel std::tr1::function<void (const byte[], size_t, u16bit)> proc_fn, TLS_Session_Manager& session_manager, const TLS_Policy& policy, - RandomNumberGenerator& rng); + RandomNumberGenerator& rng, + const std::string& servername = "", + const std::string& srp_username = ""); void add_client_cert(const X509_Certificate& cert, Private_Key* cert_key); diff --git a/src/tls/tls_extensions.cpp b/src/tls/tls_extensions.cpp new file mode 100644 index 000000000..b9881ea3a --- /dev/null +++ b/src/tls/tls_extensions.cpp @@ -0,0 +1,150 @@ +/* +* TLS Extensions +* (C) 2011 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#include <botan/internal/tls_extensions.h> +#include <botan/internal/tls_reader.h> + +#include <stdio.h> + +namespace Botan { + +TLS_Extensions::TLS_Extensions(class TLS_Data_Reader& reader) + { + if(reader.has_remaining()) + { + const u16bit all_extn_size = reader.get_u16bit(); + + if(reader.remaining_bytes() != all_extn_size) + throw Decoding_Error("Bad extension size"); + + while(reader.has_remaining()) + { + const u16bit extension_code = reader.get_u16bit(); + const u16bit extension_size = reader.get_u16bit(); + + if(extension_code == TLSEXT_SERVER_NAME_INDICATION) + extensions.push_back(new Server_Name_Indicator(reader)); + else if(extension_code == TLSEXT_SRP_IDENTIFIER) + extensions.push_back(new SRP_Identifier(reader)); + else // unknown/unhandled extension + { + printf("Unknown extension code %d\n", extension_code); + reader.discard_next(extension_size); + } + } + } + } + +MemoryVector<byte> TLS_Extensions::serialize() const + { + MemoryVector<byte> buf(2); // allocate length + + for(size_t i = 0; i != extensions.size(); ++i) + { + if(extensions[i]->empty()) + continue; + + const u16bit extn_code = extensions[i]->type(); + + MemoryVector<byte> extn_val = extensions[i]->serialize(); + + printf("serializing extn %d of %d bytes\n", extn_code, extn_val.size()); + + buf.push_back(get_byte(0, extn_code)); + buf.push_back(get_byte(1, extn_code)); + + buf.push_back(get_byte<u16bit>(0, extn_val.size())); + buf.push_back(get_byte<u16bit>(1, extn_val.size())); + + buf += extn_val; + } + + const u16bit extn_size = buf.size() - 2; + + buf[0] = get_byte(0, extn_size); + buf[1] = get_byte(1, extn_size); + + printf("%d bytes of extensions\n", buf.size()); + + // avoid sending an empty extensions block + if(buf.size() == 2) + return MemoryVector<byte>(); + + return buf; + } + +TLS_Extensions::~TLS_Extensions() + { + for(size_t i = 0; i != extensions.size(); ++i) + delete extensions[i]; + extensions.clear(); + } + +Server_Name_Indicator::Server_Name_Indicator(TLS_Data_Reader& reader) + { + u16bit name_bytes = reader.get_u16bit(); + + while(name_bytes) + { + byte name_type = reader.get_byte(); + name_bytes--; + + if(name_type == 0) // DNS + { + sni_host_name = reader.get_string(2, 1, 65535); + name_bytes -= (2 + sni_host_name.size()); + } + else // some other unknown name type + { + reader.discard_next(name_bytes); + name_bytes = 0; + } + } + } + +MemoryVector<byte> Server_Name_Indicator::serialize() const + { + MemoryVector<byte> buf; + + size_t name_len = sni_host_name.size(); + + buf.push_back(get_byte<u16bit>(0, name_len+3)); + buf.push_back(get_byte<u16bit>(1, name_len+3)); + buf.push_back(0); // DNS + + buf.push_back(get_byte<u16bit>(0, name_len)); + buf.push_back(get_byte<u16bit>(1, name_len)); + + + buf += std::make_pair( + reinterpret_cast<const byte*>(sni_host_name.data()), + sni_host_name.size()); + + printf("serializing %d bytes %s\n", buf.size(), + sni_host_name.c_str()); + return buf; + } + +SRP_Identifier::SRP_Identifier(TLS_Data_Reader& reader) + { + srp_identifier = reader.get_string(1, 1, 255); + } + +MemoryVector<byte> SRP_Identifier::serialize() const + { + MemoryVector<byte> buf; + + const byte* srp_bytes = + reinterpret_cast<const byte*>(srp_identifier.data()); + + append_tls_length_value(buf, srp_bytes, srp_identifier.size(), 1); + + return buf; + } + + +} diff --git a/src/tls/tls_extensions.h b/src/tls/tls_extensions.h new file mode 100644 index 000000000..01a4253b3 --- /dev/null +++ b/src/tls/tls_extensions.h @@ -0,0 +1,109 @@ +/* +* TLS Extensions +* (C) 2011 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#ifndef BOTAN_TLS_EXTENSIONS_H__ +#define BOTAN_TLS_EXTENSIONS_H__ + +#include <botan/secmem.h> +#include <botan/tls_magic.h> +#include <vector> +#include <string> + +namespace Botan { + +class TLS_Data_Reader; + +/** +* Base class representing a TLS extension of some kind +*/ +class TLS_Extension + { + public: + virtual TLS_Handshake_Extension_Type type() const = 0; + virtual MemoryVector<byte> serialize() const = 0; + + virtual bool empty() const = 0; + + virtual ~TLS_Extension() {} + }; + +/** +* Server Name Indicator extension (RFC 3546) +*/ +class Server_Name_Indicator : public TLS_Extension + { + public: + TLS_Handshake_Extension_Type type() const + { return TLSEXT_SERVER_NAME_INDICATION; } + + Server_Name_Indicator(const std::string& host_name) : + sni_host_name(host_name) {} + + Server_Name_Indicator(TLS_Data_Reader& reader); + + std::string host_name() const { return sni_host_name; } + + MemoryVector<byte> serialize() const; + + bool empty() const { return sni_host_name == ""; } + private: + std::string sni_host_name; + }; + +/** +* SRP identifier extension (RFC 5054) +*/ +class SRP_Identifier : public TLS_Extension + { + public: + TLS_Handshake_Extension_Type type() const + { return TLSEXT_SRP_IDENTIFIER; } + + SRP_Identifier(const std::string& identifier) : + srp_identifier(identifier) {} + + SRP_Identifier(TLS_Data_Reader& reader); + + std::string identifier() const { return srp_identifier; } + + MemoryVector<byte> serialize() const; + + bool empty() const { return srp_identifier == ""; } + private: + std::string srp_identifier; + }; + +/** +* Represents a block of extensions in a hello message +*/ +class TLS_Extensions + { + public: + size_t count() const { return extensions.size(); } + + TLS_Extension* at(size_t idx) { return extensions.at(idx); } + + void push_back(TLS_Extension* extn) + { extensions.push_back(extn); } + + MemoryVector<byte> serialize() const; + + TLS_Extensions() {} + + TLS_Extensions(TLS_Data_Reader& reader); // deserialize + + ~TLS_Extensions(); + private: + TLS_Extensions(const TLS_Extensions&) {} + TLS_Extensions& operator=(const TLS_Extensions&) { return (*this); } + + std::vector<TLS_Extension*> extensions; + }; + +} + +#endif diff --git a/src/tls/tls_magic.h b/src/tls/tls_magic.h index 9583d28fe..bddbab3ce 100644 --- a/src/tls/tls_magic.h +++ b/src/tls/tls_magic.h @@ -126,6 +126,16 @@ enum Ciphersuite_Code { TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 = 0x006B, TLS_DHE_RSA_WITH_SEED_CBC_SHA = 0x009A, + TLS_SRP_SHA_WITH_3DES_EDE_SHA = 0xC01A, + TLS_SRP_SHA_RSA_WITH_3DES_EDE_SHA = 0xC01B, + TLS_SRP_SHA_DSS_WITH_3DES_EDE_SHA = 0xC01C, + TLS_SRP_SHA_WITH_AES_128_SHA = 0xC01D, + TLS_SRP_SHA_RSA_WITH_AES_128_SHA = 0xC01E, + TLS_SRP_SHA_DSS_WITH_AES_128_SHA = 0xC01F, + TLS_SRP_SHA_WITH_AES_256_SHA = 0xC020, + TLS_SRP_SHA_RSA_WITH_AES_256_SHA = 0xC021, + TLS_SRP_SHA_DSS_WITH_AES_256_SHA = 0xC022, + TLS_ECDHE_ECDSA_WITH_RC4_128_SHA = 0xC007, TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA = 0xC008, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA = 0xC009, @@ -157,6 +167,7 @@ enum TLS_Ciphersuite_Algos { TLS_ALGO_KEYEXCH_RSA = 0x00020000, TLS_ALGO_KEYEXCH_DH = 0x00030000, TLS_ALGO_KEYEXCH_ECDH = 0x00040000, + TLS_ALGO_KEYEXCH_SRP = 0x00050000, TLS_ALGO_MAC_MASK = 0x0000FF00, TLS_ALGO_MAC_MD5 = 0x00000100, diff --git a/src/tls/tls_messages.h b/src/tls/tls_messages.h index 68d564fff..eeaf77a39 100644 --- a/src/tls/tls_messages.h +++ b/src/tls/tls_messages.h @@ -1,6 +1,6 @@ /* * TLS Messages -* (C) 2004-2010 Jack Lloyd +* (C) 2004-2011 Jack Lloyd * * Released under the terms of the Botan license */ @@ -70,7 +70,9 @@ class Client_Hello : public HandshakeMessage Client_Hello(Record_Writer& writer, TLS_Handshake_Hash& hash, const TLS_Policy& policy, - RandomNumberGenerator& rng); + RandomNumberGenerator& rng, + const std::string& hostname = "", + const std::string& srp_identifier = ""); Client_Hello(const MemoryRegion<byte>& buf, Handshake_Type type) diff --git a/src/tls/tls_reader.h b/src/tls/tls_reader.h index a7ef512e9..9f40bb457 100644 --- a/src/tls/tls_reader.h +++ b/src/tls/tls_reader.h @@ -1,6 +1,6 @@ /* * TLS Data Reader -* (C) 2010 Jack Lloyd +* (C) 2010-2011 Jack Lloyd * * Released under the terms of the Botan license */ @@ -8,8 +8,12 @@ #ifndef BOTAN_TLS_READER_H__ #define BOTAN_TLS_READER_H__ +#include <botan/exceptn.h> #include <botan/secmem.h> #include <botan/loadstor.h> +#include <string> +#include <vector> +#include <stdexcept> namespace Botan { @@ -97,6 +101,16 @@ class TLS_Data_Reader return get_elem<T, std::vector<T> >(num_elems); } + std::string get_string(size_t len_bytes, + size_t min_bytes, + size_t max_bytes) + { + std::vector<byte> v = + get_range_vector<byte>(len_bytes, min_bytes, max_bytes); + + return std::string(reinterpret_cast<char*>(&v[0]), v.size()); + } + template<typename T> SecureVector<T> get_fixed(size_t size) { diff --git a/src/tls/tls_session_key.h b/src/tls/tls_session_key.h index c967eaf22..76cb7e3e1 100644 --- a/src/tls/tls_session_key.h +++ b/src/tls/tls_session_key.h @@ -17,7 +17,7 @@ namespace Botan { /** * TLS Session Keys */ -class BOTAN_DLL SessionKeys +class SessionKeys { public: SymmetricKey client_cipher_key() const { return c_cipher; } diff --git a/src/tls/tls_suites.cpp b/src/tls/tls_suites.cpp index a8a56c626..8dbd74e4e 100644 --- a/src/tls/tls_suites.cpp +++ b/src/tls/tls_suites.cpp @@ -141,6 +141,44 @@ TLS_Ciphersuite_Algos CipherSuite::lookup_ciphersuite(u16bit suite) TLS_ALGO_MAC_SHA256 | TLS_ALGO_CIPHER_AES256_CBC); + // SRP ciphersuites + if(suite == TLS_SRP_SHA_RSA_WITH_3DES_EDE_SHA) + return TLS_Ciphersuite_Algos(TLS_ALGO_SIGNER_RSA | + TLS_ALGO_KEYEXCH_SRP | + TLS_ALGO_MAC_SHA1 | + TLS_ALGO_CIPHER_3DES_CBC); + + if(suite == TLS_SRP_SHA_DSS_WITH_3DES_EDE_SHA) + return TLS_Ciphersuite_Algos(TLS_ALGO_SIGNER_DSA | + TLS_ALGO_KEYEXCH_SRP | + TLS_ALGO_MAC_SHA1 | + TLS_ALGO_CIPHER_3DES_CBC); + + if(suite == TLS_SRP_SHA_RSA_WITH_AES_128_SHA) + return TLS_Ciphersuite_Algos(TLS_ALGO_SIGNER_RSA | + TLS_ALGO_KEYEXCH_SRP | + TLS_ALGO_MAC_SHA1 | + TLS_ALGO_CIPHER_AES128_CBC); + + if(suite == TLS_SRP_SHA_DSS_WITH_AES_128_SHA) + return TLS_Ciphersuite_Algos(TLS_ALGO_SIGNER_DSA | + TLS_ALGO_KEYEXCH_SRP | + TLS_ALGO_MAC_SHA1 | + TLS_ALGO_CIPHER_AES128_CBC); + + if(suite == TLS_SRP_SHA_RSA_WITH_AES_256_SHA) + return TLS_Ciphersuite_Algos(TLS_ALGO_SIGNER_RSA | + TLS_ALGO_KEYEXCH_SRP | + TLS_ALGO_MAC_SHA1 | + TLS_ALGO_CIPHER_AES256_CBC); + + if(suite == TLS_SRP_SHA_DSS_WITH_AES_256_SHA) + return TLS_Ciphersuite_Algos(TLS_ALGO_SIGNER_DSA | + TLS_ALGO_KEYEXCH_SRP | + TLS_ALGO_MAC_SHA1 | + TLS_ALGO_CIPHER_AES256_CBC); + + // ECC ciphersuites if(suite == TLS_ECDHE_ECDSA_WITH_RC4_128_SHA) return TLS_Ciphersuite_Algos(TLS_ALGO_SIGNER_ECDSA | TLS_ALGO_KEYEXCH_ECDH | |