From 53caed6d385a8e28aea17a7ea9577fe61f55e9e0 Mon Sep 17 00:00:00 2001 From: sowle Date: Fri, 22 Jul 2022 05:36:38 +0200 Subject: [PATCH] clsag first implementation + tests --- src/crypto/clsag.cpp | 152 +++++++++++- src/crypto/clsag.h | 18 +- src/currency_core/crypto_config.h | 4 + tests/functional_tests/crypto_tests.cpp | 41 +--- tests/functional_tests/crypto_tests_clsag.h | 230 ++++++++++++++++++ .../crypto_torsion_elements.h | 56 +++++ 6 files changed, 458 insertions(+), 43 deletions(-) create mode 100644 tests/functional_tests/crypto_tests_clsag.h create mode 100644 tests/functional_tests/crypto_torsion_elements.h diff --git a/src/crypto/clsag.cpp b/src/crypto/clsag.cpp index c64d401f..bfcb3a14 100644 --- a/src/crypto/clsag.cpp +++ b/src/crypto/clsag.cpp @@ -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& 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 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& 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 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 diff --git a/src/crypto/clsag.h b/src/crypto/clsag.h index b00dd419..66e30143 100644 --- a/src/crypto/clsag.h +++ b/src/crypto/clsag.h @@ -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& 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& ring, const public_key& pseudo_out_amount_commitment, const key_image& ki, + const CLSAG_GG_signature& sig); + } // namespace crypto diff --git a/src/currency_core/crypto_config.h b/src/currency_core/crypto_config.h index 37b9378d..aef8b575 100644 --- a/src/currency_core/crypto_config.h +++ b/src/currency_core/crypto_config.h @@ -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____" diff --git a/tests/functional_tests/crypto_tests.cpp b/tests/functional_tests/crypto_tests.cpp index 1f961df4..716dc770 100644 --- a/tests/functional_tests/crypto_tests.cpp +++ b/tests/functional_tests/crypto_tests.cpp @@ -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])); diff --git a/tests/functional_tests/crypto_tests_clsag.h b/tests/functional_tests/crypto_tests_clsag.h new file mode 100644 index 00000000..3e290393 --- /dev/null +++ b/tests/functional_tests/crypto_tests_clsag.h @@ -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 + +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 stealth_addresses; + std::vector amount_commitments; // div 8 + std::vector 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(noncanonical_torsion_elements[0]); + ASSERT_FALSE(cc.verify()); + cc.ki = parse_tpod_from_hex_string(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(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; +} diff --git a/tests/functional_tests/crypto_torsion_elements.h b/tests/functional_tests/crypto_torsion_elements.h new file mode 100644 index 00000000..51093327 --- /dev/null +++ b/tests/functional_tests/crypto_torsion_elements.h @@ -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