GEANTLink/lib/MSCHAPv2/src/MSCHAPv2.cpp

337 lines
12 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::create_des_key
//////////////////////////////////////////////////////////////////////
crypt_key eap::create_des_key(_In_ HCRYPTPROV cp, _In_count_(size) const unsigned char *key, _In_ size_t size)
{
// Prepare exported key BLOB.
struct key_blob_prefix {
PUBLICKEYSTRUC header;
DWORD size;
} static const s_prefix = {
{
PLAINTEXTKEYBLOB,
CUR_BLOB_VERSION,
0,
CALG_DES,
},
8,
};
sanitizing_blob key_blob;
key_blob.reserve(sizeof(key_blob_prefix) + 8);
key_blob.assign((const unsigned char*)&s_prefix, (const unsigned char*)(&s_prefix + 1));
// Inject parity bits.
unsigned char out = 0, parity = 1;
size_t i = 0, j = 7, bits = std::min<size_t>(size * 8, 56);
for (; i < bits; i++) {
unsigned char bit = (key[i/8] & (1 << (7 - i%8))) ? 1 : 0;
parity ^= bit;
out |= bit << j;
if (--j == 0) {
out |= parity;
key_blob.push_back(out);
out = 0; parity = 1; j = 7;
}
}
for (; i < 56; i++) {
if (--j == 0) {
out |= parity;
key_blob.push_back(out);
out = 0; parity = 1; j = 7;
}
}
// Import key.
crypt_key k;
if (!k.import(cp, key_blob.data(), (DWORD)key_blob.size(), NULL, 0))
throw winstd::win_runtime_error(__FUNCTION__ " Error importing key 1/3.");
return k;
}
//////////////////////////////////////////////////////////////////////
// eap::challenge_mschapv2
//////////////////////////////////////////////////////////////////////
void eap::challenge_mschapv2::randomize(_In_ HCRYPTPROV cp)
{
if (!CryptGenRandom(cp, sizeof(data), data))
throw win_runtime_error(__FUNCTION__ " Error creating randomness.");
}
//////////////////////////////////////////////////////////////////////
// eap::challenge_hash
//////////////////////////////////////////////////////////////////////
eap::challenge_hash::challenge_hash()
{
}
eap::challenge_hash::challenge_hash(
_In_ HCRYPTPROV cp,
_In_ const challenge_mschapv2 &challenge_server,
_In_ const challenge_mschapv2 &challenge_client,
_In_z_ const char *username)
{
const char *domain = strchr(username, '@');
size_t len_username = domain ? domain - username : strlen(username);
crypt_hash hash;
if (!hash.create(cp, CALG_SHA))
throw win_runtime_error(__FUNCTION__ " Creating SHA hash failed.");
if (!CryptHashData(hash, (const BYTE*)&challenge_client, (DWORD)sizeof(challenge_client), 0) ||
!CryptHashData(hash, (const BYTE*)&challenge_server, (DWORD)sizeof(challenge_server), 0) ||
!CryptHashData(hash, (const BYTE*)username , (DWORD)len_username , 0))
throw win_runtime_error(__FUNCTION__ " Error hashing data.");
unsigned char hash_val[20];
DWORD size_hash_val = sizeof(hash_val);
if (!CryptGetHashParam(hash, HP_HASHVAL, hash_val, &size_hash_val, 0))
throw win_runtime_error(__FUNCTION__ " Error hashing data.");
memcpy(data, hash_val, sizeof(data));
SecureZeroMemory(hash_val, size_hash_val);
}
eap::challenge_hash::challenge_hash(_In_ const sanitizing_blob_f<8> &other) :
sanitizing_blob_xf<8>(other)
{
}
#ifdef _DEBUG
eap::challenge_hash::challenge_hash(_Inout_ sanitizing_blob_zf<8> &&other) :
sanitizing_blob_xf<8>(std::move(other))
{
}
#endif
//////////////////////////////////////////////////////////////////////
// eap::nt_password_hash
//////////////////////////////////////////////////////////////////////
eap::nt_password_hash::nt_password_hash()
{
}
eap::nt_password_hash::nt_password_hash(
_In_ HCRYPTPROV cp,
_In_z_ const wchar_t *password)
{
crypt_hash hash;
if (!hash.create(cp, CALG_MD4))
throw win_runtime_error(__FUNCTION__ " Creating MD4 hash failed.");
if (!CryptHashData(hash, (const BYTE*)password, (DWORD)(wcslen(password) * sizeof(wchar_t)), 0))
throw win_runtime_error(__FUNCTION__ " Error hashing data.");
DWORD size_data = sizeof(data);
if (!CryptGetHashParam(hash, HP_HASHVAL, data, &size_data, 0))
throw win_runtime_error(__FUNCTION__ " Error hashing data.");
}
eap::nt_password_hash::nt_password_hash(
_In_ HCRYPTPROV cp,
_In_ const nt_password_hash &pwd_hash)
{
crypt_hash hash;
if (!hash.create(cp, CALG_MD4))
throw win_runtime_error(__FUNCTION__ " Creating MD4 hash failed.");
if (!CryptHashData(hash, (const BYTE*)&pwd_hash, (DWORD)sizeof(pwd_hash), 0))
throw win_runtime_error(__FUNCTION__ " Error hashing data.");
DWORD size_data = sizeof(data);
if (!CryptGetHashParam(hash, HP_HASHVAL, data, &size_data, 0))
throw win_runtime_error(__FUNCTION__ " Error hashing data.");
}
eap::nt_password_hash::nt_password_hash(_In_ const sanitizing_blob_f<16> &other) :
sanitizing_blob_xf<16>(other)
{
}
#ifdef _DEBUG
eap::nt_password_hash::nt_password_hash(_Inout_ sanitizing_blob_zf<16> &&other) :
sanitizing_blob_xf<16>(std::move(other))
{
}
#endif
//////////////////////////////////////////////////////////////////////
// eap::nt_response
//////////////////////////////////////////////////////////////////////
eap::nt_response::nt_response()
{
}
eap::nt_response::nt_response(
_In_ HCRYPTPROV cp,
_In_ const challenge_mschapv2 &challenge_server,
_In_ const challenge_mschapv2 &challenge_client,
_In_z_ const char *username,
_In_z_ const wchar_t *password)
{
challenge_hash challenge(cp, challenge_server, challenge_client, username);
nt_password_hash hash_pwd(cp, password);
// Prepare exported key BLOB.
crypt_key key;
DWORD size_data_enc;
static const DWORD mode_ecb = CRYPT_MODE_ECB;
// DesEncrypt(Challenge, 1st 7-octets of ZPasswordHash, giving 1st 8-octets of Response)
key = create_des_key(cp, (const unsigned char*)&hash_pwd, 7);
if (!CryptSetKeyParam(key, KP_MODE, (const BYTE*)&mode_ecb, 0))
throw win_runtime_error(__FUNCTION__ " Error setting ECB mode.");
memcpy(data, &challenge, 8);
size_data_enc = 8;
if (!CryptEncrypt(key, NULL, FALSE, 0, data, &size_data_enc, 8))
throw win_runtime_error(__FUNCTION__ " Error encrypting message 1/3.");
// DesEncrypt(Challenge, 2nd 7-octets of ZPasswordHash, giving 2nd 8-octets of Response)
key = create_des_key(cp, (const unsigned char*)&hash_pwd + 7, 7);
if (!CryptSetKeyParam(key, KP_MODE, (const BYTE*)&mode_ecb, 0))
throw win_runtime_error(__FUNCTION__ " Error setting ECB mode.");
memcpy(data + 8, &challenge, 8);
size_data_enc = 8;
if (!CryptEncrypt(key, NULL, FALSE, 0, data + 8, &size_data_enc, 8))
throw win_runtime_error(__FUNCTION__ " Error encrypting message 2/3.");
// DesEncrypt(Challenge, 2nd 7-octets of ZPasswordHash, giving 2nd 8-octets of Response)
key = create_des_key(cp, (const unsigned char*)&hash_pwd + 14, 2);
if (!CryptSetKeyParam(key, KP_MODE, (const BYTE*)&mode_ecb, 0))
throw win_runtime_error(__FUNCTION__ " Error setting ECB mode.");
memcpy(data + 16, &challenge, 8);
size_data_enc = 8;
if (!CryptEncrypt(key, NULL, FALSE, 0, data + 16, &size_data_enc, 8))
throw win_runtime_error(__FUNCTION__ " Error encrypting message 3/3.");
}
eap::nt_response::nt_response(_In_ const sanitizing_blob_f<24> &other) :
sanitizing_blob_xf<24>(other)
{
}
#ifdef _DEBUG
eap::nt_response::nt_response(_Inout_ sanitizing_blob_zf<24> &&other) :
sanitizing_blob_xf<24>(std::move(other))
{
}
#endif
//////////////////////////////////////////////////////////////////////
// eap::authenticator_response
//////////////////////////////////////////////////////////////////////
eap::authenticator_response::authenticator_response()
{
}
eap::authenticator_response::authenticator_response(
_In_ HCRYPTPROV cp,
_In_ const challenge_mschapv2 &challenge_server,
_In_ const challenge_mschapv2 &challenge_client,
_In_z_ const char *username,
_In_z_ const wchar_t *password,
_In_ const nt_response &nt_resp)
{
static const unsigned char s_magic1[39] = {
0x4d, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
0x65, 0x72, 0x20, 0x74, 0x6f, 0x20, 0x63, 0x6c, 0x69, 0x65,
0x6e, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67,
0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74
};
nt_password_hash hash_hash_pwd(cp, nt_password_hash(cp, password));
crypt_hash hash;
if (!hash.create(cp, CALG_SHA))
throw win_runtime_error(__FUNCTION__ " Creating SHA hash failed.");
if (!CryptHashData(hash, (const BYTE*)&hash_hash_pwd, (DWORD)sizeof(hash_hash_pwd), 0) ||
!CryptHashData(hash, (const BYTE*)&nt_resp , (DWORD)sizeof(nt_resp ), 0) ||
!CryptHashData(hash, s_magic1 , (DWORD)sizeof(s_magic1 ), 0))
throw win_runtime_error(__FUNCTION__ " Error hashing data.");
unsigned char hash_val[20];
DWORD size_hash_val = sizeof(hash_val);
if (!CryptGetHashParam(hash, HP_HASHVAL, hash_val, &size_hash_val, 0))
throw win_runtime_error(__FUNCTION__ " Error hashing data.");
static const unsigned char s_magic2[41] = {
0x50, 0x61, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x6d, 0x61, 0x6b,
0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6f, 0x20, 0x6d, 0x6f,
0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6e, 0x20, 0x6f, 0x6e,
0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f,
0x6e
};
challenge_hash challenge(cp, challenge_server, challenge_client, username);
if (!hash.create(cp, CALG_SHA))
throw win_runtime_error(__FUNCTION__ " Creating SHA hash failed.");
if (!CryptHashData(hash, hash_val , size_hash_val , 0) ||
!CryptHashData(hash, (const BYTE*)&challenge, (DWORD)sizeof(challenge), 0) ||
!CryptHashData(hash, s_magic2 , (DWORD)sizeof(s_magic2 ), 0))
throw win_runtime_error(__FUNCTION__ " Error hashing data.");
size_hash_val = sizeof(data);
if (!CryptGetHashParam(hash, HP_HASHVAL, data, &size_hash_val, 0))
throw win_runtime_error(__FUNCTION__ " Error hashing data.");
}
eap::authenticator_response::authenticator_response(_In_ const sanitizing_blob_f<20> &other) :
sanitizing_blob_xf<20>(other)
{
}
#ifdef _DEBUG
eap::authenticator_response::authenticator_response(_Inout_ sanitizing_blob_zf<20> &&other) :
sanitizing_blob_xf<20>(std::move(other))
{
}
#endif