build: vendor bcur

This commit is contained in:
tobtoht 2023-12-30 17:55:24 +01:00
parent dcbf24c5b0
commit 086379f34a
No known key found for this signature in database
GPG key ID: E45B10DD027D2472
35 changed files with 3597 additions and 6 deletions

View file

@ -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

12
cmake/FindBCUR.cmake Normal file
View file

@ -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}")

View file

@ -13,8 +13,6 @@
#include "QrScanThread.h"
#include <bcur/ur-decoder.hpp>
namespace Ui {
class QrCodeScanDialog;
}

View file

@ -9,7 +9,6 @@
#include "utils/config.h"
#include "utils/Icons.h"
#include <bcur/bc-ur.hpp>
QrCodeScanWidget::QrCodeScanWidget(QWidget *parent)
: QWidget(parent)

View file

@ -13,6 +13,7 @@
#include "QrScanThread.h"
#include <bcur/bc-ur.hpp>
#include <bcur/ur-decoder.hpp>
namespace Ui {

21
src/third-party/bcur/CMakeLists.txt vendored Normal file
View file

@ -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)

2
src/third-party/bcur/README.md vendored Normal file
View file

@ -0,0 +1,2 @@
vendored from https://github.com/BlockchainCommons/bc-ur
2bfc3fd396498c2519273aeaa732abf7ea7d24b8

28
src/third-party/bcur/bc-ur.hpp vendored Normal file
View file

@ -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

169
src/third-party/bcur/bytewords.cpp vendored Normal file
View file

@ -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 <stdexcept>
#include <algorithm>
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);
}
}
}

30
src/third-party/bcur/bytewords.hpp vendored Normal file
View file

@ -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 <string>
#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

558
src/third-party/bcur/cbor-lite.hpp vendored Normal file
View file

