Skip to content

Commit

Permalink
Auth: Enable 2FA on Classic
Browse files Browse the repository at this point in the history
  • Loading branch information
insunaa committed Oct 20, 2024
1 parent 5964022 commit f0f5371
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 6 deletions.
106 changes: 100 additions & 6 deletions src/realmd/AuthSocket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ struct sAuthLogonProof_C
uint8 crc_hash[20];
uint8 number_of_keys;
uint8 securityFlags; // 0x00-0x04
PINData pinData;
};
/*
typedef struct
Expand Down Expand Up @@ -492,13 +493,20 @@ bool AuthSocket::_HandleLogonChallenge()
if (!self->_token.empty() && self->_build >= 8606) // authenticator was added in 2.4.3
securityFlags = SECURITY_FLAG_AUTHENTICATOR;

if (!self->_token.empty() && self->_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 = self->m_gridSeed = static_cast<uint32>(0);
EndianConvert(gridSeedPkt);
self->m_serverSecuritySalt.SetRand(16 * 8); // 16 bytes random
self->m_promptPin = true;

*pkt << gridSeedPkt;
pkt->append(self->m_serverSecuritySalt.AsByteArray(16).data(), 16);
}

if (securityFlags & SECURITY_FLAG_UNK) // Matrix input
Expand Down Expand Up @@ -539,7 +547,8 @@ bool AuthSocket::_HandleLogonProof()
DEBUG_LOG("Entering _HandleLogonProof");
///- Read the packet
std::shared_ptr<sAuthLogonProof_C> lp = std::make_shared<sAuthLogonProof_C>();
Read((char*)lp.get(), sizeof(sAuthLogonProof_C), [self = shared_from_this(), lp](const boost::system::error_code& error, std::size_t read)
auto size = m_promptPin ? sizeof(sAuthLogonProof_C) : sizeof(sAuthLogonProof_C) - sizeof(PINData);
Read((char*)lp.get(), size, [self = shared_from_this(), lp](const boost::system::error_code& error, std::size_t read)
{
if (error)
{
Expand Down Expand Up @@ -575,10 +584,23 @@ bool AuthSocket::_HandleLogonProof()
self->srp.HashSessionKey();
self->srp.CalculateProof(self->_login);

bool pinResult = true;

if (self->m_promptPin && (lp->securityFlags & SECURITY_FLAG_PIN))
pinResult = false;

if (self->m_promptPin && (lp->securityFlags & SECURITY_FLAG_PIN) && !self->_token.empty())
{
auto pin = self->generateToken(self->_token.c_str());

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

///- Check if SRP6 results match (password is correct), else send an error
if (!self->srp.Proof(lp->M1, 20))
if (!self->srp.Proof(lp->M1, 20) && pinResult)
{
if (lp->securityFlags & SECURITY_FLAG_AUTHENTICATOR || !self->_token.empty())
if (self->_build > 6141 && (lp->securityFlags & SECURITY_FLAG_AUTHENTICATOR || !self->_token.empty()))
{
std::shared_ptr<uint8> pinCount = std::make_shared<uint8>();
self->Read((char*)pinCount.get(), sizeof(uint8), [self, pinCount, lp](const boost::system::error_code& error, std::size_t read)
Expand Down Expand Up @@ -1036,6 +1058,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);
}

void AuthSocket::verifyVersionAndFinalizeAuthentication(std::shared_ptr<sAuthLogonProof_C> lp)
{
if (!VerifyVersion(lp->A, sizeof(lp->A), lp->crc_hash, false))
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];
};

struct sAuthLogonProof_C;

class AuthSocket : public MaNGOS::AsyncSocket<AuthSocket>
Expand All @@ -50,6 +56,7 @@ class AuthSocket : public MaNGOS::AsyncSocket<AuthSocket>

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 @@ -94,6 +101,10 @@ class AuthSocket : public MaNGOS::AsyncSocket<AuthSocket>
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 f0f5371

Please sign in to comment.