145a3a911f
Also fixed it so that hash<...> a = hsh() << ...; Actually works. Needed a cast in the hsh class that calls the constructor in the hash class.
540 lines
22 KiB
C++
540 lines
22 KiB
C++
#pragma once
|
|
// libsodium is typeless, which I find confusing. Everything is unsigned
|
|
// bytes pointed at by naked pointers. It is well written assembly language
|
|
// written in C It is not even C written in C, let alone C++ written in C.
|
|
//
|
|
// NaCl provides a high level C++ interface to the low level C libsodium
|
|
// interface, but I just don't like the people running NaCl and suspect them
|
|
// of being in bed with my enemies, whereas I do like the people running
|
|
// LibSodium.
|
|
//
|
|
// It is nonetheless probably stupid to write my own high level interface to
|
|
// LibSodium, when one has already been written, but I am going to do so
|
|
// anyway, because elliptic curves are just way cool, and because ...
|
|
// because ... I am having trouble thinking up a good excuse ... ah yes,
|
|
// because when I read the NaCl documentation it says "X has not been
|
|
// implemented yet" when X is something that is pretty easy to implement
|
|
// if one has direct access to elliptic curve objects, and our Merkle patricia
|
|
// blockchain is going to be a great big pile of elliptic curve objects.
|
|
// NaCl suffers from the chronic disease both of open source projects,
|
|
// and also of high level interfaces to low level data structures, of getting
|
|
// fine tuned to the implementer's pet projects, which fine tuning is apt to
|
|
// get in the way of someone else's pet project.
|
|
//
|
|
// LibSodium, being assembly language written in C, rather than C written
|
|
// in C, or C++ written in C, has the problems:
|
|
// If you mix up the pointer to one kind of object with the pointer to
|
|
// another kind of object, you are sol
|
|
// If you mix up a pointer to a thirty two byte object with a pointer to a
|
|
// sixtyfour byte object, you are really sol
|
|
// if you reference something that has been de-allocated, you are sol.
|
|
// If you reference something that has not been properly initialized,
|
|
// you are sol.
|
|
// It is all raw memory, and structure exists only in the head of the
|
|
// programmer. The compiler knows nothing of these structures.
|
|
//
|
|
// That said, there is an immense amount of cryptographic knowledge
|
|
// encapsulated in this library, and I need to lift that knowledge into the
|
|
// language of C++20, from C that is almost assembly.
|
|
// There is a huge amount of knowledge embedded, and translating it
|
|
// from what is almost a machine language representation to a C++20
|
|
// representation involves a big pile of stuff.
|
|
|
|
// We are going to need to lift
|
|
// https://paragonie.com/blog/2017/06/libsodium-quick-reference-quick-comparison-similar-functions-and-which-one-use or their ristretto equivalents to C++
|
|
|
|
// Starting with ristretto points and scalars, but they are useless without a
|
|
// pile of other things, many of those other things being in
|
|
// https://download.libsodium.org/doc/helpers/
|
|
// I went there to find out what the hell "sodium_is_zero" means, but
|
|
// found a pile of other things that I am going to need, and got distracted
|
|
// by no end of odds and ends that I am going to need to be to lift to
|
|
// C++20 in order for ristretto points and scalars to be put to any use.
|
|
|
|
|
|
void randombytes_buf(std::span<byte> in);
|
|
void randombytes_buf(std::span<char > in);
|
|
|
|
|
|
namespace ristretto255 {
|
|
using
|
|
ro::to_base64_string,
|
|
ro::serialize, ro::bin2hex, ro::hex2bin,
|
|
ro::bin2hex, ro::CompileSizedString,
|
|
ro::has_machine_independent_representation;
|
|
;
|
|
// a class representing ristretto255 elliptic points
|
|
class point;
|
|
|
|
// a class representing ristretto255 scalars
|
|
class scalar;
|
|
|
|
template<unsigned int>class hsh;
|
|
template<unsigned int>class hash;
|
|
template <class T>decltype(std::declval<T>().blob, bool()) ConstantTimeEqualityTest(const T& x, const T& y) {
|
|
if (T::constant_time_required) {
|
|
return 0 == sodium_memcmp(&x.blob[0], &y.blob[0], sizeof(x.blob));
|
|
}
|
|
else return x == y;
|
|
}
|
|
|
|
template <class T>decltype(std::declval<T>().blob, wxString()) to_wxString(const T& p_blob) {
|
|
std::array<char, (sizeof(p_blob.blob) * 8 + 11) / 6> sz;
|
|
bits2base64(&(p_blob.blob[0]), 0, sizeof(p_blob.blob) * 8, std::span<char>(sz));
|
|
return wxString::FromUTF8Unchecked(&sz[0]);
|
|
}
|
|
|
|
// hsh constructs a partial hash, not yet finalized
|
|
// normally never explicitly used in code, but rather a nameless rtype value on the stack.
|
|
// To which more stuff can be added with the << operator
|
|
// usage:
|
|
// hash( hsh(a, b) << c << f << g );
|
|
// assert( hash(a, b, c, d, e) == hash(hsh(a, b, c) << d << e) );
|
|
// hash finalizes hsh.
|
|
// Of course you would never use it that way, because you would only
|
|
// use it explicitly if you wanted to keep it around
|
|
// attempting hash more things into an hsh object that has already
|
|
// been finalized will throw an exception.
|
|
// Old type C byte array arguments after the first are vulnerable to
|
|
// array decay to pointer, so wrap them in std::span(OldTypeCarray)
|
|
// or hash them using "<<" rather than putting them in the initializer.
|
|
// It always a wise precaution to not use old type C arays, or wrap them
|
|
// in a span.
|
|
// Old type zero terminated strings work. The trailing zero is included
|
|
// in the hash, thus hash("the quick ", "brown fox") != hash("the quick brown fox")
|
|
|
|
|
|
template<unsigned int hashsize = 256> class hsh {
|
|
public:
|
|
static_assert(hashsize > 63 && hashsize % 64 == 0 && crypto_generichash_BYTES_MIN * 8 <= hashsize && hashsize <= crypto_generichash_BYTES_MAX * 8, "Bad hash size.");
|
|
static constexpr unsigned int type_indentifier = 2 + (hashsize + 0x90 * 8) / 64;
|
|
static_assert(crypto_generichash_BYTES_MAX < 0x90, "Change in max hash has broken our type ids");
|
|
crypto_generichash_blake2b_state st;
|
|
hsh() {
|
|
int i{ crypto_generichash_blake2b_init(
|
|
&st,
|
|
nullptr,0,
|
|
hashsize / 8)
|
|
};
|
|
assert(i == 0);
|
|
}
|
|
|
|
operator hash<hashsize>();
|
|
|
|
template<has_machine_independent_representation T, typename... Args>
|
|
hsh<hashsize>& hashinto(const T& j, Args... args) {
|
|
auto sj = ro::serialize(j);
|
|
int i = crypto_generichash_blake2b_update(
|
|
&(this->st),
|
|
(const unsigned char*)&sj[0],
|
|
sj.size()
|
|
);
|
|
if (i) throw HashReuseException();
|
|
if constexpr (sizeof...(args) > 0) {
|
|
(*this).hashinto(args...);
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
template<has_machine_independent_representation T> hsh<hashsize>& operator << (const T& j) {
|
|
auto sj = ro::serialize(j);
|
|
auto i = crypto_generichash_blake2b_update(
|
|
&(this->st),
|
|
(const unsigned char*)&sj[0],
|
|
sj.size()
|
|
);
|
|
if (i) throw HashReuseException();
|
|
return *this;
|
|
}
|
|
|
|
|
|
};
|
|
static_assert(!has_machine_independent_representation<hsh<256> >, "Don't want to partially hash partial hashes");
|
|
|
|
// This constructs a finalized hash.
|
|
//
|
|
// Usage:
|
|
// hash c(x, y, z ...);
|
|
// creates the variable c, and initializes and finalizes it with the hash of x, y, z ...
|
|
//
|
|
// is equivalent to
|
|
// hsh b();
|
|
// b << x << y; b << z << ...; //unfinalized hash
|
|
// hash c(b); //creates c and finalizes b
|
|
// // Any subsequent operations on b should cause a run time exception.
|
|
//
|
|
// and equivalent to
|
|
// hash<256> c = hsh() << x << y << z << ...;
|
|
//
|
|
// and equivalent to
|
|
// hash<256> c(hsh() << x << y << z << ... )
|
|
//
|
|
// hash and hsh serialize all their arguments, converting them into machine,
|
|
// compiler, and locale independent form. If they don't know how to serialize
|
|
// an argument type, you get a compile time error.
|
|
// "the concept 'ro::has_machine_independent_representation<...>' evaluated to false"
|
|
//
|
|
// Suppose you don't want hsh finalized, perhaps because you are creating many
|
|
// hashes each with same very lengthy preamble but different suffixes: Then:
|
|
// hsh(b);
|
|
// b << preamble;
|
|
// hash cfoo = hsh(b) << suffix_foo;
|
|
// hash cbar = hsh(b) << suffix_bar;
|
|
//
|
|
// To serialize a new type,
|
|
// create a new overload for the function "serialize"
|
|
// to hash a wxString, use its ToUTF8 member
|
|
// wxString wxsHello(L"Hello world");
|
|
// hash hashHello(wxsHello.ToUTF8());
|
|
// Old type C array arguments after the first are vulnerable to array decay
|
|
// to pointer, which will generate a compile time error. If this happens use
|
|
// hsh and "<<" for them. or wrap them in std::span
|
|
// It always a wise precaution to not use old type C arays, or wrap them
|
|
// in a span.
|
|
// std::strings and old type zero terminated utf8 strings work. The trailing
|
|
// zero is included in the hash so that hash("tea", "spoon") != hash("teaspoon")
|
|
// The program should by running in the UTF8 locale, attempts to set that
|
|
// locale on startup. and tests for success in the unit test, so that the
|
|
// same string will yield the same hash no matter where in the world the code
|
|
// is running.
|
|
template<unsigned int hashsize = 256> class hash {
|
|
static_assert(
|
|
hashsize > 63 && hashsize % 64 == 0
|
|
&& crypto_generichash_BYTES_MIN * 8 <= hashsize
|
|
&& hashsize <= crypto_generichash_BYTES_MAX * 8,
|
|
"Bad hash size."
|
|
);
|
|
friend point;
|
|
friend scalar;
|
|
friend hsh<hashsize>;
|
|
public:
|
|
static constexpr unsigned int type_indentifier = 2 + hashsize / 64;
|
|
hash() = default;
|
|
std::array<uint8_t, hashsize / 8> blob;
|
|
~hash() = default;
|
|
hash(hash&&) = default; // Move constructor
|
|
hash(const hash&) = default; // Copy constructor
|
|
hash& operator=(hash&&) = default; // Move assignment.
|
|
hash& operator=(const hash&) = default; // Copy assignment.
|
|
explicit hash(hsh<hashsize>& in) {
|
|
int i = crypto_generichash_blake2b_final(
|
|
&in.st,
|
|
&blob[0], hashsize / 8);
|
|
assert(i == 0);
|
|
if (i) throw HashReuseException();
|
|
}
|
|
template< has_machine_independent_representation T, typename... Args>explicit hash(const T& first, Args... args) {
|
|
// not restraining the variant args by concept, because they get caught deeper in,
|
|
// and visual studio reporting of variant concept errors sucks.
|
|
hsh<hashsize> in;
|
|
in << first;
|
|
if constexpr (sizeof...(args) > 0) {
|
|
in.hashinto( ro::trigger_error(args)...);
|
|
}
|
|
int i = crypto_generichash_blake2b_final(
|
|
&in.st,
|
|
&blob[0], hashsize / 8);
|
|
assert(i == 0);
|
|
}
|
|
hash& operator=(hsh<hashsize>&& in) {
|
|
int i = crypto_generichash_blake2b_final(
|
|
&in.st,
|
|
&blob[0], hashsize / 8);
|
|
if (i) throw HashReuseException();
|
|
}
|
|
bool operator==(const hash<hashsize>& pt) const& {
|
|
return blob == pt.blob; //Do not need constant time equality test on hashes
|
|
}
|
|
bool operator!=(const hash<hashsize>& pt) const& {
|
|
return blob != pt.blob; //Do not need constant time equality test on hashes
|
|
}
|
|
};
|
|
template<unsigned int hashsize = 256>
|
|
hsh<hashsize>::operator hash<hashsize>() {
|
|
return hash(*this);
|
|
}
|
|
|
|
// a class representing ristretto255 elliptic points,
|
|
// which are conveniently of prime order.
|
|
class point
|
|
{
|
|
// We will be reading points from the database, as blobs,
|
|
// reading them from the network as blobs,
|
|
// and reading them from human entered text as base58 encoded blobs.
|
|
// Therefore, invalid point initialization data is all too possible.
|
|
public:
|
|
static constexpr unsigned int type_indentifier = 1;
|
|
std::array<uint8_t, crypto_core_ristretto255_BYTES> blob;
|
|
static_assert(
|
|
std::is_trivially_copyable<std::array<uint8_t, crypto_core_ristretto255_BYTES>>(),
|
|
"does not support memcpy init"
|
|
);
|
|
static_assert(sizeof(blob) == 32,
|
|
"watch for size and alignment bugs. "
|
|
"Everyone should standarize on 256 bit public keys except for special needs"
|
|
);
|
|
static point ptZero;
|
|
static point ptBase;
|
|
explicit point() = default;
|
|
// After loading a point as a blog from the network, from the database,
|
|
// or from user data typed as text, have to check for validity.
|
|
bool valid(void) const { return 0 != crypto_core_ristretto255_is_valid_point(&blob[0]); }
|
|
explicit constexpr point(std::array<uint8_t, crypto_core_ristretto255_BYTES>&& in) :
|
|
blob{ std::forward<std::array<uint8_t, crypto_core_ristretto255_BYTES>>(in) } { };
|
|
static_assert(crypto_core_ristretto255_BYTES == 32, "256 bit points expected");
|
|
~point() = default;
|
|
point(point&&) = default; // Move constructor
|
|
point(const point&) = default; // Copy constructor
|
|
point& operator=(point&&) = default; // Move assignment.
|
|
point& operator=(const point&) = default; // Copy assignment.
|
|
bool operator==(const point& pt) const& {
|
|
return ConstantTimeEqualityTest(*this, pt);
|
|
}
|
|
point operator+(const point &pt) const& {
|
|
point me;
|
|
auto i{ crypto_core_ristretto255_add(_Out_ & me.blob[0], &blob[0], &pt.blob[0])};
|
|
assert(i == 0);
|
|
if (i != 0)throw NonRandomScalarException();
|
|
return me;
|
|
}
|
|
|
|
point operator-(const point& pt) const& {
|
|
point me;
|
|
auto i{ crypto_core_ristretto255_sub(_Out_ &me.blob[0], &blob[0], &pt.blob[0]) };
|
|
assert(i == 0);
|
|
if (i != 0)throw NonRandomScalarException();
|
|
return me;
|
|
static_assert(sizeof(blob) == 32, "alignment?");
|
|
}
|
|
|
|
point operator*(const scalar&) const &noexcept;
|
|
point operator*(int) const& noexcept;
|
|
|
|
explicit point(const hash<512>& x) noexcept {
|
|
static_assert(crypto_core_ristretto255_HASHBYTES * 8 == 512,
|
|
"we need 512 bit randoms to ensure our points and scalars are uniformly distributed"
|
|
);
|
|
// There should be scalar from hash, not point from hash
|
|
// libsodium already supplies a random point
|
|
int i{
|
|
crypto_core_ristretto255_from_hash(&blob[0], &(x.blob)[0]) };
|
|
assert(i == 0);
|
|
}
|
|
|
|
static point random(void) {
|
|
point me;
|
|
crypto_core_ristretto255_random(_Out_ &(me.blob[0]));
|
|
return me;
|
|
}
|
|
|
|
bool operator !() const& {
|
|
return 0 != sodium_is_zero(&blob[0], sizeof(blob));
|
|
}
|
|
|
|
static bool constant_time_required;
|
|
|
|
};
|
|
|
|
class scalar
|
|
{
|
|
friend point;
|
|
public:
|
|
static constexpr unsigned int type_indentifier = 2;
|
|
std::array<uint8_t, crypto_core_ristretto255_SCALARBYTES> blob;
|
|
static_assert(sizeof(blob) == 32, "watch for size and alignment bugs. Everyone should standarize on 256 bit secret keys except for special needs");
|
|
explicit scalar() = default;
|
|
~scalar() = default;
|
|
explicit constexpr scalar(std::array<uint8_t, crypto_core_ristretto255_BYTES>&& in) : blob{ in } {};
|
|
explicit constexpr scalar(std::array<uint8_t, crypto_core_ristretto255_BYTES>* in) :blob(*in) {};
|
|
explicit constexpr scalar(uintmax_t k){ for (auto& j : blob) { j = k; k = k >> 8; } }
|
|
template <class T>
|
|
explicit constexpr scalar(std::enable_if_t < ro::is_standard_unsigned_integer<T>, T> i) :scalar(uintmax_t(i)) {}
|
|
template <class T, class U = std::enable_if_t<ro::is_standard_signed_integer<T>, uintmax_t>>
|
|
explicit constexpr scalar(T i) : scalar(U(ro::iabs<T>(i))) {
|
|
static_assert (ro::is_standard_signed_integer<T>);
|
|
if (i < 0) crypto_core_ristretto255_scalar_negate(&blob[0], &blob[0]);
|
|
}
|
|
scalar(scalar&&) = default; // Move constructor
|
|
scalar(const scalar&) = default; // Copy constructor
|
|
scalar& operator=(scalar&&) = default; // Move assignment.
|
|
scalar& operator=(const scalar&) = default; // Copy assignment.
|
|
/* Don't need constant time equality test
|
|
bool operator==(const scalar& sc) const& {
|
|
return ConstantTimeEqualityTest(*this, sc);
|
|
}*/
|
|
std::strong_ordering operator <=>(const scalar& sc) const& noexcept;
|
|
// strangely, contrary to documentation, compiler generates operator>
|
|
// but fails to generate operator==
|
|
bool operator==(const scalar& sc) const& = default;
|
|
/* {
|
|
return (*this <=> sc) == 0;
|
|
}*/
|
|
|
|
|
|
//bool operator!=(const scalar& sc) const& {
|
|
// return !ConstantTimeEqualityTest(*this, sc);
|
|
// }
|
|
scalar operator+(const scalar sclr) const& {
|
|
scalar me;
|
|
crypto_core_ristretto255_scalar_add( _Out_&me.blob[0], &blob[0], &sclr.blob[0]);
|
|
return me;
|
|
}
|
|
static_assert(sizeof(scalar::blob) == 32, "compiled");
|
|
|
|
scalar multiplicative_inverse() const &{
|
|
scalar me;
|
|
auto i = crypto_core_ristretto255_scalar_invert(_Out_&me.blob[0], &blob[0]);
|
|
assert(i == 0);
|
|
if (i != 0)throw NonRandomScalarException();
|
|
return me;
|
|
}
|
|
|
|
scalar operator-(const scalar& sclr) const& {
|
|
scalar me;
|
|
crypto_core_ristretto255_scalar_sub(_Out_&me.blob[0], &blob[0], &sclr.blob[0]);
|
|
return me;
|
|
}
|
|
|
|
scalar operator*(const scalar& sclr) const& {
|
|
scalar me;
|
|
crypto_core_ristretto255_scalar_mul(_Out_&me.blob[0], &blob[0], &sclr.blob[0]);
|
|
return me;
|
|
}
|
|
|
|
scalar operator/(const scalar sclr) const& {
|
|
return operator*(sclr.multiplicative_inverse());
|
|
}
|
|
|
|
scalar operator*(int64_t i) const& {
|
|
return operator * (scalar(i));
|
|
}
|
|
|
|
point operator*(const point& pt) const& {
|
|
point me;
|
|
auto i{ crypto_scalarmult_ristretto255(_Out_&me.blob[0], &blob[0], &pt.blob[0]) };
|
|
assert(i == 0);
|
|
if (i != 0)throw NonRandomScalarException();
|
|
return me;
|
|
}
|
|
|
|
point timesBase() const& {
|
|
point me;
|
|
auto i{ crypto_scalarmult_ristretto255_base(_Out_ & me.blob[0], &blob[0]) };
|
|
assert(i == 0);
|
|
if (i != 0)throw NonRandomScalarException();
|
|
return me;
|
|
}
|
|
|
|
explicit scalar(const hash<512>& x) {
|
|
static_assert(crypto_core_ristretto255_HASHBYTES == 64, "inconsistent use of magic numbers");
|
|
crypto_core_ristretto255_scalar_reduce(&blob[0], &(x.blob)[0]);
|
|
}
|
|
|
|
static scalar random(void) {
|
|
scalar me;
|
|
crypto_core_ristretto255_scalar_random(_Out_ & me.blob[0]);
|
|
return me;
|
|
}
|
|
|
|
bool valid(void) const& {
|
|
int x = sodium_is_zero(&blob[0], crypto_core_ed25519_SCALARBYTES);
|
|
return x==0 && *this<scOrder;
|
|
// The libsodium library allows unreduced scalars, but I do not
|
|
// except, of course, for scOrder itself
|
|
}
|
|
|
|
// We don't need constant time equality test on scalars
|
|
// since they normally appear in signatures, or are
|
|
// checked against a hash or a point
|
|
/*bool operator !() const& {
|
|
return 0 != sodium_is_zero(&blob[0], sizeof(blob));
|
|
}*/
|
|
|
|
/* explicit operator wxString() const&;
|
|
explicit operator std::string() const&;*/
|
|
static bool constant_time_required;
|
|
static const scalar& scOrder;
|
|
|
|
private:
|
|
void reverse(std::array < uint8_t, crypto_core_ristretto255_SCALARBYTES>const& ac) {
|
|
auto p = blob.rbegin();
|
|
for (auto x : ac) {
|
|
*p++ = x;
|
|
}
|
|
}
|
|
};
|
|
|
|
static_assert(ro::blob_type<scalar> && !ro::blob_type<int>);
|
|
static_assert(ro::is_blob_field_type<scalar>::value);
|
|
static_assert(ro::is_blob_field_type<scalar&>::value);
|
|
static_assert(ro::is_blob_field_type<point>::value);
|
|
static_assert(ro::is_blob_field_type<hash<256> >::value);
|
|
static_assert(false == ro::is_blob_field_type<char*>::value);
|
|
static_assert(ro::has_machine_independent_representation<scalar&>);
|
|
static_assert(ro::has_machine_independent_representation<hash<512>&>);
|
|
static_assert(ro::is_blob_field_type<int>::value == false);
|
|
static_assert(ro::has_machine_independent_representation<unsigned int>);
|
|
static_assert(ro::has_machine_independent_representation<char*>);
|
|
static_assert(ro::has_machine_independent_representation<uint8_t*> == false); //false because uint8_t * has no inband terminator
|
|
static_assert(false == ro::has_machine_independent_representation<wxString> && !ro::is_constructible_from_v<hash<256>, wxString>, "wxStrings are apt to convert anything to anything, with surprising and unexpected results");
|
|
static_assert(ro::has_machine_independent_representation<decltype(std::declval<wxString>().ToUTF8())> == true);
|
|
static_assert(ro::is_constructible_from_all_of<scalar, int, hash<512>, std::array<uint8_t, crypto_core_ristretto255_BYTES>>);
|
|
static_assert(ro::is_constructible_from_all_of<hash<256>, char*, short, unsigned short, hash<512>, point, scalar>, "want to be able to hash anything has_machine_independent_representation");
|
|
static_assert(false == ro::is_constructible_from_any_of<int, scalar, point, hsh<512>, hash<256>>);
|
|
static_assert(false == ro::is_constructible_from_any_of <scalar, wxString, hash<256>, byte*>, "do not want indiscriminate casts");
|
|
static_assert(false == ro::is_constructible_from_any_of <point, wxString, hash<256>, byte*>, "do not want indiscriminate casts ");
|
|
static_assert(false == ro::is_constructible_from_v<hash<256>, float>);
|
|
static_assert(ro::has_machine_independent_representation<float> == false);//Need to convert floats to
|
|
// their machine independent representation, possibly through idexp, frexp
|
|
// and DBL_MANT_DIG
|
|
static_assert(sizeof(decltype(ro::serialize(std::declval<scalar>())[0])) == 1);
|
|
static_assert (std::is_standard_layout<scalar>(), "scalar for some reason is not standard layout");
|
|
static_assert (std::is_trivial<scalar>(), "scalar for some reason is not trivial");
|
|
static_assert(sizeof(point) == 32, "funny alignment");
|
|
static_assert(sizeof(scalar) == 32, "funny alignment");
|
|
|
|
class signed_text {
|
|
public:
|
|
std::span<char> txt;
|
|
scalar c;
|
|
scalar s;
|
|
point K;
|
|
signed_text(
|
|
const scalar&, // Signer's secret key
|
|
const point&, // Signer's public key
|
|
std::span<char> // Text to be signed.
|
|
);
|
|
bool verify();
|
|
};
|
|
|
|
class CMasterSecret :public scalar {
|
|
public:
|
|
CMasterSecret() = default;
|
|
CMasterSecret(const scalar& pt) :scalar(pt) {}
|
|
CMasterSecret(const scalar&& pt) :scalar(pt) {}
|
|
CMasterSecret(CMasterSecret&&) = default; // Move constructor
|
|
CMasterSecret(const CMasterSecret&) = default; // Copy constructor
|
|
CMasterSecret& operator=(CMasterSecret&&) = default; // Move assignment.
|
|
CMasterSecret& operator=(const CMasterSecret&) = default; // Copy assignment.
|
|
template<class T> auto operator()(T psz) {
|
|
scalar& t(*this);
|
|
return scalar(hash<512>(t, psz));
|
|
}
|
|
|
|
};
|
|
|
|
|
|
} //End ristretto255 namespace
|
|
|
|
// Ristretto255 scalars are defined to be little endian on all hardware
|
|
// regardless of the endianess of the underlying hardware.
|
|
// though it is entirely possible that sometime in the future, this
|
|
// definition will be changed should big endian hardware ever be
|
|
// sufficiently popular for anyone to care.
|
|
// So, because scalars are in fact integers, displaying them as
|
|
// biendian on all hardware when displayed in hex
|
|
// or base64. Everything else gets displayed in memory order.
|
|
template<> ristretto255::scalar ro::hex2bin <ristretto255::scalar >(const ro::CompileSizedString< (2 * sizeof(ristretto255::scalar))>&);
|
|
template<> ro::CompileSizedString< (2 * sizeof(ristretto255::scalar)) > ro::bin2hex<ristretto255::scalar>(const ristretto255::scalar&);
|
|
template<> ro::CompileSizedString< (8 * sizeof(ristretto255::scalar) + 5) / 6> ro::to_base64_string <ristretto255::scalar>(const ristretto255::scalar&);
|