From 3dc752a02ca4b12977cc714ec29973b307f21eb3 Mon Sep 17 00:00:00 2001 From: Antoine Cyr Date: Tue, 28 Jan 2025 13:56:39 -0500 Subject: [PATCH] complete ecdsa bbf --- .../algebra/curves/weierstrass/ec_double.hpp | 121 ++- .../curves/weierstrass/ec_full_add.hpp | 187 ++--- .../curves/weierstrass/ec_incomplete_add.hpp | 98 +-- .../curves/weierstrass/ec_scalar_mult.hpp | 457 +++++++++++ .../curves/weierstrass/ec_two_t_plus_q.hpp | 344 ++++++++ .../{addition_mod_p.hpp => add_sub_mod_p.hpp} | 164 ++-- .../algebra/fields/non_native/check_mod_p.hpp | 88 +- .../non_native/flexible_multiplication.hpp | 24 +- .../fields/non_native/negation_mod_p.hpp | 47 +- .../components/detail/carry_on_addition.hpp | 6 - .../bbf/components/detail/choice_function.hpp | 6 +- .../components/detail/range_check_multi.hpp | 6 - .../pubkey/ecdsa/ecdsa_recovery.hpp | 765 ++++++++++++++++++ crypto3/libs/blueprint/test/CMakeLists.txt | 5 +- .../algebra/curves/weierstrass/ec_double.cpp | 21 +- .../curves/weierstrass/ec_full_add.cpp | 21 +- .../curves/weierstrass/ec_incomplete_add.cpp | 17 +- .../curves/weierstrass/ec_scalar_mult.cpp | 197 +++++ .../curves/weierstrass/ec_two_t_plus_q.cpp | 213 +++++ .../{addition_mod_p.cpp => add_sub_mod_p.cpp} | 87 +- .../algebra/fields/non_native/check_mod_p.cpp | 4 +- .../non_native/flexible_multiplication.cpp | 4 +- .../fields/non_native/negation_mod_p.cpp | 8 +- .../test/bbf/detail/range_check_multi.cpp | 2 +- .../test/bbf/pubkey/ecdsa/ecdsa_recovery.cpp | 248 ++++++ 25 files changed, 2661 insertions(+), 479 deletions(-) create mode 100644 crypto3/libs/blueprint/include/nil/blueprint/bbf/components/algebra/curves/weierstrass/ec_scalar_mult.hpp create mode 100644 crypto3/libs/blueprint/include/nil/blueprint/bbf/components/algebra/curves/weierstrass/ec_two_t_plus_q.hpp rename crypto3/libs/blueprint/include/nil/blueprint/bbf/components/algebra/fields/non_native/{addition_mod_p.hpp => add_sub_mod_p.hpp} (67%) create mode 100644 crypto3/libs/blueprint/include/nil/blueprint/bbf/components/pubkey/ecdsa/ecdsa_recovery.hpp create mode 100644 crypto3/libs/blueprint/test/bbf/algebra/curves/weierstrass/ec_scalar_mult.cpp create mode 100644 crypto3/libs/blueprint/test/bbf/algebra/curves/weierstrass/ec_two_t_plus_q.cpp rename crypto3/libs/blueprint/test/bbf/algebra/fields/non_native/{addition_mod_p.cpp => add_sub_mod_p.cpp} (75%) create mode 100644 crypto3/libs/blueprint/test/bbf/pubkey/ecdsa/ecdsa_recovery.cpp diff --git a/crypto3/libs/blueprint/include/nil/blueprint/bbf/components/algebra/curves/weierstrass/ec_double.hpp b/crypto3/libs/blueprint/include/nil/blueprint/bbf/components/algebra/curves/weierstrass/ec_double.hpp index 62f60e6f2..afbbc8fed 100644 --- a/crypto3/libs/blueprint/include/nil/blueprint/bbf/components/algebra/curves/weierstrass/ec_double.hpp +++ b/crypto3/libs/blueprint/include/nil/blueprint/bbf/components/algebra/curves/weierstrass/ec_double.hpp @@ -1,6 +1,6 @@ //---------------------------------------------------------------------------// // Copyright (c) 2024 Alexey Yashunsky -// Copyright (c) 2024 Antoine Cyr +// Copyright (c) 2025 Antoine Cyr // // MIT License // @@ -28,23 +28,13 @@ #ifndef CRYPTO3_BBF_COMPONENTS_EC_DOUBLE_ECDSA_HPP #define CRYPTO3_BBF_COMPONENTS_EC_DOUBLE_ECDSA_HPP -#include -#include -#include -#include -#include -#include -#include -#include - -#include +#include #include #include -#include -#include #include -#include -#include +#include +#include +#include namespace nil { namespace blueprint { @@ -56,8 +46,7 @@ namespace nil { // Expects input as k-chunked values with b bits per chunk // p' = 2^(kb) - p // Input: xQ[0],...,xQ[k-1], yQ[0],...,yQ[k-1], p[0],...,p[k-1], - // pp[0],...,pp[k-1], 0[0], ..., 0[k-1] - // (expects zero vector constant as input) + // pp[0],...,pp[k-1], 0 (expects zero constant as input) // Output: xR[0],...,xR[k-1], yR[0],...,yR[k-1] template @@ -67,7 +56,7 @@ namespace nil { std::vector yQ; std::vector p; std::vector pp; - std::vector zero; + TYPE zero; }; template::allocate; using generic_component::copy_constrain; using generic_component::constrain; - using generic_component::lookup; - using component_type = generic_component; public: using typename generic_component::TYPE; @@ -104,14 +91,14 @@ namespace nil { } static std::tuple, std::vector, - std::vector, std::vector, std::vector> + std::vector, std::vector, TYPE> form_input(context_type& context_object, raw_input_type raw_input, std::size_t num_chunks, std::size_t bit_size_chunk) { std::vector input_xQ(num_chunks); std::vector input_yQ(num_chunks); std::vector input_p(num_chunks); std::vector input_pp(num_chunks); - std::vector input_zero(num_chunks); + TYPE input_zero; if constexpr (stage == GenerationStage::ASSIGNMENT) { for (std::size_t i = 0; i < num_chunks; i++) { @@ -119,8 +106,8 @@ namespace nil { input_yQ[i] = raw_input.yQ[i]; input_p[i] = raw_input.p[i]; input_pp[i] = raw_input.pp[i]; - input_zero[i] = raw_input.zero[i]; } + input_zero = raw_input.zero; } for (std::size_t i = 0; i < num_chunks; i++) { context_object.allocate(input_xQ[i], 0, i, @@ -131,16 +118,16 @@ namespace nil { column_type::public_input); context_object.allocate(input_pp[i], 0, i + 3 * num_chunks, column_type::public_input); - context_object.allocate(input_zero[i], 0, i + 4 * num_chunks, - column_type::public_input); } + context_object.allocate(input_zero, 0, 4 * num_chunks, + column_type::public_input); return std::make_tuple(input_xQ, input_yQ, input_p, input_pp, input_zero); } ec_double(context_type& context_object, std::vector input_xQ, std::vector input_yQ, std::vector input_p, - std::vector input_pp, std::vector input_zero, + std::vector input_pp, TYPE input_zero, std::size_t num_chunks, std::size_t bit_size_chunk, bool make_links = true) : generic_component(context_object) { @@ -149,18 +136,15 @@ namespace nil { using non_native_integral_type = typename NonNativeFieldType::integral_type; - using Choice_Function = - typename bbf::components::choice_function; using Range_Check = typename bbf::components::range_check_multi; using Check_Mod_P = typename bbf::components::check_mod_p; - using Addition_Mod_P = - typename bbf::components::addition_mod_p; - using Negation_Mod_P = - typename bbf::components::negation_mod_p; + using Addition_Mod_P = typename bbf::components::add_sub_mod_p< + FieldType, stage, NonNativeFieldType, true>; + using Substraction_Mod_P = + typename bbf::components::add_sub_mod_p< + FieldType, stage, NonNativeFieldType, false>; using Multiplication_Mod_P = typename bbf::components::flexible_multiplication< FieldType, stage, NonNativeFieldType>; @@ -224,8 +208,9 @@ namespace nil { input_pp, input_zero](std::vector x) { Range_Check rc = Range_Check(context_object, x, num_chunks, bit_size_chunk); - Check_Mod_P cm = Check_Mod_P(context_object, x, input_pp, input_zero[0], - num_chunks, bit_size_chunk); + Check_Mod_P cm = + Check_Mod_P(context_object, x, input_pp, input_zero, + num_chunks, bit_size_chunk); }; // Copy constraint generation lambda expression @@ -242,46 +227,46 @@ namespace nil { check_chunked(XR); check_chunked(YR); - auto MultModP = [&context_object, input_p, input_pp, input_zero, num_chunks, - bit_size_chunk](std::vector x, - std::vector y) { - Multiplication_Mod_P t = - Multiplication_Mod_P(context_object, x, y, input_p, input_pp, input_zero[0], - num_chunks, bit_size_chunk); + auto MultModP = [&context_object, input_p, input_pp, input_zero, + num_chunks, bit_size_chunk]( + std::vector x, std::vector y) { + Multiplication_Mod_P t = Multiplication_Mod_P( + context_object, x, y, input_p, input_pp, input_zero, + num_chunks, bit_size_chunk); return t.r; }; - auto AddModP = [&context_object, input_p, input_pp, input_zero, num_chunks, - bit_size_chunk](std::vector x, - std::vector y) { + auto AddModP = [&context_object, input_p, input_pp, input_zero, + num_chunks, bit_size_chunk](std::vector x, + std::vector y) { Addition_Mod_P t = - Addition_Mod_P(context_object, x, y, input_p, input_pp, input_zero, - num_chunks, bit_size_chunk); + Addition_Mod_P(context_object, x, y, input_p, input_pp, + input_zero, num_chunks, bit_size_chunk); return t.r; }; - auto NegModP = [&context_object, input_p, input_pp, input_zero, num_chunks, - bit_size_chunk](std::vector x) { - Negation_Mod_P t = - Negation_Mod_P(context_object, x, input_p, input_pp, input_zero, num_chunks, - bit_size_chunk); + auto SubModP = [&context_object, input_p, input_pp, input_zero, + num_chunks, bit_size_chunk](std::vector x, + std::vector y) { + Substraction_Mod_P t = Substraction_Mod_P( + context_object, x, y, input_p, input_pp, input_zero, + num_chunks, bit_size_chunk); return t.r; }; - auto t1 = MultModP(input_yQ,LAMBDA); // t1 = yQ * lambda - auto t2 = AddModP(t1,t1); // t2 = t1 + t1 = 2yQ * lambda - auto t3 = AddModP(input_xQ,input_xQ); // t3 = xQ + xQ = 2xQ - auto t4 = AddModP(input_xQ,t3); // t4 = xQ + t3 = 3xQ - auto t5 = MultModP(t4,input_xQ); // t5 = t4 * xQ = 3xQ^2 - CopyConstrain(t2, t5); // 2yQ lambda = 3xQ^2 - auto t6 = AddModP(XR,t3); // t6 = xR + t3 = xR + 2xQ - auto t7 = MultModP(LAMBDA,LAMBDA); // t7 = lambda * lambda - CopyConstrain(t6, t7); // xR + 2xQ = lambda^2 - auto t8 = AddModP(YR,input_yQ); // t8 = yR + yQ - auto t9 = NegModP(XR); // t9 = -xR - auto t10 = AddModP(input_xQ,t9); // t10 = xQ + t9 = xQ - xR - auto t11 = MultModP(LAMBDA,t10); // t11 = lambda * t10 =lambda(xQ-xR) - CopyConstrain(t8, t11); // yR + yQ = lambda(xQ - xR) - auto t12 = MultModP(Z,t1); // t12 = z * t1 = z * yQ * lambda - CopyConstrain(LAMBDA, t12); // lambda = z yQ lambda + auto t1 = MultModP(input_yQ, LAMBDA); // t1 = yQ * lambda + auto t2 = AddModP(t1, t1); // t2 = t1 + t1 = 2yQ * lambda + auto t3 = AddModP(input_xQ, input_xQ); // t3 = xQ + xQ = 2xQ + auto t4 = AddModP(input_xQ, t3); // t4 = xQ + t3 = 3xQ + auto t5 = MultModP(t4, input_xQ); // t5 = t4 * xQ = 3xQ^2 + CopyConstrain(t2, t5); // 2yQ lambda = 3xQ^2 + auto t6 = AddModP(XR, t3); // t6 = xR + t3 = xR + 2xQ + auto t7 = MultModP(LAMBDA, LAMBDA); // t7 = lambda * lambda + CopyConstrain(t6, t7); // xR + 2xQ = lambda^2 + auto t8 = AddModP(YR, input_yQ); // t8 = yR + yQ + auto t9 = SubModP(input_xQ, XR); // t9 = xQ - xR + auto t10 = MultModP(LAMBDA, t9); // t10 = lambda * t9 =lambda(xQ-xR) + CopyConstrain(t8, t10); // yR + yQ = lambda(xQ - xR) + auto t11 = MultModP(Z, t1); // t11 = z * t1 = z * yQ * lambda + CopyConstrain(LAMBDA, t11); // lambda = z yQ lambda for (int i = 0; i < num_chunks; ++i) { xR.push_back(XR[i]); diff --git a/crypto3/libs/blueprint/include/nil/blueprint/bbf/components/algebra/curves/weierstrass/ec_full_add.hpp b/crypto3/libs/blueprint/include/nil/blueprint/bbf/components/algebra/curves/weierstrass/ec_full_add.hpp index 7c6bd649d..30f7d3df0 100644 --- a/crypto3/libs/blueprint/include/nil/blueprint/bbf/components/algebra/curves/weierstrass/ec_full_add.hpp +++ b/crypto3/libs/blueprint/include/nil/blueprint/bbf/components/algebra/curves/weierstrass/ec_full_add.hpp @@ -1,6 +1,6 @@ //---------------------------------------------------------------------------// // Copyright (c) 2024 Alexey Yashunsky -// Copyright (c) 2024 Antoine Cyr +// Copyright (c) 2025 Antoine Cyr // // MIT License // @@ -28,23 +28,14 @@ #ifndef CRYPTO3_BBF_COMPONENTS_EC_FULL_ADD_ECDSA_HPP #define CRYPTO3_BBF_COMPONENTS_EC_FULL_ADD_ECDSA_HPP -#include -#include -#include -#include -#include -#include -#include -#include - -#include +#include #include #include #include -#include #include -#include -#include +#include +#include +#include namespace nil { namespace blueprint { @@ -57,12 +48,9 @@ namespace nil { // Expects input as k-chunked values with b bits per chunk // p' = 2^(kb) - p // Input: xP[0],...,xP[k-1],yP[0],...,yP[k-1],xQ[0],...,xQ[k-1], - // yQ[0],...,yQ[k-1], p[0], ..., p[k-1], pp[0], ..., pp[k-1], - // 0[0], ..., 0[k-1] - // (expects zero vector constant as input) - // Output: xR[0],...,xR[k-1], - // yR[0],...,yR[k-1] - // + // yQ[0],...,yQ[k-1], p[0], ..., p[k-1], pp[0], ..., pp[k-1], + // 0 (expects zero constant as input) + // Output: xR[0],...,xR[k-1], yR[0],...,yR[k-1] template struct ec_full_add_raw_input { using TYPE = typename FieldType::value_type; @@ -72,7 +60,7 @@ namespace nil { std::vector yP; std::vector p; std::vector pp; - std::vector zero; + TYPE zero; }; template::allocate; using generic_component::copy_constrain; using generic_component::constrain; - using generic_component::lookup; - using component_type = generic_component; public: using typename generic_component::TYPE; @@ -110,7 +96,7 @@ namespace nil { static std::tuple, std::vector, std::vector, std::vector, - std::vector, std::vector, std::vector> + std::vector, std::vector, TYPE> form_input(context_type& context_object, raw_input_type raw_input, std::size_t num_chunks, std::size_t bit_size_chunk) { std::vector input_xP(num_chunks); @@ -119,7 +105,7 @@ namespace nil { std::vector input_yQ(num_chunks); std::vector input_p(num_chunks); std::vector input_pp(num_chunks); - std::vector input_zero(num_chunks); + TYPE input_zero; if constexpr (stage == GenerationStage::ASSIGNMENT) { for (std::size_t i = 0; i < num_chunks; i++) { @@ -129,8 +115,8 @@ namespace nil { input_yQ[i] = raw_input.yQ[i]; input_p[i] = raw_input.p[i]; input_pp[i] = raw_input.pp[i]; - input_zero[i] = raw_input.zero[i]; } + input_zero = raw_input.zero; } for (std::size_t i = 0; i < num_chunks; i++) { context_object.allocate(input_xP[i], 0, i, @@ -145,9 +131,9 @@ namespace nil { column_type::public_input); context_object.allocate(input_pp[i], 0, i + 5 * num_chunks, column_type::public_input); - context_object.allocate(input_zero[i], 0, i + 6 * num_chunks, - column_type::public_input); } + context_object.allocate(input_zero, 0, 6 * num_chunks, + column_type::public_input); return std::make_tuple(input_xP, input_yP, input_xQ, input_yQ, input_p, input_pp, input_zero); } @@ -155,7 +141,7 @@ namespace nil { ec_full_add(context_type& context_object, std::vector input_xP, std::vector input_yP, std::vector input_xQ, std::vector input_yQ, std::vector input_p, - std::vector input_pp, std::vector input_zero, + std::vector input_pp, TYPE input_zero, std::size_t num_chunks, std::size_t bit_size_chunk, bool make_links = true) : generic_component(context_object) { @@ -164,15 +150,15 @@ namespace nil { using non_native_integral_type = typename NonNativeFieldType::integral_type; - using Choice_Function = - typename bbf::components::choice_function; using Range_Check = typename bbf::components::range_check_multi; using Check_Mod_P = typename bbf::components::check_mod_p; - using Addition_Mod_P = - typename bbf::components::addition_mod_p; + using Addition_Mod_P = typename bbf::components::add_sub_mod_p< + FieldType, stage, NonNativeFieldType, true>; + using Substraction_Mod_P = + typename bbf::components::add_sub_mod_p< + FieldType, stage, NonNativeFieldType, false>; using Negation_Mod_P = typename bbf::components::negation_mod_p; @@ -284,7 +270,7 @@ namespace nil { Range_Check rc = Range_Check(context_object, x, num_chunks, bit_size_chunk); Check_Mod_P cm = - Check_Mod_P(context_object, x, input_pp, input_zero[0], + Check_Mod_P(context_object, x, input_pp, input_zero, num_chunks, bit_size_chunk); }; @@ -309,7 +295,7 @@ namespace nil { num_chunks, bit_size_chunk]( std::vector x, std::vector y) { Multiplication_Mod_P t = Multiplication_Mod_P( - context_object, x, y, input_p, input_pp, input_zero[0], + context_object, x, y, input_p, input_pp, input_zero, num_chunks, bit_size_chunk); return t.r; }; @@ -321,6 +307,14 @@ namespace nil { input_zero, num_chunks, bit_size_chunk); return t.r; }; + auto SubModP = [&context_object, input_p, input_pp, input_zero, + num_chunks, bit_size_chunk](std::vector x, + std::vector y) { + Substraction_Mod_P t = Substraction_Mod_P( + context_object, x, y, input_p, input_pp, input_zero, + num_chunks, bit_size_chunk); + return t.r; + }; auto NegModP = [&context_object, input_p, input_pp, input_zero, num_chunks, bit_size_chunk](std::vector x) { Negation_Mod_P t = @@ -329,73 +323,70 @@ namespace nil { return t.r; }; + std::vector input_zero_vector(num_chunks, input_zero); + // part 1 - auto t1 = NegModP(input_xQ); // t1 = -xQ - auto t2 = NegModP(input_yQ); // t2 = -yQ - auto t3 = NegModP(input_xP); // t3 = -xP - auto t4 = NegModP(input_yP); // t4 = -yP - auto t5 = AddModP(XR, t1); // t5 = xR - xQ - auto t6 = AddModP(YR, t2); // t6 = yR - yQ - auto t7 = AddModP(XR, t3); // t5 = xR - xP - auto t8 = AddModP(YR, t4); // t6 = yR - yP - auto t9 = AddModP(input_xP, t1); // t9 = xP - xQ - auto t10 = MultModP(input_yP, ZP); // t10 = yP * zP - auto t11 = MultModP(input_yQ, ZQ); // t11 = yQ * zQ - auto t12 = MultModP(t9, ZPQ); // t12 = (xP - xQ) zPQ = ZPQ - auto t13 = MultModP(t5, t10); // t13 = (xR - xQ) yP zP - CopyConstrain(t5, t13); // t5 = t13 - auto t14 = MultModP(t6, t10); // t14 = (yR - yQ) yP zP - CopyConstrain(t6, t14); // t6 = t14 - auto t15 = MultModP(t7, t11); // t15 = (xR - xP) yQ zQ - CopyConstrain(t7, t15); // t7 = t15 - auto t16 = MultModP(t8, t11); // t16 = (yR - yP) yQ zQ - CopyConstrain(t8, t16); // t8 = t16 - auto t17 = MultModP(t9, t12); // t17 = (xP - xQ) ZPQ - CopyConstrain(t9, t17); // t9 = t17 - - // part 2 - auto t18 = AddModP(input_yP, input_yQ); // t18 = yP + yQ - auto t19 = MultModP(t18, WPQ); // t19 = (yP + yQ) wPQ = WPQ - auto t20 = AddModP(t12, t19); // t20 = ZPQ + WPQ - auto t21 = MultModP(XR, t20); // t21 = xR(ZPQ + WPQ) - CopyConstrain(XR, t21); // xR = t21 - auto t22 = MultModP(YR, t20); // t22 = yR(ZPQ + WPQ) - CopyConstrain(YR, t22); // yR = t22 + auto t1 = SubModP(XR, input_xQ); // t1 = xR - xQ + auto t2 = SubModP(YR, input_yQ); // t2 = yR - yQ + auto t3 = SubModP(XR, input_xP); // t3 = xR - xP + auto t4 = SubModP(YR, input_yP); // t4 = yR - yP + auto t5 = SubModP(input_xP, input_xQ); // t5 = xP - xQ + auto t6 = MultModP(input_yP, ZP); // t6 = yP * zP + auto t7 = MultModP(input_yQ, ZQ); // t7 = yQ * zQ + auto t8 = MultModP(t5, ZPQ); // t8 = (xP - xQ) zPQ = ZPQ + auto t9 = MultModP(t1, t6); // t9 = (xR - xQ) yP zP + CopyConstrain(t1, t9); // t1 = t9 + auto t10 = MultModP(t2, t6); // t10 = (yR - yQ) yP zP + CopyConstrain(t2, t10); // t2 = t10 + auto t11 = MultModP(t3, t7); // t11 = (xR - xP) yQ zQ + CopyConstrain(t3, t11); // t3 = t11 + auto t12 = MultModP(t4, t7); // t12 = (yR - yP) yQ zQ + CopyConstrain(t4, t12); // t4 = t12 + auto t13 = MultModP(t5, t8); // t13 = (xP - xQ) ZPQ + CopyConstrain(t5, t13); // t5 = t13 + + // // part 2 + auto t14 = AddModP(input_yP, input_yQ); // t14 = yP + yQ + auto t15 = MultModP(t14, WPQ); // t15 = (yP + yQ) wPQ = WPQ + auto t16 = AddModP(t8, t15); // t16 = ZPQ + WPQ + auto t17 = MultModP(XR, t16); // t17 = xR(ZPQ + WPQ) + CopyConstrain(XR, t17); // xR = t17 + auto t18 = MultModP(YR, t16); // t18 = yR(ZPQ + WPQ) + CopyConstrain(YR, t18); // yR = t18 // part 3 - auto t23 = NegModP(t12); // t23 = -ZPQ - auto t24 = MultModP(t18, t23); // t24 = -(yP + yQ) ZPQ - auto t25 = AddModP(t18, t24); // t25 = (yP + yQ)(1 - ZPQ) - auto t26 = AddModP(t9, t25); // t26 = (xP - xQ) + (yP + yQ)(1 - ZPQ) - auto t27 = MultModP(input_yP, input_yQ); // t27 = yP * yQ - auto t28 = MultModP(t26, t27); // t28 = yP yQ (xP - xQ + (yP + yQ)(1 - ZPQ)) - auto t29 = MultModP(LAMBDA, LAMBDA); // t29 = lambda * lambda - auto t30 = NegModP(t29); // t30 = -lambda^2 - auto t31 = AddModP(XR, t30); // t31 = xR - lambda^2 - auto t32 = AddModP(t31, input_xP); // t32 = xR - lambda^2 + xP - auto t33 = AddModP(t32, input_xQ); // t33 = xR - lambda^2 + xP + xQ - auto t34 = AddModP(YR, input_yP); // t34 = yR + yP - auto t35 = MultModP(t7, LAMBDA); // t35 = (xR - xP) lambda - auto t36 = AddModP(t34, t35); // t36 = yR + yP + (xR - xP)lambda - auto t37 = MultModP(t28, t33); // t37 = yP yQ (xP - xQ + (yP + yQ)(1 - ZPQ))(xR - lambda^2 + xP + xQ) - CopyConstrain(t37, input_zero); // t37 = 0 - auto t38 = MultModP(t28, t36); // t38 = yP yQ (xP - xQ + (yP + yQ)(1 -ZPQ))(yR + yP + (xR - xP)lambda) - CopyConstrain(t38, input_zero); // t38 = 0 + auto t19 = NegModP(t8); // t19 = -ZPQ + auto t20 = MultModP(t14, t19); // t20 = -(yP + yQ) ZPQ + auto t21 = AddModP(t14, t20); // t21 = (yP + yQ)(1 - ZPQ) + auto t22 = AddModP(t5, t21); // t22 = (xP - xQ) + (yP + yQ)(1 - ZPQ) + auto t23 = MultModP(input_yP, input_yQ);// t23 = yP * yQ + auto t24 = MultModP( t22, t23); // t24 = yP yQ (xP - xQ + (yP + yQ)(1 - ZPQ)) + auto t25 = MultModP(LAMBDA, LAMBDA); // t25 = lambda * lambda + auto t26 = SubModP(XR, t25); // t26 = xR - lambda^2 + auto t27 = AddModP(t26, input_xP); // t27 = xR - lambda^2 + xP + auto t28 = AddModP(t27, input_xQ); // t28 = xR - lambda^2 + xP + xQ + auto t29 = AddModP(YR, input_yP); // t29 = yR + yP + auto t30 = MultModP(t3, LAMBDA); // t30 = (xR - xP) lambda + auto t31 = AddModP(t29, t30); // t31 = yR + yP + (xR - xP)lambda + auto t32 = MultModP(t24, t28); // t32 = yP yQ (xP - xQ + (yP + yQ)(1 - ZPQ))(xR - lambda^2 + xP + xQ) + CopyConstrain(t32, input_zero_vector); // t32 = 0 + auto t33 = MultModP(t24, t31); // t33 = yP yQ (xP - xQ + (yP + yQ)(1-ZPQ))(yR + yP + (xR - xP)lambda) + CopyConstrain(t33, input_zero_vector); // t33 = 0 // part 4 - auto t39 = MultModP(t9, LAMBDA); // t39 = (xP - xQ) lambda - auto t40 = AddModP(t39, t4); // t40 = (xP - xQ) lambda - yP - auto t41 = AddModP(t40, input_yQ); // t41 = (xP - xQ) lambda - yP + yQ - auto t42 = MultModP(t9, t41); // t42 = (xP - xQ)((xP - xQ) lambda - yP + yQ) - CopyConstrain(t42, input_zero); // t42 = 0 - auto t43 = MultModP(input_xP, t3); // t43 = -xP^2 - auto t44 = AddModP(t43, t43); // t44 = -2xP^2 - auto t45 = AddModP(t43, t44); // t45 = -3xP^2 - auto t46 = AddModP(input_yP, input_yP); // t46 = 2yP - auto t47 = MultModP(t46, LAMBDA); // t47 = 2yP lambda - auto t48 = AddModP(t47, t45); // t48 = 2yP lambda - 3xP^2 - auto t49 = MultModP(t48, t12); // t49 = (2yP lambda - 3xP^2) ZPQ - CopyConstrain(t48, t49); // t48 = t49 + auto t34 = MultModP(t5, LAMBDA); // t34 = (xP - xQ) lambda + auto t35 = SubModP(t34, input_yP); // t35 = (xP - xQ) lambda - yP + auto t36 = AddModP(t35, input_yQ); // t36 = (xP - xQ) lambda - yP + yQ + auto t37 = MultModP(t5, t36); // t37 = (xP - xQ)((xP - xQ) lambda - yP + yQ) + CopyConstrain(t37, input_zero_vector); // t37 = 0 + auto t38 = MultModP(input_xP, input_xP); // t38 = xP^2 + auto t39 = AddModP(t38, t38); // t39 = 2xP^2 + auto t40 = AddModP(t38, t39); // t40 = 3xP^2 + auto t41 = AddModP(input_yP, input_yP); // t41 = 2yP + auto t42 = MultModP(t41, LAMBDA); // t42 = 2yP lambda + auto t43 = SubModP(t42, t40); // t43 = 2yP lambda - 3xP^2 + auto t44 = MultModP(t43, t8); // t44 = (2yP lambda - 3xP^2) ZPQ + CopyConstrain(t43, t44); // t43 = t44 for (int i = 0; i < num_chunks; ++i) { xR.push_back(XR[i]); diff --git a/crypto3/libs/blueprint/include/nil/blueprint/bbf/components/algebra/curves/weierstrass/ec_incomplete_add.hpp b/crypto3/libs/blueprint/include/nil/blueprint/bbf/components/algebra/curves/weierstrass/ec_incomplete_add.hpp index 658140450..67b898c40 100644 --- a/crypto3/libs/blueprint/include/nil/blueprint/bbf/components/algebra/curves/weierstrass/ec_incomplete_add.hpp +++ b/crypto3/libs/blueprint/include/nil/blueprint/bbf/components/algebra/curves/weierstrass/ec_incomplete_add.hpp @@ -1,6 +1,6 @@ //---------------------------------------------------------------------------// // Copyright (c) 2024 Alexey Yashunsky -// Copyright (c) 2024 Antoine Cyr +// Copyright (c) 2025 Antoine Cyr // // MIT License // @@ -28,23 +28,13 @@ #ifndef CRYPTO3_BBF_COMPONENTS_EC_INCOMPLETE_ADD_ECDSA_HPP #define CRYPTO3_BBF_COMPONENTS_EC_INCOMPLETE_ADD_ECDSA_HPP -#include -#include -#include -#include -#include -#include -#include -#include - -#include +#include #include #include -#include -#include #include -#include -#include +#include +#include +#include namespace nil { namespace blueprint { @@ -58,20 +48,19 @@ namespace nil { // p' = 2^(kb) - p // Input: xP[0],...,xP[k-1],yP[0],...,yP[k-1],xQ[0],...,xQ[k-1], // yQ[0],...,yQ[k-1], p[0], ..., p[k-1], pp[0], ..., pp[k-1], - // 0[0], ..., 0[k-1] - // (expects zero vector constant as input) + // 0 (expects zero constant as input) // Output: xR[0],...,xR[k-1], yR[0],...,yR[k-1] // template struct ec_incomplete_add_raw_input { using TYPE = typename FieldType::value_type; - std::vector xQ; - std::vector yQ; std::vector xP; std::vector yP; + std::vector xQ; + std::vector yQ; std::vector p; std::vector pp; - std::vector zero; + TYPE zero; }; template::allocate; using generic_component::copy_constrain; using generic_component::constrain; - using generic_component::lookup; - using component_type = generic_component; public: using typename generic_component::TYPE; @@ -109,7 +96,7 @@ namespace nil { static std::tuple, std::vector, std::vector, std::vector, - std::vector, std::vector, std::vector> + std::vector, std::vector, TYPE> form_input(context_type& context_object, raw_input_type raw_input, std::size_t num_chunks, std::size_t bit_size_chunk) { std::vector input_xP(num_chunks); @@ -118,7 +105,7 @@ namespace nil { std::vector input_yQ(num_chunks); std::vector input_p(num_chunks); std::vector input_pp(num_chunks); - std::vector input_zero(num_chunks); + TYPE input_zero; if constexpr (stage == GenerationStage::ASSIGNMENT) { for (std::size_t i = 0; i < num_chunks; i++) { @@ -128,8 +115,8 @@ namespace nil { input_yQ[i] = raw_input.yQ[i]; input_p[i] = raw_input.p[i]; input_pp[i] = raw_input.pp[i]; - input_zero[i] = raw_input.zero[i]; } + input_zero = raw_input.zero; } for (std::size_t i = 0; i < num_chunks; i++) { context_object.allocate(input_xP[i], 0, i, @@ -144,9 +131,9 @@ namespace nil { column_type::public_input); context_object.allocate(input_pp[i], 0, i + 5 * num_chunks, column_type::public_input); - context_object.allocate(input_zero[i], 0, i + 6 * num_chunks, - column_type::public_input); } + context_object.allocate(input_zero, 0, 6 * num_chunks, + column_type::public_input); return std::make_tuple(input_xP, input_yP, input_xQ, input_yQ, input_p, input_pp, input_zero); } @@ -157,8 +144,7 @@ namespace nil { std::vector input_xQ, std::vector input_yQ, std::vector input_p, - std::vector input_pp, - std::vector input_zero, + std::vector input_pp, TYPE input_zero, std::size_t num_chunks, std::size_t bit_size_chunk, bool make_links = true) : generic_component(context_object) { @@ -167,18 +153,15 @@ namespace nil { using non_native_integral_type = typename NonNativeFieldType::integral_type; - using Choice_Function = - typename bbf::components::choice_function; using Range_Check = typename bbf::components::range_check_multi; using Check_Mod_P = typename bbf::components::check_mod_p; - using Addition_Mod_P = - typename bbf::components::addition_mod_p; - using Negation_Mod_P = - typename bbf::components::negation_mod_p; + using Addition_Mod_P = typename bbf::components::add_sub_mod_p< + FieldType, stage, NonNativeFieldType, true>; + using Substraction_Mod_P = + typename bbf::components::add_sub_mod_p< + FieldType, stage, NonNativeFieldType, false>; using Multiplication_Mod_P = typename bbf::components::flexible_multiplication< FieldType, stage, NonNativeFieldType>; @@ -243,7 +226,7 @@ namespace nil { Range_Check rc = Range_Check(context_object, x, num_chunks, bit_size_chunk); Check_Mod_P cm = - Check_Mod_P(context_object, x, input_pp, input_zero[0], + Check_Mod_P(context_object, x, input_pp, input_zero, num_chunks, bit_size_chunk); }; @@ -264,7 +247,7 @@ namespace nil { num_chunks, bit_size_chunk]( std::vector x, std::vector y) { Multiplication_Mod_P t = Multiplication_Mod_P( - context_object, x, y, input_p, input_pp, input_zero[0], + context_object, x, y, input_p, input_pp, input_zero, num_chunks, bit_size_chunk); return t.r; }; @@ -276,28 +259,27 @@ namespace nil { input_zero, num_chunks, bit_size_chunk); return t.r; }; - auto NegModP = [&context_object, input_p, input_pp, input_zero, - num_chunks, bit_size_chunk](std::vector x) { - Negation_Mod_P t = - Negation_Mod_P(context_object, x, input_p, input_pp, - input_zero, num_chunks, bit_size_chunk); + auto SubModP = [&context_object, input_p, input_pp, input_zero, + num_chunks, bit_size_chunk](std::vector x, + std::vector y) { + Substraction_Mod_P t = Substraction_Mod_P( + context_object, x, y, input_p, input_pp, input_zero, + num_chunks, bit_size_chunk); return t.r; }; - auto t1 = NegModP(input_xP); // t1 = -xP - auto t2 = AddModP(input_xQ, t1); // t2 = xQ + t1 = xQ - xP - auto t3 = MultModP(t2, LAMBDA); // t3 = t2 * lambda = (xQ-xP)lambda - auto t4 = AddModP(t3, input_yP); // t4 = t3 + yP = (xQ-xP)lambda + yP - CopyConstrain(t4, input_yQ); // (xQ - xP)lambda + yP = yQ - auto t5 = AddModP(XR, input_xP); // t5 = xR + xP - auto t6 = AddModP(t5, input_xQ); // t6 = t5 + xQ = xR + xP + xQ - auto t7 = MultModP(LAMBDA, LAMBDA); // t7 = lambda * lambda - CopyConstrain(t6, t7); // xR + xP + xQ = lambda^2 - auto t8 = AddModP(YR, input_yP); // t8 = yR + yP - auto t9 = NegModP(XR); // t9 = -xR - auto t10 = AddModP(input_xP, t9); // t10 = xP + t9 = xP - xR - auto t11 = MultModP(LAMBDA,t10); // t11 = lambda * t10 =lambda(xP-xR) - CopyConstrain(t8, t11); // yR + yP = lambda(xP - xR) + auto t1 = SubModP(input_xQ, input_xP); // t1 = xQ - xP + auto t2 = MultModP(t1, LAMBDA); // t2 = t1 * lambda = (xQ-xP)lambda + auto t3 = AddModP(t2, input_yP); // t3 = t2 + yP = (xQ-xP)lambda + yP + CopyConstrain(t3, input_yQ); // (xQ - xP)lambda + yP = yQ + auto t4 = AddModP(XR, input_xP); // t4 = xR + xP + auto t5 = AddModP(t4, input_xQ); // t5 = t4 + xQ = xR + xP + xQ + auto t6 = MultModP(LAMBDA, LAMBDA); // t6 = lambda * lambda + CopyConstrain(t5, t6); // xR + xP + xQ = lambda^2 + auto t7 = AddModP(YR, input_yP); // t7 = yR + yP + auto t8 = SubModP(input_xP, XR); // t8 = xP - xR + auto t9 = MultModP(LAMBDA, t8); // t9 = lambda * t8 =lambda(xP-xR) + CopyConstrain(t7, t9); // yR + yP = lambda(xP - xR) for (int i = 0; i < num_chunks; ++i) { xR.push_back(XR[i]); diff --git a/crypto3/libs/blueprint/include/nil/blueprint/bbf/components/algebra/curves/weierstrass/ec_scalar_mult.hpp b/crypto3/libs/blueprint/include/nil/blueprint/bbf/components/algebra/curves/weierstrass/ec_scalar_mult.hpp new file mode 100644 index 000000000..f95990121 --- /dev/null +++ b/crypto3/libs/blueprint/include/nil/blueprint/bbf/components/algebra/curves/weierstrass/ec_scalar_mult.hpp @@ -0,0 +1,457 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Alexey Yashunsky +// Copyright (c) 2025 Antoine Cyr +// +// 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. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for scalar multiplication of EC points over a non-native field +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BBF_COMPONENTS_EC_SCALAR_MULT_ECDSA_HPP +#define CRYPTO3_BBF_COMPONENTS_EC_SCALAR_MULT_ECDSA_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace bbf { + namespace components { + // Parameters: num_chunks = k, bit_size_chunk = b + // For scalar s and point input_p = (x,y), input_p != O, s != 0 (to be corrected?) + // from an elliptic curve over F[p] + // computes R = s × input_p (scalar product for EC point) + // Expects input as k-chunked values with b bits per chunk + // Other values: p' = 2^(kb) - p, n = size of EC group, m = (n-1)/2, m' = 2^(kb) - m + // Input: s[0],...,s[k-1], x[0],...,x[k-1], y[0],...,y[k-1], p[0],...,p[k-1], + // pp[0], ..., pp[k-1], n[0],...,n[k-1], mp[0],...,mp[k-1], 0[0], ..., 0[k-1] + // (expects zero vector constant as input) + // Output: xR[0],...,xR[k-1], yR[0],...,yR[k-1] + // + template + struct ec_scalar_mult_raw_input { + using TYPE = typename FieldType::value_type; + std::vector s; + std::vector x; + std::vector y; + std::vector p; + std::vector pp; + std::vector n; + std::vector mp; + TYPE zero; + }; + + template + class ec_scalar_mult : public generic_component { + using generic_component::allocate; + using generic_component::copy_constrain; + using generic_component::constrain; + + public: + using typename generic_component::TYPE; + using typename generic_component::context_type; + using typename generic_component::table_params; + using raw_input_type = + typename std::conditional, + std::tuple<>>::type; + + public: + std::vector xR; + std::vector yR; + + static table_params get_minimal_requirements( + std::size_t num_chunks, std::size_t bit_size_chunk) { + std::size_t witness = 7 * num_chunks; + constexpr std::size_t public_inputs = 1; + constexpr std::size_t constants = 0; + constexpr std::size_t rows = 65536 - 1; + return {witness, public_inputs, constants, rows}; + } + + static std::tuple, std::vector, + std::vector, std::vector, + std::vector, std::vector, + std::vector, TYPE> + form_input(context_type& context_object, raw_input_type raw_input, + std::size_t num_chunks, std::size_t bit_size_chunk) { + std::vector input_s(num_chunks); + std::vector input_x(num_chunks); + std::vector input_y(num_chunks); + std::vector input_n(num_chunks); + std::vector input_mp(num_chunks); + std::vector input_p(num_chunks); + std::vector input_pp(num_chunks); + TYPE input_zero; + + if constexpr (stage == GenerationStage::ASSIGNMENT) { + for (std::size_t i = 0; i < num_chunks; i++) { + input_s[i] = raw_input.s[i]; + input_x[i] = raw_input.x[i]; + input_y[i] = raw_input.y[i]; + input_p[i] = raw_input.p[i]; + input_pp[i] = raw_input.pp[i]; + input_n[i] = raw_input.n[i]; + input_mp[i] = raw_input.mp[i]; + } + input_zero = raw_input.zero; + } + for (std::size_t i = 0; i < num_chunks; i++) { + context_object.allocate(input_s[i], 0, i, + column_type::public_input); + context_object.allocate(input_x[i], 0, i + num_chunks, + column_type::public_input); + context_object.allocate(input_y[i], 0, i + 2 * num_chunks, + column_type::public_input); + context_object.allocate(input_p[i], 0, i + 3 * num_chunks, + column_type::public_input); + context_object.allocate(input_pp[i], 0, i + 4 * num_chunks, + column_type::public_input); + context_object.allocate(input_n[i], 0, i + 5 * num_chunks, + column_type::public_input); + context_object.allocate(input_mp[i], 0, i + 6 * num_chunks, + column_type::public_input); + } + context_object.allocate(input_zero, 0, 7 * num_chunks, + column_type::public_input); + return std::make_tuple(input_s, input_x, input_y, input_p, + input_pp, input_n, input_mp, input_zero); + } + + ec_scalar_mult(context_type& context_object, + std::vector input_s, std::vector input_x, + std::vector input_y, std::vector input_p, + std::vector input_pp, std::vector input_n, + std::vector input_mp, TYPE input_zero, + std::size_t num_chunks, std::size_t bit_size_chunk, + bool make_links = true) + : generic_component(context_object) { + using integral_type = typename FieldType::integral_type; + using NON_NATIVE_TYPE = typename NonNativeFieldType::value_type; + using non_native_integral_type = + typename NonNativeFieldType::integral_type; + + + using Choice_Function = + typename bbf::components::choice_function; + using Carry_On_addition = + typename bbf::components::carry_on_addition; + using Range_Check = + typename bbf::components::range_check_multi; + using Negation_Mod_P = + typename bbf::components::negation_mod_p; + using Ec_Double = + typename bbf::components::ec_double; + using Ec_Incomplete_Add = + typename bbf::components::ec_incomplete_add; + using Ec_Two_T_Plus_Q = + typename bbf::components::ec_two_t_plus_q; + + + std::vector EXTEND_BIT_ARRAY(num_chunks); + + const std::size_t L = bit_size_chunk*num_chunks + (bit_size_chunk*num_chunks % 2), // if odd, then +1. Thus L is always even + Q = L/2; + + std::vector> C(Q, std::vector(num_chunks)); + std::vector SP(num_chunks); + std::vector CP(Q); + std::vector CPP(Q); + + std::vector> Xi(Q, std::vector(num_chunks)); + std::vector> Yi(Q, std::vector(num_chunks)); + std::vector> XPi(Q, std::vector(num_chunks)); + std::vector> YPi(Q, std::vector(num_chunks)); + + if constexpr (stage == GenerationStage::ASSIGNMENT) { + non_native_integral_type pow = 1; + non_native_integral_type s = 0, n = 0; + + for (std::size_t i = 0; i < num_chunks; ++i) { + s += non_native_integral_type( + integral_type(input_s[i].data)) * + pow; + n += non_native_integral_type( + integral_type(input_n[i].data)) * + pow; + pow <<= bit_size_chunk; + } + + non_native_integral_type sp = n - s, + C = (s > (n-1)/2) ? sp : s; + + // binary expansion of C, LSB + for(std::size_t i = 0; i < L; i++) { + if (i % 2) { // if i is odd + CP[i/2] = C % 2; + } else { + CPP[i/2] = C % 2; + } + C /= 2; + } + + auto base = [num_chunks, bit_size_chunk](NON_NATIVE_TYPE x) { + std::vector res(num_chunks); + non_native_integral_type mask = + (non_native_integral_type(1) << bit_size_chunk) - 1; + non_native_integral_type x_value = + non_native_integral_type(x.data); + for (std::size_t i = 0; i < num_chunks; i++) { + res[i] = TYPE(x_value & mask); + x_value >>= bit_size_chunk; + } + return res; + }; + SP = base(sp); + } + + for (std::size_t i = 0; i < Q; ++i) { + allocate(CP[i]); + allocate(CPP[i]); + for (std::size_t j = 0; j < num_chunks; ++j){ + allocate(C[i][j]); + allocate(Xi[i][j]); + allocate(Yi[i][j]); + allocate(XPi[i][j]); + allocate(YPi[i][j]); + } + } + for (std::size_t i = 0; i < num_chunks; ++i){ + allocate(SP[i]); + EXTEND_BIT_ARRAY[i] = input_zero; + } + + auto RangeCheck = [&context_object, num_chunks, bit_size_chunk](std::vector x) { + Range_Check rc = Range_Check(context_object, x, num_chunks, + bit_size_chunk); + }; + auto CarryOnAddition = [&context_object, num_chunks, bit_size_chunk](std::vector x ,std::vector y) { + Carry_On_addition ca = Carry_On_addition(context_object, x,y, num_chunks, + bit_size_chunk); + return ca; + }; + auto ChoiceFunction = [&context_object, num_chunks, bit_size_chunk](TYPE q, std::vector x ,std::vector y) { + Choice_Function cf = Choice_Function(context_object, q,x,y, num_chunks, + bit_size_chunk); + return cf.r; + }; + + auto CopyConstrain = [this, num_chunks](std::vector x, + std::vector y) { + for (std::size_t i = 0; i < num_chunks; i++) { + copy_constrain(x[i], y[i]); + } + }; + + auto SingleCopyConstrain = [this, num_chunks](TYPE x, + TYPE y) { + copy_constrain(x, y); + }; + + auto NegModP = [&context_object, input_p, input_pp, input_zero, num_chunks, + bit_size_chunk](std::vector x) { + Negation_Mod_P t = + Negation_Mod_P(context_object, x, input_p, input_pp, input_zero, num_chunks, + bit_size_chunk); + return t.r; + }; + + auto ECDouble = [&context_object, input_p, input_pp, input_zero, num_chunks, + bit_size_chunk](std::vector xQ,std::vector yQ) { + Ec_Double t = + Ec_Double(context_object, xQ,yQ, input_p, input_pp, input_zero, num_chunks, + bit_size_chunk); + return t; + }; + + auto ECIncompleteAdd = [&context_object, input_p, input_pp, input_zero, num_chunks, + bit_size_chunk](std::vector xP,std::vector yP,std::vector xQ,std::vector yQ) { + Ec_Incomplete_Add t = + Ec_Incomplete_Add(context_object, xP,yP,xQ,yQ, input_p, input_pp, input_zero, num_chunks, + bit_size_chunk); + return t; + }; + + auto ECTwoTPlusQ = [&context_object, input_p, input_pp, input_zero, num_chunks, + bit_size_chunk](std::vector xt,std::vector yt,std::vector xQ,std::vector yQ) { + Ec_Two_T_Plus_Q t = + Ec_Two_T_Plus_Q(context_object, xt,yt,xQ,yQ, input_p, input_pp, input_zero, num_chunks, + bit_size_chunk); + return t; + }; + + auto CopyChunks = [num_chunks](std::vector &from, std::vector &to) { + for(std::size_t i = 0; i < num_chunks; i++) { + to[i] = from[i]; + } + }; + + // Part I : adjusting the scalar and the point + auto t = CarryOnAddition(input_s,input_mp); + RangeCheck(t.r); + auto alt_n = CarryOnAddition(input_s,SP); + CopyConstrain(alt_n.r,input_n); + SingleCopyConstrain(alt_n.c,input_zero); + RangeCheck(SP); + auto total_C = ChoiceFunction(t.c,input_s,SP); // labeled simply C without indices on the Notion page + auto y_minus = NegModP(input_y); + auto y1 = ChoiceFunction(t.c,input_y,y_minus); + // Assert s × (x,y) = C × (x,y1) + + //Part II : precompute + auto p2 = ECDouble(input_x,y1); + auto p3 = ECIncompleteAdd(input_x,y1,p2.xR,p2.yR); + auto y_minus1 = ChoiceFunction(t.c,y_minus,input_y); + auto y_minus3 = NegModP(p3.yR); + + // we now have the points {+/-1, +/-3} × (x, y1) + // Part III : the main loop + // Uses a quaternary decomposition C = c_{Q-1} c_{Q-2}...c_0, c_i = 2c_i' + c_i'' where c_i' and c_i'' are bits + // On every step we add a scalar according to the following table + // c_i | c_i' | c_i'' | scalar = 2c_i - 3 + // -----+------+-------+------- + // 0 | 0 | 0 | -3 + // 1 | 0 | 1 | -1 + // 2 | 1 | 0 | 1 + // 3 | 1 | 1 | 3 + // + // the loop + for(std::size_t i = Q-1; i > 0; i--) { + if (i < Q-1) { + auto Pp_temp = ECDouble(Xi[i+1],Yi[i+1]); + CopyChunks(Pp_temp.xR, XPi[i+1]); + CopyChunks(Pp_temp.yR, YPi[i+1]); + + auto C_p = CarryOnAddition(C[i+1],C[i+1]); + RangeCheck(C_p.r); + SingleCopyConstrain(C_p.c,input_zero); + + EXTEND_BIT_ARRAY[0] = CP[i]; + //here + auto C_pp = CarryOnAddition(C_p.r,EXTEND_BIT_ARRAY); + RangeCheck(C_pp.r); + SingleCopyConstrain(C_pp.c,input_zero); + CopyChunks(C_pp.r,C[i]); + } else { + EXTEND_BIT_ARRAY[0] = CP[i]; + CopyChunks(EXTEND_BIT_ARRAY,C[i]); + } + auto C_ppp = CarryOnAddition(C[i],C[i]); + RangeCheck(C_ppp.r); + SingleCopyConstrain(C_ppp.c,input_zero); + + EXTEND_BIT_ARRAY[0] = CPP[i]; + auto C_temp = CarryOnAddition(C_ppp.r,EXTEND_BIT_ARRAY); + SingleCopyConstrain(C_temp.c,input_zero); + CopyChunks(C_temp.r,C[i]); + RangeCheck(C[i]); + + auto xi_p = ChoiceFunction(CP[i],p3.xR,input_x); + auto xi_pp = ChoiceFunction(CP[i],input_x,p3.xR); + auto xi = ChoiceFunction(CPP[i],xi_p,xi_pp); + auto eta_p = ChoiceFunction(CP[i],y_minus3,y1); + auto eta_pp = ChoiceFunction(CP[i],y_minus1,p3.yR); + auto eta = ChoiceFunction(CPP[i],eta_p,eta_pp); + auto P_temp = ECTwoTPlusQ((i < Q-1) ? XPi[i+1] : p2.xR,(i < Q-1)? YPi[i+1] : p2.yR, xi, eta); + + CopyChunks(P_temp.xR,Xi[i]); + CopyChunks(P_temp.yR,Yi[i]); + } + // post-loop computations + auto C_p = CarryOnAddition(C[1],C[1]); + RangeCheck(C_p.r); + SingleCopyConstrain(C_p.c,input_zero); + + EXTEND_BIT_ARRAY[0] = CP[0]; + auto C_pp = CarryOnAddition(C_p.r,EXTEND_BIT_ARRAY); + RangeCheck(C_pp.r); + SingleCopyConstrain(C_pp.c,input_zero); + + auto C_ppp = CarryOnAddition(C_pp.r,C_pp.r); + RangeCheck(C_ppp.r); + SingleCopyConstrain(C_ppp.c,input_zero); + + EXTEND_BIT_ARRAY[0] = CPP[0]; + auto C_temp = CarryOnAddition(C_ppp.r,EXTEND_BIT_ARRAY); + + CopyConstrain(total_C,C_temp.r); + + SingleCopyConstrain(C_temp.c,input_zero); + auto eta = ChoiceFunction(CP[0],y_minus1,y1); + auto Pp_pre = ECDouble(Xi[1],Yi[1]); + auto Pp_temp = ECIncompleteAdd(Pp_pre.xR,Pp_pre.yR,input_x,eta); + auto Ppp_temp = ECIncompleteAdd(Pp_temp.xR,Pp_temp.yR,input_x,y_minus1); + // this ^^^ will fail for 0 scalar (needs almost full addition) + auto XR = ChoiceFunction(CPP[0],Ppp_temp.xR,Pp_temp.xR); + auto YR = ChoiceFunction(CPP[0],Ppp_temp.yR,Pp_temp.yR); + + for (int i = 0; i < num_chunks; ++i) { + xR.push_back(XR[i]); + yR.push_back(YR[i]); + } + } + }; + + template + class pallas_ec_scalar_mult + : public ec_scalar_mult< + FieldType, stage, + crypto3::algebra::curves::pallas::base_field_type> { + using Base = ec_scalar_mult< + FieldType, stage, + crypto3::algebra::curves::pallas::base_field_type>; + + public: + using Base::Base; + }; + + template + class vesta_ec_scalar_mult + : public ec_scalar_mult< + FieldType, stage, + crypto3::algebra::curves::vesta::base_field_type> { + using Base = ec_scalar_mult< + FieldType, stage, + crypto3::algebra::curves::vesta::base_field_type>; + + public: + using Base::Base; + }; + + } // namespace components + } // namespace bbf + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BBF_COMPONENTS_EC_SCALAR_MULT_ECDSA_HPP diff --git a/crypto3/libs/blueprint/include/nil/blueprint/bbf/components/algebra/curves/weierstrass/ec_two_t_plus_q.hpp b/crypto3/libs/blueprint/include/nil/blueprint/bbf/components/algebra/curves/weierstrass/ec_two_t_plus_q.hpp new file mode 100644 index 000000000..9cf18a90a --- /dev/null +++ b/crypto3/libs/blueprint/include/nil/blueprint/bbf/components/algebra/curves/weierstrass/ec_two_t_plus_q.hpp @@ -0,0 +1,344 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Alexey Yashunsky +// Copyright (c) 2025 Antoine Cyr +// +// 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. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for addition of EC points T,Q as T+Q+T over a +// non-native field +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BBF_COMPONENTS_EC_TWO_T_PLUS_Q_ECDSA_HPP +#define CRYPTO3_BBF_COMPONENTS_EC_TWO_T_PLUS_Q_ECDSA_HPP + +#include +#include +#include +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace bbf { + namespace components { + // Parameters: num_chunks = k, bit_size_chunk = b + // For points T = (x_T,y_T), Q = (x_Q,y_Q), x_T != x_T, T,Q != O + // from an elliptic curve over F[p] + // computes R = (x_R, y_R) = T + Q + T + // Expects input as k-chunked values with b bits per chunk + // p' = 2^(kb) - p + // Input: xT[0],...,xT[k-1],yT[0],...,yT[k-1],xQ[0],...,xQ[k-1], + // yQ[0],...,yQ[k-1], p[0], ..., p[k-1], pp[0], ..., pp[k-1], + // 0 (expects zero constant as input) + // Output: xR[0],...,xR[k-1], yR[0],...,yR[k-1] + // + template + struct ec_two_t_plus_q_raw_input { + using TYPE = typename FieldType::value_type; + std::vector xT; + std::vector yT; + std::vector xQ; + std::vector yQ; + std::vector p; + std::vector pp; + TYPE zero; + }; + + template + class ec_two_t_plus_q : public generic_component { + using generic_component::allocate; + using generic_component::copy_constrain; + using generic_component::constrain; + + public: + using typename generic_component::TYPE; + using typename generic_component::context_type; + using typename generic_component::table_params; + using raw_input_type = + typename std::conditional, + std::tuple<>>::type; + + public: + std::vector xR; + std::vector yR; + + static table_params get_minimal_requirements( + std::size_t num_chunks, std::size_t bit_size_chunk) { + std::size_t witness = 6 * num_chunks + 1; + 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 = 4095; + return {witness, public_inputs, constants, rows}; + } + + static std::tuple, std::vector, + std::vector, std::vector, + std::vector, std::vector, TYPE> + form_input(context_type& context_object, raw_input_type raw_input, + std::size_t num_chunks, std::size_t bit_size_chunk) { + std::vector input_xT(num_chunks); + std::vector input_yT(num_chunks); + std::vector input_xQ(num_chunks); + std::vector input_yQ(num_chunks); + std::vector input_p(num_chunks); + std::vector input_pp(num_chunks); + TYPE input_zero; + + if constexpr (stage == GenerationStage::ASSIGNMENT) { + for (std::size_t i = 0; i < num_chunks; i++) { + input_xT[i] = raw_input.xT[i]; + input_yT[i] = raw_input.yT[i]; + input_xQ[i] = raw_input.xQ[i]; + input_yQ[i] = raw_input.yQ[i]; + input_p[i] = raw_input.p[i]; + input_pp[i] = raw_input.pp[i]; + } + input_zero = raw_input.zero; + } + for (std::size_t i = 0; i < num_chunks; i++) { + context_object.allocate(input_xT[i], 0, i, + column_type::public_input); + context_object.allocate(input_yT[i], 0, i + num_chunks, + column_type::public_input); + context_object.allocate(input_xQ[i], 0, i + 2 * num_chunks, + column_type::public_input); + context_object.allocate(input_yQ[i], 0, i + 3 * num_chunks, + column_type::public_input); + context_object.allocate(input_p[i], 0, i + 4 * num_chunks, + column_type::public_input); + context_object.allocate(input_pp[i], 0, i + 5 * num_chunks, + column_type::public_input); + } + context_object.allocate(input_zero, 0, 6 * num_chunks, + column_type::public_input); + return std::make_tuple(input_xT, input_yT, input_xQ, input_yQ, + input_p, input_pp, input_zero); + } + + ec_two_t_plus_q(context_type& context_object, + std::vector input_xT, + std::vector input_yT, + std::vector input_xQ, + std::vector input_yQ, std::vector input_p, + std::vector input_pp, TYPE input_zero, + std::size_t num_chunks, std::size_t bit_size_chunk, + bool make_links = true) + : generic_component(context_object) { + using integral_type = typename FieldType::integral_type; + using NON_NATIVE_TYPE = typename NonNativeFieldType::value_type; + using non_native_integral_type = + typename NonNativeFieldType::integral_type; + + using Range_Check = + typename bbf::components::range_check_multi; + using Check_Mod_P = + typename bbf::components::check_mod_p; + using Addition_Mod_P = typename bbf::components::add_sub_mod_p< + FieldType, stage, NonNativeFieldType, true>; + using Substraction_Mod_P = + typename bbf::components::add_sub_mod_p< + FieldType, stage, NonNativeFieldType, false>; + using Multiplication_Mod_P = + typename bbf::components::flexible_multiplication< + FieldType, stage, NonNativeFieldType>; + + std::vector LAMBDA(num_chunks); + std::vector MU(num_chunks); + std::vector XS(num_chunks); + std::vector XR(num_chunks); + std::vector YR(num_chunks); + + if constexpr (stage == GenerationStage::ASSIGNMENT) { + non_native_integral_type pow = 1; + NON_NATIVE_TYPE xT = 0, yT = 0, xQ = 0, yQ = 0; + + for (std::size_t i = 0; i < num_chunks; ++i) { + xT += non_native_integral_type( + integral_type(input_xT[i].data)) * + pow; + yT += non_native_integral_type( + integral_type(input_yT[i].data)) * + pow; + xQ += non_native_integral_type( + integral_type(input_xQ[i].data)) * + pow; + yQ += non_native_integral_type( + integral_type(input_yQ[i].data)) * + pow; + pow <<= bit_size_chunk; + } + + NON_NATIVE_TYPE diff1 = xQ - xT, + lambda = (diff1 == 0) + ? 0 + : (yQ - yT) * diff1.inversed(), + xS = lambda * lambda - xT - xQ, + diff2 = xS - xT, + mu = (diff2 == 0) + ? -lambda + : -lambda - + (2 * yT) * diff2.inversed(), + xR = mu * mu - xT - xS, + yR = mu * (xT - xR) - yT; + + auto base = [num_chunks, bit_size_chunk](NON_NATIVE_TYPE x) { + std::vector res(num_chunks); + non_native_integral_type mask = + (non_native_integral_type(1) << bit_size_chunk) - 1; + non_native_integral_type x_value = + non_native_integral_type(x.data); + for (std::size_t i = 0; i < num_chunks; i++) { + res[i] = TYPE(x_value & mask); + x_value >>= bit_size_chunk; + } + return res; + }; + + LAMBDA = base(lambda); + MU = base(mu); + XS = base(xS); + XR = base(xR); + YR = base(yR); + } + + for (std::size_t i = 0; i < num_chunks; ++i) { + allocate(LAMBDA[i]); + allocate(MU[i]); + allocate(XS[i]); + allocate(XR[i]); + allocate(YR[i]); + } + + auto check_chunked = [&context_object, num_chunks, bit_size_chunk, + input_pp, input_zero](std::vector x) { + Range_Check rc = Range_Check(context_object, x, num_chunks, + bit_size_chunk); + Check_Mod_P cm = + Check_Mod_P(context_object, x, input_pp, input_zero, + num_chunks, bit_size_chunk); + }; + + // Copy constraint generation lambda expression + auto CopyConstrain = [this, num_chunks](std::vector x, + std::vector y) { + for (std::size_t i = 0; i < num_chunks; i++) { + copy_constrain(x[i], y[i]); + } + }; + + // perform range checks and mod p checks on all stored variables + check_chunked(LAMBDA); + check_chunked(MU); + check_chunked(XS); + check_chunked(XR); + check_chunked(YR); + + auto MultModP = [&context_object, input_p, input_pp, input_zero, + num_chunks, bit_size_chunk]( + std::vector x, std::vector y) { + Multiplication_Mod_P t = Multiplication_Mod_P( + context_object, x, y, input_p, input_pp, input_zero, + num_chunks, bit_size_chunk); + return t.r; + }; + auto AddModP = [&context_object, input_p, input_pp, input_zero, + num_chunks, bit_size_chunk](std::vector x, + std::vector y) { + Addition_Mod_P t = + Addition_Mod_P(context_object, x, y, input_p, input_pp, + input_zero, num_chunks, bit_size_chunk); + return t.r; + }; + auto SubModP = [&context_object, input_p, input_pp, input_zero, + num_chunks, bit_size_chunk](std::vector x, + std::vector y) { + Substraction_Mod_P t = Substraction_Mod_P( + context_object, x, y, input_p, input_pp, input_zero, + num_chunks, bit_size_chunk); + return t.r; + }; + + auto t1 = SubModP(input_xQ, input_xT); // t1 = xQ - xT + auto t2 = MultModP(t1, LAMBDA); // t2 = t1 * lambda = (xQ-xT)lambda + auto t3 = AddModP(t2, input_yT); // t3 = t2 + yT = (xQ-xP)lambda + yT + CopyConstrain(t3, input_yQ); // (xQ - xT)lambda + yP = yQ + auto t4 = AddModP(XS, input_xT); // t4 = xS + xT + auto t5 = AddModP(t4, input_xQ); // t5 = t4 + xQ = xS + xT + xQ + auto t6 = MultModP(LAMBDA, LAMBDA); // t6 = lambda * lambda + CopyConstrain(t5, t6); // xS + xT + xQ = lambda^2 + auto t7 = AddModP(LAMBDA, MU); // t7 = lambda + mu + auto t8 = SubModP(input_xT, XS); // t8 = xT - xS + auto t9 = MultModP(t7, t8); // t9 = t7*t8 = (lambda+mu)(xT-xS) + auto t10 = AddModP(input_yT, input_yT); // t10 = yT + yT = 2yT + CopyConstrain(t9, t10); // (lambda+mu)(xT - xS) = 2yT + auto t11 = AddModP(t4, XR); // t11 = t4 + xR = xS + xT + xR + auto t12 = MultModP(MU, MU); // t12 = mu * mu + CopyConstrain(t11, t12); // xS + xT + xR = mu^2 + auto t13 = AddModP(YR, input_yT); // t13 = yR + yT + auto t14 = SubModP(input_xT, XR); // t14 = xT - xR + auto t15 = MultModP(MU, t14); // t15 = mu * t14 = mu(xT-xR) + CopyConstrain(t13, t15); // yR + yT = mu(xT - xR) + + for (int i = 0; i < num_chunks; ++i) { + xR.push_back(XR[i]); + yR.push_back(YR[i]); + } + } + }; + + template + class pallas_ec_two_t_plus_q + : public ec_two_t_plus_q< + FieldType, stage, + crypto3::algebra::curves::pallas::base_field_type> { + using Base = ec_two_t_plus_q< + FieldType, stage, + crypto3::algebra::curves::pallas::base_field_type>; + + public: + using Base::Base; + }; + + template + class vesta_ec_two_t_plus_q + : public ec_two_t_plus_q< + FieldType, stage, + crypto3::algebra::curves::vesta::base_field_type> { + using Base = + ec_two_t_plus_q; + + public: + using Base::Base; + }; + + } // namespace components + } // namespace bbf + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BBF_COMPONENTS_EC_TWO_T_PLUS_Q_ECDSA_HPP diff --git a/crypto3/libs/blueprint/include/nil/blueprint/bbf/components/algebra/fields/non_native/addition_mod_p.hpp b/crypto3/libs/blueprint/include/nil/blueprint/bbf/components/algebra/fields/non_native/add_sub_mod_p.hpp similarity index 67% rename from crypto3/libs/blueprint/include/nil/blueprint/bbf/components/algebra/fields/non_native/addition_mod_p.hpp rename to crypto3/libs/blueprint/include/nil/blueprint/bbf/components/algebra/fields/non_native/add_sub_mod_p.hpp index ada2a200e..1aa744dd5 100644 --- a/crypto3/libs/blueprint/include/nil/blueprint/bbf/components/algebra/fields/non_native/addition_mod_p.hpp +++ b/crypto3/libs/blueprint/include/nil/blueprint/bbf/components/algebra/fields/non_native/add_sub_mod_p.hpp @@ -1,6 +1,5 @@ //---------------------------------------------------------------------------// -// Copyright (c) 2024 Valeh Farzaliyev -// Copyright (c) 2024 Antoine Cyr +// Copyright (c) 2025 Antoine Cyr // // MIT License // @@ -22,58 +21,49 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. //---------------------------------------------------------------------------// -// @file Declaration of interfaces for addition function on mod p. +// @file Declaration of interfaces for addition and substraction function on mod p. //---------------------------------------------------------------------------// -#ifndef CRYPTO3_BBF_COMPONENTS_ADDITION_MOD_P_HPP -#define CRYPTO3_BBF_COMPONENTS_ADDITION_MOD_P_HPP - -#include -#include -#include -#include -#include -#include -#include -#include +#ifndef CRYPTO3_BBF_COMPONENTS_ADD_SUB_MOD_P_HPP +#define CRYPTO3_BBF_COMPONENTS_ADD_SUB_MOD_P_HPP #include #include #include #include -#include +#include +#include +#include namespace nil { namespace blueprint { namespace bbf { namespace components { - // Addition mod p - // operates on k-chunked x,y, p, p' + // ADD_SUB mod p operates on k-chunked x,y, p, p' // Parameters: num_chunks = k, bit_size_chunk = b // Input: x[0], ..., x[k-1], y[0], ..., y[k-1], p[0], ..., p[k-1], p'[0], - // ..., p'[k-1], 0[0], ..., 0[k-1] (expects zero vector constant as input) + // ..., p'[k-1], 0 (expects zero constant as input) // Intermediate values: q, t[0], ..., t[k-1], carry[k-1], t'[0], ..., - // t'[k-1], t"[0], ..., t"[k-1], carry"[k-1] Output: r[0] = x[0] + y[0] - - // qp[0], ..., r[k-1] = x[k-1] + y[k-1] -qp[k-1] - // + // t'[k-1], t"[0], ..., t"[k-1], carry"[k-1] + // Output: r[0] = x[0] + y[0] - qp[0], ..., r[k-1] = x[k-1] + y[k-1] + // - qp[k-1] template - struct addition_mod_p_raw_input { + struct add_sub_mod_p_raw_input { using TYPE = typename FieldType::value_type; std::vector x; std::vector y; std::vector p; std::vector pp; - std::vector zero; + TYPE zero; }; template - class addition_mod_p : public generic_component { + typename NonNativeFieldType, bool is_add = true> + class add_sub_mod_p : public generic_component { using generic_component::allocate; using generic_component::copy_constrain; using generic_component::constrain; - using generic_component::lookup; using component_type = generic_component; public: @@ -82,7 +72,7 @@ namespace nil { using typename generic_component::table_params; using raw_input_type = typename std::conditional, + add_sub_mod_p_raw_input, std::tuple<>>::type; public: @@ -100,15 +90,14 @@ namespace nil { } static std::tuple, std::vector, - std::vector, std::vector, - std::vector> + std::vector, std::vector, TYPE> form_input(context_type &context_object, raw_input_type raw_input, std::size_t num_chunks, std::size_t bit_size_chunk) { std::vector input_x(num_chunks); std::vector input_y(num_chunks); std::vector input_p(num_chunks); std::vector input_pp(num_chunks); - std::vector input_zero(num_chunks); + TYPE input_zero; if constexpr (stage == GenerationStage::ASSIGNMENT) { for (std::size_t i = 0; i < num_chunks; i++) { @@ -116,8 +105,8 @@ namespace nil { input_y[i] = raw_input.y[i]; input_p[i] = raw_input.p[i]; input_pp[i] = raw_input.pp[i]; - input_zero[i] = raw_input.zero[i]; } + input_zero = raw_input.zero; } for (std::size_t i = 0; i < num_chunks; i++) { context_object.allocate(input_x[i], 0, i, @@ -128,18 +117,18 @@ namespace nil { column_type::public_input); context_object.allocate(input_pp[i], 0, i + 3 * num_chunks, column_type::public_input); - context_object.allocate(input_zero[i], 0, i + 4 * num_chunks, - column_type::public_input); } + context_object.allocate(input_zero, 0, 4 * num_chunks, + column_type::public_input); return std::make_tuple(input_x, input_y, input_p, input_pp, input_zero); } - addition_mod_p(context_type &context_object, - std::vector input_x, std::vector input_y, - std::vector input_p, std::vector input_pp, - std::vector input_zero, std::size_t num_chunks, - std::size_t bit_size_chunk, bool make_links = true) + add_sub_mod_p(context_type &context_object, std::vector input_x, + std::vector input_y, std::vector input_p, + std::vector input_pp, TYPE input_zero, + std::size_t num_chunks, std::size_t bit_size_chunk, + bool make_links = true) : generic_component(context_object) { using integral_type = typename FieldType::integral_type; using extended_integral_type = @@ -156,6 +145,8 @@ namespace nil { typename bbf::components::range_check_multi; std::vector R(num_chunks); + std::vector ca_1_inp(num_chunks); + std::vector ca_2_inp(num_chunks); TYPE Q; if constexpr (stage == GenerationStage::ASSIGNMENT) { @@ -175,12 +166,20 @@ namespace nil { pow; pow <<= bit_size_chunk; } - - r = x + y; // x + y = r + qp - if (r >= p) { - r -= p; - Q = 1; + if (is_add) { + r = x + y; + if (r >= p) { + r -= p; + Q = 1; + } + } else { + if (y > x) { + x += p; + Q = 1; + } + r = x - y; } + extended_integral_type mask = (extended_integral_type(1) << bit_size_chunk) - 1; for (std::size_t i = 0; i < num_chunks; ++i) { @@ -194,21 +193,34 @@ namespace nil { } allocate(Q); - Carry_On_Addition ca_1 = Carry_On_Addition( - context_object, input_x, input_y, num_chunks, bit_size_chunk); + // We prove ca_1 == ca_2 where + //  x + y = r + qp (addition) + // r + y = x + qp (substraction) + + for (std::size_t i = 0; i < num_chunks; ++i) { + if (is_add) { + ca_1_inp[i] = input_x[i]; + ca_2_inp[i] = R[i]; + } else { + ca_1_inp[i] = R[i]; + ca_2_inp[i] = input_x[i]; + } + } + + Carry_On_Addition ca_1 = + Carry_On_Addition(context_object, ca_1_inp, input_y, + num_chunks, bit_size_chunk); Range_Check rc_1 = Range_Check(context_object, ca_1.r, num_chunks, bit_size_chunk); //(qp = 0 or p) + std::vector input_zero_vector(num_chunks, input_zero); Choice_Function cf = Choice_Function( - context_object, Q, input_zero, input_p, num_chunks); + context_object, Q, input_zero_vector, input_p, num_chunks); Carry_On_Addition ca_2 = - Carry_On_Addition(context_object, cf.r, R, num_chunks, + Carry_On_Addition(context_object, ca_2_inp, cf.r, num_chunks, bit_size_chunk); // qp + r - // carry_on_addition results should be equal to each other x + y - // = r + qp - for (std::size_t i = 0; i < num_chunks; i++) { copy_constrain(ca_1.r[i], ca_2.r[i]); } @@ -216,14 +228,12 @@ namespace nil { Range_Check rc_2 = Range_Check(context_object, ca_2.r, num_chunks, bit_size_chunk); - Range_Check rc_3 = Range_Check(context_object, R, num_chunks, bit_size_chunk); Check_Mod_P cm = - Check_Mod_P(context_object, R, input_pp, input_zero[0], + Check_Mod_P(context_object, R, input_pp, input_zero, num_chunks, bit_size_chunk); - for (int i = 0; i < num_chunks; ++i) { r.push_back(R[i]); } @@ -232,12 +242,13 @@ namespace nil { template class pallas_addition_mod_p - : public addition_mod_p< + : public add_sub_mod_p< FieldType, stage, - crypto3::algebra::curves::pallas::base_field_type> { + crypto3::algebra::curves::pallas::base_field_type, true> { using Base = - addition_mod_p; + add_sub_mod_p; public: using Base::Base; @@ -245,12 +256,41 @@ namespace nil { template class vesta_addition_mod_p - : public addition_mod_p< + : public add_sub_mod_p< + FieldType, stage, + crypto3::algebra::curves::vesta::base_field_type, true> { + using Base = + add_sub_mod_p; + + public: + using Base::Base; + }; + + template + class pallas_substraction_mod_p + : public add_sub_mod_p< + FieldType, stage, + crypto3::algebra::curves::pallas::base_field_type, false> { + using Base = + add_sub_mod_p; + + public: + using Base::Base; + }; + + template + class vesta_substraction_mod_p + : public add_sub_mod_p< FieldType, stage, - crypto3::algebra::curves::vesta::base_field_type> { + crypto3::algebra::curves::vesta::base_field_type, false> { using Base = - addition_mod_p; + add_sub_mod_p; public: using Base::Base; @@ -261,4 +301,4 @@ namespace nil { } // namespace blueprint } // namespace nil -#endif // CRYPTO3_BBF_COMPONENTS_ADDITION_MOD_P_HPP +#endif // CRYPTO3_BBF_COMPONENTS_ADD_SUB_MOD_P_HPP diff --git a/crypto3/libs/blueprint/include/nil/blueprint/bbf/components/algebra/fields/non_native/check_mod_p.hpp b/crypto3/libs/blueprint/include/nil/blueprint/bbf/components/algebra/fields/non_native/check_mod_p.hpp index 54bc9d2bb..148f9d731 100644 --- a/crypto3/libs/blueprint/include/nil/blueprint/bbf/components/algebra/fields/non_native/check_mod_p.hpp +++ b/crypto3/libs/blueprint/include/nil/blueprint/bbf/components/algebra/fields/non_native/check_mod_p.hpp @@ -22,20 +22,18 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. //---------------------------------------------------------------------------// -// @file Declaration of interfaces for PLONK component wrapping the BBF-component interface +// @file Declaration of interfaces for PLONK component wrapping the BBF-component +// interface //---------------------------------------------------------------------------// #ifndef CRYPTO3_BBF_COMPONENTS_CHECK_MOD_P_HPP #define CRYPTO3_BBF_COMPONENTS_CHECK_MOD_P_HPP -#include -#include -#include -#include -#include -#include #include -#include +#include +#include +#include +#include namespace nil { namespace blueprint { @@ -45,7 +43,7 @@ namespace nil { // Checking that x is in the interval [0;p-1] // operates on k-chunked x and p' = 2^(kb) - p // Input: x[0], ..., x[k-1], pp[0], ..., pp[k-1], 0 - // (expects zero constant as input) + // (expects zero constant as input) // Output: none template @@ -61,8 +59,6 @@ namespace nil { using generic_component::allocate; using generic_component::copy_constrain; using generic_component::constrain; - using generic_component::lookup; - using component_type = generic_component; public: using typename generic_component::TYPE; @@ -72,29 +68,31 @@ namespace nil { typename std::conditional, std::tuple<>>::type; + public: TYPE output; - static table_params get_minimal_requirements(std::size_t num_chunks, - std::size_t bit_size_chunk, - bool expect_output = false) { + static table_params get_minimal_requirements( + std::size_t num_chunks, std::size_t bit_size_chunk, + bool expect_output = false) { static const std::size_t bit_size_rc = 16; - std::size_t num_rc_chunks = (bit_size_chunk / bit_size_rc) + (bit_size_chunk % bit_size_rc > 0); + std::size_t num_rc_chunks = (bit_size_chunk / bit_size_rc) + + (bit_size_chunk % bit_size_rc > 0); // Same witness columns as range_check_multi - std::size_t witness = (num_rc_chunks+1)/2 + 1; + std::size_t witness = (num_rc_chunks + 1) / 2 + 1; 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 + // 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 = 4095; return {witness, public_inputs, constants, rows}; } - static std::tuple,std::vector,TYPE> form_input(context_type &context_object, - raw_input_type raw_input, - std::size_t num_chunks, - std::size_t bit_size_chunk, - bool expect_output = false) { + static std::tuple, std::vector, TYPE> + form_input(context_type &context_object, raw_input_type raw_input, + std::size_t num_chunks, std::size_t bit_size_chunk, + bool expect_output = false) { std::vector input_x(num_chunks); std::vector input_pp(num_chunks); TYPE input_zero; @@ -103,36 +101,42 @@ namespace nil { input_x[i] = raw_input.x[i]; input_pp[i] = raw_input.pp[i]; } - input_zero =raw_input.zero; + input_zero = raw_input.zero; } - for (std::size_t i = 0; i < num_chunks; i++) - { - context_object.allocate(input_x[i], 0, i, column_type::public_input); - context_object.allocate(input_pp[i], 0, i+num_chunks, column_type::public_input); + for (std::size_t i = 0; i < num_chunks; i++) { + context_object.allocate(input_x[i], 0, i, + column_type::public_input); + context_object.allocate(input_pp[i], 0, i + num_chunks, + column_type::public_input); } - context_object.allocate(input_zero,0,2*num_chunks + 1,column_type::public_input); - return std::make_tuple(input_x,input_pp,input_zero); + context_object.allocate(input_zero, 0, 2 * num_chunks + 1, + column_type::public_input); + return std::make_tuple(input_x, input_pp, input_zero); } - check_mod_p(context_type &context_object, std::vector input_x, std::vector input_pp, TYPE input_zero, - std::size_t num_chunks, std::size_t bit_size_chunk,bool expect_output = false, - bool make_links = true) + check_mod_p(context_type &context_object, std::vector input_x, + std::vector input_pp, TYPE input_zero, + std::size_t num_chunks, std::size_t bit_size_chunk, + bool expect_output = false, bool make_links = true) : generic_component(context_object) { using integral_type = typename FieldType::integral_type; - using Carry_On_Addition = typename bbf::components::carry_on_addition; - using Range_Check = typename bbf::components::range_check_multi; + using Carry_On_Addition = + typename bbf::components::carry_on_addition; + using Range_Check = + typename bbf::components::range_check_multi; + + Carry_On_Addition ca = + Carry_On_Addition(context_object, input_x, input_pp, + num_chunks, bit_size_chunk); + Range_Check rc = + Range_Check(context_object, ca.r, num_chunks, bit_size_chunk); - Carry_On_Addition ca = Carry_On_Addition(context_object,input_x,input_pp,num_chunks,bit_size_chunk); - Range_Check rc = Range_Check(context_object, ca.r,num_chunks,bit_size_chunk); - - if(expect_output){ + if (expect_output) { output = ca.c; + } else { + copy_constrain(ca.c, input_zero); } - else{ - copy_constrain(ca.c,input_zero); - } - } }; diff --git a/crypto3/libs/blueprint/include/nil/blueprint/bbf/components/algebra/fields/non_native/flexible_multiplication.hpp b/crypto3/libs/blueprint/include/nil/blueprint/bbf/components/algebra/fields/non_native/flexible_multiplication.hpp index 1690f953f..8d4731949 100644 --- a/crypto3/libs/blueprint/include/nil/blueprint/bbf/components/algebra/fields/non_native/flexible_multiplication.hpp +++ b/crypto3/libs/blueprint/include/nil/blueprint/bbf/components/algebra/fields/non_native/flexible_multiplication.hpp @@ -30,18 +30,11 @@ #ifndef CRYPTO3_BBF_COMPONENTS_FLEXIBLE_MULTIPLICATION_HPP #define CRYPTO3_BBF_COMPONENTS_FLEXIBLE_MULTIPLICATION_HPP -#include +#include +#include #include -#include -#include -#include #include #include -#include - -#include -#include -#include namespace nil { namespace blueprint { @@ -72,8 +65,6 @@ namespace nil { using generic_component::allocate; using generic_component::copy_constrain; using generic_component::constrain; - using generic_component::lookup; - using component_type = generic_component; public: using typename generic_component::TYPE; @@ -241,10 +232,11 @@ namespace nil { // + [q_0, q_1, q_2, q_3] ⋅ [pp_0, pp_1, pp_2, pp_3] // z_0 = x_0 ⋅ y_0 + q_0 ⋅ pp_0 // z_1 = x_0 ⋅ y_1 + x_1 ⋅ y_0 + q_0 ⋅ pp_1 + q_1 ⋅ pp_0 - // z_2 = x_0 ⋅ y_2 + x_1 ⋅ y_1 + x_2 ⋅ y_0 + q_0 ⋅ pp_2 + q_1 ⋅ - // pp_1 + q_2 ⋅ pp_0 z_3 = x_0 ⋅ y_3 + x_1 ⋅ y_2 + x_2 ⋅ y_1 + - // x_3 ⋅ y_0 + q_0 ⋅ pp_3 + q_1 ⋅ pp_2 + q_2 ⋅ pp_1 + q_3 ⋅ - // pp_0 + // z_2 = x_0 ⋅ y_2 + x_1 ⋅ y_1 + x_2 ⋅ y_0 + q_0 ⋅ pp_2 + + // q_1 ⋅ pp_1 + q_2 ⋅ pp_0 + // z_3 = x_0 ⋅ y_3 + x_1 ⋅ y_2 + x_2 ⋅ y_1 + + // x_3 ⋅ y_0 + q_0 ⋅ pp_3 + q_1 ⋅ pp_2 + q_2 ⋅ pp_1 + + // q_3 ⋅ pp_0 // Result = z_0 ⋅ 2^{0b} + z_1 ⋅ 2^{1b} + z_2 ⋅ 2^{2b} + z_3 ⋅ // 2^{3b} for (std::size_t i = 0; i < num_chunks; ++i) { @@ -310,7 +302,7 @@ namespace nil { Range_Check rc2 = Range_Check(context_object, Q, num_chunks, bit_size_chunk); Range_Check rc3 = - Range_Check(context_object, B, num_chunks, bit_size_chunk); + Range_Check(context_object, B, 2 * (num_chunks - 2), bit_size_chunk); Check_Mod_P c1 = Check_Mod_P(context_object, R, PP, input_zero, num_chunks, bit_size_chunk, false); diff --git a/crypto3/libs/blueprint/include/nil/blueprint/bbf/components/algebra/fields/non_native/negation_mod_p.hpp b/crypto3/libs/blueprint/include/nil/blueprint/bbf/components/algebra/fields/non_native/negation_mod_p.hpp index f3bf3c21e..220ef5742 100644 --- a/crypto3/libs/blueprint/include/nil/blueprint/bbf/components/algebra/fields/non_native/negation_mod_p.hpp +++ b/crypto3/libs/blueprint/include/nil/blueprint/bbf/components/algebra/fields/non_native/negation_mod_p.hpp @@ -29,30 +29,22 @@ #ifndef CRYPTO3_BBF_COMPONENTS_NEGATION_MOD_P_HPP #define CRYPTO3_BBF_COMPONENTS_NEGATION_MOD_P_HPP -#include -#include -#include -#include -#include -#include -#include -#include - #include #include #include #include -#include +#include +#include +#include namespace nil { namespace blueprint { namespace bbf { namespace components { // Parameters: num_chunks = k, bit_size_chunk = b - // Finding the negative r of integer x, modulo p and checking that x + r = 0 mod p - // Input: x[0], ..., x[k-1], p[0], ..., p[k-1], pp[0], ..., pp[k-1], 0[0], ..., 0[k-1] - // (expects zero vector constant as input) - // Output: r[0], ..., r[k-1] + // Finding the negative r of integer x, modulo p and checking that x + r = + // 0 mod p Input: x[0], ..., x[k-1], p[0], ..., p[k-1], pp[0], ..., + // pp[k-1], 0 (expects zero constant as input) Output: r[0], ..., r[k-1] template struct negation_mod_p_raw_input { @@ -60,7 +52,7 @@ namespace nil { std::vector x; std::vector p; std::vector pp; - std::vector zero; + TYPE zero; }; template::allocate; using generic_component::copy_constrain; using generic_component::constrain; - using generic_component::lookup; - using component_type = generic_component; public: using typename generic_component::TYPE; @@ -96,21 +86,21 @@ namespace nil { } static std::tuple, std::vector, - std::vector, std::vector> + std::vector, TYPE> form_input(context_type &context_object, raw_input_type raw_input, std::size_t num_chunks, std::size_t bit_size_chunk) { std::vector input_x(num_chunks); std::vector input_p(num_chunks); std::vector input_pp(num_chunks); - std::vector input_zero(num_chunks); + TYPE input_zero; if constexpr (stage == GenerationStage::ASSIGNMENT) { for (std::size_t i = 0; i < num_chunks; i++) { input_x[i] = raw_input.x[i]; input_p[i] = raw_input.p[i]; input_pp[i] = raw_input.pp[i]; - input_zero[i] = raw_input.zero[i]; } + input_zero = raw_input.zero; } for (std::size_t i = 0; i < num_chunks; i++) { @@ -120,18 +110,18 @@ namespace nil { column_type::public_input); context_object.allocate(input_pp[i], 0, i + 2 * num_chunks, column_type::public_input); - context_object.allocate(input_zero[i], 0, i + 3 * num_chunks, - column_type::public_input); } + context_object.allocate(input_zero, 0, 3 * num_chunks, + column_type::public_input); return std::make_tuple(input_x, input_p, input_pp, input_zero); } negation_mod_p(context_type &context_object, std::vector input_x, std::vector input_p, - std::vector input_pp, - std::vector input_zero, std::size_t num_chunks, - std::size_t bit_size_chunk, bool make_links = true) + std::vector input_pp, TYPE input_zero, + std::size_t num_chunks, std::size_t bit_size_chunk, + bool make_links = true) : generic_component(context_object) { using integral_type = typename FieldType::integral_type; using extended_integral_type = @@ -178,8 +168,9 @@ namespace nil { allocate(R[i]); } + std::vector input_zero_vector(num_chunks, input_zero); Choice_Function cf = Choice_Function( - context_object, Q, input_zero, input_p, num_chunks); + context_object, Q, input_zero_vector, input_p, num_chunks); Carry_On_Addition ca = Carry_On_Addition( context_object, input_x, R, num_chunks, bit_size_chunk); @@ -188,13 +179,13 @@ namespace nil { for (std::size_t i = 0; i < num_chunks; i++) { copy_constrain(ca.r[i], cf.r[i]); } - copy_constrain(ca.c, input_zero[0]); + copy_constrain(ca.c, input_zero); Range_Check rc = Range_Check(context_object, R, num_chunks, bit_size_chunk); Check_Mod_P cm = - Check_Mod_P(context_object, R, input_pp, input_zero[0], + Check_Mod_P(context_object, R, input_pp, input_zero, num_chunks, bit_size_chunk); for (int i = 0; i < num_chunks; ++i) { diff --git a/crypto3/libs/blueprint/include/nil/blueprint/bbf/components/detail/carry_on_addition.hpp b/crypto3/libs/blueprint/include/nil/blueprint/bbf/components/detail/carry_on_addition.hpp index cc9dc1bde..4f1749f5b 100644 --- a/crypto3/libs/blueprint/include/nil/blueprint/bbf/components/detail/carry_on_addition.hpp +++ b/crypto3/libs/blueprint/include/nil/blueprint/bbf/components/detail/carry_on_addition.hpp @@ -29,12 +29,7 @@ #ifndef CRYPTO3_BBF_COMPONENTS_CARRY_ON_ADDITION_HPP #define CRYPTO3_BBF_COMPONENTS_CARRY_ON_ADDITION_HPP -#include #include -#include -#include -#include -#include namespace nil { namespace blueprint { @@ -53,7 +48,6 @@ namespace nil { using generic_component::allocate; using generic_component::copy_constrain; using generic_component::constrain; - using generic_component::lookup; public: using typename generic_component::TYPE; diff --git a/crypto3/libs/blueprint/include/nil/blueprint/bbf/components/detail/choice_function.hpp b/crypto3/libs/blueprint/include/nil/blueprint/bbf/components/detail/choice_function.hpp index 0fc746599..9fcb56c12 100644 --- a/crypto3/libs/blueprint/include/nil/blueprint/bbf/components/detail/choice_function.hpp +++ b/crypto3/libs/blueprint/include/nil/blueprint/bbf/components/detail/choice_function.hpp @@ -28,12 +28,8 @@ #ifndef CRYPTO3_BLUEPRINT_PLONK_BBF_CHOICE_FUNCTION_COMPONENT_HPP #define CRYPTO3_BLUEPRINT_PLONK_BBF_CHOICE_FUNCTION_COMPONENT_HPP -#include #include -#include -#include -#include -#include + namespace nil { namespace blueprint { diff --git a/crypto3/libs/blueprint/include/nil/blueprint/bbf/components/detail/range_check_multi.hpp b/crypto3/libs/blueprint/include/nil/blueprint/bbf/components/detail/range_check_multi.hpp index 2c65bc6e5..48b2a7803 100644 --- a/crypto3/libs/blueprint/include/nil/blueprint/bbf/components/detail/range_check_multi.hpp +++ b/crypto3/libs/blueprint/include/nil/blueprint/bbf/components/detail/range_check_multi.hpp @@ -29,12 +29,7 @@ #ifndef CRYPTO3_BBF_COMPONENTS_RANGE_CHECK_MULTI_HPP #define CRYPTO3_BBF_COMPONENTS_RANGE_CHECK_MULTI_HPP -#include #include -#include -#include -#include -#include namespace nil { namespace blueprint { @@ -57,7 +52,6 @@ namespace nil { using generic_component::copy_constrain; using generic_component::constrain; using generic_component::lookup; - using component_type = generic_component; public: using typename generic_component::TYPE; diff --git a/crypto3/libs/blueprint/include/nil/blueprint/bbf/components/pubkey/ecdsa/ecdsa_recovery.hpp b/crypto3/libs/blueprint/include/nil/blueprint/bbf/components/pubkey/ecdsa/ecdsa_recovery.hpp new file mode 100644 index 000000000..7b62256d4 --- /dev/null +++ b/crypto3/libs/blueprint/include/nil/blueprint/bbf/components/pubkey/ecdsa/ecdsa_recovery.hpp @@ -0,0 +1,765 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Alexey Yashunsky +// Copyright (c) 2025 Antoine Cyr +// +// 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. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for ECDSA public key recovery over a non-native field +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_ECDSA_RECOVERY_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_ECDSA_RECOVERY_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace bbf { + namespace components { + // Parameters: curve (in Weierstrass form, y² = x³ + a), + // num_chunks = k, bit_size_chunk = b + // Takes partial message hash z and extended ECDSA signature (r,s,V) + // Outputs + // bit c = signature is valid and + // QA = (xQA, yQA) = the recovered public key + // Expects input as k-chunked values with b bits per chunk + // Input: z[0],...,z[k-1], r[0],...,r[k-1], s[0],...,s[k-1], V + // Output: c, xQA[0],...,xQA[k-1], yQA[0],...,yQA[k-1] + // + template + struct ecdsa_recovery_raw_input { + using TYPE = typename FieldType::value_type; + std::vector z; + std::vector r; + std::vector s; + TYPE v; + }; + + template + class ecdsa_recovery : public generic_component { + using generic_component::allocate; + using generic_component::copy_constrain; + using generic_component::constrain; + + public: + using typename generic_component::TYPE; + using typename generic_component::context_type; + using typename generic_component::table_params; + using raw_input_type = + typename std::conditional, + std::tuple<>>::type; + + public: + TYPE c; + std::vector xQA; + std::vector yQA; + + static table_params get_minimal_requirements( + std::size_t num_chunks, std::size_t bit_size_chunk) { + std::size_t witness = 7 * num_chunks; + constexpr std::size_t public_inputs = 1; + constexpr std::size_t constants = 1; + constexpr std::size_t rows = 131072 - 1; + return {witness, public_inputs, constants, rows}; + } + + static std::tuple, std::vector, + std::vector, TYPE> + form_input(context_type& context_object, raw_input_type raw_input, + std::size_t num_chunks, std::size_t bit_size_chunk) { + std::vector input_z(num_chunks); + std::vector input_r(num_chunks); + std::vector input_s(num_chunks); + TYPE input_v; + + if constexpr (stage == GenerationStage::ASSIGNMENT) { + for (std::size_t i = 0; i < num_chunks; i++) { + input_z[i] = raw_input.z[i]; + input_r[i] = raw_input.r[i]; + input_s[i] = raw_input.s[i]; + } + input_v = raw_input.v; + } + for (std::size_t i = 0; i < num_chunks; i++) { + context_object.allocate(input_z[i], 0, i, + column_type::public_input); + context_object.allocate(input_r[i], 0, i + num_chunks, + column_type::public_input); + context_object.allocate(input_s[i], 0, i + 2 * num_chunks, + column_type::public_input); + } + context_object.allocate(input_v, 0, 3 * num_chunks, + column_type::public_input); + return std::make_tuple(input_z, input_r, input_s, input_v); + } + + ecdsa_recovery(context_type& context_object, + std::vector input_z, std::vector input_r, + std::vector input_s, TYPE input_v, + std::size_t num_chunks, std::size_t bit_size_chunk, + bool make_links = true) + : generic_component(context_object) { + using integral_type = typename FieldType::integral_type; + + using BaseField = typename CurveType::base_field_type; + using BASE_TYPE = typename BaseField::value_type; + using base_basic_integral_type = + typename BaseField::integral_type; + typedef nil::crypto3::multiprecision::big_uint< + 2 * BaseField::modulus_bits> + base_integral_type; + + using ScalarField = typename CurveType::scalar_field_type; + using SCALAR_TYPE = typename ScalarField::value_type; + using scalar_basic_integral_type = + typename ScalarField::integral_type; + typedef nil::crypto3::multiprecision::big_uint< + 2 * ScalarField::modulus_bits> + scalar_integral_type; + + using ec_point_value_type = typename CurveType::template g1_type< + nil::crypto3::algebra::curves::coordinates::affine>:: + value_type; + + using Choice_Function = + typename bbf::components::choice_function; + using Carry_On_addition = + typename bbf::components::carry_on_addition; + using Range_Check = + typename bbf::components::range_check_multi; + using Check_Mod_P = + typename bbf::components::check_mod_p; + + using Addition_Mod_P = + typename bbf::components::add_sub_mod_p; + using Substitution_Mod_P = + typename bbf::components::add_sub_mod_p; + using Multiplication_Mod_P = + typename bbf::components::flexible_multiplication< + FieldType, stage, BaseField>; + + using Addition_Mod_N = + typename bbf::components::add_sub_mod_p; + using Substitution_Mod_N = + typename bbf::components::add_sub_mod_p; + using Multiplication_Mod_N = + typename bbf::components::flexible_multiplication< + FieldType, stage, ScalarField>; + + using Ec_Full_Add = + typename bbf::components::ec_full_add; + using Ec_Scalar_Mult = + typename bbf::components::ec_scalar_mult; + + // Definition of constants + base_integral_type bB = base_integral_type(1) << bit_size_chunk, + p = BaseField::modulus, + b_ext_pow = base_integral_type(1) + << num_chunks * bit_size_chunk, + pp = b_ext_pow - p; + + scalar_integral_type sB = scalar_integral_type(1) + << bit_size_chunk, + n = ScalarField::modulus, + s_ext_pow = scalar_integral_type(1) + << num_chunks * bit_size_chunk, + np = s_ext_pow - n, m = (n - 1) / 2 + 1, + mp = s_ext_pow - m; + + ec_point_value_type G = ec_point_value_type::one(); + base_integral_type x = base_integral_type( + base_basic_integral_type(G.X.data)), + y = base_integral_type( + base_basic_integral_type(G.Y.data)); + BASE_TYPE a = CurveType::template g1_type< + nil::crypto3::algebra::curves::coordinates::affine>:: + params_type::b; + + size_t row = 0; + + // Helper functions for allocating constants + auto PushBaseChunks = [&context_object, &row, bB, num_chunks](base_integral_type x) { + std::vector X(num_chunks); + for (std::size_t i = 0; i < num_chunks; i++) { + X[i] = TYPE(x % bB); + context_object.allocate(X[i], 0, row, + column_type::constant); + x /= bB; + row++; + } + return X; + }; + + auto PushScalarChunks = [&context_object, &row, sB, num_chunks](scalar_integral_type x) { + std::vector X(num_chunks); + for (std::size_t i = 0; i < num_chunks; i++) { + X[i] = TYPE(x % sB); + context_object.allocate(X[i], 0, row, + column_type::constant); + x /= sB; + row++; + } + return X; + }; + + // Allocating constants + std::vector P = PushBaseChunks(p); + std::vector PP = PushBaseChunks(pp); + + std::vector N = PushScalarChunks(n); + std::vector NP = PushScalarChunks(np); + std::vector M = PushScalarChunks(m); + std::vector MP = PushScalarChunks(mp); + std::vector X = PushBaseChunks(x); + std::vector Y = PushBaseChunks(y); + base_basic_integral_type aBB = base_basic_integral_type(a.data); + base_integral_type aB = base_integral_type(aBB); + std::vector A = PushBaseChunks(aB); + + TYPE zero = 0; + TYPE one = 1; + allocate(zero, 0, row, column_type::constant); + row++; + allocate(one, 0, row, column_type::constant); + row++; + + std::vector CHUNKED_ZERO(num_chunks); + std::vector CHUNKED_ONE(num_chunks); + std::vector CHUNKED_BIT(num_chunks); + + for (std::size_t i = 0; i < num_chunks; i++) { + CHUNKED_ONE[i] = (i != 0) ? zero : one; + CHUNKED_BIT[i] = (i != 0) ? zero : 0; + CHUNKED_ZERO[i] = zero; + } + allocate(CHUNKED_BIT[0]); + + // Helper functions for subcomponents + auto RangeCheck = [&context_object, num_chunks, + bit_size_chunk](std::vector x) { + Range_Check rc = Range_Check(context_object, x, num_chunks, + bit_size_chunk); + }; + auto CheckModP = [&context_object, num_chunks, bit_size_chunk, + zero](std::vector x, + std::vector pp) { + Check_Mod_P rc = + Check_Mod_P(context_object, x, pp, zero, num_chunks, + bit_size_chunk, false); + }; + auto CheckModPOut = [&context_object, num_chunks, bit_size_chunk, + zero](std::vector x, + std::vector pp) { + Check_Mod_P rc = + Check_Mod_P(context_object, x, pp, zero, num_chunks, + bit_size_chunk, true); + return rc.output; + }; + auto CarryOnAddition = [&context_object, num_chunks, + bit_size_chunk](std::vector x, + std::vector y, + bool make_link = true) { + Carry_On_addition ca = + Carry_On_addition(context_object, x, y, num_chunks, + bit_size_chunk, make_link); + return ca; + }; + auto ChoiceFunction = + [&context_object, num_chunks, bit_size_chunk]( + TYPE q, std::vector x, std::vector y) { + Choice_Function cf = Choice_Function( + context_object, q, x, y, num_chunks, bit_size_chunk); + return cf.r; + }; + + auto CopyConstrain = [this, num_chunks](std::vector x, + std::vector y) { + for (std::size_t i = 0; i < num_chunks; i++) { + copy_constrain(x[i], y[i]); + } + }; + + auto SingleCopyConstrain = [this, num_chunks](TYPE x, TYPE y) { + copy_constrain(x, y); + }; + + auto SubModP = [&context_object, P, PP, zero, num_chunks, + bit_size_chunk](std::vector x, std::vector y) { + Substitution_Mod_P t = + Substitution_Mod_P(context_object, x,y, P, PP, zero, + num_chunks, bit_size_chunk); + return t.r; + }; + + auto AddModP = [&context_object, P, PP, zero, num_chunks, + bit_size_chunk](std::vector x, + std::vector y) { + Addition_Mod_P t = + Addition_Mod_P(context_object, x, y, P, PP, zero, + num_chunks, bit_size_chunk); + return t.r; + }; + + auto MultModP = [&context_object, P, PP, zero, num_chunks, + bit_size_chunk](std::vector x, + std::vector y) { + Multiplication_Mod_P t = + Multiplication_Mod_P(context_object, x, y, P, PP, zero, + num_chunks, bit_size_chunk); + return t.r; + }; + + auto SubModN = [&context_object, N, NP, zero, num_chunks, + bit_size_chunk](std::vector x, + std::vector y) { + Substitution_Mod_N t = + Substitution_Mod_N(context_object, x,y, N, NP, zero, + num_chunks, bit_size_chunk); + return t.r; + }; + + auto AddModN = [&context_object, N, NP, zero, num_chunks, + bit_size_chunk](std::vector x, + std::vector y) { + Addition_Mod_N t = + Addition_Mod_N(context_object, x, y, N, NP, zero, + num_chunks, bit_size_chunk); + return t.r; + }; + + auto MultModN = [&context_object, N, NP, zero, num_chunks, + bit_size_chunk](std::vector x, + std::vector y) { + Multiplication_Mod_N t = + Multiplication_Mod_N(context_object, x, y, N, NP, zero, + num_chunks, bit_size_chunk); + return t.r; + }; + + auto ECFullAdd = [&context_object, P, PP, zero, + num_chunks, bit_size_chunk]( + std::vector xP, std::vector yP, + std::vector xQ, std::vector yQ) { + Ec_Full_Add t = + Ec_Full_Add(context_object, xP, yP, xQ, yQ, P, PP, + zero, num_chunks, bit_size_chunk); + return t; + }; + + auto ECScalarMult = [&context_object, P, PP, N, MP, zero, + num_chunks, bit_size_chunk]( + std::vector s, std::vector x, + std::vector y) { + Ec_Scalar_Mult t = + Ec_Scalar_Mult(context_object, s, x, y, P, PP, N, MP, + zero, num_chunks, bit_size_chunk); + return t; + }; + + auto CopyChunks = [num_chunks](std::vector& from, + std::vector& to) { + for (std::size_t i = 0; i < num_chunks; i++) { + to[i] = from[i]; + } + }; + + + + // Declaring intermediate values + std::vector Z(num_chunks); + std::vector R(num_chunks); + std::vector S(num_chunks); + TYPE V; + + std::vector C(9); // the c bits, c[0] = c[1]*...*c[8] + std::vector XQA(num_chunks); + std::vector YQA(num_chunks); + std::vector U1(num_chunks); + std::vector U2(num_chunks); + std::vector XR(num_chunks); + std::vector YR(num_chunks); + std::vector I1(num_chunks); + std::vector I3(num_chunks); + std::vector I6(num_chunks); + std::vector I5(num_chunks); + std::vector D2(num_chunks); + std::vector I8(num_chunks); + + ec_point_value_type QA; + + // Assigning intermediate values + if constexpr (stage == GenerationStage::ASSIGNMENT) { + for (std::size_t i = 0; i < num_chunks; i++) { + Z[i] = input_z[i]; + R[i] = input_r[i]; + S[i] = input_s[i]; + } + V = input_v.data; + + integral_type pow = 1; + SCALAR_TYPE z = 0, r = 0, s = 0; + + for (std::size_t i = 0; i < num_chunks; ++i) { + z += + scalar_integral_type(integral_type(input_z[i].data)) * + pow; + r += + scalar_integral_type(integral_type(input_r[i].data)) * + pow; + s += + scalar_integral_type(integral_type(input_s[i].data)) * + pow; + pow <<= bit_size_chunk; + } + SCALAR_TYPE i1, i3, i6; + BASE_TYPE i5, d2, i8; + + // the computations + C[1] = 1 - r.is_zero(); + i1 = r.is_zero() ? 0 : r.inversed(); + + C[2] = (scalar_basic_integral_type(r.data) < n) ? 1 : 0; + + C[3] = 1 - s.is_zero(); + i3 = s.is_zero() ? 0 : s.inversed(); + + C[4] = (scalar_basic_integral_type(s.data) < m) ? 1 : 0; + + BASE_TYPE x1 = scalar_basic_integral_type( + r.data); // should we consider r + n also? + BASE_TYPE y1 = + (x1 * x1 * x1 + a).is_square() + ? (x1 * x1 * x1 + a).sqrt() + : 1; // should be signaled as invalid signaure + if (base_basic_integral_type(y1.data) % 2 != + scalar_basic_integral_type(V.data) % 2) { + y1 = -y1; + } + C[5] = (x1 * x1 * x1 + a - y1 * y1).is_zero(); + i5 = (x1 * x1 * x1 + a - y1 * y1).is_zero() + ? 0 + : (x1 * x1 * x1 + a - y1 * y1).inversed(); + + C[6] = (SCALAR_TYPE(base_basic_integral_type(x1.data)) - r) + .is_zero(); + i6 = + (SCALAR_TYPE(base_basic_integral_type(x1.data)) - r) + .is_zero() + ? 0 + : (SCALAR_TYPE(base_basic_integral_type(x1.data)) - r) + .inversed(); + + C[7] = ((base_basic_integral_type(y1.data) % 2) == + (scalar_basic_integral_type(V.data) % 2)); + d2 = (base_basic_integral_type(y1.data) + + base_basic_integral_type( + scalar_basic_integral_type(V.data))) / + 2; + + SCALAR_TYPE + u1 = r.is_zero() + ? 2 + : -z * r.inversed(), // if r = 0, the signature + // is invalid, but we + u2 = r.is_zero() + ? 2 + : s * r.inversed(); // don't wanto to break the + // scalar multiplication + ec_point_value_type R = + ec_point_value_type(scalar_basic_integral_type(x1.data), + scalar_basic_integral_type(y1.data)); + QA = G * u1 + R * u2; + C[8] = 1 - QA.is_zero(); + + i8 = QA.Y.is_zero() ? 0 : QA.Y.inversed(); + + C[0] = C[1] * C[2] * C[3] * C[4] * C[5] * C[6] * C[7] * C[8]; + + auto PushScalarValueChunks = [&context_object, &row, sB, + num_chunks](SCALAR_TYPE x) { + std::vector X(num_chunks); + scalar_integral_type x_value = + scalar_basic_integral_type(x.data); + for (std::size_t i = 0; i < num_chunks; i++) { + X[i] = x_value % sB; + x_value /= sB; + } + + return X; + }; + + auto PushBaseValueChunks = [&context_object, &row, bB, + num_chunks](BASE_TYPE x) { + std::vector X(num_chunks); + base_integral_type x_value = + base_basic_integral_type(x.data); + for (std::size_t i = 0; i < num_chunks; i++) { + X[i] = x_value % bB; + x_value /= bB; + } + return X; + }; + + XQA = PushBaseValueChunks(QA.X); + YQA = PushBaseValueChunks(QA.Y); + U1 = PushScalarValueChunks(u1); + U2 = PushScalarValueChunks(u2); + XR = PushBaseValueChunks(R.X); + YR = PushBaseValueChunks(R.Y); + + I1 = PushScalarValueChunks(i1); + I3 = PushScalarValueChunks(i3); + I6 = PushScalarValueChunks(i6); + + I5 = PushBaseValueChunks(i5); + D2 = PushBaseValueChunks(d2); + I8 = PushBaseValueChunks(i8); + } + + // Allocating intermediate values + for (std::size_t i = 0; i < 9; i++) { + allocate(C[i]); + } + for (std::size_t i = 0; i < num_chunks; i++) { + allocate(I1[i]); + allocate(R[i]); + allocate(I3[i]); + allocate(S[i]); + allocate(XR[i]); + allocate(YR[i]); + allocate(I5[i]); + allocate(I6[i]); + allocate(D2[i]); + allocate(U1[i]); + allocate(Z[i]); + allocate(U2[i]); + allocate(S[i]); + allocate(I8[i]); + allocate(XQA[i]); + allocate(YQA[i]); + } + allocate(V); + + // Start of the circuit constraints + + // c1 = [r != 0] + RangeCheck(I1); + CheckModP(I1, NP); // CheckModN + auto t0 = AddModN(R, CHUNKED_ZERO); + auto t1 = MultModN(t0, I1); + auto t2 = MultModN(t0, t1); + CopyConstrain(t0, t2); + CHUNKED_BIT[0] = C[1]; + CopyConstrain(t1,CHUNKED_BIT); // t1 = (0,...,0,c1) + + // c2 = [r < n] + auto t3 = CheckModPOut(R, NP); // CheckModN + auto t3p = ChoiceFunction(C[2], CHUNKED_ONE, CHUNKED_ZERO); + CHUNKED_BIT[0] = t3; + CopyConstrain(CHUNKED_BIT, t3p); // (0,...,0,t3) = t3p + + // c3 = [s != 0] + RangeCheck(I3); + CheckModP(I3, NP); // CheckModN + auto t4 = AddModN(S, CHUNKED_ZERO); + auto t5 = MultModN(t4, I3); + auto t6 = MultModN(t4, t5); + CopyConstrain(t4, t6); + CHUNKED_BIT[0] = C[3]; + CopyConstrain(t5,CHUNKED_BIT); // t5 = (0,...,0,c3) + + // c4 = [s < (n-1)/2+1] + auto t7 = CheckModPOut(S, MP); // CheckModM + auto t7p = ChoiceFunction(C[4], CHUNKED_ONE, CHUNKED_ZERO); + CHUNKED_BIT[0] = t7; + CopyConstrain(CHUNKED_BIT, t7p); // (0,...,0,t7) = t7p + + // c5 = [yR^2 = xR^3 + a] + RangeCheck(XR); + CheckModP(XR, PP); + RangeCheck(YR); + CheckModP(YR, PP); + auto t8 = MultModP(XR, XR); + auto t9 = MultModP(t8, XR); + auto t10 = AddModP(t9, A); + auto t11 = MultModP(YR, YR); + + auto t12 = SubModP(t10, t11); + RangeCheck(I5); + CheckModP(I5, PP); + auto t13 = MultModP(t12, I5); + auto t13p = ChoiceFunction(C[5], CHUNKED_ONE, CHUNKED_ZERO); + auto t14 = MultModP(t12, t13); + CopyConstrain(t12, t14); + CopyConstrain(t13, t13p); + + // c6 = [xR = r (mod n)] + auto t15 = AddModN(XR, CHUNKED_ZERO); + auto t16 = SubModN(t15, t0); + RangeCheck(I6); + CheckModP(I6, NP); // CheckModN + auto t17 = MultModN(t16, I6); + auto t18 = MultModN(t16, t17); + CopyConstrain(t16, t18); + auto t19 = ChoiceFunction(C[6], CHUNKED_ONE, CHUNKED_ZERO); + CopyConstrain(t17, t19); + + // c7 = [yR = V (mod 2)] + CHUNKED_BIT[0] = V; + RangeCheck(CHUNKED_BIT); + auto d1 = CarryOnAddition(YR, CHUNKED_BIT); + SingleCopyConstrain(d1.c, zero); + RangeCheck(D2); + auto d3 = CarryOnAddition(D2, CHUNKED_ONE); + SingleCopyConstrain(d3.c, zero); + RangeCheck(d3.r); + auto d4 = ChoiceFunction(C[7], d3.r, D2); + auto t20 = CarryOnAddition(D2, d4); + SingleCopyConstrain(t20.c, zero); + CopyConstrain(t20.r, d1.r); + + // u1 r = -z (mod n) + RangeCheck(U1); + CheckModP(U1, NP); // CheckModN + auto t21 = MultModN(U1, t0); + auto t22 = AddModN(Z, CHUNKED_ZERO); + auto t23 = MultModN(t22, t1); + auto t24 = AddModN(t21, t23); + CopyConstrain(t24, CHUNKED_ZERO); // t24 = 0 + + // u2 r = s (mod n) + RangeCheck(U2); + CheckModP(U2, NP); // CheckModN + auto t25 = MultModN(U2, t0); + auto t26 = MultModN(S, t1); + CopyConstrain(t25, t26); + + // u1 * G + auto t27 = ECScalarMult(U1, X, Y); + + // u2 * R + auto t28 = ECScalarMult(U2, XR, YR); + + // QA = u1*G + u2*R + auto t29 = ECFullAdd(t27.xR, t27.yR, t28.xR, t28.yR); + + // to assure the circuit doesn't break for invalid signatures we + // have to place the results from t29 to (xQA, yQA) + + if constexpr (stage == GenerationStage::ASSIGNMENT) { + BASE_TYPE new_yQA = 0; + for (std::size_t i = num_chunks; i > 0; i--) { + new_yQA *= sB; + new_yQA += integral_type(t29.yR[i - 1].data); + } + if (QA.Y != new_yQA) { // we also have to adjust I8, c8 and + // c0 to agree with the updated yQA + BASE_TYPE new_I8 = + new_yQA.is_zero() ? 0 : new_yQA.inversed(); + base_integral_type new_I8_int = + base_basic_integral_type(new_I8.data); + for (std::size_t i = 0; i < num_chunks; i++) { + I8[i] = new_I8_int % bB; + new_I8_int /= bB; + } + // update c8 + C[8] = TYPE(1 - new_yQA.is_zero()); + // update c0 + C[0] = TYPE(C[0] * (1 - new_yQA.is_zero())); + } + } + // copy constrain QA = t29 + CopyConstrain(XQA, t29.xR); + CopyConstrain(YQA, t29.yR); + + // c8 = [QA != O] + RangeCheck(I8); + CheckModP(I8, PP); + auto t30 = MultModP(YQA, I8); + auto t31 = MultModP(YQA, t30); + CopyConstrain(YQA, t31); + CHUNKED_BIT[0] = C[8]; + CopyConstrain(t30, CHUNKED_BIT); // t30 = (0,...,0,c8) + + // c = c[1]*....*c[8] + CHUNKED_BIT[0] = C[1]; + auto t32 = ChoiceFunction(C[2], CHUNKED_ZERO, CHUNKED_BIT); + auto t33 = ChoiceFunction(C[3], CHUNKED_ZERO, t32); + auto t34 = ChoiceFunction(C[4], CHUNKED_ZERO, t33); + auto t35 = ChoiceFunction(C[5], CHUNKED_ZERO, t34); + auto t36 = ChoiceFunction(C[6], CHUNKED_ZERO, t35); + auto t37 = ChoiceFunction(C[7], CHUNKED_ZERO, t36); + auto t38 = ChoiceFunction(C[8], CHUNKED_ZERO, t37); + CopyConstrain(t38, CHUNKED_BIT); // t38 = (0,...,0,c) + + for (int i = 0; i < num_chunks; ++i) { + xQA.push_back(XQA[i]); + yQA.push_back(YQA[i]); + } + c = C[0]; + } + }; + + template + class pallas_ecdsa_recovery + : public ecdsa_recovery { + using Base = ecdsa_recovery; + + public: + using Base::Base; + }; + + template + class vesta_ecdsa_recovery + : public ecdsa_recovery { + using Base = + ecdsa_recovery; + + public: + using Base::Base; + }; + + } // namespace components + } // namespace bbf + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_ECDSA_RECOVERY_HPP diff --git a/crypto3/libs/blueprint/test/CMakeLists.txt b/crypto3/libs/blueprint/test/CMakeLists.txt index 8ef649a41..edfff9106 100644 --- a/crypto3/libs/blueprint/test/CMakeLists.txt +++ b/crypto3/libs/blueprint/test/CMakeLists.txt @@ -102,13 +102,16 @@ set(COMMON_TEST_FILES "bbf/detail/range_check_multi" "bbf/detail/carry_on_addition" "bbf/detail/choice_function" - "bbf/algebra/fields/non_native/addition_mod_p" + "bbf/algebra/fields/non_native/add_sub_mod_p" "bbf/algebra/fields/non_native/check_mod_p" "bbf/algebra/fields/non_native/negation_mod_p" "bbf/algebra/fields/non_native/flexible_multiplication" "bbf/algebra/curves/weierstrass/ec_double" "bbf/algebra/curves/weierstrass/ec_full_add" "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 diff --git a/crypto3/libs/blueprint/test/bbf/algebra/curves/weierstrass/ec_double.cpp b/crypto3/libs/blueprint/test/bbf/algebra/curves/weierstrass/ec_double.cpp index 8e68a36dc..8062818e7 100644 --- a/crypto3/libs/blueprint/test/bbf/algebra/curves/weierstrass/ec_double.cpp +++ b/crypto3/libs/blueprint/test/bbf/algebra/curves/weierstrass/ec_double.cpp @@ -1,5 +1,5 @@ //---------------------------------------------------------------------------// -// Copyright (c) 2024 Antoine Cyr +// Copyright (c) 2025 Antoine Cyr // // MIT License // @@ -25,15 +25,10 @@ #define BOOST_TEST_MODULE bbf_ec_double_test #include -#include -#include -#include - #include - +#include #include #include -#include #include using namespace nil; @@ -76,8 +71,7 @@ void test_ec_double( public_input.begin() + 3 * num_chunks); raw_input.pp = std::vector(public_input.begin() + 3 * num_chunks, public_input.begin() + 4 * num_chunks); - raw_input.zero = std::vector(public_input.begin() + 4 * num_chunks, - public_input.begin() + 5 * num_chunks); + raw_input.zero = public_input.back(); auto [at, A, desc] = B.assign(raw_input); bool pass = B.is_satisfied(at); @@ -92,11 +86,11 @@ void test_ec_double( yR += non_native_integral_type(integral_type(A.yR[i].data)) * pow; pow <<= bit_size_chunk; } - #ifdef BLUEPRINT_PLONK_PROFILING_ENABLED +#ifdef BLUEPRINT_PLONK_PROFILING_ENABLED std::cout << "Expected xR - yR: " << std::dec << expected_xR.data << " - " << expected_yR.data << std::endl; std::cout << "Real res xR - yR: " << std::dec << xR << " - " << yR << std::endl; - #endif +#endif assert(xR == expected_xR.data); assert(yR == expected_yR.data); }; @@ -170,9 +164,8 @@ void ec_double_tests() { public_input[3 * num_chunks + j] = value_type(pp & mask); pp >>= bit_size_chunk; - - public_input[4 * num_chunks + j] = value_type(0); } + public_input[4 * num_chunks] = value_type(0); test_ec_double(public_input); @@ -184,7 +177,7 @@ constexpr static const std::size_t random_tests_amount = 10; BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) BOOST_AUTO_TEST_CASE(blueprint_plonk_bbf_ec_double_test) { - // The curve is passed in as an argument to access additionnal properties + // The curve is passed in as an argument to access additionnal properties using pallas = typename crypto3::algebra::curves::pallas; using vesta = typename crypto3::algebra::curves::vesta; diff --git a/crypto3/libs/blueprint/test/bbf/algebra/curves/weierstrass/ec_full_add.cpp b/crypto3/libs/blueprint/test/bbf/algebra/curves/weierstrass/ec_full_add.cpp index 903929433..9885e34fe 100644 --- a/crypto3/libs/blueprint/test/bbf/algebra/curves/weierstrass/ec_full_add.cpp +++ b/crypto3/libs/blueprint/test/bbf/algebra/curves/weierstrass/ec_full_add.cpp @@ -1,5 +1,5 @@ //---------------------------------------------------------------------------// -// Copyright (c) 2024 Antoine Cyr +// Copyright (c) 2025 Antoine Cyr // // MIT License // @@ -25,15 +25,10 @@ #define BOOST_TEST_MODULE bbf_ec_full_add_test #include -#include -#include -#include - #include - +#include #include #include -#include #include using namespace nil; @@ -112,8 +107,7 @@ void test_ec_full_add( public_input.begin() + 5 * num_chunks); raw_input.pp = std::vector(public_input.begin() + 5 * num_chunks, public_input.begin() + 6 * num_chunks); - raw_input.zero = std::vector(public_input.begin() + 6 * num_chunks, - public_input.begin() + 7 * num_chunks); + raw_input.zero = public_input.back(); auto [at, A, desc] = B.assign(raw_input); bool pass = B.is_satisfied(at); @@ -128,11 +122,11 @@ void test_ec_full_add( yR += non_native_integral_type(integral_type(A.yR[i].data)) * pow; pow <<= bit_size_chunk; } - #ifdef BLUEPRINT_PLONK_PROFILING_ENABLED +#ifdef BLUEPRINT_PLONK_PROFILING_ENABLED std::cout << "Expected xR - yR: " << std::dec << expected_xR.data << " - " << expected_yR.data << std::endl; std::cout << "Real res xR - yR: " << std::dec << xR << " - " << yR << std::endl; - #endif +#endif assert(xR == expected_xR.data); assert(yR == expected_yR.data); }; @@ -216,9 +210,8 @@ void ec_full_add_tests() { public_input[5 * num_chunks + j] = value_type(pp & mask); pp >>= bit_size_chunk; - - public_input[6 * num_chunks + j] = value_type(0); } + public_input[6 * num_chunks] = value_type(0); test_ec_full_add(public_input); @@ -230,7 +223,7 @@ constexpr static const std::size_t random_tests_amount = 10; BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) BOOST_AUTO_TEST_CASE(blueprint_plonk_bbf_ec_full_add_test) { - // The curve is passed in as an argument to access additionnal properties + // The curve is passed in as an argument to access additionnal properties using pallas = typename crypto3::algebra::curves::pallas; using vesta = typename crypto3::algebra::curves::vesta; diff --git a/crypto3/libs/blueprint/test/bbf/algebra/curves/weierstrass/ec_incomplete_add.cpp b/crypto3/libs/blueprint/test/bbf/algebra/curves/weierstrass/ec_incomplete_add.cpp index 1cbb6f7ed..bac308270 100644 --- a/crypto3/libs/blueprint/test/bbf/algebra/curves/weierstrass/ec_incomplete_add.cpp +++ b/crypto3/libs/blueprint/test/bbf/algebra/curves/weierstrass/ec_incomplete_add.cpp @@ -1,5 +1,5 @@ //---------------------------------------------------------------------------// -// Copyright (c) 2024 Antoine Cyr +// Copyright (c) 2025 Antoine Cyr // // MIT License // @@ -25,15 +25,10 @@ #define BOOST_TEST_MODULE bbf_ec_incomplete_add_test #include -#include -#include -#include - #include - +#include #include #include -#include #include using namespace nil; @@ -83,8 +78,7 @@ void test_ec_incomplete_add( public_input.begin() + 5 * num_chunks); raw_input.pp = std::vector(public_input.begin() + 5 * num_chunks, public_input.begin() + 6 * num_chunks); - raw_input.zero = std::vector(public_input.begin() + 6 * num_chunks, - public_input.begin() + 7 * num_chunks); + raw_input.zero = public_input.back(); auto [at, A, desc] = B.assign(raw_input); bool pass = B.is_satisfied(at); @@ -187,9 +181,8 @@ void ec_incomplete_add_tests() { public_input[5 * num_chunks + j] = value_type(pp & mask); pp >>= bit_size_chunk; - - public_input[6 * num_chunks + j] = value_type(0); } + public_input[6 * num_chunks] = value_type(0); test_ec_incomplete_add(public_input); @@ -201,7 +194,7 @@ constexpr static const std::size_t random_tests_amount = 10; BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) BOOST_AUTO_TEST_CASE(blueprint_plonk_bbf_ec_incomplete_add_test) { - // The curve is passed in as an argument to access additionnal properties + // The curve is passed in as an argument to access additionnal properties using pallas = typename crypto3::algebra::curves::pallas; using vesta = typename crypto3::algebra::curves::vesta; diff --git a/crypto3/libs/blueprint/test/bbf/algebra/curves/weierstrass/ec_scalar_mult.cpp b/crypto3/libs/blueprint/test/bbf/algebra/curves/weierstrass/ec_scalar_mult.cpp new file mode 100644 index 000000000..58288b0ef --- /dev/null +++ b/crypto3/libs/blueprint/test/bbf/algebra/curves/weierstrass/ec_scalar_mult.cpp @@ -0,0 +1,197 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2025 Antoine Cyr +// +// 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_ec_scalar_mult_test + +#include +#include +#include +#include +#include +#include + +using namespace nil; +using namespace nil::blueprint; + +template +void test_ec_scalar_mult( + const std::vector& public_input, + typename BlueprintFieldType::integral_type expected_xR, + typename BlueprintFieldType::integral_type expected_yR) { + using FieldType = BlueprintFieldType; + using TYPE = typename FieldType::value_type; + using integral_type = typename BlueprintFieldType::integral_type; + using non_native_integral_type = typename BlueprintFieldType::integral_type; + + auto assign_and_check = [&](auto& B, auto& raw_input) { + raw_input.s = + std::vector(public_input.begin(), public_input.begin() + num_chunks); + raw_input.x = std::vector(public_input.begin() + num_chunks, + public_input.begin() + 2 * num_chunks); + raw_input.y = std::vector(public_input.begin() + 2 * num_chunks, + public_input.begin() + 3 * num_chunks); + raw_input.p = std::vector(public_input.begin() + 3 * num_chunks, + public_input.begin() + 4 * num_chunks); + raw_input.pp = std::vector(public_input.begin() + 4 * num_chunks, + public_input.begin() + 5 * num_chunks); + raw_input.n = std::vector(public_input.begin() + 5 * num_chunks, + public_input.begin() + 6 * num_chunks); + raw_input.mp = std::vector(public_input.begin() + 6 * num_chunks, + public_input.begin() + 7 * num_chunks); + raw_input.zero = public_input.back(); + + auto [at, A, desc] = B.assign(raw_input); + bool pass = B.is_satisfied(at); + std::cout << "Is_satisfied = " << pass << std::endl; + + assert(pass == true); + non_native_integral_type xR = 0, yR = 0, pow = 1; + for (std::size_t i = 0; i < num_chunks; i++) { + xR += non_native_integral_type(integral_type(A.xR[i].data)) * pow; + yR += non_native_integral_type(integral_type(A.yR[i].data)) * pow; + pow <<= bit_size_chunk; + } +#ifdef BLUEPRINT_PLONK_PROFILING_ENABLED + std::cout << "Expected xR - yR: " << std::dec << expected_xR << " - " + << expected_yR << std::endl; + std::cout << "Real res xR - yR: " << std::dec << xR << " - " << yR << std::endl; +#endif + assert(xR == expected_xR); + assert(yR == expected_yR); + }; + + if constexpr (std::is_same_v) { + typename bbf::components::pallas_ec_scalar_mult< + FieldType, bbf::GenerationStage::ASSIGNMENT>::raw_input_type raw_input; + + auto B = + bbf::circuit_builder(num_chunks, bit_size_chunk); + + assign_and_check(B, raw_input); + } else if constexpr (std::is_same_v< + NonNativeFieldType, + crypto3::algebra::curves::vesta::base_field_type>) { + typename bbf::components::vesta_ec_scalar_mult< + FieldType, bbf::GenerationStage::ASSIGNMENT>::raw_input_type raw_input; + auto B = + bbf::circuit_builder(num_chunks, bit_size_chunk); + + assign_and_check(B, raw_input); + } +} + +template +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 CurveType::template g1_type< + nil::crypto3::algebra::curves::coordinates::affine>::value_type; + using scalar_value_type = typename CurveType::scalar_field_type::value_type; + + typedef nil::crypto3::multiprecision::big_uint<2 * + CurveType::base_field_type::modulus_bits> + extended_integral_type; + typedef nil::crypto3::multiprecision::big_uint<2 * + CurveType::scalar_field_type::modulus_bits> + scalar_integral_type; + + nil::crypto3::random::algebraic_engine + generate_random_scalar; + boost::random::mt19937 seed_seq; + generate_random_scalar.seed(seed_seq); + + extended_integral_type mask = (extended_integral_type(1) << bit_size_chunk) - 1; + + for (std::size_t i = 0; i < RandomTestsAmount; i++) { + std::vector public_input; + + extended_integral_type extended_base = 1, + ext_pow = extended_base << (num_chunks * bit_size_chunk), + p = CurveType::base_field_type::modulus, pp = ext_pow - p; + + 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; + + scalar_value_type S = generate_random_scalar(), D = generate_random_scalar(); + ec_point_value_type P = ec_point_value_type::one() * D, R = P * S; + + public_input.resize(8 * num_chunks); + integral_type s = integral_type(S.data); + integral_type x = integral_type(P.X.data); + integral_type y = integral_type(P.Y.data); + integral_type xR = integral_type(R.X.data); + integral_type yR = integral_type(R.Y.data); + for (std::size_t j = 0; j < num_chunks; j++) { + public_input[j] = value_type(s & mask); + s >>= bit_size_chunk; + + public_input[num_chunks + j] = value_type(x & mask); + x >>= bit_size_chunk; + + public_input[2 * num_chunks + j] = value_type(y & mask); + y >>= bit_size_chunk; + + public_input[3 * num_chunks + j] = value_type(p & mask); + p >>= bit_size_chunk; + + public_input[4 * num_chunks + j] = value_type(pp & mask); + pp >>= bit_size_chunk; + + public_input[5 * num_chunks + j] = value_type(n & mask); + n >>= bit_size_chunk; + + public_input[6 * num_chunks + j] = value_type(mp & mask); + mp >>= bit_size_chunk; + } + + public_input[7 * num_chunks] = value_type(0); + + test_ec_scalar_mult(public_input, xR, yR); + } +} + +constexpr static const std::size_t random_tests_amount = 1; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_bbf_ec_scalar_mult_test) { + // The curve is passed in as an argument to access additionnal properties + using pallas = typename crypto3::algebra::curves::pallas; + using vesta = typename crypto3::algebra::curves::vesta; + + + ec_scalar_mult_tests(); + ec_scalar_mult_tests(); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/crypto3/libs/blueprint/test/bbf/algebra/curves/weierstrass/ec_two_t_plus_q.cpp b/crypto3/libs/blueprint/test/bbf/algebra/curves/weierstrass/ec_two_t_plus_q.cpp new file mode 100644 index 000000000..53dca9858 --- /dev/null +++ b/crypto3/libs/blueprint/test/bbf/algebra/curves/weierstrass/ec_two_t_plus_q.cpp @@ -0,0 +1,213 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2025 Antoine Cyr +// +// 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_ec_two_t_plus_q_test + +#include +#include +#include +#include +#include +#include + +using namespace nil; +using namespace nil::blueprint; + +template +void test_ec_two_t_plus_q( + const std::vector& public_input) { + using FieldType = BlueprintFieldType; + using TYPE = typename FieldType::value_type; + using NON_NATIVE_TYPE = typename NonNativeFieldType::value_type; + using integral_type = typename BlueprintFieldType::integral_type; + using non_native_integral_type = typename BlueprintFieldType::integral_type; + + non_native_integral_type pow = 1; + + NON_NATIVE_TYPE xT = 0, yT = 0, xQ = 0, yQ = 0; + + for (std::size_t i = 0; i < num_chunks; ++i) { + xT += non_native_integral_type(integral_type(public_input[i].data)) * pow; + yT += non_native_integral_type(integral_type(public_input[i + num_chunks].data)) * + pow; + xQ += non_native_integral_type( + integral_type(public_input[i + 2 * num_chunks].data)) * + pow; + yQ += non_native_integral_type( + integral_type(public_input[i + 3 * num_chunks].data)) * + pow; + pow <<= bit_size_chunk; + } + + NON_NATIVE_TYPE diff1 = xQ - xT, + lambda = (diff1 == 0) ? 0 : (yQ - yT) * diff1.inversed(), + xS = lambda * lambda - xT - xQ, diff2 = xS - xT, + mu = (diff2 == 0) ? -lambda : -lambda - (2 * yT) * diff2.inversed(), + expected_xR = mu * mu - xT - xS, + expected_yR = mu * (xT - expected_xR) - yT; + + auto assign_and_check = [&](auto& B, auto& raw_input) { + raw_input.xT = + std::vector(public_input.begin(), public_input.begin() + num_chunks); + raw_input.yT = std::vector(public_input.begin() + num_chunks, + public_input.begin() + 2 * num_chunks); + raw_input.xQ = std::vector(public_input.begin() + 2 * num_chunks, + public_input.begin() + 3 * num_chunks); + raw_input.yQ = std::vector(public_input.begin() + 3 * num_chunks, + public_input.begin() + 4 * num_chunks); + raw_input.p = std::vector(public_input.begin() + 4 * num_chunks, + public_input.begin() + 5 * num_chunks); + raw_input.pp = std::vector(public_input.begin() + 5 * num_chunks, + public_input.begin() + 6 * num_chunks); + raw_input.zero = public_input.back(); + + auto [at, A, desc] = B.assign(raw_input); + bool pass = B.is_satisfied(at); + std::cout << "Is_satisfied = " << pass << std::endl; + + assert(pass == true); + non_native_integral_type xR = 0; + non_native_integral_type yR = 0; + pow = 1; + for (std::size_t i = 0; i < num_chunks; i++) { + xR += non_native_integral_type(integral_type(A.xR[i].data)) * pow; + yR += non_native_integral_type(integral_type(A.yR[i].data)) * pow; + pow <<= bit_size_chunk; + } + // #ifdef BLUEPRINT_PLONK_PROFILING_ENABLED + std::cout << "Expected xR - yR: " << std::dec << expected_xR.data << " - " + << expected_yR.data << std::endl; + std::cout << "Real res xR - yR: " << std::dec << xR << " - " << yR << std::endl; + // #endif + assert(xR == expected_xR.data); + assert(yR == expected_yR.data); + }; + + if constexpr (std::is_same_v) { + typename bbf::components::pallas_ec_two_t_plus_q< + FieldType, bbf::GenerationStage::ASSIGNMENT>::raw_input_type raw_input; + + auto B = + bbf::circuit_builder(num_chunks, bit_size_chunk); + + assign_and_check(B, raw_input); + } else if constexpr (std::is_same_v< + NonNativeFieldType, + crypto3::algebra::curves::vesta::base_field_type>) { + typename bbf::components::vesta_ec_two_t_plus_q< + FieldType, bbf::GenerationStage::ASSIGNMENT>::raw_input_type raw_input; + auto B = + bbf::circuit_builder(num_chunks, bit_size_chunk); + + assign_and_check(B, raw_input); + } +} + +template +void ec_two_t_plus_q_tests() { + using NonNativeFieldType = typename Curve::base_field_type; + using value_type = typename BlueprintFieldType::value_type; + using integral_type = typename BlueprintFieldType::integral_type; + using foreign_value_type = typename NonNativeFieldType::value_type; + using ec_point_value_type = typename Curve::template g1_type< + nil::crypto3::algebra::curves::coordinates::affine>::value_type; + + typedef nil::crypto3::multiprecision::big_uint<2 * NonNativeFieldType::modulus_bits> + extended_integral_type; + + static boost::random::mt19937 seed_seq; + static nil::crypto3::random::algebraic_engine generate_random( + seed_seq); + boost::random::uniform_int_distribution<> t_dist(0, 1); + + extended_integral_type mask = (extended_integral_type(1) << bit_size_chunk) - 1; + + for (std::size_t i = 0; i < RandomTestsAmount; i++) { + std::vector public_input; + + extended_integral_type extended_base = 1, + ext_pow = extended_base << (num_chunks * bit_size_chunk), + p = NonNativeFieldType::modulus, pp = ext_pow - p; + + value_type d = generate_random(); + ec_point_value_type T = ec_point_value_type::one(), + Q = ec_point_value_type::one(); + T = T * d; + Q = Q * d; + + public_input.resize(7 * num_chunks); + integral_type xT = integral_type(T.X.data); + integral_type yT = integral_type(T.Y.data); + integral_type xQ = integral_type(Q.X.data); + integral_type yQ = integral_type(Q.Y.data); + for (std::size_t j = 0; j < num_chunks; j++) { + public_input[j] = value_type(xT & mask); + xT >>= bit_size_chunk; + + public_input[1 * num_chunks + j] = value_type(yT & mask); + yT >>= bit_size_chunk; + + public_input[2 * num_chunks + j] = value_type(xQ & mask); + xQ >>= bit_size_chunk; + + public_input[3 * num_chunks + j] = value_type(yQ & mask); + yQ >>= bit_size_chunk; + + public_input[4 * num_chunks + j] = value_type(p & mask); + p >>= bit_size_chunk; + + public_input[5 * num_chunks + j] = value_type(pp & mask); + pp >>= bit_size_chunk; + } + public_input[6 * num_chunks] = value_type(0); + + test_ec_two_t_plus_q(public_input); + } +} + +constexpr static const std::size_t random_tests_amount = 10; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_bbf_ec_two_t_plus_q_test) { + // The curve is passed in as an argument to access additionnal properties + using pallas = typename crypto3::algebra::curves::pallas; + using vesta = typename crypto3::algebra::curves::vesta; + + ec_two_t_plus_q_tests(); + + ec_two_t_plus_q_tests(); + + ec_two_t_plus_q_tests(); + + ec_two_t_plus_q_tests(); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/crypto3/libs/blueprint/test/bbf/algebra/fields/non_native/addition_mod_p.cpp b/crypto3/libs/blueprint/test/bbf/algebra/fields/non_native/add_sub_mod_p.cpp similarity index 75% rename from crypto3/libs/blueprint/test/bbf/algebra/fields/non_native/addition_mod_p.cpp rename to crypto3/libs/blueprint/test/bbf/algebra/fields/non_native/add_sub_mod_p.cpp index 8b8e7d98b..c10d1fe17 100644 --- a/crypto3/libs/blueprint/test/bbf/algebra/fields/non_native/addition_mod_p.cpp +++ b/crypto3/libs/blueprint/test/bbf/algebra/fields/non_native/add_sub_mod_p.cpp @@ -1,6 +1,5 @@ //---------------------------------------------------------------------------// -// Copyright (c) 2024 Valeh Farzaliyev -// Copyright (c) 2024 Antoine Cyr +// Copyright (c) 2025 Antoine Cyr // // MIT License // @@ -23,13 +22,11 @@ // SOFTWARE. //---------------------------------------------------------------------------// -#define BOOST_TEST_MODULE bbf_addition_mod_p_test +#define BOOST_TEST_MODULE bbf_add_sub_mod_p_test #include #include -#include -#include -#include +#include #include #include #include @@ -39,7 +36,7 @@ using namespace nil::blueprint; template -void test_addition_mod_p( +void test_add_sub_mod_p( const std::vector &public_input) { using FieldType = BlueprintFieldType; using TYPE = typename FieldType::value_type; @@ -59,12 +56,7 @@ void test_addition_mod_p( pow <<= bit_size_chunk; } - extended_integral_type r = x + y; - if (r >= p) { - r -= p; - } - - auto assign_and_check = [&](auto &B, auto &raw_input) { + auto assign_and_check = [&](auto &B, auto &raw_input, bool is_add) { raw_input.x = std::vector(public_input.begin(), public_input.begin() + num_chunks); raw_input.y = std::vector(public_input.begin() + num_chunks, @@ -73,14 +65,20 @@ void test_addition_mod_p( public_input.begin() + 3 * num_chunks); raw_input.pp = std::vector(public_input.begin() + 3 * num_chunks, public_input.begin() + 4 * num_chunks); - raw_input.zero = std::vector(public_input.begin() + 4 * num_chunks, - public_input.begin() + 5 * num_chunks); + raw_input.zero = public_input[4 * 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); + if (!is_add) { + y = y != 0 ? p - y : y; + } + extended_integral_type r = x + y; + if (r >= p) { + r -= p; + } extended_integral_type R = 0; pow = 1; for (std::size_t i = 0; i < num_chunks; i++) { @@ -88,7 +86,7 @@ void test_addition_mod_p( pow <<= bit_size_chunk; } #ifdef BLUEPRINT_PLONK_PROFILING_ENABLED - std::cout << "addition_mod_p test" << std::endl; + std::cout << "add_sub_mod_p test" << std::endl; std::cout << "Expected res: " << std::dec << r << std::endl; std::cout << "Real res: " << std::dec << R << std::endl; #endif @@ -104,7 +102,16 @@ void test_addition_mod_p( bbf::circuit_builder(num_chunks, bit_size_chunk); - assign_and_check(B, raw_input); + assign_and_check(B, raw_input, true); + + typename bbf::components::pallas_substraction_mod_p< + FieldType, bbf::GenerationStage::ASSIGNMENT>::raw_input_type raw_input2; + + auto B2 = + bbf::circuit_builder(num_chunks, bit_size_chunk); + + assign_and_check(B2, raw_input2, false); } else if constexpr (std::is_same_v< NonNativeFieldType, crypto3::algebra::curves::vesta::base_field_type>) { @@ -114,13 +121,22 @@ void test_addition_mod_p( bbf::circuit_builder(num_chunks, bit_size_chunk); - assign_and_check(B, raw_input); + assign_and_check(B, raw_input, true); + + typename bbf::components::vesta_substraction_mod_p< + FieldType, bbf::GenerationStage::ASSIGNMENT>::raw_input_type raw_input2; + + auto B2 = + bbf::circuit_builder(num_chunks, bit_size_chunk); + + assign_and_check(B2, raw_input2, false); } } template -void addition_mod_p_tests() { +void add_sub_mod_p_tests() { using value_type = typename BlueprintFieldType::value_type; using integral_type = typename BlueprintFieldType::integral_type; using foreign_value_type = typename NonNativeFieldType::value_type; @@ -158,12 +174,12 @@ void addition_mod_p_tests() { public_input[3 * num_chunks + j] = value_type(pp & mask); pp >>= bit_size_chunk; - - public_input[4 * num_chunks + j] = value_type(0); } - test_addition_mod_p(public_input); + public_input[4 * num_chunks] = value_type(0); + + test_add_sub_mod_p(public_input); } } @@ -171,27 +187,26 @@ constexpr static const std::size_t random_tests_amount = 10; BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) -BOOST_AUTO_TEST_CASE(blueprint_plonk_bbf_addition_mod_p_test) { +BOOST_AUTO_TEST_CASE(blueprint_plonk_bbf_add_sub_mod_p_test) { using pallas_field_type = typename crypto3::algebra::curves::pallas::base_field_type; using vesta_field_type = typename crypto3::algebra::curves::vesta::base_field_type; - addition_mod_p_tests(); + add_sub_mod_p_tests(); - addition_mod_p_tests(); + add_sub_mod_p_tests(); - addition_mod_p_tests(); + add_sub_mod_p_tests(); - addition_mod_p_tests(); + add_sub_mod_p_tests(); - addition_mod_p_tests(); + add_sub_mod_p_tests(); - addition_mod_p_tests(); + add_sub_mod_p_tests(); } BOOST_AUTO_TEST_SUITE_END() diff --git a/crypto3/libs/blueprint/test/bbf/algebra/fields/non_native/check_mod_p.cpp b/crypto3/libs/blueprint/test/bbf/algebra/fields/non_native/check_mod_p.cpp index f4069095d..ab53e347f 100644 --- a/crypto3/libs/blueprint/test/bbf/algebra/fields/non_native/check_mod_p.cpp +++ b/crypto3/libs/blueprint/test/bbf/algebra/fields/non_native/check_mod_p.cpp @@ -28,8 +28,6 @@ #include #include #include -#include -#include #include #include #include @@ -115,7 +113,7 @@ BOOST_AUTO_TEST_CASE(blueprint_plonk_bbf_check_mod_p_test) { using pallas_field_type = typename crypto3::algebra::curves::pallas::base_field_type; using vesta_field_type = typename crypto3::algebra::curves::vesta::base_field_type; - mod_p_check_tests(); + mod_p_check_tests(); mod_p_check_tests(); mod_p_check_tests(); diff --git a/crypto3/libs/blueprint/test/bbf/algebra/fields/non_native/flexible_multiplication.cpp b/crypto3/libs/blueprint/test/bbf/algebra/fields/non_native/flexible_multiplication.cpp index d4a7ee487..60d3dcb4f 100644 --- a/crypto3/libs/blueprint/test/bbf/algebra/fields/non_native/flexible_multiplication.cpp +++ b/crypto3/libs/blueprint/test/bbf/algebra/fields/non_native/flexible_multiplication.cpp @@ -29,8 +29,6 @@ #include #include #include -#include -#include #include #include #include @@ -229,7 +227,7 @@ BOOST_AUTO_TEST_CASE(blueprint_plonk_bbf_flexible_multiplication_test) { using vesta_field_type = typename crypto3::algebra::curves::vesta::base_field_type; std::cout << "Scenario 1\n"; - mult_tests(); + mult_tests(); std::cout << "Scenario 2\n"; mult_tests(); diff --git a/crypto3/libs/blueprint/test/bbf/algebra/fields/non_native/negation_mod_p.cpp b/crypto3/libs/blueprint/test/bbf/algebra/fields/non_native/negation_mod_p.cpp index 93a74031a..cf46977d0 100644 --- a/crypto3/libs/blueprint/test/bbf/algebra/fields/non_native/negation_mod_p.cpp +++ b/crypto3/libs/blueprint/test/bbf/algebra/fields/non_native/negation_mod_p.cpp @@ -28,8 +28,6 @@ #include #include #include -#include -#include #include #include #include @@ -65,8 +63,7 @@ void test_negation_mod_p( public_input.begin() + 2 * num_chunks); raw_input.pp = std::vector(public_input.begin() + 2 * num_chunks, public_input.begin() + 3 * num_chunks); - raw_input.zero = std::vector(public_input.begin() + 3 * num_chunks, - public_input.begin() + 4 * num_chunks); + raw_input.zero = public_input[3 * num_chunks]; auto [at, A, desc] = B.assign(raw_input); bool pass = B.is_satisfied(at); @@ -146,9 +143,8 @@ void negation_mod_p_tests() { public_input[2 * num_chunks + j] = value_type(pp & mask); pp >>= bit_size_chunk; - - public_input[3 * num_chunks + j] = value_type(0); // the zeros } + public_input[3 * num_chunks] = value_type(0); // the zero test_negation_mod_p(public_input); diff --git a/crypto3/libs/blueprint/test/bbf/detail/range_check_multi.cpp b/crypto3/libs/blueprint/test/bbf/detail/range_check_multi.cpp index 695d3421b..3e824e87a 100644 --- a/crypto3/libs/blueprint/test/bbf/detail/range_check_multi.cpp +++ b/crypto3/libs/blueprint/test/bbf/detail/range_check_multi.cpp @@ -111,7 +111,7 @@ BOOST_AUTO_TEST_CASE(blueprint_plonk_bbf_range_check_multi_test) { using pallas_field_type = typename crypto3::algebra::curves::pallas::base_field_type; using vesta_field_type = typename crypto3::algebra::curves::vesta::base_field_type; - range_check_tests(); + range_check_tests(); range_check_tests(); range_check_tests(); diff --git a/crypto3/libs/blueprint/test/bbf/pubkey/ecdsa/ecdsa_recovery.cpp b/crypto3/libs/blueprint/test/bbf/pubkey/ecdsa/ecdsa_recovery.cpp new file mode 100644 index 000000000..63cda9983 --- /dev/null +++ b/crypto3/libs/blueprint/test/bbf/pubkey/ecdsa/ecdsa_recovery.cpp @@ -0,0 +1,248 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Alexey Yashunsky +// Copyright (c) 2025 Antoine Cyr +// +// 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 +#include +#include +#include +#include +#include + +using namespace nil; +using namespace nil::blueprint; + +template +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::value_type QA, + bool to_pass = true) { + + 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 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(public_input.begin(), public_input.begin() + num_chunks); + raw_input.r = std::vector(public_input.begin() + num_chunks, + public_input.begin() + 2 * num_chunks); + raw_input.s = std::vector(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; + std::cout << "to_pass = " << to_pass << std::endl; + assert(pass == to_pass); + + if (to_pass){ + 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(A.c.is_zero() || ((QA.X.data == xQA) && (QA.Y.data == yQA))); + } + }; + + if constexpr (std::is_same_v) { + typename bbf::components::pallas_ecdsa_recovery< + FieldType, bbf::GenerationStage::ASSIGNMENT>::raw_input_type raw_input; + + auto B = + bbf::circuit_builder(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(num_chunks, bit_size_chunk); + + assign_and_check(B, raw_input); + } +} + + +template void multi_test_recovery() { + nil::crypto3::random::algebraic_engine generate_random_scalar; + + boost::random::mt19937 seed_seq; + generate_random_scalar.seed(seed_seq); + + using ec_point_value_type = typename CurveType::template g1_type::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(z,r,s,v,QA); + } +} + +template void multi_test_recovery_invalid() { + nil::crypto3::random::algebraic_engine generate_random_scalar; + + boost::random::mt19937 seed_seq; + generate_random_scalar.seed(seed_seq); + + using ec_point_value_type = typename CurveType::template g1_type::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::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(z,r,s,v,QA,false); + + 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( + z,scalar_value_type(base_integral_type(x1.data)),s,v,QA,false); + } +} + +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; + using test = pallas_base_field::value_type; + + // + multi_test_recovery(); + + multi_test_recovery_invalid(); +} + +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; + + // + multi_test_recovery(); + + multi_test_recovery_invalid(); +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file