qesa代码解析

1. 引言

前序博客 qesa Efficient zero-knowledge arguments in the discrete log setting 学习笔记

Hoffmann等人 2019年论文 《Efficient zero-knowledge arguments in the discrete log setting 》。

相应的代码实现可参见:

  • https://github.com/crate-crypto/qesa (rust)
  • https://github.com/emsec/QESA_ZK (C++)

https://github.com/emsec/QESA_ZK 为论文官方实现代码,包含的算法全面,本文主要关注https://github.com/emsec/QESA_ZK 中的代码实现,部分算法会参考https://github.com/crate-crypto/qesa中的代码实现。

1.1 C++17

https://github.com/emsec/QESA_ZK 代码中采用的是C++2017,需要保证编译服务器的C++版本兼容。
安装升级gcc的方法可参见博客 gcc版本编译安装

1.2 relic toolkit

https://github.com/relic-toolkit/relic 为一个采用C++实现的密码学库,目前实现的内容有:

  • Multiple-precision integer arithmetic
  • Prime and Binary field arithmetic
  • Elliptic curves over prime and binary fields (NIST curves and pairing-friendly curves)
  • Bilinear maps and related extension fields
  • Cryptographic protocols (RSA, Rabin, ECDSA, ECMQV, ECSS(Schnorr), ECIES, Sakai-Ohgishi-Kasahara ID-based authenticated key aggrement, Boneh-Lynn-Schacham and Boneh-Boyen short signatures, Paillier and Benaloh homomorphic encryption systems)

https://github.com/emsec/QESA_ZK 是基于relic toolkit 来构建的,需要调用relic库。
注意,QESA_ZK与relic master 版本不兼容,需切换到relic-toolkit-0.5.0版本再编译,详细方法为:

git clone https://github.com/relic-toolkit/relic
cd relic
git checkout relic-toolkit-0.5.0

mkdir build
cd build
cmake ../ -DWITH="BC;DV;BN;MD;FP;EP" \
    -DTESTS=0 -DBENCH=0 -DCHECK=off -DCOLOR=off -DDOCUM=off \
    -DFP_PRIME=255 -DFP_PMERS=on -DRAND=UDEV -DARITH=GMP \
    -DCOMP="-O3 -funroll-loops -fomit-frame-pointer -fPIC" -DWSIZE=64 -DSTLIB=on -DSHLIB=off $1

make -j

将编译的结果 lib/librelic_s.a 库文件拷贝至 QESA_ZK/proof_system/lib 目录下。

git clone https://github.com/emsec/QESA_ZK
mkdir QESA_ZK/proof_system/lib
cp lib/librelic_s.a QESA_ZK/proof_system/lib/

编译并运行QESA_ZK proof_system:

cd QESA_ZK/proof_system/
make

1.3 基本结构体

struct ProverContext
    {
        bool first_iteration;

        Matrix<G> A;
        math::vector<BN> w;

        u32 n;
        math::vector<G> u_minus_one;
        math::vector<G> u_plus_one;

        u32 state;

        ProverContext() { state = 0; };
    };

    struct VerifierContext
    {
        Matrix<G> A;
        math::vector<G> t;

        u32 n;
        bool result;

        u32 state;

        VerifierContext() { state = 0; result = false; };
    };

2. Matrix-vector multiplication argument + Zero-Knowledge

2.1 L M P A s i m p l e Z K LMPA_{simpleZK} LMPAsimpleZK协议

在这里插入图片描述
在这里插入图片描述
∑ s t d \sum_{std} std协议+ L M P A n o Z K LMPA_{noZK} LMPAnoZK协议= L M P A s i m p l e Z K LMPA_{simpleZK} LMPAsimpleZK协议。
// ctx.state = 0 即进行的是 ∑ s t d \sum_{std} std操作,来对所有witness进行blinding。

