diff --git a/src/crypto/zarcanum.h b/src/crypto/zarcanum.h index 246ff3a1..53a6ebfc 100644 --- a/src/crypto/zarcanum.h +++ b/src/crypto/zarcanum.h @@ -64,21 +64,107 @@ namespace crypto scalar_t y1; }; - enum generator_tag { generator_tag_void = 0, generator_tag_G = 1, generator_tag_H = 2, generator_tag_H2 = 3, generator_tag_X = 4, generator_tag_U = 5 }; + enum generator_tag { gt_void = 0, gt_G = 1, gt_H = 2, gt_H2 = 3, gt_X = 4, gt_U = 5 }; - template + template bool generate_linear_composition_proof(const hash& m, const public_key& A, const scalar_t& secret_a, const scalar_t& secret_b, linear_composition_proof& result, uint8_t* p_err = nullptr) { // consider embedding generators' tags into random entropy to distinguish proofs made with different generators during verification return false; } - template + template bool verify_linear_composition_proof(const hash& m, const public_key& A, const linear_composition_proof& sig, uint8_t* p_err = nullptr) { return false; } + + struct generic_schnorr_sig + { + scalar_t c; + scalar_t y; + }; + + template + inline bool generate_schnorr_sig(const hash& m, const point_t& A, const scalar_t& secret_a, generic_schnorr_sig& result); + + template<> + inline bool generate_schnorr_sig(const hash& m, const point_t& A, const scalar_t& secret_a, generic_schnorr_sig& result) + { +#ifndef NDEBUG + if (A != secret_a * c_point_G) + return false; +#endif + scalar_t r = scalar_t::random(); + point_t R = r * c_point_G; + hash_helper_t::hs_t hsc(3); + hsc.add_hash(m); + hsc.add_point(A); + hsc.add_point(R); + result.c = hsc.calc_hash(); + result.y.assign_mulsub(result.c, secret_a, r); // y = r - c * secret_a + return true; + } + + template<> + inline bool generate_schnorr_sig(const hash& m, const point_t& A, const scalar_t& secret_a, generic_schnorr_sig& result) + { +#ifndef NDEBUG + if (A != secret_a * c_point_X) + return false; +#endif + scalar_t r = scalar_t::random(); + point_t R = r * c_point_X; + hash_helper_t::hs_t hsc(3); + hsc.add_hash(m); + hsc.add_point(A); + hsc.add_point(R); + result.c = hsc.calc_hash(); + result.y.assign_mulsub(result.c, secret_a, r); // y = r - c * secret_a + return true; + } + + template + inline bool verify_schnorr_sig(const hash& m, const public_key& A, const generic_schnorr_sig& sig) noexcept; + + template<> + inline bool verify_schnorr_sig(const hash& m, const public_key& A, const generic_schnorr_sig& sig) noexcept + { + try + { + if (!sig.c.is_reduced() || !sig.y.is_reduced()) + return false; + hash_helper_t::hs_t hsc(3); + hsc.add_hash(m); + hsc.add_pub_key(A); + hsc.add_point(point_t(A).mul_plus_G(sig.c, sig.y)); // sig.y * G + sig.c * A + return sig.c == hsc.calc_hash(); + } + catch(...) + { + return false; + } + } + + template<> + inline bool verify_schnorr_sig(const hash& m, const public_key& A, const generic_schnorr_sig& sig) noexcept + { + try + { + if (!sig.c.is_reduced() || !sig.y.is_reduced()) + return false; + hash_helper_t::hs_t hsc(3); + hsc.add_hash(m); + hsc.add_pub_key(A); + hsc.add_point(sig.y * c_point_X + sig.c * point_t(A)); + return sig.c == hsc.calc_hash(); + } + catch(...) + { + return false; + } + } // TODO: improve this proof using random weightning factor diff --git a/tests/functional_tests/crypto_tests.cpp b/tests/functional_tests/crypto_tests.cpp index d7b800f0..a2f615ad 100644 --- a/tests/functional_tests/crypto_tests.cpp +++ b/tests/functional_tests/crypto_tests.cpp @@ -1460,6 +1460,56 @@ TEST(crypto, sc_set_bit_clear_bit) } +TEST(crypto, schnorr_sig) +{ + public_key invalid_pk = parse_tpod_from_hex_string("0000000000000000000000000000000000000000000000000000000000000001"); + ASSERT_FALSE(check_key(invalid_pk)); + + hash m = *(crypto::hash*)(&scalar_t::random()); + for(size_t i = 0; i < 1000; ++i) + { + generic_schnorr_sig ss{}; + scalar_t a = scalar_t::random(); + point_t A_pt = a * c_point_G; + public_key A = A_pt.to_public_key(); + ASSERT_FALSE(generate_schnorr_sig(m, A_pt, a, ss)); + ASSERT_TRUE(generate_schnorr_sig(m, A_pt, a, ss)); + ASSERT_FALSE(verify_schnorr_sig(m, A, ss)); + ASSERT_TRUE(verify_schnorr_sig(m, A, ss)); + + A_pt = a * c_point_X; + A = A_pt.to_public_key(); + ASSERT_FALSE(generate_schnorr_sig(m, A_pt, a, ss)); + ASSERT_TRUE(generate_schnorr_sig(m, A_pt, a, ss)); + ASSERT_FALSE(verify_schnorr_sig(m, A, ss)); + ASSERT_TRUE(verify_schnorr_sig(m, A, ss)); + + ASSERT_FALSE(verify_schnorr_sig(currency::null_hash, A, ss)); + ASSERT_FALSE(verify_schnorr_sig(m, invalid_pk, ss)); + + generic_schnorr_sig bad_ss = ss; + bad_ss.c = c_scalar_Pm1; + ASSERT_FALSE(bad_ss.c.is_reduced()); + ASSERT_FALSE(verify_schnorr_sig(m, A, bad_ss)); + bad_ss = ss; + bad_ss.y = c_scalar_Pm1; + ASSERT_FALSE(bad_ss.y.is_reduced()); + ASSERT_FALSE(verify_schnorr_sig(m, A, bad_ss)); + + bad_ss = ss; + + mp::uint256_t c_mp = bad_ss.c.as_boost_mp_type(); + c_mp += c_scalar_L.as_boost_mp_type(); + memcpy(bad_ss.c.data(), c_mp.backend().limbs(), sizeof(scalar_t)); + ASSERT_FALSE(bad_ss.c.is_reduced()); + scalar_t tmp(bad_ss.c); + tmp.reduce(); + ASSERT_EQ(tmp, ss.c); + ASSERT_FALSE(verify_schnorr_sig(m, A, bad_ss)); + + } +} + // // test's runner