@ -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 <exception>
#include <iterator>
#include <string>
#include <type_traits>
#include <cstdint>
#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 Type>
typename std::enable_if<std::is_unsigned<Type>::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 Buffer>
typename std::enable_if<std::is_class<Buffer>::value, std::size_t>::type encodeTagAndAdditional(
Buffer& buffer, Tag tag, Tag additional) {
buffer.push_back(static_cast<char>(tag + additional));
return 1;
}
template <typename InputIterator>
typename std::enable_if<std::is_class<InputIterator>::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 Buffer, typename Type>
typename std::enable_if<std::is_class<Buffer>::value && std::is_unsigned<Type>::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 InputIterator, typename Type>
typename std::enable_if<std::is_class<InputIterator>::value && std::is_unsigned<Type>::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<Type>(reinterpret_cast<const unsigned char&>(*(pos++))) << 56;
t |= static_cast<Type>(reinterpret_cast<const unsigned char&>(*(pos++))) << 48;
t |= static_cast<Type>(reinterpret_cast<const unsigned char&>(*(pos++))) << 40;
t |= static_cast<Type>(reinterpret_cast<const unsigned char&>(*(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<Type>(reinterpret_cast<const unsigned char&>(*(pos++))) << 24;
t |= static_cast<Type>(reinterpret_cast<const unsigned char&>(*(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<Type>(reinterpret_cast<const unsigned char&>(*(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<Type>(reinterpret_cast<const unsigned char&>(*(pos++)));
len++;
if ((flags & Flag::requireMinimalEncoding) && t < 24) throw Exception("encoding not minimal");
return len;
}
throw Exception("bad additional value");
}
template <typename Buffer, typename Type>
typename std::enable_if<std::is_class<Buffer>::value, std::size_t>::type encodeUnsigned(Buffer& buffer, const Type& t) {
return encodeTagAndValue(buffer, Major::unsignedInteger, t);
}
template <typename InputIterator, typename Type>
typename std::enable_if<std::is_class<InputIterator>::value && std::is_unsigned<Type>::value && !std::is_const<Type>::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 Buffer, typename Type>
typename std::enable_if<std::is_class<Buffer>::value, std::size_t>::type encodeNegative(Buffer& buffer, const Type& t) {
return encodeTagAndValue(buffer, Major::negativeInteger, t);
}
template <typename InputIterator, typename Type>
typename std::enable_if<std::is_class<InputIterator>::value && std::is_unsigned<Type>::value && !std::is_const<Type>::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 Buffer, typename Type>
typename std::enable_if<std::is_class<Buffer>::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 InputIterator, typename Type>
typename std::enable_if<std::is_class<InputIterator>::value && std::is_signed<Type>::value && !std::is_const<Type>::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<long long>(val);
break;
default:
throw Exception("not integer");
}
return len;
}
template <typename Buffer, typename Type>
typename std::enable_if<std::is_class<Buffer>::value && std::is_same<bool, Type>::value, std::size_t>::type encodeBool(
Buffer& buffer, const Type& t) {
return encodeTagAndAdditional(buffer, Major::simple, t ? Minor::True : Minor::False);
}
template <typename InputIterator, typename Type>
typename std::enable_if<std::is_class<InputIterator>::value && std::is_same<bool, Type>::value && !std::is_const<Type>::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 Buffer, typename Type>
typename std::enable_if<std::is_class<Buffer>::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 InputIterator, typename Type>
typename std::enable_if<std::is_class<InputIterator>::value && !std::is_const<Type>::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<decltype(dist)>(value)) throw Exception("not enough input");
t.insert(std::end(t), pos, pos + value);
std::advance(pos, value);
return len + value;
}
template <typename Buffer, typename Type>
typename std::enable_if<std::is_class<Buffer>::value && std::is_unsigned<Type>::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 InputIterator, typename Type>
typename std::enable_if<std::is_class<InputIterator>::value && !std::is_const<Type>::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 Buffer, typename Type>
typename std::enable_if<std::is_class<Buffer>::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 InputIterator, typename Type>
typename std::enable_if<std::is_class<InputIterator>::value && !std::is_const<Type>::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 Buffer, typename Type>
typename std::enable_if<std::is_class<Buffer>::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 InputIterator, typename Type>
typename std::enable_if<std::is_class<InputIterator>::value && !std::is_const<Type>::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<decltype(dist)>(value)) throw Exception("not enough input");
t.insert(std::end(t), pos, pos + value);
std::advance(pos, value);
return len + value;
}
template <typename Buffer, typename Type>
typename std::enable_if<std::is_class<Buffer>::value && std::is_unsigned<Type>::value, std::size_t>::type encodeArraySize(
Buffer& buffer, const Type& t) {
return encodeTagAndValue(buffer, Major::array, t);
}
template <typename InputIterator, typename Type>
typename std::enable_if<std::is_class<InputIterator>::value && !std::is_const<Type>::value && std::is_unsigned<Type>::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 Buffer, typename Type>
typename std::enable_if<std::is_class<Buffer>::value && std::is_unsigned<Type>::value, std::size_t>::type encodeMapSize(
Buffer& buffer, const Type& t) {
return encodeTagAndValue(buffer, Major::map, t);
}
template <typename InputIterator, typename Type>
typename std::enable_if<std::is_class<InputIterator>::value && !std::is_const<Type>::value && std::is_unsigned<Type>::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 Buffer, typename Type>
typename std::enable_if<std::is_class<Buffer>::value && std::is_floating_point<Type>::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<const char*>(&t);
} else {
ft = static_cast<decltype(ft)>(t);
p = reinterpret_cast<char*>(&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 Buffer, typename Type>
typename std::enable_if<std::is_class<Buffer>::value && std::is_floating_point<Type>::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<const char*>(&t);
} else {
ft = t;
p = reinterpret_cast<char*>(&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 InputIterator, typename Type>
typename std::enable_if<std::is_class<InputIterator>::value && std::is_floating_point<Type>::value && !std::is_const<Type>::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<int>(sizeof(float))) throw Exception("not enough input");
char* p;
float ft;
if (sizeof(t) == sizeof(ft)) {
p = reinterpret_cast<char*>(&t);
} else {
ft = static_cast<decltype(ft)>(t);
p = reinterpret_cast<char*>(&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 InputIterator, typename Type>
typename std::enable_if<std::is_class<InputIterator>::value && std::is_floating_point<Type>::value && !std::is_const<Type>::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<int>(sizeof(double))) throw Exception("not enough input");
char* p;
double ft;
if (sizeof(t) == sizeof(ft)) {
p = reinterpret_cast<char*>(&t);
} else {
ft = t;
p = reinterpret_cast<char*>(&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

43
src/third-party/bcur/crc32.c vendored Normal file
View file

@ -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 <memory.h>
#ifdef ARDUINO
#define htonl(x) __builtin_bswap32((uint32_t) (x))
#elif _WIN32
#include <winsock2.h>
#else
#include <arpa/inet.h>
#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));
}

28
src/third-party/bcur/crc32.h vendored Normal file
View file

@ -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 <stdint.h>
#include <stdlib.h>
#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

View file

@ -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 <utility>
#include <algorithm>
#include <iostream>
#include <string>
#include <cmath>
#include <numeric>
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<ByteVector>& 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<Part> 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<Part> 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<ByteVector> 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<size_t>(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<ByteVector>(r)) {
desc = to_string(get<ByteVector>(r).size()) + " bytes";
} else if(holds_alternative<exception>(r)) {
desc = get<exception>(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<const PartIndexes, Part>& 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;
}
}

View file

@ -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 <map>
#include <exception>
#include <deque>
#include <optional>
#include <variant>
namespace ur {
class FountainDecoder final {
public:
typedef std::optional<std::variant<ByteVector, std::exception> > 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<ByteVector>(result().value()); }
bool is_failure() const { return result() && std::holds_alternative<std::exception>(result().value()); }
bool is_complete() const { return result().has_value(); }
const ByteVector& result_message() const { return std::get<ByteVector>(result().value()); }
const std::exception& result_error() const { return std::get<std::exception>(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<ByteVector>& 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<PartIndexes> last_part_indexes_;
size_t processed_parts_count_ = 0;
Result result_;
typedef std::map<PartIndexes, Part> PartDict;
std::optional<PartIndexes> _expected_part_indexes;
std::optional<size_t> _expected_fragment_len;
std::optional<size_t> _expected_message_len;
std::optional<uint32_t> _expected_checksum;
PartDict _simple_parts;
PartDict _mixed_parts;
std::deque<Part> _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

View file

@ -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 <assert.h>
#include <cmath>
#include <optional>
#include <vector>
#include <limits>
#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<size_t> 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<ByteVector> FountainEncoder::partition_message(const ByteVector &message, size_t fragment_len) {
auto remaining = message;
vector<ByteVector> 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<decltype(seq_num_)>::max()) { throw InvalidHeader(); }
seq_num_ = n;
CborLite::decodeUnsigned(i, end, n);
if(n > std::numeric_limits<decltype(seq_len_)>::max()) { throw InvalidHeader(); }
seq_len_ = n;
CborLite::decodeUnsigned(i, end, n);
if(n > std::numeric_limits<decltype(message_len_)>::max()) { throw InvalidHeader(); }
message_len_ = n;
CborLite::decodeUnsigned(i, end, n);
if(n > std::numeric_limits<decltype(checksum_)>::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<uint32_t>::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_);
}
}

View file

@ -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 <stddef.h>
#include <vector>
#include <exception>
#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<ByteVector> 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<ByteVector> fragments_;
uint32_t seq_num_;
PartIndexes last_part_indexes_;
ByteVector mix(const PartIndexes& indexes) const;
};
}
#endif // BC_UR_FOUNTAIN_ENCODER_HPP

43
src/third-party/bcur/fountain-utils.cpp vendored Normal file
View file

@ -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<double> 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<size_t> 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<size_t>({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<size_t> 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<size_t>(shuffled_indexes.begin(), shuffled_indexes.begin() + degree);
}
}
}

61
src/third-party/bcur/fountain-utils.hpp vendored Normal file
View file

@ -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 <functional>
#include <vector>
#include <set>
#include <algorithm>
#include <iterator>
#include <stdint.h>
#include "xoshiro256.hpp"
namespace ur {
typedef std::set<size_t> PartIndexes;
// Fisher-Yates shuffle
template<typename T>
std::vector<T> shuffled(const std::vector<T>& items, Xoshiro256& rng) {
auto remaining = items;
std::vector<T> 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<typename T>
bool is_strict_subset(const std::set<T>& a, const std::set<T>& b) {
if(a == b) { return false; }
return std::includes(b.begin(), b.end(), a.begin(), a.end());
}
template<typename T>
std::set<T> set_difference(const std::set<T>& a, const std::set<T>& b) {
std::set<T> result;
std::set_difference(a.begin(), a.end(), b.begin(), b.end(), std::inserter(result, result.begin()));
return result;
}
template<typename T>
bool contains(const std::set<T>& s, const T& v) {
return s.find(v) != s.end();
}
size_t choose_degree(size_t seq_len, Xoshiro256& rng);
std::set<size_t> choose_fragments(uint32_t seq_num, size_t seq_len, uint32_t checksum);
}
#endif // BC_UR_FOUNTAIN_UTILS_HPP

84
src/third-party/bcur/memzero.c vendored Normal file
View file

@ -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 <string.h>
#ifdef _WIN32
#include <Windows.h>
#endif
#ifdef __unix__
#include <strings.h>
#include <sys/param.h>
#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 <agl@google.com> 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
}

8
src/third-party/bcur/memzero.h vendored Normal file
View file

@ -0,0 +1,8 @@
#ifndef BC_UR_MEMZERO_H
#define BC_UR_MEMZERO_H
#include <stddef.h>
void memzero(void* const pnt, const size_t len);
#endif // BC_UR_MEMZERO_H

84
src/third-party/bcur/random-sampler.cpp vendored Normal file
View file

@ -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 <numeric>
#include <algorithm>
#include <assert.h>
using namespace std;
namespace ur {
RandomSampler::RandomSampler(std::vector<double> 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<double> P;
P.reserve(n);
transform(probs.begin(), probs.end(), back_inserter(P), [&](double d) { return d * double(n) / sum; });
vector<int> S;
S.reserve(n);
vector<int> 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<double> _probs(n, 0);
vector<int> _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<double()> rng) {
auto r1 = rng();
auto r2 = rng();
auto n = probs_.size();
auto i = int(double(n) * r1);
return r2 < probs_[i] ? i : aliases_[i];
}
}

38
src/third-party/bcur/random-sampler.hpp vendored Normal file
View file

@ -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 <vector>
#include <functional>
// 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<double> probs);
int next(std::function<double()> rng);
private:
std::vector<double> probs_;
std::vector<int> aliases_;
};
}
#endif // BC_UR_RANDOM_SAMPLER_HPP