bool test_lmpa()
{
    std::cout << "Testing lmpa..." << std::endl;

    lmpa::Matrix<G> matrix(4, 12);
    for (u32 i = 0; i < matrix.rows(); ++i)
    {
        for (u32 j = 0; j < matrix.cols(); ++j)
        {
            matrix(i, j) = G::rand();
        }
    }

    math::vector<BN> witness;
    for (u32 j = 0; j < matrix.cols(); ++j)
    {
        witness.push_back(BN::rand());
    }

    auto t = matrix * witness;

    simple_zk::ProverContext prover_ctx;
    simple_zk::VerifierContext verifier_ctx;

    simple_zk::begin(prover_ctx, matrix, witness);
    simple_zk::begin(verifier_ctx, matrix, t);
    std::vector<u8> buffer;
    bool continue_prover = true;
    bool continue_verifier = true;
    while (continue_prover || continue_verifier)
    {
        if (continue_prover)
        {
            continue_prover = simple_zk::step_prover(prover_ctx, buffer);
        }
        if (continue_verifier)
        {
            continue_verifier = simple_zk::step_verifier(verifier_ctx, buffer);
        }
    }

    if (simple_zk::get_result(verifier_ctx))
    {
        std::cout << "OK!" << std::endl;
        return true;
    }
    else
    {
        std::cout << "ERROR!" << std::endl;
    }

    return false;
}

2.2 L M P A Z K LMPA_{ZK} LMPAZK协议

L M P A s i m p l e Z K LMPA_{simpleZK} LMPAsimpleZK协议 协议不同,不是借助 ∑ s t d \sum_{std} std协议来引入随机数 r ⃗ ← F p n \vec{r}\leftarrow\mathbb{F}_p^n r Fpn 对整个witness w ⃗ \vec{w} w 进行blinding。
L M P A Z K LMPA_{ZK} LMPAZK协议通过引入了随机列来实现blinding,同时相应的随机数来自于masking set,为稀疏的,可减少Prover的计算压力。
详细参见博客 qesa Efficient zero-knowledge arguments in the discrete log setting 学习笔记 第4.2节 zero knowledge of Matrix-vector multiplication argument 内容。

3. zero-knowledge inner product argument

3.1 I P A n o Z K IPA_{noZK} IPAnoZK协议

在这里插入图片描述

具体参看 https://github.com/crate-crypto/qesa/blob/master/src/ipa/no_zk.rs,其中的verify_multiexp 方法 delay all exponentiations到最后一轮,可提升Verifier验证效率。(可参看博文 Bulletproofs: Short Proofs for Confidential Transactions and More学习笔记 第3.1节 “使用Multi-exponentiation对inner-product verification进行优化“。)

  fn test_create_nozk_proof() {
        let n = 4;

        let mut rng = rand::thread_rng();

        let a: Vec<Scalar> = (0..n).map(|_| Scalar::random(&mut rng)).collect();
        let b: Vec<Scalar> = (0..n).map(|_| Scalar::random(&mut rng)).collect();

        let t = inner_product(&a, &b);

        let G: Vec<RistrettoPoint> = (0..n).map(|_| RistrettoPoint::random(&mut rng)).collect();
        let H: Vec<RistrettoPoint> = (0..n).map(|_| RistrettoPoint::random(&mut rng)).collect();
        let Q = RistrettoPoint::hash_from_bytes::<Sha3_512>(b"test point");

        let mut prover_transcript = Transcript::new(b"ip_no_zk");

        let P = RistrettoPoint::vartime_multiscalar_mul(
            a.iter().chain(b.iter()).chain(iter::once(&t)),
            G.iter().chain(H.iter()).chain(iter::once(&Q)),
        );

        // We add the compressed point to the transcript, because we need some non-trivial input to generate alpha
        // If this is not done, then the prover always will be able to predict what the first challenge will be
        prover_transcript.append_message(b"P", P.compress().as_bytes());

        let proof = create(&mut prover_transcript, G.clone(), H.clone(), &Q, a, b);

        let mut verifier_transcript = Transcript::new(b"ip_no_zk");
        verifier_transcript.append_message(b"P", P.compress().as_bytes());

        assert!(proof.verify(&mut verifier_transcript, &G, &H, &Q, n, P, t));
    }

