#include "stdafx.h" void randombytes_buf(std::span in) { randombytes_buf(&in[0], in.size_bytes()); } void randombytes_buf(std::span< char> in) { randombytes_buf(&in[0], in.size_bytes()); } namespace ristretto255 { bool scalar::constant_time_required{ true }; bool point::constant_time_required{ true }; point point::operator*(const scalar &sclr) const& noexcept { point me; auto i{ crypto_scalarmult_ristretto255(&me.blob[0], &sclr.blob[0], &blob[0]) }; assert(i == 0); return me; } point point::operator*(int i) const& noexcept { return scalar(i) * (*this); } point point::ptZero({ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}); point point::ptBase({ 0xe2, 0xf2, 0xae, 0x0a, 0x6a, 0xbc, 0x4e, 0x71, 0xa8, 0x84, 0xa9, 0x61, 0xc5, 0x00, 0x51, 0x5f, 0x58, 0xe3, 0x0b, 0x6a, 0xa5, 0x82, 0xdd, 0x8d, 0xb6, 0xa6, 0x59, 0x45, 0xe0, 0x8d, 0x2d, 0x76}); static constexpr scalar curveOrder({ 0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10 }); const scalar& scalar::scOrder{ curveOrder }; std::strong_ordering scalar::operator<=>(const scalar& sclr) const& noexcept { static_assert(std::endian::native == std::endian::little); // Instead of a static assert, we really should have an if constexpr() // that defines reverse iterators for little endian order // and defines forward iterators for big endian order. // but we will fix that when the time comes. // as it depends on how the libsodium library will implement // scalars on big endian order. std::strong_ordering retval{ std::strong_ordering::equal }; constexpr auto bg = sizeof(scalar::blob); typedef const std::arrayas_uint64_t; auto lh{ reinterpret_cast(this->blob)}; auto rh{ reinterpret_cast(sclr.blob) }; static_assert(sizeof(as_uint64_t) == sizeof(sclr.blob)); auto p{ lh.end() }; auto q{ rh.end() }; do { auto vp{ *--p }; auto vq{ *--q }; retval = vp <=> vq; } while (std::is_eq(retval) && bool(p > lh.begin())); return retval; } signed_text::signed_text( const scalar &k, // Signer's secret key const point &Kin, // Signer's public key std::span txtin // Text to be signed. ) : txt(txtin), K(Kin) { assert(k.valid()); assert(K.valid()); /* • Compute r = H(k|M), a per message private key. Or r can be an unpredictable secret scalar. • Compute R = r*B, a per message public key
• Compute c = H(R|M).
• Compute s = r+c*k. (Note that sB = r*B+c*k*B = R+c*K.)*/ scalar r(hash<512>(k, txtin)); point R(r.timesBase()); c = scalar (hash<512>(R, txtin)); s = r + c * k; } bool signed_text::verify() { /* • Check that c, s are valid scalars • Check that K, the signing public key, is a valid member of the prime order group • Compute R = sB − cK • Check that c = H(R | M) */ if (!c.valid() || !s.valid() || !K.valid())return false; else { point R = s.timesBase() - c * K; return c==scalar(hash<512>(R, txt)); } } } //end ristretto255 namespace using ristretto255::scalar, ro::CompileSizedString, ro::bin2hex, ro::to_base64_string; auto reverse_byte_order(std::array < uint8_t, sizeof(scalar)>const& ac) { std::array ar; auto p = ar.rbegin(); for (auto x : ac) { *p++ = x; } return ar; } template<> CompileSizedString < 2 * sizeof(scalar)> bin2hex(const scalar& sc) { CompileSizedString <2 * sizeof(scalar)>sz; auto bigendian = reverse_byte_order(sc.blob); sodium_bin2hex(&sz[0], sizeof(sc.blob) * 2 + 1, &bigendian[0], bigendian.size()); return sz; } template<> scalar ro::hex2bin(const CompileSizedString< (2 * sizeof(scalar))>& sz) { scalar sc; sc.blob = reverse_byte_order(ro::hex2bin(sz)); return sc; } template<> CompileSizedString < (sizeof(scalar) * 8 + 5) / 6> to_base64_string(const scalar& sc) { CompileSizedString < (sizeof(sc.blob) * 8 + 4) / 6> sz; auto bigendian = reverse_byte_order(sc.blob); bits2base64( &(bigendian[0]), 4, sizeof(bigendian)*8-4, std::span(sz) ); return sz; }