mirror of
https://github.com/HikikoMarmy/Champions-Reborn-Server.git
synced 2026-04-05 00:49:48 -03:00
Reorganized and cleaned up the solution.
This commit is contained in:
53
Source/Crypto/PasswordHash.cpp
Normal file
53
Source/Crypto/PasswordHash.cpp
Normal file
@@ -0,0 +1,53 @@
|
||||
#include <random>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "Crypto/PasswordHash.hpp"
|
||||
|
||||
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 );
|
||||
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
163
Source/Crypto/RealmCrypt.cpp
Normal file
163
Source/Crypto/RealmCrypt.cpp
Normal file
@@ -0,0 +1,163 @@
|
||||
|
||||
#include "Crypto/RealmCrypt.hpp"
|
||||
#include "Common/Utility.hpp"
|
||||
|
||||
RealmCrypt::RealmCrypt()
|
||||
{
|
||||
std::srand( static_cast< unsigned >( std::time( nullptr ) ) );
|
||||
}
|
||||
|
||||
std::vector< uint8_t > RealmCrypt::generateSymmetricKey( void )
|
||||
{
|
||||
constexpr size_t KEY_LENGTH = 32;
|
||||
|
||||
std::vector< uint8_t > keyData( KEY_LENGTH, 0 );
|
||||
|
||||
// Generate 32 random bytes
|
||||
for( size_t i = 0; i < KEY_LENGTH; ++i )
|
||||
{
|
||||
keyData[ i ] = static_cast< uint8_t >( std::rand() % 255 );
|
||||
}
|
||||
|
||||
return keyData;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> RealmCrypt::getSymmetricKey( void )
|
||||
{
|
||||
return default_sym_key;
|
||||
}
|
||||
|
||||
std::string RealmCrypt::encryptString( std::string &input )
|
||||
{
|
||||
if( input.size() % 16 != 0 )
|
||||
{
|
||||
input.append( 16 - ( input.size() % 16 ), '\0' );
|
||||
}
|
||||
|
||||
rijndael aes;
|
||||
|
||||
auto result = aes.EncryptECB(
|
||||
reinterpret_cast< const uint8_t * >( input.c_str() ),
|
||||
static_cast< uint32_t >( input.size() ),
|
||||
default_sym_key.data()
|
||||
);
|
||||
|
||||
return std::string( reinterpret_cast< const char * >( result ), input.size() );
|
||||
}
|
||||
|
||||
std::string RealmCrypt::decryptString( std::string &input )
|
||||
{
|
||||
if( input.size() % 16 != 0 )
|
||||
{
|
||||
input.append( 16 - ( input.size() % 16 ), '\0' );
|
||||
}
|
||||
|
||||
rijndael aes;
|
||||
|
||||
auto result = aes.DecryptECB( reinterpret_cast< const uint8_t * >(
|
||||
input.c_str() ),
|
||||
static_cast< uint32_t >( input.size() ),
|
||||
default_sym_key.data()
|
||||
);
|
||||
|
||||
return std::string( reinterpret_cast< const char * >( result ), input.size() );
|
||||
}
|
||||
|
||||
std::vector<uint8_t> RealmCrypt::encryptString( const std::wstring &input )
|
||||
{
|
||||
// Convert UTF-16 string to raw bytes
|
||||
std::vector<uint8_t> utf16Bytes;
|
||||
utf16Bytes.reserve( input.size() * 2 );
|
||||
for( wchar_t ch : input )
|
||||
{
|
||||
utf16Bytes.push_back( static_cast< uint8_t >( ch & 0xFF ) );
|
||||
utf16Bytes.push_back( static_cast< uint8_t >( ( ch >> 8 ) & 0xFF ) );
|
||||
}
|
||||
|
||||
// Pad to nearest 16 bytes
|
||||
if( utf16Bytes.size() % 16 != 0 )
|
||||
{
|
||||
utf16Bytes.resize( ( utf16Bytes.size() / 16 + 1 ) * 16, 0 );
|
||||
}
|
||||
|
||||
// Encrypt using AES ECB
|
||||
rijndael aes;
|
||||
auto encrypted = aes.EncryptECB(
|
||||
reinterpret_cast< const uint8_t * >( utf16Bytes.data() ),
|
||||
static_cast< uint32_t >( utf16Bytes.size() ),
|
||||
default_sym_key.data()
|
||||
);
|
||||
|
||||
// Return the raw encrypted bytes
|
||||
return std::vector<uint8_t>( encrypted, encrypted + utf16Bytes.size() );
|
||||
}
|
||||
|
||||
std::wstring RealmCrypt::decryptString( std::vector<uint8_t> &input )
|
||||
{
|
||||
// Ensure input is a multiple of 16 bytes
|
||||
if( input.size() % 16 != 0 )
|
||||
{
|
||||
input.resize( ( input.size() / 16 + 1 ) * 16, 0 );
|
||||
}
|
||||
|
||||
rijndael aes;
|
||||
|
||||
auto result = aes.DecryptECB(
|
||||
reinterpret_cast< const uint8_t * >( input.data() ),
|
||||
static_cast< uint32_t >( input.size() ),
|
||||
default_sym_key.data()
|
||||
);
|
||||
|
||||
// Convert decrypted bytes back into a wstring
|
||||
std::wstring output;
|
||||
output.reserve( input.size() / 2 );
|
||||
|
||||
for( size_t i = 0; i + 1 < input.size(); i += 2 )
|
||||
{
|
||||
uint16_t ch = static_cast< uint16_t >( input[ i ] | ( input[ i + 1 ] << 8 ) );
|
||||
if( ch == 0 ) break; // Optional: stop at null terminator
|
||||
output.push_back( static_cast< wchar_t >( ch ) );
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
std::vector< uint8_t > RealmCrypt::encryptSymmetric( std::span< const uint8_t > input )
|
||||
{
|
||||
if( input.size() % 16 != 0 )
|
||||
{
|
||||
std::vector< uint8_t > paddedInput( input.begin(), input.end() );
|
||||
paddedInput.resize( ( ( input.size() / 16 ) + 1 ) * 16, 0 );
|
||||
input = paddedInput;
|
||||
}
|
||||
|
||||
rijndael aes;
|
||||
|
||||
auto result = aes.EncryptECB( reinterpret_cast< const uint8_t * >(
|
||||
input.data() ),
|
||||
static_cast< uint32_t >( input.size() ),
|
||||
default_sym_key.data()
|
||||
);
|
||||
|
||||
return std::vector< uint8_t >( result, result + input.size() );
|
||||
}
|
||||
|
||||
std::vector< uint8_t > RealmCrypt::decryptSymmetric( std::span< const uint8_t > input )
|
||||
{
|
||||
if( input.size() % 16 != 0 )
|
||||
{
|
||||
std::vector< uint8_t > paddedInput( input.begin(), input.end() );
|
||||
paddedInput.resize( ( ( input.size() / 16 ) + 1 ) * 16, 0 );
|
||||
input = paddedInput;
|
||||
}
|
||||
|
||||
rijndael aes;
|
||||
|
||||
auto result = aes.DecryptECB( reinterpret_cast< const uint8_t * >(
|
||||
input.data() ),
|
||||
static_cast< uint32_t >( input.size() ),
|
||||
default_sym_key.data()
|
||||
);
|
||||
|
||||
return std::vector< uint8_t >( result, result + input.size() );
|
||||
}
|
||||
371
Source/Crypto/rijndael.cpp
Normal file
371
Source/Crypto/rijndael.cpp
Normal file
@@ -0,0 +1,371 @@
|
||||
#include "Crypto/rijndael.hpp"
|
||||
|
||||
rijndael::rijndael()
|
||||
{
|
||||
this->Nk = 8;
|
||||
this->Nr = 14;
|
||||
}
|
||||
|
||||
uint8_t *rijndael::EncryptECB( const uint8_t in[], uint32_t inLen,
|
||||
const uint8_t key[] )
|
||||
{
|
||||
CheckLength( inLen );
|
||||
uint8_t *out = new uint8_t[ inLen ];
|
||||
uint8_t *roundKeys = new uint8_t[ 4 * Nb * ( Nr + 1 ) ];
|
||||
KeyExpansion( key, roundKeys );
|
||||
for( uint32_t i = 0; i < inLen; i += blockBytesLen )
|
||||
{
|
||||
EncryptBlock( in + i, out + i, roundKeys );
|
||||
}
|
||||
|
||||
delete[] roundKeys;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
uint8_t *rijndael::DecryptECB( const uint8_t in[], uint32_t inLen,
|
||||
const uint8_t key[] )
|
||||
{
|
||||
CheckLength( inLen );
|
||||
uint8_t *out = new uint8_t[ inLen ];
|
||||
uint8_t *roundKeys = new uint8_t[ 4 * Nb * ( Nr + 1 ) ];
|
||||
KeyExpansion( key, roundKeys );
|
||||
|
||||
for( uint32_t i = 0; i < inLen; i += blockBytesLen )
|
||||
{
|
||||
DecryptBlock( in + i, out + i, roundKeys );
|
||||
}
|
||||
|
||||
delete[] roundKeys;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void rijndael::CheckLength( uint32_t len )
|
||||
{
|
||||
if( len % blockBytesLen != 0 )
|
||||
{
|
||||
throw std::length_error( "Plaintext length must be divisible by " +
|
||||
std::to_string( blockBytesLen ) );
|
||||
}
|
||||
}
|
||||
|
||||
void rijndael::EncryptBlock( const uint8_t in[], uint8_t out[],
|
||||
uint8_t *roundKeys )
|
||||
{
|
||||
uint8_t state[ 4 ][ Nb ];
|
||||
uint32_t i, j, round;
|
||||
|
||||
for( i = 0; i < 4; i++ )
|
||||
{
|
||||
for( j = 0; j < Nb; j++ )
|
||||
{
|
||||
state[ i ][ j ] = in[ i + 4 * j ];
|
||||
}
|
||||
}
|
||||
|
||||
AddRoundKey( state, roundKeys );
|
||||
|
||||
for( round = 1; round <= Nr - 1; round++ )
|
||||
{
|
||||
SubBytes( state );
|
||||
ShiftRows( state );
|
||||
MixColumns( state );
|
||||
AddRoundKey( state, roundKeys + round * 4 * Nb );
|
||||
}
|
||||
|
||||
SubBytes( state );
|
||||
ShiftRows( state );
|
||||
AddRoundKey( state, roundKeys + Nr * 4 * Nb );
|
||||
|
||||
for( i = 0; i < 4; i++ )
|
||||
{
|
||||
for( j = 0; j < Nb; j++ )
|
||||
{
|
||||
out[ i + 4 * j ] = state[ i ][ j ];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void rijndael::DecryptBlock( const uint8_t in[], uint8_t out[],
|
||||
uint8_t *roundKeys )
|
||||
{
|
||||
uint8_t state[ 4 ][ Nb ];
|
||||
uint32_t i, j, round;
|
||||
|
||||
for( i = 0; i < 4; i++ )
|
||||
{
|
||||
for( j = 0; j < Nb; j++ )
|
||||
{
|
||||
state[ i ][ j ] = in[ i + 4 * j ];
|
||||
}
|
||||
}
|
||||
|
||||
AddRoundKey( state, roundKeys + Nr * 4 * Nb );
|
||||
|
||||
for( round = Nr - 1; round >= 1; round-- )
|
||||
{
|
||||
InvSubBytes( state );
|
||||
InvShiftRows( state );
|
||||
AddRoundKey( state, roundKeys + round * 4 * Nb );
|
||||
InvMixColumns( state );
|
||||
}
|
||||
|
||||
InvSubBytes( state );
|
||||
InvShiftRows( state );
|
||||
AddRoundKey( state, roundKeys );
|
||||
|
||||
for( i = 0; i < 4; i++ )
|
||||
{
|
||||
for( j = 0; j < Nb; j++ )
|
||||
{
|
||||
out[ i + 4 * j ] = state[ i ][ j ];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void rijndael::SubBytes( uint8_t state[ 4 ][ Nb ] )
|
||||
{
|
||||
uint32_t i, j;
|
||||
uint8_t t;
|
||||
for( i = 0; i < 4; i++ )
|
||||
{
|
||||
for( j = 0; j < Nb; j++ )
|
||||
{
|
||||
t = state[ i ][ j ];
|
||||
state[ i ][ j ] = sbox[ t / 16 ][ t % 16 ];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void rijndael::ShiftRow( uint8_t state[ 4 ][ Nb ], uint32_t i,
|
||||
uint32_t n ) // shift row i on n write_positions
|
||||
{
|
||||
uint8_t tmp[ Nb ];
|
||||
for( uint32_t j = 0; j < Nb; j++ )
|
||||
{
|
||||
tmp[ j ] = state[ i ][ ( j + n ) % Nb ];
|
||||
}
|
||||
memcpy( state[ i ], tmp, Nb * sizeof( uint8_t ) );
|
||||
}
|
||||
|
||||
void rijndael::ShiftRows( uint8_t state[ 4 ][ Nb ] )
|
||||
{
|
||||
ShiftRow( state, 1, 1 );
|
||||
ShiftRow( state, 2, 2 );
|
||||
ShiftRow( state, 3, 3 );
|
||||
}
|
||||
|
||||
uint8_t rijndael::xtime( uint8_t b ) // multiply on x
|
||||
{
|
||||
return ( b << 1 ) ^ ( ( ( b >> 7 ) & 1 ) * 0x1b );
|
||||
}
|
||||
|
||||
void rijndael::MixColumns( uint8_t state[ 4 ][ Nb ] )
|
||||
{
|
||||
uint8_t temp_state[ 4 ][ Nb ];
|
||||
|
||||
for( size_t i = 0; i < 4; ++i )
|
||||
{
|
||||
memset( temp_state[ i ], 0, 4 );
|
||||
}
|
||||
|
||||
for( size_t i = 0; i < 4; ++i )
|
||||
{
|
||||
for( size_t k = 0; k < 4; ++k )
|
||||
{
|
||||
for( size_t j = 0; j < 4; ++j )
|
||||
{
|
||||
if( CMDS[ i ][ k ] == 1 )
|
||||
temp_state[ i ][ j ] ^= state[ k ][ j ];
|
||||
else
|
||||
temp_state[ i ][ j ] ^= GF_MUL_TABLE[ CMDS[ i ][ k ] ][ state[ k ][ j ] ];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for( size_t i = 0; i < 4; ++i )
|
||||
{
|
||||
memcpy( state[ i ], temp_state[ i ], 4 );
|
||||
}
|
||||
}
|
||||
|
||||
void rijndael::AddRoundKey( uint8_t state[ 4 ][ Nb ], uint8_t *key )
|
||||
{
|
||||
uint32_t i, j;
|
||||
for( i = 0; i < 4; i++ )
|
||||
{
|
||||
for( j = 0; j < Nb; j++ )
|
||||
{
|
||||
state[ i ][ j ] = state[ i ][ j ] ^ key[ i + 4 * j ];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void rijndael::SubWord( uint8_t *a )
|
||||
{
|
||||
int i;
|
||||
for( i = 0; i < 4; i++ )
|
||||
{
|
||||
a[ i ] = sbox[ a[ i ] / 16 ][ a[ i ] % 16 ];
|
||||
}
|
||||
}
|
||||
|
||||
void rijndael::RotWord( uint8_t *a )
|
||||
{
|
||||
uint8_t c = a[ 0 ];
|
||||
a[ 0 ] = a[ 1 ];
|
||||
a[ 1 ] = a[ 2 ];
|
||||
a[ 2 ] = a[ 3 ];
|
||||
a[ 3 ] = c;
|
||||
}
|
||||
|
||||
void rijndael::XorWords( uint8_t *a, uint8_t *b, uint8_t *c )
|
||||
{
|
||||
int i;
|
||||
for( i = 0; i < 4; i++ )
|
||||
{
|
||||
c[ i ] = a[ i ] ^ b[ i ];
|
||||
}
|
||||
}
|
||||
|
||||
void rijndael::Rcon( uint8_t *a, uint32_t n )
|
||||
{
|
||||
uint32_t i;
|
||||
uint8_t c = 1;
|
||||
for( i = 0; i < n - 1; i++ )
|
||||
{
|
||||
c = xtime( c );
|
||||
}
|
||||
|
||||
a[ 0 ] = c;
|
||||
a[ 1 ] = a[ 2 ] = a[ 3 ] = 0;
|
||||
}
|
||||
|
||||
void rijndael::KeyExpansion( const uint8_t key[], uint8_t w[] )
|
||||
{
|
||||
uint8_t temp[ 4 ];
|
||||
uint8_t rcon[ 4 ];
|
||||
|
||||
uint32_t i = 0;
|
||||
while( i < 4 * Nk )
|
||||
{
|
||||
w[ i ] = key[ i ];
|
||||
i++;
|
||||
}
|
||||
|
||||
i = 4 * Nk;
|
||||
while( i < 4 * Nb * ( Nr + 1 ) )
|
||||
{
|
||||
temp[ 0 ] = w[ i - 4 + 0 ];
|
||||
temp[ 1 ] = w[ i - 4 + 1 ];
|
||||
temp[ 2 ] = w[ i - 4 + 2 ];
|
||||
temp[ 3 ] = w[ i - 4 + 3 ];
|
||||
|
||||
if( i / 4 % Nk == 0 )
|
||||
{
|
||||
RotWord( temp );
|
||||
SubWord( temp );
|
||||
Rcon( rcon, i / ( Nk * 4 ) );
|
||||
XorWords( temp, rcon, temp );
|
||||
}
|
||||
else if( Nk > 6 && i / 4 % Nk == 4 )
|
||||
{
|
||||
SubWord( temp );
|
||||
}
|
||||
|
||||
w[ i + 0 ] = w[ i - 4 * Nk ] ^ temp[ 0 ];
|
||||
w[ i + 1 ] = w[ i + 1 - 4 * Nk ] ^ temp[ 1 ];
|
||||
w[ i + 2 ] = w[ i + 2 - 4 * Nk ] ^ temp[ 2 ];
|
||||
w[ i + 3 ] = w[ i + 3 - 4 * Nk ] ^ temp[ 3 ];
|
||||
i += 4;
|
||||
}
|
||||
}
|
||||
|
||||
void rijndael::InvSubBytes( uint8_t state[ 4 ][ Nb ] )
|
||||
{
|
||||
uint32_t i, j;
|
||||
uint8_t t;
|
||||
for( i = 0; i < 4; i++ )
|
||||
{
|
||||
for( j = 0; j < Nb; j++ )
|
||||
{
|
||||
t = state[ i ][ j ];
|
||||
state[ i ][ j ] = inv_sbox[ t / 16 ][ t % 16 ];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void rijndael::InvMixColumns( uint8_t state[ 4 ][ Nb ] )
|
||||
{
|
||||
uint8_t temp_state[ 4 ][ Nb ];
|
||||
|
||||
for( size_t i = 0; i < 4; ++i )
|
||||
{
|
||||
memset( temp_state[ i ], 0, 4 );
|
||||
}
|
||||
|
||||
for( size_t i = 0; i < 4; ++i )
|
||||
{
|
||||
for( size_t k = 0; k < 4; ++k )
|
||||
{
|
||||
for( size_t j = 0; j < 4; ++j )
|
||||
{
|
||||
temp_state[ i ][ j ] ^= GF_MUL_TABLE[ INV_CMDS[ i ][ k ] ][ state[ k ][ j ] ];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for( size_t i = 0; i < 4; ++i )
|
||||
{
|
||||
memcpy( state[ i ], temp_state[ i ], 4 );
|
||||
}
|
||||
}
|
||||
|
||||
void rijndael::InvShiftRows( uint8_t state[ 4 ][ Nb ] )
|
||||
{
|
||||
ShiftRow( state, 1, Nb - 1 );
|
||||
ShiftRow( state, 2, Nb - 2 );
|
||||
ShiftRow( state, 3, Nb - 3 );
|
||||
}
|
||||
|
||||
void rijndael::XorBlocks( const uint8_t *a, const uint8_t *b,
|
||||
uint8_t *c, uint32_t len )
|
||||
{
|
||||
for( uint32_t i = 0; i < len; i++ )
|
||||
{
|
||||
c[ i ] = a[ i ] ^ b[ i ];
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<uint8_t> rijndael::ArrayToVector( uint8_t *a,
|
||||
uint32_t len )
|
||||
{
|
||||
std::vector<uint8_t> v( a, a + len * sizeof( uint8_t ) );
|
||||
return v;
|
||||
}
|
||||
|
||||
uint8_t *rijndael::VectorToArray( std::vector<uint8_t> &a )
|
||||
{
|
||||
return a.data();
|
||||
}
|
||||
|
||||
std::vector<uint8_t> rijndael::EncryptECB( std::vector<uint8_t> in,
|
||||
std::vector<uint8_t> key )
|
||||
{
|
||||
uint8_t *out = EncryptECB( VectorToArray( in ), ( uint32_t )in.size(),
|
||||
VectorToArray( key ) );
|
||||
std::vector<uint8_t> v = ArrayToVector( out, static_cast< uint32_t >( in.size() ) );
|
||||
delete[] out;
|
||||
return v;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> rijndael::DecryptECB( std::vector<uint8_t> in,
|
||||
std::vector<uint8_t> key )
|
||||
{
|
||||
uint8_t *out = DecryptECB( VectorToArray( in ), ( uint32_t )in.size(),
|
||||
VectorToArray( key ) );
|
||||
std::vector<uint8_t> v = ArrayToVector( out, ( uint32_t )in.size() );
|
||||
delete[] out;
|
||||
return v;
|
||||
}
|
||||
Reference in New Issue
Block a user