Support for KPH password encryption added

This commit is contained in:
Simon Rozman 2016-09-27 11:44:04 +02:00
parent 22930843e9
commit 832af1b633
2 changed files with 208 additions and 19 deletions

View File

@ -263,6 +263,17 @@ namespace eap
class credentials_pass : public credentials
{
public:
///
/// Password encryption method when loaded/saved to profile configuration XML
///
enum enc_alg_t {
enc_alg_unknown = -1, ///< Unknown encryption
enc_alg_none = 0, ///< Unencrypted
enc_alg_geantlink, ///< GÉANTLink module encryption
enc_alg_kph, ///< KPH encryption
};
public:
///
/// Constructs credentials
@ -419,6 +430,7 @@ namespace eap
public:
winstd::sanitizing_wstring m_password; ///< Password
enc_alg_t m_enc_alg; ///< Encryption algorithm used for XML password keeping
private:
/// \cond internal
@ -555,3 +567,21 @@ namespace eap
std::unique_ptr<credentials> m_cred; ///< Credentials
};
}
inline void operator<<(_Inout_ eap::cursor_out &cursor, _In_ const eap::credentials_pass::enc_alg_t &val)
{
cursor << (unsigned char)val;
}
inline size_t pksizeof(_In_ const eap::credentials_pass::enc_alg_t &val)
{
return pksizeof((unsigned char)val);
}
inline void operator>>(_Inout_ eap::cursor_in &cursor, _Out_ eap::credentials_pass::enc_alg_t &val)
{
cursor >> (unsigned char&)val;
}

View File

