forked from lthn/blockchain
clsag first implementation + tests
This commit is contained in:
parent
e4293af219
commit
53caed6d38
6 changed files with 458 additions and 43 deletions
|
|
@ -6,14 +6,162 @@
|
|||
// This file contains implementation of CLSAG (s.a. https://eprint.iacr.org/2019/654.pdf by Goodel at el)
|
||||
//
|
||||
#include "clsag.h"
|
||||
//#include "misc_log_ex.h"
|
||||
#include "../currency_core/crypto_config.h"
|
||||
|
||||
namespace crypto
|
||||
{
|
||||
#define DBG_VAL_PRINT(x) (void(0)) // std::cout << #x ": " << x << std::endl
|
||||
#define DBG_PRINT(x) (void(0)) // std::cout << x << std::endl
|
||||
|
||||
bool generate_CLSAG_GG(const hash& m, const std::vector<CLSAG_GG_input_ref_t>& ring, const point_t& pseudo_out_amount_commitment, const key_image& ki,
|
||||
const scalar_t& secret_x, const scalar_t& secret_f, CLSAG_GG_signature& sig)
|
||||
const scalar_t& secret_x, const scalar_t& secret_f, uint64_t secret_index, CLSAG_GG_signature& sig)
|
||||
{
|
||||
return false;
|
||||
size_t ring_size = ring.size();
|
||||
CRYPTO_CHECK_AND_THROW_MES(ring_size > 0, "ring size is zero");
|
||||
CRYPTO_CHECK_AND_THROW_MES(secret_index < ring_size, "secret_index is out of range");
|
||||
|
||||
// calculate key images
|
||||
point_t ki_base = hash_helper_t::hp(ring[secret_index].stealth_address);
|
||||
point_t key_image = secret_x * ki_base;
|
||||
CRYPTO_CHECK_AND_THROW_MES(key_image == point_t(ki), "key image 0 mismatch");
|
||||
point_t K1_div8 = (c_scalar_1div8 * secret_f) * ki_base;
|
||||
K1_div8.to_public_key(sig.K1);
|
||||
point_t K1 = K1_div8;
|
||||
K1.modify_mul8();
|
||||
|
||||
// calculate aggregation coefficients
|
||||
hash_helper_t::hs_t hsc(3 + 2 * ring_size);
|
||||
hsc.add_scalar(m);
|
||||
for(size_t i = 0; i < ring_size; ++i)
|
||||
{
|
||||
hsc.add_pub_key(ring[i].stealth_address);
|
||||
hsc.add_pub_key(ring[i].amount_commitment);
|
||||
}
|
||||
hsc.add_point(pseudo_out_amount_commitment);
|
||||
hsc.add_key_image(ki);
|
||||
hash input_hash = hsc.calc_hash_no_reduce();
|
||||
|
||||
hsc.add_32_chars(CRYPTO_HDS_CLSAG_GG_LAYER_0);
|
||||
hsc.add_hash(input_hash);
|
||||
scalar_t agg_coeff_0 = hsc.calc_hash();
|
||||
DBG_VAL_PRINT(agg_coeff_0);
|
||||
|
||||
hsc.add_32_chars(CRYPTO_HDS_CLSAG_GG_LAYER_1);
|
||||
hsc.add_hash(input_hash);
|
||||
scalar_t agg_coeff_1 = hsc.calc_hash();
|
||||
DBG_VAL_PRINT(agg_coeff_1);
|
||||
|
||||
// calculate aggregate pub keys
|
||||
std::vector<point_t> W_pub_keys;
|
||||
W_pub_keys.reserve(ring_size);
|
||||
for(size_t i = 0; i < ring_size; ++i)
|
||||
{
|
||||
W_pub_keys.emplace_back(agg_coeff_0 * point_t(ring[i].stealth_address) + agg_coeff_1 * (point_t(ring[i].amount_commitment).modify_mul8() - pseudo_out_amount_commitment));
|
||||
DBG_VAL_PRINT(W_pub_keys[i]);
|
||||
}
|
||||
|
||||
// aggregate secret key
|
||||
scalar_t w_sec_key = agg_coeff_0 * secret_x + agg_coeff_1 * secret_f;
|
||||
|
||||
// calculate aggregate key image
|
||||
point_t W_key_image = agg_coeff_0 * key_image + agg_coeff_1 * K1;
|
||||
DBG_VAL_PRINT(W_key_image);
|
||||
|
||||
// initial commitment
|
||||
scalar_t alpha = scalar_t::random();
|
||||
hsc.add_32_chars(CRYPTO_HDS_CLSAG_GG_CHALLENGE);
|
||||
hsc.add_hash(input_hash);
|
||||
hsc.add_point(alpha * c_point_G);
|
||||
hsc.add_point(alpha * ki_base);
|
||||
scalar_t c_prev = hsc.calc_hash(); // c_{secret_index + 1}
|
||||
|
||||
sig.r.clear();
|
||||
sig.r.reserve(ring_size);
|
||||
for(size_t i = 0; i < ring_size; ++i)
|
||||
sig.r.emplace_back(scalar_t::random());
|
||||
|
||||
for(size_t j = 0, i = (secret_index + 1) % ring_size; j < ring_size - 1; ++j, i = (i + 1) % ring_size)
|
||||
{
|
||||
if (i == 0)
|
||||
sig.c = c_prev; // c_0
|
||||
hsc.add_32_chars(CRYPTO_HDS_CLSAG_GG_CHALLENGE);
|
||||
hsc.add_hash(input_hash);
|
||||
hsc.add_point(sig.r[i] * c_point_G + c_prev * W_pub_keys[i]);
|
||||
hsc.add_point(sig.r[i] * hash_helper_t::hp(ring[i].stealth_address) + c_prev * W_key_image);
|
||||
c_prev = hsc.calc_hash(); // c_{i + 1}
|
||||
}
|
||||
|
||||
if (secret_index == 0)
|
||||
sig.c = c_prev;
|
||||
|
||||
sig.r[secret_index] = alpha - c_prev * w_sec_key;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool verify_CLSAG_GG(const hash& m, const std::vector<CLSAG_GG_input_ref_t>& ring, const crypto::public_key& pseudo_out_amount_commitment, const key_image& ki,
|
||||
const CLSAG_GG_signature& sig)
|
||||
{
|
||||
size_t ring_size = ring.size();
|
||||
CRYPTO_CHECK_AND_THROW_MES(ring_size > 0, "ring size is zero");
|
||||
CRYPTO_CHECK_AND_THROW_MES(ring_size == sig.r.size(), "ring size != r size");
|
||||
|
||||
point_t key_image(ki);
|
||||
CRYPTO_CHECK_AND_THROW_MES(key_image.is_in_main_subgroup(), "key image 0 does not belong to the main subgroup");
|
||||
|
||||
point_t pseudo_out_amount_commitment_pt(pseudo_out_amount_commitment);
|
||||
pseudo_out_amount_commitment_pt.modify_mul8();
|
||||
|
||||
// calculate aggregation coefficients
|
||||
hash_helper_t::hs_t hsc(3 + 2 * ring_size);
|
||||
hsc.add_scalar(m);
|
||||
for(size_t i = 0; i < ring_size; ++i)
|
||||
{
|
||||
hsc.add_pub_key(ring[i].stealth_address);
|
||||
hsc.add_pub_key(ring[i].amount_commitment);
|
||||
}
|
||||
hsc.add_point(pseudo_out_amount_commitment_pt);
|
||||
hsc.add_key_image(ki);
|
||||
hash input_hash = hsc.calc_hash_no_reduce();
|
||||
|
||||
hsc.add_32_chars(CRYPTO_HDS_CLSAG_GG_LAYER_0);
|
||||
hsc.add_hash(input_hash);
|
||||
scalar_t agg_coeff_0 = hsc.calc_hash();
|
||||
DBG_VAL_PRINT(agg_coeff_0);
|
||||
|
||||
hsc.add_32_chars(CRYPTO_HDS_CLSAG_GG_LAYER_1);
|
||||
hsc.add_hash(input_hash);
|
||||
scalar_t agg_coeff_1 = hsc.calc_hash();
|
||||
DBG_VAL_PRINT(agg_coeff_1);
|
||||
|
||||
|
||||
// calculate aggregate pub keys
|
||||
std::vector<point_t> W_pub_keys;
|
||||
W_pub_keys.reserve(ring_size);
|
||||
for(size_t i = 0; i < ring_size; ++i)
|
||||
{
|
||||
W_pub_keys.emplace_back(agg_coeff_0 * point_t(ring[i].stealth_address) + agg_coeff_1 * (point_t(ring[i].amount_commitment).modify_mul8() - pseudo_out_amount_commitment_pt));
|
||||
DBG_VAL_PRINT(W_pub_keys[i]);
|
||||
}
|
||||
|
||||
// calculate aggregate key image
|
||||
point_t W_key_image = agg_coeff_0 * point_t(ki) + agg_coeff_1 * point_t(sig.K1).modify_mul8();
|
||||
DBG_VAL_PRINT(W_key_image);
|
||||
|
||||
scalar_t c_prev = sig.c;
|
||||
for(size_t i = 0; i < ring_size; ++i)
|
||||
{
|
||||
hsc.add_32_chars(CRYPTO_HDS_CLSAG_GG_CHALLENGE);
|
||||
hsc.add_hash(input_hash);
|
||||
hsc.add_point(sig.r[i] * c_point_G + c_prev * W_pub_keys[i]);
|
||||
hsc.add_point(sig.r[i] * hash_helper_t::hp(ring[i].stealth_address) + c_prev * W_key_image);
|
||||
c_prev = hsc.calc_hash(); // c_{i + 1}
|
||||
}
|
||||
|
||||
return c_prev == sig.c;
|
||||
}
|
||||
|
||||
} // namespace crypto
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
namespace crypto
|
||||
{
|
||||
// GG stands for double layers (ring dimentions) both with respect to group element G
|
||||
// 2-CLSAG signature where both dimensions are with respect to the group element G (that's why 'GG')
|
||||
struct CLSAG_GG_signature
|
||||
{
|
||||
scalar_t c;
|
||||
|
|
@ -19,6 +19,16 @@ namespace crypto
|
|||
};
|
||||
|
||||
|
||||
inline bool operator==(const CLSAG_GG_signature& lhs, const CLSAG_GG_signature& rhs)
|
||||
{
|
||||
return
|
||||
lhs.c == rhs.c &&
|
||||
lhs.r == rhs.r &&
|
||||
lhs.K1 == rhs.K1;
|
||||
}
|
||||
|
||||
inline bool operator!=(const CLSAG_GG_signature& lhs, const CLSAG_GG_signature& rhs) { return !(lhs == rhs); }
|
||||
|
||||
struct CLSAG_GG_input_ref_t
|
||||
{
|
||||
CLSAG_GG_input_ref_t(const public_key& stealth_address, const public_key& amount_commitment)
|
||||
|
|
@ -29,6 +39,10 @@ namespace crypto
|
|||
};
|
||||
|
||||
bool generate_CLSAG_GG(const hash& m, const std::vector<CLSAG_GG_input_ref_t>& ring, const point_t& pseudo_out_amount_commitment, const key_image& ki,
|
||||
const scalar_t& secret_x, const scalar_t& secret_f, CLSAG_GG_signature& sig);
|
||||
const scalar_t& secret_x, const scalar_t& secret_f, uint64_t secret_index, CLSAG_GG_signature& sig);
|
||||
|
||||
bool verify_CLSAG_GG(const hash& m, const std::vector<CLSAG_GG_input_ref_t>& ring, const public_key& pseudo_out_amount_commitment, const key_image& ki,
|
||||
const CLSAG_GG_signature& sig);
|
||||
|
||||
|
||||
} // namespace crypto
|
||||
|
|
|
|||
|
|
@ -9,3 +9,7 @@
|
|||
#define CRYPTO_HDS_OUT_AMOUNT_MASK "ZANO_HDS_OUT_AMOUNT_MASK_______"
|
||||
#define CRYPTO_HDS_OUT_BLINDING_MASK "ZANO_HDS_OUT_BLINDING_MASK_____"
|
||||
#define CRYPTO_HDS_OUT_CONCEALING_POINT "ZANO_HDS_OUT_CONCEALING_POINT__"
|
||||
|
||||
#define CRYPTO_HDS_CLSAG_GG_LAYER_0 "ZANO_HDS_CLSAG_GG_LAYER_ZERO___"
|
||||
#define CRYPTO_HDS_CLSAG_GG_LAYER_1 "ZANO_HDS_CLSAG_GG_LAYER_ONE____"
|
||||
#define CRYPTO_HDS_CLSAG_GG_CHALLENGE "ZANO_HDS_CLSAG_GG_CHALLENGE____"
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
#include "crypto/crypto-sugar.h"
|
||||
#include "crypto/range_proofs.h"
|
||||
#include "../core_tests/random_helper.h"
|
||||
#include "crypto_torsion_elements.h"
|
||||
|
||||
using namespace crypto;
|
||||
|
||||
|
|
@ -490,6 +491,7 @@ struct test_keeper_t
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
#include "crypto_tests_ml2s.h"
|
||||
#include "crypto_tests_range_proofs.h"
|
||||
#include "crypto_tests_clsag.h"
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
|
@ -1272,35 +1274,6 @@ TEST(crypto, calc_lsb_32)
|
|||
|
||||
TEST(crypto, torsion_elements)
|
||||
{
|
||||
// let ty = -sqrt((-sqrt(D+1)-1) / D), is_neg(ty) == false
|
||||
// canonical serialization sig order EC point
|
||||
// 26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05 0 8 (sqrt(-1)*ty, ty)
|
||||
// 0000000000000000000000000000000000000000000000000000000000000000 0 4 (sqrt(-1), 0)
|
||||
// c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a 0 8 (sqrt(-1)*ty, -ty)
|
||||
// ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f 0 2 (0, -1)
|
||||
// c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa 1 8 (-sqrt(-1)*ty, -ty)
|
||||
// 0000000000000000000000000000000000000000000000000000000000000080 1 4 (-sqrt(-1), 0)
|
||||
// 26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85 1 8 (-sqrt(-1)*ty, ty)
|
||||
|
||||
struct canonical_torsion_elements_t
|
||||
{
|
||||
const char* string;
|
||||
bool sign;
|
||||
uint8_t order;
|
||||
uint8_t incorrect_order_0;
|
||||
uint8_t incorrect_order_1;
|
||||
};
|
||||
|
||||
canonical_torsion_elements_t canonical_torsion_elements[] = {
|
||||
{"26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05", false, 8, 4, 7},
|
||||
{"0000000000000000000000000000000000000000000000000000000000000000", false, 4, 2, 3},
|
||||
{"c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a", false, 8, 4, 7},
|
||||
{"ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", false, 2, 1, 3},
|
||||
{"c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa", true, 8, 4, 7},
|
||||
{"0000000000000000000000000000000000000000000000000000000000000080", true, 4, 2, 3},
|
||||
{"26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85", true, 8, 4, 7}
|
||||
};
|
||||
|
||||
point_t tor;
|
||||
|
||||
for (size_t i = 0, n = sizeof canonical_torsion_elements / sizeof canonical_torsion_elements[0]; i < n; ++i)
|
||||
|
|
@ -1318,16 +1291,6 @@ TEST(crypto, torsion_elements)
|
|||
}
|
||||
|
||||
// non-canonical elements should not load at all (thanks to the checks in ge_frombytes_vartime)
|
||||
|
||||
const char* noncanonical_torsion_elements[] = {
|
||||
"0100000000000000000000000000000000000000000000000000000000000080", // (-0, 1)
|
||||
"ECFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", // (-0, -1)
|
||||
"EEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F", // (0, 2*255-18)
|
||||
"EEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", // (-0, 2*255-18)
|
||||
"EDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F", // (sqrt(-1), 2*255-19)
|
||||
"EDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" // (-sqrt(-1), 2*255-19)
|
||||
};
|
||||
|
||||
for (size_t i = 0, n = sizeof noncanonical_torsion_elements / sizeof noncanonical_torsion_elements[0]; i < n; ++i)
|
||||
{
|
||||
ASSERT_FALSE(tor.from_string(noncanonical_torsion_elements[i]));
|
||||
|
|
|
|||
230
tests/functional_tests/crypto_tests_clsag.h
Normal file
230
tests/functional_tests/crypto_tests_clsag.h
Normal file
|
|
@ -0,0 +1,230 @@
|
|||
// Copyright (c) 2022 Zano Project (https://zano.org/)
|
||||
// Copyright (c) 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.
|
||||
#pragma once
|
||||
#include <crypto/clsag.h>
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& ss, const CLSAG_GG_signature &sig)
|
||||
{
|
||||
ss << "CLSAG_GG: c: " << sig.c << ENDL;
|
||||
ss << " r: ";
|
||||
size_t i = 0;
|
||||
for(auto el: sig.r)
|
||||
{
|
||||
if (i++ != 0)
|
||||
ss << " ";
|
||||
ss << el << ENDL;
|
||||
}
|
||||
ss << " K1: " << sig.K1 << ENDL;
|
||||
return ss;
|
||||
}
|
||||
|
||||
struct clsag_gg_sig_check_t
|
||||
{
|
||||
crypto::hash prefix_hash;
|
||||
crypto::key_image ki;
|
||||
std::vector<public_key> stealth_addresses;
|
||||
std::vector<public_key> amount_commitments; // div 8
|
||||
std::vector<CLSAG_GG_input_ref_t> ring;
|
||||
crypto::public_key pseudo_output_commitment; // div 8
|
||||
scalar_t secret_x;
|
||||
scalar_t secret_f;
|
||||
size_t secret_index;
|
||||
CLSAG_GG_signature sig;
|
||||
|
||||
clsag_gg_sig_check_t()
|
||||
{}
|
||||
|
||||
clsag_gg_sig_check_t& operator=(const clsag_gg_sig_check_t& rhs)
|
||||
{
|
||||
prefix_hash = rhs.prefix_hash;
|
||||
ki = rhs.ki;
|
||||
stealth_addresses = rhs.stealth_addresses;
|
||||
amount_commitments = rhs.amount_commitments;
|
||||
|
||||
ring.clear();
|
||||
for(size_t i = 0; i < stealth_addresses.size(); ++i)
|
||||
ring.emplace_back(stealth_addresses[i], amount_commitments[i]);
|
||||
|
||||
pseudo_output_commitment = rhs.pseudo_output_commitment;
|
||||
secret_x = rhs.secret_x;
|
||||
secret_f = rhs.secret_f;
|
||||
secret_index = rhs.secret_index;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void prepare_random_data(size_t ring_size)
|
||||
{
|
||||
stealth_addresses.clear();
|
||||
amount_commitments.clear();
|
||||
ring.clear();
|
||||
|
||||
crypto::generate_random_bytes(sizeof prefix_hash, &prefix_hash);
|
||||
|
||||
stealth_addresses.reserve(ring_size);
|
||||
amount_commitments.reserve(ring_size);
|
||||
for(size_t i = 0; i < ring_size; ++i)
|
||||
{
|
||||
stealth_addresses.push_back(hash_helper_t::hp(scalar_t::random()).to_public_key());
|
||||
amount_commitments.push_back(hash_helper_t::hp(scalar_t::random()).to_public_key()); // div 8
|
||||
ring.emplace_back(stealth_addresses.back(), amount_commitments.back());
|
||||
}
|
||||
|
||||
secret_x = scalar_t::random();
|
||||
secret_f = scalar_t::random();
|
||||
secret_index = random_in_range(0, ring_size - 1);
|
||||
|
||||
stealth_addresses[secret_index] = (secret_x * c_point_G).to_public_key();
|
||||
ki = (secret_x * hash_helper_t::hp(stealth_addresses[secret_index])).to_key_image();
|
||||
|
||||
pseudo_output_commitment = (point_t(amount_commitments[secret_index]) - c_scalar_1div8 * secret_f * c_point_G).to_public_key();
|
||||
}
|
||||
|
||||
bool generate()
|
||||
{
|
||||
try
|
||||
{
|
||||
return generate_CLSAG_GG(prefix_hash, ring, point_t(pseudo_output_commitment).modify_mul8(), ki, secret_x, secret_f, secret_index, sig);
|
||||
}
|
||||
catch(std::exception& e)
|
||||
{
|
||||
LOG_PRINT_RED(ENDL << "EXCEPTION: " << e.what(), LOG_LEVEL_0);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool verify()
|
||||
{
|
||||
try
|
||||
{
|
||||
return verify_CLSAG_GG(prefix_hash, ring, pseudo_output_commitment, ki, sig);
|
||||
}
|
||||
catch(std::exception& e)
|
||||
{
|
||||
LOG_PRINT_RED(ENDL << "EXCEPTION: " << e.what(), LOG_LEVEL_0);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TEST(clsag, basics)
|
||||
{
|
||||
clsag_gg_sig_check_t cc;
|
||||
cc.prepare_random_data(1);
|
||||
ASSERT_TRUE(cc.generate());
|
||||
ASSERT_TRUE(cc.verify());
|
||||
|
||||
cc.prepare_random_data(2);
|
||||
ASSERT_TRUE(cc.generate());
|
||||
ASSERT_TRUE(cc.verify());
|
||||
|
||||
cc.prepare_random_data(8);
|
||||
ASSERT_TRUE(cc.generate());
|
||||
ASSERT_TRUE(cc.verify());
|
||||
|
||||
cc.prepare_random_data(123);
|
||||
ASSERT_TRUE(cc.generate());
|
||||
ASSERT_TRUE(cc.verify());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TEST(clsag, bad_pub_keys)
|
||||
{
|
||||
clsag_gg_sig_check_t cc, cc_orig;
|
||||
cc.prepare_random_data(10);
|
||||
|
||||
cc_orig = cc;
|
||||
ASSERT_TRUE(cc.generate());
|
||||
cc.ki = parse_tpod_from_hex_string<key_image>(noncanonical_torsion_elements[0]);
|
||||
ASSERT_FALSE(cc.verify());
|
||||
cc.ki = parse_tpod_from_hex_string<key_image>(canonical_torsion_elements[0].string); // ki not in main subgroup
|
||||
ASSERT_FALSE(cc.verify());
|
||||
|
||||
// check all torsion elements (paranoid mode on)
|
||||
for(size_t t = 0; t < sizeof canonical_torsion_elements / sizeof canonical_torsion_elements[0]; ++t)
|
||||
{
|
||||
point_t tor = point_t(parse_tpod_from_hex_string<public_key>(canonical_torsion_elements[t].string));
|
||||
ASSERT_FALSE(tor.is_in_main_subgroup());
|
||||
ASSERT_FALSE(tor.is_zero());
|
||||
|
||||
cc = cc_orig;
|
||||
ASSERT_TRUE(cc.generate());
|
||||
cc.ki = (point_t(cc.ki) + tor).to_key_image(); // ki not in main subgroup
|
||||
ASSERT_FALSE(cc.verify());
|
||||
|
||||
// torsion component in pseudo_output_commitment should not affect protocol
|
||||
cc = cc_orig;
|
||||
cc.pseudo_output_commitment = (point_t(cc.pseudo_output_commitment) + tor).to_public_key();
|
||||
ASSERT_TRUE(cc.generate());
|
||||
ASSERT_TRUE(cc.verify());
|
||||
|
||||
// torsion component in amount_commitments[i] should not affect protocol
|
||||
cc = cc_orig;
|
||||
for(size_t i = 0; i < cc.ring.size(); ++i)
|
||||
cc.amount_commitments[i] = (point_t(cc.amount_commitments[i]) + tor).to_public_key();
|
||||
ASSERT_TRUE(cc.generate());
|
||||
ASSERT_TRUE(cc.verify());
|
||||
|
||||
// torsion component in K1 should not affect protocol
|
||||
cc = cc_orig;
|
||||
ASSERT_TRUE(cc.generate());
|
||||
cc.sig.K1 = (point_t(cc.sig.K1) + tor).to_public_key();
|
||||
ASSERT_TRUE(cc.verify());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TEST(clsag, bad_sig)
|
||||
{
|
||||
clsag_gg_sig_check_t cc;
|
||||
|
||||
// wrong prefix hash
|
||||
cc.prepare_random_data(10);
|
||||
ASSERT_TRUE(cc.generate());
|
||||
ASSERT_TRUE(cc.verify());
|
||||
cc.prefix_hash.data[5] ^= 0x7f;
|
||||
ASSERT_FALSE(cc.verify());
|
||||
|
||||
// ring size > sig.r size
|
||||
ASSERT_TRUE(cc.generate());
|
||||
cc.sig.r.clear();
|
||||
ASSERT_FALSE(cc.verify());
|
||||
|
||||
// ring size < sig.r size
|
||||
ASSERT_TRUE(cc.generate());
|
||||
cc.sig.r.push_back(scalar_t::random());
|
||||
ASSERT_FALSE(cc.verify());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TEST(clsag, sig_difference)
|
||||
{
|
||||
int cmp_res = 0;
|
||||
|
||||
clsag_gg_sig_check_t cc0, cc1;
|
||||
cc0.prepare_random_data(8);
|
||||
cc1 = cc0; // get the same input data
|
||||
|
||||
{
|
||||
// make sure two signatures generated with the same input and randoms are equal
|
||||
random_state_test_restorer rstr; // to restore random state on exit of the scope
|
||||
random_state_test_restorer::reset_random(0);
|
||||
ASSERT_TRUE(cc0.generate());
|
||||
random_state_test_restorer::reset_random(0);
|
||||
ASSERT_TRUE(cc1.generate());
|
||||
LOG_PRINT_L0("cc0: " << ENDL << cc0.sig << ", cc1: " << ENDL << cc1.sig);
|
||||
ASSERT_TRUE(cc0.sig == cc1.sig);
|
||||
ASSERT_TRUE(cc0.verify());
|
||||
}
|
||||
|
||||
// make sure two signatures generated with the same input are not equal
|
||||
ASSERT_TRUE(cc0.generate());
|
||||
ASSERT_TRUE(cc1.generate());
|
||||
ASSERT_TRUE(cc0.sig != cc1.sig);
|
||||
|
||||
return true;
|
||||
}
|
||||
56
tests/functional_tests/crypto_torsion_elements.h
Normal file
56
tests/functional_tests/crypto_torsion_elements.h
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
// Copyright (c) 2021-2022 Zano Project
|
||||
// Copyright (c) 2021-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.
|
||||
//
|
||||
#pragma once
|
||||
|
||||
//
|
||||
// Tosion elements of ed25519 group in canonical and non-canonical form
|
||||
//
|
||||
// (partly inspired by "Taming the many EdDSAs" by Chalkias et al https://eprint.iacr.org/2020/1244.pdf)
|
||||
//
|
||||
|
||||
namespace crypto
|
||||
{
|
||||
|
||||
// let ty = -sqrt((-sqrt(D+1)-1) / D), is_neg(ty) == false
|
||||
// canonical serialization sig order EC point
|
||||
// 26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05 0 8 (sqrt(-1)*ty, ty)
|
||||
// 0000000000000000000000000000000000000000000000000000000000000000 0 4 (sqrt(-1), 0)
|
||||
// c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a 0 8 (sqrt(-1)*ty, -ty)
|
||||
// ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f 0 2 (0, -1)
|
||||
// c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa 1 8 (-sqrt(-1)*ty, -ty)
|
||||
// 0000000000000000000000000000000000000000000000000000000000000080 1 4 (-sqrt(-1), 0)
|
||||
// 26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85 1 8 (-sqrt(-1)*ty, ty)
|
||||
|
||||
struct canonical_torsion_elements_t
|
||||
{
|
||||
const char* string;
|
||||
bool sign;
|
||||
uint8_t order;
|
||||
uint8_t incorrect_order_0;
|
||||
uint8_t incorrect_order_1;
|
||||
};
|
||||
|
||||
canonical_torsion_elements_t canonical_torsion_elements[] = {
|
||||
{"26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05", false, 8, 4, 7},
|
||||
{"0000000000000000000000000000000000000000000000000000000000000000", false, 4, 2, 3},
|
||||
{"c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a", false, 8, 4, 7},
|
||||
{"ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", false, 2, 1, 3},
|
||||
{"c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa", true, 8, 4, 7},
|
||||
{"0000000000000000000000000000000000000000000000000000000000000080", true, 4, 2, 3},
|
||||
{"26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85", true, 8, 4, 7}
|
||||
};
|
||||
|
||||
// non-canonical elements (should not load at all thanks to the checks in ge_frombytes_vartime)
|
||||
const char* noncanonical_torsion_elements[] = {
|
||||
"0100000000000000000000000000000000000000000000000000000000000080", // (-0, 1)
|
||||
"ECFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", // (-0, -1)
|
||||
"EEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F", // (0, 2*255-18)
|
||||
"EEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", // (-0, 2*255-18)
|
||||
"EDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F", // (sqrt(-1), 2*255-19)
|
||||
"EDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" // (-sqrt(-1), 2*255-19)
|
||||
};
|
||||
|
||||
} // namespace crypto
|
||||
Loading…
Add table
Reference in a new issue