TLS implementation continues...
This commit is contained in:
parent
ce9e636840
commit
788c8cdb16
@ -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<unsigned char> 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,31 +328,80 @@ 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<unsigned char> &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<unsigned char> data,
|
||||
_Out_ EAP_ERROR **ppEapError);
|
||||
|
||||
public:
|
||||
enum phase_t {
|
||||
@ -216,102 +410,24 @@ namespace eap
|
||||
phase_server_hello = 1,
|
||||
} m_phase; ///< Session phase
|
||||
|
||||
struct packet
|
||||
{
|
||||
EapCode m_code; ///< Packet code
|
||||
BYTE m_id; ///< Packet ID
|
||||
BYTE m_flags; ///< Packet flags
|
||||
std::vector<BYTE> m_data; ///< Packet data
|
||||
|
||||
///
|
||||
/// 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();
|
||||
}
|
||||
m_packet_req, ///< Request packet
|
||||
m_packet_res; ///< Response packet
|
||||
packet m_packet_req; ///< Request packet
|
||||
packet m_packet_res; ///< Response packet
|
||||
|
||||
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
|
||||
winstd::crypt_key m_key_encrypt; ///< Key for encrypting messages
|
||||
winstd::crypt_key m_key_decrypt; ///< Key for decrypting 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
|
||||
random m_random_client; ///< Client random
|
||||
random m_random_server; ///< Server random
|
||||
|
||||
sanitizing_blob m_session_id; ///< TLS session ID
|
||||
|
||||
std::list<winstd::cert_context> m_server_cert_chain; ///< Server certificate chain
|
||||
|
||||
bool m_send_client_cert; ///< Did server request client certificate?
|
||||
|
||||
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
|
||||
|
||||
|
@ -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<size_t>(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<unsigned char> 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<unsigned char> 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<unsigned char> &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
|
||||
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<unsigned char> 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<unsigned char> 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<unsigned char> 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;
|
||||
}
|
||||
|
||||
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));
|
||||
// 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;
|
||||
}
|
||||
|
||||
return *this;
|
||||
// Append to output data.
|
||||
data.insert(data.end(), block.begin(), block.end());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user