Skip to content

Commit

Permalink
ecdsa checkpoint bbf
Browse files Browse the repository at this point in the history
  • Loading branch information
AntoineCyr committed Jan 31, 2025
1 parent 41ab935 commit 1363a34
Show file tree
Hide file tree
Showing 7 changed files with 2,586 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,6 @@ namespace nil {
std::size_t witness = num_chunks * Q;
constexpr std::size_t public_inputs = 1;
constexpr std::size_t constants = 0;
// rows = 4096-1 so that lookup table is not too hard to fit and
// padding doesn't inflate the table
constexpr std::size_t rows = 65536 - 1;
return {witness, public_inputs, constants, rows};
}
Expand Down

Large diffs are not rendered by default.

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions crypto3/libs/blueprint/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ set(COMMON_TEST_FILES
"bbf/algebra/curves/weierstrass/ec_incomplete_add"
"bbf/algebra/curves/weierstrass/ec_two_t_plus_q"
"bbf/algebra/curves/weierstrass/ec_scalar_mult"
"bbf/pubkey/ecdsa/ecdsa_recovery"
)

set(NON_NATIVE_TESTS_FILES
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,24 +106,24 @@ void test_ec_scalar_mult(
}
}

template<typename BlueprintFieldType, typename Curve, std::size_t num_chunks,
template<typename BlueprintFieldType, typename CurveType, std::size_t num_chunks,
std::size_t bit_size_chunk, std::size_t RandomTestsAmount>
void ec_scalar_mult_tests() {
using value_type = typename BlueprintFieldType::value_type;
using integral_type = typename BlueprintFieldType::integral_type;

using ec_point_value_type = typename Curve::template g1_type<
using ec_point_value_type = typename CurveType::template g1_type<
nil::crypto3::algebra::curves::coordinates::affine>::value_type;
using scalar_value_type = typename Curve::scalar_field_type::value_type;
using scalar_value_type = typename CurveType::scalar_field_type::value_type;

typedef nil::crypto3::multiprecision::big_uint<2 *
Curve::base_field_type::modulus_bits>
CurveType::base_field_type::modulus_bits>
extended_integral_type;
typedef nil::crypto3::multiprecision::big_uint<2 *
Curve::scalar_field_type::modulus_bits>
CurveType::scalar_field_type::modulus_bits>
scalar_integral_type;

nil::crypto3::random::algebraic_engine<typename Curve::scalar_field_type>
nil::crypto3::random::algebraic_engine<typename CurveType::scalar_field_type>
generate_random_scalar;
boost::random::mt19937 seed_seq;
generate_random_scalar.seed(seed_seq);
Expand All @@ -135,9 +135,9 @@ void ec_scalar_mult_tests() {

extended_integral_type extended_base = 1,
ext_pow = extended_base << (num_chunks * bit_size_chunk),
p = Curve::base_field_type::modulus, pp = ext_pow - p;
p = CurveType::base_field_type::modulus, pp = ext_pow - p;

scalar_integral_type n = Curve::scalar_field_type::modulus,
scalar_integral_type n = CurveType::scalar_field_type::modulus,
s_ext_pow = scalar_integral_type(1)
<< (num_chunks * bit_size_chunk),
m = (n - 1) / 2 + 1, mp = s_ext_pow - m;
Expand Down Expand Up @@ -176,7 +176,7 @@ void ec_scalar_mult_tests() {
public_input[7 * num_chunks + j] = value_type(0);
}

test_ec_scalar_mult<BlueprintFieldType, typename Curve::base_field_type,
test_ec_scalar_mult<BlueprintFieldType, typename CurveType::base_field_type,
num_chunks, bit_size_chunk>(public_input, xR, yR);
}
}
Expand Down
241 changes: 241 additions & 0 deletions crypto3/libs/blueprint/test/bbf/pubkey/ecdsa/ecdsa_recovery.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
//---------------------------------------------------------------------------//
// Copyright (c) 2024 Alexey Yashunsky <[email protected]>
// Copyright (c) 2025 Antoine Cyr <[email protected]>
//
// MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//---------------------------------------------------------------------------//

#define BOOST_TEST_MODULE bbf_ecdsa_recovery_test

#include <boost/test/unit_test.hpp>
#include <nil/blueprint/bbf/circuit_builder.hpp>
#include <nil/blueprint/bbf/components/pubkey/ecdsa/ecdsa_recovery.hpp>
#include <nil/crypto3/algebra/curves/pallas.hpp>
#include <nil/crypto3/algebra/curves/vesta.hpp>
#include <nil/crypto3/random/algebraic_engine.hpp>

using namespace nil;
using namespace nil::blueprint;