889
src/third-party/bcur/sha2.c vendored Normal file
View file

@ -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 <string.h>
#include <stdint.h>
#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 <sys/types.h> (which in turn includes
* <machine/endian.h> 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);
}

100
src/third-party/bcur/sha2.h vendored Normal file
View file

@ -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 <stdint.h>
#include <stddef.h>
#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

119
src/third-party/bcur/ur-decoder.cpp vendored Normal file
View file

@ -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<string, StringVector> 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<uint32_t, size_t> 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;
}
}
}

66
src/third-party/bcur/ur-decoder.hpp vendored Normal file
View file

@ -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 <string>
#include <exception>
#include <utility>
#include <optional>
#include "ur.hpp"
#include "fountain-decoder.hpp"
namespace ur {
class URDecoder final {
public:
typedef std::optional<std::variant<UR, std::exception> > 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<std::string>& 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<UR>(result().value()); }
bool is_failure() const { return result() && std::holds_alternative<std::exception>(result().value()); }
bool is_complete() const { return result().has_value(); }
const UR& result_ur() const { return std::get<UR>(result().value()); }
const std::exception& result_error() const { return std::get<std::exception>(result().value()); }
bool receive_part(const std::string& s);
private:
FountainDecoder fountain_decoder;
std::optional<std::string> expected_type_;
Result result_;
static std::pair<std::string, StringVector> parse(const std::string& string);
static std::pair<uint32_t, size_t> 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

50
src/third-party/bcur/ur-encoder.cpp vendored Normal file
View file

@ -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);
}
}

