diff --git a/server/src/main.cpp b/server/src/main.cpp index 24c3144..4248a87 100644 --- a/server/src/main.cpp +++ b/server/src/main.cpp @@ -48,8 +48,16 @@ int main(int argc, char **argv) { std::cout << hash << std::endl; std::cout << sosc::cgc::bcrypt_check("test pwd", hash);*/ - sosc::BigUInt bi; - bi.Parse("abcdef0", 10); + sosc::BigUInt a, b; + + a.Parse("ff0000"); + b.Parse("fff"); + //assert(a - b == sosc::BigUInt("feff01")); + + //a += b; + a *= b; + + std::cout << a.ToString() << std::endl; return 0; } diff --git a/server/src/utils/bigint.cpp b/server/src/utils/bigint.cpp index 54cf412..23dd91f 100644 --- a/server/src/utils/bigint.cpp +++ b/server/src/utils/bigint.cpp @@ -21,9 +21,9 @@ bool sosc::BigUInt::Parse(std::string hex_str, int byte_count) { 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); + this->value = + std::vector(std::max(byte_count, str_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; @@ -32,6 +32,8 @@ bool sosc::BigUInt::Parse(std::string hex_str, int byte_count) { = std::stoi(hex_str.substr(i, 2), 0, 16); } + if(byte_count != -1) + this->value.resize(byte_count, 0); return true; } @@ -42,6 +44,12 @@ void sosc::BigUInt::Random(int byte_count) { this->value[i] = random_str[i]; } +sosc::BigUInt sosc::BigUInt::GenerateRandom(int byte_count) { + BigUInt num; + num.Random(byte_count); + return num; +} + void sosc::BigUInt::RandomPrime(int byte_count) { do { this->Random(byte_count); @@ -50,6 +58,24 @@ void sosc::BigUInt::RandomPrime(int byte_count) { } while(!this->IsProbablePrime()); } +sosc::BigUInt sosc::BigUInt::GenerateRandomPrime(int byte_count) { + BigUInt num; + num.RandomPrime(byte_count); + return num; +} + +size_t sosc::BigUInt::UsedByteCount() const { + size_t ptr = this->ByteCount() - 1; + for(;; --ptr) { + if(this->value[ptr] != 0) + break; + if(ptr == 0) + return 0; + } + + return ptr + 1; +} + bool sosc::BigUInt::IsZero() const { return std::all_of(this->value.begin(), this->value.end(), @@ -70,25 +96,67 @@ bool sosc::BigUInt::IsEven() const { return !this->GetBit(0); } -bool sosc::BigUInt::IsProbablePrime() const { - // TODO rabin-miller - return false; +bool sosc::BigUInt::IsProbablePrime(uint16_t rounds) const { + if(this->IsOne()) + return false; + + if (*this == BigUInt(2u) + || *this == BigUInt(3u) + || *this == BigUInt(5u)) + { + return true; + } + + if (this->IsEven() + || this->IsDivisibleBy(BigUInt(3u)) + || this->IsDivisibleBy(BigUInt(5u))) + { + return false; + } + + if(*this < BigUInt(25u)) + return true; + + for(uint16_t i = 0; i < rounds; ++i) { + BigUInt rnd = BigUInt::GenerateRandom(this->ByteCount()); + rnd = (rnd < BigUInt(2u)) + ? BigUInt(2u) + : (rnd > *this - BigUInt(2u)) + ? *this - BigUInt(2u) + : rnd; + + if(BigUInt::ModPow(rnd, *this - BigUInt(1u), *this).IsOne()) + return false; + } + + return true; } -std::tuple sosc::BigUInt::DivideWithRemainder +bool sosc::BigUInt::IsDivisibleBy(const BigUInt& value) const { + if(value.IsZero()) + return false; + if(value.IsOne()) + return true; + if(value == BigUInt(2u)) + return value.IsEven(); + + return (*this % value).IsZero(); +} + +sosc::division_t sosc::BigUInt::DivideWithRemainder (const BigUInt& num, const BigUInt& denom) { if(num.IsZero()) - return std::make_tuple(BigUInt(), BigUInt()); + return division_t(BigUInt(), BigUInt()); if(denom.IsZero()) throw "BigUInt division by zero"; if(denom.IsOne()) - return std::make_tuple(num, BigUInt()); + return division_t(num, BigUInt()); if(denom > num) - return std::make_tuple(BigUInt(), num); + return division_t(BigUInt(), num); BigUInt quotient, remainder; - for(uint64_t i = num.ByteCount() * 8 - 1;; --i) { + for(size_t i = num.ByteCount() * 8 - 1;; --i) { remainder = remainder << 1; remainder.SetBit(0, num.GetBit(i)); @@ -101,7 +169,7 @@ std::tuple sosc::BigUInt::DivideWithRemainder break; } - return std::make_tuple(quotient, remainder); + return division_t(quotient, remainder); } sosc::BigUInt sosc::BigUInt::ModPow @@ -123,7 +191,7 @@ sosc::BigUInt sosc::BigUInt::ModPow } void sosc::BigUInt::SetBit(uint64_t bit, bool value) { - uint64_t byte = bit / 8; + size_t byte = bit / 8; if(byte >= this->ByteCount()) this->value.resize(byte + 1); @@ -134,7 +202,7 @@ void sosc::BigUInt::SetBit(uint64_t bit, bool value) { } bool sosc::BigUInt::GetBit(uint64_t bit) const { - uint64_t byte = bit / 8; + size_t byte = bit / 8; if(byte >= this->ByteCount()) return false; @@ -142,14 +210,27 @@ bool sosc::BigUInt::GetBit(uint64_t bit) const { } sosc::BigUInt sosc::BigUInt::operator + (const BigUInt& rhs) const { - uint64_t sum_range = std::min(this->ByteCount(), rhs.ByteCount()); + size_t sum_range = std::max(this->ByteCount(), rhs.ByteCount()); + + auto rhs_v = rhs.value; + auto this_v = this->value; + rhs_v.resize(sum_range, 0); + this_v.resize(sum_range, 0); BigUInt sum; + sum.value.clear(); + uint8_t carry = 0; - for(uint64_t i = 0; i < sum_range; ++i) { + for(size_t i = 0; i < sum_range; ++i) { + uint16_t result = this_v[i] + rhs_v[i] + carry; + carry = result >> 8; + sum.value.push_back(result); } + if(carry != 0) + sum.value.push_back(carry); + return sum; } @@ -157,3 +238,206 @@ sosc::BigUInt& sosc::BigUInt::operator += (const BigUInt& rhs) { this->Copy(*this + rhs); return *this; } + +sosc::BigUInt sosc::BigUInt::operator - (const BigUInt& rhs) const { + if(*this < rhs) + return BigUInt(); + + size_t sub_range = std::max(this->ByteCount(), rhs.ByteCount()); + + auto rhs_v = rhs.value; + auto this_v = this->value; + rhs_v.resize(sub_range, 0); + this_v.resize(sub_range, 0); + + BigUInt sub; + sub.value.clear(); + + uint8_t carry = 0; + for(size_t i = 0; i < sub_range; ++i) { + uint8_t result = this_v[i] - rhs_v[i] - carry; + carry = (rhs_v[i] + carry > this_v[i]) ? 1 : 0; + + sub.value.push_back(result); + } + + return sub; +} + +sosc::BigUInt& sosc::BigUInt::operator -= (const BigUInt& rhs) { + this->Copy(*this - rhs); + return *this; +} + +sosc::BigUInt sosc::BigUInt::operator * (const BigUInt& rhs) const { + if(this->IsZero() || rhs.IsZero()) + return BigUInt(); + if(this->IsOne()) + return rhs; + if(rhs.IsOne()) + return *this; + + size_t prod_range = std::max(this->ByteCount(), rhs.ByteCount()); + + auto rhs_v = rhs.value; + auto this_v = this->value; + rhs_v.resize(prod_range, 0); + this_v.resize(prod_range, 0); + + BigUInt product; + for(size_t i = 0; i < prod_range; ++i) { + if(rhs_v[i] == 0) + continue; + + for(size_t j = 0; j < prod_range; ++j) { + if(this_v[j] == 0) + continue; + + BigUInt result((uint16_t)((uint16_t)this_v[j] * rhs_v[i])); + product += (result << (8 * (i + j))); + } + } + + return product; +} + +sosc::BigUInt& sosc::BigUInt::operator *= (const BigUInt& rhs) { + this->Copy(*this * rhs); + return *this; +} + +sosc::BigUInt sosc::BigUInt::operator / (const BigUInt& rhs) const { + return BigUInt::DivideWithRemainder(*this, rhs).result; +} + +sosc::BigUInt& sosc::BigUInt::operator /= (const BigUInt& rhs) { + this->Copy(*this / rhs); + return *this; +} + +sosc::BigUInt sosc::BigUInt::operator % (const BigUInt& rhs) const { + return BigUInt::DivideWithRemainder(*this, rhs).remainder; +} + +bool sosc::BigUInt::operator == (const BigUInt& rhs) const { + if(this->UsedByteCount() != rhs.UsedByteCount()) + return false; + + size_t cmp_range = std::max(this->ByteCount(), rhs.ByteCount()); + for(size_t i = 0; i < cmp_range; ++i) { + uint8_t a = i < this->ByteCount() ? this->value[i] : 0; + uint8_t b = i < rhs.ByteCount() ? rhs.value[i] : 0; + + if(a != b) + return false; + } + + return true; +} + +bool sosc::BigUInt::operator != (const BigUInt& rhs) const { + return !(*this == rhs); +} + +bool sosc::BigUInt::operator > (const BigUInt& rhs) const { + if(this->UsedByteCount() < rhs.UsedByteCount()) + return false; + + size_t cmp_range = std::max(this->ByteCount(), rhs.ByteCount()); + + for(size_t i = cmp_range - 1;; --i) { + uint8_t a = i < this->ByteCount() ? this->value[i] : 0; + uint8_t b = i < rhs.ByteCount() ? rhs.value[i] : 0; + + if(a > b) + return true; + if(i == 0) + break; + } + + return false; +} + +bool sosc::BigUInt::operator >= (const BigUInt& rhs) const { + return (*this > rhs) || (*this == rhs); +} + +bool sosc::BigUInt::operator < (const BigUInt& rhs) const { + return !(*this > rhs) && *this != rhs; +} + +bool sosc::BigUInt::operator <= (const BigUInt& rhs) const { + return !(*this > rhs) || *this == rhs; +} + +sosc::BigUInt sosc::BigUInt::operator >> (const uint64_t& rhs) const { + size_t bytes = rhs / 8; + uint8_t bits = rhs % 8; + if(bytes >= this->ByteCount()) + return BigUInt(); + + std::vector this_v + (this->value.begin() + bytes, this->value.end()); + std::vector buffer(this_v.size(), 0); + + if(bits != 0) { + uint8_t carry = 0, mask = 0; + for(uint8_t i = 0; i < bits; ++i) + mask |= (1 << i); + + for(size_t i = this_v.size() - 1;; --i) { + buffer[i] = carry | (this_v[i] >> bits); + carry = (buffer[i] & mask) << (8 - bits); + + if(i == 0) + break; + } + } + + sosc::BigUInt shifted; + shifted.value = bits == 0 ? this_v : buffer; + return shifted; +} + +sosc::BigUInt sosc::BigUInt::operator << (const uint64_t& rhs) const { + size_t bytes = rhs / 8; + uint8_t bits = rhs % 8; + + std::vector this_v = this->value; + for(size_t i = 0; i < bytes; ++i) + this_v.insert(this_v.begin(), 0); + std::vector buffer(this_v.size(), 0); + + if(bits != 0) { + uint8_t carry = 0, mask = 0; + for(uint8_t i = 0; i < bits; ++i) + mask |= (0x80 >> i); + + for(size_t i = bytes; i < this_v.size(); i++) { + buffer[i] = carry | (this_v[i] << bits); + carry = (this_v[i] & mask) >> (8 - bits); + } + + if(carry != 0) + this_v.push_back(carry); + } + + sosc::BigUInt shifted; + shifted.value = bits == 0 ? this_v : buffer; + return shifted; +} + +std::string sosc::BigUInt::ToString() const { + std::stringstream stream; + for(size_t i = this->ByteCount() - 1;; --i) { + stream << std::setfill('0') + << std::setw(2) + << std::hex + << (int)this->value[i]; + + if(i == 0) + break; + } + + return stream.str(); +} diff --git a/server/src/utils/bigint.hpp b/server/src/utils/bigint.hpp index dd0a8c7..b2eb7a1 100644 --- a/server/src/utils/bigint.hpp +++ b/server/src/utils/bigint.hpp @@ -1,22 +1,40 @@ #ifndef SOSC_UTIL_BIGINT_H #define SOSC_UTIL_BIGINT_H +#include #include #include #include #include +#include +#include #include #include "csprng.hpp" namespace sosc { +struct division_t; + class BigUInt { public: BigUInt(); + + BigUInt(uint8_t value) { this->Initialize(value); } + BigUInt(uint16_t value) { this->Initialize(value); } + BigUInt(uint32_t value) { this->Initialize(value); } + BigUInt(uint64_t value) { this->Initialize(value); } + BigUInt(std::string hex_str, int byte_count = -1); bool Parse(std::string hex_str, int byte_count = -1); + inline void Resize(uint64_t byte_count) { + this->value.resize(byte_count, 0); + } + void Random(int byte_count); void RandomPrime(int byte_count); + static BigUInt GenerateRandom(int byte_count); + static BigUInt GenerateRandomPrime(int byte_count); + size_t UsedByteCount() const; inline size_t ByteCount() const { return value.size(); } @@ -24,9 +42,10 @@ public: bool IsZero() const; bool IsOne() const; bool IsEven() const; - bool IsProbablePrime() const; + bool IsProbablePrime(uint16_t rounds = 5) const; + bool IsDivisibleBy(const BigUInt& value) const; - static std::tuple DivideWithRemainder + static division_t DivideWithRemainder (const BigUInt& num, const BigUInt& denom); static BigUInt ModPow @@ -50,6 +69,7 @@ public: 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; @@ -63,12 +83,33 @@ public: return this->ToString(); } private: + template + void Initialize(T value) { + for(int i = 0; i < sizeof(T); ++i) { + this->value.push_back(value & 0xFF); + value >>= 8; + } + } + inline void Copy(const BigUInt& from) { - + this->value = from.value; } std::vector value; }; + +struct division_t { +public: + BigUInt result; + BigUInt remainder; +private: + division_t(const BigUInt& result, const BigUInt& remainder) { + this->result = result; + this->remainder = remainder; + } + + friend class BigUInt; +}; } /*