crypto: torsion clearing for FCMP++

This commit is contained in:
j-berman
2026-07-07 14:32:13 -07:00
parent 52f02f26fc
commit 79a996be02
12 changed files with 314 additions and 4 deletions
+92
View File
@@ -39,8 +39,11 @@ extern "C"
}
#include "crypto/generators.h"
#include "cryptonote_basic/merge_mining.h"
#include "fcmp_pp/fcmp_pp_crypto.h"
#include "ringct/rctOps.h"
#include "ringct/rctSigs.h"
#include "ringct/rctTypes.h"
#include "string_tools.h"
namespace
{
@@ -429,3 +432,92 @@ TEST(Crypto, fe_equals)
ASSERT_NE(memcmp(fe_d2_reduced, fe_d2, sizeof(fe)), 0);
ASSERT_EQ(fe_equals(fe_d2_reduced, fe_d2), 1);
}
TEST(Crypto, ec_constants_rct_parity)
{
ASSERT_TRUE(memcmp(&fcmp_pp::EC_I, &rct::I, 32) == 0);
ASSERT_TRUE(memcmp(&fcmp_pp::EC_INV_EIGHT, &rct::INV_EIGHT, 32) == 0);
}
#define CHECK_CLEARED(k, cleared) \
crypto::ec_point cleared2; \
const bool r = fcmp_pp::get_valid_torsion_cleared_point_vartime(rct::rct2pt(k), cleared2); \
ASSERT_TRUE(r); \
ASSERT_EQ(cleared, cleared2);
TEST(Crypto, torsion_check_pass_random)
{
std::vector<rct::key> pts;
for (int i = 0; i < 1000; ++i)
{
const rct::key pk = rct::pkGen();
ge_p3 x;
ASSERT_EQ(ge_frombytes_vartime(&x, pk.bytes), 0);
ASSERT_TRUE(rct::isInMainSubgroup(pk));
ASSERT_FALSE(fcmp_pp::mul8_is_identity_vartime(x));
const crypto::ec_point cleared = fcmp_pp::clear_torsion_vartime(x);
ASSERT_EQ(rct::rct2pt(pk), cleared);
CHECK_CLEARED(pk, cleared);
pts.emplace_back(pk);
}
ASSERT_TRUE(rct::verPointsForTorsion(pts));
}
TEST(Crypto, torsion_check_hardcoded)
{
struct TorsionTestPoints { std::string point; bool torsion_free; };
static const std::vector<TorsionTestPoints> torsion_test_points = {
{"b10ba13e303cbe9abf7d5d44f1d417727abcc14903a74e071abd652ce1bf76dd", false},
{"9b2e4c0281c0b02e7c53291a94d1d0cbff8883f8024f5142ee494ffbbd088071", false}, // genesis (see config::GENESIS_TX)
{"785eda585dca4f3d27976106008ccfbca13146c8b21b8c7e4909032639a776e1", true},
{"9a7b10563aa266032cd075f4e347f348a3841ae4f41572633351a97dd44066b4", true},
};
std::vector<rct::key> all_pts, torsion_free_pts, torsioned_pts, torsion_cleared_pts;
for (const auto &point : torsion_test_points)
{
rct::key k;
epee::string_tools::hex_to_pod(point.point, k);
ge_p3 x;
ASSERT_EQ(ge_frombytes_vartime(&x, k.bytes), 0);
ASSERT_EQ(rct::isInMainSubgroup(k), point.torsion_free);
ASSERT_FALSE(fcmp_pp::mul8_is_identity_vartime(x));
const crypto::ec_point cleared = fcmp_pp::clear_torsion_vartime(x);
if (point.torsion_free)
{
ASSERT_EQ(rct::rct2pt(k), cleared);
torsion_free_pts.push_back(k);
}
else
{
ASSERT_NE(rct::rct2pt(k), cleared);
torsioned_pts.push_back(k);
}
CHECK_CLEARED(k, cleared);
all_pts.emplace_back(k);
torsion_cleared_pts.emplace_back(rct::pt2rct(cleared));
}
ASSERT_TRUE(rct::verPointsForTorsion(torsion_free_pts));
ASSERT_FALSE(rct::verPointsForTorsion(torsioned_pts));
ASSERT_FALSE(rct::verPointsForTorsion(all_pts));
ASSERT_TRUE(rct::verPointsForTorsion(torsion_cleared_pts));
}
TEST(Crypto, mul8_is_identity_vartime)
{
static const std::vector<rct::key> mul8_identity_points = {
rct::I,
rct::Z
};
for (const auto &point : mul8_identity_points)
{
ge_p3 x;
ASSERT_EQ(ge_frombytes_vartime(&x, point.bytes), 0);
ASSERT_TRUE(fcmp_pp::mul8_is_identity_vartime(x));
crypto::ec_point _;
ASSERT_FALSE(fcmp_pp::get_valid_torsion_cleared_point_vartime(rct::rct2pt(point), _));
}
}