52
src/third-party/bcur/ur-encoder.hpp vendored Normal file
View file

@ -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 <string>
#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

28
src/third-party/bcur/ur.cpp vendored Normal file
View file

@ -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 <iostream>
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

34
src/third-party/bcur/ur.hpp vendored Normal file
View file

@ -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 <string>
#include <exception>
#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

171
src/third-party/bcur/utils.cpp vendored Normal file
View file

@ -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 <vector>
#include <sstream>
#include <algorithm>
#include <cctype>
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;
}
}

89
src/third-party/bcur/utils.hpp vendored Normal file
View file

@ -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 <stdint.h>
#include <vector>
#include <utility>
#include <string>
#include <array>
#include <assert.h>
namespace ur {
typedef std::vector<uint8_t> ByteVector;
typedef std::vector<std::string> 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<typename T>
void append(std::vector<T>& target, const std::vector<T>& source) {
target.insert(target.end(), source.begin(), source.end());
}
template<typename T, size_t N>
void append(std::vector<T>& target, const std::array<T, N>& source) {
target.insert(target.end(), source.begin(), source.end());
}
template<typename T>
std::vector<T> join(const std::vector<std::vector<T>>& parts) {
std::vector<T> result;
for(auto part: parts) { append(result, part); }
return result;
}
template<typename T>
std::pair<std::vector<T>, std::vector<T>> split(const std::vector<T>& 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<typename T>
std::vector<T> take_first(const std::vector<T> &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

177
src/third-party/bcur/xoshiro256.cpp vendored Normal file
View file

@ -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 <limits>
#include <cstring>
/* 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 <http://creativecommons.org/publicdomain/zero/1.0/>. */
/* 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<uint64_t, 4>& a) {
s[0] = a[0];
s[1] = a[1];
s[2] = a[2];
s[3] = a[3];
}
void Xoshiro256::set_s(const std::array<uint8_t, 32>& 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<uint8_t, 32> a;
memcpy(a.data(), &digest[0], 32);
set_s(a);
}
Xoshiro256::Xoshiro256(const std::array<uint8_t, 32>& 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<uint64_t>::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;
}
}

45
src/third-party/bcur/xoshiro256.hpp vendored Normal file
View file

@ -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 <stdint.h>
#include <array>
#include <string>
#include "utils.hpp"
namespace ur {
class Xoshiro256 {
public:
explicit Xoshiro256(const std::array<uint64_t, 4>& a);
explicit Xoshiro256(const std::array<uint8_t, 32>& 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<uint8_t, 32>& a);
void hash_then_set_s(const ByteVector& bytes);
};
}
#endif // XOSHIRO256_HPP