From 933b50eab528d3d1142d566caefe7e4c48a2de95 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Thu, 5 Feb 2026 12:59:48 -0800 Subject: [PATCH] Use natural BigNum sizes for SRP hash computations TrinityCore/AzerothCore's UpdateBigNumbers uses BN_num_bytes (natural size without padding) when hashing values for u and M1. Our code was using fixed 32-byte padding which produces different hashes when any value (salt, A, B, N) has leading zeros in big-endian representation. --- src/auth/srp.cpp | 46 +++++++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/src/auth/srp.cpp b/src/auth/srp.cpp index c7cc6756..09aca539 100644 --- a/src/auth/srp.cpp +++ b/src/auth/srp.cpp @@ -113,12 +113,13 @@ void SRP::computeSessionKey() { LOG_DEBUG("Computing session key"); // u = H(A | B) - scrambling parameter - std::vector A_bytes = A.toArray(true, 32); // 32 bytes, little-endian - std::vector B_bytes = B.toArray(true, 32); // 32 bytes, little-endian + // Use natural BigNum sizes to match TrinityCore's UpdateBigNumbers behavior + std::vector A_bytes_u = A.toArray(true); + std::vector B_bytes_u = B.toArray(true); std::vector AB; - AB.insert(AB.end(), A_bytes.begin(), A_bytes.end()); - AB.insert(AB.end(), B_bytes.begin(), B_bytes.end()); + AB.insert(AB.end(), A_bytes_u.begin(), A_bytes_u.end()); + AB.insert(AB.end(), B_bytes_u.begin(), B_bytes_u.end()); std::vector u_bytes = Crypto::sha1(AB); u = BigNum(u_bytes, true); @@ -176,8 +177,9 @@ void SRP::computeProofs(const std::string& username) { std::string upperUser = username; std::transform(upperUser.begin(), upperUser.end(), upperUser.begin(), ::toupper); - // Compute H(N) and H(g) - std::vector N_bytes = N.toArray(true, 32); // 32 bytes (256-bit prime) + // Compute H(N) and H(g) using natural BigNum sizes + // This matches TrinityCore/AzerothCore's UpdateBigNumbers behavior + std::vector N_bytes = N.toArray(true); std::vector g_bytes = g.toArray(true); std::vector N_hash = Crypto::sha1(N_bytes); @@ -192,31 +194,33 @@ void SRP::computeProofs(const std::string& username) { // Compute H(username) std::vector user_hash = Crypto::sha1(upperUser); - // Get A, B, and salt as byte arrays - std::vector A_bytes = A.toArray(true, 32); - std::vector B_bytes = B.toArray(true, 32); - std::vector s_bytes = s.toArray(true, 32); + // Get A, B, and salt as byte arrays — natural sizes for hash inputs + std::vector A_bytes = A.toArray(true); + std::vector B_bytes = B.toArray(true); + std::vector s_bytes = s.toArray(true); // M1 = H( H(N)^H(g) | H(I) | s | A | B | K ) std::vector M1_input; - M1_input.insert(M1_input.end(), Ng_xor.begin(), Ng_xor.end()); // 20 bytes - M1_input.insert(M1_input.end(), user_hash.begin(), user_hash.end()); // 20 bytes - M1_input.insert(M1_input.end(), s_bytes.begin(), s_bytes.end()); // 32 bytes - M1_input.insert(M1_input.end(), A_bytes.begin(), A_bytes.end()); // 32 bytes - M1_input.insert(M1_input.end(), B_bytes.begin(), B_bytes.end()); // 32 bytes - M1_input.insert(M1_input.end(), K.begin(), K.end()); // 40 bytes + M1_input.insert(M1_input.end(), Ng_xor.begin(), Ng_xor.end()); + M1_input.insert(M1_input.end(), user_hash.begin(), user_hash.end()); + M1_input.insert(M1_input.end(), s_bytes.begin(), s_bytes.end()); + M1_input.insert(M1_input.end(), A_bytes.begin(), A_bytes.end()); + M1_input.insert(M1_input.end(), B_bytes.begin(), B_bytes.end()); + M1_input.insert(M1_input.end(), K.begin(), K.end()); - M1 = Crypto::sha1(M1_input); // 20 bytes + M1 = Crypto::sha1(M1_input); LOG_DEBUG("Client proof M1 calculated (", M1.size(), " bytes)"); + LOG_DEBUG(" M1 hash input sizes: Ng_xor=20 user=20 s=", s_bytes.size(), + " A=", A_bytes.size(), " B=", B_bytes.size(), " K=", K.size()); // M2 = H( A | M1 | K ) std::vector M2_input; - M2_input.insert(M2_input.end(), A_bytes.begin(), A_bytes.end()); // 32 bytes - M2_input.insert(M2_input.end(), M1.begin(), M1.end()); // 20 bytes - M2_input.insert(M2_input.end(), K.begin(), K.end()); // 40 bytes + M2_input.insert(M2_input.end(), A_bytes.begin(), A_bytes.end()); + M2_input.insert(M2_input.end(), M1.begin(), M1.end()); + M2_input.insert(M2_input.end(), K.begin(), K.end()); - M2 = Crypto::sha1(M2_input); // 20 bytes + M2 = Crypto::sha1(M2_input); LOG_DEBUG("Expected server proof M2 calculated (", M2.size(), " bytes)"); }