diff --git a/lib/TLS/include/Method.h b/lib/TLS/include/Method.h index 6b739ef..a4e364f 100644 --- a/lib/TLS/include/Method.h +++ b/lib/TLS/include/Method.h @@ -65,6 +65,151 @@ namespace eap flags_res_more_frag = 0x40, ///< More fragments }; + /// + /// TLS packet type + /// + /// \sa [The Transport Layer Security (TLS) Protocol Version 1.2 (Chapter: A.1. Record Layer](https://tools.ietf.org/html/rfc5246#appendix-A.1) + /// + enum message_type_t { + message_type_change_cipher_spec = 20, + message_type_alert = 21, + message_type_handshake = 22, + message_type_application_data = 23, + }; + + /// + /// TLS handshake type + /// + /// \sa [The Transport Layer Security (TLS) Protocol Version 1.2 (Chapter: A.4. Handshake Protocol](https://tools.ietf.org/html/rfc5246#appendix-A.4) + /// + enum handshake_type_t { + hello_request = 0, + client_hello = 1, + server_hello = 2, + certificate = 11, + server_key_exchange = 12, + certificate_request = 13, + server_hello_done = 14, + certificate_verify = 15, + client_key_exchange = 16, + finished = 20 + }; + + /// + /// EAP-TLS packet (data) + /// + class packet + { + public: + /// + /// Constructs an empty packet + /// + packet(); + + /// + /// Copies a packet + /// + /// \param[in] other Packet to copy from + /// + packet(_In_ const packet &other); + + /// + /// Moves a packet + /// + /// \param[in] other Packet to move from + /// + packet(_Inout_ packet &&other); + + /// + /// Copies a packet + /// + /// \param[in] other Packet to copy from + /// + /// \returns Reference to this object + /// + packet& operator=(_In_ const packet &other); + + /// + /// Moves a packet + /// + /// \param[in] other Packet to move from + /// + /// \returns Reference to this object + /// + packet& operator=(_Inout_ packet &&other); + + /// + /// Empty the packet + /// + void clear(); + + public: + EapCode m_code; ///< Packet code + unsigned char m_id; ///< Packet ID + unsigned char m_flags; ///< Packet flags + std::vector m_data; ///< Packet data + }; + +#pragma pack(push) +#pragma pack(1) + /// + /// TLS client/server random + /// + struct random + { + __time32_t time; ///< Unix time-stamp + unsigned char data[28]; ///< Randomness + + /// + /// Constructs a all-zero random + /// + random(); + + /// + /// Copies a random + /// + /// \param[in] other Random to copy from + /// + random(_In_ const random &other); + + /// + /// Destructor + /// + ~random(); + + /// + /// Copies a random + /// + /// \param[in] other Random to copy from + /// + /// \returns Reference to this object + /// + random& operator=(_In_ const random &other); + + /// + /// Empty the random + /// + void clear(); + }; +#pragma pack(pop) + +#pragma pack(push) +#pragma pack(1) + /// + /// TLS message + /// + struct message + { + unsigned char type; ///< Message type (one of `message_type_t` constants) + struct { + unsigned char major; ///< Major version + unsigned char minor; ///< Minor version + } version; ///< SSL/TLS version + unsigned char length[2]; ///< Message length (in network byte order) + unsigned char data[1]; ///< Message data + }; +#pragma pack(pop) + public: /// /// Constructs an EAP method @@ -174,7 +319,7 @@ namespace eap /// /// Makes a TLS client hello message /// - /// \sa [The Transport Layer Security (TLS) Protocol Version 1.2 (Chapter 7.4.1.2. Client Hello](https://tools.ietf.org/html/rfc5246#section-7.4.1.2) + /// \sa [The Transport Layer Security (TLS) Protocol Version 1.2 (Chapter 7.4.1.2. Client Hello)](https://tools.ietf.org/html/rfc5246#section-7.4.1.2) /// /// \returns Client Hello message /// @@ -183,139 +328,110 @@ namespace eap /// /// Makes a TLS handshake /// - /// \sa [The Transport Layer Security (TLS) Protocol Version 1.2 (Chapter A.1. Record Layer](https://tools.ietf.org/html/rfc5246#appendix-A.1) + /// \sa [The Transport Layer Security (TLS) Protocol Version 1.2 (Chapter A.1. Record Layer)](https://tools.ietf.org/html/rfc5246#appendix-A.1) /// /// \param[in] msg Handshake data contents - /// \param[in] encrypt Should make an encrypted handshake message? - /// \param[out] msg_h TLS handshake message - /// \param[out] ppEapError Pointer to error descriptor in case of failure. Free using `module::free_error_memory()`. /// - /// \returns - /// - \c true if succeeded - /// - \c false otherwise. See \p ppEapError for details. + /// \returns TLS handshake message /// - bool make_handshake(_In_ const sanitizing_blob &msg, _In_ bool encrypt, _Out_ eap::sanitizing_blob &msg_h, _Out_ EAP_ERROR **ppEapError); + eap::sanitizing_blob make_handshake(_In_ const sanitizing_blob &msg); /// - /// Encrypt block of data + /// Processes a TLS handshake /// - /// \param[in] msg TLS message to encrypt - /// \param[out] msg_enc Encrypted \p msg + /// \sa [The Transport Layer Security (TLS) Protocol Version 1.2 (Chapter A.1. Record Layer)](https://tools.ietf.org/html/rfc5246#appendix-A.1) + /// + /// \param[in] msg TLS handshake message data + /// \param[in] msg_size TLS handshake message data size /// \param[out] ppEapError Pointer to error descriptor in case of failure. Free using `module::free_error_memory()`. /// /// \returns /// - \c true if succeeded /// - \c false otherwise. See \p ppEapError for details. /// - bool encrypt_message(_In_ const sanitizing_blob &msg, _Out_ std::vector &msg_enc, _Out_ EAP_ERROR **ppEapError); + bool process_handshake(_In_bytecount_(msg_size) const void *msg, _In_ size_t msg_size, _Out_ EAP_ERROR **ppEapError); + + /// + /// Encrypt TLS message + /// + /// \param[inout] msg TLS message to encrypt + /// \param[out] ppEapError Pointer to error descriptor in case of failure. Free using `module::free_error_memory()`. + /// + /// \returns + /// - \c true if succeeded + /// - \c false otherwise. See \p ppEapError for details. + /// + bool encrypt_message(_Inout_ sanitizing_blob &msg, _Out_ EAP_ERROR **ppEapError); + + /// + /// Decrypt TLS message + /// + /// \param[inout] msg TLS message to decrypt + /// \param[out] ppEapError Pointer to error descriptor in case of failure. Free using `module::free_error_memory()`. + /// + /// \returns + /// - \c true if succeeded + /// - \c false otherwise. See \p ppEapError for details. + /// + bool decrypt_message(_Inout_ sanitizing_blob &msg, _Out_ EAP_ERROR **ppEapError); + + /// + /// Calculates pseudo-random P_hash data defined in RFC 5246 + /// + /// \sa [The Transport Layer Security (TLS) Protocol Version 1.2 (Chapter: 5. HMAC and the Pseudorandom Function)](https://tools.ietf.org/html/rfc5246#section-5) + /// + /// \param[in] alg Hashing algorithm to use (CALG_MD5 or CALG_SHA1) + /// \param[in] secret Hashing secret key + /// \param[in] size_secret \p secret size + /// \param[in] seed Hashing seed + /// \param[in] size_seed \p seed size + /// \param[in] size Minimum number of bytes of pseudo-random data required + /// \param[out] data Generated pseudo-random data (\p size or longer) + /// \param[out] ppEapError Pointer to error descriptor in case of failure. Free using `module::free_error_memory()`. + /// + /// \returns + /// - \c true if succeeded + /// - \c false otherwise. See \p ppEapError for details. + /// + bool p_hash( + _In_ ALG_ID alg, + _In_bytecount_(size_secret) const void *secret, + _In_ size_t size_secret, + _In_bytecount_(size_seed) const void *seed, + _In_ size_t size_seed, + _In_ size_t size, + _Out_ std::vector data, + _Out_ EAP_ERROR **ppEapError); public: enum phase_t { phase_unknown = -1, phase_client_hello = 0, phase_server_hello = 1, - } m_phase; ///< Session phase + } m_phase; ///< Session phase - struct packet - { - EapCode m_code; ///< Packet code - BYTE m_id; ///< Packet ID - BYTE m_flags; ///< Packet flags - std::vector m_data; ///< Packet data + packet m_packet_req; ///< Request packet + packet m_packet_res; ///< Response packet - /// - /// Constructs an empty packet - /// - packet(); + winstd::crypt_prov m_cp; ///< Cryptography provider + winstd::crypt_key m_key_hmac; ///< Symmetric key for HMAC calculation - /// - /// Copies a packet - /// - /// \param[in] other Packet to copy from - /// - packet(_In_ const packet &other); + winstd::crypt_key m_key_encrypt; ///< Key for encrypting messages + winstd::crypt_key m_key_decrypt; ///< Key for decrypting messages - /// - /// Moves a packet - /// - /// \param[in] other Packet to move from - /// - packet(_Inout_ packet &&other); + random m_random_client; ///< Client random + random m_random_server; ///< Server random - /// - /// Copies a packet - /// - /// \param[in] other Packet to copy from - /// - /// \returns Reference to this object - /// - packet& operator=(_In_ const packet &other); + sanitizing_blob m_session_id; ///< TLS session ID - /// - /// Moves a packet - /// - /// \param[in] other Packet to move from - /// - /// \returns Reference to this object - /// - packet& operator=(_Inout_ packet &&other); + std::list m_server_cert_chain; ///< Server certificate chain - /// - /// Empty the packet - /// - void clear(); - } - m_packet_req, ///< Request packet - m_packet_res; ///< Response packet + bool m_send_client_cert; ///< Did server request client certificate? - winstd::crypt_prov m_cp; ///< Cryptography provider - winstd::crypt_key m_key_hmac; ///< Symmetric key for HMAC calculation - - winstd::crypt_key m_key_write; ///< Key for encrypting messages - -#pragma pack(push) -#pragma pack(1) - struct random - { - unsigned int time; ///< Unix time-stamp - unsigned char data[28]; ///< Randomness - - /// - /// Constructs a all-zero random - /// - random(); - - /// - /// Copies a random - /// - /// \param[in] other Random to copy from - /// - random(_In_ const random &other); - - /// - /// Destructor - /// - ~random(); - - /// - /// Copies a random - /// - /// \param[in] other Random to copy from - /// - /// \returns Reference to this object - /// - random& operator=(_In_ const random &other); - } -#pragma pack(pop) - m_random_client, ///< Client random - m_random_server; ///< Server random - - sanitizing_blob m_session_id; ///< TLS session ID - - winstd::crypt_hash m_hash_handshake_msgs_md5; ///< Running MD5 hash of handshake messages sent - winstd::crypt_hash m_hash_handshake_msgs_sha1; ///< Running SHA-1 hash of handshake messages sent + winstd::crypt_hash m_hash_handshake_msgs_md5; ///< Running MD5 hash of handshake messages sent + winstd::crypt_hash m_hash_handshake_msgs_sha1; ///< Running SHA-1 hash of handshake messages sent protected: - unsigned __int64 m_seq_num; ///< Sequence number for encryption + unsigned __int64 m_seq_num; ///< Sequence number for encryption }; } diff --git a/lib/TLS/src/Method.cpp b/lib/TLS/src/Method.cpp index 198dcec..b3cb99f 100644 --- a/lib/TLS/src/Method.cpp +++ b/lib/TLS/src/Method.cpp @@ -24,12 +24,120 @@ using namespace std; using namespace winstd; +////////////////////////////////////////////////////////////////////// +// eap::method_tls::packet +////////////////////////////////////////////////////////////////////// + +eap::method_tls::packet::packet() : + m_code((EapCode)0), + m_id(0), + m_flags(0) +{ +} + + +eap::method_tls::packet::packet(_In_ const packet &other) : + m_code (other.m_code ), + m_id (other.m_id ), + m_flags(other.m_flags), + m_data (other.m_data ) +{ +} + + +eap::method_tls::packet::packet(_Inout_ packet &&other) : + m_code (std::move(other.m_code )), + m_id (std::move(other.m_id )), + m_flags(std::move(other.m_flags)), + m_data (std::move(other.m_data )) +{ +} + + +eap::method_tls::packet& eap::method_tls::packet::operator=(_In_ const packet &other) +{ + if (this != std::addressof(other)) { + m_code = other.m_code ; + m_id = other.m_id ; + m_flags = other.m_flags; + m_data = other.m_data ; + } + + return *this; +} + + +eap::method_tls::packet& eap::method_tls::packet::operator=(_Inout_ packet &&other) +{ + if (this != std::addressof(other)) { + m_code = std::move(other.m_code ); + m_id = std::move(other.m_id ); + m_flags = std::move(other.m_flags); + m_data = std::move(other.m_data ); + } + + return *this; +} + + +void eap::method_tls::packet::clear() +{ + m_code = (EapCode)0; + m_id = 0; + m_flags = 0; + m_data.clear(); +} + + +////////////////////////////////////////////////////////////////////// +// eap::method_tls::random +////////////////////////////////////////////////////////////////////// + +eap::method_tls::random::random() : + time(0) +{ + memset(data, 0, sizeof(data)); +} + + +eap::method_tls::random::random(_In_ const random &other) : + time(other.time) +{ + memcpy(data, other.data, sizeof(data)); +} + + +eap::method_tls::random::~random() +{ + SecureZeroMemory(data, sizeof(data)); +} + + +eap::method_tls::random& eap::method_tls::random::operator=(_In_ const random &other) +{ + if (this != std::addressof(other)) { + time = other.time; + memcpy(data, other.data, sizeof(data)); + } + + return *this; +} + + +void eap::method_tls::random::clear() +{ + time = 0; + memset(data, 0, sizeof(data)); +} + + ////////////////////////////////////////////////////////////////////// // eap::method_tls ////////////////////////////////////////////////////////////////////// eap::method_tls::method_tls(_In_ module &module, _In_ config_method_tls &cfg, _In_ credentials_tls &cred) : m_phase(phase_unknown), + m_send_client_cert(false), m_seq_num(0), method(module, cfg, cred) { @@ -43,6 +151,8 @@ eap::method_tls::method_tls(_In_ const method_tls &other) : m_random_client(other.m_random_client), m_random_server(other.m_random_server), m_session_id(other.m_session_id), + m_server_cert_chain(other.m_server_cert_chain), + m_send_client_cert(other.m_send_client_cert), m_hash_handshake_msgs_md5(other.m_hash_handshake_msgs_md5), m_hash_handshake_msgs_sha1(other.m_hash_handshake_msgs_sha1), m_seq_num(other.m_seq_num), @@ -58,6 +168,8 @@ eap::method_tls::method_tls(_Inout_ method_tls &&other) : m_random_client(std::move(other.m_random_client)), m_random_server(std::move(other.m_random_server)), m_session_id(std::move(other.m_session_id)), + m_server_cert_chain(std::move(other.m_server_cert_chain)), + m_send_client_cert(std::move(other.m_send_client_cert)), m_hash_handshake_msgs_md5(std::move(other.m_hash_handshake_msgs_md5)), m_hash_handshake_msgs_sha1(std::move(other.m_hash_handshake_msgs_sha1)), m_seq_num(std::move(other.m_seq_num)), @@ -76,6 +188,8 @@ eap::method_tls& eap::method_tls::operator=(_In_ const method_tls &other) m_random_client = other.m_random_client; m_random_server = other.m_random_server; m_session_id = other.m_session_id; + m_server_cert_chain = other.m_server_cert_chain; + m_send_client_cert = other.m_send_client_cert; m_hash_handshake_msgs_md5 = other.m_hash_handshake_msgs_md5; m_hash_handshake_msgs_sha1 = other.m_hash_handshake_msgs_sha1; m_seq_num = other.m_seq_num; @@ -95,6 +209,8 @@ eap::method_tls& eap::method_tls::operator=(_Inout_ method_tls &&other) m_random_client = std::move(other.m_random_client); m_random_server = std::move(other.m_random_server); m_session_id = std::move(other.m_session_id); + m_server_cert_chain = std::move(other.m_server_cert_chain); + m_send_client_cert = std::move(other.m_send_client_cert); m_hash_handshake_msgs_md5 = std::move(other.m_hash_handshake_msgs_md5); m_hash_handshake_msgs_sha1 = std::move(other.m_hash_handshake_msgs_sha1); m_seq_num = std::move(other.m_seq_num); @@ -203,13 +319,20 @@ bool eap::method_tls::process_request_packet( // This is the TLS start message: initialize method. m_phase = phase_client_hello; m_packet_res.clear(); + m_key_hmac.free(); + m_key_encrypt.free(); + m_key_decrypt.free(); // Generate client randomness. - m_random_client.time = (unsigned int)time(NULL); + _time32(&m_random_client.time); if (!CryptGenRandom(m_cp, sizeof(m_random_client.data), m_random_client.data)) { *ppEapError = m_module.make_error(GetLastError(), _T(__FUNCTION__) _T(" Error creating client randomness.")); return false; } + m_random_server.clear(); + m_server_cert_chain.clear(); + m_send_client_cert = false; + m_session_id.clear(); // Create MD5 hash object. if (!m_hash_handshake_msgs_md5.create(m_cp, CALG_MD5, NULL, 0)) { @@ -257,8 +380,7 @@ bool eap::method_tls::process_request_packet( m_packet_res.m_id = m_packet_req.m_id; m_packet_res.m_flags = 0; sanitizing_blob hello(make_client_hello()); - sanitizing_blob handshake; - if (!make_handshake(hello, false, handshake, ppEapError)) return false; + sanitizing_blob handshake(make_handshake(hello)); m_packet_res.m_data.assign(handshake.begin(), handshake.end()); pEapOutput->fAllowNotifications = FALSE; pEapOutput->action = EapPeerMethodResponseActionSend; @@ -271,7 +393,20 @@ bool eap::method_tls::process_request_packet( break; } - case phase_server_hello: + case phase_server_hello: { + if (m_packet_req.m_data.size() < 5) { + *ppEapError = m_module.make_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, wstring_printf(_T(__FUNCTION__) _T(" TLS message too small (expected >=5, received %uB)."), m_packet_req.m_data.size()).c_str()); + return false; + }; + const message *msg = (const message*)m_packet_req.m_data.data(); + if (msg->type != message_type_handshake) { + *ppEapError = m_module.make_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, wstring_printf(_T(__FUNCTION__) _T(" Wrong TLS message (expected %u, received %uB)."), message_type_handshake, msg->type).c_str()); + return false; + } else if (!process_handshake(msg->data, std::min(ntohs(*(unsigned short*)msg->length), m_packet_req.m_data.size() - 5), ppEapError)) + return false; + + //break; + } default: *ppEapError = m_module.make_error(ERROR_NOT_SUPPORTED, _T(__FUNCTION__) _T(" Not supported.")); @@ -376,7 +511,7 @@ eap::sanitizing_blob eap::method_tls::make_client_hello() const // SSL header assert(size_data <= 0xffffff); - unsigned int ssl_header = htonl(0x01000000 | (unsigned int)size_data); // client_hello (0x01) + unsigned int ssl_header = htonl(((unsigned int)client_hello << 24) | (unsigned int)size_data); msg.insert(msg.end(), (unsigned char*)&ssl_header, (unsigned char*)(&ssl_header + 1)); // SSL version: TLS 1.0 @@ -384,9 +519,7 @@ eap::sanitizing_blob eap::method_tls::make_client_hello() const msg.push_back(1); // SSL minor version // Client random - unsigned int time = htonl(m_random_client.time); - msg.insert(msg.end(), (unsigned char*)&time, (unsigned char*)(&time + 1)); - msg.insert(msg.end(), m_random_client.data, m_random_client.data + _countof(m_random_client.data)); // TODO: Check if byte order should be changed! + msg.insert(msg.end(), (unsigned char*)&m_random_client, (unsigned char*)(&m_random_client + 1)); // Session ID assert(m_session_id.size() <= 32); @@ -407,29 +540,11 @@ eap::sanitizing_blob eap::method_tls::make_client_hello() const } -bool eap::method_tls::make_handshake(_In_ const sanitizing_blob &msg, _In_ bool encrypt, _Out_ eap::sanitizing_blob &msg_h, _Out_ EAP_ERROR **ppEapError) +eap::sanitizing_blob eap::method_tls::make_handshake(_In_ const sanitizing_blob &msg) { - const unsigned char *msg_ptr; - size_t size_msg; - vector msg_enc; - - if (encrypt) { - // Create an unencrypted handshake first. - eap::sanitizing_blob msg_h_unenc; - if (!make_handshake(msg, false, msg_h_unenc, ppEapError)) return false; - - // Encrypt it. - if (!encrypt_message(msg_h_unenc, msg_enc, ppEapError)) return false; - - msg_ptr = msg_enc.data(); - size_msg = msg_enc.size(); - } else { - msg_ptr = msg.data(); - size_msg = msg.size(); - } - // Create a handshake. - msg_h.clear(); + size_t size_msg = msg.size(); + eap::sanitizing_blob msg_h; msg_h.reserve( 1 + // SSL record type 2 + // SSL version @@ -437,7 +552,7 @@ bool eap::method_tls::make_handshake(_In_ const sanitizing_blob &msg, _In_ bool size_msg); // Message // SSL record type - msg_h.push_back(22); // handshake (22) + msg_h.push_back((unsigned char)message_type_handshake); // SSL version: TLS 1.0 msg_h.push_back(3); // SSL major version @@ -447,37 +562,162 @@ bool eap::method_tls::make_handshake(_In_ const sanitizing_blob &msg, _In_ bool assert(size_msg <= 0xffff); unsigned short size_msg_n = htons((unsigned short)size_msg); msg_h.insert(msg_h.end(), (unsigned char*)&size_msg_n, (unsigned char*)(&size_msg_n + 1)); - msg_h.insert(msg_h.end(), msg_ptr, msg_ptr + size_msg); + msg_h.insert(msg_h.end(), msg.begin(), msg.end()); + + return msg_h; +} + + +bool eap::method_tls::process_handshake(_In_bytecount_(msg_size) const void *_msg, _In_ size_t msg_size, _Out_ EAP_ERROR **ppEapError) +{ + for (const unsigned char *msg = (const unsigned char*)_msg, *msg_end = msg + msg_size; msg < msg_end; ) { + // Parse record header. + if (msg + sizeof(unsigned int) > msg_end) { + *ppEapError = m_module.make_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, _T(__FUNCTION__) _T(" Incomplete record header.")); + return false; + } + unsigned int hdr = ntohl(*(unsigned int*)msg); + const unsigned char + *rec = msg + sizeof(unsigned int), + *rec_end = rec + (hdr & 0xffffff); + if (rec_end > msg_end) { + *ppEapError = m_module.make_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, _T(__FUNCTION__) _T(" Incomplete record rec.")); + return false; + } + + // Process record. + switch (hdr >> 24) { + case server_hello: + // TLS version + if (rec + 2 > rec_end) { + *ppEapError = m_module.make_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, _T(__FUNCTION__) _T(" Server SSL/TLS version missing or incomplete.")); + return false; + } else if (rec[0] != 3 || rec[1] != 1) { + *ppEapError = m_module.make_error(ERROR_NOT_SUPPORTED, _T(__FUNCTION__) _T(" Unsupported SSL/TLS version.")); + return false; + } + rec += 2; + + // Server random + if (rec + sizeof(m_random_server) > rec_end) { + *ppEapError = m_module.make_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, _T(__FUNCTION__) _T(" Server random missing or incomplete.")); + return false; + } + memcpy(&m_random_server, rec, sizeof(m_random_server)); + rec += sizeof(m_random_server); + + // Session ID + if (rec + 1 > rec_end || rec + 1 + rec[0] > rec_end) { + *ppEapError = m_module.make_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, _T(__FUNCTION__) _T(" Session ID missing or incomplete.")); + return false; + } + assert(rec[0] <= 32); // According to RFC 5246 session IDs should not be longer than 32B. + m_session_id.assign(rec + 1, rec + 1 + rec[0]); + rec += rec[0] + 1; + + // Cipher + if (rec + 2 > rec_end) { + *ppEapError = m_module.make_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, _T(__FUNCTION__) _T(" Cipher or incomplete.")); + return false; + } + if (rec[0] != 0x00 || rec[1] != 0x0a) { + *ppEapError = m_module.make_error(ERROR_NOT_SUPPORTED, wstring_printf(_T(__FUNCTION__) _T(" Other than requested cipher selected (expected 0x000a, received 0x%02x%02x)."), rec[0], rec[1]).c_str()); + return false; + } + + break; + + case certificate: { + // Certificate list size + if (rec + 3 > rec_end) { + *ppEapError = m_module.make_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, _T(__FUNCTION__) _T(" Certificate list size missing or incomplete.")); + return false; + } + const unsigned char + *list = rec + 3, + *list_end = list + ((rec[0] << 16) | (rec[1] << 8) | rec[2]); + if (list_end > rec_end) { + *ppEapError = m_module.make_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, _T(__FUNCTION__) _T(" Certificate list missing or incomplete.")); + return false; + } + + m_server_cert_chain.clear(); + while (list < list_end) { + // Certificate size + if (list + 3 > list_end) { + *ppEapError = m_module.make_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, _T(__FUNCTION__) _T(" Certificate size missing or incomplete.")); + return false; + } + const unsigned char + *cert = list + 3, + *cert_end = cert + ((list[0] << 16) | (list[1] << 8) | list[2]); + if (cert_end > list_end) { + *ppEapError = m_module.make_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, _T(__FUNCTION__) _T(" Certificate rec missing or incomplete.")); + return false; + } + + // Certificate + cert_context c; + if (!c.create(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, cert, (DWORD)(cert_end - cert))) { + *ppEapError = m_module.make_error(GetLastError(), _T(__FUNCTION__) _T(" Error reading certificate.")); + return false; + } + m_server_cert_chain.push_back(std::move(c)); + + list = cert_end; + } + + break; + } + + case certificate_request: + m_send_client_cert = true; + break; + + case finished: + if (rec_end - rec != 12) { + *ppEapError = m_module.make_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, wstring_printf(_T(__FUNCTION__) _T(" \"finished\" size incorrect (expected 12B, received %u)."), rec_end - rec).c_str()); + return false; + } + + vector hash, hash_sha1; + if (!CryptGetHashParam(m_hash_handshake_msgs_md5, HP_HASHVAL, hash, 0)) { + *ppEapError = m_module.make_error(GetLastError(), _T(__FUNCTION__) _T(" Error finishing MD5 hash calculation.")); + return false; + } + if (!CryptGetHashParam(m_hash_handshake_msgs_sha1, HP_HASHVAL, hash_sha1, 0)) { + *ppEapError = m_module.make_error(GetLastError(), _T(__FUNCTION__) _T(" Error finishing SHA-1 hash calculation.")); + return false; + } + hash.insert(hash.end(), hash_sha1.begin(), hash_sha1.end()); + } + + msg = rec_end; + } return true; } -bool eap::method_tls::encrypt_message(_In_ const sanitizing_blob &msg, _Out_ std::vector &msg_enc, _Out_ EAP_ERROR **ppEapError) +bool eap::method_tls::encrypt_message(_Inout_ sanitizing_blob &msg, _Out_ EAP_ERROR **ppEapError) { assert(ppEapError); // Create a HMAC hash. crypt_hash hash_hmac; - if (!hash_hmac.create(m_cp, CALG_HMAC, m_key_hmac, 0)) { + static const HMAC_INFO s_hmac_info = { CALG_SHA1 }; + if (!hash_hmac.create(m_cp, CALG_HMAC, m_key_hmac, 0) || + !CryptSetHashParam(hash_hmac, HP_HMAC_INFO, (const BYTE*)&s_hmac_info, 0)) + { *ppEapError = m_module.make_error(GetLastError(), _T(__FUNCTION__) _T(" Error creating HMAC hash.")); return false; } - static const HMAC_INFO s_hmac_info = { CALG_SHA1 }; - if (!CryptSetHashParam(hash_hmac, HP_HMAC_INFO, (const BYTE*)&s_hmac_info, 0)) { - *ppEapError = m_module.make_error(GetLastError(), _T(__FUNCTION__) _T(" Error setting hash parameter.")); - return false; - } - // Hash sequence number. + // Hash sequence number and message. unsigned __int64 seq_num = htonll(m_seq_num); - if (!CryptHashData(hash_hmac, (const BYTE*)&seq_num, sizeof(seq_num), 0)) { - *ppEapError = m_module.make_error(GetLastError(), _T(__FUNCTION__) _T(" Error hashing data.")); - return false; - } - - // Hash message. - if (!CryptHashData(hash_hmac, msg.data(), (DWORD)msg.size(), 0)) { + if (!CryptHashData(hash_hmac, (const BYTE*)&seq_num, sizeof(seq_num), 0) || + !CryptHashData(hash_hmac, msg.data(), (DWORD)msg.size(), 0)) + { *ppEapError = m_module.make_error(GetLastError(), _T(__FUNCTION__) _T(" Error hashing data.")); return false; } @@ -489,31 +729,30 @@ bool eap::method_tls::encrypt_message(_In_ const sanitizing_blob &msg, _Out_ std return false; } + // Remove SSL/TLS header (record type, version, message size). + msg.erase(msg.begin(), msg.begin() + 5); + size_t size = - msg.size() - 5 + // TLS message without SSL header (SSL record type, SSL version, Message size) - 20 + // SHA-1 - 1; // Padding length + msg.size() + // TLS message + 20 + // HMAC hash (SHA-1) + 1; // Padding length unsigned char padding = (8 - size) % 8; size += padding; - - // Copy data. - sanitizing_blob enc; - enc.reserve(size); - enc.assign(msg.begin() + 5, msg.end()); + msg.reserve(size); // Append HMAC hash. #ifdef _HOST_LOW_ENDIAN std::reverse(hmac.begin(), hmac.end()); #endif - enc.insert(enc.end(), hmac.begin(), hmac.end()); + msg.insert(msg.end(), hmac.begin(), hmac.end()); // Append padding. - enc.insert(enc.end(), padding + 1, padding); + msg.insert(msg.end(), padding + 1, padding); // Encrypt. assert(size < 0xffffffff); DWORD size2 = (DWORD)size; - if (!CryptEncrypt(m_key_write, NULL, FALSE, 0, enc.data(), &size2, (DWORD)size)) { + if (!CryptEncrypt(m_key_encrypt, NULL, FALSE, 0, msg.data(), &size2, (DWORD)size)) { *ppEapError = m_module.make_error(GetLastError(), _T(__FUNCTION__) _T(" Error encrypting message.")); return false; } @@ -521,107 +760,107 @@ bool eap::method_tls::encrypt_message(_In_ const sanitizing_blob &msg, _Out_ std // Increment sequence number. m_seq_num++; - // Copy to output. - msg_enc.assign(enc.begin(), enc.end()); return true; } -////////////////////////////////////////////////////////////////////// -// eap::method_tls::packet -////////////////////////////////////////////////////////////////////// - -eap::method_tls::packet::packet() : - m_code((EapCode)0), - m_id(0), - m_flags(0) +bool eap::method_tls::decrypt_message(_Inout_ sanitizing_blob &msg, _Out_ EAP_ERROR **ppEapError) { -} - - -eap::method_tls::packet::packet(_In_ const packet &other) : - m_code (other.m_code ), - m_id (other.m_id ), - m_flags(other.m_flags), - m_data (other.m_data ) -{ -} - - -eap::method_tls::packet::packet(_Inout_ packet &&other) : - m_code (std::move(other.m_code )), - m_id (std::move(other.m_id )), - m_flags(std::move(other.m_flags)), - m_data (std::move(other.m_data )) -{ -} - - -eap::method_tls::packet& eap::method_tls::packet::operator=(_In_ const packet &other) -{ - if (this != std::addressof(other)) { - m_code = other.m_code ; - m_id = other.m_id ; - m_flags = other.m_flags; - m_data = other.m_data ; + // Decrypt. + DWORD size = (DWORD)msg.size(); + if (!CryptDecrypt(m_key_decrypt, NULL, FALSE, 0, msg.data(), &size)) { + *ppEapError = m_module.make_error(GetLastError(), _T(__FUNCTION__) _T(" Error decrypting message.")); + return false; } - return *this; + // Remove padding. + msg.resize(size - msg.back() - 1); + return true; } -eap::method_tls::packet& eap::method_tls::packet::operator=(_Inout_ packet &&other) +bool eap::method_tls::p_hash( + _In_ ALG_ID alg, + _In_bytecount_(size_secret) const void *secret, + _In_ size_t size_secret, + _In_bytecount_(size_seed) const void *seed, + _In_ size_t size_seed, + _In_ size_t size, + _Out_ vector data, + _Out_ EAP_ERROR **ppEapError) { - if (this != std::addressof(other)) { - m_code = std::move(other.m_code ); - m_id = std::move(other.m_id ); - m_flags = std::move(other.m_flags); - m_data = std::move(other.m_data ); + // HMAC symmetric key generation. + crypt_hash hash_key; + if (!hash_key.create(m_cp, alg, 0, 0)) { + *ppEapError = m_module.make_error(GetLastError(), _T(__FUNCTION__) _T(" Error creating key hash.")); + return false; + } + if (!CryptHashData(hash_key, (const BYTE*)secret, (DWORD)size_secret, 0)) { + *ppEapError = m_module.make_error(GetLastError(), _T(__FUNCTION__) _T(" Error hashing secret.")); + return false; + } + crypt_key key_hmac; + key_hmac.derive(m_cp, CALG_RC4, hash_key, 0); + vector block; + const HMAC_INFO hmac_info = { alg }; + + data.clear(); + data.reserve(size); + + // https://tools.ietf.org/html/rfc5246#section-5: + // + // P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) + + // HMAC_hash(secret, A(2) + seed) + + // HMAC_hash(secret, A(3) + seed) + ... + // + // where + indicates concatenation. + // + // A() is defined as: + // + // A(0) = seed + // A(i) = HMAC_hash(secret, A(i-1)) + + vector A((unsigned char*)seed, (unsigned char*)seed + size_seed); + while (data.size() < size) { + // Hash A. + crypt_hash hash_hmac1; + if (!hash_hmac1.create(m_cp, CALG_HMAC, key_hmac, 0) || + !CryptSetHashParam(hash_hmac1, HP_HMAC_INFO, (const BYTE*)&hmac_info, 0)) + { + *ppEapError = m_module.make_error(GetLastError(), _T(__FUNCTION__) _T(" Error creating HMAC hash.")); + return false; + } + if (!CryptHashData(hash_hmac1, A.data(), (DWORD)A.size(), 0)) { + *ppEapError = m_module.make_error(GetLastError(), _T(__FUNCTION__) _T(" Error hashing A.")); + return false; + } + if (!CryptGetHashParam(hash_hmac1, HP_HASHVAL, A, 0)) { + *ppEapError = m_module.make_error(GetLastError(), _T(__FUNCTION__) _T(" Error finishing hash A calculation.")); + return false; + } + + // Hash A and seed. + crypt_hash hash_hmac2; + if (!hash_hmac2.create(m_cp, CALG_HMAC, key_hmac, 0) || + !CryptSetHashParam(hash_hmac2, HP_HMAC_INFO, (const BYTE*)&hmac_info, 0)) + { + *ppEapError = m_module.make_error(GetLastError(), _T(__FUNCTION__) _T(" Error creating A+seed hash.")); + return false; + } + if (!CryptHashData(hash_hmac2, A.data(), (DWORD)A.size(), 0) || + !CryptHashData(hash_hmac2, (const BYTE*)seed, (DWORD)size_seed, 0)) + { + *ppEapError = m_module.make_error(GetLastError(), _T(__FUNCTION__) _T(" Error hashing seed.")); + return false; + } + if (!CryptGetHashParam(hash_hmac2, HP_HASHVAL, block, 0)) { + *ppEapError = m_module.make_error(GetLastError(), _T(__FUNCTION__) _T(" Error finishing hash A+seed calculation.")); + return false; + } + + // Append to output data. + data.insert(data.end(), block.begin(), block.end()); } - return *this; -} - - -void eap::method_tls::packet::clear() -{ - m_code = (EapCode)0; - m_id = 0; - m_flags = 0; - m_data.clear(); -} - - -////////////////////////////////////////////////////////////////////// -// eap::method_tls::random -////////////////////////////////////////////////////////////////////// - -eap::method_tls::random::random() : - time(0) -{ - memset(data, 0, sizeof(data)); -} - - -eap::method_tls::random::random(_In_ const random &other) : - time(other.time) -{ - memcpy(data, other.data, sizeof(data)); -} - - -eap::method_tls::random::~random() -{ - SecureZeroMemory(data, sizeof(data)); -} - - -eap::method_tls::random& eap::method_tls::random::operator=(_In_ const random &other) -{ - if (this != std::addressof(other)) { - time = other.time; - memcpy(data, other.data, sizeof(data)); - } - - return *this; + return true; }