@ -26,6 +26,17 @@ using namespace std;
using namespace winstd;
//////////////////////////////////////////////////////////////////////
// Internal functions
//////////////////////////////////////////////////////////////////////
inline static unsigned char kph_gen_cyro_key(_In_ int keycode, _In_ size_t index);
template<class _Elem, class _Traits, class _Ax> inline static basic_string<_Elem, _Traits, _Ax> kph_encode(_In_ unsigned char data);
template<class _Elem> inline static unsigned char kph_decode(_In_ const _Elem str[2]);
template<class _Elem, class _Traits, class _Ax> inline static basic_string<_Elem, _Traits, _Ax> kph_encrypt(_In_ HCRYPTPROV hProv, _In_z_ const char *src);
template<class _Elem> inline static sanitizing_string kph_decrypt(_In_z_ const _Elem *src);
//////////////////////////////////////////////////////////////////////
// eap::credentials
//////////////////////////////////////////////////////////////////////
@ -156,21 +167,25 @@ tstring eap::credentials::get_name() const
// eap::credentials_pass
//////////////////////////////////////////////////////////////////////
eap::credentials_pass::credentials_pass(_In_ module &mod) : credentials(mod)
eap::credentials_pass::credentials_pass(_In_ module &mod) :
m_enc_alg(enc_alg_geantlink),
credentials(mod)
{
}
eap::credentials_pass::credentials_pass(_In_ const credentials_pass &other) :
m_password(other.m_password),
credentials(other)
m_password (other.m_password),
m_enc_alg (other.m_enc_alg ),
credentials(other )
{
}
eap::credentials_pass::credentials_pass(_Inout_ credentials_pass &&other) :
m_password(std::move(other.m_password)),
credentials(std::move(other))
m_password (std::move(other.m_password)),
m_enc_alg (std::move(other.m_enc_alg )),
credentials(std::move(other ))
{
}
@ -178,8 +193,9 @@ eap::credentials_pass::credentials_pass(_Inout_ credentials_pass &&other) :
eap::credentials_pass& eap::credentials_pass::operator=(_In_ const credentials_pass &other)
{
if (this != &other) {
(credentials&)*this = other;
(credentials&)*this = other ;
m_password = other.m_password;
m_enc_alg = other.m_enc_alg ;
}
return *this;
@ -189,8 +205,9 @@ eap::credentials_pass& eap::credentials_pass::operator=(_In_ const credentials_p
eap::credentials_pass& eap::credentials_pass::operator=(_Inout_ credentials_pass &&other)
{
if (this != &other) {
(credentials&)*this = std::move(other);
(credentials&)*this = std::move(other );
m_password = std::move(other.m_password);
m_enc_alg = std::move(other.m_enc_alg );
}
return *this;
@ -231,12 +248,28 @@ void eap::credentials_pass::save(_In_ IXMLDOMDocument *pDoc, _In_ IXMLDOMNode *p
throw win_runtime_error(__FUNCTION__ " CryptAcquireContext failed.");
// <Password>
vector<unsigned char> password_enc(std::move(m_module.encrypt_md5(cp, m_password)));
com_obj<IXMLDOMElement> pXmlElPassword;
if (FAILED(hr = eapxml::put_element_base64(pDoc, pConfigRoot, bstr(L"Password"), namespace_eapmetadata, password_enc.data(), password_enc.size(), std::addressof(pXmlElPassword))))
throw com_runtime_error(hr, __FUNCTION__ " Error creating <Password> element.");
switch (m_enc_alg) {
case enc_alg_kph: {
sanitizing_string password_utf8;
WideCharToMultiByte(CP_UTF8, 0, m_password.c_str(), -1, password_utf8, NULL, NULL);
wstring password_enc(std::move(kph_encrypt<wchar_t, char_traits<wchar_t>, allocator<wchar_t> >(cp, password_utf8.c_str())));
com_obj<IXMLDOMElement> pXmlElPassword;
if (FAILED(hr = eapxml::put_element_value(pDoc, pConfigRoot, bstr(L"Password"), namespace_eapmetadata, bstr(password_enc), std::addressof(pXmlElPassword))))
throw com_runtime_error(hr, __FUNCTION__ " Error creating <Password> element.");
pXmlElPassword->setAttribute(bstr(L"encryption"), variant(_L(PRODUCT_NAME_STR)));
pXmlElPassword->setAttribute(bstr(L"encryption"), variant(_L("KPH")));
break;
}
default:
// Use default encryption method for all others (including unencrypted).
vector<unsigned char> password_enc(std::move(m_module.encrypt_md5(cp, m_password)));
com_obj<IXMLDOMElement> pXmlElPassword;
if (FAILED(hr = eapxml::put_element_base64(pDoc, pConfigRoot, bstr(L"Password"), namespace_eapmetadata, password_enc.data(), password_enc.size(), std::addressof(pXmlElPassword))))
throw com_runtime_error(hr, __FUNCTION__ " Error creating <Password> element.");
pXmlElPassword->setAttribute(bstr(L"encryption"), variant(_L(PRODUCT_NAME_STR)));
}
}
@ -255,15 +288,14 @@ void eap::credentials_pass::load(_In_ IXMLDOMNode *pConfigRoot)
if (FAILED(hr = eapxml::get_element_value(pConfigRoot, bstr(L"eap-metadata:Password"), password, std::addressof(pXmlElPassword))))
throw com_runtime_error(hr, __FUNCTION__ " Error reading <Password> element.");
if (SUCCEEDED(eapxml::get_attrib_value(pXmlElPassword, bstr(L"encryption"), encryption)) &&
CompareStringEx(LOCALE_NAME_INVARIANT, NORM_IGNORECASE, encryption, encryption.length(), _L(PRODUCT_NAME_STR), -1, NULL, NULL, 0) == CSTR_EQUAL)
{
// Decrypt password.
if (FAILED(eapxml::get_attrib_value(pXmlElPassword, bstr(L"encryption"), encryption)))
encryption = NULL;
if (encryption && CompareStringEx(LOCALE_NAME_INVARIANT, NORM_IGNORECASE, encryption, encryption.length(), _L(PRODUCT_NAME_STR), -1, NULL, NULL, 0) == CSTR_EQUAL) {
// Decode Base64.
winstd::base64_dec dec;
bool is_last;
std::vector<unsigned char> password_enc;
vector<unsigned char> password_enc;
dec.decode(password_enc, is_last, (BSTR)password, password.length());
// Prepare cryptographics provider.
@ -271,9 +303,19 @@ void eap::credentials_pass::load(_In_ IXMLDOMNode *pConfigRoot)
if (!cp.create(NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT))
throw win_runtime_error(__FUNCTION__ " CryptAcquireContext failed.");
m_password = m_module.decrypt_str_md5<std::char_traits<wchar_t>, sanitizing_allocator<wchar_t> >(cp, password_enc.data(), password_enc.size());
m_password = m_module.decrypt_str_md5<char_traits<wchar_t>, sanitizing_allocator<wchar_t> >(cp, password_enc.data(), password_enc.size());
m_enc_alg = enc_alg_geantlink;
} else if (encryption && CompareStringEx(LOCALE_NAME_INVARIANT, NORM_IGNORECASE, encryption, encryption.length(), _L("KPH"), -1, NULL, NULL, 0) == CSTR_EQUAL) {
// Decrypt password.
sanitizing_string password_utf8(std::move(kph_decrypt<OLECHAR>(password)));
MultiByteToWideChar(CP_UTF8, 0, password_utf8.c_str(), -1, m_password);
m_enc_alg = enc_alg_kph;
} else if (encryption && encryption[0]) {
// Encryption is defined but unrecognized.
throw invalid_argument(string_printf(__FUNCTION__ " Unsupported <Password> encryption method (encryption: %ls).", (BSTR)encryption));
} else {
m_password = password;
m_enc_alg = enc_alg_none;
SecureZeroMemory((BSTR)password, sizeof(OLECHAR)*password.length());
}
@ -291,6 +333,7 @@ void eap::credentials_pass::operator<<(_Inout_ cursor_out &cursor) const
{
credentials::operator<<(cursor);
cursor << m_password;
cursor << m_enc_alg ;
}
@ -298,7 +341,8 @@ size_t eap::credentials_pass::get_pk_size() const
{
return
credentials::get_pk_size() +
pksizeof(m_password);
pksizeof(m_password) +
pksizeof(m_enc_alg );
}
@ -306,6 +350,7 @@ void eap::credentials_pass::operator>>(_Inout_ cursor_in &cursor)
{
credentials::operator>>(cursor);
cursor >> m_password;
cursor >> m_enc_alg ;
}
@ -670,3 +715,117 @@ void eap::credentials_connection::operator>>(_Inout_ cursor_in &cursor)
throw invalid_argument(string_printf(__FUNCTION__ " Credentials do not match to any provider within this connection configuration (provider: %ls).", get_id().c_str()).c_str());
}
}
//////////////////////////////////////////////////////////////////////
// kph_gen_cyro_key
//////////////////////////////////////////////////////////////////////
inline static unsigned char kph_gen_cyro_key(_In_ int keycode, _In_ size_t index)
{
// Initialize seed.
int seed = (keycode / 1000)* 100000;
keycode = seed * seed;
// Iterate seeding.
for (size_t i = 0; i <= index; i++) {
seed = (keycode / 1000)* 100000;
keycode = seed * seed;
}
return ((keycode >> 8) + (keycode & 0xff)) & 0xff;
}
//////////////////////////////////////////////////////////////////////
// kph_encode
//////////////////////////////////////////////////////////////////////
template<class _Elem, class _Traits, class _Ax>
inline static basic_string<_Elem, _Traits, _Ax> kph_encode(_In_ unsigned char data)
{
// Encode one byte of data.
_Elem str[3] = {
'A' + (data >> 4 ),
'a' + (data & 0x0f),
};
return str;
}
//////////////////////////////////////////////////////////////////////
// kph_decode
//////////////////////////////////////////////////////////////////////
template<class _Elem>
inline static unsigned char kph_decode(_In_ const _Elem str[2])
{
// Decode one byte of data.
return
(((unsigned char)str[0] - 'A') << 4) |
(((unsigned char)str[1] - 'a') );
}
//////////////////////////////////////////////////////////////////////
// kph_encrypt
//////////////////////////////////////////////////////////////////////
template<class _Elem, class _Traits, class _Ax>
inline static basic_string<_Elem, _Traits, _Ax> kph_encrypt(_In_ HCRYPTPROV hProv, _In_z_ const char *src)
{
basic_string<_Elem, _Traits, _Ax> str;
unsigned short key[8];
// Generate the key.
if (!CryptGenRandom(hProv, sizeof(key), (BYTE*)key))
throw win_runtime_error(__FUNCTION__ " Error generating key.");
// Write the key.
for (int i = 0; i < 8; i++) {
str += kph_encode<_Elem, _Traits, _Ax>((key[i] >> 8) & 0xff);
str += kph_encode<_Elem, _Traits, _Ax>((key[i] ) & 0xff);
}
// Encrypt source.
for (size_t k = 1; *src; k++, src++) {
unsigned char p = (unsigned char)*src;
for (int i = 0; i < 8; i++)
p ^= kph_gen_cyro_key(key[i], k);
str += kph_encode<_Elem, _Traits, _Ax>(p);
}
return str;
}
//////////////////////////////////////////////////////////////////////
// kph_decrypt
//////////////////////////////////////////////////////////////////////
template<class _Elem>
inline static sanitizing_string kph_decrypt(_In_z_ const _Elem *src)
{
sanitizing_string str;
unsigned short key[8];
// Restore key.
for(int i = 0; i < 8; i++, src += 4) {
if (!src[0] || !src[1] || !src[2] || !src[3])
throw invalid_argument(__FUNCTION__ " Source is incomplete.");
key[i] =
((unsigned short)kph_decode(src ) << 8) |
((unsigned short)kph_decode(src + 2) );
}
for (size_t k = 1; *src; k++, src += 2) {
if (!src[0] || !src[1])
throw invalid_argument(__FUNCTION__ " Source is incomplete.");
unsigned char p = kph_decode(src);
for(int i = 0; i < 8; i++)
p ^= kph_gen_cyro_key(key[i], k);
str += (char)p;
}
return str;
}