コード例 #1
0
 internal void CheckSolution(ScalarVector witness)
 {
     if (PublicPoint != witness * Generators)
     {
         throw new ArgumentException($"{nameof(witness)} is not solution of the equation");
     }
 }
コード例 #2
0
 // Given a witness and secret nonces, respond to a challenge proving the equation holds w.r.t the witness
 internal ScalarVector Respond(ScalarVector witness, ScalarVector secretNonces, Scalar challenge)
 {
     // By canceling G on both sides of the verification equation above we can
     // obtain a formula for the response s given k, e and x:
     //   s = k + ex
     return(secretNonces + challenge * witness);
 }
コード例 #3
0
 // Simulate a public nonce given a challenge and arbitrary responses (should be random)
 internal GroupElement Simulate(Scalar challenge, ScalarVector givenResponses)
 {
     // The verification equation above can be rearranged as a formula for R
     // given e, P and s by subtracting eP from both sides:
     //   R = sG - eP
     return(givenResponses * Generators - challenge * PublicPoint);
 }
コード例 #4
0
        internal Proof(GroupElementVector publicNonces, ScalarVector responses)
        {
            Guard.NotNullOrInfinity(nameof(publicNonces), publicNonces);
            Guard.NotNullOrEmpty(nameof(responses), responses);

            PublicNonces = publicNonces;
            Responses    = responses;
        }
コード例 #5
0
 // Evaluate the verification equation corresponding to the one in the statement
 internal bool Verify(GroupElement publicNonce, Scalar challenge, ScalarVector responses)
 {
     // the verification equation (for 1 generator case) is:
     //   sG =? R + eP
     // where:
     //   - R = kG is the public nonce, k is the secret nonce
     //   - P = xG is the public input, x is the secret
     //   - e is the challenge
     //   - s is the response
     return(responses * Generators == (publicNonce + challenge * PublicPoint));
 }
コード例 #6
0
        public void VerifyResponses(uint scalarSeed1, uint scalarSeed2)
        {
            var witness     = new ScalarVector(new Scalar(scalarSeed1), new Scalar(scalarSeed2));
            var generators  = new GroupElementVector(Generators.G, Generators.Ga);
            var publicPoint = witness * generators;

            var equation = new Equation(publicPoint, generators);

            // First, demonstrate proving knowledge with the witness
            var secretNonces = new ScalarVector(new Scalar(23), new Scalar(42));
            var publicNonce  = Enumerable.Zip(secretNonces, generators, (s, g) => s * g).Sum();
            var challenge    = new Scalar(101);
            var response     = Equation.Respond(witness, secretNonces, challenge);

            Assert.True(equation.Verify(publicNonce, challenge, response));

            var otherChallenge = new Scalar(103);

            // The verifier should reject invalid transcripts. This requires
            // an exception for when the public input is the point at infinity,
            // because with a different challenge the nonce should be different
            // but if the secret is 0, due to the absorption property the response
            // will be the same with the same nonce.
            if (scalarSeed1 != 0 && scalarSeed2 != 0)
            {
                Assert.False(equation.Verify(publicNonce, otherChallenge, response));
            }

            // Modifying the response should invalidate the equation
            var modifiedResponse = new ScalarVector(response.Select((x, i) => i == 0 ? x + Scalar.One : x));

            Assert.False(equation.Verify(publicNonce, challenge, modifiedResponse));
            Assert.True(equation.Verify(publicNonce + Generators.G, challenge, modifiedResponse));

            // A proof made with a different witness must be rejected. If the
            // challenge is 0 (negligible probability with Fiat-Shamir under ROM) in
            // which case any witness satisfies the equation, but the verifier should
            // still reject.
            var badWitness = new ScalarVector(new Scalar(scalarSeed1 + 1), new Scalar(scalarSeed2));

            Assert.False(equation.Verify(publicNonce, challenge, Equation.Respond(badWitness, secretNonces, challenge)));
            Assert.False(equation.Verify(publicNonce, Scalar.Zero, Equation.Respond(badWitness, secretNonces, Scalar.Zero)));

            // A proof made with different secret nonces must also be rejected, unless
            // the public nonce is tweaked (not possible with Fiat-Shamir without a
            // 2nd pre-image attack on the hash function).
            var modifiedSecretNonces  = new ScalarVector(secretNonces.Select((x, i) => i == 0 ? x + Scalar.One : x));
            var modifiedNonceResponse = Equation.Respond(witness, modifiedSecretNonces, challenge);

            Assert.False(equation.Verify(publicNonce, challenge, modifiedNonceResponse));
            Assert.True(equation.Verify(publicNonce + Generators.G, challenge, modifiedNonceResponse));
        }
