122 lines
3.6 KiB
C++
122 lines
3.6 KiB
C++
/***
|
|
Copyright (C) 2022 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
|
|
|
File: BCrypt.cpp
|
|
Date: 2022-9-15
|
|
Author: Reece
|
|
***/
|
|
#include <Source/RuntimeInternal.hpp>
|
|
#include "BCrypt.hpp"
|
|
|
|
extern "C"
|
|
{
|
|
#include "crypt_blowfish.h"
|
|
}
|
|
|
|
namespace Aurora::Crypto::BCrypt
|
|
{
|
|
static const int kMinBCryptRounds = 10;
|
|
|
|
AUKN_SYM int GetForcedMinRounds()
|
|
{
|
|
return kMinBCryptRounds;
|
|
}
|
|
|
|
AUKN_SYM AuString GenSalt(int rounds)
|
|
{
|
|
char seed[16];
|
|
char saltBuffer[31];
|
|
|
|
AuRng::RngFillArray<false>(seed);
|
|
|
|
rounds = AuMin(rounds, 31);
|
|
rounds = AuMax(rounds, kMinBCryptRounds);
|
|
auto ptr = _crypt_gensalt_blowfish_rn("$2a$",
|
|
rounds,
|
|
seed,
|
|
16,
|
|
saltBuffer,
|
|
AuArraySize(saltBuffer));
|
|
SysAssert(ptr, "?");
|
|
return ptr;
|
|
}
|
|
|
|
AUKN_SYM AuString HashPassword(const AuString &password, const AuString &salt)
|
|
{
|
|
return HashPasswordEx({ password.c_str(), password.size() + 1 }, salt);
|
|
}
|
|
|
|
AUKN_SYM AuString HashPasswordEx(const Memory::MemoryViewRead &password, const AuString &salt)
|
|
{
|
|
if (password.length > 72)
|
|
{
|
|
SysPushErrorCrypt("BCrypt password cannot be greater than 72 characters");
|
|
return {};
|
|
}
|
|
|
|
char cryptBuffer[62] { 0 };
|
|
char *pRet;
|
|
if (!(pRet = crypt_rn(password.Begin<char>(),
|
|
password.length,
|
|
salt.c_str(),
|
|
cryptBuffer,
|
|
AuArraySize(cryptBuffer))))
|
|
{
|
|
SysPushErrorCrypto("bcrypt failed");
|
|
return {};
|
|
}
|
|
|
|
return pRet;
|
|
}
|
|
|
|
|
|
AUKN_SYM AuString HashPasswordSafer(AuString &&password, const AuString &salt)
|
|
{
|
|
auto ret = HashPassword(password, salt);
|
|
AuMemset(password.data(), 0, password.size());
|
|
return ret;
|
|
}
|
|
|
|
AUKN_SYM bool CheckPassword(const AuString &password, const AuString &hashedPassword)
|
|
{
|
|
return CheckPasswordEx({ password.c_str(), password.size() + 1 }, hashedPassword);
|
|
}
|
|
|
|
AUKN_SYM bool CheckPasswordEx(const Memory::MemoryViewRead &password, const AuString &hashedPassword)
|
|
{
|
|
if (password.length == 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (hashedPassword.empty())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
char cryptBuffer[62] { 0 };
|
|
char *pRet;
|
|
if (!(pRet = crypt_rn(password.Begin<char>(),
|
|
password.length,
|
|
hashedPassword.c_str(),
|
|
cryptBuffer,
|
|
AuArraySize(cryptBuffer))))
|
|
{
|
|
SysPushErrorCrypto("bcrypt failed");
|
|
return {};
|
|
}
|
|
|
|
// I don't care about safe eval...
|
|
// I refuse to believe side channel attacks are possible outside of circlejerk academic settings.
|
|
// Especially with vectorization optimizations of memcpy. Even if you could tell how quickly we fail,
|
|
// it's highly unlikely you could backfeed this into bcrypt to reguess a similar digest string.
|
|
return cryptBuffer == hashedPassword;
|
|
}
|
|
|
|
AUKN_SYM bool CheckPasswordSafer(AuString &&password, const AuString &hashedPassword)
|
|
{
|
|
auto ret = CheckPassword(password, hashedPassword);
|
|
AuMemset(password.data(), 0, password.size());
|
|
return ret;
|
|
}
|
|
} |