Move Verify and Prove to the ProofSystem class
ADD
WalletWasabi.Tests/UnitTests/Crypto/ProofSystemHelpers.cs
+using System;
+using System.Linq;
+using NBitcoin.Secp256k1;
+using WalletWasabi.Crypto;
+using WalletWasabi.Crypto.Randomness;
+using WalletWasabi.Crypto.ZeroKnowledge;
+using WalletWasabi.Crypto.ZeroKnowledge.LinearRelation;
+using WalletWasabi.Crypto.ZeroKnowledge.NonInteractive;
+using Xunit;
+
+namespace WalletWasabi.Tests.UnitTests.Crypto
+{
+ public class ProofSystemHelpers
+ {
+ public static bool Verify(Statement statement, Proof proof)
+ {
+ return ProofSystem.Verify(new Transcript(new byte[0]), new[] { statement }, new[] { proof });
+ }
+
+ public static Proof Prove(Knowledge knowledge, WasabiRandom random)
+ {
+ return ProofSystem.Prove(new Transcript(new byte[0]), new[] { knowledge }, random).First();
+ }
+
+ public static Proof Prove(Statement statement, Scalar witness, WasabiRandom random)
+ {
+ return Prove(statement, new ScalarVector(witness), random);
+ }
+
+ public static Proof Prove(Statement statement, ScalarVector witness, WasabiRandom random)
+ {
+ return Prove(new Knowledge(statement, witness), random);
+ }
+ }
+}
\ No newline at end of file