コード例 #7
0
        public void VerifyResponsesAndSimulations(uint scalarSeed1, uint scalarSeed2)
        {
            var witness     = new ScalarVector(new Scalar(scalarSeed1), new Scalar(scalarSeed2));
            var generators  = new GroupElementVector(Generators.G, Generators.Ga);
            var publicPoint = witness * generators;

            var equation = new Equation(publicPoint, generators);

            // First, demonstrate proving knowledge with the witness
            var secretNonces = new ScalarVector(new Scalar(23), new Scalar(42));
            var publicNonce  = Enumerable.Zip(secretNonces, generators, (s, g) => s * g).Sum();
            var challenge    = new Scalar(101);
            var response     = equation.Respond(witness, secretNonces, challenge);

            Assert.True(equation.Verify(publicNonce, challenge, response));

            // Even without a witness, simulated proofs with the same response should still verify
            var simulatedNonce = equation.Simulate(challenge, response);

            Assert.True(equation.Verify(simulatedNonce, challenge, response));

            // And the simulated prover commitment should be the same as the real one
            // even if its discrete log w.r.t. the generators is not known
            Assert.True(simulatedNonce == publicNonce);

            // With a different challenge the nonce should be different
            // unless the secret is 0, due to the absorption property
            var otherChallenge      = new Scalar(103);
            var otherSimulatedNonce = equation.Simulate(otherChallenge, response);

            Assert.True(equation.Verify(otherSimulatedNonce, otherChallenge, response));
            if (scalarSeed1 != 0 && scalarSeed2 != 0)
            {
                Assert.True(otherSimulatedNonce != publicNonce);
            }

            // And with a different response the verifier should still accept
            var otherResponse       = new ScalarVector(new Scalar(2), new Scalar(3));
            var thirdSimulatedNonce = equation.Simulate(challenge, otherResponse);

            Assert.True(equation.Verify(thirdSimulatedNonce, challenge, otherResponse));
            Assert.True(thirdSimulatedNonce != otherSimulatedNonce);
            Assert.True(thirdSimulatedNonce != publicNonce);

            // The verifying should reject invalid transcripts, and this also requires
            // an exception for when the public input is the point at infinity
            if (scalarSeed1 != 0 && scalarSeed2 != 0)
            {
                Assert.False(equation.Verify(simulatedNonce, otherChallenge, response));
                Assert.False(equation.Verify(publicNonce, otherChallenge, response));
            }
        }
コード例 #8
0
ファイル: Knowledge.cs プロジェクト: xmess7/WalletWasabi
        internal Knowledge(Statement statement, ScalarVector witness)
        {
            Guard.True(nameof(witness), witness.Count() == statement.Equations.First().Generators.Count(), $"{nameof(witness)} size does not match {nameof(statement)}.{nameof(statement.Equations)}");

            // don't try to prove something which isn't true
            foreach (var equation in statement.Equations)
            {
                equation.CheckSolution(witness);
            }

            Statement = statement;
            Witness   = witness;
        }
コード例 #9
0
    public void End2EndVerificationSimple(uint scalarSeed1, uint scalarSeed2)
    {
        var secrets     = new ScalarVector(new Scalar(scalarSeed1), new Scalar(scalarSeed2));
        var generators  = new GroupElementVector(Generators.G, Generators.Ga);
        var publicPoint = secrets * generators;

        var statement  = new Statement(publicPoint, generators);
        var mockRandom = new Mock <WasabiRandom>(MockBehavior.Strict);

        mockRandom.Setup(rnd => rnd.GetBytes(32)).Returns(new byte[32]);
        var proof = ProofSystemHelpers.Prove(statement, secrets, mockRandom.Object);

        Assert.True(ProofSystemHelpers.Verify(statement, proof));
    }
