Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Fixes SNARK input consistency soundness bug identified by Bryan Parno.
A soundness bug in the input consistency check, found by Bryan Parno, has been fixed. We thank Bryan for identifying the bug and for helping us fix it. Technical details: The bug affected the R1CS-to-QAP reduction, in that some rank-1 constraint systems produced distinct, but not linearly independent, QAP polynomials. The fix ensures independence. The cost is an increase of QAP degree from cs.num_constraints()+1 to cs.num_constraints()+cs.num_inputs()+1. Typically, cs.num_inputs() is dominated by cs.num_constraints() so the degree increase is negligible. Concretely, our experiments show that the increase is typically less than 0.007% for all applications reported by us thus far, including Zerocash [BCGGMTV14, S&P], SNARKs for TinyRAM [BCTV14, USENIX Security], and Scalable Zero-Knowledge [BCTV14, CRYPTO]. See Remark 2.5 in the (revised) extended version of "Succinct Non-Interactive Zero Knowledge for a von Neumann Architecture", at http://eprint.iacr.org/2013/879 .
- Loading branch information
Showing
with
36 additions
and 29 deletions.
| @@ -18,8 +18,6 @@ | ||
| #include "common/utils.hpp" | ||
| #include "algebra/evaluation_domain/evaluation_domain.hpp" | ||
|
|
||
| #define R1CS_TO_QAP_ADDITIONAL_CONSTRAINTS 1 // +1 is for an additional constraint needed for the soundness of input consistency | ||
|
|
||
| namespace libsnark { | ||
|
|
||
| /** | ||
| @@ -39,41 +37,41 @@ qap_instance<FieldT> r1cs_to_qap_instance_map(const r1cs_constraint_system<Field | ||
| { | ||
| enter_block("Call to r1cs_to_qap_instance_map"); | ||
|
|
||
| const std::shared_ptr<evaluation_domain<FieldT> > domain = get_evaluation_domain<FieldT>(cs.num_constraints() + R1CS_TO_QAP_ADDITIONAL_CONSTRAINTS); | ||
| const std::shared_ptr<evaluation_domain<FieldT> > domain = get_evaluation_domain<FieldT>(cs.num_constraints() + cs.num_inputs() + 1); | ||
|
|
||
| std::vector<std::map<size_t, FieldT> > A_in_Lagrange_basis(cs.num_variables()+1); | ||
| std::vector<std::map<size_t, FieldT> > B_in_Lagrange_basis(cs.num_variables()+1); | ||
| std::vector<std::map<size_t, FieldT> > C_in_Lagrange_basis(cs.num_variables()+1); | ||
|
|
||
| enter_block("Compute polynomials A, B, C in Lagrange basis"); | ||
| /** | ||
| * add and process the constraint | ||
| * (1 + \sum_{i=1}^{num_inputs} (i+1) * input_i) * 0 = 0 | ||
| * add and process the constraints | ||
| * input_i * 0 = 0 | ||
| * to ensure soundness of input consistency | ||
| */ | ||
| for (size_t i = 0; i <= cs.num_inputs(); ++i) | ||
| { | ||
| A_in_Lagrange_basis[i][0] += FieldT(i+1); | ||
| A_in_Lagrange_basis[i][cs.num_constraints() + i] = FieldT::one(); | ||
| } | ||
| /* process all other constraints */ | ||
| for (size_t i = 0; i < cs.num_constraints(); ++i) | ||
| { | ||
| for (size_t j = 0; j < cs.constraints[i].a.terms.size(); ++j) | ||
| { | ||
| A_in_Lagrange_basis[cs.constraints[i].a.terms[j].index][i+1] += | ||
| A_in_Lagrange_basis[cs.constraints[i].a.terms[j].index][i] += | ||
| cs.constraints[i].a.terms[j].coeff; | ||
| } | ||
|
|
||
| for (size_t j = 0; j < cs.constraints[i].b.terms.size(); ++j) | ||
| { | ||
| B_in_Lagrange_basis[cs.constraints[i].b.terms[j].index][i+1] += | ||
| B_in_Lagrange_basis[cs.constraints[i].b.terms[j].index][i] += | ||
| cs.constraints[i].b.terms[j].coeff; | ||
| } | ||
|
|
||
| for (size_t j = 0; j < cs.constraints[i].c.terms.size(); ++j) | ||
| { | ||
| C_in_Lagrange_basis[cs.constraints[i].c.terms[j].index][i+1] += | ||
| cs.constraints[i].c.terms[j].coeff; | ||
| C_in_Lagrange_basis[cs.constraints[i].c.terms[j].index][i] += | ||
| cs.constraints[i].c.terms[j].coeff; | ||
| } | ||
| } | ||
| leave_block("Compute polynomials A, B, C in Lagrange basis"); | ||
| @@ -109,7 +107,7 @@ qap_instance_evaluation<FieldT> r1cs_to_qap_instance_map_with_evaluation(const r | ||
| { | ||
| enter_block("Call to r1cs_to_qap_instance_map_with_evaluation"); | ||
|
|
||
| const std::shared_ptr<evaluation_domain<FieldT> > domain = get_evaluation_domain<FieldT>(cs.num_constraints() + R1CS_TO_QAP_ADDITIONAL_CONSTRAINTS); | ||
| const std::shared_ptr<evaluation_domain<FieldT> > domain = get_evaluation_domain<FieldT>(cs.num_constraints() + cs.num_inputs() + 1); | ||
This comment has been minimized.
This comment has been minimized.
arielgabizon
|
||
|
|
||
| std::vector<FieldT> At, Bt, Ct, Ht; | ||
|
|
||
| @@ -123,33 +121,33 @@ qap_instance_evaluation<FieldT> r1cs_to_qap_instance_map_with_evaluation(const r | ||
| enter_block("Compute evaluations of A, B, C, H at t"); | ||
| const std::vector<FieldT> u = domain->lagrange_coeffs(t); | ||
| /** | ||
| * add and process the constraint | ||
| * (1 + \sum_{i=1}^{num_inputs} (i+1) * input_i) * 0 = 0 | ||
| * add and process the constraints | ||
| * input_i * 0 = 0 | ||
| * to ensure soundness of input consistency | ||
| */ | ||
| for (size_t i = 0; i <= cs.num_inputs(); ++i) | ||
| { | ||
| At[i] += u[0] * FieldT(i+1); | ||
| At[i] = u[cs.num_constraints() + i]; | ||
| } | ||
| /* process all other constraints */ | ||
| for (size_t i = 0; i < cs.num_constraints(); ++i) | ||
| { | ||
| for (size_t j = 0; j < cs.constraints[i].a.terms.size(); ++j) | ||
| { | ||
| At[cs.constraints[i].a.terms[j].index] += | ||
| u[i+1]*cs.constraints[i].a.terms[j].coeff; | ||
| u[i]*cs.constraints[i].a.terms[j].coeff; | ||
| } | ||
|
|
||
| for (size_t j = 0; j < cs.constraints[i].b.terms.size(); ++j) | ||
| { | ||
| Bt[cs.constraints[i].b.terms[j].index] += | ||
| u[i+1]*cs.constraints[i].b.terms[j].coeff; | ||
| u[i]*cs.constraints[i].b.terms[j].coeff; | ||
| } | ||
|
|
||
| for (size_t j = 0; j < cs.constraints[i].c.terms.size(); ++j) | ||
| { | ||
| Ct[cs.constraints[i].c.terms[j].index] += | ||
| u[i+1]*cs.constraints[i].c.terms[j].coeff; | ||
| u[i]*cs.constraints[i].c.terms[j].coeff; | ||
| } | ||
| } | ||
|
|
||
| @@ -217,25 +215,24 @@ qap_witness<FieldT> r1cs_to_qap_witness_map(const r1cs_constraint_system<FieldT> | ||
| /* sanity check */ | ||
| assert(cs.is_satisfied(primary_input, auxiliary_input)); | ||
|
|
||
| const std::shared_ptr<evaluation_domain<FieldT> > domain = get_evaluation_domain<FieldT>(cs.num_constraints() + R1CS_TO_QAP_ADDITIONAL_CONSTRAINTS); | ||
| const std::shared_ptr<evaluation_domain<FieldT> > domain = get_evaluation_domain<FieldT>(cs.num_constraints() + cs.num_inputs() + 1); | ||
This comment has been minimized. |
||
|
|
||
| r1cs_variable_assignment<FieldT> full_variable_assignment = primary_input; | ||
| full_variable_assignment.insert(full_variable_assignment.end(), auxiliary_input.begin(), auxiliary_input.end()); | ||
|
|
||
| enter_block("Compute evaluation of polynomials A, B on set S"); | ||
| std::vector<FieldT> aA(domain->m, FieldT::zero()), aB(domain->m, FieldT::zero()); | ||
|
|
||
| /* account for the additional constraint (1 + \sum_{i=1}^{num_inputs} (i+1) * input_i) * 0 = 0 */ | ||
| aA[0] = FieldT::one(); | ||
| for (size_t i = 0; i < cs.num_inputs(); ++i) | ||
| /* account for the additional constraints input_i * 0 = 0 */ | ||
| for (size_t i = 0; i <= cs.num_inputs(); ++i) | ||
| { | ||
| aA[0] += full_variable_assignment[i] * FieldT(i+2); | ||
| aA[i+cs.num_constraints()] = (i > 0 ? full_variable_assignment[i-1] : FieldT::one()); | ||
| } | ||
| /* account for all other constraints */ | ||
| for (size_t i = 0; i < cs.num_constraints(); ++i) | ||
| { | ||
| aA[i+1] += cs.constraints[i].a.evaluate(full_variable_assignment); | ||
| aB[i+1] += cs.constraints[i].b.evaluate(full_variable_assignment); | ||
| aA[i] += cs.constraints[i].a.evaluate(full_variable_assignment); | ||
| aB[i] += cs.constraints[i].b.evaluate(full_variable_assignment); | ||
| } | ||
| leave_block("Compute evaluation of polynomials A, B on set S"); | ||
|
|
||
| @@ -284,7 +281,7 @@ qap_witness<FieldT> r1cs_to_qap_witness_map(const r1cs_constraint_system<FieldT> | ||
| std::vector<FieldT> aC(domain->m, FieldT::zero()); | ||
| for (size_t i = 0; i < cs.num_constraints(); ++i) | ||
| { | ||
| aC[i+1] += cs.constraints[i].c.evaluate(full_variable_assignment); | ||
| aC[i] += cs.constraints[i].c.evaluate(full_variable_assignment); | ||
| } | ||
| leave_block("Compute evaluation of polynomial C on set S"); | ||
|
|
||
(Not related directly to the security bug...)
Can the code duplication between this function and
r1cs_to_qap_instance_mapabove be reduced?