diff --git a/CMakeLists.txt b/CMakeLists.txt index cc4d49f..69c0f94 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -98,9 +98,10 @@ find_package(QREncode REQUIRED) # bc-ur if(WITH_SCANNER) - find_path(BCUR_INCLUDE_DIR "bcur/bc-ur.hpp") - find_library(BCUR_LIBRARY bcur) - message(STATUS "bcur: libraries at ${BCUR_INCLUDE_DIR}") + find_package(BCUR REQUIRED) + if(BCUR_VENDORED) + add_subdirectory(src/third-party/bcur EXCLUDE_FROM_ALL) + endif() endif() # Polyseed diff --git a/cmake/FindBCUR.cmake b/cmake/FindBCUR.cmake new file mode 100644 index 0000000..671ce4c --- /dev/null +++ b/cmake/FindBCUR.cmake @@ -0,0 +1,12 @@ +find_path(BCUR_INCLUDE_DIR "bcur/bc-ur.hpp") +find_library(BCUR_LIBRARY bcur) + +if (NOT BCUR_INCLUDE_DIR OR NOT BCUR_LIBRARY) + MESSAGE(STATUS "Could not find installed BCUR, using vendored library instead") + set(BCUR_VENDORED "ON") + set(BCUR_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/src/third-party) + set(BCUR_LIBRARY bcur_static) +endif() + +message(STATUS "BCUR PATH ${BCUR_INCLUDE_DIR}") +message(STATUS "BCUR LIBRARY ${BCUR_LIBRARY}") \ No newline at end of file diff --git a/src/qrcode/scanner/QrCodeScanDialog.h b/src/qrcode/scanner/QrCodeScanDialog.h index 0527c1e..e8025f3 100644 --- a/src/qrcode/scanner/QrCodeScanDialog.h +++ b/src/qrcode/scanner/QrCodeScanDialog.h @@ -13,8 +13,6 @@ #include "QrScanThread.h" -#include - namespace Ui { class QrCodeScanDialog; } diff --git a/src/qrcode/scanner/QrCodeScanWidget.cpp b/src/qrcode/scanner/QrCodeScanWidget.cpp index 39edf2d..258059f 100644 --- a/src/qrcode/scanner/QrCodeScanWidget.cpp +++ b/src/qrcode/scanner/QrCodeScanWidget.cpp @@ -9,7 +9,6 @@ #include "utils/config.h" #include "utils/Icons.h" -#include QrCodeScanWidget::QrCodeScanWidget(QWidget *parent) : QWidget(parent) diff --git a/src/qrcode/scanner/QrCodeScanWidget.h b/src/qrcode/scanner/QrCodeScanWidget.h index a239ba4..059f6da 100644 --- a/src/qrcode/scanner/QrCodeScanWidget.h +++ b/src/qrcode/scanner/QrCodeScanWidget.h @@ -13,6 +13,7 @@ #include "QrScanThread.h" +#include #include namespace Ui { diff --git a/src/third-party/bcur/CMakeLists.txt b/src/third-party/bcur/CMakeLists.txt new file mode 100644 index 0000000..de5f8f3 --- /dev/null +++ b/src/third-party/bcur/CMakeLists.txt @@ -0,0 +1,21 @@ +cmake_minimum_required(VERSION 3.5) + +project(bcur) + +set(bcur_sources + bytewords.cpp + fountain-encoder.cpp + fountain-decoder.cpp + fountain-utils.cpp + xoshiro256.cpp + utils.cpp + random-sampler.cpp + ur-decoder.cpp + ur.cpp + ur-encoder.cpp + memzero.c + crc32.c + sha2.c) + +add_library(bcur_static STATIC ${bcur_sources}) +set_property(TARGET bcur_static PROPERTY POSITION_INDEPENDENT_CODE ON) diff --git a/src/third-party/bcur/README.md b/src/third-party/bcur/README.md new file mode 100644 index 0000000..478fce3 --- /dev/null +++ b/src/third-party/bcur/README.md @@ -0,0 +1,2 @@ +vendored from https://github.com/BlockchainCommons/bc-ur +2bfc3fd396498c2519273aeaa732abf7ea7d24b8 \ No newline at end of file diff --git a/src/third-party/bcur/bc-ur.hpp b/src/third-party/bcur/bc-ur.hpp new file mode 100644 index 0000000..2f695ee --- /dev/null +++ b/src/third-party/bcur/bc-ur.hpp @@ -0,0 +1,28 @@ +// +// bc-ur.hpp +// +// Copyright © 2020 by Blockchain Commons, LLC +// Licensed under the "BSD-2-Clause Plus Patent License" +// + +#ifndef BC_UR_HPP +#define BC_UR_HPP + +#include "ur.hpp" +#include "ur-encoder.hpp" +#include "ur-decoder.hpp" +#include "fountain-encoder.hpp" +#include "fountain-decoder.hpp" +#include "fountain-utils.hpp" +#include "utils.hpp" +#include "bytewords.hpp" +#include "xoshiro256.hpp" +#include "random-sampler.hpp" + +namespace ur { + +#include "cbor-lite.hpp" + +} + +#endif // BC_UR_HPP diff --git a/src/third-party/bcur/bytewords.cpp b/src/third-party/bcur/bytewords.cpp new file mode 100644 index 0000000..34e9d4e --- /dev/null +++ b/src/third-party/bcur/bytewords.cpp @@ -0,0 +1,169 @@ +// +// bytewords.cpp +// +// Copyright © 2020 by Blockchain Commons, LLC +// Licensed under the "BSD-2-Clause Plus Patent License" +// + +#include "bytewords.hpp" +#include "utils.hpp" +#include +#include + +namespace ur { + +using namespace std; + +static const char* bytewords = "ableacidalsoapexaquaarchatomauntawayaxisbackbaldbarnbeltbetabiasbluebodybragbrewbulbbuzzcalmcashcatschefcityclawcodecolacookcostcruxcurlcuspcyandarkdatadaysdelidicedietdoordowndrawdropdrumdulldutyeacheasyechoedgeepicevenexamexiteyesfactfairfernfigsfilmfishfizzflapflewfluxfoxyfreefrogfuelfundgalagamegeargemsgiftgirlglowgoodgraygrimgurugushgyrohalfhanghardhawkheathelphighhillholyhopehornhutsicedideaidleinchinkyintoirisironitemjadejazzjoinjoltjowljudojugsjumpjunkjurykeepkenokeptkeyskickkilnkingkitekiwiknoblamblavalazyleaflegsliarlimplionlistlogoloudloveluaulucklungmainmanymathmazememomenumeowmildmintmissmonknailnavyneednewsnextnoonnotenumbobeyoboeomitonyxopenovalowlspaidpartpeckplaypluspoempoolposepuffpumapurrquadquizraceramprealredorichroadrockroofrubyruinrunsrustsafesagascarsetssilkskewslotsoapsolosongstubsurfswantacotasktaxitenttiedtimetinytoiltombtoystriptunatwinuglyundouniturgeuservastveryvetovialvibeviewvisavoidvowswallwandwarmwaspwavewaxywebswhatwhenwhizwolfworkyankyawnyellyogayurtzapszerozestzinczonezoom"; + +uint8_t decode_word(const string& word, size_t word_len) { + if(word.length() != word_len) { + throw runtime_error("Invalid Bytewords."); + } + + static int16_t* array = NULL; + const size_t dim = 26; + + // Since the first and last letters of each Byteword are unique, + // we can use them as indexes into a two-dimensional lookup table. + // This table is generated lazily. + if(array == NULL) { + const size_t array_len = dim * dim; + array = (int16_t*)malloc(array_len * sizeof(int16_t)); + for(size_t i = 0; i < array_len; i++) { + array[i] = -1; + } + for(size_t i = 0; i < 256; i++) { + const char* byteword = bytewords + i * 4; + size_t x = byteword[0] - 'a'; + size_t y = byteword[3] - 'a'; + size_t offset = y * dim + x; + array[offset] = i; + } + } + + // If the coordinates generated by the first and last letters are out of bounds, + // or the lookup table contains -1 at the coordinates, then the word is not valid. + int x = tolower(word[0]) - 'a'; + int y = tolower(word[word_len == 4 ? 3 : 1]) - 'a'; + if(!(0 <= x && x < dim && 0 <= y && y < dim)) { + throw runtime_error("Invalid Bytewords."); + } + size_t offset = y * dim + x; + int16_t value = array[offset]; + if(value == -1) { + throw runtime_error("Invalid Bytewords."); + } + + // If we're decoding a full four-letter word, verify that the two middle letters are correct. + if(word_len == 4) { + const char* byteword = bytewords + value * 4; + int c1 = tolower(word[1]); + int c2 = tolower(word[2]); + if(c1 != byteword[1] || c2 != byteword[2]) { + throw runtime_error("Invalid Bytewords."); + } + } + + // Successful decode. + return value; +} + +static const string get_word(uint8_t index) { + auto p = &bytewords[index * 4]; + return string(p, p + 4); +} + +static const string get_minimal_word(uint8_t index) { + string word; + word.reserve(2); + auto p = &bytewords[index * 4]; + word.push_back(*p); + word.push_back(*(p + 3)); + return word; +} + +static const string encode(const ByteVector& buf, const string& separator) { + auto len = buf.size(); + StringVector words; + words.reserve(len); + for(int i = 0; i < len; i++) { + auto byte = buf[i]; + words.push_back(get_word(byte)); + } + return join(words, separator); +} + +static const ByteVector add_crc(const ByteVector& buf) { + auto crc_buf = crc32_bytes(buf); + auto result = buf; + append(result, crc_buf); + return result; +} + +static const string encode_with_separator(const ByteVector& buf, const string& separator) { + auto crc_buf = add_crc(buf); + return encode(crc_buf, separator); +} + +static const string encode_minimal(const ByteVector& buf) { + string result; + auto crc_buf = add_crc(buf); + auto len = crc_buf.size(); + for(int i = 0; i < len; i++) { + auto byte = crc_buf[i]; + result.append(get_minimal_word(byte)); + } + return result; +} + +static const ByteVector _decode(const string& s, char separator, size_t word_len) { + StringVector words; + if(word_len == 4) { + words = split(s, separator); + } else { + words = partition(s, 2); + } + ByteVector buf; + transform(words.begin(), words.end(), back_inserter(buf), [&](auto word) { return decode_word(word, word_len); }); + if(buf.size() < 5) { + throw runtime_error("Invalid Bytewords."); + } + auto p = split(buf, buf.size() - 4); + auto body = p.first; + auto body_checksum = p.second; + auto checksum = crc32_bytes(body); + if(checksum != body_checksum) { + throw runtime_error("Invalid Bytewords."); + } + + return body; +} + +string Bytewords::encode(style style, const ByteVector& bytes) { + switch(style) { + case standard: + return encode_with_separator(bytes, " "); + case uri: + return encode_with_separator(bytes, "-"); + case minimal: + return encode_minimal(bytes); + default: + assert(false); + } +} + +ByteVector Bytewords::decode(style style, const string& string) { + switch(style) { + case standard: + return _decode(string, ' ', 4); + case uri: + return _decode(string, '-', 4); + case minimal: + return _decode(string, 0, 2); + default: + assert(false); + } +} + +} diff --git a/src/third-party/bcur/bytewords.hpp b/src/third-party/bcur/bytewords.hpp new file mode 100644 index 0000000..427f2c3 --- /dev/null +++ b/src/third-party/bcur/bytewords.hpp @@ -0,0 +1,30 @@ +// +// bytewords.hpp +// +// Copyright © 2020 by Blockchain Commons, LLC +// Licensed under the "BSD-2-Clause Plus Patent License" +// + +#ifndef BC_UR_BYTEWORDS_HPP +#define BC_UR_BYTEWORDS_HPP + +#include +#include "utils.hpp" + +namespace ur { + +class Bytewords final { +public: + enum style { + standard, + uri, + minimal + }; + + static std::string encode(style style, const ByteVector& bytes); + static ByteVector decode(style style, const std::string& string); +}; + +} + +#endif // BC_UR_BYTEWORDS_HPP diff --git a/src/third-party/bcur/cbor-lite.hpp b/src/third-party/bcur/cbor-lite.hpp new file mode 100644 index 0000000..267474a --- /dev/null +++ b/src/third-party/bcur/cbor-lite.hpp @@ -0,0 +1,558 @@ +#ifndef BC_UR_CBOR_LITE_HPP +#define BC_UR_CBOR_LITE_HPP + +// From: https://bitbucket.org/isode/cbor-lite/raw/6c770624a97e3229e3f200be092c1b9c70a60ef1/include/cbor-lite/codec.h + +// This file is part of CBOR-lite which is copyright Isode Limited +// and others and released under a MIT license. For details, see the +// COPYRIGHT.md file in the top-level folder of the CBOR-lite software +// distribution. + +#include +#include +#include +#include +#include + +#ifndef __BYTE_ORDER__ +#error __BYTE_ORDER__ not defined +#elif (__BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__) && (__BYTE_ORDER__ != __ORDER_BIG_ENDIAN__) +#error __BYTE_ORDER__ neither __ORDER_BIG_ENDIAN__ nor __ORDER_LITTLE_ENDIAN__ +#endif + +namespace CborLite { + +class Exception : public std::exception { +public: + Exception() noexcept { + } + virtual ~Exception() noexcept = default; + + explicit Exception(const char* d) noexcept { + what_ += std::string(": ") + d; + } + + explicit Exception(const std::string& d) noexcept { + what_ += ": " + d; + } + + Exception(const Exception& e) noexcept : what_(e.what_) { + } + + Exception(Exception&& e) noexcept : what_(std::move(e.what_)) { + // Note that e.what_ is not re-initialized to "CBOR Exception" as + // the moved-from object is not expected to ever be reused. + } + + Exception& operator=(const Exception&) = delete; + Exception& operator=(Exception&&) = delete; + + virtual const char* what() const noexcept { + return what_.c_str(); + } + +private: + std::string what_ = "CBOR Exception"; +}; + +using Tag = std::uint_fast64_t; + +namespace Major { +constexpr Tag unsignedInteger = 0u; +constexpr Tag negativeInteger = 1u << 5; +constexpr Tag byteString = 2u << 5; +constexpr Tag textString = 3u << 5; +constexpr Tag array = 4u << 5; +constexpr Tag map = 5u << 5; +constexpr Tag semantic = 6u << 5; +constexpr Tag floatingPoint = 7u << 5; +constexpr Tag simple = 7u << 5; +constexpr Tag mask = 0xe0u; +} // namespace Major + +namespace Minor { +constexpr Tag length1 = 24u; +constexpr Tag length2 = 25u; +constexpr Tag length4 = 26u; +constexpr Tag length8 = 27u; + +constexpr Tag False = 20u; +constexpr Tag True = 21u; +constexpr Tag null = 22u; +constexpr Tag undefined = 23u; +constexpr Tag halfFloat = 25u; // not implemented +constexpr Tag singleFloat = 26u; +constexpr Tag doubleFloat = 27u; + +constexpr Tag dataTime = 0u; +constexpr Tag epochDataTime = 1u; +constexpr Tag positiveBignum = 2u; +constexpr Tag negativeBignum = 3u; +constexpr Tag decimalFraction = 4u; +constexpr Tag bigfloat = 5u; +constexpr Tag convertBase64Url = 21u; +constexpr Tag convertBase64 = 22u; +constexpr Tag convertBase16 = 23u; +constexpr Tag cborEncodedData = 24u; +constexpr Tag uri = 32u; +constexpr Tag base64Url = 33u; +constexpr Tag base64 = 34u; +constexpr Tag regex = 35u; +constexpr Tag mimeMessage = 36u; +constexpr Tag selfDescribeCbor = 55799u; + +constexpr Tag mask = 0x1fu; +} // namespace Minor + +constexpr Tag undefined = Major::semantic + Minor::undefined; + +using Flags = unsigned; +namespace Flag { +constexpr Flags none = 0; +constexpr Flags requireMinimalEncoding = 1 << 0; +} // namespace Flag + +template +typename std::enable_if::value, std::size_t>::type length(Type val) { + if (val < 24) return 0; + for (std::size_t i = 1; i <= ((sizeof val) >> 1); i <<= 1) { + if (!(val >> (i << 3))) return i; + } + return sizeof val; +} + +template +typename std::enable_if::value, std::size_t>::type encodeTagAndAdditional( + Buffer& buffer, Tag tag, Tag additional) { + buffer.push_back(static_cast(tag + additional)); + return 1; +} + +template +typename std::enable_if::value, std::size_t>::type decodeTagAndAdditional( + InputIterator& pos, InputIterator end, Tag& tag, Tag& additional, Flags = Flag::none) { + if (pos == end) throw Exception("not enough input"); + auto octet = *(pos++); + tag = octet & Major::mask; + additional = octet & Minor::mask; + return 1; +} + +template +typename std::enable_if::value && std::is_unsigned::value, std::size_t>::type encodeTagAndValue( + Buffer& buffer, Tag tag, const Type t) { + auto len = length(t); + buffer.reserve(buffer.size() + len + 1); + + switch (len) { + case 8: + encodeTagAndAdditional(buffer, tag, Minor::length8); + break; + case 4: + encodeTagAndAdditional(buffer, tag, Minor::length4); + break; + case 2: + encodeTagAndAdditional(buffer, tag, Minor::length2); + break; + case 1: + encodeTagAndAdditional(buffer, tag, Minor::length1); + break; + case 0: + return encodeTagAndAdditional(buffer, tag, t); + default: + throw Exception("too long"); + } + + switch (len) { + case 8: + buffer.push_back((t >> 56) & 0xffU); + buffer.push_back((t >> 48) & 0xffU); + buffer.push_back((t >> 40) & 0xffU); + buffer.push_back((t >> 32) & 0xffU); + case 4: + buffer.push_back((t >> 24) & 0xffU); + buffer.push_back((t >> 16) & 0xffU); + case 2: + buffer.push_back((t >> 8) & 0xffU); + case 1: + buffer.push_back(t & 0xffU); + } + + return 1 + len; +} + +template +typename std::enable_if::value && std::is_unsigned::value, std::size_t>::type decodeTagAndValue( + InputIterator& pos, InputIterator end, Tag& tag, Type& t, Flags flags = Flag::none) { + if (pos == end) throw Exception("not enough input"); + auto additional = Minor::undefined; + auto len = decodeTagAndAdditional(pos, end, tag, additional, flags); + if (additional < Minor::length1) { + t = additional; + return len; + } + t = 0u; + switch (additional) { + case Minor::length8: + if (std::distance(pos, end) < 8) throw Exception("not enough input"); + t |= static_cast(reinterpret_cast(*(pos++))) << 56; + t |= static_cast(reinterpret_cast(*(pos++))) << 48; + t |= static_cast(reinterpret_cast(*(pos++))) << 40; + t |= static_cast(reinterpret_cast(*(pos++))) << 32; + len += 4; + if ((flags & Flag::requireMinimalEncoding) && !t) throw Exception("encoding not minimal"); + case Minor::length4: + if (std::distance(pos, end) < 4) throw Exception("not enough input"); + t |= static_cast(reinterpret_cast(*(pos++))) << 24; + t |= static_cast(reinterpret_cast(*(pos++))) << 16; + len += 2; + if ((flags & Flag::requireMinimalEncoding) && !t) throw Exception("encoding not minimal"); + case Minor::length2: + if (std::distance(pos, end) < 2) throw Exception("not enough input"); + t |= static_cast(reinterpret_cast(*(pos++))) << 8; + len++; + if ((flags & Flag::requireMinimalEncoding) && !t) throw Exception("encoding not minimal"); + case Minor::length1: + if (std::distance(pos, end) < 1) throw Exception("not enough input"); + t |= static_cast(reinterpret_cast(*(pos++))); + len++; + if ((flags & Flag::requireMinimalEncoding) && t < 24) throw Exception("encoding not minimal"); + return len; + } + throw Exception("bad additional value"); +} + +template +typename std::enable_if::value, std::size_t>::type encodeUnsigned(Buffer& buffer, const Type& t) { + return encodeTagAndValue(buffer, Major::unsignedInteger, t); +} + +template +typename std::enable_if::value && std::is_unsigned::value && !std::is_const::value, + std::size_t>::type +decodeUnsigned(InputIterator& pos, InputIterator end, Type& t, Flags flags = Flag::none) { + auto tag = undefined; + auto len = decodeTagAndValue(pos, end, tag, t, flags); + if (tag != Major::unsignedInteger) throw Exception("not Unsigned"); + return len; +} + +template +typename std::enable_if::value, std::size_t>::type encodeNegative(Buffer& buffer, const Type& t) { + return encodeTagAndValue(buffer, Major::negativeInteger, t); +} + +template +typename std::enable_if::value && std::is_unsigned::value && !std::is_const::value, + std::size_t>::type +decodeNegative(InputIterator& pos, InputIterator end, Type& t, Flags flags = Flag::none) { + auto tag = undefined; + auto len = decodeTagAndValue(pos, end, tag, t, flags); + if (tag != Major::negativeInteger) throw Exception("not Unsigned"); + return len; +} + +template +typename std::enable_if::value, std::size_t>::type encodeInteger(Buffer& buffer, const Type& t) { + if (t >= 0) { + unsigned long long val = t; + return encodeUnsigned(buffer, val); + } else { + unsigned long long val = -t - 1; + return encodeNegative(buffer, val); + } +} + +template +typename std::enable_if::value && std::is_signed::value && !std::is_const::value, + std::size_t>::type +decodeInteger(InputIterator& pos, InputIterator end, Type& t, Flags flags = Flag::none) { + auto tag = undefined; + unsigned long long val; + auto len = decodeTagAndValue(pos, end, tag, val, flags); + switch (tag) { + case Major::unsignedInteger: + t = val; + break; + case Major::negativeInteger: + t = -1 - static_cast(val); + break; + default: + throw Exception("not integer"); + } + return len; +} + +template +typename std::enable_if::value && std::is_same::value, std::size_t>::type encodeBool( + Buffer& buffer, const Type& t) { + return encodeTagAndAdditional(buffer, Major::simple, t ? Minor::True : Minor::False); +} + +template +typename std::enable_if::value && std::is_same::value && !std::is_const::value, + std::size_t>::type +decodeBool(InputIterator& pos, InputIterator end, Type& t, Flags flags = Flag::none) { + auto tag = undefined; + auto value = undefined; + auto len = decodeTagAndValue(pos, end, tag, value, flags); + if (tag == Major::simple) { + if (value == Minor::True) { + t = true; + return len; + } else if (value == Minor::False) { + t = false; + return len; + } + throw Exception("not Boolean"); + } + throw Exception("not Simple"); +} + +template +typename std::enable_if::value, std::size_t>::type encodeBytes(Buffer& buffer, const Type& t) { + auto len = encodeTagAndValue(buffer, Major::byteString, t.size()); + buffer.insert(std::end(buffer), std::begin(t), std::end(t)); + return len + t.size(); +} + +template +typename std::enable_if::value && !std::is_const::value, std::size_t>::type decodeBytes( + InputIterator& pos, InputIterator end, Type& t, Flags flags = Flag::none) { + auto tag = undefined; + auto value = undefined; + auto len = decodeTagAndValue(pos, end, tag, value, flags); + if (tag != Major::byteString) throw Exception("not ByteString"); + + auto dist = std::distance(pos, end); + if (dist < static_cast(value)) throw Exception("not enough input"); + t.insert(std::end(t), pos, pos + value); + std::advance(pos, value); + return len + value; +} + +template +typename std::enable_if::value && std::is_unsigned::value, std::size_t>::type encodeEncodedBytesPrefix( + Buffer& buffer, const Type& t) { + auto len = encodeTagAndValue(buffer, Major::semantic, Minor::cborEncodedData); + return len + encodeTagAndValue(buffer, Major::byteString, t); +} + +template +typename std::enable_if::value && !std::is_const::value, std::size_t>::type +decodeEncodedBytesPrefix(InputIterator& pos, InputIterator end, Type& t, Flags flags = Flag::none) { + auto tag = undefined; + auto value = undefined; + auto len = decodeTagAndValue(pos, end, tag, value, flags); + if (tag != Major::semantic || value != Minor::cborEncodedData) { + throw Exception("not CBOR Encoded Data"); + } + tag = undefined; + len += decodeTagAndValue(pos, end, tag, value, flags); + if (tag != Major::byteString) throw Exception("not ByteString"); + t = value; + return len; +} + +template +typename std::enable_if::value, std::size_t>::type encodeEncodedBytes(Buffer& buffer, const Type& t) { + auto len = encodeTagAndValue(buffer, Major::semantic, Minor::cborEncodedData); + return len + encodeBytes(buffer, t); +} + +template +typename std::enable_if::value && !std::is_const::value, std::size_t>::type decodeEncodedBytes( + InputIterator& pos, InputIterator end, Type& t, Flags flags = Flag::none) { + auto tag = undefined; + auto value = undefined; + auto len = decodeTagAndValue(pos, end, tag, value, flags); + if (tag != Major::semantic || value != Minor::cborEncodedData) { + throw Exception("not CBOR Encoded Data"); + } + return len + decodeBytes(pos, end, t, flags); +} + +template +typename std::enable_if::value, std::size_t>::type encodeText(Buffer& buffer, const Type& t) { + auto len = encodeTagAndValue(buffer, Major::textString, t.size()); + buffer.insert(std::end(buffer), std::begin(t), std::end(t)); + return len + t.size(); +} + +template +typename std::enable_if::value && !std::is_const::value, std::size_t>::type decodeText( + InputIterator& pos, InputIterator end, Type& t, Flags flags = Flag::none) { + auto tag = undefined; + auto value = undefined; + auto len = decodeTagAndValue(pos, end, tag, value, flags); + if (tag != Major::textString) throw Exception("not TextString"); + + auto dist = std::distance(pos, end); + if (dist < static_cast(value)) throw Exception("not enough input"); + t.insert(std::end(t), pos, pos + value); + std::advance(pos, value); + return len + value; +} + +template +typename std::enable_if::value && std::is_unsigned::value, std::size_t>::type encodeArraySize( + Buffer& buffer, const Type& t) { + return encodeTagAndValue(buffer, Major::array, t); +} + +template +typename std::enable_if::value && !std::is_const::value && std::is_unsigned::value, + std::size_t>::type +decodeArraySize(InputIterator& pos, InputIterator end, Type& t, Flags flags = Flag::none) { + auto tag = undefined; + auto value = undefined; + auto len = decodeTagAndValue(pos, end, tag, value, flags); + if (tag != Major::array) throw Exception("not Array"); + t = value; + return len; +} + +template +typename std::enable_if::value && std::is_unsigned::value, std::size_t>::type encodeMapSize( + Buffer& buffer, const Type& t) { + return encodeTagAndValue(buffer, Major::map, t); +} + +template +typename std::enable_if::value && !std::is_const::value && std::is_unsigned::value, + std::size_t>::type +decodeMapSize(InputIterator& pos, InputIterator end, Type& t, Flags flags = Flag::none) { + auto tag = undefined; + auto value = undefined; + auto len = decodeTagAndValue(pos, end, tag, value, flags); + if (tag != Major::map) throw Exception("not Map"); + t = value; + return len; +} + +// +// codec-fp.h +// + +template +typename std::enable_if::value && std::is_floating_point::value, std::size_t>::type encodeSingleFloat( + Buffer& buffer, const Type& t) { + static_assert(sizeof(float) == 4, "sizeof(float) expected to be 4"); + auto len = encodeTagAndAdditional(buffer, Major::floatingPoint, Minor::singleFloat); + const char* p; + float ft; + if (sizeof(t) == sizeof(ft)) { + p = reinterpret_cast(&t); + } else { + ft = static_cast(t); + p = reinterpret_cast(&ft); + } +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + for (auto i = 0u; i < sizeof(ft); ++i) { + buffer.push_back(p[i]); + } +#else + for (auto i = 1u; i <= sizeof(ft); ++i) { + buffer.push_back(p[sizeof(ft) - i]); + } +#endif + return len + sizeof(ft); +} + +template +typename std::enable_if::value && std::is_floating_point::value, std::size_t>::type encodeDoubleFloat( + Buffer& buffer, const Type& t) { + static_assert(sizeof(double) == 8, "sizeof(double) expected to be 8"); + auto len = encodeTagAndAdditional(buffer, Major::floatingPoint, Minor::doubleFloat); + const char* p; + double ft; + if (sizeof(t) == sizeof(ft)) { + p = reinterpret_cast(&t); + } else { + ft = t; + p = reinterpret_cast(&ft); + } +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + for (auto i = 0u; i < sizeof(ft); ++i) { + buffer.push_back(p[i]); + } +#else + for (auto i = 1u; i <= sizeof(ft); ++i) { + buffer.push_back(p[sizeof(ft) - i]); + } +#endif + return len + sizeof(ft); +} + +template +typename std::enable_if::value && std::is_floating_point::value && !std::is_const::value, + std::size_t>::type +decodeSingleFloat(InputIterator& pos, InputIterator end, Type& t, Flags flags = Flag::none) { + static_assert(sizeof(float) == 4, "sizeof(float) expected to be 4"); + auto tag = undefined; + auto value = undefined; + auto len = decodeTagAndAdditional(pos, end, tag, value, flags); + if (tag != Major::floatingPoint) throw Exception("not floating-point"); + if (value != Minor::singleFloat) throw Exception("not single-precision floating-point"); + if (std::distance(pos, end) < static_cast(sizeof(float))) throw Exception("not enough input"); + + char* p; + float ft; + if (sizeof(t) == sizeof(ft)) { + p = reinterpret_cast(&t); + } else { + ft = static_cast(t); + p = reinterpret_cast(&ft); + } + +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + for (auto i = 0u; i < sizeof(ft); ++i) { + p[i] = *(pos++); + } +#else + for (auto i = 1u; i <= sizeof(ft); ++i) { + p[sizeof(ft) - i] = *(pos++); + } +#endif + if (sizeof(t) != sizeof(ft)) t = ft; + return len + sizeof(ft); +} + +template +typename std::enable_if::value && std::is_floating_point::value && !std::is_const::value, + std::size_t>::type +decodeDoubleFloat(InputIterator& pos, InputIterator end, Type& t, Flags flags = Flag::none) { + static_assert(sizeof(double) == 8, "sizeof(double) expected to be 8"); + auto tag = undefined; + auto value = undefined; + auto len = decodeTagAndAdditional(pos, end, tag, value, flags); + if (tag != Major::floatingPoint) throw Exception("not floating-point"); + if (value != Minor::doubleFloat) throw Exception("not double-precision floating-point"); + if (std::distance(pos, end) < static_cast(sizeof(double))) throw Exception("not enough input"); + + char* p; + double ft; + if (sizeof(t) == sizeof(ft)) { + p = reinterpret_cast(&t); + } else { + ft = t; + p = reinterpret_cast(&ft); + } + +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + for (auto i = 0u; i < sizeof(ft); ++i) { + p[i] = *(pos++); + } +#else + for (auto i = 1u; i <= sizeof(ft); ++i) { + p[sizeof(ft) - i] = *(pos++); + } +#endif + + if (sizeof(t) != sizeof(ft)) t = ft; + return len + sizeof(ft); +} + +} // namespace CborLite + +#endif // BC_UR_CBOR_LITE_HPP diff --git a/src/third-party/bcur/crc32.c b/src/third-party/bcur/crc32.c new file mode 100644 index 0000000..38e739a --- /dev/null +++ b/src/third-party/bcur/crc32.c @@ -0,0 +1,43 @@ +// +// crc32.c +// +// Copyright © 2020 by Blockchain Commons, LLC +// Licensed under the "BSD-2-Clause Plus Patent License" +// + +#include "crc32.h" +#include + +#ifdef ARDUINO +#define htonl(x) __builtin_bswap32((uint32_t) (x)) +#elif _WIN32 +#include +#else +#include +#endif + +uint32_t ur_crc32(const uint8_t* bytes, size_t len) { + static uint32_t* table = NULL; + + if(table == NULL) { + table = malloc(256 * sizeof(uint32_t)); + for(int i = 0; i < 256; i++) { + uint32_t c = i; + for(int j = 0; j < 8; j++) { + c = (c % 2 == 0) ? (c >> 1) : (0xEDB88320 ^ (c >> 1)); + } + table[i] = c; + } + } + + uint32_t crc = ~0; + for(int i = 0; i < len; i++) { + uint32_t byte = bytes[i]; + crc = (crc >> 8) ^ table[(crc ^ byte) & 0xFF]; + } + return ~crc; +} + +uint32_t ur_crc32n(const uint8_t* bytes, size_t len) { + return htonl(ur_crc32(bytes, len)); +} diff --git a/src/third-party/bcur/crc32.h b/src/third-party/bcur/crc32.h new file mode 100644 index 0000000..cfa38ef --- /dev/null +++ b/src/third-party/bcur/crc32.h @@ -0,0 +1,28 @@ +// +// crc32.h +// +// Copyright © 2020 by Blockchain Commons, LLC +// Licensed under the "BSD-2-Clause Plus Patent License" +// + +#ifndef BC_UR_CRC32_H +#define BC_UR_CRC32_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// Returns the CRC-32 checksum of the input buffer. +uint32_t ur_crc32(const uint8_t* bytes, size_t len); + +// Returns the CRC-32 checksum of the input buffer in network byte order (big endian). +uint32_t ur_crc32n(const uint8_t* bytes, size_t len); + +#ifdef __cplusplus +} +#endif + +#endif // BC_UR_CRC32_H diff --git a/src/third-party/bcur/fountain-decoder.cpp b/src/third-party/bcur/fountain-decoder.cpp new file mode 100644 index 0000000..e236062 --- /dev/null +++ b/src/third-party/bcur/fountain-decoder.cpp @@ -0,0 +1,253 @@ +// +// fountain-decoder.cpp +// +// Copyright © 2020 by Blockchain Commons, LLC +// Licensed under the "BSD-2-Clause Plus Patent License" +// + +#include "fountain-decoder.hpp" +#include +#include +#include +#include +#include +#include + +using namespace std; + +namespace ur { + +FountainDecoder::FountainDecoder() { } + +FountainDecoder::Part::Part(const FountainEncoder::Part& p) + : indexes_(choose_fragments(p.seq_num(), p.seq_len(), p.checksum())) + , data_(p.data()) +{ +} + +FountainDecoder::Part::Part(PartIndexes& indexes, ByteVector& data) + : indexes_(indexes) + , data_(data) +{ +} + +const ByteVector FountainDecoder::join_fragments(const vector& fragments, size_t message_len) { + auto message = join(fragments); + return take_first(message, message_len); +} + +double FountainDecoder::estimated_percent_complete() const { + if(is_complete()) return 1; + if(!_expected_part_indexes.has_value()) return 0; + auto estimated_input_parts = expected_part_count() * 1.75; + return min(0.99, processed_parts_count_ / estimated_input_parts); +} + +bool FountainDecoder::receive_part(FountainEncoder::Part& encoder_part) { + // Don't process the part if we're already done + if(is_complete()) return false; + + // Don't continue if this part doesn't validate + if(!validate_part(encoder_part)) return false; + + // Add this part to the queue + auto p = Part(encoder_part); + last_part_indexes_ = p.indexes(); + enqueue(p); + + // Process the queue until we're done or the queue is empty + while(!is_complete() && !_queued_parts.empty()) { + process_queue_item(); + } + + // Keep track of how many parts we've processed + processed_parts_count_ += 1; + + //print_part_end(); + + return true; +} + +void FountainDecoder::enqueue(Part &&p) { + _queued_parts.push_back(p); +} + +void FountainDecoder::enqueue(const Part &p) { + _queued_parts.push_back(p); +} + +void FountainDecoder::process_queue_item() { + auto part = _queued_parts.front(); + //print_part(part); + _queued_parts.pop_front(); + if(part.is_simple()) { + process_simple_part(part); + } else { + process_mixed_part(part); + } + //print_state(); +} + +void FountainDecoder::reduce_mixed_by(const Part& p) { + // Reduce all the current mixed parts by the given part + vector reduced_parts; + for(auto i = _mixed_parts.begin(); i != _mixed_parts.end(); i++) { + reduced_parts.push_back(reduce_part_by_part(i->second, p)); + } + + // Collect all the remaining mixed parts + PartDict new_mixed; + for(auto reduced_part: reduced_parts) { + // If this reduced part is now simple + if(reduced_part.is_simple()) { + // Add it to the queue + enqueue(reduced_part); + } else { + // Otherwise, add it to the list of current mixed parts + new_mixed.insert(pair(reduced_part.indexes(), reduced_part)); + } + } + _mixed_parts = new_mixed; +} + +FountainDecoder::Part FountainDecoder::reduce_part_by_part(const Part& a, const Part& b) const { + // If the fragments mixed into `b` are a strict (proper) subset of those in `a`... + if(is_strict_subset(b.indexes(), a.indexes())) { + // The new fragments in the revised part are `a` - `b`. + auto new_indexes = set_difference(a.indexes(), b.indexes()); + // The new data in the revised part are `a` XOR `b` + auto new_data = xor_with(a.data(), b.data()); + return Part(new_indexes, new_data); + } else { + // `a` is not reducable by `b`, so return a + return a; + } +} + +void FountainDecoder::process_simple_part(Part& p) { + // Don't process duplicate parts + auto fragment_index = p.index(); + if(contains(received_part_indexes_, fragment_index)) return; + + // Record this part + _simple_parts.insert(pair(p.indexes(), p)); + received_part_indexes_.insert(fragment_index); + + // If we've received all the parts + if(received_part_indexes_ == _expected_part_indexes) { + // Reassemble the message from its fragments + vector sorted_parts; + transform(_simple_parts.begin(), _simple_parts.end(), back_inserter(sorted_parts), [&](auto elem) { return elem.second; }); + sort(sorted_parts.begin(), sorted_parts.end(), + [](const Part& a, const Part& b) -> bool { + return a.index() < b.index(); + } + ); + vector fragments; + transform(sorted_parts.begin(), sorted_parts.end(), back_inserter(fragments), [&](auto part) { return part.data(); }); + auto message = join_fragments(fragments, *_expected_message_len); + + // Verify the message checksum and note success or failure + auto checksum = crc32_int(message); + if(checksum == _expected_checksum) { + result_ = message; + } else { + result_ = InvalidChecksum(); + } + } else { + // Reduce all the mixed parts by this part + reduce_mixed_by(p); + } +} + +void FountainDecoder::process_mixed_part(const Part& p) { + // Don't process duplicate parts + if(any_of(_mixed_parts.begin(), _mixed_parts.end(), [&](auto r) { return r.first == p.indexes(); })) { + return; + } + + // Reduce this part by all the others + auto p2 = accumulate(_simple_parts.begin(), _simple_parts.end(), p, [&](auto p, auto r) { return reduce_part_by_part(p, r.second); }); + p2 = accumulate(_mixed_parts.begin(), _mixed_parts.end(), p2, [&](auto p, auto r) { return reduce_part_by_part(p, r.second); }); + + // If the part is now simple + if(p2.is_simple()) { + // Add it to the queue + enqueue(p2); + } else { + // Reduce all the mixed parts by this one + reduce_mixed_by(p2); + // Record this new mixed part + _mixed_parts.insert(pair(p2.indexes(), p2)); + } +} + +bool FountainDecoder::validate_part(const FountainEncoder::Part& p) { + // If this is the first part we've seen + if(!_expected_part_indexes.has_value()) { + // Record the things that all the other parts we see will have to match to be valid. + _expected_part_indexes = PartIndexes(); + for(size_t i = 0; i < p.seq_len(); i++) { _expected_part_indexes->insert(i); } + _expected_message_len = p.message_len(); + _expected_checksum = p.checksum(); + _expected_fragment_len = p.data().size(); + } else { + // If this part's values don't match the first part's values, throw away the part + if(expected_part_count() != p.seq_len()) return false; + if(_expected_message_len != p.message_len()) return false; + if(_expected_checksum != p.checksum()) return false; + if(_expected_fragment_len != p.data().size()) return false; + } + // This part should be processed + return true; +} + +string FountainDecoder::indexes_to_string(const PartIndexes& indexes) { + auto i = vector(indexes.begin(), indexes.end()); + sort(i.begin(), i.end()); + StringVector s; + transform(i.begin(), i.end(), back_inserter(s), [](size_t a) { return to_string(a); }); + return "[" + join(s, ", ") + "]"; +} + +void FountainDecoder::print_part(const Part& p) const { + cout << "part indexes: " << indexes_to_string(p.indexes()) << endl; +} + +void FountainDecoder::print_part_end() const { + auto expected = _expected_part_indexes.has_value() ? to_string(expected_part_count()) : "nil"; + auto percent = int(round(estimated_percent_complete() * 100)); + cout << "processed: " << processed_parts_count_ << ", expected: " << expected << ", received: " << received_part_indexes_.size() << ", percent: " << percent << "%" << endl; +} + +string FountainDecoder::result_description() const { + string desc; + if(!result_.has_value()) { + desc = "nil"; + } else { + auto r = *result_; + if(holds_alternative(r)) { + desc = to_string(get(r).size()) + " bytes"; + } else if(holds_alternative(r)) { + desc = get(r).what(); + } else { + assert(false); + } + } + return desc; +} + +void FountainDecoder::print_state() const { + auto parts = _expected_part_indexes.has_value() ? to_string(expected_part_count()) : "nil"; + auto received = indexes_to_string(received_part_indexes_); + StringVector mixed; + transform(_mixed_parts.begin(), _mixed_parts.end(), back_inserter(mixed), [](const pair& p) { + return indexes_to_string(p.first); + }); + auto mixed_s = "[" + join(mixed, ", ") + "]"; + auto queued = _queued_parts.size(); + auto res = result_description(); + cout << "parts: " << parts << ", received: " << received << ", mixed: " << mixed_s << ", queued: " << queued << ", result: " << res << endl; +} + +} diff --git a/src/third-party/bcur/fountain-decoder.hpp b/src/third-party/bcur/fountain-decoder.hpp new file mode 100644 index 0000000..6dc92d7 --- /dev/null +++ b/src/third-party/bcur/fountain-decoder.hpp @@ -0,0 +1,103 @@ +// +// fountain-decoder.hpp +// +// Copyright © 2020 by Blockchain Commons, LLC +// Licensed under the "BSD-2-Clause Plus Patent License" +// + +#ifndef BC_UR_FOUNTAIN_DECODER_HPP +#define BC_UR_FOUNTAIN_DECODER_HPP + +#include "utils.hpp" +#include "fountain-encoder.hpp" +#include +#include +#include +#include +#include + +namespace ur { + +class FountainDecoder final { +public: + typedef std::optional > Result; + + class InvalidPart: public std::exception { }; + class InvalidChecksum: public std::exception { }; + + FountainDecoder(); + + size_t expected_part_count() const { return _expected_part_indexes.value().size(); } + const PartIndexes& received_part_indexes() const { return received_part_indexes_; } + const PartIndexes& last_part_indexes() const { return last_part_indexes_.value(); } + size_t processed_parts_count() const { return processed_parts_count_; } + const Result& result() const { return result_; } + bool is_success() const { return result() && std::holds_alternative(result().value()); } + bool is_failure() const { return result() && std::holds_alternative(result().value()); } + bool is_complete() const { return result().has_value(); } + const ByteVector& result_message() const { return std::get(result().value()); } + const std::exception& result_error() const { return std::get(result().value()); } + + double estimated_percent_complete() const; + bool receive_part(FountainEncoder::Part& encoder_part); + + // Join all the fragments of a message together, throwing away any padding + static const ByteVector join_fragments(const std::vector& fragments, size_t message_len); + +private: + class Part { + private: + PartIndexes indexes_; + ByteVector data_; + + public: + explicit Part(const FountainEncoder::Part& p); + Part(PartIndexes& indexes, ByteVector& data); + + const PartIndexes& indexes() const { return indexes_; } + const ByteVector& data() const { return data_; } + bool is_simple() const { return indexes_.size() == 1; } + size_t index() const { return *indexes_.begin(); } + }; + + PartIndexes received_part_indexes_; + std::optional last_part_indexes_; + size_t processed_parts_count_ = 0; + + Result result_; + + typedef std::map PartDict; + + std::optional _expected_part_indexes; + std::optional _expected_fragment_len; + std::optional _expected_message_len; + std::optional _expected_checksum; + + PartDict _simple_parts; + PartDict _mixed_parts; + std::deque _queued_parts; + + void enqueue(const Part &p); + void enqueue(Part &&p); + void process_queue_item(); + void reduce_mixed_by(const Part& p); + Part reduce_part_by_part(const Part& a, const Part& b) const; + void process_simple_part(Part& p); + void process_mixed_part(const Part& p); + bool validate_part(const FountainEncoder::Part& p); + + // debugging + static std::string indexes_to_string(const PartIndexes& indexes); + std::string result_description() const; + + // cppcheck-suppress unusedPrivateFunction + void print_part(const Part& p) const; + // cppcheck-suppress unusedPrivateFunction + void print_part_end() const; + // cppcheck-suppress unusedPrivateFunction + void print_state() const; +}; + +} + +#endif // BC_UR_FOUNTAIN_DECODER_HPP diff --git a/src/third-party/bcur/fountain-encoder.cpp b/src/third-party/bcur/fountain-encoder.cpp new file mode 100644 index 0000000..f7f6cf9 --- /dev/null +++ b/src/third-party/bcur/fountain-encoder.cpp @@ -0,0 +1,126 @@ +// +// fountain-encoder.cpp +// +// Copyright © 2020 by Blockchain Commons, LLC +// Licensed under the "BSD-2-Clause Plus Patent License" +// + +#include "fountain-encoder.hpp" +#include +#include +#include +#include +#include +#include "cbor-lite.hpp" + +using namespace std; + +namespace ur { + +size_t FountainEncoder::find_nominal_fragment_length(size_t message_len, size_t min_fragment_len, size_t max_fragment_len) { + assert(message_len > 0); + assert(min_fragment_len > 0); + assert(max_fragment_len >= min_fragment_len); + auto max_fragment_count = message_len / min_fragment_len; + optional fragment_len; + for(size_t fragment_count = 1; fragment_count <= max_fragment_count; fragment_count++) { + fragment_len = size_t(ceil(double(message_len) / fragment_count)); + if(fragment_len <= max_fragment_len) { + break; + } + } + assert(fragment_len.has_value()); + return *fragment_len; +} + +vector FountainEncoder::partition_message(const ByteVector &message, size_t fragment_len) { + auto remaining = message; + vector fragments; + while(!remaining.empty()) { + auto a = split(remaining, fragment_len); + auto fragment = a.first; + remaining = a.second; + auto padding = fragment_len - fragment.size(); + while(padding > 0) { + fragment.push_back(0); + padding--; + } + fragments.push_back(fragment); + } + return fragments; +} + +FountainEncoder::Part::Part(const ByteVector& cbor) { + try { + auto i = cbor.begin(); + auto end = cbor.end(); + size_t array_size; + CborLite::decodeArraySize(i, end, array_size); + if(array_size != 5) { throw InvalidHeader(); } + + uint64_t n; + + CborLite::decodeUnsigned(i, end, n); + if(n > std::numeric_limits::max()) { throw InvalidHeader(); } + seq_num_ = n; + + CborLite::decodeUnsigned(i, end, n); + if(n > std::numeric_limits::max()) { throw InvalidHeader(); } + seq_len_ = n; + + CborLite::decodeUnsigned(i, end, n); + if(n > std::numeric_limits::max()) { throw InvalidHeader(); } + message_len_ = n; + + CborLite::decodeUnsigned(i, end, n); + if(n > std::numeric_limits::max()) { throw InvalidHeader(); } + checksum_ = n; + + CborLite::decodeBytes(i, end, data_); + } catch(...) { + throw InvalidHeader(); + } +} + +ByteVector FountainEncoder::Part::cbor() const { + using namespace CborLite; + + ByteVector result; + + encodeArraySize(result, (size_t)5); + encodeInteger(result, seq_num()); + encodeInteger(result, seq_len()); + encodeInteger(result, message_len()); + encodeInteger(result, checksum()); + encodeBytes(result, data()); + + return result; +} + +FountainEncoder::FountainEncoder(const ByteVector& message, size_t max_fragment_len, uint32_t first_seq_num, size_t min_fragment_len) { + assert(message.size() <= std::numeric_limits::max()); + message_len_ = message.size(); + checksum_ = crc32_int(message); + fragment_len_ = find_nominal_fragment_length(message_len_, min_fragment_len, max_fragment_len); + fragments_ = partition_message(message, fragment_len_); + seq_num_ = first_seq_num; +} + +ByteVector FountainEncoder::mix(const PartIndexes& indexes) const { + ByteVector result(fragment_len_, 0); + for(auto index: indexes) { xor_into(result, fragments_[index]); } + return result; +} + +FountainEncoder::Part FountainEncoder::next_part() { + seq_num_ += 1; // wrap at period 2^32 + auto indexes = choose_fragments(seq_num_, seq_len(), checksum_); + auto mixed = mix(indexes); + return Part(seq_num_, seq_len(), message_len_, checksum_, mixed); +} + +string FountainEncoder::Part::description() const { + return "seqNum:" + to_string(seq_num_) + ", seqLen:" + to_string(seq_len_) + ", messageLen:" + to_string(message_len_) + ", checksum:" + to_string(checksum_) + ", data:" + data_to_hex(data_); +} + +} diff --git a/src/third-party/bcur/fountain-encoder.hpp b/src/third-party/bcur/fountain-encoder.hpp new file mode 100644 index 0000000..1381dd5 --- /dev/null +++ b/src/third-party/bcur/fountain-encoder.hpp @@ -0,0 +1,81 @@ +// +// fountain-encoder.hpp +// +// Copyright © 2020 by Blockchain Commons, LLC +// Licensed under the "BSD-2-Clause Plus Patent License" +// + +#ifndef BC_UR_FOUNTAIN_ENCODER_HPP +#define BC_UR_FOUNTAIN_ENCODER_HPP + +#include +#include +#include +#include "utils.hpp" +#include "fountain-utils.hpp" + +namespace ur { + +// Implements Luby transform code rateless coding +// https://en.wikipedia.org/wiki/Luby_transform_code + +class FountainEncoder final { +public: + class Part { + public: + class InvalidHeader: public std::exception { }; + + Part(uint32_t seq_num, size_t seq_len, size_t message_len, uint32_t checksum, const ByteVector& data) + : seq_num_(seq_num), seq_len_(seq_len), message_len_(message_len), checksum_(checksum), data_(data) + { } + explicit Part(const ByteVector& cbor); + + uint32_t seq_num() const { return seq_num_; } + size_t seq_len() const { return seq_len_; } + size_t message_len() const { return message_len_; } + uint32_t checksum() const { return checksum_; } + const ByteVector& data() const { return data_; } + + ByteVector cbor() const; + std::string description() const; + + private: + uint32_t seq_num_; + size_t seq_len_; + size_t message_len_; + uint32_t checksum_; + ByteVector data_; + }; + + FountainEncoder(const ByteVector& message, size_t max_fragment_len, uint32_t first_seq_num = 0, size_t min_fragment_len = 10); + + static size_t find_nominal_fragment_length(size_t message_len, size_t min_fragment_len, size_t max_fragment_len); + static std::vector partition_message(const ByteVector &message, size_t fragment_len); + + uint32_t seq_num() const { return seq_num_; } + const PartIndexes& last_part_indexes() const { return last_part_indexes_; } + size_t seq_len() const { return fragments_.size(); } + + // This becomes `true` when the minimum number of parts + // to relay the complete message have been generated + bool is_complete() const { return seq_num_ >= seq_len(); } + + /// True if only a single part will be generated. + bool is_single_part() const { return seq_len() == 1; } + + Part next_part(); + +private: + size_t message_len_; + uint32_t checksum_; + size_t fragment_len_; + std::vector fragments_; + uint32_t seq_num_; + PartIndexes last_part_indexes_; + + ByteVector mix(const PartIndexes& indexes) const; +}; + +} + +#endif // BC_UR_FOUNTAIN_ENCODER_HPP diff --git a/src/third-party/bcur/fountain-utils.cpp b/src/third-party/bcur/fountain-utils.cpp new file mode 100644 index 0000000..fa59118 --- /dev/null +++ b/src/third-party/bcur/fountain-utils.cpp @@ -0,0 +1,43 @@ +// +// fountain-utils.cpp +// +// Copyright © 2020 by Blockchain Commons, LLC +// Licensed under the "BSD-2-Clause Plus Patent License" +// + +#include "fountain-utils.hpp" +#include "random-sampler.hpp" +#include "utils.hpp" + +using namespace std; + +namespace ur { + +size_t choose_degree(size_t seq_len, Xoshiro256& rng) { + vector degree_probabilities; + for(int i = 1; i <= seq_len; i++) { + degree_probabilities.push_back(1.0 / i); + } + auto degree_chooser = RandomSampler(degree_probabilities); + return degree_chooser.next([&]() { return rng.next_double(); }) + 1; +} + +set choose_fragments(uint32_t seq_num, size_t seq_len, uint32_t checksum) { + // The first `seq_len` parts are the "pure" fragments, not mixed with any + // others. This means that if you only generate the first `seq_len` parts, + // then you have all the parts you need to decode the message. + if(seq_num <= seq_len) { + return set({seq_num - 1}); + } else { + auto seed = join(vector({int_to_bytes(seq_num), int_to_bytes(checksum)})); + auto rng = Xoshiro256(seed); + auto degree = choose_degree(seq_len, rng); + vector indexes; + indexes.reserve(seq_len); + for(int i = 0; i < seq_len; i++) { indexes.push_back(i); } + auto shuffled_indexes = shuffled(indexes, rng); + return set(shuffled_indexes.begin(), shuffled_indexes.begin() + degree); + } +} + +} diff --git a/src/third-party/bcur/fountain-utils.hpp b/src/third-party/bcur/fountain-utils.hpp new file mode 100644 index 0000000..602cef7 --- /dev/null +++ b/src/third-party/bcur/fountain-utils.hpp @@ -0,0 +1,61 @@ +// +// fountain-utils.hpp +// +// Copyright © 2020 by Blockchain Commons, LLC +// Licensed under the "BSD-2-Clause Plus Patent License" +// + +#ifndef BC_UR_FOUNTAIN_UTILS_HPP +#define BC_UR_FOUNTAIN_UTILS_HPP + +#include +#include +#include +#include +#include +#include +#include "xoshiro256.hpp" + +namespace ur { + +typedef std::set PartIndexes; + +// Fisher-Yates shuffle +template +std::vector shuffled(const std::vector& items, Xoshiro256& rng) { + auto remaining = items; + std::vector result; + while(!remaining.empty()) { + auto index = rng.next_int(0, remaining.size() - 1); + auto item = remaining[index]; + remaining.erase(remaining.begin() + index); + result.push_back(item); + } + return result; +} + +// Return `true` if `a` is a strict subset of `b`. +template +bool is_strict_subset(const std::set& a, const std::set& b) { + if(a == b) { return false; } + return std::includes(b.begin(), b.end(), a.begin(), a.end()); +} + +template +std::set set_difference(const std::set& a, const std::set& b) { + std::set result; + std::set_difference(a.begin(), a.end(), b.begin(), b.end(), std::inserter(result, result.begin())); + return result; +} + +template +bool contains(const std::set& s, const T& v) { + return s.find(v) != s.end(); +} + +size_t choose_degree(size_t seq_len, Xoshiro256& rng); +std::set choose_fragments(uint32_t seq_num, size_t seq_len, uint32_t checksum); + +} + +#endif // BC_UR_FOUNTAIN_UTILS_HPP diff --git a/src/third-party/bcur/memzero.c b/src/third-party/bcur/memzero.c new file mode 100644 index 0000000..5edc797 --- /dev/null +++ b/src/third-party/bcur/memzero.c @@ -0,0 +1,84 @@ +#include "memzero.h" + +#ifndef __STDC_WANT_LIB_EXT1__ +#define __STDC_WANT_LIB_EXT1__ 1 // C11's bounds-checking interface. +#endif +#include + +#ifdef _WIN32 +#include +#endif + +#ifdef __unix__ +#include +#include +#endif + +// C11's bounds-checking interface. +#if defined(__STDC_LIB_EXT1__) +#define HAVE_MEMSET_S 1 +#endif + +// GNU C Library version 2.25 or later. +#if defined(__GLIBC__) && \ + (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 25)) +#define HAVE_EXPLICIT_BZERO 1 +#endif + +// Newlib +#if defined(__NEWLIB__) +#define HAVE_EXPLICIT_BZERO 1 +#endif + +// FreeBSD version 11.0 or later. +#if defined(__FreeBSD__) && __FreeBSD_version >= 1100037 +#define HAVE_EXPLICIT_BZERO 1 +#endif + +// OpenBSD version 5.5 or later. +#if defined(__OpenBSD__) && OpenBSD >= 201405 +#define HAVE_EXPLICIT_BZERO 1 +#endif + +// NetBSD version 7.2 or later. +#if defined(__NetBSD__) && __NetBSD_Version__ >= 702000000 +#define HAVE_EXPLICIT_MEMSET 1 +#endif + +// Adapted from +// https://github.com/jedisct1/libsodium/blob/1647f0d53ae0e370378a9195477e3df0a792408f/src/libsodium/sodium/utils.c#L102-L130 + +void memzero(void *const pnt, const size_t len) { +#ifdef _WIN32 + SecureZeroMemory(pnt, len); +#elif defined(HAVE_MEMSET_S) + memset_s(pnt, (rsize_t)len, 0, (rsize_t)len); +#elif defined(HAVE_EXPLICIT_BZERO) + bzero(pnt, len); +#elif defined(HAVE_EXPLICIT_MEMSET) + explicit_memset(pnt, 0, len); +#else + volatile unsigned char *volatile pnt_ = (volatile unsigned char *volatile)pnt; + size_t i = (size_t)0U; + + while (i < len) { + pnt_[i++] = 0U; + } + + /* Memory barrier that scares the compiler away from optimizing out + * the above loop. + * + * Quoting Adam Langley in commit + * ad1907fe73334d6c696c8539646c21b11178f20f of BoringSSL (ISC License): + * + * As best as we can tell, this is sufficient to break any optimisations + * that might try to eliminate "superfluous" memsets. This method is used + * in memzero_explicit() the Linux kernel, too. Its advantage is that it + * is pretty efficient because the compiler can still implement the + * memset() efficiently, just not remove it entirely. See "Dead Store + * Elimination (Still) Considered Harmful" by Yang et al. (USENIX Security + * 2017) for more background. + */ + __asm__ __volatile__("" : : "r"(pnt_) : "memory"); +#endif +} diff --git a/src/third-party/bcur/memzero.h b/src/third-party/bcur/memzero.h new file mode 100644 index 0000000..0a950de --- /dev/null +++ b/src/third-party/bcur/memzero.h @@ -0,0 +1,8 @@ +#ifndef BC_UR_MEMZERO_H +#define BC_UR_MEMZERO_H + +#include + +void memzero(void* const pnt, const size_t len); + +#endif // BC_UR_MEMZERO_H diff --git a/src/third-party/bcur/random-sampler.cpp b/src/third-party/bcur/random-sampler.cpp new file mode 100644 index 0000000..7822d6e --- /dev/null +++ b/src/third-party/bcur/random-sampler.cpp @@ -0,0 +1,84 @@ +// +// random-sampler.cpp +// +// Copyright © 2020 by Blockchain Commons, LLC +// Licensed under the "BSD-2-Clause Plus Patent License" +// + +#include "random-sampler.hpp" +#include +#include +#include + +using namespace std; + +namespace ur { + +RandomSampler::RandomSampler(std::vector probs) { + for(auto p: probs) { assert(p >= 0); } + + // Normalize given probabilities + auto sum = accumulate(probs.begin(), probs.end(), 0.0); + assert(sum > 0); + + auto n = probs.size(); + + vector P; + P.reserve(n); + transform(probs.begin(), probs.end(), back_inserter(P), [&](double d) { return d * double(n) / sum; }); + + vector S; + S.reserve(n); + vector L; + L.reserve(n); + + // Set separate index lists for small and large probabilities: + for(int i = n - 1; i >= 0; i--) { + // at variance from Schwarz, we reverse the index order + if(P[i] < 1) { + S.push_back(i); + } else { + L.push_back(i); + } + } + + // Work through index lists + vector _probs(n, 0); + vector _aliases(n, 0); + while(!S.empty() && !L.empty()) { + auto a = S.back(); S.pop_back(); // Schwarz's l + auto g = L.back(); L.pop_back(); // Schwarz's g + _probs[a] = P[a]; + _aliases[a] = g; + P[g] += P[a] - 1; + if(P[g] < 1) { + S.push_back(g); + } else { + L.push_back(g); + } + } + + while(!L.empty()) { + _probs[L.back()] = 1; + L.pop_back(); + } + + while(!S.empty()) { + // can only happen through numeric instability + _probs[S.back()] = 1; + S.pop_back(); + } + + this->probs_ = _probs; + this->aliases_ = _aliases; +} + +int RandomSampler::next(std::function rng) { + auto r1 = rng(); + auto r2 = rng(); + auto n = probs_.size(); + auto i = int(double(n) * r1); + return r2 < probs_[i] ? i : aliases_[i]; +} + +} diff --git a/src/third-party/bcur/random-sampler.hpp b/src/third-party/bcur/random-sampler.hpp new file mode 100644 index 0000000..24b79b4 --- /dev/null +++ b/src/third-party/bcur/random-sampler.hpp @@ -0,0 +1,38 @@ +// +// random-sampler.hpp +// +// Copyright © 2020 by Blockchain Commons, LLC +// Licensed under the "BSD-2-Clause Plus Patent License" +// + +#ifndef BC_UR_RANDOM_SAMPLER_HPP +#define BC_UR_RANDOM_SAMPLER_HPP + +#include +#include + +// Random-number sampling using the Walker-Vose alias method, +// as described by Keith Schwarz (2011) +// http://www.keithschwarz.com/darts-dice-coins + +// Based on C implementation: +// https://jugit.fz-juelich.de/mlz/ransampl + +// Translated to C++ by Wolf McNally + +namespace ur { + +class RandomSampler final { +public: + explicit RandomSampler(std::vector probs); + + int next(std::function rng); + +private: + std::vector probs_; + std::vector aliases_; +}; + +} + +#endif // BC_UR_RANDOM_SAMPLER_HPP diff --git a/src/third-party/bcur/sha2.c b/src/third-party/bcur/sha2.c new file mode 100644 index 0000000..4d2227b --- /dev/null +++ b/src/third-party/bcur/sha2.c @@ -0,0 +1,889 @@ +/** + * Copyright (c) 2000-2001 Aaron D. Gifford + * Copyright (c) 2013-2014 Pavol Rusnak + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include "sha2.h" +#include "memzero.h" + +/* + * ASSERT NOTE: + * Some sanity checking code is included using assert(). On my FreeBSD + * system, this additional code can be removed by compiling with NDEBUG + * defined. Check your own systems manpage on assert() to see how to + * compile WITHOUT the sanity checking code on your system. + * + * UNROLLED TRANSFORM LOOP NOTE: + * You can define SHA2_UNROLL_TRANSFORM to use the unrolled transform + * loop version for the hash transform rounds (defined using macros + * later in this file). Either define on the command line, for example: + * + * cc -DSHA2_UNROLL_TRANSFORM -o sha2 sha2.c sha2prog.c + * + * or define below: + * + * #define SHA2_UNROLL_TRANSFORM + * + */ + + +/*** SHA-256/384/512 Machine Architecture Definitions *****************/ +/* + * BYTE_ORDER NOTE: + * + * Please make sure that your system defines BYTE_ORDER. If your + * architecture is little-endian, make sure it also defines + * LITTLE_ENDIAN and that the two (BYTE_ORDER and LITTLE_ENDIAN) are + * equivilent. + * + * If your system does not define the above, then you can do so by + * hand like this: + * + * #define LITTLE_ENDIAN 1234 + * #define BIG_ENDIAN 4321 + * + * And for little-endian machines, add: + * + * #define BYTE_ORDER LITTLE_ENDIAN + * + * Or for big-endian machines: + * + * #define BYTE_ORDER BIG_ENDIAN + * + * The FreeBSD machine this was written on defines BYTE_ORDER + * appropriately by including (which in turn includes + * where the appropriate definitions are actually + * made). + */ + +#if !defined(BYTE_ORDER) || (BYTE_ORDER != LITTLE_ENDIAN && BYTE_ORDER != BIG_ENDIAN) +#error Define BYTE_ORDER to be equal to either LITTLE_ENDIAN or BIG_ENDIAN +#endif + +typedef uint8_t sha2_byte; /* Exactly 1 byte */ +typedef uint32_t sha2_word32; /* Exactly 4 bytes */ +typedef uint64_t sha2_word64; /* Exactly 8 bytes */ + +/*** SHA-256/384/512 Various Length Definitions ***********************/ +/* NOTE: Most of these are in sha2.h */ +#define SHA256_SHORT_BLOCK_LENGTH (SHA256_BLOCK_LENGTH - 8) +#define SHA512_SHORT_BLOCK_LENGTH (SHA512_BLOCK_LENGTH - 16) + +/* + * Macro for incrementally adding the unsigned 64-bit integer n to the + * unsigned 128-bit integer (represented using a two-element array of + * 64-bit words): + */ +#define ADDINC128(w,n) { \ + (w)[0] += (sha2_word64)(n); \ + if ((w)[0] < (n)) { \ + (w)[1]++; \ + } \ +} + +#define MEMCPY_BCOPY(d,s,l) memcpy((d), (s), (l)) + +/*** THE SIX LOGICAL FUNCTIONS ****************************************/ +/* + * Bit shifting and rotation (used by the six SHA-XYZ logical functions: + * + * NOTE: In the original SHA-256/384/512 document, the shift-right + * function was named R and the rotate-right function was called S. + * (See: http://csrc.nist.gov/cryptval/shs/sha256-384-512.pdf on the + * web.) + * + * The newer NIST FIPS 180-2 document uses a much clearer naming + * scheme, SHR for shift-right, ROTR for rotate-right, and ROTL for + * rotate-left. (See: + * http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf + * on the web.) + * + * WARNING: These macros must be used cautiously, since they reference + * supplied parameters sometimes more than once, and thus could have + * unexpected side-effects if used without taking this into account. + */ + +/* Shift-right (used in SHA-256, SHA-384, and SHA-512): */ +#define SHR(b,x) ((x) >> (b)) +/* 32-bit Rotate-right (used in SHA-256): */ +#define ROTR32(b,x) (((x) >> (b)) | ((x) << (32 - (b)))) +/* 64-bit Rotate-right (used in SHA-384 and SHA-512): */ +#define ROTR64(b,x) (((x) >> (b)) | ((x) << (64 - (b)))) +/* 32-bit Rotate-left (used in SHA-1): */ +#define ROTL32(b,x) (((x) << (b)) | ((x) >> (32 - (b)))) + +/* Two of six logical functions used in SHA-1, SHA-256, SHA-384, and SHA-512: */ +#define Ch(x,y,z) (((x) & (y)) ^ ((~(x)) & (z))) +#define Maj(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) + +/* Function used in SHA-1: */ +#define Parity(x,y,z) ((x) ^ (y) ^ (z)) + +/* Four of six logical functions used in SHA-256: */ +#define Sigma0_256(x) (ROTR32(2, (x)) ^ ROTR32(13, (x)) ^ ROTR32(22, (x))) +#define Sigma1_256(x) (ROTR32(6, (x)) ^ ROTR32(11, (x)) ^ ROTR32(25, (x))) +#define sigma0_256(x) (ROTR32(7, (x)) ^ ROTR32(18, (x)) ^ SHR(3 , (x))) +#define sigma1_256(x) (ROTR32(17, (x)) ^ ROTR32(19, (x)) ^ SHR(10, (x))) + +/* Four of six logical functions used in SHA-384 and SHA-512: */ +#define Sigma0_512(x) (ROTR64(28, (x)) ^ ROTR64(34, (x)) ^ ROTR64(39, (x))) +#define Sigma1_512(x) (ROTR64(14, (x)) ^ ROTR64(18, (x)) ^ ROTR64(41, (x))) +#define sigma0_512(x) (ROTR64( 1, (x)) ^ ROTR64( 8, (x)) ^ SHR( 7, (x))) +#define sigma1_512(x) (ROTR64(19, (x)) ^ ROTR64(61, (x)) ^ SHR( 6, (x))) + +/*** INTERNAL FUNCTION PROTOTYPES *************************************/ +/* NOTE: These should not be accessed directly from outside this + * library -- they are intended for private internal visibility/use + * only. + */ +static void sha512_Last(SHA512_CTX*); + + +/*** SHA-XYZ INITIAL HASH VALUES AND CONSTANTS ************************/ + +/* Hash constant words K for SHA-256: */ +static const sha2_word32 K256[64] = { + 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, + 0x3956c25bUL, 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, + 0xd807aa98UL, 0x12835b01UL, 0x243185beUL, 0x550c7dc3UL, + 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, 0xc19bf174UL, + 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL, + 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, + 0x983e5152UL, 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, + 0xc6e00bf3UL, 0xd5a79147UL, 0x06ca6351UL, 0x14292967UL, + 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, 0x53380d13UL, + 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL, + 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, + 0xd192e819UL, 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, + 0x19a4c116UL, 0x1e376c08UL, 0x2748774cUL, 0x34b0bcb5UL, + 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, 0x682e6ff3UL, + 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL, + 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL +}; + +/* Initial hash value H for SHA-256: */ +const sha2_word32 sha256_initial_hash_value[8] = { + 0x6a09e667UL, + 0xbb67ae85UL, + 0x3c6ef372UL, + 0xa54ff53aUL, + 0x510e527fUL, + 0x9b05688cUL, + 0x1f83d9abUL, + 0x5be0cd19UL +}; + +/* Hash constant words K for SHA-384 and SHA-512: */ +static const sha2_word64 K512[80] = { + 0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, + 0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL, + 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL, + 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL, + 0xd807aa98a3030242ULL, 0x12835b0145706fbeULL, + 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL, + 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, + 0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL, + 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, + 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, + 0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL, + 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL, + 0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, + 0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL, + 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL, + 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL, + 0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL, + 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL, + 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, + 0x81c2c92e47edaee6ULL, 0x92722c851482353bULL, + 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, + 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, + 0xd192e819d6ef5218ULL, 0xd69906245565a910ULL, + 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL, + 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, + 0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL, + 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, + 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL, + 0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL, + 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL, + 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, + 0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL, + 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL, + 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, + 0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL, + 0x113f9804bef90daeULL, 0x1b710b35131c471bULL, + 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, + 0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL, + 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, + 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL +}; + +/* Initial hash value H for SHA-512 */ +const sha2_word64 sha512_initial_hash_value[8] = { + 0x6a09e667f3bcc908ULL, + 0xbb67ae8584caa73bULL, + 0x3c6ef372fe94f82bULL, + 0xa54ff53a5f1d36f1ULL, + 0x510e527fade682d1ULL, + 0x9b05688c2b3e6c1fULL, + 0x1f83d9abfb41bd6bULL, + 0x5be0cd19137e2179ULL +}; + +/* + * Constant used by SHA256/384/512_End() functions for converting the + * digest to a readable hexadecimal character string: + */ +static const char *sha2_hex_digits = "0123456789abcdef"; + + +/*** SHA-256: *********************************************************/ +void sha256_Init(SHA256_CTX* context) { + if (context == (SHA256_CTX*)0) { + return; + } + MEMCPY_BCOPY(context->state, sha256_initial_hash_value, SHA256_DIGEST_LENGTH); + memzero(context->buffer, SHA256_BLOCK_LENGTH); + context->bitcount = 0; +} + +#ifdef SHA2_UNROLL_TRANSFORM + +/* Unrolled SHA-256 round macros: */ + +#define ROUND256_0_TO_15(a,b,c,d,e,f,g,h) \ + T1 = (h) + Sigma1_256(e) + Ch((e), (f), (g)) + \ + K256[j] + (W256[j] = *data++); \ + (d) += T1; \ + (h) = T1 + Sigma0_256(a) + Maj((a), (b), (c)); \ + j++ + +#define ROUND256(a,b,c,d,e,f,g,h) \ + s0 = W256[(j+1)&0x0f]; \ + s0 = sigma0_256(s0); \ + s1 = W256[(j+14)&0x0f]; \ + s1 = sigma1_256(s1); \ + T1 = (h) + Sigma1_256(e) + Ch((e), (f), (g)) + K256[j] + \ + (W256[j&0x0f] += s1 + W256[(j+9)&0x0f] + s0); \ + (d) += T1; \ + (h) = T1 + Sigma0_256(a) + Maj((a), (b), (c)); \ + j++ + +void sha256_Transform(const sha2_word32* state_in, const sha2_word32* data, sha2_word32* state_out) { + sha2_word32 a = 0, b = 0, c = 0, d = 0, e = 0, f = 0, g = 0, h = 0; + sha2_word32 T1 = 0; + sha2_word32 W256[16] = {0}; + int j = 0; + + /* Initialize registers with the prev. intermediate value */ + a = state_in[0]; + b = state_in[1]; + c = state_in[2]; + d = state_in[3]; + e = state_in[4]; + f = state_in[5]; + g = state_in[6]; + h = state_in[7]; + + j = 0; + do { + /* Rounds 0 to 15 (unrolled): */ + ROUND256_0_TO_15(a,b,c,d,e,f,g,h); + ROUND256_0_TO_15(h,a,b,c,d,e,f,g); + ROUND256_0_TO_15(g,h,a,b,c,d,e,f); + ROUND256_0_TO_15(f,g,h,a,b,c,d,e); + ROUND256_0_TO_15(e,f,g,h,a,b,c,d); + ROUND256_0_TO_15(d,e,f,g,h,a,b,c); + ROUND256_0_TO_15(c,d,e,f,g,h,a,b); + ROUND256_0_TO_15(b,c,d,e,f,g,h,a); + } while (j < 16); + + /* Now for the remaining rounds to 64: */ + do { + ROUND256(a,b,c,d,e,f,g,h); + ROUND256(h,a,b,c,d,e,f,g); + ROUND256(g,h,a,b,c,d,e,f); + ROUND256(f,g,h,a,b,c,d,e); + ROUND256(e,f,g,h,a,b,c,d); + ROUND256(d,e,f,g,h,a,b,c); + ROUND256(c,d,e,f,g,h,a,b); + ROUND256(b,c,d,e,f,g,h,a); + } while (j < 64); + + /* Compute the current intermediate hash value */ + state_out[0] = state_in[0] + a; + state_out[1] = state_in[1] + b; + state_out[2] = state_in[2] + c; + state_out[3] = state_in[3] + d; + state_out[4] = state_in[4] + e; + state_out[5] = state_in[5] + f; + state_out[6] = state_in[6] + g; + state_out[7] = state_in[7] + h; + + /* Clean up */ + // cppcheck-suppress unreadVariable + a = b = c = d = e = f = g = h = T1 = 0; +} + +#else /* SHA2_UNROLL_TRANSFORM */ + +void sha256_Transform(const sha2_word32* state_in, const sha2_word32* data, sha2_word32* state_out) { + sha2_word32 a = 0, b = 0, c = 0, d = 0, e = 0, f = 0, g = 0, h = 0; + sha2_word32 T1 = 0, T2 = 0 , W256[16] = {0}; + int j = 0; + + /* Initialize registers with the prev. intermediate value */ + a = state_in[0]; + b = state_in[1]; + c = state_in[2]; + d = state_in[3]; + e = state_in[4]; + f = state_in[5]; + g = state_in[6]; + h = state_in[7]; + + j = 0; + do { + /* Apply the SHA-256 compression function to update a..h with copy */ + T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + (W256[j] = *data++); + T2 = Sigma0_256(a) + Maj(a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + + j++; + } while (j < 16); + + do { + /* Part of the message block expansion: */ + sha2_word32 s0 = 0, s1 = 0; + s0 = W256[(j+1)&0x0f]; + s0 = sigma0_256(s0); + s1 = W256[(j+14)&0x0f]; + s1 = sigma1_256(s1); + + /* Apply the SHA-256 compression function to update a..h */ + T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + + (W256[j&0x0f] += s1 + W256[(j+9)&0x0f] + s0); + T2 = Sigma0_256(a) + Maj(a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + + j++; + } while (j < 64); + + /* Compute the current intermediate hash value */ + state_out[0] = state_in[0] + a; + state_out[1] = state_in[1] + b; + state_out[2] = state_in[2] + c; + state_out[3] = state_in[3] + d; + state_out[4] = state_in[4] + e; + state_out[5] = state_in[5] + f; + state_out[6] = state_in[6] + g; + state_out[7] = state_in[7] + h; + + /* Clean up */ + // cppcheck-suppress unreadVariable + a = b = c = d = e = f = g = h = T1 = T2 = 0; +} + +#endif /* SHA2_UNROLL_TRANSFORM */ + +void sha256_Update(SHA256_CTX* context, const sha2_byte *data, size_t len) { + unsigned int freespace = 0, usedspace = 0; + + if (len == 0) { + /* Calling with no data is valid - we do nothing */ + return; + } + + usedspace = (context->bitcount >> 3) % SHA256_BLOCK_LENGTH; + if (usedspace > 0) { + /* Calculate how much free space is available in the buffer */ + freespace = SHA256_BLOCK_LENGTH - usedspace; + + if (len >= freespace) { + /* Fill the buffer completely and process it */ + MEMCPY_BCOPY(((uint8_t*)context->buffer) + usedspace, data, freespace); + context->bitcount += freespace << 3; + len -= freespace; + data += freespace; +#if BYTE_ORDER == LITTLE_ENDIAN + /* Convert TO host byte order */ + for (int j = 0; j < 16; j++) { + REVERSE32(context->buffer[j],context->buffer[j]); + } +#endif + sha256_Transform(context->state, context->buffer, context->state); + } else { + /* The buffer is not yet full */ + MEMCPY_BCOPY(((uint8_t*)context->buffer) + usedspace, data, len); + context->bitcount += len << 3; + /* Clean up: */ + // cppcheck-suppress unreadVariable + usedspace = freespace = 0; + return; + } + } + while (len >= SHA256_BLOCK_LENGTH) { + /* Process as many complete blocks as we can */ + MEMCPY_BCOPY(context->buffer, data, SHA256_BLOCK_LENGTH); +#if BYTE_ORDER == LITTLE_ENDIAN + /* Convert TO host byte order */ + for (int j = 0; j < 16; j++) { + REVERSE32(context->buffer[j],context->buffer[j]); + } +#endif + sha256_Transform(context->state, context->buffer, context->state); + context->bitcount += SHA256_BLOCK_LENGTH << 3; + len -= SHA256_BLOCK_LENGTH; + data += SHA256_BLOCK_LENGTH; + } + if (len > 0) { + /* There's left-overs, so save 'em */ + MEMCPY_BCOPY(context->buffer, data, len); + context->bitcount += len << 3; + } + /* Clean up: */ + // cppcheck-suppress unreadVariable + usedspace = freespace = 0; +} + +void sha256_Final(SHA256_CTX* context, sha2_byte digest[]) { + unsigned int usedspace = 0; + + /* If no digest buffer is passed, we don't bother doing this: */ + if (digest != (sha2_byte*)0) { + usedspace = (context->bitcount >> 3) % SHA256_BLOCK_LENGTH; + /* Begin padding with a 1 bit: */ + ((uint8_t*)context->buffer)[usedspace++] = 0x80; + + if (usedspace > SHA256_SHORT_BLOCK_LENGTH) { + memzero(((uint8_t*)context->buffer) + usedspace, SHA256_BLOCK_LENGTH - usedspace); + +#if BYTE_ORDER == LITTLE_ENDIAN + /* Convert TO host byte order */ + for (int j = 0; j < 16; j++) { + REVERSE32(context->buffer[j],context->buffer[j]); + } +#endif + /* Do second-to-last transform: */ + sha256_Transform(context->state, context->buffer, context->state); + + /* And prepare the last transform: */ + usedspace = 0; + } + /* Set-up for the last transform: */ + memzero(((uint8_t*)context->buffer) + usedspace, SHA256_SHORT_BLOCK_LENGTH - usedspace); + +#if BYTE_ORDER == LITTLE_ENDIAN + /* Convert TO host byte order */ + for (int j = 0; j < 14; j++) { + REVERSE32(context->buffer[j],context->buffer[j]); + } +#endif + /* Set the bit count: */ + context->buffer[14] = context->bitcount >> 32; + context->buffer[15] = context->bitcount & 0xffffffff; + + /* Final transform: */ + sha256_Transform(context->state, context->buffer, context->state); + +#if BYTE_ORDER == LITTLE_ENDIAN + /* Convert FROM host byte order */ + for (int j = 0; j < 8; j++) { + REVERSE32(context->state[j],context->state[j]); + } +#endif + MEMCPY_BCOPY(digest, context->state, SHA256_DIGEST_LENGTH); + } + + /* Clean up state data: */ + memzero(context, sizeof(SHA256_CTX)); + // cppcheck-suppress unreadVariable + usedspace = 0; +} + +char *sha256_End(SHA256_CTX* context, char buffer[]) { + sha2_byte digest[SHA256_DIGEST_LENGTH] = {0}, *d = digest; + + if (buffer != (char*)0) { + sha256_Final(context, digest); + + for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) { + *buffer++ = sha2_hex_digits[(*d & 0xf0) >> 4]; + *buffer++ = sha2_hex_digits[*d & 0x0f]; + d++; + } + *buffer = (char)0; + } else { + memzero(context, sizeof(SHA256_CTX)); + } + memzero(digest, SHA256_DIGEST_LENGTH); + return buffer; +} + +void sha256_Raw(const sha2_byte* data, size_t len, uint8_t digest[SHA256_DIGEST_LENGTH]) { + SHA256_CTX context = {0}; + sha256_Init(&context); + sha256_Update(&context, data, len); + sha256_Final(&context, digest); +} + +char* sha256_Data(const sha2_byte* data, size_t len, char digest[SHA256_DIGEST_STRING_LENGTH]) { + SHA256_CTX context = {0}; + + sha256_Init(&context); + sha256_Update(&context, data, len); + return sha256_End(&context, digest); +} + + +/*** SHA-512: *********************************************************/ +void sha512_Init(SHA512_CTX* context) { + if (context == (SHA512_CTX*)0) { + return; + } + MEMCPY_BCOPY(context->state, sha512_initial_hash_value, SHA512_DIGEST_LENGTH); + memzero(context->buffer, SHA512_BLOCK_LENGTH); + context->bitcount[0] = context->bitcount[1] = 0; +} + +#ifdef SHA2_UNROLL_TRANSFORM + +/* Unrolled SHA-512 round macros: */ +#define ROUND512_0_TO_15(a,b,c,d,e,f,g,h) \ + T1 = (h) + Sigma1_512(e) + Ch((e), (f), (g)) + \ + K512[j] + (W512[j] = *data++); \ + (d) += T1; \ + (h) = T1 + Sigma0_512(a) + Maj((a), (b), (c)); \ + j++ + +#define ROUND512(a,b,c,d,e,f,g,h) \ + s0 = W512[(j+1)&0x0f]; \ + s0 = sigma0_512(s0); \ + s1 = W512[(j+14)&0x0f]; \ + s1 = sigma1_512(s1); \ + T1 = (h) + Sigma1_512(e) + Ch((e), (f), (g)) + K512[j] + \ + (W512[j&0x0f] += s1 + W512[(j+9)&0x0f] + s0); \ + (d) += T1; \ + (h) = T1 + Sigma0_512(a) + Maj((a), (b), (c)); \ + j++ + +void sha512_Transform(const sha2_word64* state_in, const sha2_word64* data, sha2_word64* state_out) { + sha2_word64 a = 0, b = 0, c = 0, d = 0, e = 0, f = 0, g = 0, h = 0; + sha2_word64 T1 = 0, W512[16] = {0}; + int j = 0; + + /* Initialize registers with the prev. intermediate value */ + a = state_in[0]; + b = state_in[1]; + c = state_in[2]; + d = state_in[3]; + e = state_in[4]; + f = state_in[5]; + g = state_in[6]; + h = state_in[7]; + + j = 0; + do { + ROUND512_0_TO_15(a,b,c,d,e,f,g,h); + ROUND512_0_TO_15(h,a,b,c,d,e,f,g); + ROUND512_0_TO_15(g,h,a,b,c,d,e,f); + ROUND512_0_TO_15(f,g,h,a,b,c,d,e); + ROUND512_0_TO_15(e,f,g,h,a,b,c,d); + ROUND512_0_TO_15(d,e,f,g,h,a,b,c); + ROUND512_0_TO_15(c,d,e,f,g,h,a,b); + ROUND512_0_TO_15(b,c,d,e,f,g,h,a); + } while (j < 16); + + /* Now for the remaining rounds up to 79: */ + do { + ROUND512(a,b,c,d,e,f,g,h); + ROUND512(h,a,b,c,d,e,f,g); + ROUND512(g,h,a,b,c,d,e,f); + ROUND512(f,g,h,a,b,c,d,e); + ROUND512(e,f,g,h,a,b,c,d); + ROUND512(d,e,f,g,h,a,b,c); + ROUND512(c,d,e,f,g,h,a,b); + ROUND512(b,c,d,e,f,g,h,a); + } while (j < 80); + + /* Compute the current intermediate hash value */ + state_out[0] = state_in[0] + a; + state_out[1] = state_in[1] + b; + state_out[2] = state_in[2] + c; + state_out[3] = state_in[3] + d; + state_out[4] = state_in[4] + e; + state_out[5] = state_in[5] + f; + state_out[6] = state_in[6] + g; + state_out[7] = state_in[7] + h; + + /* Clean up */ + // cppcheck-suppress unreadVariable + a = b = c = d = e = f = g = h = T1 = 0; +} + +#else /* SHA2_UNROLL_TRANSFORM */ + +void sha512_Transform(const sha2_word64* state_in, const sha2_word64* data, sha2_word64* state_out) { + sha2_word64 a = 0, b = 0, c = 0, d = 0, e = 0, f = 0, g = 0, h = 0; + sha2_word64 T1 = 0, T2 = 0, W512[16] = {0}; + int j = 0; + + /* Initialize registers with the prev. intermediate value */ + a = state_in[0]; + b = state_in[1]; + c = state_in[2]; + d = state_in[3]; + e = state_in[4]; + f = state_in[5]; + g = state_in[6]; + h = state_in[7]; + + j = 0; + do { + /* Apply the SHA-512 compression function to update a..h with copy */ + T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] + (W512[j] = *data++); + T2 = Sigma0_512(a) + Maj(a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + + j++; + } while (j < 16); + + do { + /* Part of the message block expansion: */ + sha2_word64 s0 = 0, s1 = 0; + s0 = W512[(j+1)&0x0f]; + s0 = sigma0_512(s0); + s1 = W512[(j+14)&0x0f]; + s1 = sigma1_512(s1); + + /* Apply the SHA-512 compression function to update a..h */ + T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] + + (W512[j&0x0f] += s1 + W512[(j+9)&0x0f] + s0); + T2 = Sigma0_512(a) + Maj(a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + + j++; + } while (j < 80); + + /* Compute the current intermediate hash value */ + state_out[0] = state_in[0] + a; + state_out[1] = state_in[1] + b; + state_out[2] = state_in[2] + c; + state_out[3] = state_in[3] + d; + state_out[4] = state_in[4] + e; + state_out[5] = state_in[5] + f; + state_out[6] = state_in[6] + g; + state_out[7] = state_in[7] + h; + + /* Clean up */ + // cppcheck-suppress unreadVariable + a = b = c = d = e = f = g = h = T1 = T2 = 0; +} + +#endif /* SHA2_UNROLL_TRANSFORM */ + +void sha512_Update(SHA512_CTX* context, const sha2_byte *data, size_t len) { + unsigned int freespace = 0, usedspace = 0; + + if (len == 0) { + /* Calling with no data is valid - we do nothing */ + return; + } + + usedspace = (context->bitcount[0] >> 3) % SHA512_BLOCK_LENGTH; + if (usedspace > 0) { + /* Calculate how much free space is available in the buffer */ + freespace = SHA512_BLOCK_LENGTH - usedspace; + + if (len >= freespace) { + /* Fill the buffer completely and process it */ + MEMCPY_BCOPY(((uint8_t*)context->buffer) + usedspace, data, freespace); + ADDINC128(context->bitcount, freespace << 3); + len -= freespace; + data += freespace; +#if BYTE_ORDER == LITTLE_ENDIAN + /* Convert TO host byte order */ + for (int j = 0; j < 16; j++) { + REVERSE64(context->buffer[j],context->buffer[j]); + } +#endif + sha512_Transform(context->state, context->buffer, context->state); + } else { + /* The buffer is not yet full */ + MEMCPY_BCOPY(((uint8_t*)context->buffer) + usedspace, data, len); + ADDINC128(context->bitcount, len << 3); + /* Clean up: */ + // cppcheck-suppress unreadVariable + usedspace = freespace = 0; + return; + } + } + while (len >= SHA512_BLOCK_LENGTH) { + /* Process as many complete blocks as we can */ + MEMCPY_BCOPY(context->buffer, data, SHA512_BLOCK_LENGTH); +#if BYTE_ORDER == LITTLE_ENDIAN + /* Convert TO host byte order */ + for (int j = 0; j < 16; j++) { + REVERSE64(context->buffer[j],context->buffer[j]); + } +#endif + sha512_Transform(context->state, context->buffer, context->state); + ADDINC128(context->bitcount, SHA512_BLOCK_LENGTH << 3); + len -= SHA512_BLOCK_LENGTH; + data += SHA512_BLOCK_LENGTH; + } + if (len > 0) { + /* There's left-overs, so save 'em */ + MEMCPY_BCOPY(context->buffer, data, len); + ADDINC128(context->bitcount, len << 3); + } + /* Clean up: */ + // cppcheck-suppress unreadVariable + usedspace = freespace = 0; +} + +static void sha512_Last(SHA512_CTX* context) { + unsigned int usedspace = 0; + + usedspace = (context->bitcount[0] >> 3) % SHA512_BLOCK_LENGTH; + /* Begin padding with a 1 bit: */ + ((uint8_t*)context->buffer)[usedspace++] = 0x80; + + if (usedspace > SHA512_SHORT_BLOCK_LENGTH) { + memzero(((uint8_t*)context->buffer) + usedspace, SHA512_BLOCK_LENGTH - usedspace); + +#if BYTE_ORDER == LITTLE_ENDIAN + /* Convert TO host byte order */ + for (int j = 0; j < 16; j++) { + REVERSE64(context->buffer[j],context->buffer[j]); + } +#endif + /* Do second-to-last transform: */ + sha512_Transform(context->state, context->buffer, context->state); + + /* And prepare the last transform: */ + usedspace = 0; + } + /* Set-up for the last transform: */ + memzero(((uint8_t*)context->buffer) + usedspace, SHA512_SHORT_BLOCK_LENGTH - usedspace); + +#if BYTE_ORDER == LITTLE_ENDIAN + /* Convert TO host byte order */ + for (int j = 0; j < 14; j++) { + REVERSE64(context->buffer[j],context->buffer[j]); + } +#endif + /* Store the length of input data (in bits): */ + context->buffer[14] = context->bitcount[1]; + context->buffer[15] = context->bitcount[0]; + + /* Final transform: */ + sha512_Transform(context->state, context->buffer, context->state); +} + +void sha512_Final(SHA512_CTX* context, sha2_byte digest[]) { + /* If no digest buffer is passed, we don't bother doing this: */ + if (digest != (sha2_byte*)0) { + sha512_Last(context); + + /* Save the hash data for output: */ +#if BYTE_ORDER == LITTLE_ENDIAN + /* Convert FROM host byte order */ + for (int j = 0; j < 8; j++) { + REVERSE64(context->state[j],context->state[j]); + } +#endif + MEMCPY_BCOPY(digest, context->state, SHA512_DIGEST_LENGTH); + } + + /* Zero out state data */ + memzero(context, sizeof(SHA512_CTX)); +} + +char *sha512_End(SHA512_CTX* context, char buffer[]) { + sha2_byte digest[SHA512_DIGEST_LENGTH] = {0}, *d = digest; + + if (buffer != (char*)0) { + sha512_Final(context, digest); + + for (int i = 0; i < SHA512_DIGEST_LENGTH; i++) { + *buffer++ = sha2_hex_digits[(*d & 0xf0) >> 4]; + *buffer++ = sha2_hex_digits[*d & 0x0f]; + d++; + } + *buffer = (char)0; + } else { + memzero(context, sizeof(SHA512_CTX)); + } + memzero(digest, SHA512_DIGEST_LENGTH); + return buffer; +} + +void sha512_Raw(const sha2_byte* data, size_t len, uint8_t digest[SHA512_DIGEST_LENGTH]) { + SHA512_CTX context = {0}; + sha512_Init(&context); + sha512_Update(&context, data, len); + sha512_Final(&context, digest); +} + +char* sha512_Data(const sha2_byte* data, size_t len, char digest[SHA512_DIGEST_STRING_LENGTH]) { + SHA512_CTX context = {0}; + + sha512_Init(&context); + sha512_Update(&context, data, len); + return sha512_End(&context, digest); +} diff --git a/src/third-party/bcur/sha2.h b/src/third-party/bcur/sha2.h new file mode 100644 index 0000000..9426e56 --- /dev/null +++ b/src/third-party/bcur/sha2.h @@ -0,0 +1,100 @@ +/** + * Copyright (c) 2000-2001 Aaron D. Gifford + * Copyright (c) 2013-2014 Pavol Rusnak + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef BC_UR_SHA2_H +#define BC_UR_SHA2_H + +#include +#include + +#define SHA256_BLOCK_LENGTH 64 +#define SHA256_DIGEST_LENGTH 32 +#define SHA256_DIGEST_STRING_LENGTH (SHA256_DIGEST_LENGTH * 2 + 1) +#define SHA512_BLOCK_LENGTH 128 +#define SHA512_DIGEST_LENGTH 64 +#define SHA512_DIGEST_STRING_LENGTH (SHA512_DIGEST_LENGTH * 2 + 1) + +typedef struct _SHA256_CTX { + uint32_t state[8]; + uint64_t bitcount; + uint32_t buffer[SHA256_BLOCK_LENGTH/sizeof(uint32_t)]; +} SHA256_CTX; +typedef struct _SHA512_CTX { + uint64_t state[8]; + uint64_t bitcount[2]; + uint64_t buffer[SHA512_BLOCK_LENGTH/sizeof(uint64_t)]; +} SHA512_CTX; + +/*** ENDIAN REVERSAL MACROS *******************************************/ +#ifndef LITTLE_ENDIAN +#define LITTLE_ENDIAN 1234 +#define BIG_ENDIAN 4321 +#endif + +#ifndef BYTE_ORDER +#define BYTE_ORDER LITTLE_ENDIAN +#endif + +#if BYTE_ORDER == LITTLE_ENDIAN +#define REVERSE32(w,x) { \ + uint32_t tmp = (w); \ + tmp = (tmp >> 16) | (tmp << 16); \ + (x) = ((tmp & 0xff00ff00UL) >> 8) | ((tmp & 0x00ff00ffUL) << 8); \ +} +#define REVERSE64(w,x) { \ + uint64_t tmp = (w); \ + tmp = (tmp >> 32) | (tmp << 32); \ + tmp = ((tmp & 0xff00ff00ff00ff00ULL) >> 8) | \ + ((tmp & 0x00ff00ff00ff00ffULL) << 8); \ + (x) = ((tmp & 0xffff0000ffff0000ULL) >> 16) | \ + ((tmp & 0x0000ffff0000ffffULL) << 16); \ +} +#endif /* BYTE_ORDER == LITTLE_ENDIAN */ + +extern const uint32_t sha256_initial_hash_value[8]; +extern const uint64_t sha512_initial_hash_value[8]; + +void sha256_Transform(const uint32_t* state_in, const uint32_t* data, uint32_t* state_out); +void sha256_Init(SHA256_CTX *); +void sha256_Update(SHA256_CTX*, const uint8_t*, size_t); +void sha256_Final(SHA256_CTX*, uint8_t[SHA256_DIGEST_LENGTH]); +char* sha256_End(SHA256_CTX*, char[SHA256_DIGEST_STRING_LENGTH]); +void sha256_Raw(const uint8_t*, size_t, uint8_t[SHA256_DIGEST_LENGTH]); +char* sha256_Data(const uint8_t*, size_t, char[SHA256_DIGEST_STRING_LENGTH]); + +void sha512_Transform(const uint64_t* state_in, const uint64_t* data, uint64_t* state_out); +void sha512_Init(SHA512_CTX*); +void sha512_Update(SHA512_CTX*, const uint8_t*, size_t); +void sha512_Final(SHA512_CTX*, uint8_t[SHA512_DIGEST_LENGTH]); +char* sha512_End(SHA512_CTX*, char[SHA512_DIGEST_STRING_LENGTH]); +void sha512_Raw(const uint8_t*, size_t, uint8_t[SHA512_DIGEST_LENGTH]); +char* sha512_Data(const uint8_t*, size_t, char[SHA512_DIGEST_STRING_LENGTH]); + +#endif // BC_UR_SHA2_H diff --git a/src/third-party/bcur/ur-decoder.cpp b/src/third-party/bcur/ur-decoder.cpp new file mode 100644 index 0000000..ec6cea2 --- /dev/null +++ b/src/third-party/bcur/ur-decoder.cpp @@ -0,0 +1,119 @@ +// +// ur-decoder.cpp +// +// Copyright © 2020 by Blockchain Commons, LLC +// Licensed under the "BSD-2-Clause Plus Patent License" +// + +#include "ur-decoder.hpp" +#include "bytewords.hpp" + +using namespace std; + +namespace ur { + +UR URDecoder::decode(const string& s) { + auto [type, components] = parse(s); + + if(components.empty()) throw InvalidPathLength(); + auto body = components.front(); + + return decode(type, body); +} + +URDecoder::URDecoder() { } + +UR URDecoder::decode(const std::string& type, const std::string& body) { + auto cbor = Bytewords::decode(Bytewords::style::minimal, body); + return UR(type, cbor); +} + +pair URDecoder::parse(const string& s) { + // Don't consider case + auto lowered = to_lowercase(s); + + // Validate URI scheme + if(!has_prefix(lowered, "ur:")) throw InvalidScheme(); + auto path = drop_first(lowered, 3); + + // Split the remainder into path components + auto components = split(path, '/'); + + // Make sure there are at least two path components + if(components.size() < 2) throw InvalidPathLength(); + + // Validate the type + auto type = components.front(); + if(!is_ur_type(type)) throw InvalidType(); + + auto comps = StringVector(components.begin() + 1, components.end()); + return pair(type, comps); +} + +pair URDecoder::parse_sequence_component(const string& s) { + try { + auto comps = split(s, '-'); + if(comps.size() != 2) throw InvalidSequenceComponent(); + uint32_t seq_num = stoul(comps[0]); + size_t seq_len = stoul(comps[1]); + if(seq_num < 1 || seq_len < 1) throw InvalidSequenceComponent(); + return pair(seq_num, seq_len); + } catch(...) { + throw InvalidSequenceComponent(); + } +} + +bool URDecoder::validate_part(const std::string& type) { + if(!expected_type_.has_value()) { + if(!is_ur_type(type)) return false; + expected_type_ = type; + return true; + } else { + return type == expected_type_; + } +} + +bool URDecoder::receive_part(const std::string& s) { + try { + // Don't process the part if we're already done + if(result_.has_value()) return false; + + // Don't continue if this part doesn't validate + auto [type, components] = parse(s); + if(!validate_part(type)) return false; + + // If this is a single-part UR then we're done + if(components.size() == 1) { + auto body = components.front(); + result_ = decode(type, body); + return true; + } + + // Multi-part URs must have two path components: seq/fragment + if(components.size() != 2) throw InvalidPathLength(); + auto seq = components[0]; + auto fragment = components[1]; + + // Parse the sequence component and the fragment, and + // make sure they agree. + auto [seq_num, seq_len] = parse_sequence_component(seq); + auto cbor = Bytewords::decode(Bytewords::style::minimal, fragment); + auto part = FountainEncoder::Part(cbor); + if(seq_num != part.seq_num() || seq_len != part.seq_len()) return false; + + // Process the part + if(!fountain_decoder.receive_part(part)) return false; + + if(fountain_decoder.is_success()) { + result_ = UR(type, fountain_decoder.result_message()); + } else if(fountain_decoder.is_failure()) { + result_ = fountain_decoder.result_error(); + } + + return true; + } catch(...) { + return false; + } +} + +} diff --git a/src/third-party/bcur/ur-decoder.hpp b/src/third-party/bcur/ur-decoder.hpp new file mode 100644 index 0000000..132fa77 --- /dev/null +++ b/src/third-party/bcur/ur-decoder.hpp @@ -0,0 +1,66 @@ +// +// ur-decoder.hpp +// +// Copyright © 2020 by Blockchain Commons, LLC +// Licensed under the "BSD-2-Clause Plus Patent License" +// + +#ifndef BC_UR_DECODER_HPP +#define BC_UR_DECODER_HPP + +#include +#include +#include +#include + +#include "ur.hpp" +#include "fountain-decoder.hpp" + +namespace ur { + +class URDecoder final { +public: + typedef std::optional > Result; + + class InvalidScheme: public std::exception { }; + class InvalidType: public std::exception { }; + class InvalidPathLength: public std::exception { }; + class InvalidSequenceComponent: public std::exception { }; + class InvalidFragment: public std::exception { }; + + // Decode a single-part UR. + static UR decode(const std::string& string); + + // Start decoding a (possibly) multi-part UR. + URDecoder(); + + const std::optional& expected_type() const { return expected_type_; } + size_t expected_part_count() const { return fountain_decoder.expected_part_count(); } + const PartIndexes& received_part_indexes() const { return fountain_decoder.received_part_indexes(); } + const PartIndexes& last_part_indexes() const { return fountain_decoder.last_part_indexes(); } + size_t processed_parts_count() const { return fountain_decoder.processed_parts_count(); } + double estimated_percent_complete() const { return fountain_decoder.estimated_percent_complete(); } + const Result& result() const { return result_; } + bool is_success() const { return result() && std::holds_alternative(result().value()); } + bool is_failure() const { return result() && std::holds_alternative(result().value()); } + bool is_complete() const { return result().has_value(); } + const UR& result_ur() const { return std::get(result().value()); } + const std::exception& result_error() const { return std::get(result().value()); } + + bool receive_part(const std::string& s); + +private: + FountainDecoder fountain_decoder; + + std::optional expected_type_; + Result result_; + + static std::pair parse(const std::string& string); + static std::pair parse_sequence_component(const std::string& string); + static UR decode(const std::string& type, const std::string& body); + bool validate_part(const std::string& type); +}; + +} + +#endif // BC_UR_DECODER_HPP \ No newline at end of file diff --git a/src/third-party/bcur/ur-encoder.cpp b/src/third-party/bcur/ur-encoder.cpp new file mode 100644 index 0000000..873137b --- /dev/null +++ b/src/third-party/bcur/ur-encoder.cpp @@ -0,0 +1,50 @@ +// +// ur-encoder.cpp +// +// Copyright © 2020 by Blockchain Commons, LLC +// Licensed under the "BSD-2-Clause Plus Patent License" +// + +#include "ur-encoder.hpp" +#include "bytewords.hpp" + +using namespace std; + +namespace ur { + +string UREncoder::encode(const UR& ur) { + auto body = Bytewords::encode(Bytewords::style::minimal, ur.cbor()); + return encode_ur({ur.type(), body}); +} + +UREncoder::UREncoder(const UR& ur, size_t max_fragment_len, uint32_t first_seq_num, size_t min_fragment_len) + : ur_(ur), + fountain_encoder_(FountainEncoder(ur.cbor(), max_fragment_len, first_seq_num, min_fragment_len)) +{ +} + +std::string UREncoder::next_part() { + auto part = fountain_encoder_.next_part(); + if(is_single_part()) { + return encode(ur_); + } else { + return encode_part(ur_.type(), part); + } +} + +string UREncoder::encode_part(const string& type, const FountainEncoder::Part& part) { + auto seq = to_string(part.seq_num()) + "-" + to_string(part.seq_len()); + auto body = Bytewords::encode(Bytewords::style::minimal, part.cbor()); + return encode_ur({type, seq, body}); +} + +string UREncoder::encode_uri(const string& scheme, const StringVector& path_components) { + auto path = join(path_components, "/"); + return join({scheme, path}, ":"); +} + +string UREncoder::encode_ur(const StringVector& path_components) { + return encode_uri("ur", path_components); +} + +} diff --git a/src/third-party/bcur/ur-encoder.hpp b/src/third-party/bcur/ur-encoder.hpp new file mode 100644 index 0000000..5b2dec4 --- /dev/null +++ b/src/third-party/bcur/ur-encoder.hpp @@ -0,0 +1,52 @@ +// +// ur-encoder.hpp +// +// Copyright © 2020 by Blockchain Commons, LLC +// Licensed under the "BSD-2-Clause Plus Patent License" +// + +#ifndef BC_UR_ENCODER_HPP +#define BC_UR_ENCODER_HPP + +#include +#include "ur.hpp" +#include "utils.hpp" +#include "fountain-encoder.hpp" + +namespace ur { + +class UREncoder final { +public: + // Encode a single-part UR. + static std::string encode(const UR& ur); + + // Start encoding a (possibly) multi-part UR. + UREncoder(const UR& ur, size_t max_fragment_len, uint32_t first_seq_num = 0, size_t min_fragment_len = 10); + + uint32_t seq_num() const { return fountain_encoder_.seq_num(); } + size_t seq_len() const { return fountain_encoder_.seq_len(); } + PartIndexes last_part_indexes() const { return fountain_encoder_.last_part_indexes(); } + + // `true` if the minimal number of parts to transmit the message have been + // generated. Parts generated when this is `true` will be fountain codes + // containing various mixes of the part data. + bool is_complete() const { return fountain_encoder_.is_complete(); } + + // `true` if this UR can be contained in a single part. If `true`, repeated + // calls to `next_part()` will all return the same single-part UR. + bool is_single_part() const { return fountain_encoder_.is_single_part(); } + + std::string next_part(); + +private: + UR ur_; + FountainEncoder fountain_encoder_; + + static std::string encode_part(const std::string& type, const FountainEncoder::Part& part); + static std::string encode_uri(const std::string& scheme, const StringVector& path_components); + static std::string encode_ur(const StringVector& path_components); +}; + +} + +#endif // BC_UR_ENCODER_HPP \ No newline at end of file diff --git a/src/third-party/bcur/ur.cpp b/src/third-party/bcur/ur.cpp new file mode 100644 index 0000000..2227340 --- /dev/null +++ b/src/third-party/bcur/ur.cpp @@ -0,0 +1,28 @@ +// +// ur.cpp +// +// Copyright © 2020 by Blockchain Commons, LLC +// Licensed under the "BSD-2-Clause Plus Patent License" +// + +#include "ur.hpp" + +#include + +using namespace std; + +namespace ur { + +UR::UR(const std::string &type, const ByteVector &cbor) + : type_(type), cbor_(cbor) +{ + if (!is_ur_type(type)) { + throw invalid_type(); + } +} + +bool operator==(const UR& lhs, const UR& rhs) { + return lhs.type() == rhs.type() && lhs.cbor() == rhs.cbor(); +} + +} // namespace ur diff --git a/src/third-party/bcur/ur.hpp b/src/third-party/bcur/ur.hpp new file mode 100644 index 0000000..c26943c --- /dev/null +++ b/src/third-party/bcur/ur.hpp @@ -0,0 +1,34 @@ +// +// ur.hpp +// +// Copyright © 2020 by Blockchain Commons, LLC +// Licensed under the "BSD-2-Clause Plus Patent License" +// + +#ifndef BC_UR_UR_HPP +#define BC_UR_UR_HPP + +#include +#include +#include "utils.hpp" + +namespace ur { + +class UR final { +private: + std::string type_; + ByteVector cbor_; +public: + class invalid_type: public std::exception { }; + + const std::string& type() const { return type_; } + const ByteVector& cbor() const { return cbor_; } + + UR(const std::string& type, const ByteVector& cbor); +}; + +bool operator==(const UR& lhs, const UR& rhs); + +} + +#endif // BC_UR_UR_HPP \ No newline at end of file diff --git a/src/third-party/bcur/utils.cpp b/src/third-party/bcur/utils.cpp new file mode 100644 index 0000000..9b3d434 --- /dev/null +++ b/src/third-party/bcur/utils.cpp @@ -0,0 +1,171 @@ +// +// utils.cpp +// +// Copyright © 2020 by Blockchain Commons, LLC +// Licensed under the "BSD-2-Clause Plus Patent License" +// + +#include "utils.hpp" + +extern "C" { + +#include "sha2.h" +#include "crc32.h" + +} + +#include +#include +#include +#include + +using namespace std; + +namespace ur { + +ByteVector sha256(const ByteVector &buf) { + uint8_t digest[SHA256_DIGEST_LENGTH]; + sha256_Raw(&buf[0], buf.size(), digest); + return ByteVector(digest, digest + SHA256_DIGEST_LENGTH); +} + +ByteVector crc32_bytes(const ByteVector &buf) { + uint32_t checksum = ur_crc32n(&buf[0], buf.size()); + auto cbegin = (uint8_t*)&checksum; + auto cend = cbegin + sizeof(uint32_t); + return ByteVector(cbegin, cend); +} + +uint32_t crc32_int(const ByteVector &buf) { + return ur_crc32(&buf[0], buf.size()); +} + +ByteVector string_to_bytes(const string& s) { + return ByteVector(s.begin(), s.end()); +} + +string data_to_hex(const ByteVector& in) { + auto hex = "0123456789abcdef"; + string result; + for(auto c: in) { + result.append(1, hex[(c >> 4) & 0xF]); + result.append(1, hex[c & 0xF]); + } + return result; +} + +string data_to_hex(uint32_t n) { + return data_to_hex(int_to_bytes(n)); +} + +ByteVector int_to_bytes(uint32_t n) { + ByteVector b; + b.reserve(4); + b.push_back((n >> 24 & 0xff)); + b.push_back((n >> 16) & 0xff); + b.push_back((n >> 8) & 0xff); + b.push_back(n & 0xff); + return b; +} + +uint32_t bytes_to_int(const ByteVector& in) { + assert(in.size() >= 4); + uint32_t result = 0; + result |= in[0] << 24; + result |= in[1] << 16; + result |= in[2] << 8; + result |= in[3]; + return result; +} + +string join(const StringVector &strings, const string &separator) { + ostringstream result; + bool first = true; + for(auto s: strings) { + if(!first) { + result << separator; + } + result << s; + first = false; + } + return result.str(); +} + +StringVector split(const string& s, char separator) { + StringVector result; + string buf; + + for(auto c: s) { + if(c != separator) { + buf += c; + } else if(c == separator && buf.length() > 0) { + result.push_back(buf); + buf = ""; + } + } + + if(buf != "") { + result.push_back(buf); + } + + return result; +} + +StringVector partition(const string& s, size_t size) { + StringVector result; + auto remaining = s; + while(remaining.length() > 0) { + result.push_back(take_first(remaining, size)); + remaining = drop_first(remaining, size); + } + return result; +} + +string take_first(const string &s, size_t count) { + auto first = s.begin(); + auto c = min(s.size(), count); + auto last = first + c; + return string(first, last); +} + +string drop_first(const string& s, size_t count) { + if(count >= s.length()) { return ""; } + return string(s.begin() + count, s.end()); +} + +void xor_into(ByteVector& target, const ByteVector& source) { + auto count = target.size(); + assert(count == source.size()); + for(int i = 0; i < count; i++) { + target[i] ^= source[i]; + } +} + +ByteVector xor_with(const ByteVector& a, const ByteVector& b) { + auto target = a; + xor_into(target, b); + return target; +} + +bool is_ur_type(char c) { + if('a' <= c && c <= 'z') return true; + if('0' <= c && c <= '9') return true; + if(c == '-') return true; + return false; +} + +bool is_ur_type(const string& s) { + return none_of(s.begin(), s.end(), [](auto c) { return !is_ur_type(c); }); +} + +string to_lowercase(const string& s) { + string result; + transform(s.begin(), s.end(), back_inserter(result), [](char c){ return tolower(c); }); + return result; +} + +bool has_prefix(const string& s, const string& prefix) { + return s.rfind(prefix, 0) == 0; +} + +} diff --git a/src/third-party/bcur/utils.hpp b/src/third-party/bcur/utils.hpp new file mode 100644 index 0000000..2e45e5c --- /dev/null +++ b/src/third-party/bcur/utils.hpp @@ -0,0 +1,89 @@ +// +// utils.hpp +// +// Copyright © 2020 by Blockchain Commons, LLC +// Licensed under the "BSD-2-Clause Plus Patent License" +// + +#ifndef UTILS_HPP +#define UTILS_HPP + +#include +#include +#include +#include +#include +#include + +namespace ur { + +typedef std::vector ByteVector; +typedef std::vector StringVector; + +ByteVector sha256(const ByteVector &buf); +ByteVector crc32_bytes(const ByteVector &buf); +uint32_t crc32_int(const ByteVector &buf); + +ByteVector string_to_bytes(const std::string& s); + +std::string data_to_hex(const ByteVector& in); +std::string data_to_hex(uint32_t n); + +ByteVector int_to_bytes(uint32_t n); +uint32_t bytes_to_int(const ByteVector& in); + +std::string join(const StringVector &strings, const std::string &separator); +StringVector split(const std::string& s, char separator); + +StringVector partition(const std::string& string, size_t size); + +std::string take_first(const std::string &s, size_t count); +std::string drop_first(const std::string &s, size_t count); + +template +void append(std::vector& target, const std::vector& source) { + target.insert(target.end(), source.begin(), source.end()); +} + +template +void append(std::vector& target, const std::array& source) { + target.insert(target.end(), source.begin(), source.end()); +} + +template +std::vector join(const std::vector>& parts) { + std::vector result; + for(auto part: parts) { append(result, part); } + return result; +} + +template +std::pair, std::vector> split(const std::vector& buf, size_t count) { + auto first = buf.begin(); + auto c = std::min(buf.size(), count); + auto last = first + c; + auto a = std::vector(first, last); + auto b = std::vector(last, buf.end()); + return std::make_pair(a, b); +} + +template +std::vector take_first(const std::vector &buf, size_t count) { + auto first = buf.begin(); + auto c = std::min(buf.size(), count); + auto last = first + c; + return std::vector(first, last); +} + +void xor_into(ByteVector& target, const ByteVector& source); +ByteVector xor_with(const ByteVector& a, const ByteVector& b); + +bool is_ur_type(char c); +bool is_ur_type(const std::string& s); + +std::string to_lowercase(const std::string& s); +bool has_prefix(const std::string& s, const std::string& prefix); + +} + +#endif // UTILS_HPP diff --git a/src/third-party/bcur/xoshiro256.cpp b/src/third-party/bcur/xoshiro256.cpp new file mode 100644 index 0000000..2f40b8b --- /dev/null +++ b/src/third-party/bcur/xoshiro256.cpp @@ -0,0 +1,177 @@ +// +// xoshiro256.cpp +// +// Copyright © 2020 by Blockchain Commons, LLC +// Licensed under the "BSD-2-Clause Plus Patent License" +// + +#include "xoshiro256.hpp" +#include +#include + +/* Written in 2018 by David Blackman and Sebastiano Vigna (vigna@acm.org) + +To the extent possible under law, the author has dedicated all copyright +and related and neighboring rights to this software to the public domain +worldwide. This software is distributed without any warranty. + +See . */ + +/* This is xoshiro256** 1.0, one of our all-purpose, rock-solid + generators. It has excellent (sub-ns) speed, a state (256 bits) that is + large enough for any parallel application, and it passes all tests we + are aware of. + + For generating just floating-point numbers, xoshiro256+ is even faster. + + The state must be seeded so that it is not everywhere zero. If you have + a 64-bit seed, we suggest to seed a splitmix64 generator and use its + output to fill s. */ + +namespace ur { + +static inline uint64_t rotl(const uint64_t x, int k) { + return (x << k) | (x >> (64 - k)); +} + +Xoshiro256::Xoshiro256(const std::array& a) { + s[0] = a[0]; + s[1] = a[1]; + s[2] = a[2]; + s[3] = a[3]; +} + +void Xoshiro256::set_s(const std::array& a) { + for(int i = 0; i < 4; i++) { + auto o = i * 8; + uint64_t v = 0; + for(int n = 0; n < 8; n++) { + v <<= 8; + v |= a[o + n]; + } + s[i] = v; + } +} + +void Xoshiro256::hash_then_set_s(const ByteVector& bytes) { + auto digest = sha256(bytes); + std::array a; + memcpy(a.data(), &digest[0], 32); + set_s(a); +} + +Xoshiro256::Xoshiro256(const std::array& a) { + set_s(a); +} + +Xoshiro256::Xoshiro256(const ByteVector& bytes) { + hash_then_set_s(bytes); +} + +Xoshiro256::Xoshiro256(const std::string& s) { + ByteVector bytes(s.begin(), s.end()); + hash_then_set_s(bytes); +} + +Xoshiro256::Xoshiro256(uint32_t crc32) { + auto bytes = int_to_bytes(crc32); + hash_then_set_s(bytes); +} + +double Xoshiro256::next_double() { + auto m = ((double)std::numeric_limits::max()) + 1; + return next() / m; +} + +uint64_t Xoshiro256::next_int(uint64_t low, uint64_t high) { + return uint64_t(next_double() * (high - low + 1)) + low; +} + +uint8_t Xoshiro256::next_byte() { + return uint8_t(next_int(0, 255)); +} + +ByteVector Xoshiro256::next_data(size_t count) { + ByteVector result; + result.reserve(count); + for(int i = 0; i < count; i++) { + result.push_back(next_byte()); + } + return result; +} + +uint64_t Xoshiro256::next() { + const uint64_t result = rotl(s[1] * 5, 7) * 9; + + const uint64_t t = s[1] << 17; + + s[2] ^= s[0]; + s[3] ^= s[1]; + s[1] ^= s[2]; + s[0] ^= s[3]; + + s[2] ^= t; + + s[3] = rotl(s[3], 45); + + return result; +} + +/* This is the jump function for the generator. It is equivalent + to 2^128 calls to next(); it can be used to generate 2^128 + non-overlapping subsequences for parallel computations. */ + +void Xoshiro256::jump() { + static const uint64_t JUMP[] = { 0x180ec6d33cfd0aba, 0xd5a61266f0c9392c, 0xa9582618e03fc9aa, 0x39abdc4529b1661c }; + + uint64_t s0 = 0; + uint64_t s1 = 0; + uint64_t s2 = 0; + uint64_t s3 = 0; + for(int i = 0; i < sizeof JUMP / sizeof *JUMP; i++) + for(int b = 0; b < 64; b++) { + if (JUMP[i] & UINT64_C(1) << b) { + s0 ^= s[0]; + s1 ^= s[1]; + s2 ^= s[2]; + s3 ^= s[3]; + } + next(); + } + + s[0] = s0; + s[1] = s1; + s[2] = s2; + s[3] = s3; +} + +/* This is the long-jump function for the generator. It is equivalent to + 2^192 calls to next(); it can be used to generate 2^64 starting points, + from each of which jump() will generate 2^64 non-overlapping + subsequences for parallel distributed computations. */ + +void Xoshiro256::long_jump() { + static const uint64_t LONG_JUMP[] = { 0x76e15d3efefdcbbf, 0xc5004e441c522fb3, 0x77710069854ee241, 0x39109bb02acbe635 }; + + uint64_t s0 = 0; + uint64_t s1 = 0; + uint64_t s2 = 0; + uint64_t s3 = 0; + for(int i = 0; i < sizeof LONG_JUMP / sizeof *LONG_JUMP; i++) + for(int b = 0; b < 64; b++) { + if (LONG_JUMP[i] & UINT64_C(1) << b) { + s0 ^= s[0]; + s1 ^= s[1]; + s2 ^= s[2]; + s3 ^= s[3]; + } + next(); + } + + s[0] = s0; + s[1] = s1; + s[2] = s2; + s[3] = s3; +} + +} diff --git a/src/third-party/bcur/xoshiro256.hpp b/src/third-party/bcur/xoshiro256.hpp new file mode 100644 index 0000000..2654cbc --- /dev/null +++ b/src/third-party/bcur/xoshiro256.hpp @@ -0,0 +1,45 @@ +// +// xoshiro256.hpp +// +// Copyright © 2020 by Blockchain Commons, LLC +// Licensed under the "BSD-2-Clause Plus Patent License" +// + +#ifndef XOSHIRO256_HPP +#define XOSHIRO256_HPP + +#include +#include +#include +#include "utils.hpp" + +namespace ur { + +class Xoshiro256 { +public: + explicit Xoshiro256(const std::array& a); + explicit Xoshiro256(const std::array& a); + + explicit Xoshiro256(const ByteVector& bytes); + explicit Xoshiro256(const std::string& s); + explicit Xoshiro256(uint32_t crc32); + + uint64_t next(); + double next_double(); + uint64_t next_int(uint64_t low, uint64_t high); + uint8_t next_byte(); + ByteVector next_data(size_t count); + + void jump(); + void long_jump(); + +private: + uint64_t s[4]; + + void set_s(const std::array& a); + void hash_then_set_s(const ByteVector& bytes); +}; + +} + +#endif // XOSHIRO256_HPP