MODIFY
WalletWasabi.Tests/UnitTests/Crypto/ProofSystemTests.cs
WalletWasabi.Tests/UnitTests/Crypto/ProofSystemTests.cs
@@ -36,22 +36,22 @@ namespace WalletWasabi.Tests.UnitTests.Crypto
var mac = MAC.ComputeMAC(coordinatorKey, Ma, t);
var coordinatorKnowledge = ProofSystem.IssuerParameters(mac, Ma, coordinatorKey);
- var proofOfMac = ProofSystem.Prove(coordinatorKnowledge, rnd);
+ var proofOfMac = ProofSystemHelpers.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);
+ var isValidProof = ProofSystemHelpers.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);
+ isValidProof = ProofSystemHelpers.Verify(clientStatement, invalidProofOfMac);
Assert.False(isValidProof);
var corruptedPublicNonces = new GroupElementVector(proofOfMac.PublicNonces.Reverse());
invalidProofOfMac = new Proof(corruptedPublicNonces, proofOfMac.Responses);
- isValidProof = ProofSystem.Verify(clientStatement, invalidProofOfMac);
+ isValidProof = ProofSystemHelpers.Verify(clientStatement, invalidProofOfMac);
Assert.False(isValidProof);
}
@@ -82,14 +82,14 @@ namespace WalletWasabi.Tests.UnitTests.Crypto
var z = rnd.GetScalar();
var randomizedCredential = credential.Present(z);
var knowledge = ProofSystem.ShowCredential(randomizedCredential, z, credential, coordinatorParameters);
- var proofOfMacShow = ProofSystem.Prove(knowledge, rnd);
+ var proofOfMacShow = ProofSystemHelpers.Prove(knowledge, rnd);
// The coordinator must verify the received randomized credential is valid.
var Z = randomizedCredential.ComputeZ(coordinatorKey);
Assert.Equal(Z, z * coordinatorParameters.I);
var statement = ProofSystem.ShowCredential(randomizedCredential, Z, coordinatorParameters);
- var isValidProof = ProofSystem.Verify(statement, proofOfMacShow);
+ var isValidProof = ProofSystemHelpers.Verify(statement, proofOfMacShow);
Assert.True(isValidProof);
}
@@ -106,16 +106,16 @@ namespace WalletWasabi.Tests.UnitTests.Crypto
var Ca = z * Generators.Ga + a * Generators.Gg + r * Generators.Gh;
var knowledge = ProofSystem.BalanceProof(z, r);
- var proofOfBalance = ProofSystem.Prove(knowledge, rnd);
+ var proofOfBalance = ProofSystemHelpers.Prove(knowledge, rnd);
var statement = ProofSystem.BalanceProof(Ca - a * Generators.Gg);
- Assert.True(ProofSystem.Verify(statement, proofOfBalance));
+ Assert.True(ProofSystemHelpers.Verify(statement, proofOfBalance));
var badStatement = ProofSystem.BalanceProof(Ca + Generators.Gg - a * Generators.Gg);
- Assert.False(ProofSystem.Verify(badStatement, proofOfBalance));
+ Assert.False(ProofSystemHelpers.Verify(badStatement, proofOfBalance));
badStatement = ProofSystem.BalanceProof(Ca);
- Assert.False(ProofSystem.Verify(badStatement, proofOfBalance));
+ Assert.False(ProofSystemHelpers.Verify(badStatement, proofOfBalance));
}
[Fact]
@@ -129,13 +129,13 @@ namespace WalletWasabi.Tests.UnitTests.Crypto
var Ma = a * Generators.Gg + r * Generators.Gh;
var knowledge = ProofSystem.BalanceProof(Scalar.Zero, r.Negate());
- var proofOfBalance = ProofSystem.Prove(knowledge, rnd);
+ var proofOfBalance = ProofSystemHelpers.Prove(knowledge, rnd);
var statement = ProofSystem.BalanceProof(a * Generators.Gg - Ma);
- Assert.True(ProofSystem.Verify(statement, proofOfBalance));
+ Assert.True(ProofSystemHelpers.Verify(statement, proofOfBalance));
var badStatement = ProofSystem.BalanceProof(Ma);
- Assert.False(ProofSystem.Verify(badStatement, proofOfBalance));
+ Assert.False(ProofSystemHelpers.Verify(badStatement, proofOfBalance));
}
[Theory]
@@ -169,13 +169,13 @@ namespace WalletWasabi.Tests.UnitTests.Crypto
delta = presentedAmount > requestedAmount ? delta.Negate() : delta;
var knowledge = ProofSystem.BalanceProof(z, r + rp.Negate());
- var proofOfBalance = ProofSystem.Prove(knowledge, rnd);
+ var proofOfBalance = ProofSystemHelpers.Prove(knowledge, rnd);
var statement = ProofSystem.BalanceProof(Ca + delta * Generators.Gg - Ma);
- Assert.True(ProofSystem.Verify(statement, proofOfBalance));
+ Assert.True(ProofSystemHelpers.Verify(statement, proofOfBalance));
var badStatement = ProofSystem.BalanceProof(Ca + (delta + Scalar.One) * Generators.Gg - Ma);
- Assert.False(ProofSystem.Verify(badStatement, proofOfBalance));
+ Assert.False(ProofSystemHelpers.Verify(badStatement, proofOfBalance));
}
[Theory]
@@ -206,9 +206,9 @@ namespace WalletWasabi.Tests.UnitTests.Crypto
var maskedScalar = new Scalar(amount & ((1ul << width) - 1));
var (knowledge, bitCommitments) = ProofSystem.RangeProof(maskedScalar, randomness, width, rnd);
- var rangeProof = ProofSystem.Prove(knowledge, rnd);
+ var rangeProof = ProofSystemHelpers.Prove(knowledge, rnd);
- Assert.Equal(pass, ProofSystem.Verify(ProofSystem.RangeProof(commitment, bitCommitments), rangeProof));
+ Assert.Equal(pass, ProofSystemHelpers.Verify(ProofSystem.RangeProof(commitment, bitCommitments), rangeProof));
if (!pass)
{
@@ -236,7 +236,7 @@ namespace WalletWasabi.Tests.UnitTests.Crypto
ProofSystem.ZeroProof(Ma1, r1)
};
- var proofs = Prover.Prove(new Transcript(new byte[0]), knowledge, rnd);
+ var proofs = ProofSystem.Prove(new Transcript(new byte[0]), knowledge, rnd);
var statements = new[]
{
@@ -244,7 +244,7 @@ namespace WalletWasabi.Tests.UnitTests.Crypto
ProofSystem.ZeroProof(Ma1)
};
- Assert.True(Verifier.Verify(new Transcript(new byte[0]), statements, proofs));
+ Assert.True(ProofSystem.Verify(new Transcript(new byte[0]), statements, proofs));
}
}
}

