// Copyright (c) 2020-2022 Zano Project // Copyright (c) 2020-2022 sowle (val@zano.org, crypto.sowle@gmail.com) // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. // // Note: This file originates from tests/functional_tests/crypto_tests.cpp #pragma once #include #include #include "crypto.h" namespace crypto { extern "C" { #include "crypto/crypto-ops.h" } // extern "C" // // Helpers // template std::string pod_to_hex_reversed(const pod_t &h) { constexpr char hexmap[] = "0123456789abcdef"; const unsigned char* data = reinterpret_cast(&h); size_t len = sizeof h; std::string s(len * 2, ' '); for (size_t i = 0; i < len; ++i) { s[2 * i] = hexmap[data[len - 1 - i] >> 4]; s[2 * i + 1] = hexmap[data[len - 1 - i] & 0x0F]; } return s; } template std::string pod_to_hex(const pod_t &h) { constexpr char hexmap[] = "0123456789abcdef"; const unsigned char* data = reinterpret_cast(&h); size_t len = sizeof h; std::string s(len * 2, ' '); for (size_t i = 0; i < len; ++i) { s[2 * i] = hexmap[data[i] >> 4]; s[2 * i + 1] = hexmap[data[i] & 0x0F]; } return s; } template std::string pod_to_hex_comma_separated_bytes(const pod_t &h) { std::stringstream ss; ss << std::hex << std::setfill('0'); size_t len = sizeof h; const unsigned char* p = (const unsigned char*)&h; for (size_t i = 0; i < len; ++i) { ss << "0x" << std::setw(2) << static_cast(p[i]); if (i + 1 != len) ss << ", "; } return ss.str(); } template std::string pod_to_hex_comma_separated_uint64(const pod_t &h) { static_assert((sizeof h) % 8 == 0, "size of h should be a multiple of 64 bit"); size_t len = (sizeof h) / 8; std::stringstream ss; ss << std::hex << std::setfill('0'); const uint64_t* p = (const uint64_t*)&h; for (size_t i = 0; i < len; ++i) { ss << "0x" << std::setw(16) << static_cast(p[i]); if (i + 1 != len) ss << ", "; } return ss.str(); } template bool parse_tpod_from_hex_string(const std::string& hex_str, t_pod_type& t_pod) { static const int16_t char_map[256] = { // 0-9, a-f, A-F is only allowed -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x00 - 0x1F -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, // 0x20 - 0x3F -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x40 - 0x5F -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x60 - 0x7F -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x80 - 0x9F -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0xA0 - 0xBF -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0xC0 - 0xDF -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; // 0xE0 - 0xFF size_t pod_size = sizeof t_pod; uint8_t *p = reinterpret_cast(&t_pod); if (hex_str.size() != 2 * pod_size) return false; for (size_t i = 0; i < pod_size; ++i) { int16_t hi = char_map[static_cast(hex_str[2 * i])]; int16_t lo = char_map[static_cast(hex_str[2 * i + 1])]; if (hi < 0 || lo < 0) { // invalid characters in hex_str memset(p, 0, pod_size); return false; } p[i] = static_cast(hi * 16 + lo); // write byte to pod } return true; } template t_pod_type parse_tpod_from_hex_string(const std::string& hex_str) { t_pod_type t_pod = AUTO_VAL_INIT(t_pod); crypto::parse_tpod_from_hex_string(hex_str, t_pod); // using fully qualified name to avoid Argument-Dependent Lookup issues return t_pod; } // // scalar_t - holds a 256-bit scalar, normally in [0..L-1] // struct alignas(32) scalar_t { union { uint64_t m_u64[4]; unsigned char m_s[32]; }; scalar_t() {} // won't check scalar range validity (< L) scalar_t(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3) { m_u64[0] = a0; m_u64[1] = a1; m_u64[2] = a2; m_u64[3] = a3; } // won't check scalar range validity (< L) scalar_t(const unsigned char(&v)[32]) { memcpy(m_s, v, 32); } // won't check secret key validity (sk < L) scalar_t(const crypto::secret_key& sk) { from_secret_key(sk); } // copy data and reduce scalar_t(const crypto::hash& hash) { m_u64[0] = ((uint64_t*)&hash)[0]; m_u64[1] = ((uint64_t*)&hash)[1]; m_u64[2] = ((uint64_t*)&hash)[2]; m_u64[3] = ((uint64_t*)&hash)[3]; sc_reduce32(&m_s[0]); } scalar_t(uint64_t v) { zero(); m_u64[0] = v; // do not need to call reduce as 2^64 < L } // copy at most 256 bits (32 bytes) and reduce template explicit scalar_t(const boost::multiprecision::number& bigint) { zero(); unsigned int bytes_to_copy = bigint.backend().size() * bigint.backend().limb_bits / 8; if (bytes_to_copy > sizeof *this) bytes_to_copy = sizeof *this; memcpy(&m_s[0], bigint.backend().limbs(), bytes_to_copy); sc_reduce32(&m_s[0]); } unsigned char* data() { return &m_s[0]; } const unsigned char* data() const { return &m_s[0]; } crypto::secret_key &as_secret_key() { return *(crypto::secret_key*)&m_s[0]; } const crypto::secret_key& as_secret_key() const { return *(const crypto::secret_key*)&m_s[0]; } operator crypto::secret_key() const { crypto::secret_key result; memcpy(result.data, &m_s, sizeof result.data); return result; } void from_secret_key(const crypto::secret_key& sk) { uint64_t *p_sk64 = (uint64_t*)&sk; m_u64[0] = p_sk64[0]; m_u64[1] = p_sk64[1]; m_u64[2] = p_sk64[2]; m_u64[3] = p_sk64[3]; // assuming secret key is correct (< L), so we don't need to call reduce here } void zero() { m_u64[0] = 0; m_u64[1] = 0; m_u64[2] = 0; m_u64[3] = 0; } // genrate 0 <= x < L static scalar_t random() { scalar_t result; result.make_random(); return result; } // generate 0 <= x < L void make_random() { unsigned char tmp[64]; crypto::generate_random_bytes(64, tmp); sc_reduce(tmp); memcpy(&m_s, tmp, sizeof m_s); /* // for tests int x[8] = { rand() }; crypto::cn_fast_hash(&x, sizeof x, *(crypto::hash*)this); sc_reduce32(m_s); */ } bool is_zero() const { return sc_isnonzero(&m_s[0]) == 0; } bool is_reduced() const { return sc_check(&m_s[0]) == 0; } void reduce() { sc_reduce32(&m_s[0]); } scalar_t operator+(const scalar_t& v) const { scalar_t result; sc_add(&result.m_s[0], &m_s[0], &v.m_s[0]); return result; } scalar_t& operator+=(const scalar_t& v) { sc_add(&m_s[0], &m_s[0], &v.m_s[0]); return *this; } scalar_t operator-(const scalar_t& v) const { scalar_t result; sc_sub(&result.m_s[0], &m_s[0], &v.m_s[0]); return result; } scalar_t& operator-=(const scalar_t& v) { sc_sub(&m_s[0], &m_s[0], &v.m_s[0]); return *this; } scalar_t operator*(const scalar_t& v) const { scalar_t result; sc_mul(result.m_s, m_s, v.m_s); return result; } scalar_t& operator*=(const scalar_t& v) { sc_mul(m_s, m_s, v.m_s); return *this; } // returns this = a * b scalar_t& assign_mul(const scalar_t& a, const scalar_t& b) { sc_mul(m_s, a.m_s, b.m_s); return *this; } /* I think it has bad symantic (operator-like), consider rename/reimplement -- sowle */ // returns this * b + c scalar_t muladd(const scalar_t& b, const scalar_t& c) const { scalar_t result; sc_muladd(result.m_s, m_s, b.m_s, c.m_s); return result; } // returns this = a * b + c scalar_t& assign_muladd(const scalar_t& a, const scalar_t& b, const scalar_t& c) { sc_muladd(m_s, a.m_s, b.m_s, c.m_s); return *this; } scalar_t reciprocal() const { scalar_t result; sc_invert(result.m_s, m_s); return result; } scalar_t operator/(const scalar_t& v) const { return operator*(v.reciprocal()); } scalar_t& operator/=(const scalar_t& v) { scalar_t reciprocal; sc_invert(&reciprocal.m_s[0], &v.m_s[0]); sc_mul(&m_s[0], &m_s[0], &reciprocal.m_s[0]); return *this; } bool operator==(const scalar_t& rhs) const { return m_u64[0] == rhs.m_u64[0] && m_u64[1] == rhs.m_u64[1] && m_u64[2] == rhs.m_u64[2] && m_u64[3] == rhs.m_u64[3]; } bool operator!=(const scalar_t& rhs) const { return m_u64[0] != rhs.m_u64[0] || m_u64[1] != rhs.m_u64[1] || m_u64[2] != rhs.m_u64[2] || m_u64[3] != rhs.m_u64[3]; } bool operator<(const scalar_t& rhs) const { if (m_u64[3] < rhs.m_u64[3]) return true; if (m_u64[3] > rhs.m_u64[3]) return false; if (m_u64[2] < rhs.m_u64[2]) return true; if (m_u64[2] > rhs.m_u64[2]) return false; if (m_u64[1] < rhs.m_u64[1]) return true; if (m_u64[1] > rhs.m_u64[1]) return false; if (m_u64[0] < rhs.m_u64[0]) return true; if (m_u64[0] > rhs.m_u64[0]) return false; return false; } bool operator>(const scalar_t& rhs) const { if (m_u64[3] < rhs.m_u64[3]) return false; if (m_u64[3] > rhs.m_u64[3]) return true; if (m_u64[2] < rhs.m_u64[2]) return false; if (m_u64[2] > rhs.m_u64[2]) return true; if (m_u64[1] < rhs.m_u64[1]) return false; if (m_u64[1] > rhs.m_u64[1]) return true; if (m_u64[0] < rhs.m_u64[0]) return false; if (m_u64[0] > rhs.m_u64[0]) return true; return false; } friend std::ostream& operator<<(std::ostream& ss, const scalar_t &v) { return ss << pod_to_hex(v); } std::string to_string_as_hex_number() const { return pod_to_hex_reversed(*this); } std::string to_string_as_secret_key() const { return pod_to_hex(*this); } template MP_type as_boost_mp_type() const { MP_type result = 0; static_assert(sizeof result >= sizeof *this, "size missmatch"); // to avoid using types less than uint256_t unsigned int sz = sizeof *this / sizeof(boost::multiprecision::limb_type); result.backend().resize(sz, sz); memcpy(result.backend().limbs(), &m_s[0], sizeof *this); result.backend().normalize(); return result; } // Little-endian assumed; TODO: consider Big-endian support bool get_bit(uint8_t bit_index) const { return (m_u64[bit_index >> 6] & (1ull << (bit_index & 63))) != 0; } // Little-endian assumed; TODO: consider Big-endian support void set_bit(size_t bit_index) { m_u64[bit_index >> 6] |= (1ull << (bit_index & 63)); } // Little-endian assumed; TODO: consider Big-endian support void clear_bit(size_t bit_index) { m_u64[bit_index >> 6] &= ~(1ull << (bit_index & 63)); } static scalar_t power_of_2(uint8_t exponent) { scalar_t result = 0; result.set_bit(exponent); return result; } }; // struct scalar_t // // Global constants // extern const scalar_t c_scalar_1; extern const scalar_t c_scalar_L; extern const scalar_t c_scalar_Lm1; extern const scalar_t c_scalar_P; extern const scalar_t c_scalar_Pm1; extern const scalar_t c_scalar_256m1; extern const scalar_t c_scalar_1div8; // // // struct point_t { struct tag_zero {}; // A point(x, y) is represented in extended homogeneous coordinates (X, Y, Z, T) // with x = X / Z, y = Y / Z, x * y = T / Z. ge_p3 m_p3; point_t() { } explicit point_t(const crypto::public_key& pk) { if (!from_public_key(pk)) zero(); } point_t(const unsigned char(&v)[32]) { static_assert(sizeof(crypto::public_key) == sizeof v, "size missmatch"); if (!from_public_key(*(const crypto::public_key*)v)) zero(); } point_t(const uint64_t(&v)[4]) { static_assert(sizeof(crypto::public_key) == sizeof v, "size missmatch"); if (!from_public_key(*(const crypto::public_key*)v)) zero(); } point_t(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3) { crypto::public_key pk; ((uint64_t*)&pk)[0] = a0; ((uint64_t*)&pk)[1] = a1; ((uint64_t*)&pk)[2] = a2; ((uint64_t*)&pk)[3] = a3; if (!from_public_key(pk)) zero(); } explicit point_t(tag_zero&&) { zero(); } // as we're using additive notation, zero means identity group element (EC point (0, 1)) here and after void zero() { ge_p3_0(&m_p3); } bool is_zero() const { // (0, 1) ~ (0, z, z, 0) if (fe_isnonzero(m_p3.X) != 0) return false; fe y_minus_z; fe_sub(y_minus_z, m_p3.Y, m_p3.Z); return fe_isnonzero(y_minus_z) == 0; } bool is_in_main_subgroup() const { return (c_scalar_L * *this).is_zero(); } bool from_public_key(const crypto::public_key& pk) { return ge_frombytes_vartime(&m_p3, reinterpret_cast(&pk)) == 0; } bool from_key_image(const crypto::key_image& ki) { return ge_frombytes_vartime(&m_p3, reinterpret_cast(&ki)) == 0; } bool from_string(const std::string& str) { crypto::public_key pk; if (!parse_tpod_from_hex_string(str, pk)) return false; return from_public_key(pk); } crypto::public_key to_public_key() const { crypto::public_key result; ge_p3_tobytes((unsigned char*)&result, &m_p3); return result; } void to_public_key(crypto::public_key& result) const { ge_p3_tobytes((unsigned char*)&result, &m_p3); } crypto::key_image to_key_image() const { crypto::key_image result; ge_p3_tobytes((unsigned char*)&result, &m_p3); return result; } point_t operator+(const point_t& rhs) const { point_t result; ge_cached rhs_c; ge_p1p1 t; ge_p3_to_cached(&rhs_c, &rhs.m_p3); ge_add(&t, &m_p3, &rhs_c); ge_p1p1_to_p3(&result.m_p3, &t); return result; } point_t& operator+=(const point_t& rhs) { ge_cached rhs_c; ge_p1p1 t; ge_p3_to_cached(&rhs_c, &rhs.m_p3); ge_add(&t, &m_p3, &rhs_c); ge_p1p1_to_p3(&m_p3, &t); return *this; } point_t operator-(const point_t& rhs) const { point_t result; ge_cached rhs_c; ge_p1p1 t; ge_p3_to_cached(&rhs_c, &rhs.m_p3); ge_sub(&t, &m_p3, &rhs_c); ge_p1p1_to_p3(&result.m_p3, &t); return result; } point_t& operator-=(const point_t& rhs) { ge_cached rhs_c; ge_p1p1 t; ge_p3_to_cached(&rhs_c, &rhs.m_p3); ge_sub(&t, &m_p3, &rhs_c); ge_p1p1_to_p3(&m_p3, &t); return *this; } friend point_t operator*(const scalar_t& lhs, const point_t& rhs) { point_t result; ge_scalarmult_p3(&result.m_p3, lhs.m_s, &rhs.m_p3); return result; } point_t& operator*=(const scalar_t& rhs) { // TODO: ge_scalarmult_vartime_p3 ge_scalarmult_p3(&m_p3, rhs.m_s, &m_p3); return *this; } friend point_t operator/(const point_t& lhs, const scalar_t& rhs) { point_t result; scalar_t reciprocal; sc_invert(&reciprocal.m_s[0], &rhs.m_s[0]); ge_scalarmult_p3(&result.m_p3, &reciprocal.m_s[0], &lhs.m_p3); return result; } point_t& modify_mul8() { ge_mul8_p3(&m_p3, &m_p3); return *this; } // returns a * this + G point_t mul_plus_G(const scalar_t& a) const { static const unsigned char one[32] = { 1 }; static_assert(sizeof one == sizeof(crypto::ec_scalar), "size missmatch"); point_t result; ge_double_scalarmult_base_vartime_p3(&result.m_p3, &a.m_s[0], &m_p3, &one[0]); return result; } // returns a * this + b * G point_t mul_plus_G(const scalar_t& a, const scalar_t& b) const { point_t result; ge_double_scalarmult_base_vartime_p3(&result.m_p3, &a.m_s[0], &m_p3, &b.m_s[0]); return result; } // *this = a * A + b * G void assign_mul_plus_G(const scalar_t& a, const point_t& A, const scalar_t& b) { ge_double_scalarmult_base_vartime_p3(&m_p3, &a.m_s[0], &A.m_p3, &b.m_s[0]); } friend bool operator==(const point_t& lhs, const point_t& rhs) { // convert to xy form, then compare components (because (x, y, z, t) representation is not unique) fe lrecip, lx, ly; fe rrecip, rx, ry; fe_invert(lrecip, lhs.m_p3.Z); fe_invert(rrecip, rhs.m_p3.Z); fe_mul(lx, lhs.m_p3.X, lrecip); fe_mul(rx, rhs.m_p3.X, rrecip); if (memcmp(&lx, &rx, sizeof lx) != 0) return false; fe_mul(ly, lhs.m_p3.Y, lrecip); fe_mul(ry, rhs.m_p3.Y, rrecip); if (memcmp(&ly, &ry, sizeof ly) != 0) return false; return true; }; friend bool operator!=(const point_t& lhs, const point_t& rhs) { return !(lhs == rhs); }; friend std::ostream& operator<<(std::ostream& ss, const point_t &v) { crypto::public_key pk = v.to_public_key(); return ss << pod_to_hex(pk); } operator std::string() const { crypto::public_key pk = to_public_key(); return pod_to_hex(pk); } std::string to_string() const { crypto::public_key pk = to_public_key(); return pod_to_hex(pk); } std::string to_hex_comma_separated_bytes_str() const { crypto::public_key pk = to_public_key(); return pod_to_hex_comma_separated_bytes(pk); } std::string to_hex_comma_separated_uint64_str() const { crypto::public_key pk = to_public_key(); return pod_to_hex_comma_separated_uint64(pk); } }; // struct point_t // // point_g_t -- special type for curve's base point // struct point_g_t : public point_t { point_g_t() { scalar_t one(1); ge_scalarmult_base(&m_p3, &one.m_s[0]); } friend point_t operator*(const scalar_t& lhs, const point_g_t&) { point_t result; ge_scalarmult_base(&result.m_p3, &lhs.m_s[0]); return result; } friend point_t operator/(const point_g_t&, const scalar_t& rhs) { point_t result; scalar_t reciprocal; sc_invert(&reciprocal.m_s[0], &rhs.m_s[0]); ge_scalarmult_base(&result.m_p3, &reciprocal.m_s[0]); return result; } static_assert(sizeof(crypto::public_key) == 32, "size error"); }; // struct point_g_t // // vector of scalars // struct scalar_vec_t : public std::vector { typedef std::vector super_t; scalar_vec_t() {} scalar_vec_t(size_t n) : super_t(n) {} scalar_vec_t(std::initializer_list init_list) : super_t(init_list) {} bool is_reduced() const { for (auto& el : *this) if (!el.is_reduced()) return false; return true; } // add a scalar rhs to each element scalar_vec_t operator+(const scalar_t& rhs) const { scalar_vec_t result(size()); for (size_t i = 0, n = size(); i < n; ++i) result[i] = at(i) + rhs; return result; } // subtract a scalar rhs to each element scalar_vec_t operator-(const scalar_t& rhs) const { scalar_vec_t result(size()); for (size_t i = 0, n = size(); i < n; ++i) result[i] = at(i) - rhs; return result; } // multiply each element of the vector by a scalar scalar_vec_t operator*(const scalar_t& rhs) const { scalar_vec_t result(size()); for (size_t i = 0, n = size(); i < n; ++i) result[i] = at(i) * rhs; return result; } // component-wise multiplication (a.k.a the Hadamard product) (only if their sizes match) scalar_vec_t operator*(const scalar_vec_t& rhs) const { scalar_vec_t result; const size_t n = size(); if (n != rhs.size()) return result; result.resize(size()); for (size_t i = 0; i < n; ++i) result[i] = at(i) * rhs[i]; return result; } // add each element of two vectors, but only if their sizes match scalar_vec_t operator+(const scalar_vec_t& rhs) const { scalar_vec_t result; const size_t n = size(); if (n != rhs.size()) return result; result.resize(size()); for (size_t i = 0; i < n; ++i) result[i] = at(i) + rhs[i]; return result; } // zeroes all elements void zero() { size_t size_bytes = sizeof(scalar_t) * size(); memset(data(), 0, size_bytes); } // invert all elements in-place efficiently: 4*N muptiplications + 1 inversion void invert() { // muls muls_rev // 0: 1 2 3 .. n-1 // 1: 0 2 3 .. n-1 // 2: 0 1 3 .. n-1 // // n-1: 0 1 2 3 .. n-2 const size_t size = this->size(); if (size < 2) { if (size == 1) at(0) = at(0).reciprocal(); return; } scalar_vec_t muls(size), muls_rev(size); muls[0] = 1; for (size_t i = 0; i < size - 1; ++i) muls[i + 1] = at(i) * muls[i]; muls_rev[size - 1] = 1; for (size_t i = size - 1; i != 0; --i) muls_rev[i - 1] = at(i) * muls_rev[i]; scalar_t inv = (muls[size - 1] * at(size - 1)).reciprocal(); for (size_t i = 0; i < size; ++i) at(i) = muls[i] * inv * muls_rev[i]; } scalar_t calc_hs() const; }; // scalar_vec_t // treats vector of scalars as an M x N matrix just for convenience template struct scalar_mat_t : public scalar_vec_t { typedef scalar_vec_t super_t; static_assert(N > 0, "invalid N value"); scalar_mat_t() {} scalar_mat_t(size_t n) : super_t(n) {} scalar_mat_t(std::initializer_list init_list) : super_t(init_list) {} // matrix accessor M rows x N cols scalar_t& operator()(size_t row, size_t col) { return at(row * N + col); } }; // scalar_mat_t // // Global constants // extern const point_g_t c_point_G; extern const point_t c_point_H; extern const point_t c_point_H2; extern const point_t c_point_0; // // hash functions' helper // struct hash_helper_t { static scalar_t hs(const scalar_t& s) { return scalar_t(crypto::cn_fast_hash(s.data(), sizeof s)); // will reduce mod L } static scalar_t hs(const void* data, size_t size) { return scalar_t(crypto::cn_fast_hash(data, size)); // will reduce mod L } static scalar_t hs(const std::string& str) { return scalar_t(crypto::cn_fast_hash(str.c_str(), str.size())); // will reduce mod L } struct hs_t { hs_t() { static_assert(sizeof(scalar_t) == sizeof(crypto::public_key), "unexpected size of data"); } void reserve(size_t elements_count) { m_elements.reserve(elements_count); } void resize(size_t elements_count) { m_elements.resize(elements_count); } void clear() { m_elements.clear(); } void add_scalar(const scalar_t& scalar) { m_elements.emplace_back(scalar); } void add_point(const point_t& point) { m_elements.emplace_back(point.to_public_key()); // faster? /* static_assert(sizeof point.m_p3 == 5 * sizeof(item_t), "size missmatch"); const item_t *p = (item_t*)&point.m_p3; m_elements.emplace_back(p[0]); m_elements.emplace_back(p[1]); m_elements.emplace_back(p[2]); m_elements.emplace_back(p[3]); m_elements.emplace_back(p[4]); */ } void add_pub_key(const crypto::public_key& pk) { m_elements.emplace_back(pk); } scalar_t& access_scalar(size_t index) { return m_elements[index].scalar; } public_key& access_public_key(size_t index) { return m_elements[index].pk; } void add_points_array(const std::vector& points_array) { for (size_t i = 0, size = points_array.size(); i < size; ++i) add_point(points_array[i]); } void add_pub_keys_array(const std::vector& pub_keys_array) { for (size_t i = 0, size = pub_keys_array.size(); i < size; ++i) m_elements.emplace_back(pub_keys_array[i]); } void add_key_images_array(const std::vector& key_image_array) { for (size_t i = 0, size = key_image_array.size(); i < size; ++i) m_elements.emplace_back(key_image_array[i]); } scalar_t calc_hash(bool clear = true) { size_t data_size_bytes = m_elements.size() * sizeof(item_t); crypto::hash hash; crypto::cn_fast_hash(m_elements.data(), data_size_bytes, hash); if (clear) this->clear(); return scalar_t(hash); // this will reduce to L } void assign_calc_hash(scalar_t& result, bool clear = true) { static_assert(sizeof result == sizeof(crypto::hash), "size missmatch"); size_t data_size_bytes = m_elements.size() * sizeof(item_t); crypto::cn_fast_hash(m_elements.data(), data_size_bytes, (crypto::hash&)result); result.reduce(); if (clear) this->clear(); } union item_t { item_t() {} item_t(const scalar_t& scalar) : scalar(scalar) {} item_t(const crypto::public_key& pk) : pk(pk) {} item_t(const crypto::key_image& ki) : ki(ki) {} scalar_t scalar; crypto::public_key pk; crypto::key_image ki; }; std::vector m_elements; }; static scalar_t hs(const scalar_t& s, const std::vector& ps0, const std::vector& ps1) { hs_t hs_calculator; hs_calculator.add_scalar(s); hs_calculator.add_points_array(ps0); hs_calculator.add_points_array(ps1); return hs_calculator.calc_hash(); } static scalar_t hs(const crypto::hash& s, const std::vector& ps0, const std::vector& ps1) { static_assert(sizeof(crypto::hash) == sizeof(scalar_t), "size missmatch"); hs_t hs_calculator; hs_calculator.add_scalar(*reinterpret_cast(&s)); hs_calculator.add_pub_keys_array(ps0); hs_calculator.add_key_images_array(ps1); return hs_calculator.calc_hash(); } static scalar_t hs(const std::vector& ps0, const std::vector& ps1) { hs_t hs_calculator; hs_calculator.add_points_array(ps0); hs_calculator.add_points_array(ps1); return hs_calculator.calc_hash(); } static point_t hp(const point_t& p) { point_t result; crypto::public_key pk = p.to_public_key(); ge_bytes_hash_to_ec_32(&result.m_p3, (const unsigned char*)&pk); return result; } static point_t hp(const crypto::public_key& p) { point_t result; ge_bytes_hash_to_ec_32(&result.m_p3, (const unsigned char*)&p); return result; } static point_t hp(const scalar_t& s) { point_t result; ge_bytes_hash_to_ec_32(&result.m_p3, s.data()); return result; } static point_t hp(const void* data, size_t size) { point_t result; ge_bytes_hash_to_ec(&result.m_p3, data, size); return result; } }; // hash_helper_t struct inline scalar_t scalar_vec_t::calc_hs() const { // hs won't touch memory if size is 0, so it's safe return hash_helper_t::hs(data(), sizeof(scalar_t) * size()); } } // namespace crypto