/*** Copyright (C) 2022 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: BCrypt.cpp Date: 2022-9-15 Author: Reece ***/ #include #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(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(), 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(), 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; } }