3.2 I P A a l m Z K IPA_{almZK} IPAalmZK协议

在这里插入图片描述
参见 https://github.com/crate-crypto/qesa/blob/master/src/ipa/alm_zk.rs
其中随机向量 r ⃗ ′ , r ⃗ ′ ′ \vec{r}',\vec{r}'' r ,r 遵循kenerl guideline来选择,实际生成方式为:

    //1. Compute r' and r''
    //
    let r_prime = sample_gram_schmidt(&b_Vec);

    let r_prime_prime = sample_gram_schmidt_twice(&a_Vec, &r_prime);
fn sample_m_n_plus(n: usize) -> Vec<Scalar> {
    let mut r: Vec<Scalar> = vec![Scalar::zero(); n];

    let mut rng = rand::thread_rng();

    r[0] = Scalar::random(&mut rng);
    r[1] = Scalar::random(&mut rng);

    let mut i = 4;
    while i <= n {
        r[i / 2] = Scalar::random(&mut rng);
        r[i / 2 + 1] = Scalar::random(&mut rng);

        i = i * 2
    }

    r[n - 2] = Scalar::random(&mut rng);
    r[n - 1] = Scalar::random(&mut rng);

    assert_eq!(n, r.len());

    r
}

/// Returns an vector that is orthogonal to `a`
/// Formally, this function projects `b` orthogonally onto the
/// line spanned by `a`
pub fn orth(a: &[Scalar], b: &[Scalar]) -> Vec<Scalar> {
    let aa: Scalar = inner_product(&a, &a);
    let ab: Scalar = inner_product(&a, &b);

    assert_ne!(Scalar::zero(), ab);

    let x: Scalar = ab * aa.invert();

    let ax: Vec<Scalar> = a.iter().map(|k| k * x).collect();

    let b_minus_ax = b.iter().zip(ax.iter()).map(|(r, s)| r - s).collect();

    b_minus_ax
}

/// Samples a random matrix and returns a random vector
/// which is orthogonal to `a`
pub fn sample_gram_schmidt(a: &[Scalar]) -> Vec<Scalar> {
    let sampled_matrix = sample_m_n_plus(a.len());
    orth(a, &sampled_matrix)
}
/// Samples a random matrix and returns a random vector
/// which is orthogonal to `a` and to `b`
/// XXX: We can make a better API for this and naming convention
/// XXX: Naming it sample_gram_schmidt twice could imply that we sample twice
pub fn sample_gram_schmidt_twice(a: &[Scalar], b: &[Scalar]) -> Vec<Scalar> {
    let orth_a_b = orth(a, &b);
    let orth_a_sample = sample_gram_schmidt(a);

    orth(&orth_a_b, &orth_a_sample)
}

I P A a l m Z K IPA_{almZK} IPAalmZK协议 代码为:

fn test_create_almzk_proof() {
        let n = 4;

        let mut rng = rand::thread_rng();

        let a: Vec<Scalar> = (0..n).map(|_| Scalar::random(&mut rng)).collect();
        let b: Vec<Scalar> = (0..n).map(|_| Scalar::random(&mut rng)).collect();

        let t = inner_product(&a, &b);

        let G: Vec<RistrettoPoint> = (0..n).map(|_| RistrettoPoint::random(&mut rng)).collect();
        let H: Vec<RistrettoPoint> = (0..n).map(|_| RistrettoPoint::random(&mut rng)).collect();

        let mut prover_transcript = Transcript::new(b"ip_alm_zk");

        let Q = RistrettoPoint::hash_from_bytes::<Sha3_512>(b"test point");

        let C_w = RistrettoPoint::vartime_multiscalar_mul(
            a.iter().chain(b.iter()),
            G.iter().chain(H.iter()),
        );

        let proof = super::create(
            &mut prover_transcript,
            G.clone(),
            H.clone(),
            &Q,
            C_w,
            a,
            b,
            t,
        );

        let mut verifier_transcript = Transcript::new(b"ip_alm_zk");
        assert!(proof.verify(&mut verifier_transcript, &G, &H, &Q, n, C_w, t));
    }

