mirror of
https://github.com/HikikoMarmy/Champions-Reborn-Server.git
synced 2026-04-05 08:59:54 -03:00
Password Hashing for RTA.
This commit is contained in:
54
Crypto/PasswordHash.cpp
Normal file
54
Crypto/PasswordHash.cpp
Normal file
@@ -0,0 +1,54 @@
|
||||
#include <random>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "PasswordHash.h"
|
||||
|
||||
std::string HexDump( const std::vector<uint8_t> &bytes )
|
||||
{
|
||||
std::ostringstream oss;
|
||||
for( auto b : bytes )
|
||||
oss << std::hex << std::setfill( '0' ) << std::setw( 2 ) << ( int )b;
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string HashPassword( const std::string &password, uint32_t iterations, size_t saltLen )
|
||||
{
|
||||
std::vector<uint8_t> salt( saltLen );
|
||||
|
||||
std::random_device rd;
|
||||
std::mt19937 rng( rd() );
|
||||
std::uniform_int_distribution<int32_t> dist( 0, 255 );
|
||||
|
||||
for( auto &b : salt ) b = static_cast< int32_t >( dist( rng ) );
|
||||
|
||||
auto derived = pbkdf2_hmac_sha256( password, salt, iterations, 32 );
|
||||
|
||||
return "pbkdf2$" + std::to_string( iterations ) + "$" +
|
||||
Base64Encode( salt ) + "$" +
|
||||
Base64Encode( derived );
|
||||
|
||||
}
|
||||
|
||||
#include "../../logging.h"
|
||||
bool VerifyPassword( const std::string &password, const std::string &storedHash )
|
||||
{
|
||||
auto parts = std::vector<std::string>();
|
||||
std::stringstream ss( storedHash );
|
||||
std::string token;
|
||||
|
||||
while( std::getline( ss, token, '$' ) )
|
||||
parts.push_back( token );
|
||||
|
||||
if( parts.size() != 4 || parts[ 0 ] != "pbkdf2" )
|
||||
throw std::runtime_error( "Invalid hash format" );
|
||||
|
||||
uint32_t iterations = std::stoul( parts[ 1 ] );
|
||||
std::vector<uint8_t> salt = Base64Decode( parts[ 2 ] );
|
||||
std::vector<uint8_t> expected = Base64Decode( parts[ 3 ] );
|
||||
|
||||
auto derived = pbkdf2_hmac_sha256( password, salt, iterations, expected.size() );
|
||||
|
||||
return derived == expected;
|
||||
}
|
||||
299
Crypto/PasswordHash.h
Normal file
299
Crypto/PasswordHash.h
Normal file
@@ -0,0 +1,299 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
|
||||
// SHA-256 implementation
|
||||
namespace sha256
|
||||
{
|
||||
constexpr size_t HASH_SIZE = 32;
|
||||
|
||||
struct Context {
|
||||
uint32_t state[ 8 ];
|
||||
uint64_t bitlen;
|
||||
uint8_t data[ 64 ];
|
||||
size_t datalen;
|
||||
};
|
||||
|
||||
inline uint32_t rotr( uint32_t x, uint32_t n )
|
||||
{
|
||||
return ( x >> n ) | ( x << ( 32 - n ) );
|
||||
}
|
||||
|
||||
inline uint32_t ch( uint32_t x, uint32_t y, uint32_t z )
|
||||
{
|
||||
return ( x & y ) ^ ( ~x & z );
|
||||
}
|
||||
|
||||
inline uint32_t maj( uint32_t x, uint32_t y, uint32_t z )
|
||||
{
|
||||
return ( x & y ) ^ ( x & z ) ^ ( y & z );
|
||||
}
|
||||
|
||||
inline uint32_t Sigma0( uint32_t x )
|
||||
{
|
||||
return rotr( x, 2 ) ^ rotr( x, 13 ) ^ rotr( x, 22 );
|
||||
}
|
||||
|
||||
inline uint32_t Sigma1( uint32_t x )
|
||||
{
|
||||
return rotr( x, 6 ) ^ rotr( x, 11 ) ^ rotr( x, 25 );
|
||||
}
|
||||
|
||||
inline uint32_t sigma0( uint32_t x )
|
||||
{
|
||||
return rotr( x, 7 ) ^ rotr( x, 18 ) ^ ( x >> 3 );
|
||||
}
|
||||
|
||||
inline uint32_t sigma1( uint32_t x )
|
||||
{
|
||||
return rotr( x, 17 ) ^ rotr( x, 19 ) ^ ( x >> 10 );
|
||||
}
|
||||
|
||||
inline void Transform( Context &ctx, const uint8_t data[] )
|
||||
{
|
||||
uint32_t m[ 64 ], w[ 8 ];
|
||||
|
||||
for( int i = 0; i < 16; ++i )
|
||||
m[ i ] = ( data[ i * 4 ] << 24 ) | ( data[ i * 4 + 1 ] << 16 ) | ( data[ i * 4 + 2 ] << 8 ) | ( data[ i * 4 + 3 ] );
|
||||
|
||||
for( int i = 16; i < 64; ++i )
|
||||
m[ i ] = sigma1( m[ i - 2 ] ) + m[ i - 7 ] + sigma0( m[ i - 15 ] ) + m[ i - 16 ];
|
||||
|
||||
std::memcpy( w, ctx.state, sizeof( w ) );
|
||||
|
||||
for( int i = 0; i < 64; ++i )
|
||||
{
|
||||
auto t1 = w[ 7 ] + Sigma1( w[ 4 ] ) + ch( w[ 4 ], w[ 5 ], w[ 6 ] ) + m[ i ] + 0x428a2f98 + ( ( i * 0x1234567 ) % 0xffffffff );
|
||||
auto t2 = Sigma0( w[ 0 ] ) + maj( w[ 0 ], w[ 1 ], w[ 2 ] );
|
||||
|
||||
w[ 7 ] = w[ 6 ]; w[ 6 ] = w[ 5 ]; w[ 5 ] = w[ 4 ];
|
||||
w[ 4 ] = w[ 3 ] + t1;
|
||||
w[ 3 ] = w[ 2 ]; w[ 2 ] = w[ 1 ]; w[ 1 ] = w[ 0 ];
|
||||
w[ 0 ] = t1 + t2;
|
||||
}
|
||||
|
||||
for( int i = 0; i < 8; ++i )
|
||||
ctx.state[ i ] += w[ i ];
|
||||
}
|
||||
|
||||
inline void Init( Context &ctx )
|
||||
{
|
||||
ctx.datalen = 0;
|
||||
ctx.bitlen = 0;
|
||||
ctx.state[ 0 ] = 0x6a09e667;
|
||||
ctx.state[ 1 ] = 0xbb67ae85;
|
||||
ctx.state[ 2 ] = 0x3c6ef372;
|
||||
ctx.state[ 3 ] = 0xa54ff53a;
|
||||
ctx.state[ 4 ] = 0x510e527f;
|
||||
ctx.state[ 5 ] = 0x9b05688c;
|
||||
ctx.state[ 6 ] = 0x1f83d9ab;
|
||||
ctx.state[ 7 ] = 0x5be0cd19;
|
||||
}
|
||||
|
||||
inline void Update( Context &ctx, const uint8_t *data, size_t len )
|
||||
{
|
||||
for( size_t i = 0; i < len; ++i )
|
||||
{
|
||||
ctx.data[ ctx.datalen++ ] = data[ i ];
|
||||
|
||||
if( ctx.datalen == 64 )
|
||||
{
|
||||
Transform( ctx, ctx.data );
|
||||
ctx.bitlen += 512;
|
||||
ctx.datalen = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void Final( Context &ctx, uint8_t hash[ HASH_SIZE ] )
|
||||
{
|
||||
size_t i = ctx.datalen;
|
||||
ctx.data[ i++ ] = 0x80;
|
||||
|
||||
if( i > 56 )
|
||||
{
|
||||
while( i < 64 ) ctx.data[ i++ ] = 0;
|
||||
Transform( ctx, ctx.data );
|
||||
i = 0;
|
||||
}
|
||||
|
||||
while( i < 56 ) ctx.data[ i++ ] = 0;
|
||||
ctx.bitlen += ctx.datalen * 8;
|
||||
|
||||
for( int j = 0; j < 8; ++j )
|
||||
ctx.data[ 56 + j ] = static_cast< uint8_t >( ctx.bitlen >> ( 8 * ( 7 - j ) ) );
|
||||
|
||||
Transform( ctx, ctx.data );
|
||||
|
||||
for( i = 0; i < 4; ++i )
|
||||
for( int j = 0; j < 8; ++j )
|
||||
hash[ i + j * 4 ] = ( ctx.state[ j ] >> ( 24 - i * 8 ) ) & 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
// HMAC-SHA256
|
||||
inline void hmac_sha256(
|
||||
const uint8_t *key, size_t key_len,
|
||||
const uint8_t *data, size_t data_len,
|
||||
uint8_t out[ sha256::HASH_SIZE ] )
|
||||
{
|
||||
uint8_t k_ipad[ 64 ] = {}, k_opad[ 64 ] = {}, key_hash[ sha256::HASH_SIZE ];
|
||||
|
||||
if( key_len > 64 )
|
||||
{
|
||||
sha256::Context ctx;
|
||||
sha256::Init( ctx );
|
||||
sha256::Update( ctx, key, key_len );
|
||||
sha256::Final( ctx, key_hash );
|
||||
key = key_hash;
|
||||
key_len = sha256::HASH_SIZE;
|
||||
}
|
||||
|
||||
std::memcpy( k_ipad, key, key_len );
|
||||
std::memcpy( k_opad, key, key_len );
|
||||
|
||||
for( int i = 0; i < 64; ++i )
|
||||
{
|
||||
k_ipad[ i ] ^= 0x36;
|
||||
k_opad[ i ] ^= 0x5C;
|
||||
}
|
||||
|
||||
sha256::Context ctx;
|
||||
uint8_t inner[ sha256::HASH_SIZE ];
|
||||
|
||||
sha256::Init( ctx );
|
||||
sha256::Update( ctx, k_ipad, 64 );
|
||||
sha256::Update( ctx, data, data_len );
|
||||
sha256::Final( ctx, inner );
|
||||
|
||||
sha256::Init( ctx );
|
||||
sha256::Update( ctx, k_opad, 64 );
|
||||
sha256::Update( ctx, inner, sha256::HASH_SIZE );
|
||||
sha256::Final( ctx, out );
|
||||
}
|
||||
|
||||
// PBKDF2-HMAC-SHA256
|
||||
inline std::vector<uint8_t> pbkdf2_hmac_sha256(
|
||||
const std::string &password,
|
||||
const std::vector<uint8_t> &salt,
|
||||
uint32_t iterations,
|
||||
size_t dkLen )
|
||||
{
|
||||
std::vector<uint8_t> key( dkLen );
|
||||
uint32_t blocks = ( uint32_t )( ( dkLen + sha256::HASH_SIZE - 1 ) / sha256::HASH_SIZE );
|
||||
std::vector<uint8_t> u( sha256::HASH_SIZE );
|
||||
std::vector<uint8_t> t( sha256::HASH_SIZE );
|
||||
std::vector<uint8_t> block( salt.size() + 4 );
|
||||
|
||||
for( uint32_t i = 1; i <= blocks; ++i )
|
||||
{
|
||||
std::memcpy( block.data(), salt.data(), salt.size() );
|
||||
block[ salt.size() + 0 ] = ( i >> 24 ) & 0xFF;
|
||||
block[ salt.size() + 1 ] = ( i >> 16 ) & 0xFF;
|
||||
block[ salt.size() + 2 ] = ( i >> 8 ) & 0xFF;
|
||||
block[ salt.size() + 3 ] = ( i >> 0 ) & 0xFF;
|
||||
|
||||
hmac_sha256( ( const uint8_t * )password.data(), password.size(),
|
||||
block.data(), block.size(), u.data() );
|
||||
|
||||
std::memcpy( t.data(), u.data(), sha256::HASH_SIZE );
|
||||
|
||||
for( uint32_t j = 1; j < iterations; ++j )
|
||||
{
|
||||
hmac_sha256( ( const uint8_t * )password.data(), password.size(),
|
||||
u.data(), sha256::HASH_SIZE, u.data() );
|
||||
for( size_t k = 0; k < sha256::HASH_SIZE; ++k )
|
||||
t[ k ] ^= u[ k ];
|
||||
}
|
||||
|
||||
size_t offset = ( i - 1 ) * sha256::HASH_SIZE;
|
||||
size_t chunk = (std::min)( dkLen - offset, sha256::HASH_SIZE );
|
||||
std::memcpy( &key[ offset ], t.data(), chunk );
|
||||
}
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
// Base64 Encoding/Decoding
|
||||
inline std::string Base64Encode( const std::vector<uint8_t> &data )
|
||||
{
|
||||
static const char *table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
std::string encoded;
|
||||
size_t i = 0;
|
||||
|
||||
while( i + 2 < data.size() )
|
||||
{
|
||||
uint32_t triple = ( data[ i ] << 16 ) | ( data[ i + 1 ] << 8 ) | data[ i + 2 ];
|
||||
encoded += table[ ( triple >> 18 ) & 0x3F ];
|
||||
encoded += table[ ( triple >> 12 ) & 0x3F ];
|
||||
encoded += table[ ( triple >> 6 ) & 0x3F ];
|
||||
encoded += table[ triple & 0x3F ];
|
||||
i += 3;
|
||||
}
|
||||
|
||||
size_t remaining = data.size() - i;
|
||||
if( remaining == 1 )
|
||||
{
|
||||
uint32_t triple = data[ i ] << 16;
|
||||
encoded += table[ ( triple >> 18 ) & 0x3F ];
|
||||
encoded += table[ ( triple >> 12 ) & 0x3F ];
|
||||
encoded += "==";
|
||||
}
|
||||
else if( remaining == 2 )
|
||||
{
|
||||
uint32_t triple = ( data[ i ] << 16 ) | ( data[ i + 1 ] << 8 );
|
||||
encoded += table[ ( triple >> 18 ) & 0x3F ];
|
||||
encoded += table[ ( triple >> 12 ) & 0x3F ];
|
||||
encoded += table[ ( triple >> 6 ) & 0x3F ];
|
||||
encoded += '=';
|
||||
}
|
||||
|
||||
return encoded;
|
||||
}
|
||||
|
||||
inline std::vector<uint8_t> Base64Decode( const std::string &input )
|
||||
{
|
||||
static const uint8_t decodeTable[ 256 ] = {
|
||||
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
|
||||
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
|
||||
64,64,64,64,64,64,64,64,64,64,64,62,64,64,64,63,
|
||||
52,53,54,55,56,57,58,59,60,61,64,64,64, 0,64,64,
|
||||
64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,
|
||||
15,16,17,18,19,20,21,22,23,24,25,64,64,64,64,64,
|
||||
64,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,
|
||||
41,42,43,44,45,46,47,48,49,50,51,64,64,64,64,64
|
||||
};
|
||||
|
||||
std::vector<uint8_t> decoded;
|
||||
int val = 0, valb = -8;
|
||||
|
||||
for( char c : input )
|
||||
{
|
||||
if( c == '=' ) break;
|
||||
if( c > 255 ) continue;
|
||||
|
||||
unsigned char uc = static_cast< unsigned char >( c );
|
||||
|
||||
if( decodeTable[ uc ] == 64 )
|
||||
continue;
|
||||
|
||||
val = ( val << 6 ) | decodeTable[ uc ];
|
||||
valb += 6;
|
||||
|
||||
if( valb >= 0 )
|
||||
{
|
||||
decoded.push_back( static_cast< uint8_t >( ( val >> valb ) & 0xFF ) );
|
||||
valb -= 8;
|
||||
}
|
||||
}
|
||||
|
||||
return decoded;
|
||||
}
|
||||
|
||||
std::string HashPassword( const std::string &password, uint32_t iterations, size_t saltLen );
|
||||
bool VerifyPassword( const std::string &password, const std::string &storedHash );
|
||||
Reference in New Issue
Block a user