From 1dd2836f1bb922e308f397c4c4cc64ace99cbaee Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Fri, 13 Sep 2013 10:42:56 -0700 Subject: [PATCH] Add support for multiprecision integer arithmetic and binary data encoding --- Builds/VisualStudio2012/beast.vcxproj | 8 + Builds/VisualStudio2012/beast.vcxproj.filters | 9 + modules/beast_crypto/beast_crypto.cpp | 1 + modules/beast_crypto/beast_crypto.h | 2 + modules/beast_crypto/math/BinaryEncoding.cpp | 395 ++++++++++++++++ modules/beast_crypto/math/BinaryEncoding.h | 24 + modules/beast_crypto/math/UnsignedInteger.cpp | 349 +++++++++++++- modules/beast_crypto/math/UnsignedInteger.h | 248 ++++------ .../beast_crypto/math/UnsignedIntegerCalc.h | 428 ++++++++++++++++++ 9 files changed, 1281 insertions(+), 183 deletions(-) create mode 100644 modules/beast_crypto/math/BinaryEncoding.cpp create mode 100644 modules/beast_crypto/math/BinaryEncoding.h create mode 100644 modules/beast_crypto/math/UnsignedIntegerCalc.h diff --git a/Builds/VisualStudio2012/beast.vcxproj b/Builds/VisualStudio2012/beast.vcxproj index eb2aac8ed..99a33dcd2 100644 --- a/Builds/VisualStudio2012/beast.vcxproj +++ b/Builds/VisualStudio2012/beast.vcxproj @@ -323,7 +323,9 @@ + + @@ -1265,6 +1267,12 @@ true + + true + true + true + true + true true diff --git a/Builds/VisualStudio2012/beast.vcxproj.filters b/Builds/VisualStudio2012/beast.vcxproj.filters index db6326b29..8e6eaabfb 100644 --- a/Builds/VisualStudio2012/beast.vcxproj.filters +++ b/Builds/VisualStudio2012/beast.vcxproj.filters @@ -1028,6 +1028,12 @@ beast_crypto\math + + beast_crypto\math + + + beast_crypto\math + @@ -1552,6 +1558,9 @@ beast_crypto\math + + beast_crypto\math + diff --git a/modules/beast_crypto/beast_crypto.cpp b/modules/beast_crypto/beast_crypto.cpp index 072c02ddb..f5ef3ad86 100644 --- a/modules/beast_crypto/beast_crypto.cpp +++ b/modules/beast_crypto/beast_crypto.cpp @@ -24,6 +24,7 @@ namespace beast { +#include "math/BinaryEncoding.cpp" #include "math/UnsignedInteger.cpp" } diff --git a/modules/beast_crypto/beast_crypto.h b/modules/beast_crypto/beast_crypto.h index 4556a5fac..4e9039bbc 100644 --- a/modules/beast_crypto/beast_crypto.h +++ b/modules/beast_crypto/beast_crypto.h @@ -44,7 +44,9 @@ namespace beast { +# include "math/UnsignedIntegerCalc.h" #include "math/UnsignedInteger.h" +#include "math/BinaryEncoding.h" } diff --git a/modules/beast_crypto/math/BinaryEncoding.cpp b/modules/beast_crypto/math/BinaryEncoding.cpp new file mode 100644 index 000000000..c67e4396c --- /dev/null +++ b/modules/beast_crypto/math/BinaryEncoding.cpp @@ -0,0 +1,395 @@ +//------------------------------------------------------------------------------ +/* + This file is part of Beast: https://github.com/vinniefalco/Beast + Copyright 2013, Vinnie Falco + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +/** Generic algorithms for base encoding and decoding. */ +class BinaryEncoding +{ +public: + /** Concept: Conversion + + X denotes a Conversion class, a is a value of type X, + i is an integral type. + + Requirements: + + Expression Type Notes/Contracts + ------------- ----------- ------------------ + X a; + X::radix size_type constexpr + a.map (i) char maps base numeral to a char + */ + + /** Encode the unsigned integer into a string using the specified conversion. */ + template + static std::string encode (UnsignedInteger v, Conversion c = Conversion ()) + { + // bi is destroyed in this process + typename UnsignedInteger ::CalcType bi (v.toCalcType ()); + std::size_t const radix (Conversion::radix); + std::string s; + s.reserve (bi.size() * 3); // guess + while (bi.isNotZero ()) + { + std::size_t const m (bi % radix); + bi /= radix; + s.push_back (c.map (m)); + } + std::reverse (s.begin(), s.end()); + return s; + } + + /** Decode the string into an unsigned integer. + The size must match exactly + @return `true` on success. + */ + template + static bool decode (UnsignedInteger & rv, + std::string const& s, Conversion c = Conversion ()) + { + typename UnsignedInteger ::CalcType bi (rv.toCalcType (false)); + std::size_t const radix (Conversion::radix); + bi.clear (); + for (std::string::const_iterator iter (s.begin()); iter != s.end(); ++iter) + { + int const v (c.invert (*iter)); + if (v == -1) + return false; + bi *= radix; + bi += v; + } + bi.toCanonical(); + return true; + } +}; + +//------------------------------------------------------------------------------ + +// Some common code +template +class BaseConversion +{ +public: + char map (std::size_t i) const + { + return Conversion::alphabet () [i]; + } + + int invert (char c) const + { + return Conversion::inverse_alphabet () [c]; + } + + static std::vector const& inverse_alphabet () + { + static std::vector t (invert (Conversion::alphabet(), Conversion::radix)); + return t; + } + + /** Build the inverse mapping table from characters to digits. */ + static std::vector invert (std::string const& alphabet, std::size_t radix) + { + std::vector table (256, -1); + for (std::size_t i (0); i < radix; ++i) + table [alphabet [i]] = i; + return table; + } + +}; + +//------------------------------------------------------------------------------ + +/** Foolproof hexadecimal encoding and decoding facility. + This is to check the correctness of the more complex converters. +*/ +class HexEncoding +{ +public: + template + static std::string encode (UnsignedInteger const& v) + { + std::string s; + uint8 const* src (v.cbegin()-1); + char const* const tab (alphabet().c_str()); + s.reserve (Bytes * 2); + for (std::size_t bytes (v.sizeInBytes);bytes--;) + { + uint8 const v (*++src); + s.push_back (tab [v>>4]); + s.push_back (tab [v&0x0f]); + } + return s; + } + + template + static bool decode (UnsignedInteger & rv, + std::string const& s) + { + // can't have an odd size + if (s.size() & 1) + return false; + uint8* dest (rv.begin()-1); + int const* const tab (&inverse_alphabet().front()); + for (std::string::const_iterator iter (s.begin()); iter != s.end();) + { + int const n1 (tab [*iter++]); + if (n1 == -1) + return false; + int const n2 (tab [*iter++]); + if (n2 == -1) + return false; + *++dest = ((uint8)((n1<<4)|n2)); + } + return true; + } + + static std::string const& alphabet () + { + static std::string s ("0123456789ABCDEF"); + return s; + } + + static std::vector const& inverse_alphabet () + { + static std::vector s (invert (alphabet(), 16)); + return s; + } + + /** Build the inverse mapping table from characters to digits. */ + static std::vector invert (std::string const& alphabet, std::size_t radix) + { + std::vector table (256, -1); + for (std::size_t i (0); i < radix; ++i) + table [alphabet [i]] = i; + return table; + } +}; + +//------------------------------------------------------------------------------ + +/** Base58 conversion used by Bitcoin. */ +class BitcoinBase58Conversion : public BaseConversion +{ +public: + static std::size_t const radix = 58; + + char const* name () const + { + return "BitcoinBase58"; + } + + static std::string const& alphabet () + { + static std::string s ( + "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" + ); + return s; + } +}; + +//------------------------------------------------------------------------------ + +/** Base58 conversion used by Ripple. */ +class RippleBase58Conversion : public BaseConversion +{ +public: + static std::size_t const radix = 58; + + char const* name () const + { + return "RippleBase58"; + } + + static std::string const& alphabet () + { + static std::string s ( + "rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz" + ); + return s; + } +}; + +//------------------------------------------------------------------------------ + +class Base64Conversion : public BaseConversion +{ +public: + static std::size_t const radix = 64; + + char const* name () const + { + return "Base64"; + } + + static std::string const& alphabet () + { + static std::string s ( + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" + ); + return s; + } +}; + +//------------------------------------------------------------------------------ + +class Base16Conversion : public BaseConversion +{ +public: + static std::size_t const radix = 16; + + char const* name () const + { + return "Hex"; + } + + static std::string const& alphabet () + { + static std::string s ( + "0123456789ABCDEF" + ); + return s; + } +}; + +//------------------------------------------------------------------------------ + +class BinaryEncodingTests : public UnitTest +{ +public: + // This is a baseline for the other tests + template + void testBase16 () + { + beginTestCase ("base16"); + for (int i = 0; i < 50; ++i) + { + typedef UnsignedInteger UInt; + UInt v0; + random().fillBitsRandomly (v0.begin(), UInt::sizeInBytes); + std::string const good (HexEncoding::encode (v0)); + + UInt v1; + bool const success (HexEncoding::decode (v1, good)); + if (expect (success)) + { + expect (v0 == v1); + + Base16Conversion c; + std::string const check (BinaryEncoding::encode (v0, c)); + if (! expect (good == check)) + logMessage (String ("expected ") + good + " but got " + check); + } + } + } + + template + void testBase64Bytes ( + std::string const& vin, std::string const& vout, + Base64Conversion c = Base64Conversion ()) + { + typedef UnsignedInteger UInt; + UInt v1 (vin.c_str()); + std::string const s1 (BinaryEncoding::encode (v1, c)); + logMessage (vout + " to " + s1); + expect (vout == s1); + + UInt v2; + bool const success (BinaryEncoding::decode (v2, vout, c)); + if (expect (success)) + { + std::string const s2 (BinaryEncoding::encode (v2, c)); + logMessage (vin + " to " + s2); + //expect (vin == v2); + } + } + + void testBase64 () + { + beginTestCase ("Base64"); + + // input (uint) + std::string const vin [] = { + "","f","fo","foo","foob","fooba","foobar" + }; + + // output (encoded string) + std::string const vout [] = { + "","Zg==","Zm8=","Zm9v","Zm9vYg==","Zm9vYmE=","Zm9vYmFy" + }; + + //testBase64Bytes <0> (vin [0], vout [0]); + testBase64Bytes <1> (vin [1], vout [1]); + testBase64Bytes <2> (vin [2], vout [2]); + testBase64Bytes <3> (vin [3], vout [3]); + testBase64Bytes <4> (vin [4], vout [4]); + testBase64Bytes <5> (vin [5], vout [5]); + testBase64Bytes <6> (vin [6], vout [6]); + } + + template + void testEncode (Conversion c = Conversion()) + { + typedef UnsignedInteger UInt; + + beginTestCase (String (c.name()) + " <" + String::fromNumber (Bytes) + ">"); + + for (int i = 0; i < 50; ++i) + { + UInt v1; + random().fillBitsRandomly (v1.begin(), UInt::sizeInBytes); + std::string const s1 (BinaryEncoding::encode (v1, c)); + + UInt v2; + bool success (BinaryEncoding::decode (v2, s1, c)); + if (expect (success)) + expect (v1 == v2); + } + } + + void runTest () + { + testBase16 <10> (); + +#if 0 + testEncode (); + testEncode (); + testEncode (); + testEncode (); + testEncode (); + testEncode (); + testEncode (); + testEncode (); + testEncode (); + testEncode (); + + testBase64 (); + testEncode (); + testEncode (); + testEncode (); + testEncode (); + testEncode (); + testEncode (); +#endif + } + + BinaryEncodingTests () : UnitTest ("BinaryEncoding", "beast", runManual) + { + } +}; + +static BinaryEncodingTests BinaryEncodingTests; diff --git a/modules/beast_crypto/math/BinaryEncoding.h b/modules/beast_crypto/math/BinaryEncoding.h new file mode 100644 index 000000000..d93691e4a --- /dev/null +++ b/modules/beast_crypto/math/BinaryEncoding.h @@ -0,0 +1,24 @@ +//------------------------------------------------------------------------------ +/* + This file is part of Beast: https://github.com/vinniefalco/Beast + Copyright 2013, Vinnie Falco + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef BEAST_CRYPTO_BINARYENCODING_H_INCLUDED +#define BEAST_CRYPTO_BINARYENCODING_H_INCLUDED + + +#endif diff --git a/modules/beast_crypto/math/UnsignedInteger.cpp b/modules/beast_crypto/math/UnsignedInteger.cpp index 2315d4344..40b079d06 100644 --- a/modules/beast_crypto/math/UnsignedInteger.cpp +++ b/modules/beast_crypto/math/UnsignedInteger.cpp @@ -17,6 +17,327 @@ */ //============================================================================== +//------------------------------------------------------------------------------ + +/* Copyright (c) 2013 the authors listed at the following URL, and/or + the authors of referenced articles or incorporated external code: + http://en.literateprograms.org/Arbitrary-precision_integer_arithmetic_(C)?action=history&offset=20100923155004 + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Retrieved from: http://en.literateprograms.org/Arbitrary-precision_integer_arithmetic_(C)?oldid=16902 +*/ + +namespace multiprecsion +{ + +#if 0 +//------------------------------------------------------------------------------ + +typedef unsigned short component_t; +typedef unsigned long double_component_t; + +#define MAX_COMPONENT ((component_t)(-1)) +#define COMPONENT_BITS (sizeof(component_t)*CHAR_BIT) + +#define LOG_2_10 3.3219280948873623478703194294894 + +#define MIN(x,y) ((x)<(y) ? (x) : (y)) +#define MAX(x,y) ((x)>(y) ? (x) : (y)) + +typedef struct { + component_t* c; /* least-significant word first */ + int num_components; +} integer; + + +integer create_integer(int components); +void free_integer(integer i); +void set_zero_integer(integer i); +void copy_integer(integer source, integer target); +void add_integer(integer left, integer right, integer result); +void subtract_integer(integer left, integer right, integer result); +void multiply_small_integer(integer left, component_t right, integer result); +void multiply_integer(integer left, integer right, integer result); +int compare_integers(integer left, integer right); +void shift_left_one_integer(integer arg); +void shift_right_one_integer(integer arg); +component_t mod_small_integer(integer left, component_t right); +void mod_integer(integer left, integer right, integer result); +void divide_small_integer(integer left, component_t right, integer result); +integer string_to_integer(char* s); +char* integer_to_string(integer x); +int is_zero_integer(integer x); + +//------------------------------------------------------------------------------ + +integer create_integer(int components) { + integer result; + result.num_components = components; + result.c = (component_t*)malloc(sizeof(component_t)*components); + return result; +} + + +void free_integer(integer i) { + free(i.c); +} + + +void set_zero_integer(integer i) { + memset(i.c, 0, sizeof(component_t)*i.num_components); +} + + +int is_zero_integer(integer x) { + int i; + for(i=0; i < x.num_components; i++) { + if (x.c[i] != 0) return 0; + } + return 1; +} + +void copy_integer(integer source, integer target) { + memmove(target.c, source.c, + sizeof(component_t)*MIN(source.num_components, target.num_components)); + if (target.num_components > source.num_components) { + memset(target.c + source.num_components, 0, + sizeof(component_t)*(target.num_components - source.num_components)); + } +} + +void add_integer(integer left, integer right, integer result) { + double_component_t carry = 0; + int i; + for(i=0; i MAX_COMPONENT) { + partial_sum &= MAX_COMPONENT; + carry = 1; + } + result.c[i] = (component_t)partial_sum; + } + for ( ; i < result.num_components; i++) { result.c[i] = 0; } +} + +void subtract_integer(integer left, integer right, integer result) { + int borrow = 0; + int i; + for(i=0; i> COMPONENT_BITS; + result.c[i] = (component_t)(partial_sum & MAX_COMPONENT); + } + for ( ; i < result.num_components; i++) { result.c[i] = 0; } +} + +void multiply_integer(integer left, integer right, integer result) { + int i, lidx, ridx; + double_component_t carry = 0; + int max_size_no_carry; + int left_max_component = left.num_components - 1; + int right_max_component = right.num_components - 1; + while(left.c[left_max_component] == 0) left_max_component--; + while(right.c[right_max_component] == 0) right_max_component--; + max_size_no_carry = left_max_component + right_max_component; + for(i=0; i <= max_size_no_carry || carry != 0; i++) { + double_component_t partial_sum = carry; + carry = 0; + lidx = MIN(i, left_max_component); + ridx = i - lidx; + while(lidx >= 0 && ridx <= right_max_component) { + partial_sum += ((double_component_t)left.c[lidx])*right.c[ridx]; + carry += partial_sum >> COMPONENT_BITS; + partial_sum &= MAX_COMPONENT; + lidx--; ridx++; + } + result.c[i] = partial_sum; + } + for ( ; i < result.num_components; i++) { result.c[i] = 0; } +} + +int compare_integers(integer left, integer right) { + int i = MAX(left.num_components - 1, right.num_components - 1); + for ( ; i >= 0; i--) { + component_t left_comp = + (i < left.num_components) ? left.c[i] : 0; + component_t right_comp = + (i < right.num_components) ? right.c[i] : 0; + if (left_comp < right_comp) + return -1; + else if (left_comp > right_comp) + return 1; + } + return 0; +} + +void shift_left_one_integer(integer arg) { + int i; + arg.c[arg.num_components - 1] <<= 1; + for (i = arg.num_components - 2; i >= 0; i--) { + arg.c[i + 1] |= arg.c[i] >> (COMPONENT_BITS - 1); + arg.c[i] <<= 1; + } +} + +void shift_right_one_integer(integer arg) { + int i; + arg.c[0] >>= 1; + for (i = 1; i < arg.num_components; i++) { + arg.c[i - 1] |= (arg.c[i] & 1) << (COMPONENT_BITS - 1); + arg.c[i] >>= 1; + } +} + +component_t mod_small_integer(integer left, component_t right) { + double_component_t mod_two_power = 1; + double_component_t result = 0; + int i, bit; + for(i=0; i= right) { + result -= right; + } + } + mod_two_power <<= 1; + if (mod_two_power >= right) { + mod_two_power -= right; + } + } + } + return (component_t)result; +} + +void mod_integer(integer left, integer right, integer result) { + integer mod_two_power = create_integer(right.num_components + 1); + int i, bit; + set_zero_integer(result); + set_zero_integer(mod_two_power); + mod_two_power.c[0] = 1; + for(i=0; i= 0) { + subtract_integer(result, right, result); + } + } + shift_left_one_integer(mod_two_power); + if (compare_integers(mod_two_power, right) >= 0) { + subtract_integer(mod_two_power, right, mod_two_power); + } + } + } + free_integer(mod_two_power); +} + +void divide_small_integer(integer left, component_t right, integer result) { + double_component_t dividend = 0; + int i; + for (i = left.num_components - 1; i >= 0; i--) { + dividend |= left.c[i]; + result.c[i] = dividend/right; + dividend = (dividend % right) << COMPONENT_BITS; + } +} + +integer string_to_integer(char* s) { + integer result = create_integer((int)ceil(LOG_2_10*strlen(s)/COMPONENT_BITS)); + set_zero_integer(result); + integer digit = create_integer(1); + int i; + for (i = 0; s[i] != '\0'; i++) { + multiply_small_integer(result, 10, result); + digit.c[0] = s[i] - '0'; + add_integer(result, digit, result); + } + free_integer(digit); + return result; +} + + +char* integer_to_string(integer x) { + int i, result_len; + char* result = + (char*)malloc((int)ceil(COMPONENT_BITS*x.num_components/LOG_2_10) + 2); + integer ten = create_integer(1); + ten.c[0] = 10; + + if (is_zero_integer(x)) { + strcpy(result, "0"); + } else { + for (i = 0; !is_zero_integer(x); i++) { + result[i] = (char)mod_small_integer(x, 10) + '0'; + divide_small_integer(x, 10, x); + } + result[i] = '\0'; + } + + result_len = strlen(result); + for(i=0; i < result_len/2; i++) { + char temp = result[i]; + result[i] = result[result_len - i - 1]; + result[result_len - i - 1] = temp; + } + + free_integer(ten); + return result; +} + +#endif + +} + +//------------------------------------------------------------------------------ + class UnsignedIntegerTests : public UnitTest { public: @@ -27,21 +348,21 @@ public: template void runTest () { + typedef UnsignedInteger UInt; + String s; s << "Bytes=" << String(Bytes); beginTestCase (s); - UnsignedInteger zero; + UInt zero; zero.fill (0); expect (zero.isZero (), "should be zero"); expect (! zero.isNotZero (), "sould not be non-zero"); - UnsignedInteger one; - one.clear (); - one [Bytes - 1] = 1; - expect (one == UnsignedInteger ::createFromInteger (1U), "should be equal"); + UInt one (UInt::createFromInteger (1U)); + expect (one == UInt::createFromInteger (1U), "should be equal"); expect (! one.isZero (), "should not be zero"); expect (one.isNotZero (), "sould be non-zero"); @@ -54,23 +375,13 @@ public: expect (zero == zero, "should be equal"); expect (zero != one, "should not be equal"); - expect ((zero | zero) == zero, "should be zero"); - expect ((zero | one) != zero, "should not be zero"); - expect ((one | one) != zero, "should not be zero"); + expect (zero == UInt::createFromInteger (0U), "should be zero"); + expect (one == UInt::createFromInteger (1U), "should be one"); + expect (one != UInt::createFromInteger (2U), "should not be two"); - expect ((one & zero) == zero, "should be zero"); - expect ((one & one) == one, "should be one"); - expect ((zero & zero) == zero, "should be zero"); - - expect (zero == UnsignedInteger ::createFromInteger (0U), "should be zero"); - expect (one == UnsignedInteger ::createFromInteger (1U), "should be one"); - expect (one != UnsignedInteger ::createFromInteger (2U), "should not be two"); - - UnsignedInteger largest = UnsignedInteger ::createFilled (0xff); + UInt largest = UInt::createFilled (0xff); expect (largest > zero && largest > one, "should be greater"); - expect (~largest == zero, "should be zero"); - expect (~one < largest, "should be less"); } void runTest() diff --git a/modules/beast_crypto/math/UnsignedInteger.h b/modules/beast_crypto/math/UnsignedInteger.h index 72ab8ed0f..04f32a720 100644 --- a/modules/beast_crypto/math/UnsignedInteger.h +++ b/modules/beast_crypto/math/UnsignedInteger.h @@ -17,15 +17,18 @@ */ //============================================================================== -#ifndef BEAST_UNSIGNEDINTEGER_H_INCLUDED -#define BEAST_UNSIGNEDINTEGER_H_INCLUDED +#ifndef BEAST_CRYPTO_UNSIGNEDINTEGER_H_INCLUDED +#define BEAST_CRYPTO_UNSIGNEDINTEGER_H_INCLUDED /** Represents a set of bits of fixed size. - Integer representations are stored in network / big endian byte order. - @note The number of bits represented can only be a multiple of 8. - @tparam Bytes The number of bytes of storage. + + The data is stored in "canonical" format which is network (big endian) + byte order, most significant byte first. + + In this implementation the pointer to the beginning of the canonical format + may not be aligned. */ -template +template class UnsignedInteger : public SafeBool > { public: @@ -36,10 +39,16 @@ public: sizeInBytes = Bytes }; + // The underlying integer type we use when converting to calculation format. + typedef uint32 IntCalcType; + + // The type of object resulting from a conversion to calculation format. + typedef UnsignedIntegerCalc CalcType; + // Standard container compatibility - typedef uint8 value_type; - typedef value_type* iterator; - typedef value_type const* const_iterator; + typedef uint8 ValueType; + typedef ValueType* iterator; + typedef ValueType const* const_iterator; /** Hardened hash function for use with HashMap. The seed is used to make the hash unpredictable. This prevents @@ -61,14 +70,14 @@ public: } /** Generates a simple hash from an UnsignedInteger. */ - HashValue generateHash (UnsignedInteger const& key) const noexcept + HashValue generateHash (UnsignedInteger const& key) const { HashValue hash; Murmur::Hash (key.cbegin (), key.sizeInBytes, m_seed, &hash); return hash; } - HashValue operator() (UnsignedInteger const& key) const noexcept + HashValue operator() (UnsignedInteger const& key) const { HashValue hash; Murmur::Hash (key.cbegin (), key.sizeInBytes, m_seed, &hash); @@ -84,12 +93,12 @@ public: /** Construct the object. The values are uninitialized. */ - UnsignedInteger () noexcept + UnsignedInteger () { } /** Construct a copy. */ - UnsignedInteger (UnsignedInteger const& other) noexcept + UnsignedInteger (UnsignedInteger const& other) { this->operator= (other); } @@ -101,37 +110,31 @@ public: /** @{ */ explicit UnsignedInteger (void const* buf) { + m_values [0] = 0; // clear any pad bytes std::memcpy (begin(), buf, Bytes); } template explicit UnsignedInteger (T const* buf) { + m_values [0] = 0; // clear any pad bytes std::memcpy (begin(), buf, Bytes); } /** @} */ - /** Construct from a sequence. */ - template - UnsignedInteger (ForwardIterator start, ForwardIterator finish) + /** Assign from another UnsignedInteger. */ + UnsignedInteger& operator= (UnsignedInteger const& other) { - bassert (std::distance (start, finish) == Bytes); - std::copy (start, finish, begin()); - } - - /** Assign from another value. */ - UnsignedInteger & operator= (UnsignedInteger const& other) noexcept - { - std::memcpy (begin(), other.begin(), Bytes); + // Perform an aligned, all inclusive copy that includes padding. + std::copy (other.m_values, other.m_values + CalcCount, m_values); return *this; } /** Create from an integer type. - @invariant IntegerType must be an unsigned integer type. */ template - static UnsignedInteger createFromInteger (IntegerType value) + static UnsignedInteger createFromInteger (IntegerType value) { static_bassert (Bytes >= sizeof (IntegerType)); UnsignedInteger result; @@ -143,31 +146,39 @@ public: /** Construct with a filled value. */ - static UnsignedInteger createFilled (value_type value) + static UnsignedInteger createFilled (ValueType value) { - UnsignedInteger result; + UnsignedInteger result; result.fill (value); return result; } /** Fill with a particular byte value. */ - void fill (value_type value) noexcept + void fill (ValueType value) { - std::fill (begin(), end(), value); + IntCalcType c; + memset (&c, value, sizeof (c)); + std::fill (m_values, m_values + CalcCount, c); } /** Clear the contents to zero. */ - void clear () noexcept + void clear () { - fill (0); + std::fill (m_values, m_values + CalcCount, 0); + } + + /** Convert to calculation format. */ + CalcType toCalcType (bool convert = true) + { + return CalcType::fromCanonical (m_values, Bytes, convert); } /** Determine if all bits are zero. */ - bool isZero () const noexcept + bool isZero () const { for (int i = 0; i < Bytes; ++i) { - if (m_byte [i] != 0) + if (m_values [i] != 0) return false; } @@ -175,7 +186,7 @@ public: } /** Determine if any bit is non-zero. */ - bool isNotZero () const noexcept + bool isNotZero () const { return ! isZero (); } @@ -184,198 +195,107 @@ public: @return `true` if any bit is non-zero. @see SafeBool */ - bool asBoolean () const noexcept + bool asBoolean () const { return isNotZero (); } - /** Access a particular byte. */ - value_type& getByte (int byteIndex) noexcept - { - bassert (byteIndex >= 0 && byteIndex < Bytes); - - return m_byte [byteIndex]; - } - - /** Access a particular byte as `const`. */ - value_type getByte (int byteIndex) const noexcept - { - bassert (byteIndex >= 0 && byteIndex < Bytes); - - return m_byte [byteIndex]; - } - - /** Access a particular byte. */ - value_type& operator[] (int byteIndex) noexcept - { - return getByte (byteIndex); - } - - /** Access a particular byte as `const`. */ - value_type const operator[] (int byteIndex) const noexcept - { - return getByte (byteIndex); - } - /** Get an iterator to the beginning. */ - iterator begin () noexcept + iterator begin () { - return &m_byte [0]; + return get(); } /** Get an iterator to past-the-end. */ - iterator end () noexcept + iterator end () { - return &m_byte [Bytes]; + return get()+Bytes; } /** Get a const iterator to the beginning. */ - const_iterator begin () const noexcept + const_iterator begin () const { - return &m_byte [0]; + return get(); } /** Get a const iterator to past-the-end. */ - const_iterator end () const noexcept + const_iterator end () const { - return &m_byte [Bytes]; + return get()+Bytes; } /** Get a const iterator to the beginning. */ - const_iterator cbegin () const noexcept + const_iterator cbegin () const { - return &m_byte [0]; + return get(); } /** Get a const iterator to past-the-end. */ - const_iterator cend () const noexcept + const_iterator cend () const { - return &m_byte [Bytes]; + return get()+Bytes; } - /** Compare two objects. */ - int compare (UnsignedInteger const& other) const noexcept + /** Compare two objects of equal size. + The comparison is performed using a numeric lexicographical comparison. + */ + int compare (UnsignedInteger const& other) const { return memcmp (cbegin (), other.cbegin (), Bytes); } /** Determine equality. */ - bool operator== (UnsignedInteger const& other) const noexcept + bool operator== (UnsignedInteger const& other) const { return compare (other) == 0; } /** Determine inequality. */ - bool operator!= (UnsignedInteger const& other) const noexcept + bool operator!= (UnsignedInteger const& other) const { return compare (other) != 0; } /** Ordered comparison. */ - bool operator< (UnsignedInteger const& other) const noexcept + bool operator< (UnsignedInteger const& other) const { return compare (other) < 0; } /** Ordered comparison. */ - bool operator<= (UnsignedInteger const& other) const noexcept + bool operator<= (UnsignedInteger const& other) const { return compare (other) <= 0; } /** Ordered comparison. */ - bool operator> (UnsignedInteger const& other) const noexcept + bool operator> (UnsignedInteger const& other) const { return compare (other) > 0; } /** Ordered comparison. */ - bool operator>= (UnsignedInteger const& other) const noexcept + bool operator>= (UnsignedInteger const& other) const { return compare (other) >= 0; } - /** Perform bitwise logical-not. */ - UnsignedInteger operator~ () const noexcept - { - UnsignedInteger result; - - for (int i = 0; i < Bytes; ++i) - result [i] = ~getByte (i); - - return result; - } - - /** Perform bitwise logical-or. */ - UnsignedInteger & operator|= (UnsignedInteger const& rhs) noexcept - { - for (int i = 0; i < Bytes; ++i) - getByte (i) |= rhs [i]; - - return *this; - } - - /** Perform bitwise logical-or. */ - UnsignedInteger operator| (UnsignedInteger const& rhs) const noexcept - { - UnsignedInteger result; - - for (int i = 0; i < Bytes; ++i) - result [i] = getByte (i) | rhs [i]; - - return result; - } - - /** Perform bitwise logical-and. */ - UnsignedInteger & operator&= (UnsignedInteger const& rhs) noexcept - { - for (int i = 0; i < Bytes; ++i) - getByte (i) &= rhs [i]; - - return *this; - } - - /** Perform bitwise logical-and. */ - UnsignedInteger operator& (UnsignedInteger const& rhs) const noexcept - { - UnsignedInteger result; - - for (int i = 0; i < Bytes; ++i) - result [i] = getByte (i) & rhs [i]; - - return result; - } - - /** Perform bitwise logical-xor. */ - UnsignedInteger & operator^= (UnsignedInteger const& rhs) noexcept - { - for (int i = 0; i < Bytes; ++i) - getByte (i) ^= rhs [i]; - - return *this; - } - - /** Perform bitwise logical-xor. */ - UnsignedInteger operator^ (UnsignedInteger const& rhs) const noexcept - { - UnsignedInteger result; - - for (int i = 0; i < Bytes; ++i) - result [i] = getByte (i) ^ rhs [i]; - - return result; - } - - // VFALCO TODO: - // - // increment, decrement, add, subtract - // negate - // other stuff that makes sense from base_uint <> - // missing stuff that built-in integers do - // - private: - value_type m_byte [Bytes]; + static std::size_t const CalcCount = (Bytes + sizeof (IntCalcType) - 1) / sizeof (IntCalcType); + + ValueType* get () + { + return (reinterpret_cast (&m_values [0])) + + ((sizeof(IntCalcType)-(Bytes&(sizeof(IntCalcType)-1)))&(sizeof(IntCalcType)-1)); + } + + ValueType const* get () const + { + return (reinterpret_cast (&m_values [0])) + + ((sizeof(IntCalcType)-(Bytes&(sizeof(IntCalcType)-1)))&(sizeof(IntCalcType)-1)); + } + + IntCalcType m_values [CalcCount]; }; #endif diff --git a/modules/beast_crypto/math/UnsignedIntegerCalc.h b/modules/beast_crypto/math/UnsignedIntegerCalc.h new file mode 100644 index 000000000..74afd7cb8 --- /dev/null +++ b/modules/beast_crypto/math/UnsignedIntegerCalc.h @@ -0,0 +1,428 @@ +//------------------------------------------------------------------------------ +/* + This file is part of Beast: https://github.com/vinniefalco/Beast + Copyright 2013, Vinnie Falco + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef BEAST_CRYPTO_UNSIGNEDINTEGERCALC_H_INCLUDED +#define BEAST_CRYPTO_UNSIGNEDINTEGERCALC_H_INCLUDED + +namespace detail +{ + +template +struct DoubleWidthUInt; + +template <> +struct DoubleWidthUInt +{ + typedef uint32 type; +}; + +template <> +struct DoubleWidthUInt +{ + typedef uint64 type; +}; + +} + +/** Multiprecision unsigned integer suitable for calculations. + + The data is stored in "calculation" format, which means it can be + readily used for performing calculations, but no raw access to the + bytes are provided. To transmit a serialized unsigned integer or + perform base encodings, it must be converted back into UnsignedInteger. + The number is represented as a series of native UInt unsigned integer + types, in order of increasing significance. + + This is a lightweight object, storage and ownership of the underlying + data buffer is an external responsibility. The makes the class cheap to + copy and pass by value. + + A consequence of this ownership model is that arithmetics operators + which return results by value cannot be included in the interface. +*/ +template +class UnsignedIntegerCalc : public SafeBool > +{ +public: + typedef typename detail::DoubleWidthUInt ::type UIntBig; + + typedef std::size_t size_type; + + static UInt const maxUInt = ((UInt)(-1)); + static size_type const numBits = (sizeof(UInt)*8); + + //-------------------------------------------------------------------------- + + /** Construct an empty integer / zero bits. */ + UnsignedIntegerCalc () + : m_size (0) + , m_values (nullptr) + { + } + + /** Construct a reference to an existing buffer. */ + UnsignedIntegerCalc (UnsignedIntegerCalc const& other) + : m_size (other.m_size) + , m_values (other.m_values) + { + } + + /** Construct from an existing array of values. + The existing data must already be in the "calculation" format. + */ + UnsignedIntegerCalc (size_type count, UInt* values) + : m_size (count) + , m_values (values) + { + } + + /** Convert to calculation format from canonical format. + This overwrites the callers memory without transferring ownership. + Canonical format is defined as a big endian byte oriented + multiprecision integer format. The buffer should point to the + beginning of the storage area and not the beginning of the canonical + data. Bytes is the desired canonical bytes. + */ + static UnsignedIntegerCalc fromCanonical ( + void* buffer, size_type const bytes, bool swizzle = true) + { + UInt* const values (reinterpret_cast (buffer)); + size_type const count ((bytes + sizeof (UInt) - 1) / sizeof (UInt)); + if (swizzle) + { + // Zero fill the possibly uninitialized pad bytes + memset (buffer, 0, + ((sizeof(UInt)-(bytes&(sizeof(UInt)-1)))&(sizeof(UInt)-1))); + // Swap and swizzle + UInt* lo (values); + UInt* hi (values + count - 1); + while (lo < hi) + { + std::swap (*lo, *hi); + *lo++ = fromNetworkByteOrder (*lo); + *hi-- = fromNetworkByteOrder (*hi); + } + if (lo == hi) + *lo = fromNetworkByteOrder (*lo); + } + return UnsignedIntegerCalc (count, values); + } + + /** Convert the buffer back into canonical format. + Since ownership was never transferred, the caller's data is + restored to its original format. Typically this will be done + as the last step of a series of operations. + */ + void toCanonical () + { + // Swap and swizzle + UInt* lo (m_values); + UInt* hi (m_values + m_size - 1); + while (lo < hi) + { + std::swap (*lo, *hi); + *lo++ = toNetworkByteOrder (*lo); + *hi-- = toNetworkByteOrder (*hi); + } + if (lo == hi) + *lo = toNetworkByteOrder (*lo); + } + + /** Assign a value from another integer. + @note This does not transfer the reference to the buffer, it + copies the values from one buffer to the other. + */ + UnsignedIntegerCalc& operator= (UnsignedIntegerCalc const& other) + { + bassert (other.size() <= size()); + size_type n (size()); + UInt* dest (m_values + size()); + for (; n-- > other.size();) + *--dest = 0; + UInt const* rhs (other.m_values + n); + for (; n--;) + *--dest = *--rhs; + return *this; + } + + /** Returns `true` if this represents the number zero. */ + bool isZero () const + { + for (size_type n (size()); n--;) + if (m_values [n] != 0) + return false; + return true; + } + + /** Returns `true` if this represents any number other than zero. */ + bool isNotZero () const + { + return ! isZero (); + } + + /** Safe conversion to `bool`, `true` means a non-zero value. */ + bool asBoolean () const + { + return isNotZero (); + } + + /** Returns `true` if the buffer has 0 values. */ + bool empty () const + { + return m_size == 0; + } + + /** Returns the size of the buffer, in values. */ + size_type size () const + { + return m_size; + } + + /** Safe array indexing to arbitrary positions. + If the index is out of range, zero is returned. + */ + UInt operator[] (size_type n) const + { + if (n >= 0 && n < size()) + return m_values [n]; + return 0; + } + + /** Universal comparison. + The comparison is performed numerically. + The return values have the same meaning as memcmp(). + */ + int compare (UnsignedIntegerCalc const& other) const + { + if (size() == 0) + { + if (other.size() == 0) + return 0; + return -1; + } + else if (other.size() == 0) + { + return 1; + } + + for (size_type n (std::max (size(), other.size())); n--;) + { + UInt lhs ((*this)[n]); + UInt rhs (other[n]); + if (lhs < rhs) + return -1; + else if (lhs > rhs) + return 1; + } + + return 0; + } + + /** Determine equality. */ + bool operator== (UnsignedIntegerCalc const& other) const + { + return compare (other) == 0; + } + + /** Determine inequality. */ + bool operator!= (UnsignedIntegerCalc const& other) const + { + return compare (other) != 0; + } + + /** Ordered comparison. */ + bool operator< (UnsignedIntegerCalc const& other) const + { + return compare (other) < 0; + } + + /** Ordered comparison. */ + bool operator<= (UnsignedIntegerCalc const& other) const + { + return compare (other) <= 0; + } + + /** Ordered comparison. */ + bool operator> (UnsignedIntegerCalc const& other) const + { + return compare (other) > 0; + } + + /** Ordered comparison. */ + bool operator>= (UnsignedIntegerCalc const& other) const + { + return compare (other) >= 0; + } + + /** Assign zero. */ + void clear () + { + UInt* dest (m_values - 1); + for (size_type n (size()); n--;) + *++dest = 0; + } + + /** Perform bitwise logical-not. */ + /* + UnsignedIntegerCalc& not () + { + unaryAssign (Not()); + return *this; + } + */ + + /** Perform bitwise logical-or. */ + UnsignedIntegerCalc& operator|= (UnsignedIntegerCalc const& rhs) + { + binaryAssign (rhs, Or()); + return *this; + } + + /** Perform bitwise logical-and. */ + UnsignedIntegerCalc& operator&= (UnsignedIntegerCalc const& rhs) + { + binaryAssign (rhs, And()); + return *this; + } + + /** Perform bitwise logical-xor. */ + UnsignedIntegerCalc& operator^= (UnsignedIntegerCalc const& rhs) + { + binaryAssign (rhs, Xor()); + return *this; + } + + /** Perform addition. */ + UnsignedIntegerCalc& operator+= (UnsignedIntegerCalc const& v) + { + UIntBig carry (0); + UInt* lhs (m_values); + UInt const* rhs (v.m_values - 1); + for (size_type n (0); n maxUInt) + { + part &= maxUInt; + carry = 1; + } + *lhs++ = UInt (part); + } + bassert (carry == 0) // overflow + return *this; + } + + /** Perform small addition. */ + UnsignedIntegerCalc& operator+= (UInt rhs) + { + UnsignedIntegerCalc const v (1, &rhs); + return operator+= (v); + } + + /** Perform small multiply. */ + UnsignedIntegerCalc& operator*= (UInt rhs) + { + UIntBig carry (0); + UInt* lhs (m_values - 1); + for (size_type n (size()); n--;) + { + UIntBig part (carry); + carry = 0; + part += (*++lhs) * UIntBig(rhs); + carry = part >> numBits; + *lhs = UInt (part & maxUInt); + } + bassert (carry == 0); // overflow + return *this; + } + + /** Small division. */ + UnsignedIntegerCalc operator/= (UInt rhs) + { + UIntBig dividend (0); + UInt* lhs (m_values+size()); + for (size_type n (size()); n--;) + { + dividend |= *--lhs; + *lhs = UInt (dividend / rhs); + dividend = (dividend % rhs) << numBits; + } + return *this; + } + + /** Small modulus. */ + UInt operator% (UInt rhs) const + { + UIntBig modsq = 1; + UIntBig result = 0; + UInt const* lhs (m_values); + for (size_type n (size()); n--; ++lhs) + { + for (int bit (0); bit < numBits; ++bit) + { + if (((*lhs) & (1 << bit)) != 0) + { + result += modsq; + if (result >= rhs) + result -= rhs; + } + modsq <<= 1; + if (modsq >= rhs) + modsq -= rhs; + } + } + return UInt (result); + } + +private: + struct Not { void operator() (UInt& rv) const { rv = ~rv; } }; + struct Or { void operator() (UInt& lhs, UInt rhs) const { lhs |= rhs; } }; + struct And { void operator() (UInt& lhs, UInt rhs) const { lhs &= rhs; } }; + struct Xor { void operator() (UInt& lhs, UInt rhs) const { lhs ^= rhs; } }; + + template + void unaryAssign (Operator op = Operator()) + { + UInt* dest (m_values-1); + for (size_type n (size()); n--;) + op (*++dest); + } + + template + void binaryAssign (UnsignedIntegerCalc const& other, Operator op = Operator ()) + { + UInt* dest (m_values + size()); + size_type n (size()); + for (; n-- > other.size();) + *--dest = 0; + UInt const* rhs (other.m_values + n); + for (UInt const* rhs (other.m_values + n); n--;) + op (*--dest, *--rhs); + } + + size_type m_size; + UInt* m_values; +}; + +#endif