mirror of
https://github.com/mod-playerbots/azerothcore-wotlk.git
synced 2026-01-13 09:17:18 +00:00
feat(Core/Authserver): TOTP rewrite (#5620)
This commit is contained in:
@@ -52,6 +52,7 @@ target_link_libraries(common
|
||||
PUBLIC
|
||||
acore-core-interface
|
||||
ace
|
||||
argon2
|
||||
g3dlib
|
||||
Detour
|
||||
sfmt
|
||||
|
||||
53
src/common/Cryptography/AES.cpp
Normal file
53
src/common/Cryptography/AES.cpp
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
|
||||
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
|
||||
*/
|
||||
|
||||
#include "AES.h"
|
||||
#include "Errors.h"
|
||||
#include <limits>
|
||||
|
||||
acore::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);
|
||||
}
|
||||
|
||||
acore::Crypto::AES::~AES()
|
||||
{
|
||||
EVP_CIPHER_CTX_free(_ctx);
|
||||
}
|
||||
|
||||
void acore::Crypto::AES::Init(Key const& key)
|
||||
{
|
||||
int status = EVP_CipherInit_ex(_ctx, nullptr, nullptr, key.data(), nullptr, -1);
|
||||
ASSERT(status);
|
||||
}
|
||||
|
||||
bool acore::Crypto::AES::Process(IV const& iv, uint8* data, size_t length, Tag& tag)
|
||||
{
|
||||
ASSERT(length <= static_cast<size_t>(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;
|
||||
}
|
||||
39
src/common/Cryptography/AES.h
Normal file
39
src/common/Cryptography/AES.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
|
||||
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
|
||||
*/
|
||||
|
||||
#ifndef Warhead_AES_h__
|
||||
#define Warhead_AES_h__
|
||||
|
||||
#include "Define.h"
|
||||
#include <array>
|
||||
#include <openssl/evp.h>
|
||||
|
||||
namespace acore::Crypto
|
||||
{
|
||||
class AC_COMMON_API AES
|
||||
{
|
||||
public:
|
||||
static constexpr size_t IV_SIZE_BYTES = 12;
|
||||
static constexpr size_t KEY_SIZE_BYTES = 16;
|
||||
static constexpr 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, size_t length, Tag& tag);
|
||||
|
||||
private:
|
||||
EVP_CIPHER_CTX* _ctx;
|
||||
bool _encrypting;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // Warhead_AES_h__
|
||||
@@ -8,7 +8,6 @@
|
||||
#define _AUTH_SARC4_H
|
||||
|
||||
#include "Define.h"
|
||||
|
||||
#include <array>
|
||||
#include <openssl/evp.h>
|
||||
|
||||
@@ -16,19 +15,19 @@ namespace acore::Crypto
|
||||
{
|
||||
class ARC4
|
||||
{
|
||||
public:
|
||||
ARC4();
|
||||
~ARC4();
|
||||
public:
|
||||
ARC4();
|
||||
~ARC4();
|
||||
|
||||
void Init(uint8 const* seed, size_t len);
|
||||
template <typename Container>
|
||||
void Init(Container const& c) { Init(std::data(c), std::size(c)); }
|
||||
void Init(uint8 const* seed, size_t len);
|
||||
template <typename Container>
|
||||
void Init(Container const& c) { Init(std::data(c), std::size(c)); }
|
||||
|
||||
void UpdateData(uint8* data, size_t len);
|
||||
template <typename Container>
|
||||
void UpdateData(Container& c) { UpdateData(std::data(c), std::size(c)); }
|
||||
private:
|
||||
EVP_CIPHER_CTX* _ctx;
|
||||
void UpdateData(uint8* data, size_t len);
|
||||
template <typename Container>
|
||||
void UpdateData(Container& c) { UpdateData(std::data(c), std::size(c)); }
|
||||
private:
|
||||
EVP_CIPHER_CTX* _ctx;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
32
src/common/Cryptography/Argon2.cpp
Normal file
32
src/common/Cryptography/Argon2.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
|
||||
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
|
||||
*/
|
||||
|
||||
#include "Argon2.h"
|
||||
#include <argon2/argon2.h>
|
||||
|
||||
/*static*/ Optional<std::string> acore::Crypto::Argon2::Hash(std::string const& password, BigNumber const& salt, uint32 nIterations, uint32 kibMemoryCost)
|
||||
{
|
||||
char buf[ENCODED_HASH_LEN];
|
||||
std::vector<uint8> saltBytes = salt.ToByteVector();
|
||||
int status = argon2id_hash_encoded(
|
||||
nIterations,
|
||||
kibMemoryCost,
|
||||
PARALLELISM,
|
||||
password.c_str(), password.length(),
|
||||
saltBytes.data(), saltBytes.size(),
|
||||
HASH_LEN, buf, ENCODED_HASH_LEN
|
||||
);
|
||||
|
||||
if (status == ARGON2_OK)
|
||||
return std::string(buf);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
/*static*/ bool acore::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);
|
||||
}
|
||||
29
src/common/Cryptography/Argon2.h
Normal file
29
src/common/Cryptography/Argon2.h
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
|
||||
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
|
||||
*/
|
||||
|
||||
#ifndef WARHEAD_ARGON2_H
|
||||
#define WARHEAD_ARGON2_H
|
||||
|
||||
#include "BigNumber.h"
|
||||
#include "Define.h"
|
||||
#include "Optional.h"
|
||||
#include <string>
|
||||
|
||||
namespace acore::Crypto
|
||||
{
|
||||
struct AC_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
|
||||
@@ -75,7 +75,7 @@ std::optional<SessionKey> SRP6::VerifyChallengeResponse(EphemeralKey const& A, S
|
||||
_used = true;
|
||||
|
||||
BigNumber const _A(A);
|
||||
if ((_A % _N).isZero())
|
||||
if ((_A % _N).IsZero())
|
||||
return std::nullopt;
|
||||
|
||||
BigNumber const u(SHA1::GetDigestOf(A, B));
|
||||
@@ -1,23 +1,22 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
|
||||
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
|
||||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
|
||||
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
|
||||
*/
|
||||
|
||||
#include "Cryptography/BigNumber.h"
|
||||
#include "Errors.h"
|
||||
#include <openssl/bn.h>
|
||||
#include <openssl/crypto.h>
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
BigNumber::BigNumber()
|
||||
: _bn(BN_new())
|
||||
{
|
||||
}
|
||||
{ }
|
||||
|
||||
BigNumber::BigNumber(BigNumber const& bn)
|
||||
: _bn(BN_dup(bn.BN()))
|
||||
{
|
||||
}
|
||||
{ }
|
||||
|
||||
BigNumber::~BigNumber()
|
||||
{
|
||||
@@ -64,9 +63,10 @@ void BigNumber::SetBinary(uint8 const* bytes, int32 len, bool littleEndian)
|
||||
BN_bin2bn(bytes, len, _bn);
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -83,19 +83,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;
|
||||
|
||||
@@ -106,7 +106,7 @@ BigNumber BigNumber::operator*=(BigNumber const& bn)
|
||||
return *this;
|
||||
}
|
||||
|
||||
BigNumber BigNumber::operator/=(BigNumber const& bn)
|
||||
BigNumber& BigNumber::operator/=(BigNumber const& bn)
|
||||
{
|
||||
BN_CTX* bnctx;
|
||||
|
||||
@@ -117,7 +117,7 @@ BigNumber BigNumber::operator/=(BigNumber const& bn)
|
||||
return *this;
|
||||
}
|
||||
|
||||
BigNumber BigNumber::operator%=(BigNumber const& bn)
|
||||
BigNumber& BigNumber::operator%=(BigNumber const& bn)
|
||||
{
|
||||
BN_CTX* bnctx;
|
||||
|
||||
@@ -128,6 +128,17 @@ BigNumber BigNumber::operator%=(BigNumber const& bn)
|
||||
return *this;
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -157,16 +168,21 @@ int32 BigNumber::GetNumBytes() const
|
||||
return BN_num_bytes(_bn);
|
||||
}
|
||||
|
||||
uint32 BigNumber::AsDword()
|
||||
uint32 BigNumber::AsDword() const
|
||||
{
|
||||
return (uint32)BN_get_word(_bn);
|
||||
}
|
||||
|
||||
bool BigNumber::isZero() const
|
||||
bool BigNumber::IsZero() const
|
||||
{
|
||||
return BN_is_zero(_bn);
|
||||
}
|
||||
|
||||
bool BigNumber::IsNegative() const
|
||||
{
|
||||
return BN_is_negative(_bn);
|
||||
}
|
||||
|
||||
void BigNumber::GetBytes(uint8* buf, size_t bufsize, bool littleEndian) const
|
||||
{
|
||||
#if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER < 0x10100000L
|
||||
@@ -201,12 +217,18 @@ std::vector<uint8> BigNumber::ToByteVector(int32 minSize, bool littleEndian) con
|
||||
return v;
|
||||
}
|
||||
|
||||
char* BigNumber::AsHexStr() const
|
||||
std::string BigNumber::AsHexStr() const
|
||||
{
|
||||
return BN_bn2hex(_bn);
|
||||
char* ch = BN_bn2hex(_bn);
|
||||
std::string ret = ch;
|
||||
OPENSSL_free(ch);
|
||||
return ret;
|
||||
}
|
||||
|
||||
char* BigNumber::AsDecStr() const
|
||||
std::string BigNumber::AsDecStr() const
|
||||
{
|
||||
return BN_bn2dec(_bn);
|
||||
char* ch = BN_bn2dec(_bn);
|
||||
std::string ret = ch;
|
||||
OPENSSL_free(ch);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
|
||||
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
|
||||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
|
||||
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
|
||||
*/
|
||||
|
||||
#ifndef _AUTH_BIGNUMBER_H
|
||||
#define _AUTH_BIGNUMBER_H
|
||||
|
||||
#include "Define.h"
|
||||
#include "Errors.h"
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
@@ -16,7 +14,7 @@
|
||||
|
||||
struct bignum_st;
|
||||
|
||||
class BigNumber
|
||||
class AC_COMMON_API BigNumber
|
||||
{
|
||||
public:
|
||||
BigNumber();
|
||||
@@ -24,7 +22,8 @@ public:
|
||||
BigNumber(uint32 v) : BigNumber() { SetDword(v); }
|
||||
BigNumber(int32 v) : BigNumber() { SetDword(v); }
|
||||
BigNumber(std::string const& v) : BigNumber() { SetHexStr(v); }
|
||||
template<size_t Size>
|
||||
|
||||
template <size_t Size>
|
||||
BigNumber(std::array<uint8, Size> const& v, bool littleEndian = true) : BigNumber() { SetBinary(v.data(), Size, littleEndian); }
|
||||
|
||||
~BigNumber();
|
||||
@@ -33,51 +32,68 @@ public:
|
||||
void SetDword(uint32);
|
||||
void SetQword(uint64);
|
||||
void SetBinary(uint8 const* bytes, int32 len, bool littleEndian = true);
|
||||
template<typename Container>
|
||||
|
||||
template <typename Container>
|
||||
auto SetBinary(Container const& c, bool littleEndian = true) -> std::enable_if_t<!std::is_pointer_v<std::decay_t<Container>>> { SetBinary(std::data(c), std::size(c), littleEndian); }
|
||||
void SetHexStr(char const* str);
|
||||
void SetHexStr(std::string const& str) { SetHexStr(str.c_str()); }
|
||||
|
||||
bool SetHexStr(char const* str);
|
||||
bool SetHexStr(std::string const& str) { return SetHexStr(str.c_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) const
|
||||
{
|
||||
BigNumber t(*this);
|
||||
return t += 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) const
|
||||
{
|
||||
BigNumber t(*this);
|
||||
return t *= 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) const
|
||||
{
|
||||
BigNumber t(*this);
|
||||
return t %= bn;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool isZero() const;
|
||||
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) const;
|
||||
BigNumber Exp(BigNumber const&) const;
|
||||
@@ -87,12 +103,12 @@ public:
|
||||
struct bignum_st* BN() { return _bn; }
|
||||
struct bignum_st const* BN() const { return _bn; }
|
||||
|
||||
uint32 AsDword();
|
||||
uint32 AsDword() const;
|
||||
|
||||
void GetBytes(uint8* buf, size_t bufsize, bool littleEndian = true) const;
|
||||
std::vector<uint8> ToByteVector(int32 minSize = 0, bool littleEndian = true) const;
|
||||
|
||||
template<std::size_t Size>
|
||||
template <std::size_t Size>
|
||||
std::array<uint8, Size> ToByteArray(bool littleEndian = true) const
|
||||
{
|
||||
std::array<uint8, Size> buf;
|
||||
@@ -100,10 +116,11 @@ public:
|
||||
return buf;
|
||||
}
|
||||
|
||||
[[nodiscard]] char* AsHexStr() const;
|
||||
[[nodiscard]] char* AsDecStr() const;
|
||||
std::string AsHexStr() const;
|
||||
std::string AsDecStr() const;
|
||||
|
||||
private:
|
||||
struct bignum_st* _bn;
|
||||
|
||||
};
|
||||
#endif
|
||||
|
||||
42
src/common/Cryptography/TOTP.cpp
Normal file
42
src/common/Cryptography/TOTP.cpp
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
|
||||
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
|
||||
*/
|
||||
|
||||
#include "TOTP.h"
|
||||
#include <cstring>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/hmac.h>
|
||||
|
||||
constexpr std::size_t acore::Crypto::TOTP::RECOMMENDED_SECRET_LENGTH;
|
||||
static constexpr uint32 TOTP_INTERVAL = 30;
|
||||
static constexpr uint32 HMAC_RESULT_SIZE = 20;
|
||||
|
||||
/*static*/ uint32 acore::Crypto::TOTP::GenerateToken(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 acore::Crypto::TOTP::ValidateToken(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))
|
||||
);
|
||||
}
|
||||
25
src/common/Cryptography/TOTP.h
Normal file
25
src/common/Cryptography/TOTP.h
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
|
||||
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
|
||||
*/
|
||||
|
||||
#ifndef WARHEAD_TOTP_H
|
||||
#define WARHEAD_TOTP_H
|
||||
|
||||
#include "Define.h"
|
||||
#include <ctime>
|
||||
#include <vector>
|
||||
|
||||
namespace acore::Crypto
|
||||
{
|
||||
struct AC_COMMON_API TOTP
|
||||
{
|
||||
static constexpr 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
|
||||
43
src/common/Encoding/Base32.cpp
Normal file
43
src/common/Encoding/Base32.cpp
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
|
||||
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
|
||||
*/
|
||||
|
||||
#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 (v == '8') return Decode('B');
|
||||
if (('A' <= v) && (v <= 'Z')) return (v-'A');
|
||||
if (('a' <= v) && (v <= 'z')) return (v-'a');
|
||||
if (('2' <= v) && (v <= '7')) return (v-'2')+26;
|
||||
return DECODE_ERROR;
|
||||
}
|
||||
};
|
||||
|
||||
/*static*/ std::string acore::Encoding::Base32::Encode(std::vector<uint8> const& data)
|
||||
{
|
||||
return acore::Impl::GenericBaseEncoding<B32Impl>::Encode(data);
|
||||
}
|
||||
|
||||
/*static*/ Optional<std::vector<uint8>> acore::Encoding::Base32::Decode(std::string const& data)
|
||||
{
|
||||
return acore::Impl::GenericBaseEncoding<B32Impl>::Decode(data);
|
||||
}
|
||||
23
src/common/Encoding/Base32.h
Normal file
23
src/common/Encoding/Base32.h
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
|
||||
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
|
||||
*/
|
||||
|
||||
#ifndef WARHEAD_BASE32_H
|
||||
#define WARHEAD_BASE32_H
|
||||
|
||||
#include "Define.h"
|
||||
#include "Optional.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace acore::Encoding
|
||||
{
|
||||
struct AC_COMMON_API Base32
|
||||
{
|
||||
static std::string Encode(std::vector<uint8> const& data);
|
||||
static Optional<std::vector<uint8>> Decode(std::string const& data);
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
45
src/common/Encoding/Base64.cpp
Normal file
45
src/common/Encoding/Base64.cpp
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
|
||||
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
|
||||
*/
|
||||
|
||||
#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 acore::Encoding::Base64::Encode(std::vector<uint8> const& data)
|
||||
{
|
||||
return acore::Impl::GenericBaseEncoding<B64Impl>::Encode(data);
|
||||
}
|
||||
|
||||
/*static*/ Optional<std::vector<uint8>> acore::Encoding::Base64::Decode(std::string const& data)
|
||||
{
|
||||
return acore::Impl::GenericBaseEncoding<B64Impl>::Decode(data);
|
||||
}
|
||||
23
src/common/Encoding/Base64.h
Normal file
23
src/common/Encoding/Base64.h
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
|
||||
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
|
||||
*/
|
||||
|
||||
#ifndef WARHEAD_BASE64_H
|
||||
#define WARHEAD_BASE64_H
|
||||
|
||||
#include "Define.h"
|
||||
#include "Optional.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace acore::Encoding
|
||||
{
|
||||
struct AC_COMMON_API Base64
|
||||
{
|
||||
static std::string Encode(std::vector<uint8> const& data);
|
||||
static Optional<std::vector<uint8>> Decode(std::string const& data);
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
145
src/common/Encoding/BaseEncoding.h
Normal file
145
src/common/Encoding/BaseEncoding.h
Normal file
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
|
||||
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
|
||||
*/
|
||||
|
||||
#ifndef WARHEAD_BASE_ENCODING_HPP
|
||||
#define WARHEAD_BASE_ENCODING_HPP
|
||||
|
||||
#include "Define.h"
|
||||
#include "Optional.h"
|
||||
#include <numeric>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace acore::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 = std::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
|
||||
@@ -28,7 +28,6 @@ inline T standard_deviation(Container&& c)
|
||||
return std::sqrt(accum / (size - 1));
|
||||
}
|
||||
|
||||
|
||||
template <typename Container, typename T = typename std::decay<decltype(*std::begin(std::declval<Container>()))>::type>
|
||||
inline T mean(Container&& c)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user