diff options
| author | Treeston <treeston.mmoc@gmail.com> | 2019-08-10 21:34:51 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2019-08-10 21:34:51 +0200 |
| commit | 4211645834c467a03c60248e80818d3607be9ea7 (patch) | |
| tree | 673a1695581503b6ea3e49da5c3e0d06bf5d892e /src/common | |
| parent | 3d356b97d4cc4c7ec4c641487241eae6dcc0558e (diff) | |
[3.3.5] Core/Authserver: TOTP rewrite: (PR #23633)
- Proper management commands (.account 2fa)
- Secrets can now be encrypted (set TOTPTokenSecret in .conf)
- Secret now stored in binary
- Argon2 and AES primitives
- Base32/64 support
Diffstat (limited to 'src/common')
| -rw-r--r-- | src/common/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | src/common/Cryptography/AES.cpp | 65 | ||||
| -rw-r--r-- | src/common/Cryptography/AES.h | 54 | ||||
| -rw-r--r-- | src/common/Cryptography/Argon2.cpp | 43 | ||||
| -rw-r--r-- | src/common/Cryptography/Argon2.h | 44 | ||||
| -rw-r--r-- | src/common/Cryptography/BigNumber.cpp | 67 | ||||
| -rw-r--r-- | src/common/Cryptography/BigNumber.h | 54 | ||||
| -rw-r--r-- | src/common/Cryptography/CryptoGenerics.h | 115 | ||||
| -rw-r--r-- | src/common/Cryptography/TOTP.cpp | 52 | ||||
| -rw-r--r-- | src/common/Cryptography/TOTP.h | 40 | ||||
| -rw-r--r-- | src/common/Encoding/Base32.cpp | 54 | ||||
| -rw-r--r-- | src/common/Encoding/Base32.h | 38 | ||||
| -rw-r--r-- | src/common/Encoding/Base64.cpp | 57 | ||||
| -rw-r--r-- | src/common/Encoding/Base64.h | 38 | ||||
| -rw-r--r-- | src/common/Encoding/BaseEncoding.h | 160 | ||||
| -rw-r--r-- | src/common/Utilities/advstd.h | 40 |
16 files changed, 882 insertions, 40 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 2ba4db69cd9..44875737957 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -63,6 +63,7 @@ target_link_libraries(common process trinity-core-interface PUBLIC + argon2 boost fmt g3dlib diff --git a/src/common/Cryptography/AES.cpp b/src/common/Cryptography/AES.cpp new file mode 100644 index 00000000000..31e02485aff --- /dev/null +++ b/src/common/Cryptography/AES.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2008-2019 TrinityCore <https://www.trinitycore.org/> + * + * This program 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 2 of the License, or (at your + * option) any later version. + * + * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "AES.h" +#include "Errors.h" +#include <limits> + +Trinity::Crypto::AES::AES(bool encrypting) : _ctx(EVP_CIPHER_CTX_new()), _encrypting(encrypting) +{ + EVP_CIPHER_CTX_init(_ctx); + int status = EVP_CipherInit_ex(_ctx, EVP_aes_128_gcm(), nullptr, nullptr, nullptr, _encrypting ? 1 : 0); + ASSERT(status); +} + +Trinity::Crypto::AES::~AES() +{ + EVP_CIPHER_CTX_free(_ctx); +} + +void Trinity::Crypto::AES::Init(Key const& key) +{ + int status = EVP_CipherInit_ex(_ctx, nullptr, nullptr, key.data(), nullptr, -1); + ASSERT(status); +} + +bool Trinity::Crypto::AES::Process(IV const& iv, uint8* data, std::size_t length, Tag& tag) +{ + ASSERT(length <= std::numeric_limits<int>::max()); + int len = static_cast<int>(length); + if (!EVP_CipherInit_ex(_ctx, nullptr, nullptr, nullptr, iv.data(), -1)) + return false; + + int outLen; + if (!EVP_CipherUpdate(_ctx, data, &outLen, data, len)) + return false; + + len -= outLen; + + if (!_encrypting && !EVP_CIPHER_CTX_ctrl(_ctx, EVP_CTRL_GCM_SET_TAG, sizeof(tag), tag)) + return false; + + if (!EVP_CipherFinal_ex(_ctx, data + outLen, &outLen)) + return false; + + ASSERT(len == outLen); + + if (_encrypting && !EVP_CIPHER_CTX_ctrl(_ctx, EVP_CTRL_GCM_GET_TAG, sizeof(tag), tag)) + return false; + + return true; +} diff --git a/src/common/Cryptography/AES.h b/src/common/Cryptography/AES.h new file mode 100644 index 00000000000..9306fc31980 --- /dev/null +++ b/src/common/Cryptography/AES.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2008-2019 TrinityCore <https://www.trinitycore.org/> + * + * This program 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 2 of the License, or (at your + * option) any later version. + * + * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef Trinity_AES_h__ +#define Trinity_AES_h__ + +#include "Define.h" +#include <array> +#include <openssl/evp.h> + +namespace Trinity +{ +namespace Crypto +{ +class TC_COMMON_API AES +{ +public: + static constexpr std::size_t IV_SIZE_BYTES = 12; + static constexpr std::size_t KEY_SIZE_BYTES = 16; + static constexpr std::size_t TAG_SIZE_BYTES = 12; + + using IV = std::array<uint8, IV_SIZE_BYTES>; + using Key = std::array<uint8, KEY_SIZE_BYTES>; + using Tag = uint8[TAG_SIZE_BYTES]; + + AES(bool encrypting); + ~AES(); + + void Init(Key const& key); + + bool Process(IV const& iv, uint8* data, std::size_t length, Tag& tag); + +private: + EVP_CIPHER_CTX* _ctx; + bool _encrypting; +}; +} +} + +#endif // Trinity_AES_h__ diff --git a/src/common/Cryptography/Argon2.cpp b/src/common/Cryptography/Argon2.cpp new file mode 100644 index 00000000000..fb849261e57 --- /dev/null +++ b/src/common/Cryptography/Argon2.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2008-2019 TrinityCore <https://www.trinitycore.org/> + * + * This program 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 2 of the License, or (at your + * option) any later version. + * + * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "Argon2.h" +#include <argon2/argon2.h> + +/*static*/ Optional<std::string> Trinity::Crypto::Argon2::Hash(std::string const& password, BigNumber const& salt, uint32 nIterations, uint32 kibMemoryCost) +{ + char buf[ENCODED_HASH_LEN]; + int status = argon2id_hash_encoded( + nIterations, + kibMemoryCost, + PARALLELISM, + password.c_str(), password.length(), + salt.AsByteArray().get(), salt.GetNumBytes(), + HASH_LEN, buf, ENCODED_HASH_LEN + ); + + if (status == ARGON2_OK) + return std::string(buf); + + return {}; +} + +/*static*/ bool Trinity::Crypto::Argon2::Verify(std::string const& password, std::string const& hash) +{ + int status = argon2id_verify(hash.c_str(), password.c_str(), password.length()); + return (status == ARGON2_OK); +} diff --git a/src/common/Cryptography/Argon2.h b/src/common/Cryptography/Argon2.h new file mode 100644 index 00000000000..afd37794854 --- /dev/null +++ b/src/common/Cryptography/Argon2.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2008-2019 TrinityCore <https://www.trinitycore.org/> + * + * This program 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 2 of the License, or (at your + * option) any later version. + * + * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef TRINITY_ARGON2_H +#define TRINITY_ARGON2_H + +#include "BigNumber.h" +#include "Define.h" +#include "Optional.h" +#include <string> + +namespace Trinity +{ +namespace Crypto +{ +struct TC_COMMON_API Argon2 +{ + static constexpr uint32 HASH_LEN = 16; // 128 bits, in bytes + static constexpr uint32 ENCODED_HASH_LEN = 100; // in chars + static constexpr uint32 DEFAULT_ITERATIONS = 10; // determined by dice roll, guaranteed to be secure (not really) + static constexpr uint32 DEFAULT_MEMORY_COST = (1u << 17); // 2^17 kibibytes is 2^7 mebibytes is ~100MB + static constexpr uint32 PARALLELISM = 1; // we don't support threaded hashing + + static Optional<std::string> Hash(std::string const& password, BigNumber const& salt, uint32 nIterations = DEFAULT_ITERATIONS, uint32 kibMemoryCost = DEFAULT_MEMORY_COST); + static bool Verify(std::string const& password, std::string const& hash); +}; +} +} + +#endif diff --git a/src/common/Cryptography/BigNumber.cpp b/src/common/Cryptography/BigNumber.cpp index 96f9144181e..19de13572bf 100644 --- a/src/common/Cryptography/BigNumber.cpp +++ b/src/common/Cryptography/BigNumber.cpp @@ -17,6 +17,7 @@ */ #include "Cryptography/BigNumber.h" +#include "Errors.h" #include <openssl/bn.h> #include <cstring> #include <algorithm> @@ -65,9 +66,10 @@ void BigNumber::SetBinary(uint8 const* bytes, int32 len) delete[] array; } -void BigNumber::SetHexStr(char const* str) +bool BigNumber::SetHexStr(char const* str) { - BN_hex2bn(&_bn, str); + int n = BN_hex2bn(&_bn, str); + return (n > 0); } void BigNumber::SetRand(int32 numbits) @@ -84,19 +86,19 @@ BigNumber& BigNumber::operator=(BigNumber const& bn) return *this; } -BigNumber BigNumber::operator+=(BigNumber const& bn) +BigNumber& BigNumber::operator+=(BigNumber const& bn) { BN_add(_bn, _bn, bn._bn); return *this; } -BigNumber BigNumber::operator-=(BigNumber const& bn) +BigNumber& BigNumber::operator-=(BigNumber const& bn) { BN_sub(_bn, _bn, bn._bn); return *this; } -BigNumber BigNumber::operator*=(BigNumber const& bn) +BigNumber& BigNumber::operator*=(BigNumber const& bn) { BN_CTX *bnctx; @@ -107,7 +109,7 @@ BigNumber BigNumber::operator*=(BigNumber const& bn) return *this; } -BigNumber BigNumber::operator/=(BigNumber const& bn) +BigNumber& BigNumber::operator/=(BigNumber const& bn) { BN_CTX *bnctx; @@ -118,7 +120,7 @@ BigNumber BigNumber::operator/=(BigNumber const& bn) return *this; } -BigNumber BigNumber::operator%=(BigNumber const& bn) +BigNumber& BigNumber::operator%=(BigNumber const& bn) { BN_CTX *bnctx; @@ -129,7 +131,18 @@ BigNumber BigNumber::operator%=(BigNumber const& bn) return *this; } -BigNumber BigNumber::Exp(BigNumber const& bn) +BigNumber& BigNumber::operator<<=(int n) +{ + BN_lshift(_bn, _bn, n); + return *this; +} + +int BigNumber::CompareTo(BigNumber const& bn) const +{ + return BN_cmp(_bn, bn._bn); +} + +BigNumber BigNumber::Exp(BigNumber const& bn) const { BigNumber ret; BN_CTX *bnctx; @@ -141,7 +154,7 @@ BigNumber BigNumber::Exp(BigNumber const& bn) return ret; } -BigNumber BigNumber::ModExp(BigNumber const& bn1, BigNumber const& bn2) +BigNumber BigNumber::ModExp(BigNumber const& bn1, BigNumber const& bn2) const { BigNumber ret; BN_CTX *bnctx; @@ -153,12 +166,12 @@ BigNumber BigNumber::ModExp(BigNumber const& bn1, BigNumber const& bn2) return ret; } -int32 BigNumber::GetNumBytes(void) +int32 BigNumber::GetNumBytes() const { return BN_num_bytes(_bn); } -uint32 BigNumber::AsDword() +uint32 BigNumber::AsDword() const { return (uint32)BN_get_word(_bn); } @@ -173,25 +186,37 @@ bool BigNumber::IsNegative() const return BN_is_negative(_bn); } -std::unique_ptr<uint8[]> BigNumber::AsByteArray(int32 minSize, bool littleEndian) +bool BigNumber::AsByteArray(uint8* buf, std::size_t bufsize, bool littleEndian) const { - int numBytes = GetNumBytes(); - int length = (minSize >= numBytes) ? minSize : numBytes; + int nBytes = GetNumBytes(); + ASSERT(!(nBytes < 0)); + std::size_t numBytes = static_cast<std::size_t>(nBytes); - uint8* array = new uint8[length]; + // too large to store + if (bufsize < numBytes) + return false; // If we need more bytes than length of BigNumber set the rest to 0 - if (length > numBytes) - memset((void*)array, 0, length); + if (numBytes < bufsize) + memset((void*)buf, 0, bufsize); - BN_bn2bin(_bn, array + (length-numBytes)); + BN_bn2bin(_bn, buf + (bufsize - numBytes)); // openssl's BN stores data internally in big endian format, reverse if little endian desired if (littleEndian) - std::reverse(array, array + length); + std::reverse(buf, buf + bufsize); - std::unique_ptr<uint8[]> ret(array); - return ret; + return true; +} + +std::unique_ptr<uint8[]> BigNumber::AsByteArray(int32 minSize, bool littleEndian) const +{ + std::size_t length = std::max(GetNumBytes(), minSize); + uint8* array = new uint8[length]; + bool success = AsByteArray(array, length, littleEndian); + ASSERT(success); + + return std::unique_ptr<uint8[]>(array); } std::string BigNumber::AsHexStr() const diff --git a/src/common/Cryptography/BigNumber.h b/src/common/Cryptography/BigNumber.h index 0f53d1b7160..e2c70fe0dd8 100644 --- a/src/common/Cryptography/BigNumber.h +++ b/src/common/Cryptography/BigNumber.h @@ -20,6 +20,7 @@ #define _AUTH_BIGNUMBER_H #include "Define.h" +#include <array> #include <memory> #include <string> @@ -36,60 +37,81 @@ class TC_COMMON_API BigNumber void SetDword(uint32); void SetQword(uint64); void SetBinary(uint8 const* bytes, int32 len); - void SetHexStr(char const* str); + bool SetHexStr(char const* str); void SetRand(int32 numbits); BigNumber& operator=(BigNumber const& bn); - BigNumber operator+=(BigNumber const& bn); - BigNumber operator+(BigNumber const& bn) + BigNumber& operator+=(BigNumber const& bn); + BigNumber operator+(BigNumber const& bn) const { BigNumber t(*this); return t += bn; } - BigNumber operator-=(BigNumber const& bn); - BigNumber operator-(BigNumber const& bn) + BigNumber& operator-=(BigNumber const& bn); + BigNumber operator-(BigNumber const& bn) const { BigNumber t(*this); return t -= bn; } - BigNumber operator*=(BigNumber const& bn); - BigNumber operator*(BigNumber const& bn) + BigNumber& operator*=(BigNumber const& bn); + BigNumber operator*(BigNumber const& bn) const { BigNumber t(*this); return t *= bn; } - BigNumber operator/=(BigNumber const& bn); - BigNumber operator/(BigNumber const& bn) + BigNumber& operator/=(BigNumber const& bn); + BigNumber operator/(BigNumber const& bn) const { BigNumber t(*this); return t /= bn; } - BigNumber operator%=(BigNumber const& bn); - BigNumber operator%(BigNumber const& bn) + BigNumber& operator%=(BigNumber const& bn); + BigNumber operator%(BigNumber const& bn) const { BigNumber t(*this); return t %= bn; } + BigNumber& operator<<=(int n); + BigNumber operator<<(int n) const + { + BigNumber t(*this); + return t <<= n; + } + + int CompareTo(BigNumber const& bn) const; + bool operator<=(BigNumber const& bn) const { return (CompareTo(bn) <= 0); } + bool operator==(BigNumber const& bn) const { return (CompareTo(bn) == 0); } + bool operator>=(BigNumber const& bn) const { return (CompareTo(bn) >= 0); } + bool operator<(BigNumber const& bn) const { return (CompareTo(bn) < 0); } + bool operator>(BigNumber const& bn) const { return (CompareTo(bn) > 0); } + bool IsZero() const; bool IsNegative() const; - BigNumber ModExp(BigNumber const& bn1, BigNumber const& bn2); - BigNumber Exp(BigNumber const&); + BigNumber ModExp(BigNumber const& bn1, BigNumber const& bn2) const; + BigNumber Exp(BigNumber const&) const; - int32 GetNumBytes(void); + int32 GetNumBytes() const; struct bignum_st *BN() { return _bn; } - uint32 AsDword(); + uint32 AsDword() const; - std::unique_ptr<uint8[]> AsByteArray(int32 minSize = 0, bool littleEndian = true); + bool AsByteArray(uint8* buf, std::size_t bufsize, bool littleEndian = true) const; + std::unique_ptr<uint8[]> AsByteArray(int32 minSize = 0, bool littleEndian = true) const; + template <std::size_t N> std::array<uint8, N> AsByteArray(bool littleEndian = true) const + { + std::array<uint8, N> buf; + AsByteArray(buf.data(), N, littleEndian); + return buf; + } std::string AsHexStr() const; std::string AsDecStr() const; diff --git a/src/common/Cryptography/CryptoGenerics.h b/src/common/Cryptography/CryptoGenerics.h new file mode 100644 index 00000000000..4ecdb109692 --- /dev/null +++ b/src/common/Cryptography/CryptoGenerics.h @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2008-2019 TrinityCore <https://www.trinitycore.org/> + * + * This program 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 2 of the License, or (at your + * option) any later version. + * + * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef TRINITY_CRYPTO_GENERICS_HPP +#define TRINITY_CRYPTO_GENERICS_HPP + +#include "advstd.h" +#include "BigNumber.h" +#include "Define.h" +#include "Errors.h" +#include <vector> +#include <openssl/rand.h> + +namespace Trinity +{ + namespace Impl + { + struct CryptoGenericsImpl + { + template <typename Cipher> + static typename Cipher::IV GenerateRandomIV() + { + typename Cipher::IV iv; + int status = RAND_bytes(advstd::data(iv), advstd::size(iv)); + ASSERT(status); + return iv; + } + + template <typename C> + static void AppendToBack(std::vector<uint8>& data, C const& tail) + { + data.insert(data.end(), std::begin(tail), std::end(tail)); + } + + template <typename C> + static void SplitFromBack(std::vector<uint8>& data, C& tail) + { + ASSERT(data.size() >= advstd::size(tail)); + for (size_t i = 1, N = advstd::size(tail); i <= N; ++i) + { + tail[N - i] = data.back(); + data.pop_back(); + } + } + }; + } + + namespace Crypto + { + template <typename Cipher> + void AEEncryptWithRandomIV(std::vector<uint8>& data, typename Cipher::Key const& key) + { + using IV = typename Cipher::IV; + using Tag = typename Cipher::Tag; + // select random IV + IV iv = Trinity::Impl::CryptoGenericsImpl::GenerateRandomIV<Cipher>(); + Tag tag; + + // encrypt data + Cipher cipher(true); + cipher.Init(key); + bool success = cipher.Process(iv, data.data(), data.size(), tag); + ASSERT(success); + + // append trailing IV and tag + Trinity::Impl::CryptoGenericsImpl::AppendToBack(data, iv); + Trinity::Impl::CryptoGenericsImpl::AppendToBack(data, tag); + } + + template <typename Cipher> + void AEEncryptWithRandomIV(std::vector<uint8>& data, BigNumber const& key) + { + AEEncryptWithRandomIV<Cipher>(data, key.AsByteArray<Cipher::KEY_SIZE_BYTES>()); + } + + template <typename Cipher> + bool AEDecrypt(std::vector<uint8>& data, typename Cipher::Key const& key) + { + using IV = typename Cipher::IV; + using Tag = typename Cipher::Tag; + // extract trailing IV and tag + IV iv; + Tag tag; + Trinity::Impl::CryptoGenericsImpl::SplitFromBack(data, tag); + Trinity::Impl::CryptoGenericsImpl::SplitFromBack(data, iv); + + // decrypt data + Cipher cipher(false); + cipher.Init(key); + return cipher.Process(iv, data.data(), data.size(), tag); + } + + template <typename Cipher> + bool AEDecrypt(std::vector<uint8>& data, BigNumber const& key) + { + return AEDecrypt<Cipher>(data, key.AsByteArray<Cipher::KEY_SIZE_BYTES>()); + } + } +} + +#endif diff --git a/src/common/Cryptography/TOTP.cpp b/src/common/Cryptography/TOTP.cpp new file mode 100644 index 00000000000..1b720ab082a --- /dev/null +++ b/src/common/Cryptography/TOTP.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2008-2019 TrinityCore <https://www.trinitycore.org/> + * + * This program 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 2 of the License, or (at your + * option) any later version. + * + * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "TOTP.h" +#include <cstring> +#include <openssl/evp.h> +#include <openssl/hmac.h> + +using namespace Trinity::Crypto; + +static constexpr uint32 TOTP_INTERVAL = 30; +static constexpr uint32 HMAC_RESULT_SIZE = 20; +/*static*/ uint32 TOTP::GenerateToken(TOTP::Secret const& secret, time_t timestamp) +{ + timestamp /= TOTP_INTERVAL; + unsigned char challenge[8]; + for (int i = 8; i--; timestamp >>= 8) + challenge[i] = timestamp; + + unsigned char digest[HMAC_RESULT_SIZE]; + uint32 digestSize = HMAC_RESULT_SIZE; + HMAC(EVP_sha1(), secret.data(), secret.size(), challenge, 8, digest, &digestSize); + + uint32 offset = digest[19] & 0xF; + uint32 truncated = (digest[offset] << 24) | (digest[offset + 1] << 16) | (digest[offset + 2] << 8) | (digest[offset + 3]); + truncated &= 0x7FFFFFFF; + return (truncated % 1000000); +} + +/*static*/ bool TOTP::ValidateToken(TOTP::Secret const& secret, uint32 token) +{ + time_t now = time(nullptr); + return ( + (token == GenerateToken(secret, now - TOTP_INTERVAL)) || + (token == GenerateToken(secret, now)) || + (token == GenerateToken(secret, now + TOTP_INTERVAL)) + ); +} diff --git a/src/common/Cryptography/TOTP.h b/src/common/Cryptography/TOTP.h new file mode 100644 index 00000000000..d90988757c6 --- /dev/null +++ b/src/common/Cryptography/TOTP.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2008-2019 TrinityCore <https://www.trinitycore.org/> + * + * This program 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 2 of the License, or (at your + * option) any later version. + * + * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef TRINITY_TOTP_H +#define TRINITY_TOTP_H + +#include "Define.h" +#include <ctime> +#include <vector> + +namespace Trinity +{ +namespace Crypto +{ +struct TC_COMMON_API TOTP +{ + static constexpr std::size_t RECOMMENDED_SECRET_LENGTH = 20; + using Secret = std::vector<uint8>; + + static uint32 GenerateToken(Secret const& key, time_t timestamp); + static bool ValidateToken(Secret const& key, uint32 token); +}; +} +} + +#endif diff --git a/src/common/Encoding/Base32.cpp b/src/common/Encoding/Base32.cpp new file mode 100644 index 00000000000..70460b4c86c --- /dev/null +++ b/src/common/Encoding/Base32.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2008-2019 TrinityCore <https://www.trinitycore.org/> + * + * This program 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 2 of the License, or (at your + * option) any later version. + * + * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "Base32.h" +#include "BaseEncoding.h" +#include "Errors.h" + +struct B32Impl +{ + static constexpr std::size_t BITS_PER_CHAR = 5; + + static constexpr char PADDING = '='; + static constexpr char Encode(uint8 v) + { + ASSERT(v < 0x20); + if (v < 26) return 'A'+v; + else return '2' + (v-26); + } + + static constexpr uint8 DECODE_ERROR = 0xff; + static constexpr uint8 Decode(uint8 v) + { + if (v == '0') return Decode('O'); + if (v == '1') return Decode('l'); + if (('A' <= v) && (v <= 'Z')) return (v-'A'); + if (('a' <= v) && (v <= 'z')) return (v-'a'); + if (('2' <= v) && (v <= '8')) return (v-'2')+26; + return DECODE_ERROR; + } +}; + +/*static*/ std::string Trinity::Encoding::Base32::Encode(std::vector<uint8> const& data) +{ + return Trinity::Impl::GenericBaseEncoding<B32Impl>::Encode(data); +} + +/*static*/ Optional<std::vector<uint8>> Trinity::Encoding::Base32::Decode(std::string const& data) +{ + return Trinity::Impl::GenericBaseEncoding<B32Impl>::Decode(data); +} diff --git a/src/common/Encoding/Base32.h b/src/common/Encoding/Base32.h new file mode 100644 index 00000000000..8b4dda2a9fd --- /dev/null +++ b/src/common/Encoding/Base32.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2008-2019 TrinityCore <https://www.trinitycore.org/> + * + * This program 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 2 of the License, or (at your + * option) any later version. + * + * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef TRINITY_BASE32_H +#define TRINITY_BASE32_H + +#include "Define.h" +#include "Optional.h" +#include <string> +#include <vector> + +namespace Trinity +{ +namespace Encoding +{ +struct TC_COMMON_API Base32 +{ + static std::string Encode(std::vector<uint8> const& data); + static Optional<std::vector<uint8>> Decode(std::string const& data); +}; +} +} + +#endif diff --git a/src/common/Encoding/Base64.cpp b/src/common/Encoding/Base64.cpp new file mode 100644 index 00000000000..21e02ade85d --- /dev/null +++ b/src/common/Encoding/Base64.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2008-2019 TrinityCore <https://www.trinitycore.org/> + * + * This program 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 2 of the License, or (at your + * option) any later version. + * + * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "Base64.h" +#include "BaseEncoding.h" +#include "Errors.h" + +struct B64Impl +{ + static constexpr std::size_t BITS_PER_CHAR = 6; + + static constexpr char PADDING = '='; + static constexpr char Encode(uint8 v) + { + ASSERT(v < 0x40); + if (v < 26) return 'A' + v; + if (v < 52) return 'a' + (v - 26); + if (v < 62) return '0' + (v - 52); + if (v == 62) return '+'; + else return '/'; + } + + static constexpr uint8 DECODE_ERROR = 0xff; + static constexpr uint8 Decode(uint8 v) + { + if (('A' <= v) && (v <= 'Z')) return (v - 'A'); + if (('a' <= v) && (v <= 'z')) return (v - 'a') + 26; + if (('0' <= v) && (v <= '9')) return (v - '0') + 52; + if (v == '+') return 62; + if (v == '/') return 63; + return DECODE_ERROR; + } +}; + +/*static*/ std::string Trinity::Encoding::Base64::Encode(std::vector<uint8> const& data) +{ + return Trinity::Impl::GenericBaseEncoding<B64Impl>::Encode(data); +} + +/*static*/ Optional<std::vector<uint8>> Trinity::Encoding::Base64::Decode(std::string const& data) +{ + return Trinity::Impl::GenericBaseEncoding<B64Impl>::Decode(data); +} diff --git a/src/common/Encoding/Base64.h b/src/common/Encoding/Base64.h new file mode 100644 index 00000000000..d421118711d --- /dev/null +++ b/src/common/Encoding/Base64.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2008-2019 TrinityCore <https://www.trinitycore.org/> + * + * This program 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 2 of the License, or (at your + * option) any later version. + * + * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef TRINITY_BASE64_H +#define TRINITY_BASE64_H + +#include "Define.h" +#include "Optional.h" +#include <string> +#include <vector> + +namespace Trinity +{ +namespace Encoding +{ +struct TC_COMMON_API Base64 +{ + static std::string Encode(std::vector<uint8> const& data); + static Optional<std::vector<uint8>> Decode(std::string const& data); +}; +} +} + +#endif diff --git a/src/common/Encoding/BaseEncoding.h b/src/common/Encoding/BaseEncoding.h new file mode 100644 index 00000000000..d8b1453f271 --- /dev/null +++ b/src/common/Encoding/BaseEncoding.h @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2008-2019 TrinityCore <https://www.trinitycore.org/> + * + * This program 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 2 of the License, or (at your + * option) any later version. + * + * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef TRINITY_BASE_ENCODING_HPP +#define TRINITY_BASE_ENCODING_HPP + +#include "advstd.h" +#include "Define.h" +#include "Optional.h" +#include <string> +#include <vector> + +namespace Trinity +{ +namespace Impl +{ +template <typename Encoding> +struct GenericBaseEncoding +{ + static constexpr std::size_t BITS_PER_CHAR = Encoding::BITS_PER_CHAR; + static constexpr std::size_t PAD_TO = advstd::lcm(8u, BITS_PER_CHAR); + + static_assert(BITS_PER_CHAR < 8, "Encoding parameters are invalid"); + + static constexpr uint8 DECODE_ERROR = Encoding::DECODE_ERROR; + static constexpr char PADDING = Encoding::PADDING; + + static constexpr std::size_t EncodedSize(std::size_t size) + { + size *= 8; // bits in input + if (size % PAD_TO) // pad to boundary + size += (PAD_TO - (size % PAD_TO)); + return (size / BITS_PER_CHAR); + } + + static constexpr std::size_t DecodedSize(std::size_t size) + { + size *= BITS_PER_CHAR; // bits in input + if (size % PAD_TO) // pad to boundary + size += (PAD_TO - (size % PAD_TO)); + return (size / 8); + } + + static std::string Encode(std::vector<uint8> const& data) + { + auto it = data.begin(), end = data.end(); + if (it == end) + return ""; + + std::string s; + s.reserve(EncodedSize(data.size())); + + uint8 bitsLeft = 8; // in current byte + do + { + uint8 thisC = 0; + if (bitsLeft >= BITS_PER_CHAR) + { + bitsLeft -= BITS_PER_CHAR; + thisC = ((*it >> bitsLeft) & ((1 << BITS_PER_CHAR)-1)); + if (!bitsLeft) + { + ++it; + bitsLeft = 8; + } + } + else + { + thisC = (*it & ((1 << bitsLeft) - 1)) << (BITS_PER_CHAR - bitsLeft); + bitsLeft += (8 - BITS_PER_CHAR); + if ((++it) != end) + thisC |= (*it >> bitsLeft); + } + s.append(1, Encoding::Encode(thisC)); + } while (it != end); + + while (bitsLeft != 8) + { + if (bitsLeft > BITS_PER_CHAR) + bitsLeft -= BITS_PER_CHAR; + else + bitsLeft += (8 - BITS_PER_CHAR); + s.append(1, PADDING); + } + + return s; + } + + static Optional<std::vector<uint8>> Decode(std::string const& data) + { + auto it = data.begin(), end = data.end(); + if (it == end) + return std::vector<uint8>(); + + std::vector<uint8> v; + v.reserve(DecodedSize(data.size())); + + uint8 currentByte = 0; + uint8 bitsLeft = 8; // in current byte + while ((it != end) && (*it != PADDING)) + { + uint8 cur = Encoding::Decode(*(it++)); + if (cur == DECODE_ERROR) + return {}; + + if (bitsLeft > BITS_PER_CHAR) + { + bitsLeft -= BITS_PER_CHAR; + currentByte |= (cur << bitsLeft); + } + else + { + bitsLeft = BITS_PER_CHAR - bitsLeft; // in encoded char + currentByte |= (cur >> bitsLeft); + v.push_back(currentByte); + currentByte = (cur & ((1 << bitsLeft) - 1)); + bitsLeft = 8 - bitsLeft; // in byte again + currentByte <<= bitsLeft; + } + } + + if (currentByte) + return {}; // decode error, trailing non-zero bits + + // process padding + while ((it != end) && (*it == PADDING) && (bitsLeft != 8)) + { + if (bitsLeft > BITS_PER_CHAR) + bitsLeft -= BITS_PER_CHAR; + else + bitsLeft += (8 - BITS_PER_CHAR); + ++it; + } + + // ok, all padding should be consumed, and we should be at end of string + if (it == end) + return v; + + // anything else is an error + return {}; + } +}; +} +} + +#endif diff --git a/src/common/Utilities/advstd.h b/src/common/Utilities/advstd.h index 5606b84cfa2..86edcd4445d 100644 --- a/src/common/Utilities/advstd.h +++ b/src/common/Utilities/advstd.h @@ -86,12 +86,46 @@ namespace advstd #undef forward_2v // C++17 std::size - template <class C> - constexpr auto size(const C& c) -> decltype(c.size()) { return c.size(); } + template <typename C> + constexpr auto size(const C& c) { return c.size(); } - template <class T, std::size_t N> + template <typename T, std::size_t N> constexpr std::size_t size(const T(&)[N]) noexcept { return N; } + // C++17 std::data + template <typename C> + constexpr auto data(C& c) { return c.data(); } + + template <typename C> + constexpr auto data(C const& c) { return c.data(); } + + template <typename T, std::size_t N> + constexpr T* data(T(&a)[N]) noexcept { return a; } + + template <typename T, std::size_t N> + constexpr T const* data(const T(&a)[N]) noexcept { return a; } + + template <typename T> + constexpr T const* data(std::initializer_list<T> l) noexcept { return l.begin(); } + + // C++17 std::gcd + template <typename T1, typename T2> + constexpr std::enable_if_t<advstd::is_unsigned_v<T1> && advstd::is_unsigned_v<T2>, std::common_type_t<T1, T2>> gcd(T1 m, T2 n) + { + if (m < n) + return gcd(n, m); + if (!n) + return m; + return gcd(n, m%n); + } + + // C++17 std::lcm + template <typename T1, typename T2> + constexpr std::enable_if_t<advstd::is_unsigned_v<T1> && advstd::is_unsigned_v<T2>, std::common_type_t<T1, T2>> lcm(T1 m, T2 n) + { + return (m/gcd(m, n))*n; + } + // C++20 std::remove_cvref_t template <class T> using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<T>>; |
