dalek-cryptography/zkp——基于merlin的Schnorr零知识证明

zkp 为使用merlin 的Schnorr零知识证明工具包,基于ristretto group做的实例化。【ristretto为对cofactor>1曲线的抽象,ristretto对外表现为将non-prime-order的曲线抽象为a prime-order group。具体的细节可参见博客1ristretto对cofactor>1的椭圆曲线(如Curve25519等)的兼容(含Curve25519 cofactor的sage验证)、博客2ristretto255 point压缩和解压缩算法(1)——affine坐标系下、博客3ristretto255 point压缩和解压缩算法(2)——extended坐标系下】

在Merlin中,一个transcript即为a public-coin argument,通过Transcript::new()函数来创建。Prover的消息通过append_message()(老的版本采用的是commit_bytes())函数来添加到transcript中。Verifier的challenges则通过challenge_bytes()函数来计算。

Merlin的transcript应由协议的API消费者创建,而不能由protocol内部创建。

Merlin提供的接口是面向字节的byte-oriented,而实际证明协议中,需要的是特定格式的数据,如接收challenge scalars,而不是challenge bytes。这个转化,可通过博客Merlin——零知识证明(2)应用篇中的transcript protocol来实现。不同的协议,可以自定义实现自己的trait TranscriptProtocol

1. 性能测试

在4核8G Ubuntu16.04系统上运行。

cargo +nightly bench
running 8 tests
test batch_verify_batchable_dleq_1  ... bench:     198,877 ns/iter (+/- 27,535)
test batch_verify_batchable_dleq_16 ... bench:   1,452,702 ns/iter (+/- 208,581)
test batch_verify_batchable_dleq_4  ... bench:     450,939 ns/iter (+/- 45,632)
test batch_verify_batchable_dleq_64 ... bench:   5,339,812 ns/iter (+/- 273,354)
test create_batchable_dleq          ... bench:     221,563 ns/iter (+/- 21,566)
test create_compact_dleq            ... bench:     221,027 ns/iter (+/- 14,245)
test verify_batchable_dleq          ... bench:     183,422 ns/iter (+/- 11,620)
test verify_compact_dleq            ... bench:     210,587 ns/iter (+/- 12,661)

由此可知,batch_verifier.rs中的verify_batchable()函数verify proof的效率要由于verifier.rs中的实现。

2. 代码解析

sig_and_vrf_example.rssign/verify验证的是Schnorr签名的流程,最关键的是这段代码:(可参考博客Schnorr signature (Schnorr 签名)数学原理、
elliptic-curve签名验证verify signature in EdDSA)
dalek-cryptography/zkp——基于merlin的Schnorr零知识证明_第1张图片

注意,由于使用的是Merlin transcript,变量的赋值顺序会影响transcript state,所以Prover和Verifier应严格按照相同的次序【将interactive变为non-interactive】来进行Merlin transcript的更新,否则会出现verify失败的情况。

/// This proof has `m+1` 32-byte elements, where `m` is the number of
/// secret variables.  This means there is no space savings for a
/// `CompactProof` over a `BatchableProof` when there is only one
/// statement.
#[derive(Clone, Serialize, Deserialize)]
pub struct CompactProof {
    /// The Fiat-Shamir challenge.
    pub challenge: Scalar,
    /// The prover's responses, one per secret variable.
    pub responses: Vec,
}

