diff options
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>>;  | 