3.3 Q E S A I n n e r QESA_{Inner} QESAInner 协议

在这里插入图片描述
其中 r ⃗ ′ ← F p 2 \vec{r}'\leftarrow\mathbb{F}_p^2 r Fp2
代码实现参见https://github.com/crate-crypto/qesa/blob/master/src/ipa/qesa_inner.rs

#[derive(Clone)]
pub struct Inner {
    pub(crate) alm_zk: alm_zk::AlmZK,
    pub(crate) c_prime_w: CompressedRistretto,
    pub(crate) c_prime_prime_w: CompressedRistretto,
}
 fn test_create_qesa_inner_proof() {
        let mut rng = rand::thread_rng();

        let n = 4;

        let (witness, matrix) = helper_create_solutions(n - 2, 2);

        let G: Vec<RistrettoPoint> = (0..n).map(|_| RistrettoPoint::random(&mut rng)).collect();
        let H: Vec<RistrettoPoint> = (0..n).map(|_| RistrettoPoint::random(&mut rng)).collect();
        let Q = RistrettoPoint::hash_from_bytes::<Sha3_512>(b"test point");

        let r_prime: Vec<Scalar> = (0..2).map(|_| Scalar::random(&mut rng)).collect();

        let mut prover_transcript = Transcript::new(b"qesa_inner");

        let proof = create(
            &mut prover_transcript,
            G.clone(),
            H.clone(),
            &Q,
            &matrix,
            witness,
            r_prime,
        );

        let mut verifier_transcript = Transcript::new(b"qesa_inner");
        assert!(proof.verify(&mut verifier_transcript, G, H, &Q, &matrix))
    }

	// Creates a system of quadratic equations with solutions
    // and a witness
    fn helper_create_solutions(n: usize, num_of_matrices: usize) -> (Vec<Scalar>, BlockMatrix) {
        let mut rng = rand::thread_rng();
        let mut witness: Vec<Scalar> = (0..n).map(|_| Scalar::random(&mut rng)).collect();
        witness[0] = Scalar::one();

        let mut bm = BlockMatrix::new();

        for _ in 0..num_of_matrices {
            let mut gamma_i: Vec<Vec<Scalar>> = Vec::new();
            for _ in 0..n {
                // Use gram schmidt to create suitable solutions for each system of eqns
                let x: Vec<Scalar> = (0..n).map(|_| Scalar::random(&mut rng)).collect();
                let row_of_eqns = crate::ipa::gramschmidt::orth(&witness, &x);
                gamma_i.push(row_of_eqns)
            }
            bm.push(gamma_i);
        }

        // For now, we only use one set of system of equations
        (witness, bm)
    }

4. QESA协议

4.1 Q E S A Z K QESA_{ZK} QESAZK协议

在这里插入图片描述
Q E S A Z K QESA_{ZK} QESAZK协议 是对 Q E S A I n n e r QESA_{Inner} QESAInner 协议 的封装。
参见https://github.com/crate-crypto/qesa/blob/master/src/qesa_zk.rs代码:

// QESA_ZK is a glorified wrapper around Qesa_Inner
pub struct Zk {
    inner: qesa_inner::Inner,
}

pub fn create(
    transcript: &mut Transcript,
    G_Vec: Vec<RistrettoPoint>,
    H_Vec: Vec<RistrettoPoint>,
    Q: RistrettoPoint,
    gamma_i: &BlockMatrix,
    w: Vec<Scalar>,
) -> Zk {
    // 1. Compute r'
    //
    let mut rng = rand::thread_rng();
    let r_prime = vec![Scalar::random(&mut rng), Scalar::random(&mut rng)];

    // The paper states that we should generate C'_w in Qesa_Zk
    // Then pass it to Qesa_Inner.
    // However, it makes more sense to generate it in Qesa_Inner

    let qesa_inner_proof = qesa_inner::create(transcript, G_Vec, H_Vec, &Q, gamma_i, w, r_prime);
    Zk {
        inner: qesa_inner_proof,
    }
}