MODIFY
WalletWasabi.Tests/UnitTests/Crypto/ZeroKnowledge/KnowledgeOfDlogTests.cs
WalletWasabi.Tests/UnitTests/Crypto/ZeroKnowledge/KnowledgeOfDlogTests.cs
@@ -26,8 +26,8 @@ namespace WalletWasabi.Tests.UnitTests.Crypto.ZeroKnowledge
var statement = new Statement(publicPoint, generator);
var random = new MockRandom();
random.GetBytesResults.Add(new byte[32]);
- var proof = ProofSystem.Prove(statement, secret, random);
- Assert.True(ProofSystem.Verify(statement, proof));
+ var proof = ProofSystemHelpers.Prove(statement, secret, random);
+ Assert.True(ProofSystemHelpers.Verify(statement, proof));
}
[Fact]
@@ -38,8 +38,8 @@ namespace WalletWasabi.Tests.UnitTests.Crypto.ZeroKnowledge
var generator = Generators.G;
var publicPoint = secret * generator;
var statement = new Statement(publicPoint, Generators.G);
- var proof = ProofSystem.Prove(statement, secret, new SecureRandom());
- Assert.True(ProofSystem.Verify(statement, proof));
+ var proof = ProofSystemHelpers.Prove(statement, secret, new SecureRandom());
+ Assert.True(ProofSystemHelpers.Verify(statement, proof));
}
}
@@ -53,32 +53,32 @@ namespace WalletWasabi.Tests.UnitTests.Crypto.ZeroKnowledge
var secret = new Scalar(val, val, val, val, val, val, val, val);
var p = secret * gen;
var statement = new Statement(p, gen);
- var proof = ProofSystem.Prove(statement, secret, random);
- Assert.True(ProofSystem.Verify(statement, proof));
+ var proof = ProofSystemHelpers.Prove(statement, secret, random);
+ Assert.True(ProofSystemHelpers.Verify(statement, proof));
secret = EC.N + Scalar.One.Negate();
p = secret * gen;
statement = new Statement(p, gen);
- proof = ProofSystem.Prove(statement, secret, random);
- Assert.True(ProofSystem.Verify(statement, proof));
+ proof = ProofSystemHelpers.Prove(statement, secret, random);
+ Assert.True(ProofSystemHelpers.Verify(statement, proof));
secret = EC.NC;
p = secret * gen;
statement = new Statement(p, gen);
- proof = ProofSystem.Prove(statement, secret, random);
- Assert.True(ProofSystem.Verify(statement, proof));
+ proof = ProofSystemHelpers.Prove(statement, secret, random);
+ Assert.True(ProofSystemHelpers.Verify(statement, proof));
secret = EC.NC + Scalar.One;
p = secret * gen;
statement = new Statement(p, gen);
- proof = ProofSystem.Prove(statement, secret, random);
- Assert.True(ProofSystem.Verify(statement, proof));
+ proof = ProofSystemHelpers.Prove(statement, secret, random);
+ Assert.True(ProofSystemHelpers.Verify(statement, proof));
secret = EC.NC + Scalar.One.Negate();
p = secret * gen;
statement = new Statement(p, gen);
- proof = ProofSystem.Prove(statement, secret, random);
- Assert.True(ProofSystem.Verify(statement, proof));
+ proof = ProofSystemHelpers.Prove(statement, secret, random);
+ Assert.True(ProofSystemHelpers.Verify(statement, proof));
}
}
}

MODIFY
WalletWasabi.Tests/UnitTests/Crypto/ZeroKnowledge/KnowledgeOfRepTests.cs
WalletWasabi.Tests/UnitTests/Crypto/ZeroKnowledge/KnowledgeOfRepTests.cs
@@ -31,8 +31,8 @@ namespace WalletWasabi.Tests.UnitTests.Crypto.ZeroKnowledge
var statement = new Statement(publicPoint, generators);
var random = new MockRandom();
random.GetBytesResults.Add(new byte[32]);
- var proof = ProofSystem.Prove(statement, secrets, random);
- Assert.True(ProofSystem.Verify(statement, proof));
+ var proof = ProofSystemHelpers.Prove(statement, secrets, random);
+ Assert.True(ProofSystemHelpers.Verify(statement, proof));
}
[Fact]
@@ -47,8 +47,8 @@ namespace WalletWasabi.Tests.UnitTests.Crypto.ZeroKnowledge
var generators = new GroupElementVector(Generators.G, Generators.Ga);
var publicPoint = secrets * generators;
var statement = new Statement(publicPoint, generators);
- var proof = ProofSystem.Prove(statement, secrets, new SecureRandom());
- Assert.True(ProofSystem.Verify(statement, proof));
+ var proof = ProofSystemHelpers.Prove(statement, secrets, new SecureRandom());
+ Assert.True(ProofSystemHelpers.Verify(statement, proof));
}
}
}