/// A Schnorr proof in batchable format.
///
/// This proof has `m+n` 32-byte elements, where `m` is the number of
/// secret variables and `n` is the number of statements.
#[derive(Clone, Serialize, Deserialize)]
pub struct BatchableProof {
    /// Commitments to the prover's blinding factors.
    pub commitments: Vec,
    /// The prover's responses, one per secret variable.
    pub responses: Vec,
}
#[macro_export]
macro_rules! define_proof {
    (
        $proof_module_name:ident // Name of the module to create
        ,
        $proof_label_string:expr // A string literal, used as a domain separator
        ,
        ( $($secret_var:ident),+ ) // Secret variables, sep by commas
        ,
        ( $($instance_var:ident),* ) // Public instance variables, separated by commas
        ,
        ( $($common_var:ident),* ) // Public common variables, separated by commas
        :
        // List of statements to prove
        // Format: LHS = ( ... RHS expr ... ),
        $($lhs:ident = $statement:tt),+
    ) => {......}

			/// Named parameters for [`prove_compact`] and [`prove_batchable`].
            #[derive(Copy, Clone)]
            pub struct ProveAssignments<'a> {
                $(pub $secret_var: &'a Scalar,)+
                $(pub $instance_var: &'a RistrettoPoint,)+
                $(pub $common_var: &'a RistrettoPoint,)+
            }

			/// Named parameters for [`verify_compact`] and [`verify_batchable`].
            #[derive(Copy, Clone)]
            pub struct VerifyAssignments<'a> {
                $(pub $instance_var: &'a CompressedRistretto,)+
                $(pub $common_var: &'a CompressedRistretto,)+
            }
 			/// Given a transcript and assignments to secret and public variables, produce a proof in batchable format.
            pub fn prove_batchable(
                transcript: &mut Transcript,
                assignments: ProveAssignments,
            ) -> (BatchableProof, CompressedPoints) {
                let (prover, compressed) = build_prover(transcript, assignments);

                (prover.prove_batchable(), compressed)
            }

			fn build_prover<'a>(
                transcript: &'a mut Transcript,
                assignments: ProveAssignments,
            ) -> (Prover<'a>, CompressedPoints) {
                use self::internal::*;
                use $crate::toolbox::prover::*;

                let mut prover = Prover::new(PROOF_LABEL.as_bytes(), transcript);

                let secret_vars = SecretVars {
                    $(
                        $secret_var: prover.allocate_scalar(
                            TRANSCRIPT_LABELS.$secret_var.as_bytes(),
                            *assignments.$secret_var,
                        ),
                    )+
                };

                struct VarPointPairs {
                    $( pub $instance_var: (PointVar, CompressedRistretto), )+
                    $( pub $common_var: (PointVar, CompressedRistretto), )+
                }

                let pairs = VarPointPairs {
                    $(
                        $instance_var: prover.allocate_point(
                            TRANSCRIPT_LABELS.$instance_var.as_bytes(),
                            *assignments.$instance_var,
                        ),
                    )+
                    $(
                        $common_var: prover.allocate_point(
                            TRANSCRIPT_LABELS.$common_var.as_bytes(),
                            *assignments.$common_var,
                        ),
                    )+
                };

                // XXX return compressed points
                let public_vars = PublicVars {
                    $($instance_var: pairs.$instance_var.0,)+
                    $($common_var: pairs.$common_var.0,)+
                };

                let compressed = CompressedPoints {
                    $($instance_var: pairs.$instance_var.1,)+
                    $($common_var: pairs.$common_var.1,)+
                };

                proof_statement(&mut prover, secret_vars, public_vars);

                (prover, compressed)
            }
				/// The transcript labels used for each secret variable.
                pub const TRANSCRIPT_LABELS: TranscriptLabels = TranscriptLabels {
                    $( $secret_var: stringify!($secret_var), )+
                    $( $instance_var: stringify!($instance_var), )+
                    $( $common_var: stringify!($common_var), )+
                };

				 /// The underlying proof statement generated by the macro invocation.
                ///
                /// This function exists separately from the proving
                /// and verification functions to allow composition of
                /// different proof statements with common variable
                /// assignments.
                pub fn proof_statement(
                    cs: &mut CS,
                    secrets: SecretVars,
                    publics: PublicVars,
                ) {
                    $(
                        cs.constrain(
                            publics.$lhs,
                            __compute_formula_constraint!( (publics, secrets) $statement ),
                        );
                    )+
                }
pub struct Prover<'a> {
    transcript: &'a mut Transcript,
    scalars: Vec,
    points: Vec,
    point_labels: Vec<&'static [u8]>,
    constraints: Vec<(PointVar, Vec<(ScalarVar, PointVar)>)>,
}

/// A secret variable used during proving.
#[derive(Copy, Clone)]
pub struct ScalarVar(usize);
/// A public variable used during proving.
#[derive(Copy, Clone)]
pub struct PointVar(usize);

/// Allocate and assign a secret variable with the given `label`.
    pub fn allocate_scalar(&mut self, label: &'static [u8], assignment: Scalar) -> ScalarVar {
        self.transcript.append_scalar_var(label);
        self.scalars.push(assignment);
        ScalarVar(self.scalars.len() - 1)
    }

	 /// The compact and batchable proofs differ only by which data they store.
    fn prove_impl(self) -> (Scalar, Vec, Vec) {
        // Construct a TranscriptRng
        let mut rng_builder = self.transcript.build_rng();
        for scalar in &self.scalars {
            rng_builder = rng_builder.commit_witness_bytes(b"", scalar.as_bytes());
        }
        let mut transcript_rng = rng_builder.finalize(&mut thread_rng());

        // Generate a blinding factor for each secret variable
        let blindings = self
            .scalars
            .iter()
            .map(|_| Scalar::random(&mut transcript_rng))
            .collect::>();

        // Commit to each blinded LHS
        let mut commitments = Vec::with_capacity(self.constraints.len());
        for (lhs_var, rhs_lc) in &self.constraints {
            let commitment = RistrettoPoint::multiscalar_mul(
                rhs_lc.iter().map(|(sc_var, _pt_var)| blindings[sc_var.0]),
                rhs_lc.iter().map(|(_sc_var, pt_var)| self.points[pt_var.0]),
            );
            let encoding = self
                .transcript
                .append_blinding_commitment(self.point_labels[lhs_var.0], &commitment);

            commitments.push(encoding);
        }

        // Obtain a scalar challenge and compute responses
        let challenge = self.transcript.get_challenge(b"chal");
        let responses = Iterator::zip(self.scalars.iter(), blindings.iter())
            .map(|(s, b)| s * challenge + b)
            .collect::>();

        (challenge, responses, commitments)
    }

    /// Consume this prover to produce a compact proof.
    pub fn prove_compact(self) -> CompactProof {
        let (challenge, responses, _) = self.prove_impl();

        CompactProof {
            challenge,
            responses,
        }
    }

    /// Consume this prover to produce a batchable proof.
    pub fn prove_batchable(self) -> BatchableProof {
        let (_, responses, commitments) = self.prove_impl();

        BatchableProof {
            commitments,
            responses,
        }
    }
			

参考资料:
[1] zkp
[2] merlin
[3] Schnorr signature (Schnorr 签名)数学原理
[4] Merlin——零知识证明(2)应用篇
[5] https://medium.com/@hdevalence/merlin-flexible-composable-transcripts-for-zero-knowledge-proofs-28d9fda22d9a

你可能感兴趣的:(dalek-cryptography/zkp——基于merlin的Schnorr零知识证明)