impl Zk {
    pub fn verify(
        &self,
        transcript: &mut Transcript,
        G_Vec: Vec<RistrettoPoint>,
        H_Vec: Vec<RistrettoPoint>,
        Q: &RistrettoPoint,
        gamma_i: &BlockMatrix,
    ) -> bool {
        self.inner.verify(transcript, G_Vec, H_Vec, Q, gamma_i)
    }
}

4.2 Q E S A C o p y QESA_{Copy} QESACopy协议

在这里插入图片描述

参见https://github.com/emsec/QESA_ZK/blob/master/proof_system/src/test/test_qesa_copy.cpp 代码:

bool test_qesa_copy()
{
    std::cout << "testing QESA Copy" << std::endl;
    std::vector<qesa::SparseMatrix> matrices;

    // setup witness
    math::vector<BN> witness = {1};

    // setup CRS and commitment CRSs
    auto crs = qesa::gen_CRS(1 + 14);
    auto com_crs_1 = qesa::copy::get_commitment_crs(crs, {1, 2});
    auto com_crs_2 = qesa::copy::get_commitment_crs(crs, {2, 4, 7});

    // commitment messages
    std::vector<BN> m1 = {BN::rand(), BN::rand()};
    BN r1 = BN::rand();

    std::vector<BN> m2 = {BN::rand(), BN::rand(), BN::rand()};
    BN r2 = BN::rand();

    std::vector<BN> m3 = {BN::rand(), BN::rand()};
    BN r3 = BN::rand();

    // commit to messages
    auto com_1 = qesa::copy::commit(crs, com_crs_1, m1, r1);
    auto com_2 = qesa::copy::commit(crs, com_crs_2, m2, r2);
    auto com_3 = qesa::copy::commit(crs, com_crs_1, m3, r3);

    // create qesa::copy mapping
    auto mapping = qesa::copy::compute_mapping(crs, 1, {com_crs_1, com_crs_2, com_crs_1});

    // execute qesa::copy
    qesa::copy::ProverContext pctx(crs);
    qesa::copy::VerifierContext vctx(crs);

    std::vector<u8> buffer;
    buffer.reserve(10000);

    bool continue_prover = true;
    bool continue_verifier = true;

    timer::start();

    {
        std::vector<std::tuple<std::vector<BN>, BN>> commitments;
        commitments.emplace_back(m1, r1);
        commitments.emplace_back(m2, r2);
        commitments.emplace_back(m3, r3);
        qesa::copy::begin(pctx, matrices, mapping, witness, commitments);
    }

    qesa::copy::begin(vctx, matrices, mapping, {com_1, com_2, com_3});


    while (continue_prover || continue_verifier)
    {
        if (continue_prover)
        {
            continue_prover = qesa::copy::step_prover(pctx, buffer);
        }
        if (continue_verifier)
        {
            continue_verifier = qesa::copy::step_verifier(vctx, buffer);
        }
    }
    auto result = qesa::copy::get_result(vctx);
    timer::stop();
    std::cout << timer::milliseconds() << "ms, " << std::boolalpha << "result = " << result << std::endl << std::endl;

    return result;
}

5. 基于 Q E S A C o p y QESA_{Copy} QESACopy 协议构建range proof

参见https://github.com/emsec/QESA_ZK/blob/master/proof_system/src/test/test_qesa_range_proof.cpp