DELETE
WalletWasabi/Crypto/ZeroKnowledge/NonInteractive/Prover.cs
-using NBitcoin.Secp256k1;
-using System.Collections.Generic;
-using System.Linq;
-using WalletWasabi.Crypto.Randomness;
-using WalletWasabi.Crypto.ZeroKnowledge.LinearRelation;
-
-namespace WalletWasabi.Crypto.ZeroKnowledge.NonInteractive
-{
- public static class Prover
- {
- private delegate Proof DeferredProofCreator(Scalar challenge);
-
- 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));
- }
- }
-}

DELETE
WalletWasabi/Crypto/ZeroKnowledge/NonInteractive/Verifier.cs
-using System.Linq;
-using System.Collections.Generic;
-using WalletWasabi.Crypto.ZeroKnowledge.LinearRelation;
-using WalletWasabi.Helpers;
-
-namespace WalletWasabi.Crypto.ZeroKnowledge.NonInteractive
-{
- public static class Verifier
- {
- public static bool Verify(Transcript transcript, IEnumerable<Statement> statements, IEnumerable<Proof> proofs)
- {
- Guard.Same(nameof(proofs), proofs.Count(), statements.Count());
-
- // 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 statement in statements)
- {
- transcript.CommitStatement(statement);
- }
-
- // After all the statements have been committed, the public nonces are
- // added to the transcript. This is done separately from the statement
- // commitments because the prover derives these based on the compound
- // statements, and the verifier must add data to the transcript in the
- // same order as the prover.
- foreach (var proof in proofs)
- {
- transcript.CommitPublicNonces(proof.PublicNonces);
- }
-
- // After all the public nonces have been committed, a challenge can be
- // generated based on transcript state. Since challenges are deterministic
- // outputs of a hash function which depends on the prover commitments, the
- // verifier obtains the same challenge and then accepts if the responses
- // satisfy the verification equation.
- var challenge = transcript.GenerateChallenge();
-
- return Enumerable.Zip(statements, proofs, (s, p) => s.CheckVerificationEquation(p.PublicNonces, challenge, p.Responses)).All(x => x);
- }
- }
-}

MODIFY
WalletWasabi/Crypto/ZeroKnowledge/ProofSystem.cs
WalletWasabi/Crypto/ZeroKnowledge/ProofSystem.cs
@@ -9,26 +9,83 @@ using WalletWasabi.Helpers;
namespace WalletWasabi.Crypto.ZeroKnowledge
{
- public static class ProofSystem
+ public static partial class ProofSystem
{
private static GroupElement O = GroupElement.Infinity;
- public static bool Verify(Statement statement, Proof proof)
+ private delegate Proof DeferredProofCreator(Scalar challenge);
+
+ 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)
{
- return Verifier.Verify(new Transcript(new byte[0]), new[] { statement }, new[] { proof });
+ transcript.CommitStatement(k.Statement);
}
- public static Proof Prove(Knowledge knowledge, WasabiRandom random)
+ var deferredResponds = new List<DeferredProofCreator>();
+ foreach (var k in knowledge)
{
- return Prover.Prove(new Transcript(new byte[0]), new[] { knowledge }, random).First();
+ // 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)));
}
- // Syntactic sugar used in tests
- public static Proof Prove(Statement statement, Scalar witness, WasabiRandom random)
- => Prove(statement, new ScalarVector(witness), random);
+ // 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));
+ }
- public static Proof Prove(Statement statement, ScalarVector witness, WasabiRandom random)
- => Prove(new Knowledge(statement, witness), random);
+ public static bool Verify(Transcript transcript, IEnumerable<Statement> statements, IEnumerable<Proof> proofs)
+ {
+ Guard.Same(nameof(proofs), proofs.Count(), statements.Count());
+
+ // 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 statement in statements)
+ {
+ transcript.CommitStatement(statement);
+ }
+
+ // After all the statements have been committed, the public nonces are
+ // added to the transcript. This is done separately from the statement
+ // commitments because the prover derives these based on the compound
+ // statements, and the verifier must add data to the transcript in the
+ // same order as the prover.
+ foreach (var proof in proofs)
+ {
+ transcript.CommitPublicNonces(proof.PublicNonces);
+ }
+
+ // After all the public nonces have been committed, a challenge can be
+ // generated based on transcript state. Since challenges are deterministic
+ // outputs of a hash function which depends on the prover commitments, the
+ // verifier obtains the same challenge and then accepts if the responses
+ // satisfy the verification equation.
+ var challenge = transcript.GenerateChallenge();
+
+ return Enumerable.Zip(statements, proofs, (s, p) => s.CheckVerificationEquation(p.PublicNonces, challenge, p.Responses)).All(x => x);
+ }
public static Knowledge IssuerParameters(MAC mac, GroupElement ma, CoordinatorSecretKey sk)
=> new Knowledge(IssuerParameters(sk.ComputeCoordinatorParameters(), mac, ma), new ScalarVector(sk.W, sk.Wp, sk.X0, sk.X1, sk.Ya));