コード例 #10
0
        public void End2EndVerificationSimple(uint scalarSeed1, uint scalarSeed2)
        {
            var secrets     = new ScalarVector(new Scalar(scalarSeed1), new Scalar(scalarSeed2));
            var generators  = new GroupElementVector(Generators.G, Generators.Ga);
            var publicPoint = secrets * generators;

            var statement = new Statement(publicPoint, generators);
            var random    = new MockRandom();

            random.GetBytesResults.Add(new byte[32]);
            var proof = ProofSystemHelpers.Prove(statement, secrets, random);

            Assert.True(ProofSystemHelpers.Verify(statement, proof));
        }
コード例 #11
0
ファイル: Knowledge.cs プロジェクト: zero77/WalletWasabi
        public Knowledge(Statement statement, ScalarVector witness)
        {
            Guard.NotNull(nameof(statement), statement);
            Guard.NotNullOrEmpty(nameof(witness), witness);
            Guard.True($"{nameof(witness)} size does not match {nameof(statement)}.{nameof(statement.Equations)}", witness.Count() == statement.Equations.First().Generators.Count());

            // don't try to prove something which isn't true
            foreach (var equation in statement.Equations)
            {
                Guard.True($"{nameof(witness)} is not solution of the {nameof(equation)}", equation.VerifySolution(witness));
            }

            Statement = statement;
            Witness   = witness;
        }
コード例 #12
0
        // Given a witness and secret nonces, respond to a challenge proving the equation holds w.r.t the witness
        internal static ScalarVector Respond(ScalarVector witness, ScalarVector secretNonces, Scalar challenge)
        {
            // blinding terms are required in order to protect the witness (unless the
            // challenge is 0), so only respond if that is the case
            foreach (var secretNonce in secretNonces)
            {
                Guard.NotZero(nameof(secretNonce), secretNonce);
            }

            // Taking the discrete logarithms of both sides of the verification
            // equation with respect to G results in a formula for the response s
            // given k, e and x:
            //   s = k + ex
            return(secretNonces + challenge * witness);
        }
コード例 #13
0
        // Evaluate the verification equation corresponding to the one in the statement
        internal bool Verify(GroupElement publicNonce, Scalar challenge, ScalarVector responses)
        {
            // A challenge of 0 does not place any constraint on the witness
            if (challenge.IsZero)
            {
                return(false);
            }

            // the verification equation (for 1 generator case) is:
            //   sG =? R + eP
            // where:
            //   - R = kG is the public nonce, k is the secret nonce
            //   - P = xG is the public input, x is the secret
            //   - e is the challenge
            //   - s is the response
            return(responses * Generators == (publicNonce + challenge * PublicPoint));
        }
コード例 #14
0
        public void End2EndVerification()
        {
            var goodScalars = CryptoHelpers.GetScalars(x => !x.IsOverflow && !x.IsZero);

            foreach (var secret1 in goodScalars)
            {
                foreach (var secret2 in goodScalars.Where(x => x != secret1))
                {
                    var secrets     = new ScalarVector(secret1, secret2);
                    var generators  = new GroupElementVector(Generators.G, Generators.Ga);
                    var publicPoint = secrets * generators;
                    var statement   = new Statement(publicPoint, generators);
                    var proof       = ProofSystemHelpers.Prove(statement, secrets, new SecureRandom());
                    Assert.True(ProofSystemHelpers.Verify(statement, proof));
                }
            }
        }
コード例 #15
0
        public void CanProveAndVerifyMAC()
        {
            // The coordinator generates a composed private key called CoordinatorSecretKey
            // and derives from that the coordinator's public parameters called CoordinatorParameters.
            var rnd                   = new SecureRandom();
            var coordinatorKey        = new CoordinatorSecretKey(rnd);
            var coordinatorParameters = coordinatorKey.ComputeCoordinatorParameters();

            // A blinded amount is known as an `attribute`. In this case the attribute Ma is the
            // value 10000 blinded with a random `blindingFactor`. This attribute is sent to
            // the coordinator.
            var amount = new Scalar(10_000);
            var r      = rnd.GetScalar();
            var Ma     = amount * Generators.G + r * Generators.Gh;

            // The coordinator generates a MAC and a proof that the MAC was generated using the
            // coordinator's secret key. The coordinator sends the pair (MAC + proofOfMac) back
            // to the client.
            var t   = rnd.GetScalar();
            var mac = MAC.ComputeMAC(coordinatorKey, Ma, t);

            var coordinatorKnowledge = ProofSystem.IssuerParameters(mac, Ma, coordinatorKey);
            var proofOfMac           = ProofSystem.Prove(coordinatorKnowledge, rnd);

            // The client receives the MAC and the proofOfMac which let the client know that the MAC
            // was generated with the coordinator's secret key.
            var clientStatement = ProofSystem.IssuerParameters(coordinatorParameters, mac, Ma);
            var isValidProof    = ProofSystem.Verify(clientStatement, proofOfMac);

            Assert.True(isValidProof);

            var corruptedResponses = new ScalarVector(proofOfMac.Responses.Reverse());
            var invalidProofOfMac  = new Proof(proofOfMac.PublicNonces, corruptedResponses);

            isValidProof = ProofSystem.Verify(clientStatement, invalidProofOfMac);
            Assert.False(isValidProof);

            var corruptedPublicNonces = new GroupElementVector(proofOfMac.PublicNonces.Reverse());

            invalidProofOfMac = new Proof(corruptedPublicNonces, proofOfMac.Responses);
            isValidProof      = ProofSystem.Verify(clientStatement, invalidProofOfMac);
            Assert.False(isValidProof);
        }
