Encryption/decryption revised

- Number of memory copying reduced
- HMAC verification of server packets added
- Handshake hashing simplified
This commit is contained in:
Simon Rozman 2016-08-14 18:51:18 +02:00
parent 735d669887
commit 95e2f7e01b
4 changed files with 168 additions and 122 deletions

View File

@ -127,7 +127,7 @@ namespace eap
/// ///
/// TLS message /// TLS message
/// ///
struct message struct message_header
{ {
unsigned char type; ///< Message type (one of `message_type_t` constants) unsigned char type; ///< Message type (one of `message_type_t` constants)
struct { struct {
@ -135,7 +135,6 @@ namespace eap
unsigned char minor; ///< Minor version unsigned char minor; ///< Minor version
} version; ///< SSL/TLS version } version; ///< SSL/TLS version
unsigned char length[2]; ///< Message length (in network byte order) unsigned char length[2]; ///< Message length (in network byte order)
unsigned char data[1]; ///< Message data
}; };
#pragma pack(pop) #pragma pack(pop)
@ -283,31 +282,40 @@ namespace eap
/// ///
/// \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] type Message type /// \param[in] type Message type
/// \param[in] msg Message data contents /// \param[in] data Message data contents
/// \param[in] encrypt Should \p data get encrypted?
/// ///
/// \returns TLS message message /// \returns TLS message message
/// ///
static eap::sanitizing_blob make_message(_In_ tls_message_type_t type, _In_ const sanitizing_blob &msg); eap::sanitizing_blob make_message(_In_ tls_message_type_t type, _Inout_ sanitizing_blob &data, _In_ bool encrypt);
/// ///
/// Makes a TLS message /// Hashes handshake message for "finished" message validation.
/// ///
/// \param[in] type Message type /// \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] msg Message data contents
/// \param[in] encrypt Should the message be encrypted?
/// ///
/// \returns TLS message message /// \param[in] data Data to hash
/// \param[in] size \p data size in bytes
/// ///
inline eap::sanitizing_blob make_message(_In_ tls_message_type_t type, _In_ const sanitizing_blob &msg, _In_ bool encrypted) inline void hash_handshake(_In_count_(size) const void *data, _In_ size_t size)
{ {
if (encrypted) { CryptHashData(m_hash_handshake_msgs_md5 , (const BYTE*)data, (DWORD)size, 0);
// Make unencrypted handshake, encrypt it, then make a new handshake message. CryptHashData(m_hash_handshake_msgs_sha1, (const BYTE*)data, (DWORD)size, 0);
sanitizing_blob msg_enc(make_message(type, msg)); }
encrypt_message(msg_enc);
return make_message(type, msg_enc); ///
} else /// Hashes handshake message for "finished" message validation.
return make_message(type, msg); ///
/// \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<class _Ty, class _Ax>
inline void hash_handshake(_In_ const std::vector<_Ty, _Ax> &data)
{
hash_handshake(data.data(), data.size() * sizeof(_Ty));
} }
/// ///
@ -391,16 +399,18 @@ namespace eap
/// ///
/// Encrypt TLS message /// Encrypt TLS message
/// ///
/// \param[inout] msg TLS message to encrypt /// \param[in] hdr Original TLS header for HMAC verification
/// \param[inout] data TLS message to encrypt
/// ///
void encrypt_message(_Inout_ sanitizing_blob &msg); void encrypt_message(_In_ const message_header *hdr, _Inout_ sanitizing_blob &data);
/// ///
/// Decrypt TLS message /// Decrypt TLS message
/// ///
/// \param[inout] msg TLS message to decrypt /// \param[in] hdr Original TLS header for HMAC verification
/// \param[inout] data TLS message to decrypt
/// ///
void decrypt_message(_Inout_ sanitizing_blob &msg) const; void decrypt_message(_In_ const message_header *hdr, _Inout_ sanitizing_blob &data);
/// ///
/// Calculates pseudo-random P_hash data defined in RFC 5246 /// Calculates pseudo-random P_hash data defined in RFC 5246
@ -478,7 +488,7 @@ namespace eap
tls_conn_state m_state; ///< TLS connection state for fast reconnect tls_conn_state m_state; ///< TLS connection state for fast reconnect
sanitizing_blob m_padding_hmac_client; ///< Padding (key) for client side HMAC calculation sanitizing_blob m_padding_hmac_client; ///< Padding (key) for client side HMAC calculation
//sanitizing_blob m_padding_hmac_server; ///< Padding (key) for server side HMAC calculation sanitizing_blob m_padding_hmac_server; ///< Padding (key) for server side HMAC calculation
winstd::crypt_key m_key_client; ///< Key for encrypting messages winstd::crypt_key m_key_client; ///< Key for encrypting messages
winstd::crypt_key m_key_server; ///< Key for decrypting messages winstd::crypt_key m_key_server; ///< Key for decrypting messages
@ -497,7 +507,8 @@ namespace eap
bool m_server_finished; ///< Did server send a valid finish message? bool m_server_finished; ///< Did server send a valid finish message?
bool m_cipher_spec; ///< Did server specify cipher? bool m_cipher_spec; ///< Did server specify cipher?
unsigned __int64 m_seq_num; ///< Sequence number for encryption unsigned __int64 m_seq_num_client; ///< Sequence number for encrypting
unsigned __int64 m_seq_num_server; ///< Sequence number for decrypting
// The following members are required to avoid memory leakage in get_result() // The following members are required to avoid memory leakage in get_result()
EAP_ATTRIBUTES m_eap_attr_desc; ///< EAP Radius attributes descriptor EAP_ATTRIBUTES m_eap_attr_desc; ///< EAP Radius attributes descriptor

View File

@ -356,8 +356,10 @@ namespace eap
ALG_ID m_alg_encrypt; ///> Bulk encryption algorithm 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_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`) size_t m_size_enc_iv; ///> Encryption initialization vector size in bytes (has to comply with `m_alg_encrypt`)
size_t m_size_enc_block; ///> Encryption block size in bytes (has to comply with `m_alg_encrypt`)
ALG_ID m_alg_mac; ///> Message authenticy check algorithm ALG_ID m_alg_mac; ///> Message authenticy check algorithm
size_t m_size_mac_key; ///> Message authenticy check algorithm key size (has to comply with `m_alg_mac`) size_t m_size_mac_key; ///> Message authenticy check algorithm key size (has to comply with `m_alg_mac`)
size_t m_size_mac_hash; ///> Message authenticy check algorithm result size (has to comply with `m_alg_mac`)
tls_master_secret m_master_secret; ///< TLS master secret tls_master_secret m_master_secret; ///< TLS master secret
tls_random m_random_client; ///< Client tls_random tls_random m_random_client; ///< Client tls_random
tls_random m_random_server; ///< Server tls_random tls_random m_random_server; ///< Server tls_random

View File

@ -101,7 +101,8 @@ eap::method_tls::method_tls(_In_ module &module, _In_ config_method_tls &cfg, _I
m_server_hello_done(false), m_server_hello_done(false),
m_server_finished(false), m_server_finished(false),
m_cipher_spec(false), m_cipher_spec(false),
m_seq_num(0), m_seq_num_client(0),
m_seq_num_server(0),
m_blob_cfg(NULL), m_blob_cfg(NULL),
method(module, cfg, cred) method(module, cfg, cred)
{ {
@ -116,7 +117,7 @@ eap::method_tls::method_tls(_In_ const method_tls &other) :
m_packet_res(other.m_packet_res), m_packet_res(other.m_packet_res),
m_state(other.m_state), m_state(other.m_state),
m_padding_hmac_client(other.m_padding_hmac_client), m_padding_hmac_client(other.m_padding_hmac_client),
//m_padding_hmac_server(other.m_padding_hmac_server), m_padding_hmac_server(other.m_padding_hmac_server),
m_key_client(other.m_key_client), m_key_client(other.m_key_client),
m_key_server(other.m_key_server), m_key_server(other.m_key_server),
m_key_mppe_send(other.m_key_mppe_send), m_key_mppe_send(other.m_key_mppe_send),
@ -129,7 +130,8 @@ eap::method_tls::method_tls(_In_ const method_tls &other) :
m_server_hello_done(other.m_server_hello_done), m_server_hello_done(other.m_server_hello_done),
m_server_finished(other.m_server_finished), m_server_finished(other.m_server_finished),
m_cipher_spec(other.m_cipher_spec), m_cipher_spec(other.m_cipher_spec),
m_seq_num(other.m_seq_num), m_seq_num_client(other.m_seq_num_client),
m_seq_num_server(other.m_seq_num_server),
method(other) method(other)
{ {
} }
@ -143,7 +145,7 @@ eap::method_tls::method_tls(_Inout_ method_tls &&other) :
m_packet_res(std::move(other.m_packet_res)), m_packet_res(std::move(other.m_packet_res)),
m_state(std::move(other.m_state)), m_state(std::move(other.m_state)),
m_padding_hmac_client(std::move(other.m_padding_hmac_client)), m_padding_hmac_client(std::move(other.m_padding_hmac_client)),
//m_padding_hmac_server(std::move(other.m_padding_hmac_server)), m_padding_hmac_server(std::move(other.m_padding_hmac_server)),
m_key_client(std::move(other.m_key_client)), m_key_client(std::move(other.m_key_client)),
m_key_server(std::move(other.m_key_server)), m_key_server(std::move(other.m_key_server)),
m_key_mppe_send(std::move(other.m_key_mppe_send)), m_key_mppe_send(std::move(other.m_key_mppe_send)),
@ -156,7 +158,8 @@ eap::method_tls::method_tls(_Inout_ method_tls &&other) :
m_server_hello_done(std::move(other.m_server_hello_done)), m_server_hello_done(std::move(other.m_server_hello_done)),
m_server_finished(std::move(other.m_server_finished)), m_server_finished(std::move(other.m_server_finished)),
m_cipher_spec(std::move(other.m_cipher_spec)), m_cipher_spec(std::move(other.m_cipher_spec)),
m_seq_num(std::move(other.m_seq_num)), m_seq_num_client(std::move(other.m_seq_num_client)),
m_seq_num_server(std::move(other.m_seq_num_server)),
method(std::move(other)) method(std::move(other))
{ {
} }
@ -180,7 +183,7 @@ eap::method_tls& eap::method_tls::operator=(_In_ const method_tls &other)
m_packet_res = other.m_packet_res; m_packet_res = other.m_packet_res;
m_state = other.m_state; m_state = other.m_state;
m_padding_hmac_client = other.m_padding_hmac_client; m_padding_hmac_client = other.m_padding_hmac_client;
//m_padding_hmac_server = other.m_padding_hmac_server; m_padding_hmac_server = other.m_padding_hmac_server;
m_key_client = other.m_key_client; m_key_client = other.m_key_client;
m_key_server = other.m_key_server; m_key_server = other.m_key_server;
m_key_mppe_send = other.m_key_mppe_send; m_key_mppe_send = other.m_key_mppe_send;
@ -193,7 +196,8 @@ eap::method_tls& eap::method_tls::operator=(_In_ const method_tls &other)
m_server_hello_done = other.m_server_hello_done; m_server_hello_done = other.m_server_hello_done;
m_server_finished = other.m_server_finished; m_server_finished = other.m_server_finished;
m_cipher_spec = other.m_cipher_spec; m_cipher_spec = other.m_cipher_spec;
m_seq_num = other.m_seq_num; m_seq_num_client = other.m_seq_num_client;
m_seq_num_server = other.m_seq_num_server;
} }
return *this; return *this;
@ -211,7 +215,7 @@ eap::method_tls& eap::method_tls::operator=(_Inout_ method_tls &&other)
m_packet_res = std::move(other.m_packet_res); m_packet_res = std::move(other.m_packet_res);
m_state = std::move(other.m_state); m_state = std::move(other.m_state);
m_padding_hmac_client = std::move(other.m_padding_hmac_client); m_padding_hmac_client = std::move(other.m_padding_hmac_client);
//m_padding_hmac_server = std::move(other.m_padding_hmac_server); m_padding_hmac_server = std::move(other.m_padding_hmac_server);
m_key_client = std::move(other.m_key_client); m_key_client = std::move(other.m_key_client);
m_key_server = std::move(other.m_key_server); m_key_server = std::move(other.m_key_server);
m_key_mppe_send = std::move(other.m_key_mppe_send); m_key_mppe_send = std::move(other.m_key_mppe_send);
@ -224,7 +228,8 @@ eap::method_tls& eap::method_tls::operator=(_Inout_ method_tls &&other)
m_server_hello_done = std::move(other.m_server_hello_done); m_server_hello_done = std::move(other.m_server_hello_done);
m_server_finished = std::move(other.m_server_finished); m_server_finished = std::move(other.m_server_finished);
m_cipher_spec = std::move(other.m_cipher_spec); m_cipher_spec = std::move(other.m_cipher_spec);
m_seq_num = std::move(other.m_seq_num); m_seq_num_client = std::move(other.m_seq_num_client);
m_seq_num_server = std::move(other.m_seq_num_server);
} }
return *this; return *this;
@ -237,7 +242,7 @@ void eap::method_tls::begin_session(
_In_ HANDLE hTokenImpersonateUser, _In_ HANDLE hTokenImpersonateUser,
_In_ DWORD dwMaxSendPacketSize) _In_ DWORD dwMaxSendPacketSize)
{ {
eap::method::begin_session(dwFlags, pAttributeArray, hTokenImpersonateUser, dwMaxSendPacketSize); method::begin_session(dwFlags, pAttributeArray, hTokenImpersonateUser, dwMaxSendPacketSize);
// Create cryptographics provider. // Create cryptographics provider.
if (!m_cp.create(NULL, MS_ENHANCED_PROV, PROV_RSA_FULL)) if (!m_cp.create(NULL, MS_ENHANCED_PROV, PROV_RSA_FULL))
@ -343,7 +348,7 @@ void eap::method_tls::process_request_packet(
// Generate client randomness. // Generate client randomness.
m_padding_hmac_client.clear(); m_padding_hmac_client.clear();
//m_padding_hmac_server.clear(); m_padding_hmac_server.clear();
m_key_client.free(); m_key_client.free();
m_key_server.free(); m_key_server.free();
m_key_mppe_send.clear(); m_key_mppe_send.clear();
@ -359,11 +364,13 @@ void eap::method_tls::process_request_packet(
if (!m_hash_handshake_msgs_sha1.create(m_cp, CALG_SHA1)) if (!m_hash_handshake_msgs_sha1.create(m_cp, CALG_SHA1))
throw win_runtime_error(__FUNCTION__ " Error creating SHA-1 hashing object."); throw win_runtime_error(__FUNCTION__ " Error creating SHA-1 hashing object.");
m_send_client_cert = false; m_send_client_cert = false;
m_server_hello_done = false; m_server_hello_done = false;
m_server_finished = false; m_server_finished = false;
m_cipher_spec = false; m_cipher_spec = false;
m_seq_num = 0;
m_seq_num_client = 0;
m_seq_num_server = 0;
} }
switch (m_phase) { switch (m_phase) {
@ -373,10 +380,9 @@ void eap::method_tls::process_request_packet(
m_packet_res.m_id = m_packet_req.m_id; m_packet_res.m_id = m_packet_req.m_id;
m_packet_res.m_flags = 0; m_packet_res.m_flags = 0;
sanitizing_blob hello(make_client_hello()); sanitizing_blob hello(make_client_hello());
hash_handshake(hello);
sanitizing_blob handshake(make_message(tls_message_type_handshake, hello, m_cipher_spec)); sanitizing_blob handshake(make_message(tls_message_type_handshake, hello, m_cipher_spec));
m_packet_res.m_data.assign(handshake.begin(), handshake.end()); m_packet_res.m_data.assign(handshake.begin(), handshake.end());
CryptHashData(m_hash_handshake_msgs_md5 , hello.data(), (DWORD)hello.size(), 0);
CryptHashData(m_hash_handshake_msgs_sha1, hello.data(), (DWORD)hello.size(), 0);
m_phase = phase_req_server_hello; m_phase = phase_req_server_hello;
@ -416,10 +422,9 @@ void eap::method_tls::process_request_packet(
if (m_send_client_cert) { if (m_send_client_cert) {
// Client certificate requested. // Client certificate requested.
sanitizing_blob client_cert(make_client_cert()); sanitizing_blob client_cert(make_client_cert());
hash_handshake(client_cert);
sanitizing_blob handshake(make_message(tls_message_type_handshake, client_cert, m_cipher_spec)); sanitizing_blob handshake(make_message(tls_message_type_handshake, client_cert, m_cipher_spec));
m_packet_res.m_data.insert(m_packet_res.m_data.end(), handshake.begin(), handshake.end()); m_packet_res.m_data.insert(m_packet_res.m_data.end(), handshake.begin(), handshake.end());
CryptHashData(m_hash_handshake_msgs_md5 , client_cert.data(), (DWORD)client_cert.size(), 0);
CryptHashData(m_hash_handshake_msgs_sha1, client_cert.data(), (DWORD)client_cert.size(), 0);
} }
// Generate pre-master secret. PMS will get sanitized in its destructor when going out-of-scope. // Generate pre-master secret. PMS will get sanitized in its destructor when going out-of-scope.
@ -434,10 +439,9 @@ void eap::method_tls::process_request_packet(
// Create client key exchange message, and append to packet. // Create client key exchange message, and append to packet.
sanitizing_blob client_key_exchange(make_client_key_exchange(pms)); sanitizing_blob client_key_exchange(make_client_key_exchange(pms));
hash_handshake(client_key_exchange);
sanitizing_blob handshake(make_message(tls_message_type_handshake, client_key_exchange, m_cipher_spec)); sanitizing_blob handshake(make_message(tls_message_type_handshake, client_key_exchange, m_cipher_spec));
m_packet_res.m_data.insert(m_packet_res.m_data.end(), handshake.begin(), handshake.end()); m_packet_res.m_data.insert(m_packet_res.m_data.end(), handshake.begin(), handshake.end());
CryptHashData(m_hash_handshake_msgs_md5 , client_key_exchange.data(), (DWORD)client_key_exchange.size(), 0);
CryptHashData(m_hash_handshake_msgs_sha1, client_key_exchange.data(), (DWORD)client_key_exchange.size(), 0);
if (m_send_client_cert) { if (m_send_client_cert) {
// TODO: Create and append certificate_verify message! // TODO: Create and append certificate_verify message!
@ -458,10 +462,9 @@ void eap::method_tls::process_request_packet(
// Create finished message, and append to packet. // Create finished message, and append to packet.
sanitizing_blob finished(make_finished()); sanitizing_blob finished(make_finished());
hash_handshake(finished);
sanitizing_blob handshake(make_message(tls_message_type_handshake, finished, m_cipher_spec)); sanitizing_blob handshake(make_message(tls_message_type_handshake, finished, m_cipher_spec));
m_packet_res.m_data.insert(m_packet_res.m_data.end(), handshake.begin(), handshake.end()); m_packet_res.m_data.insert(m_packet_res.m_data.end(), handshake.begin(), handshake.end());
CryptHashData(m_hash_handshake_msgs_md5 , finished.data(), (DWORD)finished.size(), 0);
CryptHashData(m_hash_handshake_msgs_sha1, finished.data(), (DWORD)finished.size(), 0);
pEapOutput->fAllowNotifications = FALSE; pEapOutput->fAllowNotifications = FALSE;
pEapOutput->action = EapPeerMethodResponseActionSend; pEapOutput->action = EapPeerMethodResponseActionSend;
@ -748,7 +751,7 @@ eap::sanitizing_blob eap::method_tls::make_change_chiper_spec()
1, // Message size (low-order byte) 1, // Message size (low-order byte)
1, // Message: change_cipher_spec is always "1" 1, // Message: change_cipher_spec is always "1"
}; };
return eap::sanitizing_blob(s_msg_css, s_msg_css + _countof(s_msg_css)); return sanitizing_blob(s_msg_css, s_msg_css + _countof(s_msg_css));
} }
@ -782,30 +785,41 @@ eap::sanitizing_blob eap::method_tls::make_finished() const
} }
eap::sanitizing_blob eap::method_tls::make_message(_In_ tls_message_type_t type, _In_ const sanitizing_blob &msg) eap::sanitizing_blob eap::method_tls::make_message(_In_ tls_message_type_t type, _Inout_ sanitizing_blob &data, _In_ bool encrypt)
{ {
size_t size_msg = msg.size(); size_t size_data = data.size();
eap::sanitizing_blob msg_h; assert(size_data <= 0xffff);
msg_h.reserve( message_header hdr = {
1 + // SSL record type (unsigned char)type, // SSL record type
2 + // SSL version {
2 + // Message size 3, // SSL major version
size_msg); // Message 1, // SSL minor version
},
{
// Data length (unencrypted, network byte order)
(unsigned char)((size_data >> 8) & 0xff),
(unsigned char)((size_data ) & 0xff),
}
};
// SSL record type sanitizing_blob msg;
msg_h.push_back((unsigned char)type); if (encrypt) {
encrypt_message(&hdr, data);
// SSL version: TLS 1.0 // Update message size.
msg_h.push_back(3); // SSL major version size_t size_data_enc = data.size();
msg_h.push_back(1); // SSL minor version *(unsigned short*)hdr.length = htons((unsigned short)size_data_enc);
msg.reserve(sizeof(message_header) + size_data_enc);
} else
msg.reserve(sizeof(message_header) + size_data);
// Message // TLS header
assert(size_msg <= 0xffff); msg.assign((const unsigned char*)&hdr, (const unsigned char*)(&hdr + 1));
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.begin(), msg.end());
return msg_h; // Data
msg.insert(msg.end(), data.begin(), data.end());
return msg;
} }
@ -828,8 +842,8 @@ void eap::method_tls::derive_keys()
_key_block += m_state.m_size_mac_key; _key_block += m_state.m_size_mac_key;
// server_write_MAC_secret // server_write_MAC_secret
//m_padding_hmac_server.resize(sizeof(hash_hmac::padding_t)); m_padding_hmac_server.resize(sizeof(hash_hmac::padding_t));
//hash_hmac::inner_padding(m_cp, m_state.m_alg_mac, _key_block, m_state.m_size_mac_key, m_padding_hmac_server.data()); hash_hmac::inner_padding(m_cp, m_state.m_alg_mac, _key_block, m_state.m_size_mac_key, m_padding_hmac_server.data());
_key_block += m_state.m_size_mac_key; _key_block += m_state.m_size_mac_key;
// Microsoft CryptoAPI does not support importing clear text session keys. // Microsoft CryptoAPI does not support importing clear text session keys.
@ -879,12 +893,14 @@ void eap::method_tls::derive_msk()
void eap::method_tls::process_packet(_In_bytecount_(size_pck) const void *_pck, _In_ size_t size_pck) 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; ) { for (const unsigned char *pck = (const unsigned char*)_pck, *pck_end = pck + size_pck; pck < pck_end; ) {
if (pck + 5 > pck_end) if (pck + 5 > pck_end)
throw win_runtime_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, __FUNCTION__ " Incomplete message header."); throw win_runtime_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, __FUNCTION__ " Incomplete message header.");
const message *hdr = (const message*)pck; const message_header *hdr = (const message_header*)pck;
const unsigned char const unsigned char
*msg = hdr->data, *msg = (const unsigned char*)(hdr + 1),
*msg_end = msg + ntohs(*(unsigned short*)hdr->length); *msg_end = msg + ntohs(*(unsigned short*)hdr->length);
if (msg_end > pck_end) if (msg_end > pck_end)
throw win_runtime_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, __FUNCTION__ " Incomplete message data."); throw win_runtime_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, __FUNCTION__ " Incomplete message data.");
@ -899,7 +915,7 @@ void eap::method_tls::process_packet(_In_bytecount_(size_pck) const void *_pck,
case tls_message_type_alert: case tls_message_type_alert:
if (m_cipher_spec) { if (m_cipher_spec) {
sanitizing_blob msg_dec(msg, msg_end); sanitizing_blob msg_dec(msg, msg_end);
decrypt_message(msg_dec); decrypt_message(hdr, msg_dec);
process_alert(msg_dec.data(), msg_dec.size()); process_alert(msg_dec.data(), msg_dec.size());
} else } else
process_alert(msg, msg_end - msg); process_alert(msg, msg_end - msg);
@ -908,7 +924,7 @@ void eap::method_tls::process_packet(_In_bytecount_(size_pck) const void *_pck,
case tls_message_type_handshake: case tls_message_type_handshake:
if (m_cipher_spec) { if (m_cipher_spec) {
sanitizing_blob msg_dec(msg, msg_end); sanitizing_blob msg_dec(msg, msg_end);
decrypt_message(msg_dec); decrypt_message(hdr, msg_dec);
process_handshake(msg_dec.data(), msg_dec.size()); process_handshake(msg_dec.data(), msg_dec.size());
} else } else
process_handshake(msg, msg_end - msg); process_handshake(msg, msg_end - msg);
@ -919,7 +935,7 @@ void eap::method_tls::process_packet(_In_bytecount_(size_pck) const void *_pck,
throw win_runtime_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, __FUNCTION__ " Application data should be encrypted."); throw win_runtime_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, __FUNCTION__ " Application data should be encrypted.");
sanitizing_blob msg_dec(msg, msg_end); sanitizing_blob msg_dec(msg, msg_end);
decrypt_message(msg_dec); decrypt_message(hdr, msg_dec);
process_application_data(msg_dec.data(), msg_dec.size()); process_application_data(msg_dec.data(), msg_dec.size());
break; break;
} }
@ -927,7 +943,7 @@ void eap::method_tls::process_packet(_In_bytecount_(size_pck) const void *_pck,
default: default:
if (m_cipher_spec) { if (m_cipher_spec) {
sanitizing_blob msg_dec(msg, msg_end); sanitizing_blob msg_dec(msg, msg_end);
decrypt_message(msg_dec); decrypt_message(hdr, msg_dec);
process_vendor_data(hdr->type, msg_dec.data(), msg_dec.size()); process_vendor_data(hdr->type, msg_dec.data(), msg_dec.size());
} else } else
process_vendor_data(hdr->type, msg, msg_end - msg); process_vendor_data(hdr->type, msg, msg_end - msg);
@ -1017,11 +1033,13 @@ void eap::method_tls::process_handshake(_In_bytecount_(msg_size) const void *_ms
throw win_runtime_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, __FUNCTION__ " Cipher or incomplete."); throw win_runtime_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, __FUNCTION__ " Cipher or incomplete.");
if (rec[0] == 0x00 || rec[1] == 0x0a) { if (rec[0] == 0x00 || rec[1] == 0x0a) {
// TLS_RSA_WITH_3DES_EDE_CBC_SHA // TLS_RSA_WITH_3DES_EDE_CBC_SHA
m_state.m_alg_encrypt = CALG_3DES; m_state.m_alg_encrypt = CALG_3DES;
m_state.m_size_enc_key = 192/8; // 3DES 192bits m_state.m_size_enc_key = 192/8; // 3DES 192bits
m_state.m_size_enc_iv = 64/8; // 3DES 64bits m_state.m_size_enc_iv = 64/8; // 3DES 64bits
m_state.m_alg_mac = CALG_SHA1; m_state.m_size_enc_block = 64/8; // 3DES 64bits
m_state.m_size_mac_key = 160/8; // SHA-1 m_state.m_alg_mac = CALG_SHA1;
m_state.m_size_mac_key = 160/8; // SHA-1
m_state.m_size_mac_hash = 160/8; // SHA-1
} else } else
throw win_runtime_error(ERROR_NOT_SUPPORTED, string_printf(__FUNCTION__ " Other than requested cipher selected (received 0x%02x%02x).", rec[0], rec[1])); throw win_runtime_error(ERROR_NOT_SUPPORTED, string_printf(__FUNCTION__ " Other than requested cipher selected (received 0x%02x%02x).", rec[0], rec[1]));
@ -1109,8 +1127,7 @@ void eap::method_tls::process_handshake(_In_bytecount_(msg_size) const void *_ms
msg = rec_end; msg = rec_end;
} }
CryptHashData(m_hash_handshake_msgs_md5 , (const BYTE*)_msg, (DWORD)msg_size, 0); hash_handshake(_msg, msg_size);
CryptHashData(m_hash_handshake_msgs_sha1, (const BYTE*)_msg, (DWORD)msg_size, 0);
} }
@ -1226,69 +1243,83 @@ void eap::method_tls::verify_server_trust() const
} }
void eap::method_tls::encrypt_message(_Inout_ sanitizing_blob &msg) void eap::method_tls::encrypt_message(_In_ const message_header *hdr, _Inout_ sanitizing_blob &data)
{ {
// Create a HMAC hash. // Hash sequence number, TLS header, and message.
size_t size_data = data.size();
assert(size_data == ntohs(*(unsigned short*)hdr->length));
hash_hmac hash(m_cp, m_state.m_alg_mac, m_padding_hmac_client.data()); hash_hmac hash(m_cp, m_state.m_alg_mac, m_padding_hmac_client.data());
unsigned __int64 seq_num = htonll(m_seq_num_client);
// Hash sequence number and message. if (!CryptHashData(hash, (const BYTE*)&seq_num , sizeof(seq_num ), 0) ||
unsigned __int64 seq_num = htonll(m_seq_num); !CryptHashData(hash, (const BYTE*)hdr , sizeof(message_header), 0) ||
if (!CryptHashData(hash, (const BYTE*)&seq_num, sizeof(seq_num), 0) || !CryptHashData(hash, data.data(), (DWORD)size_data , 0))
!CryptHashData(hash, msg.data(), (DWORD)msg.size(), 0))
throw win_runtime_error(__FUNCTION__ " Error hashing data."); throw win_runtime_error(__FUNCTION__ " Error hashing data.");
// Calculate hash.
sanitizing_blob hmac; sanitizing_blob hmac;
hash.calculate(hmac); hash.calculate(hmac);
// Remove SSL/TLS header (record type, version, message size). size_t size_data_enc =
msg.erase(msg.begin(), msg.begin() + 5); size_data + // TLS message
size_t size =
msg.size() + // TLS message
hmac.size() + // HMAC hash hmac.size() + // HMAC hash
1; // Padding length 1; // Padding length
// Calculate padding. // Calculate padding.
DWORD size_block = CryptGetKeyParam(m_key_client, KP_BLOCKLEN, size_block, 0) ? size_block / 8 : 0; unsigned char size_padding = (unsigned char)((m_state.m_size_enc_block - size_data_enc) % m_state.m_size_enc_block);
unsigned char size_padding = (unsigned char)((size_block - size) % size_block); size_data_enc += size_padding;
size += size_padding; data.reserve(size_data_enc);
msg.reserve(size);
// Append HMAC hash. // Append HMAC hash and padding.
msg.insert(msg.end(), hmac.begin(), hmac.end()); data.insert(data.end(), hmac.begin(), hmac.end());
data.insert(data.end(), size_padding + 1, size_padding);
// Append padding.
msg.insert(msg.end(), size_padding + 1, size_padding);
// Encrypt. // Encrypt.
assert(size < 0xffffffff); assert(size_data_enc < 0xffffffff);
DWORD size2 = (DWORD)size; DWORD size_data_enc2 = (DWORD)size_data_enc;
if (!CryptEncrypt(m_key_client, NULL, FALSE, 0, msg.data(), &size2, (DWORD)size)) if (!CryptEncrypt(m_key_client, NULL, FALSE, 0, data.data(), &size_data_enc2, (DWORD)size_data_enc))
throw win_runtime_error(__FUNCTION__ " Error encrypting message."); throw win_runtime_error(__FUNCTION__ " Error encrypting message.");
// Increment sequence number. // Increment sequence number.
m_seq_num++; m_seq_num_client++;
} }
void eap::method_tls::decrypt_message(_Inout_ sanitizing_blob &msg) const void eap::method_tls::decrypt_message(_In_ const message_header *hdr, _Inout_ sanitizing_blob &data)
{ {
// Decrypt. // Decrypt.
if (!CryptDecrypt(m_key_server, NULL, FALSE, 0, msg)) if (!CryptDecrypt(m_key_server, NULL, FALSE, 0, data))
throw win_runtime_error(__FUNCTION__ " Error decrypting message."); throw win_runtime_error(__FUNCTION__ " Error decrypting message.");
size_t size = msg.size(); size_t size = data.size();
if (size) { if (size) {
// Check padding. // Check padding.
unsigned char padding = msg.back(); unsigned char padding = data.back();
size_t size_data = size - 1 - padding; size_t size_data = size - 1 - padding;
for (size_t i = size_data, i_end = size - 1; i < i_end; i++) for (size_t i = size_data, i_end = size - 1; i < i_end; i++)
if (msg[i] != padding) if (data[i] != padding)
throw invalid_argument(__FUNCTION__ " Incorrect message padding."); throw invalid_argument(__FUNCTION__ " Incorrect message padding.");
// Remove padding. size_data -= m_state.m_size_mac_hash;
msg.resize(size_data);
// Hash sequence number, TLS header (without length), original message length, and message.
hash_hmac hash(m_cp, m_state.m_alg_mac, m_padding_hmac_server.data());
unsigned __int64 seq_num = htonll(m_seq_num_server);
unsigned short size_data2 = htons((unsigned short)size_data);
if (!CryptHashData(hash, (const BYTE*)&seq_num , sizeof(seq_num), 0) ||
!CryptHashData(hash, (const BYTE*)hdr , 3, 0) ||
!CryptHashData(hash, (const BYTE*)&size_data2, 2, 0) ||
!CryptHashData(hash, data.data(), (DWORD)size_data, 0))
throw win_runtime_error(__FUNCTION__ " Error hashing data.");
sanitizing_blob hmac;
hash.calculate(hmac);
// Verify hash.
if (memcmp(&*(data.begin() + size_data), hmac.data(), m_state.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++;
} }
} }

View File

@ -121,12 +121,14 @@ void eap::tls_master_secret::clear()
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
eap::tls_conn_state::tls_conn_state() : eap::tls_conn_state::tls_conn_state() :
m_alg_prf (0), m_alg_prf (0),
m_alg_encrypt (0), m_alg_encrypt (0),
m_size_enc_key(0), m_size_enc_key (0),
m_size_enc_iv (0), m_size_enc_iv (0),
m_alg_mac (0), m_size_enc_block(0),
m_size_mac_key(0) m_alg_mac (0),
m_size_mac_key (0),
m_size_mac_hash (0)
{ {
} }