From c42917624849daeac0b4bc2fb1cd1f2539470b28 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 29 Jun 2018 15:03:00 +0100 Subject: [PATCH] bulletproofs: reject points not in the main subgroup --- src/ringct/bulletproofs.cc | 13 ++++++ src/ringct/rctOps.cpp | 17 ++++++++ src/ringct/rctOps.h | 3 ++ tests/performance_tests/crypto_ops.h | 2 + tests/performance_tests/main.cpp | 1 + tests/unit_tests/bulletproofs.cpp | 61 ++++++++++++++++++++++++++++ 6 files changed, 97 insertions(+) diff --git a/src/ringct/bulletproofs.cc b/src/ringct/bulletproofs.cc index bc7e15f35..7ec87378b 100644 --- a/src/ringct/bulletproofs.cc +++ b/src/ringct/bulletproofs.cc @@ -922,6 +922,19 @@ bool bulletproof_VERIFY(const std::vector &proofs) for (const Bulletproof *p: proofs) { const Bulletproof &proof = *p; + + // check subgroup + for (const rct::key &k: proof.V) + CHECK_AND_ASSERT_MES(rct::isInMainSubgroup(k), false, "Input point not in subgroup"); + for (const rct::key &k: proof.L) + CHECK_AND_ASSERT_MES(rct::isInMainSubgroup(k), false, "Input point not in subgroup"); + for (const rct::key &k: proof.R) + CHECK_AND_ASSERT_MES(rct::isInMainSubgroup(k), false, "Input point not in subgroup"); + CHECK_AND_ASSERT_MES(rct::isInMainSubgroup(proof.A), false, "Input point not in subgroup"); + CHECK_AND_ASSERT_MES(rct::isInMainSubgroup(proof.S), false, "Input point not in subgroup"); + CHECK_AND_ASSERT_MES(rct::isInMainSubgroup(proof.T1), false, "Input point not in subgroup"); + CHECK_AND_ASSERT_MES(rct::isInMainSubgroup(proof.T2), false, "Input point not in subgroup"); + CHECK_AND_ASSERT_MES(proof.V.size() >= 1, false, "V does not have at least one element"); CHECK_AND_ASSERT_MES(proof.L.size() == proof.R.size(), false, "Mismatched L and R sizes"); CHECK_AND_ASSERT_MES(proof.L.size() > 0, false, "Empty proof"); diff --git a/src/ringct/rctOps.cpp b/src/ringct/rctOps.cpp index fe0ad8747..df027f4b6 100644 --- a/src/ringct/rctOps.cpp +++ b/src/ringct/rctOps.cpp @@ -60,6 +60,17 @@ namespace rct { //Various key generation functions + bool toPointCheckOrder(ge_p3 *P, const unsigned char *data) + { + if (ge_frombytes_vartime(P, data)) + return false; + ge_p2 R; + ge_scalarmult(&R, curveOrder().bytes, P); + key tmp; + ge_tobytes(tmp.bytes, &R); + return tmp == identity(); + } + //generates a random scalar which can be used as a secret key or mask void skGen(key &sk) { random32_unbiased(sk.bytes); @@ -200,6 +211,12 @@ namespace rct { return aP; } + //Computes aL where L is the curve order + bool isInMainSubgroup(const key & a) { + ge_p3 p3; + return toPointCheckOrder(&p3, a.bytes); + } + //Curve addition / subtractions //for curve points: AB = A + B diff --git a/src/ringct/rctOps.h b/src/ringct/rctOps.h index f8889af5c..f0320f333 100644 --- a/src/ringct/rctOps.h +++ b/src/ringct/rctOps.h @@ -83,6 +83,7 @@ namespace rct { keyM keyMInit(size_t rows, size_t cols); //Various key generation functions + bool toPointCheckOrder(ge_p3 *P, const unsigned char *data); //generates a random scalar which can be used as a secret key or mask key skGen(); @@ -119,6 +120,8 @@ namespace rct { key scalarmultKey(const key &P, const key &a); //Computes aH where H= toPoint(cn_fast_hash(G)), G the basepoint key scalarmultH(const key & a); + // checks a is in the main subgroup (ie, not a small one) + bool isInMainSubgroup(const key & a); //Curve addition / subtractions diff --git a/tests/performance_tests/crypto_ops.h b/tests/performance_tests/crypto_ops.h index 852a6670d..f52faa708 100644 --- a/tests/performance_tests/crypto_ops.h +++ b/tests/performance_tests/crypto_ops.h @@ -51,6 +51,7 @@ enum test_op op_addKeys2, op_addKeys3, op_addKeys3_2, + op_isInMainSubgroup, }; template @@ -102,6 +103,7 @@ public: case op_addKeys2: rct::addKeys2(key, scalar0, scalar1, point0); break; case op_addKeys3: rct::addKeys3(key, scalar0, point0, scalar1, precomp1); break; case op_addKeys3_2: rct::addKeys3(key, scalar0, precomp0, scalar1, precomp1); break; + case op_isInMainSubgroup: rct::isInMainSubgroup(point0); break; default: return false; } return true; diff --git a/tests/performance_tests/main.cpp b/tests/performance_tests/main.cpp index 142e544dd..19c2cb5e5 100644 --- a/tests/performance_tests/main.cpp +++ b/tests/performance_tests/main.cpp @@ -237,6 +237,7 @@ int main(int argc, char** argv) TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_addKeys2); TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_addKeys3); TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_addKeys3_2); + TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_isInMainSubgroup); TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_bos_coster, 2); TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_bos_coster, 4); diff --git a/tests/unit_tests/bulletproofs.cpp b/tests/unit_tests/bulletproofs.cpp index f163f7a20..2358aa27a 100644 --- a/tests/unit_tests/bulletproofs.cpp +++ b/tests/unit_tests/bulletproofs.cpp @@ -30,6 +30,7 @@ #include "gtest/gtest.h" +#include "string_tools.h" #include "ringct/rctOps.h" #include "ringct/rctSigs.h" #include "ringct/bulletproofs.h" @@ -193,3 +194,63 @@ TEST(bulletproofs, invalid_gamma_ff) rct::Bulletproof proof = bulletproof_PROVE(invalid_amount, gamma); ASSERT_FALSE(rct::bulletproof_VERIFY(proof)); } + +static const char * const torsion_elements[] = +{ + "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa", + "0000000000000000000000000000000000000000000000000000000000000000", + "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85", + "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05", + "0000000000000000000000000000000000000000000000000000000000000080", + "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a", +}; + +TEST(bulletproofs, invalid_torsion) +{ + rct::Bulletproof proof = bulletproof_PROVE(7329838943733, rct::skGen()); + ASSERT_TRUE(rct::bulletproof_VERIFY(proof)); + for (const auto &xs: torsion_elements) + { + rct::key x; + ASSERT_TRUE(epee::string_tools::hex_to_pod(xs, x)); + ASSERT_FALSE(rct::isInMainSubgroup(x)); + for (auto &k: proof.V) + { + const rct::key org_k = k; + rct::addKeys(k, org_k, x); + ASSERT_FALSE(rct::bulletproof_VERIFY(proof)); + k = org_k; + } + for (auto &k: proof.L) + { + const rct::key org_k = k; + rct::addKeys(k, org_k, x); + ASSERT_FALSE(rct::bulletproof_VERIFY(proof)); + k = org_k; + } + for (auto &k: proof.R) + { + const rct::key org_k = k; + rct::addKeys(k, org_k, x); + ASSERT_FALSE(rct::bulletproof_VERIFY(proof)); + k = org_k; + } + const rct::key org_A = proof.A; + rct::addKeys(proof.A, org_A, x); + ASSERT_FALSE(rct::bulletproof_VERIFY(proof)); + proof.A = org_A; + const rct::key org_S = proof.S; + rct::addKeys(proof.S, org_S, x); + ASSERT_FALSE(rct::bulletproof_VERIFY(proof)); + proof.S = org_S; + const rct::key org_T1 = proof.T1; + rct::addKeys(proof.T1, org_T1, x); + ASSERT_FALSE(rct::bulletproof_VERIFY(proof)); + proof.T1 = org_T1; + const rct::key org_T2 = proof.T2; + rct::addKeys(proof.T2, org_T2, x); + ASSERT_FALSE(rct::bulletproof_VERIFY(proof)); + proof.T2 = org_T2; + } +}