From 832af1b63369115a371a17570f34d59dd0649524 Mon Sep 17 00:00:00 2001 From: Simon Rozman Date: Tue, 27 Sep 2016 11:44:04 +0200 Subject: [PATCH] Support for KPH password encryption added --- lib/EAPBase/include/Credentials.h | 30 +++++ lib/EAPBase/src/Credentials.cpp | 197 +++++++++++++++++++++++++++--- 2 files changed, 208 insertions(+), 19 deletions(-) diff --git a/lib/EAPBase/include/Credentials.h b/lib/EAPBase/include/Credentials.h index f4d0073..c7b59a7 100644 --- a/lib/EAPBase/include/Credentials.h +++ b/lib/EAPBase/include/Credentials.h @@ -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 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; +} diff --git a/lib/EAPBase/src/Credentials.cpp b/lib/EAPBase/src/Credentials.cpp index 899060d..3c02e29 100644 --- a/lib/EAPBase/src/Credentials.cpp +++ b/lib/EAPBase/src/Credentials.cpp @@ -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 inline static basic_string<_Elem, _Traits, _Ax> kph_encode(_In_ unsigned char data); +template inline static unsigned char kph_decode(_In_ const _Elem str[2]); +template inline static basic_string<_Elem, _Traits, _Ax> kph_encrypt(_In_ HCRYPTPROV hProv, _In_z_ const char *src); +template 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."); // - vector password_enc(std::move(m_module.encrypt_md5(cp, m_password))); - com_obj 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 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, allocator >(cp, password_utf8.c_str()))); + com_obj 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 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 password_enc(std::move(m_module.encrypt_md5(cp, m_password))); + com_obj 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 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 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 password_enc; + vector 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, sanitizing_allocator >(cp, password_enc.data(), password_enc.size()); + m_password = m_module.decrypt_str_md5, sanitizing_allocator >(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(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 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 +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 +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 +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 +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; +}