Skip to content

Commit

Permalink
Auth: Use PIN Field in 1.12.x for TOTP auth if applicable
Browse files Browse the repository at this point in the history
  • Loading branch information
insunaa committed Jul 4, 2023
1 parent 68b674e commit e8c2f75
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 4 deletions.
109 changes: 105 additions & 4 deletions src/realmd/AuthSocket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include "AuthCodes.h"
#include "Auth/SRP6.h"
#include "Util/CommonDefines.h"
#include "Util/Util.h"

#include <openssl/md5.h>
#include <ctime>
Expand Down Expand Up @@ -473,13 +474,20 @@ bool AuthSocket::_HandleLogonChallenge()
if (!_token.empty() && _build >= 8606) // authenticator was added in 2.4.3
securityFlags = SECURITY_FLAG_AUTHENTICATOR;

if (!_token.empty() && _build <= 6141)
securityFlags = SECURITY_FLAG_PIN;

pkt << uint8(securityFlags); // security flags (0x0...0x04)

if (securityFlags & SECURITY_FLAG_PIN) // PIN input
{
pkt << uint32(0);
pkt << uint64(0);
pkt << uint64(0);
uint32 gridSeedPkt = m_gridSeed = static_cast<uint32>(0/*urand()*/);
EndianConvert(gridSeedPkt);
m_serverSecuritySalt.SetRand(16 * 8); // 16 bytes random
m_promptPin = true;

pkt << gridSeedPkt;
pkt.append(m_serverSecuritySalt.AsByteArray(16).data(), 16);
}

if (securityFlags & SECURITY_FLAG_UNK) // Matrix input
Expand Down Expand Up @@ -520,6 +528,14 @@ bool AuthSocket::_HandleLogonProof()
if (!Read((char*)&lp, sizeof(sAuthLogonProof_C)))
return false;


PINData pinData;

if (lp.securityFlags == SECURITY_FLAG_PIN)
{
if (!Read((char*)&pinData, sizeof(pinData)))
return false;
}
///- Session is closed unless overriden
_status = STATUS_CLOSED;

Expand Down Expand Up @@ -548,8 +564,21 @@ bool AuthSocket::_HandleLogonProof()
srp.HashSessionKey();
srp.CalculateProof(_login);

bool pinResult = true;

if (m_promptPin && lp.securityFlags == SECURITY_FLAG_PIN)
pinResult = false;

if (m_promptPin && lp.securityFlags == SECURITY_FLAG_PIN)
{
auto pin = generateToken(_token.c_str());

if (pin != uint32(-1))
pinResult = VerifyPinData(pin, pinData);
}

///- Check if SRP6 results match (password is correct), else send an error
if (!srp.Proof(lp.M1, 20))
if (!srp.Proof(lp.M1, 20) && pinResult)
{
if (_build > 6141 && (lp.securityFlags & SECURITY_FLAG_AUTHENTICATOR || !_token.empty()))
{
Expand Down Expand Up @@ -1012,6 +1041,78 @@ bool AuthSocket::_HandleXferAccept()
return true;
}

// Verify PIN entry data
bool AuthSocket::VerifyPinData(uint32 pin, const PINData& clientData)
{
// remap the grid to match the client's layout
std::vector<uint8> grid { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
std::vector<uint8> remappedGrid(grid.size());

uint8* remappedIndex = remappedGrid.data();
uint32 seed = m_gridSeed;

for (size_t i = grid.size(); i > 0; --i)
{
auto remainder = seed % i;
seed /= i;
*remappedIndex = grid[remainder];

size_t copySize = i;
copySize -= remainder;
--copySize;

uint8* srcPtr = grid.data() + remainder + 1;
uint8* dstPtr = grid.data() + remainder;

std::copy(srcPtr, srcPtr + copySize, dstPtr);
++remappedIndex;
}

// convert the PIN to bytes (for ex. '1234' to {1, 2, 3, 4})
std::vector<uint8> pinBytes;

while (pin != 0)
{
pinBytes.push_back(pin % 10);
pin /= 10;
}

std::reverse(pinBytes.begin(), pinBytes.end());

// validate PIN length
if (pinBytes.size() < 4 || pinBytes.size() > 10)
return false; // PIN outside of expected range

// remap the PIN to calculate the expected client input sequence
for (size_t i = 0; i < pinBytes.size(); ++i)
{
auto index = std::find(remappedGrid.begin(), remappedGrid.end(), pinBytes[i]);
pinBytes[i] = std::distance(remappedGrid.begin(), index);
}

// convert PIN bytes to their ASCII values
for (size_t i = 0; i < pinBytes.size(); ++i)
pinBytes[i] += 0x30;

// validate the PIN, x = H(client_salt | H(server_salt | ascii(pin_bytes)))
Sha1Hash sha;
sha.UpdateData(m_serverSecuritySalt.AsByteArray());
sha.UpdateData(pinBytes.data(), pinBytes.size());
sha.Finalize();

BigNumber hash, clientHash;
hash.SetBinary(sha.GetDigest(), sha.GetLength());
clientHash.SetBinary(clientData.hash, 20);

sha.Initialize();
sha.UpdateData(clientData.salt, sizeof(clientData.salt));
sha.UpdateData(hash.AsByteArray());
sha.Finalize();
hash.SetBinary(sha.GetDigest(), sha.GetLength());

return !memcmp(hash.AsDecStr(), clientHash.AsDecStr(), 20);
}

int32 AuthSocket::generateToken(char const* b32key)
{
size_t keySize = strlen(b32key);
Expand Down
11 changes: 11 additions & 0 deletions src/realmd/AuthSocket.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@

#define HMAC_RES_SIZE 20

struct PINData
{
uint8 salt[16];
uint8 hash[20];
};

class AuthSocket : public MaNGOS::Socket
{
public:
Expand All @@ -48,6 +54,7 @@ class AuthSocket : public MaNGOS::Socket

void SendProof(Sha1Hash sha);
void LoadRealmlist(ByteBuffer& pkt, uint32 acctid, uint8 accountSecurityLevel = 0);
bool VerifyPinData(uint32 pin, const PINData& clientData);
int32 generateToken(char const* b32key);

uint8 getEligibleRealmCount(uint8 accountSecurityLevel);
Expand Down Expand Up @@ -90,6 +97,10 @@ class AuthSocket : public MaNGOS::Socket
uint16 _build;
AccountTypes _accountSecurityLevel;

BigNumber m_serverSecuritySalt;
uint32 m_gridSeed = 0;
bool m_promptPin = false;

boost::asio::deadline_timer m_timeoutTimer;

virtual bool ProcessIncomingData() override;
Expand Down

0 comments on commit e8c2f75

Please sign in to comment.