コード例 #16
0
        public static IEnumerable <Proof> Prove(Transcript transcript, IEnumerable <Knowledge> knowledge, WasabiRandom random)
        {
            // Before anything else all components in a compound proof commit to the
            // individual sub-statement that will be proven, ensuring that the
            // challenges and therefore the responses depend on the statement as a
            // whole.
            foreach (var k in knowledge)
            {
                transcript.CommitStatement(k.Statement);
            }

            var deferredResponds = new List <DeferredProofCreator>();

            foreach (var k in knowledge)
            {
                // With all the statements committed, generate a vector of random secret
                // nonces for every equation in underlying proof system. In order to
                // ensure that nonces are never reused (e.g. due to an insecure RNG) with
                // different challenges which would leak the witness, these are generated
                // as synthetic nonces that also depend on the witness data.
                var          secretNonceProvider = transcript.CreateSyntheticSecretNonceProvider(k.Witness, random);
                ScalarVector secretNonces        = secretNonceProvider.GetScalarVector();

                // The prover then commits to these, adding the corresponding public
                // points to the transcript.
                var equations    = k.Statement.Equations;
                var publicNonces = new GroupElementVector(equations.Select(equation => secretNonces * equation.Generators));
                transcript.CommitPublicNonces(publicNonces);

                deferredResponds.Add((challenge) => new Proof(publicNonces, k.RespondToChallenge(challenge, secretNonces)));
            }

            // With the public nonces committed to the transcript the prover can then
            // derive a challenge that depend on the transcript state without needing
            // to interact with the verifier, but ensuring that they can't know the
            // challenge before the prover commitments are generated.
            Scalar challenge = transcript.GenerateChallenge();

            return(deferredResponds.Select(createProof => createProof(challenge)));
        }
コード例 #17
0
        public void IgnoredWitnessComponents()
        {
            // Sometimes an equation uses the point at infinity as a generator,
            // effectively canceling out the corresponding component of the witness
            var generators  = new GroupElementVector(Generators.G, GroupElement.Infinity);
            var publicPoint = new Scalar(42) * Generators.G;
            var equation    = new Equation(publicPoint, generators);

            var witness1 = new ScalarVector(new Scalar(42), new Scalar(23));
            var witness2 = new ScalarVector(new Scalar(42), new Scalar(100));

            // Generate a single nonce to be shared by both proofs.
            // note that in normal circumstances this is catastrophic because nonce
            // reuse with different challenges allows recovery of the witness.
            // in this case this is intentional, so that the test can compare the
            // responses which would otherwise be different.
            var secretNonces = new ScalarVector(new Scalar(7), new Scalar(11));
            var publicNonce  = Enumerable.Zip(secretNonces, generators, (s, g) => s * g).Sum();
            var challenge    = new Scalar(13);

            // Derive two responses with the two different witnesses for the same
            // point, and ensure that both are valid, implying that the second
            // component in the witness is ignored.
            var response1 = Equation.Respond(witness1, secretNonces, challenge);

            Assert.True(equation.Verify(publicNonce, challenge, response1));
            var response2 = Equation.Respond(witness2, secretNonces, challenge);

            Assert.True(equation.Verify(publicNonce, challenge, response2));

            // With different witnesses the responses should be different even if the
            // nonces are the same, but since the first part of the witness is the
            // same that sub-response should be the same for the same nonce
            Assert.False(response1 == response2);
            Assert.True(response1.First() == response2.First());
        }