template<typename FieldType, typename CurveType, std::size_t num_chunks,
std::size_t bit_size_chunk>
void test_ecdsa_recovery(
typename CurveType::scalar_field_type::value_type z,
typename CurveType::scalar_field_type::value_type r,
typename CurveType::scalar_field_type::value_type s,
typename CurveType::scalar_field_type::value_type v,
typename CurveType::template g1_type<nil::crypto3::algebra::curves::coordinates::affine>::value_type QA) {

using foreign_basic_integral_type = typename CurveType::scalar_field_type::integral_type;
typedef nil::crypto3::multiprecision::big_uint<2 *
CurveType::scalar_field_type::modulus_bits>
foreign_integral_type;
using TYPE = typename FieldType::value_type;
using integral_type = typename FieldType::integral_type;

using BaseField = typename CurveType::base_field_type;

std::vector<TYPE> public_input;

foreign_integral_type B = foreign_integral_type(1) << bit_size_chunk,
zf = foreign_integral_type(foreign_basic_integral_type(z.data)),
rf = foreign_integral_type(foreign_basic_integral_type(r.data)),
sf = foreign_integral_type(foreign_basic_integral_type(s.data)),
vf = foreign_integral_type(foreign_basic_integral_type(v.data));

auto chunks_to_public_input = [&public_input, &B](foreign_integral_type &t) {
for(std::size_t i = 0; i < num_chunks; i++) {
public_input.push_back(TYPE(t % B));
t /= B;
}
};
chunks_to_public_input(zf);
chunks_to_public_input(rf);
chunks_to_public_input(sf);
public_input.push_back(TYPE(vf));

auto assign_and_check = [&](auto& B, auto& raw_input) {
raw_input.z =
std::vector<TYPE>(public_input.begin(), public_input.begin() + num_chunks);
raw_input.r = std::vector<TYPE>(public_input.begin() + num_chunks,
public_input.begin() + 2 * num_chunks);
raw_input.s = std::vector<TYPE>(public_input.begin() + 2 * num_chunks,
public_input.begin() + 3 * num_chunks);
raw_input.v = public_input[3 * num_chunks];

auto [at, A, desc] = B.assign(raw_input);
bool pass = B.is_satisfied(at);
std::cout << "Is_satisfied = " << pass << std::endl;

assert(pass == true);
foreign_integral_type xQA = 0, yQA = 0, pow = 1;
for (std::size_t i = 0; i < num_chunks; i++) {
xQA += foreign_integral_type(integral_type(A.xQA[i].data)) * pow;
yQA += foreign_integral_type(integral_type(A.yQA[i].data)) * pow;
pow <<= bit_size_chunk;
}
//ifdef BLUEPRINT_PLONK_PROFILING_ENABLED
std::cout << "expected: " << QA.X.data << " " << QA.Y.data << "\n";
std::cout << "real : " << xQA << " " << yQA << "\n\n";
//#endif
assert(xQA == QA.X.data);
assert(yQA == QA.Y.data);
};

if constexpr (std::is_same_v<BaseField,
crypto3::algebra::curves::pallas::base_field_type>) {
typename bbf::components::pallas_ecdsa_recovery<
FieldType, bbf::GenerationStage::ASSIGNMENT>::raw_input_type raw_input;

auto B =
bbf::circuit_builder<FieldType, bbf::components::pallas_ecdsa_recovery,
std::size_t, std::size_t>(num_chunks, bit_size_chunk);

assign_and_check(B, raw_input);
} else if constexpr (std::is_same_v<
BaseField,
crypto3::algebra::curves::vesta::base_field_type>) {
typename bbf::components::vesta_ecdsa_recovery<
FieldType, bbf::GenerationStage::ASSIGNMENT>::raw_input_type raw_input;
auto B =
bbf::circuit_builder<FieldType, bbf::components::vesta_ecdsa_recovery,
std::size_t, std::size_t>(num_chunks, bit_size_chunk);

assign_and_check(B, raw_input);
}
}


template<typename FieldType, typename CurveType, std::size_t num_chunks, std::size_t bit_size_chunk, std::size_t RandomTestAmount> void multi_test_recovery() {
nil::crypto3::random::algebraic_engine<typename CurveType::scalar_field_type> generate_random_scalar;

boost::random::mt19937 seed_seq;
generate_random_scalar.seed(seed_seq);

using ec_point_value_type = typename CurveType::template g1_type<nil::crypto3::algebra::curves::coordinates::affine>::value_type;
using scalar_value_type = typename CurveType::scalar_field_type::value_type;
using scalar_integral_type = typename CurveType::scalar_field_type::integral_type;
using base_integral_type = typename CurveType::base_field_type::integral_type;

scalar_value_type d, z, k, r, s, v;
scalar_integral_type n = CurveType::scalar_field_type::modulus,
m = (n-1)/2 + 1;
ec_point_value_type G = ec_point_value_type::one(),
QA, R;


for (std::size_t i = 0; i < RandomTestAmount; i++) {
d = generate_random_scalar(); // private key
QA = G*d; // public key

z = generate_random_scalar(); // instead of taking part of the hash we just generate a random number

do {
k = generate_random_scalar(); // this random generation is part of the signature procedure
R = G*k;
v = scalar_value_type(scalar_integral_type(R.Y.data) % 2);
r = base_integral_type(R.X.data);
s = k.inversed() * (z + r*d);
} while(r.is_zero() || s.is_zero() || (scalar_integral_type(r.data) >= n) || (scalar_integral_type(s.data) >= m));

std::cout << "Random test # " << (i+1) << std::endl;
test_ecdsa_recovery<FieldType,CurveType,num_chunks,bit_size_chunk>(z,r,s,v,QA);
}
}

