433 lines
16 KiB
C++
433 lines
16 KiB
C++
/*
|
|
Copyright 2015-2016 Amebis
|
|
Copyright 2016 GÉANT
|
|
|
|
This file is part of GÉANTLink.
|
|
|
|
GÉANTLink is free software: you can redistribute it and/or modify it
|
|
under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
GÉANTLink is distributed in the hope that it will be useful, but
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with GÉANTLink. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "StdAfx.h"
|
|
|
|
using namespace std;
|
|
using namespace winstd;
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// eap::tls_version
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
const eap::tls_version eap::tls_version_1_0 = { 3, 1 };
|
|
const eap::tls_version eap::tls_version_1_1 = { 3, 2 };
|
|
const eap::tls_version eap::tls_version_1_2 = { 3, 3 };
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// eap::tls_random
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
void eap::tls_random::randomize(_In_ HCRYPTPROV cp)
|
|
{
|
|
_time32((__time32_t*)data);
|
|
if (!CryptGenRandom(cp, sizeof(data) - sizeof(__time32_t), data + sizeof(__time32_t)))
|
|
throw win_runtime_error(__FUNCTION__ " Error creating randomness.");
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// eap::tls_master_secret
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
eap::tls_master_secret::tls_master_secret()
|
|
{
|
|
}
|
|
|
|
|
|
eap::tls_master_secret::tls_master_secret(_In_ HCRYPTPROV cp, _In_ tls_version ver)
|
|
{
|
|
data[0] = ver.major;
|
|
data[1] = ver.minor;
|
|
|
|
if (!CryptGenRandom(cp, sizeof(data) - 2, data + 2))
|
|
throw win_runtime_error(__FUNCTION__ " Error creating PMS randomness.");
|
|
}
|
|
|
|
|
|
eap::tls_master_secret::tls_master_secret(_In_ const sanitizing_blob_f<48> &other) :
|
|
sanitizing_blob_xf<48>(other)
|
|
{
|
|
}
|
|
|
|
|
|
#ifdef _DEBUG
|
|
|
|
eap::tls_master_secret::tls_master_secret(_Inout_ sanitizing_blob_zf<48> &&other) :
|
|
sanitizing_blob_xf<48>(std::move(other))
|
|
{
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// eap::hmac_padding
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
eap::hmac_padding::hmac_padding()
|
|
{
|
|
}
|
|
|
|
|
|
eap::hmac_padding::hmac_padding(
|
|
_In_ HCRYPTPROV cp,
|
|
_In_ ALG_ID alg,
|
|
_In_bytecount_(size_secret ) const void *secret,
|
|
_In_ size_t size_secret,
|
|
_In_opt_ unsigned char pad)
|
|
{
|
|
if (size_secret > sizeof(hmac_padding)) {
|
|
// If the secret is longer than padding, use secret's hash instead.
|
|
crypt_hash hash;
|
|
if (!hash.create(cp, alg))
|
|
throw win_runtime_error(__FUNCTION__ " Error creating hash.");
|
|
if (!CryptHashData(hash, (const BYTE*)secret, (DWORD)size_secret, 0))
|
|
throw win_runtime_error(__FUNCTION__ " Error hashing.");
|
|
DWORD size_hash = sizeof(hmac_padding);
|
|
if (!CryptGetHashParam(hash, HP_HASHVAL, data, &size_hash, 0))
|
|
throw win_runtime_error(__FUNCTION__ " Error finishing hash.");
|
|
size_secret = size_hash;
|
|
} else
|
|
memcpy(data, secret, size_secret);
|
|
for (size_t i = 0; i < size_secret; i++)
|
|
data[i] ^= pad;
|
|
memset(data + size_secret, pad, sizeof(hmac_padding) - size_secret);
|
|
}
|
|
|
|
|
|
eap::hmac_padding::hmac_padding(_In_ const sanitizing_blob_f<64> &other) :
|
|
sanitizing_blob_xf<64>(other)
|
|
{
|
|
}
|
|
|
|
|
|
#ifdef _DEBUG
|
|
|
|
eap::hmac_padding::hmac_padding(_Inout_ sanitizing_blob_zf<64> &&other) :
|
|
sanitizing_blob_xf<64>(std::move(other))
|
|
{
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// eap::hmac_hash
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
eap::hmac_hash::hmac_hash(
|
|
_In_ HCRYPTPROV cp,
|
|
_In_ ALG_ID alg,
|
|
_In_bytecount_(size_secret ) const void *secret,
|
|
_In_ size_t size_secret)
|
|
{
|
|
// Prepare inner padding and forward to the other constructor.
|
|
this->hmac_hash::hmac_hash(cp, alg, hmac_padding(cp, alg, secret, size_secret));
|
|
}
|
|
|
|
|
|
eap::hmac_hash::hmac_hash(
|
|
_In_ HCRYPTPROV cp,
|
|
_In_ ALG_ID alg,
|
|
_In_ const hmac_padding &padding)
|
|
{
|
|
// Create inner hash.
|
|
if (!m_hash_inner.create(cp, alg))
|
|
throw win_runtime_error(__FUNCTION__ " Error creating inner hash.");
|
|
|
|
// Initialize it with the inner padding.
|
|
if (!CryptHashData(m_hash_inner, padding.data, sizeof(hmac_padding), 0))
|
|
throw win_runtime_error(__FUNCTION__ " Error hashing secret XOR inner padding.");
|
|
|
|
// Convert inner padding to outer padding for final calculation.
|
|
hmac_padding padding_out;
|
|
for (size_t i = 0; i < sizeof(hmac_padding); i++)
|
|
padding_out.data[i] = padding.data[i] ^ (0x36 ^ 0x5c);
|
|
|
|
// Create outer hash.
|
|
if (!m_hash_outer.create(cp, alg))
|
|
throw win_runtime_error(__FUNCTION__ " Error creating outer hash.");
|
|
|
|
// Initialize it with the outer padding.
|
|
if (!CryptHashData(m_hash_outer, padding_out.data, sizeof(hmac_padding), 0))
|
|
throw win_runtime_error(__FUNCTION__ " Error hashing secret XOR inner padding.");
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// eap::tls_conn_state
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
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),
|
|
m_size_enc_block(0),
|
|
m_alg_mac (0),
|
|
m_size_mac_key (0),
|
|
m_size_mac_hash (0)
|
|
#endif
|
|
{
|
|
}
|
|
|
|
|
|
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 ),
|
|
m_size_enc_block(other.m_size_enc_block),
|
|
m_key (other.m_key ),
|
|
m_alg_mac (other.m_alg_mac ),
|
|
m_size_mac_key (other.m_size_mac_key ),
|
|
m_size_mac_hash (other.m_size_mac_hash ),
|
|
m_padding_hmac (other.m_padding_hmac )
|
|
{
|
|
}
|
|
|
|
|
|
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 )),
|
|
m_size_enc_block(std::move(other.m_size_enc_block)),
|
|
m_key (std::move(other.m_key )),
|
|
m_alg_mac (std::move(other.m_alg_mac )),
|
|
m_size_mac_key (std::move(other.m_size_mac_key )),
|
|
m_size_mac_hash (std::move(other.m_size_mac_hash )),
|
|
m_padding_hmac (std::move(other.m_padding_hmac ))
|
|
{
|
|
#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;
|
|
other.m_size_enc_block = 0;
|
|
other.m_alg_mac = 0;
|
|
other.m_size_mac_key = 0;
|
|
other.m_size_mac_hash = 0;
|
|
#endif
|
|
}
|
|
|
|
|
|
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 ;
|
|
m_size_enc_block = other.m_size_enc_block;
|
|
m_key = other.m_key ;
|
|
m_alg_mac = other.m_alg_mac ;
|
|
m_size_mac_key = other.m_size_mac_key ;
|
|
m_size_mac_hash = other.m_size_mac_hash ;
|
|
m_padding_hmac = other.m_padding_hmac ;
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
|
|
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 );
|
|
m_size_enc_block = std::move(other.m_size_enc_block);
|
|
m_key = std::move(other.m_key );
|
|
m_alg_mac = std::move(other.m_alg_mac );
|
|
m_size_mac_key = std::move(other.m_size_mac_key );
|
|
m_size_mac_hash = std::move(other.m_size_mac_hash );
|
|
m_padding_hmac = std::move(other.m_padding_hmac );
|
|
|
|
#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;
|
|
other.m_size_enc_block = 0;
|
|
other.m_alg_mac = 0;
|
|
other.m_size_mac_key = 0;
|
|
other.m_size_mac_hash = 0;
|
|
#endif
|
|
}
|
|
|
|
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]));
|
|
}
|