コード例 #18
0
        public void StatementAndKnowledge()
        {
            var x = new Scalar(42);
            var a = x * Generators.Gg;
            var b = x * Generators.Gh;

            // Discrete log equality (Chaum-Pedersen proof)
            var statement = new Statement(new GroupElement[, ]
            {
                { a, Generators.Gg },
                { b, Generators.Gh },
            });

            var challenge = new Scalar(13);

            // Create transcripts using a witness to the relation
            var knowledge    = new Knowledge(statement, new ScalarVector(x));
            var secretNonces = new ScalarVector(new Scalar(7));
            var publicNonces = new GroupElementVector(statement.Equations.Select(equation => secretNonces * equation.Generators));
            var responses    = knowledge.RespondToChallenge(challenge, secretNonces);

            Assert.Single(responses);
            Assert.True(statement.CheckVerificationEquation(publicNonces, challenge, responses));

            // Ensure that verifier rejects invalid transcripts
            Assert.False(statement.CheckVerificationEquation(publicNonces, new Scalar(17), responses));

            // Ensure that verifier rejects when proofs have been altered but not
            // when they are valid (which are prevented using Fiat-Shamir transform)
            var modifiedNonces    = new GroupElementVector(publicNonces.First() + Generators.Gg, publicNonces.Last() + Generators.Gh);
            var modifiedResponses = new ScalarVector(responses.Select(x => x + Scalar.One));

            Assert.True(statement.CheckVerificationEquation(modifiedNonces, challenge, modifiedResponses));
            Assert.False(statement.CheckVerificationEquation(modifiedNonces, challenge, responses));
            Assert.False(statement.CheckVerificationEquation(publicNonces, challenge, modifiedResponses));
        }
コード例 #19
0
 internal ScalarVector RespondToChallenge(Scalar challenge, ScalarVector secretNonces) =>
 Equation.Respond(Witness, secretNonces, challenge);
コード例 #20
0
 internal bool VerifySolution(ScalarVector witness)
 {
     return(PublicPoint == witness * Generators);
 }