MODIFY
WalletWasabi/Wabisabi/CredentialIssuer.cs
WalletWasabi/Wabisabi/CredentialIssuer.cs
@@ -134,7 +134,7 @@ namespace WalletWasabi.Wabisabi
var transcript = BuildTransnscript(registrationRequest.IsNullRequest);
// Verify all statements.
- var areProofsValid = Verifier.Verify(transcript, statements, registrationRequest.Proofs);
+ var areProofsValid = ProofSystem.Verify(transcript, statements, registrationRequest.Proofs);
if (!areProofsValid)
{
throw new WabiSabiException(WabiSabiErrorCode.CoordinatorReceivedInvalidProofs);
@@ -144,7 +144,7 @@ namespace WalletWasabi.Wabisabi
var credentials = requested.Select(x => IssueCredential(x.Ma, RandomNumberGenerator.GetScalar())).ToArray();
// Construct response.
- var proofs = Prover.Prove(transcript, credentials.Select(x => x.Knowledge), RandomNumberGenerator);
+ var proofs = ProofSystem.Prove(transcript, credentials.Select(x => x.Knowledge), RandomNumberGenerator);
var macs = credentials.Select(x => x.Mac);
var response = new RegistrationResponse(macs, proofs);

MODIFY
WalletWasabi/Wabisabi/WabiSabiClient.cs
WalletWasabi/Wabisabi/WabiSabiClient.cs
@@ -8,7 +8,6 @@ using WalletWasabi.Crypto.Groups;
using WalletWasabi.Crypto.Randomness;
using WalletWasabi.Crypto.ZeroKnowledge;
using WalletWasabi.Crypto.ZeroKnowledge.LinearRelation;
-using WalletWasabi.Crypto.ZeroKnowledge.NonInteractive;
using WalletWasabi.Helpers;
namespace WalletWasabi.Wabisabi
@@ -57,7 +56,7 @@ namespace WalletWasabi.Wabisabi
Money.Zero,
Enumerable.Empty<CredentialPresentation>(),
credentialsToRequest,
- Prover.Prove(transcript, knowledge, RandomNumberGenerator)),
+ ProofSystem.Prove(transcript, knowledge, RandomNumberGenerator)),
new RegistrationValidationData(
transcript,
Enumerable.Empty<Credential>(),
@@ -144,7 +143,7 @@ namespace WalletWasabi.Wabisabi
amountsToRequest.Sum() - credentialsToPresent.Sum(x => x.Amount.ToMoney()),
presentations,
credentialsToRequest,
- Prover.Prove(transcript, knowledgeToProve, RandomNumberGenerator)),
+ ProofSystem.Prove(transcript, knowledgeToProve, RandomNumberGenerator)),
new RegistrationValidationData(
transcript,
credentialsToPresent,
@@ -173,7 +172,7 @@ namespace WalletWasabi.Wabisabi
var statements = credentials
.Select(x => ProofSystem.IssuerParameters(CoordinatorParameters, x.Issued, x.Requested.Ma));
- var areCorrectlyIssued = Verifier.Verify(registrationValidationData.Transcript, statements, registrationResponse.Proofs);
+ var areCorrectlyIssued = ProofSystem.Verify(registrationValidationData.Transcript, statements, registrationResponse.Proofs);
if (!areCorrectlyIssued)
{
throw new WabiSabiException(WabiSabiErrorCode.ClientReceivedInvalidProofs);

Move Verify and Prove to the ProofSystem class