📅  最后修改于: 2023-12-03 15:28:52.919000             🧑  作者: Mango
非交互式零知识证明(non-interactive zero-knowledge proof,NIZK)是一种简化版的零知识证明,它允许证明者在与验证者只有一次交互的情况下证明自己的某个知识,而且不会暴露任何关于这个知识的信息。
NIZK 最初是由 Fiat 和 Shamir 在 1986 年提出的,是为了方便使用在密码学中。与标准的交互式零知识证明(interactive zero-knowledge proof,IZK)相比,NIZK 实现起来更加简单、高效。因此在很多应用中被使用,比如匿名认证、数字签名、智能合约等。
NIZK 技术的原理基于一个难题:如何通过仅有一次交互来证明某种知识而不泄漏任何有关这种知识的信息。
NIZK 的基本构建块包括三个算法:
在 NIZK 中,证明者需要先选择一个随机量,对这个随机量和证明的策略做一个「提交」。提交的方案由一个哈希函数生成,且满足哈希函数的单向性质。这里的哈希函数可以用 Pedersen 哈希、Merkle 树哈希等。
然后,证明者利用选择的随机量和提交的方案生成一个证明,证明需要让验证者公开验证一定的知识,但又不能泄露其它的任何信息。
请注意,提交的方案是一个不可反演的哈希结果,因此证明者无法从提交的方案中推断出提交的随机量。
下面是一个使用 libsnark 库实现 NIZK 证明的例子。代码片段包括了主函数和一些工具函数。
#include <cstdio>
#include <libsnark/common/default_types/r1cs_ppzksnark_pp.hpp>
#include <libsnark/common/utils.hpp>
#include <libsnark/gadgetlib1/gadgets/basic_gadgets.hpp>
#include <libsnark/gadgetlib1/gadgets/pairing/pairing_params.hpp>
#include <libsnark/gadgetlib1/gadgets/pairing/pairing_product_gadget.hpp>
#include <libsnark/gadgetlib1/gadgets/pairing/pairing_params.hpp>
#include <libff/common/utils.hpp>
#include <libff/common/default_types/ec_pp.hpp>
#include <libff/common/default_types/ec_pp.hpp>
#include <libff/common/default_types/r1cs_ppzksnark_pp.hpp>
using namespace libsnark;
typedef default_r1cs_ppzksnark_pp ppT;
int main()
{
start_profiling();
// Initialize the curve parameters and elliptic curve precomputation table.
default_ec_pp::init_public_params();
const size_t SCALING_FACTOR = 8;
const size_t HALF_SCALING_FACTOR = 4;
// Generate an R1CS instance.
std::vector<r1cs_constraint<libff::Fr<ppT>>> constraints;
protoboard<libff::Fr<ppT>> pb;
pb_variable_array<libff::Fr<ppT>> A, B, C;
pb_variable<libff::Fr<ppT>> out;
protoboard<libff::Fr<ppT>> pb_A, pb_B, pb_C, pb_out;
pb_A.generate_r1cs_witness(5);
pb_B.generate_r1cs_witness(5);
pb_C.generate_r1cs_witness(6);
pb_out.generate_r1cs_witness(((5 * 5) + 6));
out.allocate(pb_out, "out");
A.allocate(pb_A, 3, "A");
B.allocate(pb_B, 3, "B");
C.allocate(pb_C, 3, "C");
for (size_t i = 0; i < 3; ++i)
{
A[i] = pb_A.val(i);
B[i] = pb_B.val(i);
C[i] = pb_C.val(i);
}
out = pb_out.val();
R1CS_constraint_system<libff::Fr<ppT>> cs = pb.get_constraint_system(); // R1CS constraint system hosting the above constraints.
const libsnark::r1cs_variable_assignment<libff::Fr<ppT>> full_variable_assignment = get_variable_assignment_from_r1cs(cs);
std::vector<libff::Fr<ppT>> primary_input;
for (size_t i = 0; i < cs.num_inputs(); ++i)
{
primary_input.emplace_back(full_variable_assignment[cs.num_constraints() + i]);
}
// Generate the proof.
r1cs_ppzksnark_keypair<ppT> keypair = r1cs_ppzksnark_generator<ppT>(cs);
r1cs_ppzksnark_proof<ppT> proof = r1cs_ppzksnark_prover<ppT>(keypair.pk, pb.primary_input(), pb.auxiliary_input());
// Verify the proof.
bool verified = r1cs_ppzksnark_verifier_strong_IC<ppT>(keypair.vk, pb.primary_input(), proof);
print_header("[entering main]", " ");
print_indent_str("[start] proving program", " ");
print_indent_str("[start] constraint system", " ");
std::cout << cs.num_inputs() << " primary inputs" << std::endl;
std::cout << cs.num_constraints() << " constraints" << std::endl;
print_indent_str("[end] constraint system", " ");
print_indent_str("[start] generating proof", " ");
std::cout << "proof size: " << proof.size_in_bits() << " bits" << std::endl;
print_indent_str("[end] generating proof", " ");
print_indent_str("[start] verifying proof", " ");
if (verified)
{
std::cout << "verification succeeded" << std::endl;
}
else
{
std::cout << "verification failed" << std::endl;
}
print_indent_str("[end] verifying proof", " ");
print_header("[leaving main]", " ");
stop_profiling();
}
非交互式零知识证明作为一种可轻松实现且高效的零知识证明技术,其被广泛应用在许多领域,包括匿名认证、数字签名、智能合约等等。程序员们可以使用现有的 NIZK 库轻松实现自己的 NIZK 证明,来保证用户数据的隐私性和数据交换过程中的安全性。