コード例 #21
0
        public void FiatShamirComposition()
        {
            var rnd = new MockRandom();

            rnd.GetBytesResults.Add(new byte[32]);
            rnd.GetBytesResults.Add(new byte[32]);

            var witness1 = new ScalarVector(Scalar.One);
            var witness2 = new ScalarVector(Scalar.One + Scalar.One);

            var g = new GroupElementVector(Generators.G);

            var publicPoint1 = witness1 * g;
            var publicPoint2 = witness2 * g;

            var statement1 = new Statement(new Equation(publicPoint1, g));
            var statement2 = new Statement(new Equation(publicPoint2, g));

            var prover1 = new Prover(new Knowledge(statement1, witness1));
            var prover2 = new Prover(new Knowledge(statement2, witness2));

            var proverTranscript   = new WalletWasabi.Crypto.ZeroKnowledge.Transcript(new byte[0]);
            var verifierTranscript = proverTranscript.MakeCopy();

            var prover1Nonces = prover1.CommitToStatements(proverTranscript);
            var prover2Nonces = prover2.CommitToStatements(proverTranscript);

            var prover1Respond = prover1Nonces(rnd);
            var prover2Respond = prover2Nonces(rnd);

            var proof1 = prover1Respond();
            var proof2 = prover2Respond();

            var verifier1 = new Verifier(statement1);
            var verifier2 = new Verifier(statement2);

            // First, verify as a compound proof
            var correctVerifierTranscript = verifierTranscript.MakeCopy();
            var correctVerifier1Nonces    = verifier1.CommitToStatements(correctVerifierTranscript);
            var correctVerifier2Nonces    = verifier2.CommitToStatements(correctVerifierTranscript);
            var correctVerifier1Verify    = correctVerifier1Nonces(proof1);
            var correctVerifier2Verify    = correctVerifier2Nonces(proof2);

            Assert.True(correctVerifier1Verify());
            Assert.True(correctVerifier2Verify());

            // If the verifiers are not run interleaved, they should reject.
            var notInterleavedVerifierTranscript = verifierTranscript.MakeCopy();
            var notInterleavedVerifier1Nonces    = verifier1.CommitToStatements(correctVerifierTranscript);
            var notInterleavedVerifier1Verify    = notInterleavedVerifier1Nonces(proof1);

            Assert.False(notInterleavedVerifier1Verify());
            var notInterleavedVerifier2Nonces = verifier2.CommitToStatements(correctVerifierTranscript);
            var notInterleavedVerifier2Verify = notInterleavedVerifier2Nonces(proof2);

            Assert.False(notInterleavedVerifier2Verify());

            // If the verifiers are run independently (without sharing a transcript),
            // they should reject.
            var incompleteTranscript1 = verifierTranscript.MakeCopy();
            var incompleteTranscript2 = verifierTranscript.MakeCopy();
            var incompleteTranscriptVerifier1Nonces = verifier1.CommitToStatements(incompleteTranscript1);
            var incompleteTranscriptVerifier2Nonces = verifier2.CommitToStatements(incompleteTranscript2);
            var incompleteTranscriptVerifier1Verify = incompleteTranscriptVerifier1Nonces(proof1);
            var incompleteTranscriptVerifier2Verify = incompleteTranscriptVerifier2Nonces(proof2);

            Assert.False(incompleteTranscriptVerifier1Verify());
            Assert.False(incompleteTranscriptVerifier2Verify());

            // If the sub-proofs are swapped between the verifiers, they should reject.
            var incorrectProofVerifierTranscript = verifierTranscript.MakeCopy();
            var incorrectProofVerifier1Nonces    = verifier1.CommitToStatements(correctVerifierTranscript);
            var incorrectProofVerifier2Nonces    = verifier2.CommitToStatements(correctVerifierTranscript);
            var incorrectProofVerifier1Verify    = incorrectProofVerifier1Nonces(proof2);
            var incorrectProofVerifier2Verify    = incorrectProofVerifier2Nonces(proof1);

            Assert.False(incorrectProofVerifier1Verify());
            Assert.False(incorrectProofVerifier2Verify());

            // If the order of the verifiers is changed, they should reject.
            var incorrectOrderVerifierTranscript = verifierTranscript.MakeCopy();
            var incorrectOrderVerifier1Nonces    = verifier1.CommitToStatements(correctVerifierTranscript);
            var incorrectOrderVerifier2Nonces    = verifier2.CommitToStatements(correctVerifierTranscript);
            var incorrectOrderVerifier2Verify    = incorrectOrderVerifier2Nonces(proof2);
            var incorrectOrderVerifier1Verify    = incorrectOrderVerifier1Nonces(proof1);

            Assert.False(incorrectOrderVerifier1Verify());
            Assert.False(incorrectOrderVerifier2Verify());

            // If the proofs are committed to the transcript in the right order but
            // with the wrong verifier (combination of previous two cases) they should
            // reject.
            var incorrectOrderAndProofVerifierTranscript = verifierTranscript.MakeCopy();
            var incorrectOrderAndProofVerifier1Nonces    = verifier1.CommitToStatements(correctVerifierTranscript);
            var incorrectOrderAndProofVerifier2Nonces    = verifier2.CommitToStatements(correctVerifierTranscript);
            var incorrectOrderAndProofVerifier2Verify    = incorrectOrderAndProofVerifier2Nonces(proof1);
            var incorrectOrderAndProofVerifier1Verify    = incorrectOrderAndProofVerifier1Nonces(proof2);

            Assert.False(incorrectOrderAndProofVerifier2Verify());
            Assert.False(incorrectOrderAndProofVerifier1Verify());
        }
コード例 #22
0
 internal bool VerifySolution(ScalarVector witness)
 => PublicPoint == witness * Generators;
コード例 #23
0
 public static Proof Prove(Statement statement, ScalarVector witness, WasabiRandom random)
 {
     return(Prove(new Knowledge(statement, witness), random));
 }
コード例 #24
0
        public bool CheckVerificationEquation(GroupElementVector publicNonces, Scalar challenge, ScalarVector responses)
        {
            // The responses matrix should match the generators in the equations and
            // there should be once nonce per equation.
            Guard.True(nameof(publicNonces), Equations.Count() == publicNonces.Count());

            return(Equations.Zip(publicNonces, (equation, r) => equation.Verify(r, challenge, responses)).All(x => x));
        }
コード例 #25
0
ファイル: Statement.cs プロジェクト: zero77/WalletWasabi
 public Knowledge ToKnowledge(ScalarVector witness) =>
 new Knowledge(this, witness);