bool test_qesa_range_proof()
{

    std::cout << "Testing QESA range proof..." << std::endl;

    u32 bitrange = 60;
    u32 num_values = 2;

    qesa::CRS crs = qesa::gen_CRS(2 + bitrange * num_values);

    std::cout << "CRS size: " << crs.n << std::endl;

    std::vector<std::tuple<BN, BN>> openings;
    std::vector<G> commitments;

    for (u32 i = 0; i < num_values; ++i)
    {
        BN v = BN::rand(bitrange - 1, true);
        BN r = BN::rand();
        openings.push_back({v, r});
        commitments.push_back(qesa::range_proof::commit(crs, v, r));
    }


    qesa::range_proof::ProverContext pctx(crs);
    qesa::range_proof::VerifierContext vctx(crs);

    std::vector<u8> buffer;
    buffer.reserve(10000);

    bool continue_prover = true;
    bool continue_verifier = true;

    timer::start();

    qesa::range_proof::begin(pctx, bitrange, openings);
    qesa::range_proof::begin(vctx, bitrange, commitments);

    while (continue_prover || continue_verifier)
    {
        if (continue_prover)
        {
            continue_prover = qesa::range_proof::step_prover(pctx, buffer);
        }
        if (continue_verifier)
        {
            continue_verifier = qesa::range_proof::step_verifier(vctx, buffer);
        }
    }
    auto result = qesa::range_proof::get_result(vctx);
    timer::stop();
    std::cout << timer::milliseconds() << "ms, " << std::boolalpha << "result = " << result << std::endl << std::endl;
    if (!result) return false;
    return true;
}

6. 基于 Q E S A C o p y QESA_{Copy} QESACopy 协议构建shuffle argument

参见https://github.com/emsec/QESA_ZK/blob/master/proof_system/src/test/test_shuffle_proof.cpp

bool test_shuffle_proof()
{
    std::cout << "Testing shuffle proof..." << std::endl;
    const u32 shuffle_size = 400;
    auto crs = gen_CRS(shuffle_size);

    auto keys = el_gamal::keygen();

    std::vector<G> plaintexts;
    std::vector<el_gamal::Ciphertext> c_old;
    std::vector<el_gamal::Ciphertext> c_new;
    std::vector<u32> inverse_permutation;
    math::vector<BN> rerandomization;

    for (u32 i = 0; i < shuffle_size; ++i)
    {
        auto ptxt = G::rand();

        auto ctxt = el_gamal::encrypt(keys.pk, ptxt);

        plaintexts.push_back(ptxt);
        c_old.push_back(ctxt);

        inverse_permutation.push_back(i);
    }

    std::shuffle(std::begin(inverse_permutation), std::end(inverse_permutation), std::default_random_engine());

    std::vector<u32> permutation(shuffle_size, (u32)0);
    for (u32 i = 0; i < shuffle_size; ++i)
    {
        auto rho = BN::rand();

        auto ctxt = c_old.at(inverse_permutation.at(i));
        ctxt.c_0 += rho * G::get_gen();
        ctxt.c_1 += rho * keys.pk;

        c_new.push_back(ctxt);
        rerandomization.push_back(rho);

        permutation[inverse_permutation.at(i)] = i;
    }


    std::cout << "Begin protocol..." << std::endl;

    ProverContext prover_ctx(crs);
    VerifierContext verifier_ctx(crs);

    begin(prover_ctx, c_old, c_new, keys.pk, permutation, rerandomization);
    begin(verifier_ctx, c_old, c_new, keys.pk);
    std::vector<u8> buffer;
    bool continue_prover = true;
    bool continue_verifier = true;

    timer::start();
    while (continue_prover || continue_verifier)
    {
        if (continue_prover)
        {
            continue_prover = step_prover(prover_ctx, buffer);
        }
        if (continue_verifier)
        {
            continue_verifier = step_verifier(verifier_ctx, buffer);
        }
    }
    timer::stop();
    std::cout << timer::milliseconds() << "ms" << std::endl;

    if (get_result(verifier_ctx))
    {
        std::cout << "OK!" << std::endl;
        return true;
    }
    else
    {
        std::cout << "ERROR!" << std::endl;
    }

    return false;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值