From 336c40d8097eefa0d2136b4020dead8731387323 Mon Sep 17 00:00:00 2001 From: malloc Date: Wed, 21 Mar 2018 17:07:30 -0500 Subject: [PATCH] IT WAS ALL IN HIS HEAD --- server/src/crypto/bcrypt.cpp | 58 ++++++++++++++++---- server/src/crypto/bcrypt.hpp | 6 ++- server/src/crypto/bfish.hpp | 3 +- server/src/main.cpp | 9 +++- server/src/utils/bigint.cpp | 100 +++++++++++++++++++++++++++++++++++ server/src/utils/bigint.hpp | 53 +++++++++++++++---- 6 files changed, 206 insertions(+), 23 deletions(-) diff --git a/server/src/crypto/bcrypt.cpp b/server/src/crypto/bcrypt.cpp index aceb736..cf87337 100644 --- a/server/src/crypto/bcrypt.cpp +++ b/server/src/crypto/bcrypt.cpp @@ -5,18 +5,20 @@ static const uint32_t bcrypt_ciphertext[6] = { 0x64657253, 0x63727944, 0x6f756274 }; -std::string sosc::cgc::bcrypt_hash(const std::string& input) { - std::string salt = csprng::next_bytes(16), - input_t = input.substr(0, 72); +std::string sosc::cgc::bcrypt_hash_raw + (const std::string& input, const std::string& salt, uint32_t cost) +{ + std::string salt_t = salt.substr(0, 16), + input_t = input.substr(0, 72); uint32_t ctext[6]; std::memcpy(ctext, bcrypt_ciphertext, 6 * sizeof(uint32_t)); Blowfish bfish; - bfish.SetEksKey(salt, input); - for(int i = 0; i < BCRYPT_COST; ++i) { - bfish.SetKey(input.c_str(), input.length()); - bfish.SetKey(salt.c_str(), salt.length()); + bfish.SetEksKey(salt_t, input_t); + for(uint32_t i = 0; i < 1 << (cost % 32); ++i) { + bfish.SetKey(input_t.c_str(), input_t.length()); + bfish.SetKey(salt_t.c_str(), salt_t.length()); } for(int i = 0; i < 64; ++i) @@ -33,9 +35,47 @@ std::string sosc::cgc::bcrypt_hash(const std::string& input) { std::stringstream stream; stream << "$2b$" - << (BCRYPT_COST < 10 ? "0" : "") << BCRYPT_COST - << "$" << base64_encode(salt, true) + << (cost < 10 ? "0" : "") << cost + << "$" << base64_encode(salt_t, true) << base64_encode(ct_hash, true); return stream.str(); } + +std::string sosc::cgc::bcrypt_hash(const std::string& input) { + return bcrypt_hash_raw(input, sosc::csprng::next_bytes(16), BCRYPT_COST); +} + +bool sosc::cgc::bcrypt_check + (const std::string& input, const std::string& hash) +{ + if(hash.length() != 60 && !str::starts(hash, "$2b$")) + return false; + + auto parts = str::split(hash, '$'); + if(parts.size() != 4) + return false; + parts.erase(parts.begin()); + + if(parts[1].length() != 2) + return false; + + int cost; + try { + cost = std::stoi(parts[1], 0, 10); + } catch(...) { + return false; + } + + std::string salt = parts[2].substr(0, 22), + input_t = input.substr(0, 72), + hash_chk = bcrypt_hash_raw(input_t, base64_decode(salt, true), cost); + + // timing safe compare DO NOT OPTIMIZE + bool success = true; + for(int i = 0; i < hash.length(); ++i) + if(hash[i] != hash_chk[i]) + success = false; + + return success; +} diff --git a/server/src/crypto/bcrypt.hpp b/server/src/crypto/bcrypt.hpp index 789421c..931809b 100644 --- a/server/src/crypto/bcrypt.hpp +++ b/server/src/crypto/bcrypt.hpp @@ -7,13 +7,17 @@ #include "base64.hpp" #include "bfish.hpp" #include "../utils/csprng.hpp" +#include "../utils/string.hpp" #define BCRYPT_COST 10 namespace sosc { namespace cgc { std::string bcrypt_hash(const std::string& input); -bool bcrypt_check(std::string input, std::string hash); +std::string bcrypt_hash_raw + (const std::string& input, const std::string& salt, uint32_t cost); + +bool bcrypt_check(const std::string& input, const std::string& hash); }} #endif diff --git a/server/src/crypto/bfish.hpp b/server/src/crypto/bfish.hpp index 5ac7762..68842d8 100644 --- a/server/src/crypto/bfish.hpp +++ b/server/src/crypto/bfish.hpp @@ -25,7 +25,8 @@ private: uint32_t parr[18]; uint32_t sbox[4][256]; - friend std::string sosc::cgc::bcrypt_hash(const std::string&); + friend std::string sosc::cgc::bcrypt_hash_raw + (const std::string& input, const std::string& salt, uint32_t cost); }; }} diff --git a/server/src/main.cpp b/server/src/main.cpp index 9d9f72f..24c3144 100644 --- a/server/src/main.cpp +++ b/server/src/main.cpp @@ -10,6 +10,7 @@ #include "crypto/bfish.hpp" #include "utils/csprng.hpp" #include "crypto/bcrypt.hpp" +#include "utils/bigint.hpp" int main(int argc, char **argv) { //auto sock = sosc::TcpClient(); @@ -43,8 +44,12 @@ int main(int argc, char **argv) { uint32_t teest = sosc::csprng::next(); std::cout << std::hex << teest;*/ - std::string hash = sosc::cgc::bcrypt_hash("test pwd"); - std::cout << hash; + /*std::string hash = sosc::cgc::bcrypt_hash("test pwd"); + std::cout << hash << std::endl; + std::cout << sosc::cgc::bcrypt_check("test pwd", hash);*/ + + sosc::BigUInt bi; + bi.Parse("abcdef0", 10); return 0; } diff --git a/server/src/utils/bigint.cpp b/server/src/utils/bigint.cpp index 29e279c..255822c 100644 --- a/server/src/utils/bigint.cpp +++ b/server/src/utils/bigint.cpp @@ -1,2 +1,102 @@ #include "bigint.hpp" +static bool is_hex_char(char c) { + return ((c >= 'a') && (c <= 'f')) + || ((c >= 'A') && (c <= 'F')) + || ((c >= '0') && (c <= '9')); +} + +sosc::BigUInt::BigUInt() { + this->value = { 0 }; +} + +sosc::BigUInt::BigUInt(std::string hex_str, int byte_count) { + this->Parse(hex_str, byte_count); +} + +bool sosc::BigUInt::Parse(std::string hex_str, int byte_count) { + if(hex_str.length() == 0) + hex_str = "00"; + if(hex_str.length() % 2 != 0) + hex_str.insert(hex_str.begin(), '0'); + + int str_byte_count = hex_str.length() / 2; + byte_count = std::max(byte_count, str_byte_count); + + this->value = std::vector(byte_count, 0); + for(int i = 0; i < hex_str.length(); i += 2) { + if(!is_hex_char(hex_str[i]) || !is_hex_char(hex_str[i + 1])) + return false; + + this->value[str_byte_count - (i / 2) - 1] + = std::stoi(hex_str.substr(i, 2), 0, 16); + } + + return true; +} + +void sosc::BigUInt::Random(int byte_count) { + this->value = std::vector(byte_count, 0); + std::string random_str = csprng::next_bytes(byte_count); + for(int i = 0; i < byte_count; ++i) + this->value[i] = random_str[i]; +} + +void sosc::BigUInt::RandomPrime(int byte_count) { + do { + this->Random(byte_count); + this->value[0] |= 0x01; + this->value[byte_count - 1] |= 0x80; + } while(!this->IsProbablePrime()); +} + +bool sosc::BigUInt::IsProbablePrime() const { + // TODO rabin-miller +} + +std::tuple sosc::BigUInt::DivideWithRemainder + (const BigUInt& num, const BigUInt& denom) +{ + if(num.IsZero()) + return std::make_tuple(BigUInt(), BigUInt()); + if(denom.IsZero()) + throw "BigUInt division by zero"; + if(denom.IsOne()) + return std::make_tuple(num, BigUInt()); + if(denom > num) + return std::make_tuple(BigUInt(), num); + + BigUInt quotient, remainder; + for(uint64_t i = num.ByteCount() * 8 - 1;; --i) { + remainder = remainder << 1; + remainder.SetBit(0, num.GetBit(i)); + + if(remainder >= denom) { + remainder -= denom; + quotient.SetBit(i, true); + } + + if(i == 0) + break; + } + + return std::make_tuple(quotient, remainder); +} + +sosc::BigUInt sosc::BigUInt::ModPow + (const BigUInt& base, const BigUInt& exp, const BigUInt& mod) +{ + BigUInt accum("1"); + BigUInt x = exp; + BigUInt bpow = base; + + while(!x.IsZero()) { + if(!x.IsEven()) + accum = (accum * bpow) % mod; + + x = x >> 1; + bpow = (bpow * bpow) % mod; + } + + return accum; +} diff --git a/server/src/utils/bigint.hpp b/server/src/utils/bigint.hpp index ee99c73..51334af 100644 --- a/server/src/utils/bigint.hpp +++ b/server/src/utils/bigint.hpp @@ -5,25 +5,58 @@ #include #include #include +#include "csprng.hpp" namespace sosc { -class BigInteger { +class BigUInt { public: - BigInteger(); - BigInteger(std::string hex_str, int byte_count = -1); + BigUInt(); + BigUInt(std::string hex_str, int byte_count = -1); + bool Parse(std::string hex_str, int byte_count = -1); + void Random(int byte_count); + void RandomPrime(int byte_count); - inline size_t ByteCount() { + inline size_t ByteCount() const { return value.size(); } - static std::tuple DivideWithRemainder - (const BigInteger& num, const BigInteger& denom); - - static BigInteger ModPow - (const BigInteger& base, const BigInteger& exp, const BigInteger& mod); - + bool IsZero() const; + bool IsOne() const; + bool IsEven() const; + bool IsProbablePrime() const; + static std::tuple DivideWithRemainder + (const BigUInt& num, const BigUInt& denom); + static BigUInt ModPow + (const BigUInt& base, const BigUInt& exp, const BigUInt& mod); + + void SetBit(uint64_t bit, bool value); + bool GetBit(uint64_t bit) const; + + BigUInt& operator + (const BigUInt& rhs) const; + BigUInt& operator += (const BigUInt& rhs); + + BigUInt& operator - (const BigUInt& rhs) const; + BigUInt& operator -= (const BigUInt& rhs); + + BigUInt& operator * (const BigUInt& rhs) const; + BigUInt& operator *= (const BigUInt& rhs); + + BigUInt& operator / (const BigUInt& rhs) const; + BigUInt& operator /= (const BigUInt& rhs); + + BigUInt& operator % (const BigUInt& rhs) const; + + bool operator == (const BigUInt& rhs) const; + bool operator > (const BigUInt& rhs) const; + bool operator >= (const BigUInt& rhs) const; + bool operator < (const BigUInt& rhs) const; + bool operator <= (const BigUInt& rhs) const; + + BigUInt operator >> (const uint64_t& rhs) const; + BigUInt operator << (const uint64_t& rhs) const; + std::string ToString() const; inline operator std::string () const { return this->ToString();