template<typename FieldType, typename CurveType, std::size_t num_chunks, std::size_t bit_size_chunk, std::size_t RandomTestAmount> void multi_test_recovery_invalid() {
nil::crypto3::random::algebraic_engine<typename CurveType::scalar_field_type> generate_random_scalar;

boost::random::mt19937 seed_seq;
generate_random_scalar.seed(seed_seq);

using ec_point_value_type = typename CurveType::template g1_type<nil::crypto3::algebra::curves::coordinates::affine>::value_type;
using scalar_value_type = typename CurveType::scalar_field_type::value_type;
using scalar_integral_type = typename CurveType::scalar_field_type::integral_type;
using base_value_type = typename CurveType::base_field_type::value_type;
using base_integral_type = typename CurveType::base_field_type::integral_type;

scalar_value_type d, z, k, r, s, v;
scalar_integral_type n = CurveType::scalar_field_type::modulus,
m = (n-1)/2 + 1;
ec_point_value_type G = ec_point_value_type::one(),
QA, R;
base_value_type a = CurveType::template g1_type<nil::crypto3::algebra::curves::coordinates::affine>::params_type::b;

for (std::size_t i = 0; i < RandomTestAmount; i++) {
std::cout << "Random test # " << (i+1) << std::endl;
d = generate_random_scalar(); // private key
QA = G*d; // public key

z = generate_random_scalar(); // instead of taking part of the hash we just generate a random number

std::cout << "Invalid with s > n/2" << std::endl;
do {
k = generate_random_scalar(); // this random generation is part of the signature procedure
R = G*k;
v = scalar_value_type(scalar_integral_type(R.Y.data) % 2);
r = base_integral_type(R.X.data);
s = k.inversed() * (z + r*d);
} while(r.is_zero() || s.is_zero() || (scalar_integral_type(r.data) >= n) || (scalar_integral_type(s.data) < m));
test_ecdsa_recovery<FieldType,CurveType,num_chunks,bit_size_chunk>(z,r,s,v,QA);

std::cout << "Invalid off elliptic curve" << std::endl;
do {
k = generate_random_scalar(); // this random generation is part of the signature procedure
R = G*k;
v = scalar_value_type(scalar_integral_type(R.Y.data) % 2);
r = base_integral_type(R.X.data);
s = k.inversed() * (z + r*d);
} while(r.is_zero() || s.is_zero() || (scalar_integral_type(r.data) >= n) || (scalar_integral_type(s.data) >= m));

base_value_type x1 = base_integral_type(r.data);
while((x1*x1*x1 + a).is_square()) {
x1 = x1 + 1;
}

test_ecdsa_recovery<FieldType,CurveType,num_chunks,bit_size_chunk>(
z,scalar_value_type(base_integral_type(x1.data)),s,v,QA);
}
}

constexpr static const std::size_t random_tests_amount = 1;

BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite)

BOOST_AUTO_TEST_CASE(blueprint_plonk_pubkey_non_native_ecdsa_vesta) {
using vesta = typename crypto3::algebra::curves::vesta;
using pallas_base_field = typename crypto3::algebra::curves::pallas::base_field_type;

// <base_field_type,curve_type, num_chunks, bit_size_chunk, random_tests_amount>
multi_test_recovery<pallas_base_field,vesta, 3, 96, random_tests_amount>();
multi_test_recovery_invalid<pallas_base_field,vesta, 3, 96, random_tests_amount>();
}

BOOST_AUTO_TEST_CASE(blueprint_plonk_pubkey_non_native_ecdsa_pallas) {
using pallas = typename crypto3::algebra::curves::pallas;
using vesta_field_type = typename crypto3::algebra::curves::vesta::base_field_type;

// <base_field_type,curve_type, num_chunks, bit_size_chunk, random_tests_amount>
multi_test_recovery<vesta_field_type,pallas, 3, 96, random_tests_amount>();
multi_test_recovery_invalid<vesta_field_type,pallas, 3, 96, random_tests_amount>();
}

BOOST_AUTO_TEST_SUITE_END()
Loading

0 comments on commit 1363a34

Please sign in to comment.