diff --git a/include/Common.props b/include/Common.props index 68b5890..f384c8e 100644 --- a/include/Common.props +++ b/include/Common.props @@ -32,7 +32,7 @@ Level4 - _WIN32_WINNT=0x0600;ISOLATION_AWARE_ENABLED=1;SECURITY_WIN32;CERT_CHAIN_PARA_HAS_EXTRA_FIELDS;%(PreprocessorDefinitions) + _WIN32_WINNT=0x0600;ISOLATION_AWARE_ENABLED=1;SECURITY_WIN32;CERT_CHAIN_PARA_HAS_EXTRA_FIELDS;EAP_TLS=1;%(PreprocessorDefinitions) Use StdAfx.h ProgramDatabase diff --git a/lib/TLS/include/Config.h b/lib/TLS/include/Config.h index 7271632..78a42db 100644 --- a/lib/TLS/include/Config.h +++ b/lib/TLS/include/Config.h @@ -25,6 +25,10 @@ #include +#define EAP_TLS_OWN 0 ///< We do the TLS ourself +#define EAP_TLS_SCHANNEL 1 ///< TLS is done by Schannel, but server certificate check is done ourself +#define EAP_TLS_SCHANNEL_FULL 2 ///< TLS is fully done by Schannel + namespace eap { /// @@ -168,5 +172,11 @@ namespace eap public: std::list m_trusted_root_ca; ///< Trusted root CAs std::list m_server_names; ///< Acceptable authenticating server names + +#if EAP_TLS < EAP_TLS_SCHANNEL + // Following members are used for session resumptions. They are not exported/imported to XML. + sanitizing_blob m_session_id; ///< TLS session ID + tls_master_secret m_master_secret; ///< TLS master secret +#endif }; } diff --git a/lib/TLS/include/Method.h b/lib/TLS/include/Method.h index 589e762..dd20087 100644 --- a/lib/TLS/include/Method.h +++ b/lib/TLS/include/Method.h @@ -128,6 +128,19 @@ namespace eap std::vector m_data; ///< Packet data }; +#pragma pack(push) +#pragma pack(1) + /// + /// TLS message + /// + struct message_header + { + tls_message_type_t type; ///< Message type (one of `message_type_t` constants) + tls_version version; ///< SSL/TLS version + unsigned char length[2]; ///< Message length (in network byte order) + }; +#pragma pack(pop) + public: /// /// Constructs an EAP method @@ -204,6 +217,152 @@ namespace eap /// @} protected: +#if EAP_TLS < EAP_TLS_SCHANNEL + /// \name Client handshake message generation + /// @{ + + /// + /// 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) + /// + /// \returns Client hello message + /// + sanitizing_blob make_client_hello(); + + /// + /// Makes a TLS client certificate message + /// + /// \sa [The Transport Layer Security (TLS) Protocol Version 1.2 (Chapter 7.4.6. Client Certificate)](https://tools.ietf.org/html/rfc5246#section-7.4.6) + /// + /// \returns Client certificate message + /// + sanitizing_blob make_client_cert() const; + + /// + /// Makes a TLS client key exchange message + /// + /// \sa [The Transport Layer Security (TLS) Protocol Version 1.2 (Chapter 7.4.7. Client Key Exchange Message )](https://tools.ietf.org/html/rfc5246#section-7.4.7) + /// + /// \param[in] pms Pre-master secret + /// + /// \returns Client key exchange message + /// + sanitizing_blob make_client_key_exchange(_In_ const tls_master_secret &pms) const; + + /// + /// Makes a TLS finished message + /// + /// \sa [The Transport Layer Security (TLS) Protocol Version 1.2 (Chapter A.1. Record Layer)](https://tools.ietf.org/html/rfc5246#appendix-A.1) + /// + /// \returns Change cipher spec + /// + eap::sanitizing_blob make_finished() const; + + /// @} + + /// \name Client/Server handshake hashing + /// @{ + + /// + /// Hashes handshake message for "finished" message validation. + /// + /// \sa [The Transport Layer Security (TLS) Protocol Version 1.2 (Chapter 7.4.9. Finished)](https://tools.ietf.org/html/rfc5246#section-7.4.9) + /// + /// \param[in] data Data to hash + /// \param[in] size \p data size in bytes + /// + inline void hash_handshake(_In_count_(size) const void *data, _In_ size_t size) + { + CryptHashData(m_hash_handshake_msgs_md5 , (const BYTE*)data, (DWORD)size, 0); + CryptHashData(m_hash_handshake_msgs_sha1 , (const BYTE*)data, (DWORD)size, 0); + CryptHashData(m_hash_handshake_msgs_sha256, (const BYTE*)data, (DWORD)size, 0); + } + + /// + /// Hashes handshake message for "finished" message validation. + /// + /// \sa [The Transport Layer Security (TLS) Protocol Version 1.2 (Chapter 7.4.9. Finished)](https://tools.ietf.org/html/rfc5246#section-7.4.9) + /// + /// \param[in] data Data to hash + /// \param[in] size \p data size in bytes + /// + template + inline void hash_handshake(_In_ const std::vector<_Ty, _Ax> &data) + { + hash_handshake(data.data(), data.size() * sizeof(_Ty)); + } + + /// @} + + /// + /// Makes a TLS message + /// + /// \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] type Message type + /// \param[inout] data Message data contents + /// + /// \returns TLS message message + /// + eap::sanitizing_blob make_message(_In_ tls_message_type_t type, _Inout_ sanitizing_blob &&data); + + /// @} + + /// \name Key derivation + /// @{ + + /// + /// Generates master session key + /// + /// \sa [The EAP-TLS Authentication Protocol (Chapter 2.3. Key Hierarchy)](https://tools.ietf.org/html/rfc5216#section-2.3) + /// + virtual void derive_msk(); + + /// @} + + /// \name Server message processing + /// @{ + + /// + /// Processes messages in a TLS packet + /// + /// \param[in] pck Packet data + /// \param[in] size_pck \p pck size in bytes + /// + void process_packet(_In_bytecount_(size_pck) const void *pck, _In_ size_t size_pck); + + /// + /// Processes a TLS change_cipher_spec message + /// + /// \sa [The Transport Layer Security (TLS) Protocol Version 1.2 (Chapter 7.1. Change Cipher Spec Protocol)](https://tools.ietf.org/html/rfc5246#section-7.1) + /// + /// \param[in] msg TLS change_cipher_spec message data + /// \param[in] msg_size TLS change_cipher_spec message data size + /// + virtual void process_change_cipher_spec(_In_bytecount_(msg_size) const void *msg, _In_ size_t msg_size); + + /// + /// Processes a TLS alert message + /// + /// \sa [The Transport Layer Security (TLS) Protocol Version 1.2 (Chapter 7.2. Alert Protocol)](https://tools.ietf.org/html/rfc5246#section-7.2) + /// + /// \param[in] msg TLS alert message data + /// \param[in] msg_size TLS alert message data size + /// + virtual void process_alert(_In_bytecount_(msg_size) const void *msg, _In_ size_t msg_size); + + /// + /// Processes a TLS handshake message + /// + /// \sa [The Transport Layer Security (TLS) Protocol Version 1.2 (Chapter 7.4. Handshake Protocol)](https://tools.ietf.org/html/rfc5246#section-7.4) + /// + /// \param[in] msg TLS handshake message data + /// \param[in] msg_size TLS handshake message data size + /// + virtual void process_handshake(_In_bytecount_(msg_size) const void *msg, _In_ size_t msg_size); + +#else /// /// Process handshake /// @@ -213,29 +372,172 @@ namespace eap /// Process application data /// void process_application_data(); +#endif /// - /// Processes an application message + /// Processes a TLS application_data message /// - /// \param[in] msg Application message data - /// \param[in] size_msg Application message data size + /// \sa [The Transport Layer Security (TLS) Protocol Version 1.2 (Chapter 10. Application Data Protocol)](https://tools.ietf.org/html/rfc5246#section-10) /// - virtual void process_application_data(_In_bytecount_(size_msg) const void *msg, _In_ size_t size_msg); + /// \param[in] msg TLS application_data message data + /// \param[in] msg_size TLS application_data message data size + /// + virtual void process_application_data(_In_bytecount_(msg_size) const void *msg, _In_ size_t msg_size); -#ifndef SCHANNEL_SRV_CERT_CHECK + /// @} + +#if EAP_TLS < EAP_TLS_SCHANNEL_FULL /// /// Verifies server's certificate if trusted by configuration /// void verify_server_trust() const; #endif +#if EAP_TLS < EAP_TLS_SCHANNEL + /// \name Encryption + /// @{ + + /// + /// Encrypt TLS message + /// + /// \param[in] type Message type + /// \param[inout] data TLS message to encrypt + /// + void encrypt_message(_In_ tls_message_type_t type, _Inout_ sanitizing_blob &data); + + /// + /// Decrypt TLS message + /// + /// \param[in] type Original message type for HMAC verification + /// \param[inout] data TLS message to decrypt + /// + void decrypt_message(_In_ tls_message_type_t type, _Inout_ sanitizing_blob &data); + + /// @} + + /// \name Pseudo-random generation + /// @{ + + /// + /// Calculates pseudo-random P_hash data defined in RFC 5246 + /// + /// \sa [The Transport Layer Security (TLS) Protocol Version 1.1 (Chapter 5. HMAC and the Pseudorandom Function)](https://tools.ietf.org/html/rfc4346#section-5) + /// \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] cp Handle of the cryptographics provider + /// \param[in] alg Hashing Algorithm to use (CALG_TLS1PRF = combination of MD5 and SHA-1, CALG_SHA_256...) + /// \param[in] secret Hashing secret key + /// \param[in] seed Random seed + /// \param[in] size_seed \p seed size + /// \param[in] size Number of bytes of pseudo-random data required + /// + /// \returns Generated pseudo-random data (\p size bytes) + /// + static sanitizing_blob prf( + _In_ HCRYPTPROV cp, + _In_ ALG_ID alg, + _In_ const tls_master_secret &secret, + _In_bytecount_(size_seed) const void *seed, + _In_ size_t size_seed, + _In_ size_t size); + + /// + /// Calculates pseudo-random P_hash data defined in RFC 5246 + /// + /// \sa [The Transport Layer Security (TLS) Protocol Version 1.1 (Chapter 5. HMAC and the Pseudorandom Function)](https://tools.ietf.org/html/rfc4346#section-5) + /// \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] cp Handle of the cryptographics provider + /// \param[in] alg Hashing Algorithm to use (CALG_TLS1PRF = combination of MD5 and SHA-1, CALG_SHA_256...) + /// \param[in] secret Hashing secret key + /// \param[in] seed Random seed + /// \param[in] size Number of bytes of pseudo-random data required + /// + /// \returns Generated pseudo-random data (\p size bytes) + /// + template + inline static sanitizing_blob prf( + _In_ HCRYPTPROV cp, + _In_ ALG_ID alg, + _In_ const tls_master_secret &secret, + _In_ const std::vector<_Ty, _Ax> &seed, + _In_ size_t size) + { + return prf(cp, alg, secret, seed.data(), seed.size() * sizeof(_Ty), size); + } + + /// @} + + /// + /// Creates a key + /// + /// \sa [How to export and import plain text session keys by using CryptoAPI](https://support.microsoft.com/en-us/kb/228786) + /// + /// \param[in] cp Handle of the cryptographics provider + /// \param[in] alg Key algorithm + /// \param[in] key Key that decrypts \p secret + /// \param[in] secret Key data + /// \param[in] size_secret \p secret size + /// + /// \returns Key + /// + HCRYPTKEY create_key( + _In_ HCRYPTPROV cp, + _In_ ALG_ID alg, + _In_ HCRYPTKEY key, + _In_bytecount_(size_secret) const void *secret, + _In_ size_t size_secret); +#endif + protected: credentials_tls &m_cred; ///< EAP-TLS user credentials + HANDLE m_user_ctx; ///< Handle to user context packet m_packet_req; ///< Request packet packet m_packet_res; ///< Response packet - HANDLE m_user_ctx; ///< Handle to user context +#if EAP_TLS < EAP_TLS_SCHANNEL + winstd::crypt_prov m_cp; ///< Cryptography provider for general services + winstd::crypt_prov m_cp_enc_client; ///< Cryptography provider for encryption + winstd::crypt_prov m_cp_enc_server; ///< Cryptography provider for encryption + winstd::crypt_key m_key_exp1; ///< Key for importing derived keys + + tls_version m_tls_version; ///< TLS version in use + ALG_ID m_alg_prf; ///< Pseudo-random function algorithm in use + + tls_conn_state m_state_client; ///< Client TLS connection state + tls_conn_state m_state_client_pending; ///< Client TLS connection state (pending) + tls_conn_state m_state_server; ///< Server TLS connection state + tls_conn_state m_state_server_pending; ///< Server TLS connection state (pending) + + tls_master_secret m_master_secret; ///< TLS master secret + tls_random m_random_client; ///< Client random + tls_random m_random_server; ///< Server random + + tls_random m_key_mppe_client; ///< MS-MPPE-Recv-Key + tls_random m_key_mppe_server; ///< MS-MPPE-Send-Key + + sanitizing_blob m_session_id; ///< TLS session ID + + std::list m_server_cert_chain; ///< Server certificate chain + + winstd::crypt_hash m_hash_handshake_msgs_md5; ///< Running MD5 hash of handshake messages + winstd::crypt_hash m_hash_handshake_msgs_sha1; ///< Running SHA-1 hash of handshake messages + winstd::crypt_hash m_hash_handshake_msgs_sha256; ///< Running SHA-256 hash of handshake messages + + bool m_handshake[tls_handshake_type_max]; ///< Handshake flags (map od handshake messages received) + + enum { + phase_unknown = -1, ///< Unknown phase + phase_client_hello = 0, ///< Send client hello + phase_server_hello, ///< Wait for server hello + phase_change_cipher_spec, ///< Wait for change cipher spec + phase_application_data ///< Exchange application data + } m_phase; ///< What phase is our communication at? + + unsigned __int64 m_seq_num_client; ///< Sequence number for encrypting + unsigned __int64 m_seq_num_server; ///< Sequence number for decrypting +#else winstd::tstring m_sc_target_name; ///< Schannel target name winstd::sec_credentials m_sc_cred; ///< Schannel client credentials std::vector m_sc_queue; ///< TLS data queue @@ -248,6 +550,7 @@ namespace eap phase_application_data, ///< Exchange application data phase_shutdown, ///< Connection shut down } m_phase; ///< What phase is our communication at? +#endif // The following members are required to avoid memory leakage in get_result() EAP_ATTRIBUTES m_eap_attr_desc; ///< EAP Radius attributes descriptor diff --git a/lib/TLS/include/TLS.h b/lib/TLS/include/TLS.h index f04ee90..c0419af 100644 --- a/lib/TLS/include/TLS.h +++ b/lib/TLS/include/TLS.h @@ -503,7 +503,16 @@ namespace eap /// tls_conn_state& operator=(_Inout_ tls_conn_state &&other); + /// + /// Configures state according to given cipher + /// + /// \param[in] cipher Cipher ID + /// + void set_cipher(_In_ const unsigned char cipher[2]); + public: + LPCTSTR m_prov_name; ///< Cryptography provider name + DWORD m_prov_type; ///< Cryptography provider type ALG_ID m_alg_encrypt; ///< Bulk encryption algorithm size_t m_size_enc_key; ///< Encryption key size in bytes (has to comply with `m_alg_encrypt`) size_t m_size_enc_iv; ///< Encryption initialization vector size in bytes (has to comply with `m_alg_encrypt`) diff --git a/lib/TLS/src/Config.cpp b/lib/TLS/src/Config.cpp index 56d27f1..a07ed20 100644 --- a/lib/TLS/src/Config.cpp +++ b/lib/TLS/src/Config.cpp @@ -75,6 +75,10 @@ eap::config_method_tls::config_method_tls(_In_ module &mod) : config_method_with eap::config_method_tls::config_method_tls(_In_ const config_method_tls &other) : m_trusted_root_ca(other.m_trusted_root_ca), m_server_names(other.m_server_names), +#if EAP_TLS < EAP_TLS_SCHANNEL + m_session_id(other.m_session_id), + m_master_secret(other.m_master_secret), +#endif config_method_with_cred(other) { } @@ -83,6 +87,10 @@ eap::config_method_tls::config_method_tls(_In_ const config_method_tls &other) : eap::config_method_tls::config_method_tls(_Inout_ config_method_tls &&other) : m_trusted_root_ca(std::move(other.m_trusted_root_ca)), m_server_names(std::move(other.m_server_names)), +#if EAP_TLS < EAP_TLS_SCHANNEL + m_session_id(std::move(other.m_session_id)), + m_master_secret(std::move(other.m_master_secret)), +#endif config_method_with_cred(std::move(other)) { } @@ -94,6 +102,10 @@ eap::config_method_tls& eap::config_method_tls::operator=(_In_ const config_meth (config_method_with_cred&)*this = other; m_trusted_root_ca = other.m_trusted_root_ca; m_server_names = other.m_server_names; +#if EAP_TLS < EAP_TLS_SCHANNEL + m_session_id = other.m_session_id; + m_master_secret = other.m_master_secret; +#endif } return *this; @@ -106,6 +118,10 @@ eap::config_method_tls& eap::config_method_tls::operator=(_Inout_ config_method_ (config_method_with_cred&&)*this = std::move(other); m_trusted_root_ca = std::move(other.m_trusted_root_ca); m_server_names = std::move(other.m_server_names); +#if EAP_TLS < EAP_TLS_SCHANNEL + m_session_id = std::move(other.m_session_id); + m_master_secret = std::move(other.m_master_secret); +#endif } return *this; @@ -235,6 +251,10 @@ void eap::config_method_tls::operator<<(_Inout_ cursor_out &cursor) const config_method_with_cred::operator<<(cursor); cursor << m_trusted_root_ca; cursor << m_server_names ; +#if EAP_TLS < EAP_TLS_SCHANNEL + cursor << m_session_id ; + cursor << m_master_secret ; +#endif } @@ -243,7 +263,14 @@ size_t eap::config_method_tls::get_pk_size() const return config_method_with_cred::get_pk_size() + pksizeof(m_trusted_root_ca) + - pksizeof(m_server_names ); + pksizeof(m_server_names ) +#if EAP_TLS < EAP_TLS_SCHANNEL + + + pksizeof(m_session_id ) + + pksizeof(m_master_secret ); +#else + ; +#endif } @@ -252,6 +279,10 @@ void eap::config_method_tls::operator>>(_Inout_ cursor_in &cursor) config_method_with_cred::operator>>(cursor); cursor >> m_trusted_root_ca; cursor >> m_server_names ; +#if EAP_TLS < EAP_TLS_SCHANNEL + cursor >> m_session_id ; + cursor >> m_master_secret ; +#endif } diff --git a/lib/TLS/src/Method.cpp b/lib/TLS/src/Method.cpp index bd698fc..a62b677 100644 --- a/lib/TLS/src/Method.cpp +++ b/lib/TLS/src/Method.cpp @@ -20,11 +20,41 @@ #include "StdAfx.h" +#if EAP_TLS >= EAP_TLS_SCHANNEL #pragma comment(lib, "Secur32.lib") +#endif using namespace std; using namespace winstd; +////////////////////////////////////////////////////////////////////// +// Data +////////////////////////////////////////////////////////////////////// + +#if EAP_TLS < EAP_TLS_SCHANNEL + +static const unsigned char s_cipher_suite[] = { + //0xc0, 0x28, // ECDHE-RSA-AES256-SHA384 Kx=ECDH Au=RSA Enc=AES(256) Mac=SHA384 + //0xc0, 0x24, // ECDHE-ECDSA-AES256-SHA384 Kx=ECDH Au=ECDSA Enc=AES(256) Mac=SHA384 + 0x00, 0x3d, // AES256-SHA256 Kx=RSA Au=RSA Enc=AES(256) Mac=SHA256 + //0x00, 0x6b, // DHE-RSA-AES256-SHA256 Kx=DH Au=RSA Enc=AES(256) Mac=SHA256 + //0x00, 0x6a, // DHE-DSS-AES256-SHA256 Kx=DH Au=DSS Enc=AES(256) Mac=SHA256 + //0xc0, 0x27, // ECDHE-RSA-AES128-SHA256 Kx=ECDH Au=RSA Enc=AES(128) Mac=SHA256 + //0xc0, 0x23, // ECDHE-ECDSA-AES128-SHA256 Kx=ECDH Au=ECDSA Enc=AES(128) Mac=SHA256 + 0x00, 0x3c, // AES128-SHA256 Kx=RSA Au=RSA Enc=AES(128) Mac=SHA256 + //0x00, 0x67, // DHE-RSA-AES128-SHA256 Kx=DH Au=RSA Enc=AES(128) Mac=SHA256 + //0x00, 0x40, // DHE-DSS-AES128-SHA256 Kx=DH Au=DSS Enc=AES(128) Mac=SHA256 + + // Backward compatibility ciphers + 0x00, 0x2f, // TLS_RSA_WITH_AES_128_CBC_SHA (required by TLS 1.2) + 0x00, 0x0a, // TLS_RSA_WITH_3DES_EDE_CBC_SHA (required by EAP-TLS) +}; +static const unsigned char s_compression_suite[] = { + 0x00, // No compression +}; + +#endif + ////////////////////////////////////////////////////////////////////// // eap::method_tls::packet @@ -98,28 +128,72 @@ void eap::method_tls::packet::clear() eap::method_tls::method_tls(_In_ module &module, _In_ config_provider_list &cfg, _In_ credentials_tls &cred) : m_cred(cred), m_user_ctx(NULL), +#if EAP_TLS < EAP_TLS_SCHANNEL m_phase(phase_unknown), + m_seq_num_client(0), + m_seq_num_server(0), +#else + m_phase(phase_unknown), +#endif m_blob_cfg(NULL), #ifdef EAP_USE_NATIVE_CREDENTIAL_CACHE m_blob_cred(NULL), #endif method(module, cfg, cred) { +#if EAP_TLS < EAP_TLS_SCHANNEL + m_tls_version = tls_version_1_2; +#ifdef _DEBUG + memset(m_handshake, 0, sizeof(m_handshake)); +#endif +#endif } eap::method_tls::method_tls(_Inout_ method_tls &&other) : - m_cred ( other.m_cred ), - m_packet_req (std::move(other.m_packet_req )), - m_packet_res (std::move(other.m_packet_res )), - m_user_ctx (std::move(other.m_user_ctx )), - m_sc_target_name(std::move(other.m_sc_target_name)), - m_sc_cred (std::move(other.m_sc_cred )), - m_sc_queue (std::move(other.m_sc_queue )), - m_sc_ctx (std::move(other.m_sc_ctx )), - m_phase (std::move(other.m_phase )), - method (std::move(other )) + m_cred ( other.m_cred ), + m_user_ctx (std::move(other.m_user_ctx )), + m_packet_req (std::move(other.m_packet_req )), + m_packet_res (std::move(other.m_packet_res )), +#if EAP_TLS < EAP_TLS_SCHANNEL + m_cp (std::move(other.m_cp )), + m_cp_enc_client (std::move(other.m_cp_enc_client )), + m_cp_enc_server (std::move(other.m_cp_enc_server )), + m_key_exp1 (std::move(other.m_key_exp1 )), + m_tls_version (std::move(other.m_tls_version )), + m_alg_prf (std::move(other.m_alg_prf )), + m_state_client (std::move(other.m_state_client )), + m_state_client_pending (std::move(other.m_state_client_pending )), + m_state_server (std::move(other.m_state_server )), + m_state_server_pending (std::move(other.m_state_server_pending )), + m_master_secret (std::move(other.m_master_secret )), + m_random_client (std::move(other.m_random_client )), + m_random_server (std::move(other.m_random_server )), + m_key_mppe_client (std::move(other.m_key_mppe_client )), + m_key_mppe_server (std::move(other.m_key_mppe_server )), + m_session_id (std::move(other.m_session_id )), + m_server_cert_chain (std::move(other.m_server_cert_chain )), + 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_hash_handshake_msgs_sha256(std::move(other.m_hash_handshake_msgs_sha256)), + m_phase (std::move(other.m_phase )), + m_seq_num_client (std::move(other.m_seq_num_client )), + m_seq_num_server (std::move(other.m_seq_num_server )), +#else + m_sc_target_name (std::move(other.m_sc_target_name )), + m_sc_cred (std::move(other.m_sc_cred )), + m_sc_queue (std::move(other.m_sc_queue )), + m_sc_ctx (std::move(other.m_sc_ctx )), + m_phase (std::move(other.m_phase )), +#endif + method (std::move(other )) { +#if EAP_TLS < EAP_TLS_SCHANNEL + memcpy(m_handshake, other.m_handshake, sizeof(m_handshake)); +#ifdef _DEBUG + memset(other.m_handshake, 0, sizeof(m_handshake)); +#endif +#endif } @@ -139,15 +213,46 @@ eap::method_tls& eap::method_tls::operator=(_Inout_ method_tls &&other) { if (this != std::addressof(other)) { assert(std::addressof(m_cred) == std::addressof(other.m_cred)); // Move method with same credentials only! - (method&)*this = std::move(other ); - m_packet_req = std::move(other.m_packet_req ); - m_packet_res = std::move(other.m_packet_res ); - m_user_ctx = std::move(other.m_user_ctx ); - m_sc_target_name = std::move(other.m_sc_target_name); - m_sc_cred = std::move(other.m_sc_cred ); - m_sc_queue = std::move(other.m_sc_queue ); - m_sc_ctx = std::move(other.m_sc_ctx ); - m_phase = std::move(other.m_phase ); + (method&)*this = std::move(other ); + m_user_ctx = std::move(other.m_user_ctx ); + m_packet_req = std::move(other.m_packet_req ); + m_packet_res = std::move(other.m_packet_res ); +#if EAP_TLS < EAP_TLS_SCHANNEL + m_cp = std::move(other.m_cp ); + m_cp_enc_client = std::move(other.m_cp_enc_client ); + m_cp_enc_server = std::move(other.m_cp_enc_server ); + m_key_exp1 = std::move(other.m_key_exp1 ); + m_tls_version = std::move(other.m_tls_version ); + m_alg_prf = std::move(other.m_alg_prf ); + m_state_client = std::move(other.m_state_client ); + m_state_client_pending = std::move(other.m_state_client_pending ); + m_state_server = std::move(other.m_state_server ); + m_state_server_pending = std::move(other.m_state_server_pending ); + m_master_secret = std::move(other.m_master_secret ); + m_random_client = std::move(other.m_random_client ); + m_random_server = std::move(other.m_random_server ); + m_key_mppe_client = std::move(other.m_key_mppe_client ); + m_key_mppe_server = std::move(other.m_key_mppe_server ); + m_session_id = std::move(other.m_session_id ); + m_server_cert_chain = std::move(other.m_server_cert_chain ); + 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_hash_handshake_msgs_sha256 = std::move(other.m_hash_handshake_msgs_sha256); + m_phase = std::move(other.m_phase ); + m_seq_num_client = std::move(other.m_seq_num_client ); + m_seq_num_server = std::move(other.m_seq_num_server ); + + memcpy(m_handshake, other.m_handshake, sizeof(m_handshake)); +#ifdef _DEBUG + memset(other.m_handshake, 0, sizeof(m_handshake)); +#endif +#else + m_sc_target_name = std::move(other.m_sc_target_name ); + m_sc_cred = std::move(other.m_sc_cred ); + m_sc_queue = std::move(other.m_sc_queue ); + m_sc_ctx = std::move(other.m_sc_ctx ); + m_phase = std::move(other.m_phase ); +#endif } return *this; @@ -172,6 +277,20 @@ void eap::method_tls::begin_session( const config_method_tls *cfg_method = dynamic_cast(cfg_prov.m_methods.front().get()); assert(cfg_method); +#if EAP_TLS < EAP_TLS_SCHANNEL + // Create cryptographics provider for support needs (handshake hashing, client random, temporary keys...). + if (!m_cp.create(NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) + throw win_runtime_error(__FUNCTION__ " Error creating cryptographics provider."); + + // Microsoft CryptoAPI does not support importing clear text session keys. + // Therefore, we trick it to say the session key is "encrypted" with an exponent-of-one key. + if (!m_key_exp1.create_exp1(m_cp, AT_KEYEXCHANGE)) + throw win_runtime_error(__FUNCTION__ " Error creating exponent-of-one key."); + + // Restore previous session ID and master secret. We might get lucky. + m_session_id = cfg_method->m_session_id; + m_master_secret = cfg_method->m_master_secret; +#else // Build (expected) server name(s) for Schannel. m_sc_target_name.clear(); for (list::const_iterator name = cfg_method->m_server_names.cbegin(), name_end = cfg_method->m_server_names.cend(); name != name_end; ++name) { @@ -201,7 +320,7 @@ void eap::method_tls::begin_session( 0, // dwMinimumCipherStrength: Use system configured default 0, // dwMaximumCipherStrength: Use system configured default 0, // dwSessionLifespan: Use system configured default = 10hr -#ifdef SCHANNEL_SRV_CERT_CHECK +#if EAP_TLS >= EAP_TLS_SCHANNEL_FULL SCH_CRED_AUTO_CRED_VALIDATION | // dwFlags: Let Schannel verify server certificate #else SCH_CRED_MANUAL_CRED_VALIDATION | // dwFlags: Prevent Schannel verify server certificate (we want to use custom root CA store and multiple name checking) @@ -217,6 +336,7 @@ void eap::method_tls::begin_session( SECURITY_STATUS stat = m_sc_cred.acquire(NULL, UNISP_NAME, SECPKG_CRED_OUTBOUND, NULL, &cred); if (FAILED(stat)) throw sec_runtime_error(stat, __FUNCTION__ " Error acquiring Schannel credentials handle."); +#endif } @@ -308,6 +428,155 @@ void eap::method_tls::process_request_packet( m_packet_res.m_id = m_packet_req.m_id; m_packet_res.m_flags = 0; + user_impersonator impersonating(m_user_ctx); + +#if EAP_TLS < EAP_TLS_SCHANNEL + if (pReceivedPacket->Code == EapCodeRequest && (m_packet_req.m_flags & flags_req_start)) { + // This is the EAP-TLS start message: (re)initialize method. + m_module.log_event(&EAPMETHOD_TLS_HANDSHAKE_START2, event_data((unsigned int)eap_type_tls), event_data::blank); + m_phase = phase_client_hello; + } else { + // Process the packet. + memset(m_handshake, 0, sizeof(m_handshake)); + m_packet_res.m_data.clear(); + process_packet(m_packet_req.m_data.data(), m_packet_req.m_data.size()); + } + + switch (m_phase) { + case phase_client_hello: { + m_tls_version = tls_version_1_2; + + m_key_mppe_client.clear(); + m_key_mppe_server.clear(); + + m_server_cert_chain.clear(); + + // Create handshake hashing objects. + if (!m_hash_handshake_msgs_md5.create(m_cp, CALG_MD5)) + throw win_runtime_error(__FUNCTION__ " Error creating MD5 hashing object."); + if (!m_hash_handshake_msgs_sha1.create(m_cp, CALG_SHA1)) + throw win_runtime_error(__FUNCTION__ " Error creating SHA-1 hashing object."); + if (!m_hash_handshake_msgs_sha256.create(m_cp, CALG_SHA_256)) + throw win_runtime_error(__FUNCTION__ " Error creating SHA-256 hashing object."); + + m_seq_num_client = 0; + m_seq_num_server = 0; + + // Build client hello packet. + sanitizing_blob msg_client_hello(make_message(tls_message_type_handshake, make_client_hello())); + m_packet_res.m_data.insert(m_packet_res.m_data.end(), msg_client_hello.begin(), msg_client_hello.end()); + + m_phase = phase_server_hello; + break; + } + + case phase_server_hello: { + if (!m_handshake[tls_handshake_type_server_hello]) + throw win_runtime_error(__FUNCTION__ " Server did not hello back. No server random! What cipher to use?"); + + // Adopt server state as client pending. + // If server already send the change cipher spec, use active server state. Otherwise pending. + m_state_client_pending = m_state_server.m_alg_encrypt ? m_state_server : m_state_server_pending; + + // Create cryptographics provider. + if (!m_cp_enc_client.create(NULL, m_state_client_pending.m_prov_name, m_state_client_pending.m_prov_type, CRYPT_VERIFYCONTEXT)) + throw win_runtime_error(__FUNCTION__ " Error creating cryptographics provider."); + + if (m_handshake[tls_handshake_type_certificate]) { + // Do we trust this server? + if (m_server_cert_chain.empty()) + throw win_runtime_error(ERROR_ENCRYPTION_FAILED, __FUNCTION__ " Server sent an empty certificate (chain)."); + verify_server_trust(); + } + + if (m_handshake[tls_handshake_type_certificate_request]) { + // Client certificate requested. + sanitizing_blob msg_client_cert(make_message(tls_message_type_handshake, make_client_cert())); + m_packet_res.m_data.insert(m_packet_res.m_data.end(), msg_client_cert.begin(), msg_client_cert.end()); + } + + if (m_handshake[tls_handshake_type_server_hello_done]) { + if (m_server_cert_chain.empty()) + throw win_runtime_error(ERROR_ENCRYPTION_FAILED, __FUNCTION__ " Can not do a client key exchange without a server public key (missing server certificate)."); + + // Generate pre-master secret. PMS will get sanitized in its destructor when going out-of-scope. + // Always use latest supported version by client (not negotiated one, to detect version rollback attacks). + tls_master_secret pms(m_cp, tls_version_1_2); + + // Derive master secret. + static const unsigned char s_label[] = "master secret"; + sanitizing_blob seed(s_label, s_label + _countof(s_label) - 1); + seed.insert(seed.end(), (const unsigned char*)&m_random_client, (const unsigned char*)(&m_random_client + 1)); + seed.insert(seed.end(), (const unsigned char*)&m_random_server, (const unsigned char*)(&m_random_server + 1)); + memcpy(&m_master_secret, prf(m_cp, m_alg_prf, pms, seed, sizeof(tls_master_secret)).data(), sizeof(tls_master_secret)); + + // Create client key exchange message, and append to packet. + sanitizing_blob msg_client_key_exchange(make_message(tls_message_type_handshake, make_client_key_exchange(pms))); + m_packet_res.m_data.insert(m_packet_res.m_data.end(), msg_client_key_exchange.begin(), msg_client_key_exchange.end()); + } + + if (m_handshake[tls_handshake_type_certificate_request]) { + // TODO: Create and append client certificate verify message! + } + + // Append change cipher spec to packet. + sanitizing_blob ccs(make_message(tls_message_type_change_cipher_spec, sanitizing_blob(1, 1))); + m_packet_res.m_data.insert(m_packet_res.m_data.end(), ccs.begin(), ccs.end()); + + // Derive client side keys. + static const unsigned char s_label[] = "key expansion"; + sanitizing_blob seed(s_label, s_label + _countof(s_label) - 1); + seed.insert(seed.end(), (const unsigned char*)&m_random_server, (const unsigned char*)(&m_random_server + 1)); + seed.insert(seed.end(), (const unsigned char*)&m_random_client, (const unsigned char*)(&m_random_client + 1)); + sanitizing_blob key_block(prf(m_cp, m_alg_prf, m_master_secret, seed, + 2*m_state_client_pending.m_size_mac_key + // client_write_MAC_secret & server_write_MAC_secret (SHA1) + 2*m_state_client_pending.m_size_enc_key + // client_write_key & server_write_key + 2*m_state_client_pending.m_size_enc_iv )); // client_write_IV & server_write_IV + const unsigned char *_key_block = key_block.data(); + + // client_write_MAC_secret + m_state_client_pending.m_padding_hmac = hmac_padding(m_cp, m_state_client_pending.m_alg_mac, _key_block, m_state_client_pending.m_size_mac_key); + _key_block += m_state_client_pending.m_size_mac_key; + + // server_write_MAC_secret + _key_block += m_state_client_pending.m_size_mac_key; + + // client_write_key + m_state_client_pending.m_key = create_key(m_cp_enc_client, m_state_client_pending.m_alg_encrypt, m_key_exp1, _key_block, m_state_client_pending.m_size_enc_key); + _key_block += m_state_client_pending.m_size_enc_key; + + // server_write_key + _key_block += m_state_client_pending.m_size_enc_key; + + if (m_state_client_pending.m_size_enc_iv && m_tls_version < tls_version_1_1) { + // client_write_IV + if (!CryptSetKeyParam(m_state_client_pending.m_key, KP_IV, _key_block, 0)) + throw win_runtime_error(__FUNCTION__ " Error setting client_write_IV."); + _key_block += m_state_client_pending.m_size_enc_iv; + } + + // Accept client pending state as current client state. + m_state_client = std::move(m_state_client_pending); + + // Create finished message, and append to packet. + sanitizing_blob msg_finished(make_message(tls_message_type_handshake, make_finished())); + m_packet_res.m_data.insert(m_packet_res.m_data.end(), msg_finished.begin(), msg_finished.end()); + + m_phase = m_handshake[tls_handshake_type_finished] ? phase_application_data : phase_change_cipher_spec; + break; + } + + case phase_change_cipher_spec: + // Wait in this phase until server sends change cipher spec and finish. + if (m_state_server.m_alg_encrypt && m_handshake[tls_handshake_type_finished]) + m_phase = phase_application_data; + break; + + case phase_application_data: + if (m_handshake[tls_handshake_type_hello_request]) + m_phase = phase_client_hello; + } +#else if (pReceivedPacket->Code == EapCodeRequest && (m_packet_req.m_flags & flags_req_start)) { // This is the EAP-TLS start message: (re)initialize method. m_phase = phase_handshake_init; @@ -325,6 +594,7 @@ void eap::method_tls::process_request_packet( process_application_data(); break; } +#endif pEapOutput->fAllowNotifications = TRUE; pEapOutput->action = EapPeerMethodResponseActionSend; @@ -404,6 +674,20 @@ void eap::method_tls::get_result( case EapPeerMethodResultSuccess: { m_module.log_event(&EAPMETHOD_TLS_SUCCESS, event_data((unsigned int)eap_type_tls), event_data::blank); +#if EAP_TLS < EAP_TLS_SCHANNEL + // Derive MSK/EMSK for line encryption. + derive_msk(); + + // Fill array with RADIUS attributes. + eap_attr a; + m_eap_attr.clear(); + m_eap_attr.reserve(3); + a.create_ms_mppe_key(16, (LPCBYTE)&m_key_mppe_client, sizeof(tls_random)); + m_eap_attr.push_back(std::move(a)); + a.create_ms_mppe_key(17, (LPCBYTE)&m_key_mppe_server, sizeof(tls_random)); + m_eap_attr.push_back(std::move(a)); + m_eap_attr.push_back(eap_attr::blank); +#else // Derive MSK/EMSK for line encryption. SecPkgContext_EapKeyBlock key_block; SECURITY_STATUS status = QueryContextAttributes(m_sc_ctx, SECPKG_ATTR_EAP_KEY_BLOCK, &key_block); @@ -422,6 +706,7 @@ void eap::method_tls::get_result( m_eap_attr.push_back(std::move(a)); _key_block += sizeof(tls_random); m_eap_attr.push_back(eap_attr::blank); +#endif m_eap_attr_desc.dwNumberOfAttributes = (DWORD)m_eap_attr.size(); m_eap_attr_desc.pAttribs = m_eap_attr.data(); @@ -433,12 +718,24 @@ void eap::method_tls::get_result( ppResult->fIsSuccess = TRUE; ppResult->dwFailureReasonCode = ERROR_SUCCESS; +#if EAP_TLS < EAP_TLS_SCHANNEL + // Update configuration with session resumption data and prepare BLOB. + cfg_method->m_session_id = m_session_id; + cfg_method->m_master_secret = m_master_secret; +#endif + break; } case EapPeerMethodResultFailure: m_module.log_event(&EAPMETHOD_TLS_FAILURE, event_data((unsigned int)eap_type_tls), event_data::blank); +#if EAP_TLS < EAP_TLS_SCHANNEL + // Clear session resumption data. + cfg_method->m_session_id.clear(); + cfg_method->m_master_secret.clear(); +#endif + // Mark credentials as failed, so GUI can re-prompt user. cfg_method->m_auth_failed = true; @@ -470,6 +767,531 @@ void eap::method_tls::get_result( } +#if EAP_TLS < EAP_TLS_SCHANNEL + +eap::sanitizing_blob eap::method_tls::make_client_hello() +{ + size_t size_data; + sanitizing_blob msg; + msg.reserve( + 4 + // SSL header + (size_data = + 2 + // SSL version + sizeof(tls_random) + // Client random + 1 + // Session ID size + m_session_id.size() + // Session ID + 2 + // Length of cypher suite list + sizeof(s_cipher_suite) + // Cipher suite list + 1 + // Length of compression suite + sizeof(s_compression_suite))); // Compression suite + + // SSL header + assert(size_data <= 0xffffff); + unsigned int ssl_header = htonl((tls_handshake_type_client_hello << 24) | (unsigned int)size_data); + msg.insert(msg.end(), (unsigned char*)&ssl_header, (unsigned char*)(&ssl_header + 1)); + + // SSL version + msg.insert(msg.end(), (unsigned char*)&m_tls_version, (unsigned char*)(&m_tls_version + 1)); + + // Generate client random and add it to the message + m_random_client.randomize(m_cp); + msg.insert(msg.end(), (unsigned char*)&m_random_client, (unsigned char*)(&m_random_client + 1)); + + // Session ID + assert(m_session_id.size() <= 32); + msg.push_back((unsigned char)m_session_id.size()); + msg.insert(msg.end(), m_session_id.begin(), m_session_id.end()); + + // Cypher suite list + unsigned short size_cipher_suite2 = htons((unsigned short)sizeof(s_cipher_suite)); + msg.insert(msg.end(), (unsigned char*)&size_cipher_suite2, (unsigned char*)(&size_cipher_suite2 + 1)); + msg.insert(msg.end(), s_cipher_suite, s_cipher_suite + _countof(s_cipher_suite)); + + // Compression + msg.push_back((unsigned char)sizeof(s_compression_suite)); + msg.insert(msg.end(), s_compression_suite, s_compression_suite + _countof(s_compression_suite)); + + return msg; +} + + +eap::sanitizing_blob eap::method_tls::make_client_cert() const +{ + // Select client certificate. + PCCERT_CONTEXT cert = m_cred.m_cert ? m_cred.m_cert : NULL; + + size_t size_data, size_list; + sanitizing_blob msg; + msg.reserve( + 4 + // SSL header + (size_data = + 3 + // Certificate list size + (size_list = + (cert ? 3 + cert->cbCertEncoded : 0)))); // Certificate (optional) + + // SSL header + assert(size_data <= 0xffffff); + unsigned int ssl_header = htonl((tls_handshake_type_certificate << 24) | (unsigned int)size_data); + msg.insert(msg.end(), (unsigned char*)&ssl_header, (unsigned char*)(&ssl_header + 1)); + + // List size + assert(size_list <= 0xffffff); + msg.push_back((unsigned char)((size_list >> 16) & 0xff)); + msg.push_back((unsigned char)((size_list >> 8) & 0xff)); + msg.push_back((unsigned char)((size_list ) & 0xff)); + + if (cert) { + // Cert size + assert(cert->cbCertEncoded <= 0xffffff); + msg.push_back((unsigned char)((cert->cbCertEncoded >> 16) & 0xff)); + msg.push_back((unsigned char)((cert->cbCertEncoded >> 8) & 0xff)); + msg.push_back((unsigned char)((cert->cbCertEncoded ) & 0xff)); + + msg.insert(msg.end(), cert->pbCertEncoded, cert->pbCertEncoded + cert->cbCertEncoded); + } + + return msg; +} + + +eap::sanitizing_blob eap::method_tls::make_client_key_exchange(_In_ const tls_master_secret &pms) const +{ + // Encrypt pre-master key with server public key first. + sanitizing_blob pms_enc((const unsigned char*)&pms, (const unsigned char*)(&pms + 1)); + crypt_key key; + if (!key.import_public(m_cp_enc_client, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &(m_server_cert_chain.front()->pCertInfo->SubjectPublicKeyInfo))) + throw win_runtime_error(__FUNCTION__ " Error importing server's public key."); + if (!CryptEncrypt(key, NULL, TRUE, 0, pms_enc)) + throw win_runtime_error(__FUNCTION__ " Error encrypting PMS."); + + size_t size_data, size_pms_enc = pms_enc.size(); + sanitizing_blob msg; + msg.reserve( + 4 + // SSL header + (size_data = + 2 + // Encrypted pre master secret size + size_pms_enc)); // Encrypted pre master secret + + // SSL header + assert(size_data <= 0xffffff); + unsigned int ssl_header = htonl((tls_handshake_type_client_key_exchange << 24) | (unsigned int)size_data); + msg.insert(msg.end(), (unsigned char*)&ssl_header, (unsigned char*)(&ssl_header + 1)); + + // Encrypted pre master secret size + assert(size_pms_enc <= 0xffff); + msg.push_back((unsigned char)((size_pms_enc >> 8) & 0xff)); + msg.push_back((unsigned char)((size_pms_enc ) & 0xff)); + + // Encrypted pre master secret +#ifdef _HOST_LOW_ENDIAN + std::reverse(pms_enc.begin(), pms_enc.end()); +#endif + msg.insert(msg.end(), pms_enc.begin(), pms_enc.end()); + + return msg; +} + + +eap::sanitizing_blob eap::method_tls::make_finished() const +{ + sanitizing_blob msg; + msg.reserve( + 4 + // SSL header + 12); // verify_data is 12B + + // SSL header + unsigned int ssl_header = htonl((unsigned int)(tls_handshake_type_finished << 24) | 12); + msg.insert(msg.end(), (unsigned char*)&ssl_header, (unsigned char*)(&ssl_header + 1)); + + // Create label + hash MD5 + hash SHA-1 seed. + crypt_hash hash; + static const unsigned char s_label[] = "client finished"; + sanitizing_blob seed(s_label, s_label + _countof(s_label) - 1), hash_data; + if (m_tls_version < tls_version_1_2) { + hash = m_hash_handshake_msgs_md5; // duplicate + if (!CryptGetHashParam(hash, HP_HASHVAL, hash_data, 0)) + throw win_runtime_error(__FUNCTION__ " Error finishing MD5 hash calculation."); + seed.insert(seed.end(), hash_data.begin(), hash_data.end()); + hash = m_hash_handshake_msgs_sha1; // duplicate + if (!CryptGetHashParam(hash, HP_HASHVAL, hash_data, 0)) + throw win_runtime_error(__FUNCTION__ " Error finishing SHA-1 hash calculation."); + seed.insert(seed.end(), hash_data.begin(), hash_data.end()); + } else { + hash = m_hash_handshake_msgs_sha256; // duplicate + if (!CryptGetHashParam(hash, HP_HASHVAL, hash_data, 0)) + throw win_runtime_error(__FUNCTION__ " Error finishing SHA-256 hash calculation."); + seed.insert(seed.end(), hash_data.begin(), hash_data.end()); + } + sanitizing_blob verify(prf(m_cp, m_alg_prf, m_master_secret, seed, 12)); + msg.insert(msg.end(), verify.begin(), verify.end()); + + return msg; +} + + +eap::sanitizing_blob eap::method_tls::make_message(_In_ tls_message_type_t type, _Inout_ sanitizing_blob &&data) +{ + if (type == tls_message_type_handshake) + hash_handshake(data); + + if (m_state_client.m_alg_encrypt) + encrypt_message(type, data); + + size_t size_data = data.size(); + assert(size_data <= 0xffff); + message_header hdr = { + type, // SSL record type + { + m_tls_version.major, // SSL major version + m_tls_version.minor, // SSL minor version + }, + { + // Data length (unencrypted, network byte order) + (unsigned char)((size_data >> 8) & 0xff), + (unsigned char)((size_data ) & 0xff), + } + }; + + sanitizing_blob msg; + msg.reserve(sizeof(message_header) + size_data); + msg.assign((const unsigned char*)&hdr, (const unsigned char*)(&hdr + 1)); + msg.insert(msg.end(), data.begin(), data.end()); + return msg; +} + + +void eap::method_tls::derive_msk() +{ + static const unsigned char s_label[] = "client EAP encryption"; + sanitizing_blob seed(s_label, s_label + _countof(s_label) - 1); + seed.insert(seed.end(), (const unsigned char*)&m_random_client, (const unsigned char*)(&m_random_client + 1)); + seed.insert(seed.end(), (const unsigned char*)&m_random_server, (const unsigned char*)(&m_random_server + 1)); + sanitizing_blob key_block(prf(m_cp, m_alg_prf, m_master_secret, seed, 2*sizeof(tls_random))); + const unsigned char *_key_block = key_block.data(); + + // MS-MPPE-Recv-Key + memcpy(&m_key_mppe_client, _key_block, sizeof(tls_random)); + _key_block += sizeof(tls_random); + + // MS-MPPE-Send-Key + memcpy(&m_key_mppe_server, _key_block, sizeof(tls_random)); + _key_block += sizeof(tls_random); +} + + +void eap::method_tls::process_packet(_In_bytecount_(size_pck) const void *_pck, _In_ size_t size_pck) +{ + sanitizing_blob data; + + for (const unsigned char *pck = (const unsigned char*)_pck, *pck_end = pck + size_pck; pck < pck_end; ) { + if (pck + 5 > pck_end) + throw win_runtime_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, __FUNCTION__ " Incomplete message header."); + const message_header *hdr = (const message_header*)pck; + const unsigned char + *msg = (const unsigned char*)(hdr + 1), + *msg_end = msg + ntohs(*(unsigned short*)hdr->length); + if (msg_end > pck_end) + throw win_runtime_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, __FUNCTION__ " Incomplete message data."); + + if (hdr->version >= tls_version_1_0) { + // Process TLS message. + switch (hdr->type) { + case tls_message_type_change_cipher_spec: + if (m_state_server.m_alg_encrypt) { + sanitizing_blob msg_dec(msg, msg_end); + decrypt_message(hdr->type, msg_dec); + process_change_cipher_spec(msg_dec.data(), msg_dec.size()); + } else + process_change_cipher_spec(msg, msg_end - msg); + break; + + case tls_message_type_alert: + if (m_state_server.m_alg_encrypt) { + sanitizing_blob msg_dec(msg, msg_end); + decrypt_message(hdr->type, msg_dec); + process_alert(msg_dec.data(), msg_dec.size()); + } else + process_alert(msg, msg_end - msg); + break; + + case tls_message_type_handshake: + if (m_state_server.m_alg_encrypt) { + sanitizing_blob msg_dec(msg, msg_end); + decrypt_message(hdr->type, msg_dec); + process_handshake(msg_dec.data(), msg_dec.size()); + } else + process_handshake(msg, msg_end - msg); + break; + + case tls_message_type_application_data: { + if (!m_state_server.m_alg_encrypt) + throw win_runtime_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, __FUNCTION__ " Application data should be encrypted."); + + sanitizing_blob msg_dec(msg, msg_end); + decrypt_message(hdr->type, msg_dec); + process_application_data(msg_dec.data(), msg_dec.size()); + break; + } + } + } + + pck = msg_end; + } +} + + +void eap::method_tls::process_change_cipher_spec(_In_bytecount_(msg_size) const void *_msg, _In_ size_t msg_size) +{ + if (msg_size < 1) + throw win_runtime_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, __FUNCTION__ " Incomplete change cipher spec."); + + const unsigned char *msg = (const unsigned char*)_msg; + if (msg[0] != 1) + throw win_runtime_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, string_printf(__FUNCTION__ " Invalid change cipher spec message (expected 1, received %u).", msg[0])); + + m_module.log_event(&EAPMETHOD_TLS_CHANGE_CIPHER_SPEC, event_data((unsigned int)eap_type_tls), event_data::blank); + + if (!m_state_server_pending.m_alg_encrypt) + throw win_runtime_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, __FUNCTION__ " Change cipher spec received without cipher being negotiated first."); + + // Create cryptographics provider (based on server selected cipher?). + if (!m_cp_enc_server.create(NULL, m_state_server_pending.m_prov_name, m_state_server_pending.m_prov_type)) + throw win_runtime_error(__FUNCTION__ " Error creating cryptographics provider."); + + static const unsigned char s_label[] = "key expansion"; + sanitizing_blob seed(s_label, s_label + _countof(s_label) - 1); + seed.insert(seed.end(), (const unsigned char*)&m_random_server, (const unsigned char*)(&m_random_server + 1)); + seed.insert(seed.end(), (const unsigned char*)&m_random_client, (const unsigned char*)(&m_random_client + 1)); + sanitizing_blob key_block(prf(m_cp, m_alg_prf, m_master_secret, seed, + 2*m_state_server_pending.m_size_mac_key + // client_write_MAC_secret & server_write_MAC_secret (SHA1) + 2*m_state_server_pending.m_size_enc_key + // client_write_key & server_write_key + 2*m_state_server_pending.m_size_enc_iv )); // client_write_IV & server_write_IV + const unsigned char *_key_block = key_block.data(); + + // client_write_MAC_secret + _key_block += m_state_server_pending.m_size_mac_key; + + // server_write_MAC_secret + m_state_server_pending.m_padding_hmac = hmac_padding(m_cp, m_state_server_pending.m_alg_mac, _key_block, m_state_server_pending.m_size_mac_key); + _key_block += m_state_server_pending.m_size_mac_key; + + // client_write_key + _key_block += m_state_server_pending.m_size_enc_key; + + // server_write_key + m_state_server_pending.m_key = create_key(m_cp_enc_server, m_state_server_pending.m_alg_encrypt, m_key_exp1, _key_block, m_state_server_pending.m_size_enc_key); + _key_block += m_state_server_pending.m_size_enc_key; + + if (m_state_server_pending.m_size_enc_iv && m_tls_version < tls_version_1_1) { + // client_write_IV + _key_block += m_state_server_pending.m_size_enc_iv; + + // server_write_IV + if (!CryptSetKeyParam(m_state_server_pending.m_key, KP_IV, _key_block, 0)) + throw win_runtime_error(__FUNCTION__ " Error setting server_write_IV."); + _key_block += m_state_server_pending.m_size_enc_iv; + } + + // Accept server pending state as current server state. + m_state_server = std::move(m_state_server_pending); + m_state_server_pending.m_alg_encrypt = 0; // Explicitly invalidate server pending state. (To mark that server must re-negotiate cipher.) +} + + +void eap::method_tls::process_alert(_In_bytecount_(msg_size) const void *_msg, _In_ size_t msg_size) +{ + if (msg_size < 2) + throw win_runtime_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, __FUNCTION__ " Incomplete alert."); + + const unsigned char *msg = (const unsigned char*)_msg; + + m_module.log_event(&EAPMETHOD_TLS_ALERT, event_data((unsigned int)eap_type_tls), event_data((unsigned char)msg[0]), event_data((unsigned char)msg[1]), event_data::blank); + + //if (msg[0] == alert_level_fatal) { + // // Clear session ID to avoid reconnection attempts. + // m_session_id.clear(); + //} +} + + +void eap::method_tls::process_handshake(_In_bytecount_(msg_size) const void *_msg, _In_ size_t msg_size) +{ + 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) + throw win_runtime_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, __FUNCTION__ " Incomplete record header."); + 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) + throw win_runtime_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, __FUNCTION__ " Incomplete record data."); + + // Process record. + tls_handshake_type_t type = (tls_handshake_type_t)((hdr >> 24) & 0xff); + switch (type) { + case tls_handshake_type_server_hello: + // TLS version + if (rec + 2 > rec_end) + throw win_runtime_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, __FUNCTION__ " Server SSL/TLS version missing or incomplete."); + else if (*(tls_version*)rec < tls_version_1_0 || m_tls_version < *(tls_version*)rec) + throw win_runtime_error(ERROR_NOT_SUPPORTED, __FUNCTION__ " Unsupported SSL/TLS version."); + m_tls_version = *(tls_version*)rec; + m_alg_prf = m_tls_version < tls_version_1_2 ? CALG_TLS1PRF : CALG_SHA_256; + rec += 2; + + // Server random + if (rec + sizeof(tls_random) > rec_end) + throw win_runtime_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, __FUNCTION__ " Server random missing or incomplete."); + memcpy(&m_random_server, rec, sizeof(tls_random)); + rec += sizeof(tls_random); + + // Session ID + if (rec + 1 > rec_end || rec + 1 + rec[0] > rec_end) + throw win_runtime_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, __FUNCTION__ " Session ID missing or incomplete."); + assert(rec[0] <= 32); // According to RFC 5246 session IDs should not be longer than 32B. + if (m_session_id.size() != rec[0] || memcmp(m_session_id.data(), rec + 1, rec[0]) != 0) { + m_module.log_event(&EAPMETHOD_TLS_SESSION_NEW, event_data((unsigned int)eap_type_tls), event_data::blank); + m_session_id.assign(rec + 1, rec + 1 + rec[0]); + } else + m_module.log_event(&EAPMETHOD_TLS_SESSION_RESUME, event_data((unsigned int)eap_type_tls), event_data::blank); + rec += rec[0] + 1; + + // Cipher + if (rec + 2 > rec_end) + throw win_runtime_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, __FUNCTION__ " Cipher missing or incomplete."); + + // Verify the server selected one of our ciphers. + for (size_t i = 0; ; i += 2) { + if (i < _countof(s_cipher_suite)) { + if (s_cipher_suite[i] == rec[0] && s_cipher_suite[i + 1] == rec[1]) + break; + } else + throw win_runtime_error(ERROR_NOT_SUPPORTED, string_printf(__FUNCTION__ " Other than requested cipher selected (received 0x%02x%02x).", rec[0], rec[1])); + } + m_state_server_pending.set_cipher(rec); + rec += 2; + + // Compression + if (rec + 1 > rec_end) + throw win_runtime_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, __FUNCTION__ " Compression missing or incomplete."); + + // Verify the server selected one of our compression schemes. + for (size_t i = 0; ; i++) { + if (i < _countof(s_compression_suite)) { + if (s_compression_suite[i] == rec[0]) + break; + } else + throw win_runtime_error(ERROR_NOT_SUPPORTED, string_printf(__FUNCTION__ " Other than requested compression selected (received 0x%02).", rec[0])); + } + rec++; + + m_module.log_event(&EAPMETHOD_TLS_SERVER_HELLO1, + event_data((unsigned int)eap_type_tls), + event_data(((unsigned int)m_tls_version.major << 8) | (unsigned int)m_tls_version.minor), + event_data((unsigned int)m_session_id.size()), + event_data(m_session_id.data(), (ULONG)m_session_id.size()), + event_data::blank); + break; + + case tls_handshake_type_certificate: { + // Certificate list size + if (rec + 3 > rec_end) + throw win_runtime_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, __FUNCTION__ " Certificate list size missing or incomplete."); + const unsigned char + *list = rec + 3, + *list_end = list + ((rec[0] << 16) | (rec[1] << 8) | rec[2]); + if (list_end > rec_end) + throw win_runtime_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, __FUNCTION__ " Certificate list missing or incomplete."); + + m_server_cert_chain.clear(); + while (list < list_end) { + // Certificate size + if (list + 3 > list_end) + throw win_runtime_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, __FUNCTION__ " Certificate size missing or incomplete."); + const unsigned char + *cert = list + 3, + *cert_end = cert + ((list[0] << 16) | (list[1] << 8) | list[2]); + if (cert_end > list_end) + throw win_runtime_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, __FUNCTION__ " Certificate rec missing or incomplete."); + + // Certificate + cert_context c; + if (!c.create(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, cert, (DWORD)(cert_end - cert))) + throw win_runtime_error(__FUNCTION__ " Error reading certificate."); + m_server_cert_chain.push_back(std::move(c)); + + list = cert_end; + } + + wstring cert_name(!m_server_cert_chain.empty() ? get_cert_title(m_server_cert_chain.front()) : L""); + m_module.log_event(&EAPMETHOD_TLS_CERTIFICATE, event_data((unsigned int)eap_type_tls), event_data(cert_name), event_data::blank); + break; + } + + case tls_handshake_type_certificate_request: + m_module.log_event(&EAPMETHOD_TLS_CERTIFICATE_REQUEST, event_data((unsigned int)eap_type_tls), event_data::blank); + break; + + case tls_handshake_type_server_hello_done: + m_module.log_event(&EAPMETHOD_TLS_SERVER_HELLO_DONE, event_data((unsigned int)eap_type_tls), event_data::blank); + break; + + case tls_handshake_type_finished: { + if (!m_state_server.m_alg_encrypt) + throw win_runtime_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, __FUNCTION__ " Finished message should be encrypted."); + + // According to https://tools.ietf.org/html/rfc5246#section-7.4.9 all verify_data is 12B. + if (rec_end - rec != 12) + throw win_runtime_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, string_printf(__FUNCTION__ " Finished record size incorrect (expected 12B, received %uB).", rec_end - rec)); + + // Create label + hash MD5 + hash SHA-1 seed. + crypt_hash hash; + static const unsigned char s_label[] = "server finished"; + sanitizing_blob seed(s_label, s_label + _countof(s_label) - 1), hash_data; + if (m_tls_version < tls_version_1_2) { + hash = m_hash_handshake_msgs_md5; // duplicate + if (!CryptGetHashParam(hash, HP_HASHVAL, hash_data, 0)) + throw win_runtime_error(__FUNCTION__ " Error finishing MD5 hash calculation."); + seed.insert(seed.end(), hash_data.begin(), hash_data.end()); + hash = m_hash_handshake_msgs_sha1; // duplicate + if (!CryptGetHashParam(hash, HP_HASHVAL, hash_data, 0)) + throw win_runtime_error(__FUNCTION__ " Error finishing SHA-1 hash calculation."); + seed.insert(seed.end(), hash_data.begin(), hash_data.end()); + } else { + hash = m_hash_handshake_msgs_sha256; // duplicate + if (!CryptGetHashParam(hash, HP_HASHVAL, hash_data, 0)) + throw win_runtime_error(__FUNCTION__ " Error finishing SHA-256 hash calculation."); + seed.insert(seed.end(), hash_data.begin(), hash_data.end()); + } + + if (memcmp(prf(m_cp, m_alg_prf, m_master_secret, seed, 12).data(), rec, 12)) + throw win_runtime_error(ERROR_ENCRYPTION_FAILED, __FUNCTION__ " Integrity check failed."); + + m_module.log_event(&EAPMETHOD_TLS_FINISHED, event_data((unsigned int)eap_type_tls), event_data::blank); + break; + } + + default: + m_module.log_event(&EAPMETHOD_TLS_HANDSHAKE_IGNORE, event_data((unsigned int)eap_type_tls), event_data((unsigned char)type), event_data::blank); + } + + if (type < tls_handshake_type_max) { + // Set the flag this handshake message was received. + m_handshake[type] = true; + } + + if (type != tls_handshake_type_hello_request) { + // Hash all but hello requests (https://tools.ietf.org/html/rfc5246#section-7.4.1.1). + hash_handshake(msg, rec_end - msg); + } + + msg = rec_end; + } +} + +#else + void eap::method_tls::process_handshake() { // Prepare input buffer(s). @@ -494,7 +1316,6 @@ void eap::method_tls::process_handshake() }; sec_buffer_desc buf_out_desc(buf_out, _countof(buf_out)); - user_impersonator impersonating(m_user_ctx); SECURITY_STATUS status; if (m_phase == phase_handshake_init) { m_module.log_event(&EAPMETHOD_TLS_HANDSHAKE_START2, event_data((unsigned int)eap_type_tls), event_data::blank); @@ -515,7 +1336,7 @@ void eap::method_tls::process_handshake() &buf_out_desc); } -#ifndef SCHANNEL_SRV_CERT_CHECK +#if EAP_TLS < EAP_TLS_SCHANNEL_FULL if (status == SEC_E_OK) verify_server_trust(); #endif @@ -602,6 +1423,8 @@ void eap::method_tls::process_application_data() } } +#endif + void eap::method_tls::process_application_data(_In_bytecount_(size_msg) const void *msg, _In_ size_t size_msg) { @@ -612,16 +1435,19 @@ void eap::method_tls::process_application_data(_In_bytecount_(size_msg) const vo } -#ifndef SCHANNEL_SRV_CERT_CHECK +#if EAP_TLS < EAP_TLS_SCHANNEL_FULL void eap::method_tls::verify_server_trust() const { - SECURITY_STATUS status; - +#if EAP_TLS < EAP_TLS_SCHANNEL + assert(!m_server_cert_chain.empty()); + const cert_context &cert = m_server_cert_chain.front(); +#else cert_context cert; - status = QueryContextAttributes(m_sc_ctx, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (PVOID)&cert); + SECURITY_STATUS status = QueryContextAttributes(m_sc_ctx, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (PVOID)&cert); if (FAILED(status)) throw sec_runtime_error(status, __FUNCTION__ " Error retrieving server certificate from Schannel."); +#endif const config_provider &cfg_prov(m_cfg.m_providers.front()); const config_method_tls *cfg_method = dynamic_cast(cfg_prov.m_methods.front().get()); @@ -708,6 +1534,19 @@ void eap::method_tls::verify_server_trust() const CertAddCertificateContextToStore(store, *c, CERT_STORE_ADD_REPLACE_EXISTING, NULL); // Add all intermediate certificates from the server's certificate chain. +#if EAP_TLS < EAP_TLS_SCHANNEL + for (list::const_iterator c = m_server_cert_chain.cbegin(), c_end = m_server_cert_chain.cend(); ++c != c_end;) { + const cert_context &_c = *c; + if (_c->pCertInfo->Issuer.cbData == _c->pCertInfo->Subject.cbData && + memcmp(_c->pCertInfo->Issuer.pbData, _c->pCertInfo->Subject.pbData, _c->pCertInfo->Issuer.cbData) == 0) + { + // Skip the root CA certificates (self-signed). We define in whom we trust! + continue; + } + + CertAddCertificateContextToStore(store, *c, CERT_STORE_ADD_REPLACE_EXISTING, NULL); + } +#else for (cert_context c(cert); c;) { DWORD flags = 0; c.attach(CertGetIssuerCertificateFromStore(cert->hCertStore, c, NULL, &flags)); @@ -722,6 +1561,7 @@ void eap::method_tls::verify_server_trust() const CertAddCertificateContextToStore(store, c, CERT_STORE_ADD_REPLACE_EXISTING, NULL); } +#endif // Prepare the certificate chain validation, and check. CERT_CHAIN_PARA chain_params = { @@ -782,3 +1622,330 @@ void eap::method_tls::verify_server_trust() const } #endif + +#if EAP_TLS < EAP_TLS_SCHANNEL + +void eap::method_tls::encrypt_message(_In_ tls_message_type_t type, _Inout_ sanitizing_blob &data) +{ + // Hash sequence number, TLS header, and message. + size_t size_data = data.size(); + hmac_hash hash(m_cp, m_state_client.m_alg_mac, m_state_client.m_padding_hmac); + unsigned __int64 seq_num2 = htonll(m_seq_num_client); + unsigned short size_data2 = htons((unsigned short)size_data); + if (!CryptHashData(hash, (const BYTE*)&seq_num2 , sizeof(seq_num2 ), 0) || + !CryptHashData(hash, (const BYTE*)&type , sizeof(type ), 0) || + !CryptHashData(hash, (const BYTE*)&m_tls_version, sizeof(m_tls_version), 0) || + !CryptHashData(hash, (const BYTE*)&size_data2 , sizeof(size_data2 ), 0) || + !CryptHashData(hash, data.data() , (DWORD)size_data , 0)) + throw win_runtime_error(__FUNCTION__ " Error hashing data."); + sanitizing_blob hmac; + hash.calculate(hmac); + + size_t size_data_enc = + size_data + // TLS message + hmac.size(); // HMAC hash + + if (m_state_client.m_size_enc_block) { + // Block cypher + + if (m_tls_version >= tls_version_1_1) { + // TLS 1.1+: Set random IV. + data.insert(data.begin(), m_state_client.m_size_enc_iv, 0); + if (!CryptGenRandom(m_cp, (DWORD)m_state_client.m_size_enc_iv, data.data())) + throw win_runtime_error(__FUNCTION__ " Error generating IV."); + size_data_enc += m_state_client.m_size_enc_iv; + } + + // Calculate padding. + size_data_enc += 1; // Padding length + unsigned char size_padding = (unsigned char)((m_state_client.m_size_enc_block - size_data_enc) % m_state_client.m_size_enc_block); + size_data_enc += size_padding; + + // Append HMAC hash and padding. + data.reserve(size_data_enc); + data.insert(data.end(), hmac.begin(), hmac.end()); + data.insert(data.end(), size_padding + 1, size_padding); + } else { + // Stream cipher + + // Append HMAC hash. + data.reserve(size_data_enc); + data.insert(data.end(), hmac.begin(), hmac.end()); + } + + // Encrypt. + assert(size_data_enc < 0xffffffff); + DWORD size_data_enc2 = (DWORD)size_data_enc; + if (!CryptEncrypt(m_state_client.m_key, NULL, FALSE, 0, data.data(), &size_data_enc2, (DWORD)size_data_enc)) + throw win_runtime_error(__FUNCTION__ " Error encrypting message."); + + // Increment sequence number. + m_seq_num_client++; +} + + +void eap::method_tls::decrypt_message(_In_ tls_message_type_t type, _Inout_ sanitizing_blob &data) +{ + // Decrypt. + if (!CryptDecrypt(m_state_server.m_key, NULL, FALSE, 0, data)) + throw win_runtime_error(__FUNCTION__ " Error decrypting message."); + + if (!data.empty()) { + size_t size_data = data.size(); + bool padding_ok = true; + + if (m_state_server.m_size_enc_block) { + // Check padding. Do not throw until HMAC is calculated. + // [Canvel, B., "Password Interception in a SSL/TLS Channel"](http://lasecwww.epfl.ch/memo_ssl.shtml) + unsigned char padding = data.back(); + size_data = (size_t)padding + 1 <= size_data ? size_data - (padding + 1) : 0; + for (size_t i = size_data, i_end = data.size() - 1; i < i_end; i++) + if (data[i] != padding) + padding_ok = false; + + // Remove padding. + data.resize(size_data); + + if (m_tls_version >= tls_version_1_1) { + // TLS 1.1+: Remove random IV. + data.erase(data.begin(), data.begin() + m_state_server.m_size_enc_iv); + size_data -= m_state_server.m_size_enc_iv; + } + } + + size_data -= m_state_server.m_size_mac_hash; + + // Hash sequence number, TLS header (without length), original message length, and message. + hmac_hash hash(m_cp, m_state_server.m_alg_mac, m_state_server.m_padding_hmac); + unsigned __int64 seq_num2 = htonll(m_seq_num_server); + unsigned short size_data2 = htons((unsigned short)size_data); + if (!CryptHashData(hash, (const BYTE*)&seq_num2 , sizeof(seq_num2 ), 0) || + !CryptHashData(hash, (const BYTE*)&type , sizeof(type ), 0) || + !CryptHashData(hash, (const BYTE*)&m_tls_version, sizeof(m_tls_version), 0) || + !CryptHashData(hash, (const BYTE*)&size_data2 , sizeof(size_data2 ), 0) || + !CryptHashData(hash, data.data() , (DWORD)size_data , 0)) + throw win_runtime_error(__FUNCTION__ " Error hashing data."); + sanitizing_blob hmac; + hash.calculate(hmac); + + // // Check padding results. + if (!padding_ok) + throw invalid_argument(__FUNCTION__ " Incorrect message padding."); + + // Verify hash. + if (memcmp(&*(data.begin() + size_data), hmac.data(), m_state_server.m_size_mac_hash) != 0) + throw win_runtime_error(ERROR_DECRYPTION_FAILED, __FUNCTION__ " Integrity check failed."); + + // Strip hash and padding. + data.resize(size_data); + + // Increment sequence number. + m_seq_num_server++; + } +} + + +eap::sanitizing_blob eap::method_tls::prf( + _In_ HCRYPTPROV cp, + _In_ ALG_ID alg, + _In_ const tls_master_secret &secret, + _In_bytecount_(size_seed) const void *seed, + _In_ size_t size_seed, + _In_ size_t size) +{ + sanitizing_blob data; + data.reserve(size); + + if (alg == CALG_TLS1PRF) { + // Split secret in two halves. + size_t + size_S1 = (sizeof(tls_master_secret) + 1) / 2, + size_S2 = size_S1; + const void + *S1 = &secret, + *S2 = (const unsigned char*)&secret + (sizeof(tls_master_secret) - size_S2); + + // Precalculate HMAC padding for speed. + hmac_padding + padding1(cp, CALG_MD5 , S1, size_S1), + padding2(cp, CALG_SHA1, S2, size_S2); + + // Prepare A for p_hash. + sanitizing_blob + A1((unsigned char*)seed, (unsigned char*)seed + size_seed), + A2((unsigned char*)seed, (unsigned char*)seed + size_seed); + + sanitizing_blob + hmac1, + hmac2; + data.resize(size); + for (size_t i = 0, off1 = 0, off2 = 0; i < size; ) { + if (off1 >= hmac1.size()) { + // Rehash A. + hmac_hash hash1(cp, CALG_MD5 , padding1); + if (!CryptHashData(hash1, A1.data(), (DWORD)A1.size(), 0)) + throw win_runtime_error(__FUNCTION__ " Error hashing A1."); + hash1.calculate(A1); + + // Hash A and seed. + hmac_hash hash2(cp, CALG_MD5 , padding1); + if (!CryptHashData(hash2, A1.data(), (DWORD)A1.size(), 0) || + !CryptHashData(hash2, (const BYTE*)seed , (DWORD)size_seed, 0)) + throw win_runtime_error(__FUNCTION__ " Error hashing seed,label or data."); + hash2.calculate(hmac1); + off1 = 0; + } + + if (off2 >= hmac2.size()) { + // Rehash A. + hmac_hash hash1(cp, CALG_SHA1 , padding2); + if (!CryptHashData(hash1, A2.data(), (DWORD)A2.size(), 0)) + throw win_runtime_error(__FUNCTION__ " Error hashing A2."); + hash1.calculate(A2); + + // Hash A and seed. + hmac_hash hash2(cp, CALG_SHA1 , padding2); + if (!CryptHashData(hash2, A2.data(), (DWORD)A2.size(), 0) || + !CryptHashData(hash2, (const BYTE*)seed , (DWORD)size_seed, 0)) + throw win_runtime_error(__FUNCTION__ " Error hashing seed,label or data."); + hash2.calculate(hmac2); + off2 = 0; + } + + // XOR combine amount of data we have (and need). + size_t i_end = std::min(i + std::min(hmac1.size() - off1, hmac2.size() - off2), size); + while (i < i_end) + data[i++] = hmac1[off1++] ^ hmac2[off2++]; + } + } else { + // Precalculate HMAC padding for speed. + hmac_padding padding(cp, alg, &secret, sizeof(tls_master_secret)); + + // Prepare A for p_hash. + sanitizing_blob A((unsigned char*)seed, (unsigned char*)seed + size_seed); + + sanitizing_blob hmac; + for (size_t i = 0; i < size; ) { + // Rehash A. + hmac_hash hash1(cp, alg, padding); + if (!CryptHashData(hash1, A.data(), (DWORD)A.size(), 0)) + throw win_runtime_error(__FUNCTION__ " Error hashing A."); + hash1.calculate(A); + + // Hash A and seed. + hmac_hash hash2(cp, alg, padding); + if (!CryptHashData(hash2, A.data(), (DWORD)A.size() , 0) || + !CryptHashData(hash2, (const BYTE*)seed , (DWORD)size_seed, 0)) + throw win_runtime_error(__FUNCTION__ " Error hashing seed,label or data."); + hash2.calculate(hmac); + + size_t n = std::min(hmac.size(), size - i); + data.insert(data.end(), hmac.begin(), hmac.begin() + n); + i += n; + } + } + + return data; +} + + +HCRYPTKEY eap::method_tls::create_key( + _In_ HCRYPTPROV cp, + _In_ ALG_ID alg, + _In_ HCRYPTKEY key, + _In_bytecount_(size_secret) const void *secret, + _In_ size_t size_secret) +{ +#if 1 + UNREFERENCED_PARAMETER(key); + assert(size_secret <= 0xffffffff); + + // Prepare exported key BLOB. + struct key_blob_prefix { + PUBLICKEYSTRUC header; + DWORD size; + } const prefix = { + { + PLAINTEXTKEYBLOB, + CUR_BLOB_VERSION, + 0, + alg, + }, + (DWORD)size_secret, + }; + sanitizing_blob key_blob; + key_blob.reserve(sizeof(key_blob_prefix) + size_secret); + key_blob.assign((const unsigned char*)&prefix, (const unsigned char*)(&prefix + 1)); + key_blob.insert(key_blob.end(), (const unsigned char*)secret, (const unsigned char*)secret + size_secret); + + // Import the key. + winstd::crypt_key key_out; + if (!key_out.import(cp, key_blob.data(), (DWORD)key_blob.size(), NULL, 0)) + throw winstd::win_runtime_error(__FUNCTION__ " Error importing key."); + return key_out.detach(); +#else + // Get private key's algorithm. + ALG_ID alg_key; + if (!CryptGetKeyParam(key, KP_ALGID, alg_key, 0)) + throw win_runtime_error(__FUNCTION__ " Error getting key's algorithm.'"); + + // Get private key's length in bytes. + DWORD size_key = CryptGetKeyParam(key, KP_KEYLEN, size_key, 0) ? size_key/8 : 0; + + // SIMPLEBLOB Format is documented in SDK + // Copy header to buffer +#pragma pack(push) +#pragma pack(1) + struct key_blob_prefix { + PUBLICKEYSTRUC header; + ALG_ID alg; + } const prefix = { + { + SIMPLEBLOB, + CUR_BLOB_VERSION, + 0, + alg, + }, + alg_key, + }; +#pragma pack(pop) + sanitizing_blob key_blob; + key_blob.reserve(sizeof(key_blob_prefix) + size_key); + key_blob.assign((const unsigned char*)&prefix, (const unsigned char*)(&prefix + 1)); + + // Key in EME-PKCS1-v1_5 (RFC 3447). + key_blob.push_back(0); // Initial zero + key_blob.push_back(2); // PKCS #1 block type = 2 + + // PS + size_t size_ps = size_key - size_secret - 3; + assert(size_ps >= 8); +#if 1 + key_blob.insert(key_blob.end(), size_ps, 1); +#else + // Is random PS required at all? We are importing a clear-text session key with the exponent-of-one key. How low on security can we get? + key_blob.insert(key_blob.end(), size_ps, 0); + unsigned char *ps = &*(key_blob.end() - size_ps); + CryptGenRandom(cp, (DWORD)size_ps, ps); + for (size_t i = 0; i < size_ps; i++) + if (ps[i] == 0) ps[i] = 1; +#endif + + key_blob.push_back(0); // PS and M zero delimiter + + // M + key_blob.insert(key_blob.end(), (const unsigned char*)secret, (const unsigned char*)secret + size_secret); + +#ifdef _HOST_LOW_ENDIAN + std::reverse(key_blob.end() - size_key, key_blob.end()); +#endif + + // Import the key. + winstd::crypt_key key_out; + if (!key_out.import(cp, key_blob.data(), (DWORD)key_blob.size(), key, 0)) + throw winstd::win_runtime_error(__FUNCTION__ " Error importing key."); + return key_out.detach(); +#endif +} + +#endif diff --git a/lib/TLS/src/TLS.cpp b/lib/TLS/src/TLS.cpp index fd43bbb..9f5d728 100644 --- a/lib/TLS/src/TLS.cpp +++ b/lib/TLS/src/TLS.cpp @@ -182,6 +182,8 @@ eap::tls_conn_state::tls_conn_state() #ifdef _DEBUG // Initialize state primitive members for diagnostic purposes. : + m_prov_name (NULL), + m_prov_type (0), m_alg_encrypt (0), m_size_enc_key (0), m_size_enc_iv (0), @@ -195,6 +197,8 @@ eap::tls_conn_state::tls_conn_state() eap::tls_conn_state::tls_conn_state(_In_ const tls_conn_state &other) : + m_prov_name (other.m_prov_name ), + m_prov_type (other.m_prov_type ), m_alg_encrypt (other.m_alg_encrypt ), m_size_enc_key (other.m_size_enc_key ), m_size_enc_iv (other.m_size_enc_iv ), @@ -209,6 +213,8 @@ eap::tls_conn_state::tls_conn_state(_In_ const tls_conn_state &other) : eap::tls_conn_state::tls_conn_state(_Inout_ tls_conn_state &&other) : + m_prov_name (std::move(other.m_prov_name )), + m_prov_type (std::move(other.m_prov_type )), m_alg_encrypt (std::move(other.m_alg_encrypt )), m_size_enc_key (std::move(other.m_size_enc_key )), m_size_enc_iv (std::move(other.m_size_enc_iv )), @@ -221,6 +227,8 @@ eap::tls_conn_state::tls_conn_state(_Inout_ tls_conn_state &&other) : { #ifdef _DEBUG // Reinitialize other state primitive members for diagnostic purposes. + other.m_prov_name = NULL; + other.m_prov_type = 0; other.m_alg_encrypt = 0; other.m_size_enc_key = 0; other.m_size_enc_iv = 0; @@ -235,6 +243,8 @@ eap::tls_conn_state::tls_conn_state(_Inout_ tls_conn_state &&other) : eap::tls_conn_state& eap::tls_conn_state::operator=(_In_ const tls_conn_state &other) { if (this != std::addressof(other)) { + m_prov_name = other.m_prov_name ; + m_prov_type = other.m_prov_type ; m_alg_encrypt = other.m_alg_encrypt ; m_size_enc_key = other.m_size_enc_key ; m_size_enc_iv = other.m_size_enc_iv ; @@ -253,6 +263,8 @@ eap::tls_conn_state& eap::tls_conn_state::operator=(_In_ const tls_conn_state &o eap::tls_conn_state& eap::tls_conn_state::operator=(_Inout_ tls_conn_state &&other) { if (this != std::addressof(other)) { + m_prov_name = std::move(other.m_prov_name ); + m_prov_type = std::move(other.m_prov_type ); m_alg_encrypt = std::move(other.m_alg_encrypt ); m_size_enc_key = std::move(other.m_size_enc_key ); m_size_enc_iv = std::move(other.m_size_enc_iv ); @@ -265,6 +277,8 @@ eap::tls_conn_state& eap::tls_conn_state::operator=(_Inout_ tls_conn_state &&oth #ifdef _DEBUG // Reinitialize other state primitive members for diagnostic purposes. + other.m_prov_name = NULL; + other.m_prov_type = 0; other.m_alg_encrypt = 0; other.m_size_enc_key = 0; other.m_size_enc_iv = 0; @@ -277,3 +291,142 @@ eap::tls_conn_state& eap::tls_conn_state::operator=(_Inout_ tls_conn_state &&oth return *this; } + + +void eap::tls_conn_state::set_cipher(_In_ const unsigned char cipher[2]) +{ + if (cipher[0] == 0x00 && cipher[1] == 0x0a) { + // TLS_RSA_WITH_3DES_EDE_CBC_SHA + m_prov_name = NULL; + m_prov_type = PROV_RSA_AES; + m_alg_encrypt = CALG_3DES; + m_size_enc_key = 192/8; // 3DES 192bits + m_size_enc_iv = 64/8; // 3DES 64bits + m_size_enc_block = 64/8; // 3DES 64bits + m_alg_mac = CALG_SHA1; + m_size_mac_key = 160/8; // SHA-1 + m_size_mac_hash = 160/8; // SHA-1 + } else if (cipher[0] == 0x00 && cipher[1] == 0x2f) { + // TLS_RSA_WITH_AES_128_CBC_SHA + m_prov_name = NULL; + m_prov_type = PROV_RSA_AES; + m_alg_encrypt = CALG_AES_128; + m_size_enc_key = 128/8; // AES-128 + m_size_enc_iv = 128/8; // AES-128 + m_size_enc_block = 128/8; // AES-128 + m_alg_mac = CALG_SHA1; + m_size_mac_key = 160/8; // SHA-1 + m_size_mac_hash = 160/8; // SHA-1 + } else if (cipher[0] == 0x00 && cipher[1] == 0x3c) { + // AES128-SHA256 + m_prov_name = NULL; + m_prov_type = PROV_RSA_AES; + m_alg_encrypt = CALG_AES_128; + m_size_enc_key = 128/8; // AES-128 + m_size_enc_iv = 128/8; // AES-128 + m_size_enc_block = 128/8; // AES-128 + m_alg_mac = CALG_SHA_256; + m_size_mac_key = 256/8; // SHA-256 + m_size_mac_hash = 256/8; // SHA-256 + } else if (cipher[0] == 0x00 && cipher[1] == 0x3d) { + // AES256-SHA256 + m_prov_name = MS_ENH_RSA_AES_PROV; + m_prov_type = PROV_RSA_AES; + m_alg_encrypt = CALG_AES_256; + m_size_enc_key = 256/8; // AES-256 + m_size_enc_iv = 128/8; // AES-256 + m_size_enc_block = 128/8; // AES-256 + m_alg_mac = CALG_SHA_256; + m_size_mac_key = 256/8; // SHA-256 + m_size_mac_hash = 256/8; // SHA-256 + } else if (cipher[0] == 0x00 && cipher[1] == 0x40) { + // DHE-DSS-AES128-SHA256 + m_prov_name = MS_ENH_DSS_DH_PROV; + m_prov_type = PROV_DSS_DH; + m_alg_encrypt = CALG_AES_128; + m_size_enc_key = 128/8; // AES-128 + m_size_enc_iv = 128/8; // AES-128 + m_size_enc_block = 128/8; // AES-128 + m_alg_mac = CALG_SHA_256; + m_size_mac_key = 256/8; // SHA-256 + m_size_mac_hash = 256/8; // SHA-256 + } else if (cipher[0] == 0x00 && cipher[1] == 0x67) { + // DHE-RSA-AES128-SHA256 + m_prov_name = MS_DEF_DH_SCHANNEL_PROV; + m_prov_type = PROV_DH_SCHANNEL; + m_alg_encrypt = CALG_AES_128; + m_size_enc_key = 128/8; // AES-128 + m_size_enc_iv = 128/8; // AES-128 + m_size_enc_block = 128/8; // AES-128 + m_alg_mac = CALG_SHA_256; + m_size_mac_key = 256/8; // SHA-256 + m_size_mac_hash = 256/8; // SHA-256 + } else if (cipher[0] == 0x00 && cipher[1] == 0x6a) { + // DHE-DSS-AES256-SHA256 + m_prov_name = MS_ENH_DSS_DH_PROV; + m_prov_type = PROV_DSS_DH; + m_alg_encrypt = CALG_AES_256; + m_size_enc_key = 256/8; // AES-256 + m_size_enc_iv = 128/8; // AES-256 + m_size_enc_block = 128/8; // AES-256 + m_alg_mac = CALG_SHA_256; + m_size_mac_key = 256/8; // SHA-256 + m_size_mac_hash = 256/8; // SHA-256 + } else if (cipher[0] == 0x00 && cipher[1] == 0x6b) { + // DHE-RSA-AES256-SHA256 + m_prov_name = MS_DEF_DH_SCHANNEL_PROV; + m_prov_type = PROV_DH_SCHANNEL; + m_alg_encrypt = CALG_AES_256; + m_size_enc_key = 256/8; // AES-256 + m_size_enc_iv = 128/8; // AES-256 + m_size_enc_block = 128/8; // AES-256 + m_alg_mac = CALG_SHA_256; + m_size_mac_key = 256/8; // SHA-256 + m_size_mac_hash = 256/8; // SHA-256 + } else if (cipher[0] == 0xc0 && cipher[1] == 0x23) { + // ECDHE-ECDSA-AES128-SHA256 + m_prov_name = MS_ENH_DSS_DH_PROV; + m_prov_type = PROV_DSS_DH; + m_alg_encrypt = CALG_AES_128; + m_size_enc_key = 128/8; // AES-128 + m_size_enc_iv = 128/8; // AES-128 + m_size_enc_block = 128/8; // AES-128 + m_alg_mac = CALG_SHA_256; + m_size_mac_key = 256/8; // SHA-256 + m_size_mac_hash = 256/8; // SHA-256 + } else if (cipher[0] == 0xc0 && cipher[1] == 0x24) { + // ECDHE-ECDSA-AES256-SHA384 + m_prov_name = MS_ENH_DSS_DH_PROV; + m_prov_type = PROV_DSS_DH; + m_alg_encrypt = CALG_AES_256; + m_size_enc_key = 256/8; // AES-256 + m_size_enc_iv = 128/8; // AES-256 + m_size_enc_block = 128/8; // AES-256 + m_alg_mac = CALG_SHA_384; + m_size_mac_key = 384/8; // SHA-384 + m_size_mac_hash = 384/8; // SHA-384 + } else if (cipher[0] == 0xc0 && cipher[1] == 0x27) { + // ECDHE-RSA-AES128-SHA256 + m_prov_name = MS_ENH_DSS_DH_PROV; + m_prov_type = PROV_DSS_DH; + m_alg_encrypt = CALG_AES_128; + m_size_enc_key = 128/8; // AES-128 + m_size_enc_iv = 128/8; // AES-128 + m_size_enc_block = 128/8; // AES-128 + m_alg_mac = CALG_SHA_256; + m_size_mac_key = 256/8; // SHA-256 + m_size_mac_hash = 256/8; // SHA-256 + } else if (cipher[0] == 0xc0 && cipher[1] == 0x28) { + // ECDHE-RSA-AES256-SHA384 + m_prov_name = MS_ENH_DSS_DH_PROV; + m_prov_type = PROV_DSS_DH; + m_alg_encrypt = CALG_AES_256; + m_size_enc_key = 256/8; // AES-256 + m_size_enc_iv = 128/8; // AES-256 + m_size_enc_block = 128/8; // AES-256 + m_alg_mac = CALG_SHA_384; + m_size_mac_key = 384/8; // SHA-384 + m_size_mac_hash = 384/8; // SHA-384 + } else + throw win_runtime_error(ERROR_NOT_SUPPORTED, string_printf(__FUNCTION__ " Unknown cipher (received 0x%02x%02x).", cipher[0], cipher[1])); +} diff --git a/lib/TLS_UI/include/TLS_UI.h b/lib/TLS_UI/include/TLS_UI.h index 1d59ad6..e8d0697 100644 --- a/lib/TLS_UI/include/TLS_UI.h +++ b/lib/TLS_UI/include/TLS_UI.h @@ -331,6 +331,9 @@ public: protected: /// \cond internal virtual void OnInitDialog(wxInitDialogEvent& event); +#if EAP_TLS < EAP_TLS_SCHANNEL + virtual bool TransferDataFromWindow(); +#endif /// \endcond protected: diff --git a/lib/TLS_UI/src/TLS_UI.cpp b/lib/TLS_UI/src/TLS_UI.cpp index 05cbe83..d3f741e 100644 --- a/lib/TLS_UI/src/TLS_UI.cpp +++ b/lib/TLS_UI/src/TLS_UI.cpp @@ -603,3 +603,23 @@ void wxTLSConfigPanel::OnInitDialog(wxInitDialogEvent& event) if (m_credentials) m_credentials->GetEventHandler()->ProcessEvent(event); } + + +#if EAP_TLS < EAP_TLS_SCHANNEL + +bool wxTLSConfigPanel::TransferDataFromWindow() +{ + wxCHECK(wxPanel::TransferDataFromWindow(), false); + + if (!m_prov.m_read_only) { + // This is not a provider-locked configuration. The data will get saved. + + // Reset session ID and master secret to force clean connect next time. + m_cfg.m_session_id.clear(); + m_cfg.m_master_secret.clear(); + } + + return true; +} + +#endif diff --git a/lib/TTLS/include/Method.h b/lib/TTLS/include/Method.h index 2a74f47..99eed27 100644 --- a/lib/TTLS/include/Method.h +++ b/lib/TTLS/include/Method.h @@ -113,6 +113,17 @@ namespace eap /// @} protected: +#if EAP_TLS < EAP_TLS_SCHANNEL + + /// + /// Generates master session key + /// + /// \sa [The EAP-TLS Authentication Protocol (Chapter 2.3. Key Hierarchy)](https://tools.ietf.org/html/rfc5216#section-2.3) + /// + virtual void derive_msk(); + +#else + /// /// Processes an application message /// @@ -121,6 +132,8 @@ namespace eap /// virtual void process_application_data(_In_bytecount_(size_msg) const void *msg, _In_ size_t size_msg); +#endif + /// /// Makes a PAP client message /// diff --git a/lib/TTLS/src/Method.cpp b/lib/TTLS/src/Method.cpp index 6698343..ac022c0 100644 --- a/lib/TTLS/src/Method.cpp +++ b/lib/TTLS/src/Method.cpp @@ -71,6 +71,22 @@ void eap::method_ttls::process_request_packet( // Do the TLS. method_tls::process_request_packet(pReceivedPacket, dwReceivedPacketSize, pEapOutput); + +#if EAP_TLS < EAP_TLS_SCHANNEL + if (m_phase == phase_application_data) { + // Send inner authentication. + if (!m_state_client.m_alg_encrypt) + throw runtime_error(__FUNCTION__ " Refusing to send credentials unencrypted."); + + m_module.log_event(&EAPMETHOD_TTLS_INNER_CRED, event_data((unsigned int)eap_type_ttls), event_data(m_cred.m_inner->get_name()), event_data::blank); + + m_packet_res.m_code = EapCodeResponse; + m_packet_res.m_id = m_packet_req.m_id; + m_packet_res.m_flags = 0; + sanitizing_blob msg_application(make_message(tls_message_type_application_data, make_pap_client())); + m_packet_res.m_data.insert(m_packet_res.m_data.end(), msg_application.begin(), msg_application.end()); + } +#endif } @@ -116,12 +132,14 @@ void eap::method_ttls::get_result( throw win_runtime_error(ERROR_NOT_SUPPORTED, __FUNCTION__ " Not supported."); } +#if EAP_TLS >= EAP_TLS_SCHANNEL // EAP-TTLS uses different label in PRF for MSK derivation than EAP-TLS. static const DWORD s_key_id = 0x01; // EAP-TTLSv0 Keying Material static const SecPkgContext_EapPrfInfo s_prf_info = { 0, sizeof(s_key_id), (PBYTE)&s_key_id }; SECURITY_STATUS status = SetContextAttributes(m_sc_ctx, SECPKG_ATTR_EAP_PRF_INFO, (void*)&s_prf_info, sizeof(s_prf_info)); if (FAILED(status)) throw sec_runtime_error(status, __FUNCTION__ "Error setting EAP-TTLS PRF in Schannel."); +#endif // The TLS was OK. method_tls::get_result(EapPeerMethodResultSuccess, ppResult); @@ -136,6 +154,43 @@ void eap::method_ttls::get_result( } +#if EAP_TLS < EAP_TLS_SCHANNEL + +void eap::method_ttls::derive_msk() +{ + // + // TLS versions 1.0 [RFC2246] and 1.1 [RFC4346] define the same PRF + // function, and any EAP-TTLSv0 implementation based on these versions + // of TLS must use the PRF defined therein. It is expected that future + // versions of or extensions to the TLS protocol will permit alternative + // PRF functions to be negotiated. If an alternative PRF function is + // specified for the underlying TLS version or has been negotiated + // during the TLS handshake negotiation, then that alternative PRF + // function must be used in EAP-TTLSv0 computations instead of the TLS + // 1.0/1.1 PRF. + // + // [Extensible Authentication Protocol Tunneled Transport Layer Security Authenticated Protocol Version 0 (EAP-TTLSv0) (Chapter 7.8. Use of TLS PRF)](https://tools.ietf.org/html/rfc5281#section-7.8) + // + // If we use PRF_SHA256() the key exchange fails. Therefore we use PRF of TLS 1.0/1.1. + // + static const unsigned char s_label[] = "ttls keying material"; + sanitizing_blob seed(s_label, s_label + _countof(s_label) - 1); + seed.insert(seed.end(), (const unsigned char*)&m_random_client, (const unsigned char*)(&m_random_client + 1)); + seed.insert(seed.end(), (const unsigned char*)&m_random_server, (const unsigned char*)(&m_random_server + 1)); + sanitizing_blob key_block(prf(m_cp, CALG_TLS1PRF, m_master_secret, seed, 2*sizeof(tls_random))); + const unsigned char *_key_block = key_block.data(); + + // MSK: MPPE-Recv-Key + memcpy(&m_key_mppe_client, _key_block, sizeof(tls_random)); + _key_block += sizeof(tls_random); + + // MSK: MPPE-Send-Key + memcpy(&m_key_mppe_server, _key_block, sizeof(tls_random)); + _key_block += sizeof(tls_random); +} + +#else + void eap::method_ttls::process_application_data(_In_bytecount_(size_msg) const void *msg, _In_ size_t size_msg) { UNREFERENCED_PARAMETER(msg); @@ -183,6 +238,8 @@ void eap::method_ttls::process_application_data(_In_bytecount_(size_msg) const v m_packet_res.m_data.insert(m_packet_res.m_data.end(), (const unsigned char*)buf[0].pvBuffer, (const unsigned char*)buf[0].pvBuffer + buf[0].cbBuffer + buf[1].cbBuffer + buf[2].cbBuffer); } +#endif + eap::sanitizing_blob eap::method_ttls::make_pap_client() const {