Skip to content

Commit

Permalink
set up impl of kayaba's faster torsion check impl
Browse files Browse the repository at this point in the history
j-berman committed Dec 19, 2024
1 parent 3d0aff7 commit bddc3c4
Showing 9 changed files with 365 additions and 5 deletions.
10 changes: 10 additions & 0 deletions src/crypto/crypto-ops-data.c
Original file line number Diff line number Diff line change
@@ -38,6 +38,12 @@ const fe fe_d = {-10913610, 13857413, -15372611, 6949391, 114729, -8787816, -627
const fe fe_sqrtm1 = {-32595792, -7943725, 9377950, 3500415, 12389472, -272473, -25146209, -2005654, 326686, 11406482}; /* sqrt(-1) */
const fe fe_d2 = {-21827239, -5839606, -30745221, 13898782, 229458, 15978800, -12551817, -6495438, 29715968, 9444199}; /* 2 * d */

/* a = -1 */
// TODO: double check these consts
const fe fe_a_sub_d = {10913609, -13857413, 15372611, -6949391, -114729, 8787816, 6275908, 3247719, 18696448, 12055116}; /* a - d */
const fe fe_a0 = {-21827222, 27714826, -30745222, 13898782, 229458, -17575632, -12551816, -6495438, -37392896, -24110232}; /* A0 = 2 * (a + d) */
const fe fe_ap = {43654444, -55429652, 61490444, -27797564, -458916, 35151264, 25103632, 12990876, 74785792, 48220464}; /* Ap = -2 * A0 */

/* base[i][j] = (j+1)*256^i*B */
const ge_precomp ge_base[32][8] = {
{
@@ -871,6 +877,10 @@ const fe fe_fffb2 = {8166131, -6741800, -17040804, 3154616, 21461005, 1466302, -
const fe fe_fffb3 = {-13620103, 14639558, 4532995, 7679154, 16815101, -15883539, -22863840, -14813421, 13716513, -6477756}; /* sqrt(-sqrt(-1) * A * (A + 2)) */
const fe fe_fffb4 = {-21786234, -12173074, 21573800, 4524538, -4645904, 16204591, 8012863, -8444712, 3212926, 6885324}; /* sqrt(sqrt(-1) * A * (A + 2)) */
const fe fe_a_inv_3 = {-22207407, 11184811, 22369621, -11184811, -22369621, 11184811, 22369621, -11184811, -22369621, 11184811}; /* A / 3*/
const fe fe_one = {1, 0, 0, 0, 0, 0, 0, 0, 0, 0};
const fe fe_m1 = {-1, 0, 0, 0, 0, 0, 0, 0, 0, 0};
const fe fe_inv2 = {10, 0, 0, 0, 0, 0, 0, 0, 0, -16777216}; /* 1 / 2 */
const fe fe_mod_5_8 = {-3, 0, 0, 0, 0, 0, 0, 0, 0, 4194304}; /* -5 / 8 */
const ge_p3 ge_p3_identity = { {0}, {1, 0}, {1, 0}, {0} };
const ge_p3 ge_p3_H = {
{7329926, -15101362, 31411471, 7614783, 27996851, -3197071, -11157635, -6878293, 466949, -7986503},
9 changes: 4 additions & 5 deletions src/crypto/crypto-ops.c
Original file line number Diff line number Diff line change
@@ -40,7 +40,6 @@ DISABLE_VS_WARNINGS(4146 4244)

/* Predeclarations */

static void fe_sq(fe, const fe);
static void ge_madd(ge_p1p1 *, const ge_p3 *, const ge_precomp *);
static void ge_msub(ge_p1p1 *, const ge_p3 *, const ge_precomp *);
static void ge_p2_0(ge_p2 *);
@@ -232,7 +231,7 @@ static void fe_cmov(fe f, const fe g, unsigned int b) {
h = f
*/

static void fe_copy(fe h, const fe f) {
void fe_copy(fe h, const fe f) {
int32_t f0 = f[0];
int32_t f1 = f[1];
int32_t f2 = f[2];
@@ -358,7 +357,7 @@ return 0 if f is in {0,2,4,...,q-1}
|f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
*/

static int fe_isnegative(const fe f) {
int fe_isnegative(const fe f) {
unsigned char s[32];
fe_tobytes(s, f);
return s[0] & 1;
@@ -639,7 +638,7 @@ h = -f
|h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
*/

static void fe_neg(fe h, const fe f) {
void fe_neg(fe h, const fe f) {
int32_t f0 = f[0];
int32_t f1 = f[1];
int32_t f2 = f[2];
@@ -689,7 +688,7 @@ Can overlap h with f.
See fe_mul.c for discussion of implementation strategy.
*/

static void fe_sq(fe h, const fe f) {
void fe_sq(fe h, const fe f) {
int32_t f0 = f[0];
int32_t f1 = f[1];
int32_t f2 = f[2];
11 changes: 11 additions & 0 deletions src/crypto/crypto-ops.h
Original file line number Diff line number Diff line change
@@ -138,13 +138,20 @@ void ge_triple_scalarmult_precomp_vartime(ge_p2 *, const unsigned char *, const
void ge_double_scalarmult_precomp_vartime2(ge_p2 *, const unsigned char *, const ge_dsmp, const unsigned char *, const ge_dsmp);
void ge_double_scalarmult_precomp_vartime2_p3(ge_p3 *, const unsigned char *, const ge_dsmp, const unsigned char *, const ge_dsmp);
void ge_mul8(ge_p1p1 *, const ge_p2 *);
extern const fe fe_a_sub_d;
extern const fe fe_a0;
extern const fe fe_ap;
extern const fe fe_ma2;
extern const fe fe_ma;
extern const fe fe_fffb1;
extern const fe fe_fffb2;
extern const fe fe_fffb3;
extern const fe fe_fffb4;
extern const fe fe_a_inv_3;
extern const fe fe_one;
extern const fe fe_m1;
extern const fe fe_inv2;
extern const fe fe_mod_5_8;
extern const ge_p3 ge_p3_identity;
extern const ge_p3 ge_p3_H;
void ge_fromfe_frombytes_vartime(ge_p2 *, const unsigned char *);
@@ -163,10 +170,14 @@ uint64_t load_3(const unsigned char *in);
uint64_t load_4(const unsigned char *in);
void ge_sub(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q);
void fe_add(fe h, const fe f, const fe g);
void fe_neg(fe h, const fe f);
void fe_tobytes(unsigned char *, const fe);
void fe_copy(fe h, const fe f);
int fe_isnegative(const fe f);
void fe_invert(fe out, const fe z);
int fe_batch_invert(fe *out, const fe *in, const int n);
void fe_mul(fe out, const fe, const fe);
void fe_sq(fe h, const fe f);
void fe_sub(fe h, const fe f, const fe g);
void fe_0(fe h);
void fe_1(fe h);
177 changes: 177 additions & 0 deletions src/fcmp_pp/fcmp_pp_crypto.cpp
Original file line number Diff line number Diff line change
@@ -30,9 +30,186 @@

#include "ringct/rctOps.h"

static void inv_iso(fe u_out, fe w_out, const fe u, const fe w)
{
// 4u
fe_add(u_out, u, u);
fe_add(u_out, u_out, u_out);
// 2w
fe_add(w_out, w, w);
};

static bool sqrt_ext(fe y, const fe x)
{
fe x2;
fe_add(x2, x, x);

fe b;
// FIXME: fe_pow(b, x2, fe_mod_5_8);

fe b_sq;
fe_sq(b_sq, b);

fe c;
fe_mul(c, x2, b_sq);

if (memcmp(c, fe_one, sizeof(fe)) == 0 || memcmp(c, fe_m1, sizeof(fe)) == 0)
{
static const fe fe_3 = {3, 0, 0, 0, 0, 0, 0, 0, 0, 0};
fe_copy(c, fe_3);
}

fe c_sub_1;
fe_sub(c_sub_1, c, fe_one);

fe_mul(y, x, b);
fe_mul(y, y, c_sub_1);

// TODO: double check this
if (fe_isnegative(y))
fe_neg(y, y);

fe y_sq;
fe_sq(y_sq, y);

// return true;
return memcmp(x, y_sq, sizeof(fe)) == 0;
};

static void inv_psi1(fe e_out, fe u_out, fe w_out, const fe e, const fe u, const fe w, const fe neg_sqrt_2b)
{
fe tt;
bool cc = sqrt_ext(tt, u);
memcpy(w_out, tt, sizeof(fe));
fe w_;
memcpy(w_, w, sizeof(fe));
memcpy(e_out, e, sizeof(fe));

if (!cc)
{
fe tt_sq;
fe_sq(tt_sq, tt);
fe neg_u_dbl;
fe_add(neg_u_dbl, u, u);
fe_neg(neg_u_dbl, neg_u_dbl);
if (memcmp(tt_sq, neg_u_dbl, sizeof(fe)) == 0)
fe_mul(tt, tt, fe_sqrtm1);

fe_mul(w_, w_, tt);

fe e_sq;
fe_sq(e_sq, e);

fe_mul(w_out, neg_sqrt_2b, e_sq);
fe_mul(e_out, e, tt);
}

fe w_out_sq;
fe_sq(w_out_sq, w_out);

fe e_out_sq;
fe_sq(e_out_sq, e_out);

fe A_e_sq;
fe_mul(A_e_sq, fe_a0, e_out_sq);

fe w_out_w;
fe_mul(w_out_w, w_out, w_);

fe_sub(u_out, w_out_sq, A_e_sq);
fe_sub(u_out, u_out, w_out_w);
fe_mul(u_out, u_out, fe_inv2);
};

static bool inv_psi2(fe u_out, fe w_out, const fe e, const fe u, const fe w)
{
if (!sqrt_ext(w_out, u))
return false;
fe e_sq;
fe_sq(e_sq, e);
fe Ap_e_sq;
fe_mul(Ap_e_sq, fe_ap, e_sq);

fe w_out_w;
fe_mul(w_out_w, w_out, w);

fe_sub(u_out, u, Ap_e_sq);
fe_sub(u_out, u_out, w_out_w);
fe_mul(u_out, u_out, fe_inv2);

return true;
};

namespace fcmp_pp
{
//----------------------------------------------------------------------------------------------------------------------
// https://github.com/kayabaNerve/fcmp-plus-plus/blob/94744c5324e869a9483bbbd93a864e108304bf76/crypto/divisors/src/tests/torsion_check.rs
bool torsion_check(const rct::key &k) {
// TODO: static consts
// These are constants of the elliptic curve
static fe B;
fe_sq(B, fe_a_sub_d);
static fe Asq;
fe_sq(Asq, fe_a0);
fe B_mul4;
fe_add(B_mul4, B, B);
fe_add(B_mul4, B_mul4, B_mul4);
static fe Bp;
fe_sub(Bp, Asq, B_mul4);
static fe neg_sqrt_2b;
fe_add(neg_sqrt_2b, Bp, Bp);
// FIXME: get square root of neg_sqrt_2b here
fe_neg(neg_sqrt_2b, neg_sqrt_2b);

// De-compress the point
ge_p3 point;
if (ge_frombytes_vartime(&point, k.bytes) != 0)
return false;

// Make sure point not equal to identity
ge_p2 point_ge_p2;
ge_p3_to_p2(&point_ge_p2, &point);
ge_p1p1 point_mul8;
ge_mul8(&point_mul8, &point_ge_p2);

ge_p2 point_mul8_p2;
ge_p1p1_to_p2(&point_mul8_p2, &point_mul8);
rct::key tmp;
ge_tobytes(tmp.bytes, &point_mul8_p2);
if (tmp == rct::I)
return false;

// ed to wei
fe z_plus_ed_y;
fe_add(z_plus_ed_y, fe_one, point.Y);
fe z_minus_ed_y;
fe_sub(z_minus_ed_y, fe_one, point.Y);
fe e;
fe_mul(e, z_minus_ed_y, point.X);
fe u;
fe_mul(u, fe_a0, z_plus_ed_y);
fe_mul(u, u, point.X);
fe_mul(u, u, e);
fe w;
fe_add(w, z_minus_ed_y, z_minus_ed_y);

inv_iso(u, w, u, w);
if (!inv_psi2(u, w, e, u, w))
return false;
inv_psi1(e, u, w, e, u , w, neg_sqrt_2b);

inv_iso(u, w, u, w);
if (!inv_psi2(u, w, e, u, w))
return false;
inv_psi1(e, u, w, e, u , w, neg_sqrt_2b);

inv_iso(u, w, u, w);

// TODO: check if u has a square root via legendre symbol

return true;
}
//----------------------------------------------------------------------------------------------------------------------
bool clear_torsion(const rct::key &k, rct::key &k_out) {
ge_p3 point;
if (ge_frombytes_vartime(&point, k.bytes) != 0)
1 change: 1 addition & 0 deletions src/fcmp_pp/fcmp_pp_crypto.h
Original file line number Diff line number Diff line change
@@ -45,6 +45,7 @@ struct EdYDerivatives final
};
//----------------------------------------------------------------------------------------------------------------------
// TODO: tests for these functions
bool torsion_check(const rct::key &k);
bool clear_torsion(const rct::key &k, rct::key &k_out);
bool point_to_ed_y_derivatives(const rct::key &pub, EdYDerivatives &ed_y_derivatives);
void ed_y_derivatives_to_wei_x(const EdYDerivatives &ed_y_derivatives, rct::key &wei_x);
3 changes: 3 additions & 0 deletions tests/performance_tests/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -57,6 +57,8 @@ set(performance_tests_headers
performance_tests.h
performance_utils.h
single_tx_test_base.h
torsion_check.h
torsion_clear.h
zero_commit.h)

monero_add_minimal_executable(performance_tests
@@ -69,6 +71,7 @@ target_link_libraries(performance_tests
common
cncrypto
epee
fcmp_pp
ringct
seraphis_crypto
${Boost_CHRONO_LIBRARY}
4 changes: 4 additions & 0 deletions tests/performance_tests/main.cpp
Original file line number Diff line number Diff line change
@@ -66,6 +66,8 @@
#include "multiexp.h"
#include "sig_mlsag.h"
#include "sig_clsag.h"
#include "torsion_check.h"
#include "torsion_clear.h"
#include "zero_commit.h"

namespace po = boost::program_options;
@@ -210,6 +212,8 @@ int main(int argc, char** argv)
TEST_PERFORMANCE0(filter, p, test_derive_secret_key);
TEST_PERFORMANCE1(filter, p, test_fe_batch_invert, true); // batched
TEST_PERFORMANCE1(filter, p, test_fe_batch_invert, false); // individual inversions
TEST_PERFORMANCE0(filter, p, test_torsion_check);
TEST_PERFORMANCE0(filter, p, test_torsion_clear);
TEST_PERFORMANCE0(filter, p, test_ge_frombytes_vartime);
TEST_PERFORMANCE0(filter, p, test_ge_tobytes);
TEST_PERFORMANCE0(filter, p, test_generate_keypair);
76 changes: 76 additions & 0 deletions tests/performance_tests/torsion_check.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// Copyright (c) 2014-2024, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers

#pragma once

#include "crypto/crypto.h"
#include "cryptonote_basic/cryptonote_basic.h"
#include "fcmp_pp/fcmp_pp_crypto.h"

#include "single_tx_test_base.h"

class test_torsion_check : public multi_tx_test_base<1>
{
public:
static const size_t loop_count = 10000;

typedef multi_tx_test_base<1> base_class;

bool init()
{
using namespace cryptonote;

if (!base_class::init())
return false;

cryptonote::account_base m_alice;
cryptonote::transaction m_tx;

m_alice.generate();

std::vector<tx_destination_entry> destinations;
destinations.push_back(tx_destination_entry(1, m_alice.get_keys().m_account_address, false));

if (!construct_tx(this->m_miners[this->real_source_idx].get_keys(), this->m_sources, destinations, boost::none, std::vector<uint8_t>(), m_tx))
return false;

const cryptonote::txin_to_key& txin = boost::get<cryptonote::txin_to_key>(m_tx.vin[0]);
m_rct_key = rct::ki2rct(txin.k_image);
return true;
}

bool test()
{
return fcmp_pp::torsion_check(m_rct_key);
}

private:
rct::key m_rct_key;
};
79 changes: 79 additions & 0 deletions tests/performance_tests/torsion_clear.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Copyright (c) 2014-2024, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers

#pragma once

#include "crypto/crypto.h"
#include "cryptonote_basic/cryptonote_basic.h"

#include "single_tx_test_base.h"

class test_torsion_clear : public multi_tx_test_base<1>
{
public:
static const size_t loop_count = 10000;

typedef multi_tx_test_base<1> base_class;

bool init()
{
using namespace cryptonote;

if (!base_class::init())
return false;

cryptonote::account_base m_alice;
cryptonote::transaction m_tx;

m_alice.generate();

std::vector<tx_destination_entry> destinations;
destinations.push_back(tx_destination_entry(1, m_alice.get_keys().m_account_address, false));

if (!construct_tx(this->m_miners[this->real_source_idx].get_keys(), this->m_sources, destinations, boost::none, std::vector<uint8_t>(), m_tx))
return false;

const cryptonote::txin_to_key& txin = boost::get<cryptonote::txin_to_key>(m_tx.vin[0]);
return ge_frombytes_vartime(&m_ge_p3, (const unsigned char*) &rct::ki2rct(txin.k_image)) == 0;
}

bool test()
{
// TODO: make this an apples-to-apples comparison with torsion_check.h
ge_p2 point_inv_8;
ge_scalarmult(&point_inv_8, rct::INV_EIGHT.bytes, &m_ge_p3);
ge_p1p1 point_inv_8_mul_8;
ge_mul8(&point_inv_8_mul_8, &point_inv_8);
return true;
}

private:
ge_p3 m_ge_p3;
};

0 comments on commit bddc3c4

Please sign in to comment.