Introduce Statement
MODIFY
WalletWasabi.Tests/UnitTests/Crypto/ZeroKnowledge/ChallengeTests.cs
WalletWasabi.Tests/UnitTests/Crypto/ZeroKnowledge/ChallengeTests.cs
@@ -13,22 +13,18 @@ namespace WalletWasabi.Tests.UnitTests.Crypto.ZeroKnowledge
public void BuildThrows()
{
// Demonstrate when it shouldn't throw.
- Challenge.Build(Generators.G, Generators.Ga);
- Challenge.Build(Generators.G, Generators.Ga, Generators.Gg, Generators.Gh);
+ Statement statement1 = new Statement(Generators.Ga, Generators.Gg);
+ Statement statement2 = new Statement(Generators.Ga, Generators.Gg, Generators.Gh);
+ Challenge.Build(Generators.G, statement1);
+ Challenge.Build(Generators.G, statement2);
// Infinity cannot pass through.
- Assert.ThrowsAny<ArgumentException>(() => Challenge.Build(Generators.G, GroupElement.Infinity));
- Assert.ThrowsAny<ArgumentException>(() => Challenge.Build(GroupElement.Infinity, Generators.Ga));
- Assert.ThrowsAny<ArgumentException>(() => Challenge.Build(GroupElement.Infinity, GroupElement.Infinity));
-
- Assert.ThrowsAny<ArgumentException>(() => Challenge.Build(GroupElement.Infinity, Generators.Ga, Generators.Gg, Generators.Gh));
- Assert.ThrowsAny<ArgumentException>(() => Challenge.Build(Generators.G, GroupElement.Infinity, Generators.Gg, Generators.Gh));
- Assert.ThrowsAny<ArgumentException>(() => Challenge.Build(Generators.G, Generators.Ga, GroupElement.Infinity, Generators.Gh));
- Assert.ThrowsAny<ArgumentException>(() => Challenge.Build(Generators.G, Generators.Ga, Generators.Gg, GroupElement.Infinity));
+ Assert.ThrowsAny<ArgumentException>(() => Challenge.Build(GroupElement.Infinity, statement1));
+ Assert.ThrowsAny<ArgumentException>(() => Challenge.Build(GroupElement.Infinity, statement2));
// Public and random points cannot be the same.
- Assert.ThrowsAny<InvalidOperationException>(() => Challenge.Build(Generators.G, Generators.G));
- Assert.ThrowsAny<InvalidOperationException>(() => Challenge.Build(Generators.G, Generators.G, Generators.Gg, Generators.Gh));
+ Assert.ThrowsAny<InvalidOperationException>(() => Challenge.Build(Generators.G, new Statement(Generators.G, Generators.Gg)));
+ Assert.ThrowsAny<InvalidOperationException>(() => Challenge.Build(Generators.G, new Statement(Generators.G, Generators.Gg, Generators.Gh)));
}
}
}

MODIFY
WalletWasabi.Tests/UnitTests/Crypto/ZeroKnowledge/KnowledgeOfDiscreteLogTests.cs
WalletWasabi.Tests/UnitTests/Crypto/ZeroKnowledge/KnowledgeOfDiscreteLogTests.cs
@@ -26,8 +26,9 @@ namespace WalletWasabi.Tests.UnitTests.Crypto.ZeroKnowledge
var secret = new Scalar(scalarSeed);
var generator = Generators.G;
var publicPoint = secret * generator;
- var proof = Prover.CreateProof(secret, publicPoint, generator);
- Assert.True(Verifier.Verify(proof, publicPoint, generator));
+ var statement = new Statement(publicPoint, generator);
+ var proof = Prover.CreateProof(secret, statement);
+ Assert.True(Verifier.Verify(proof, statement));
}
[Fact]
@@ -37,8 +38,9 @@ namespace WalletWasabi.Tests.UnitTests.Crypto.ZeroKnowledge
{
var generator = Generators.G;
var publicPoint = secret * generator;
- var proof = Prover.CreateProof(secret, publicPoint, generator);
- Assert.True(Verifier.Verify(proof, publicPoint, generator));
+ var statement = new Statement(publicPoint, generator);
+ var proof = Prover.CreateProof(secret, statement);
+ Assert.True(Verifier.Verify(proof, statement));
}
}
@@ -49,26 +51,31 @@ namespace WalletWasabi.Tests.UnitTests.Crypto.ZeroKnowledge
var gen = new Scalar(4) * Generators.G;
var secret = new Scalar(val, val, val, val, val, val, val, val);
var p = secret * gen;
- var proof = Prover.CreateProof(secret, p, gen);
- Assert.True(Verifier.Verify(proof, p, gen));
+ var statement = new Statement(p, gen);
+ var proof = Prover.CreateProof(secret, statement);
+ Assert.True(Verifier.Verify(proof, statement));
secret = EC.N + (new Scalar(1)).Negate();
p = secret * gen;
- proof = Prover.CreateProof(secret, p, gen);
- Assert.True(Verifier.Verify(proof, p, gen));
+ statement = new Statement(p, gen);
+ proof = Prover.CreateProof(secret, statement);
+ Assert.True(Verifier.Verify(proof, statement));
secret = EC.NC;
p = secret * gen;
- proof = Prover.CreateProof(secret, p, gen);
- Assert.True(Verifier.Verify(proof, p, gen));
+ statement = new Statement(p, gen);
+ proof = Prover.CreateProof(secret, statement);
+ Assert.True(Verifier.Verify(proof, statement));
secret = EC.NC + new Scalar(1);
p = secret * gen;
- proof = Prover.CreateProof(secret, p, gen);
- Assert.True(Verifier.Verify(proof, p, gen));
+ statement = new Statement(p, gen);
+ proof = Prover.CreateProof(secret, statement);
+ Assert.True(Verifier.Verify(proof, statement));
secret = EC.NC + (new Scalar(1)).Negate();
p = secret * gen;
- proof = Prover.CreateProof(secret, p, gen);
- Assert.True(Verifier.Verify(proof, p, gen));
+ statement = new Statement(p, gen);
+ proof = Prover.CreateProof(secret, statement);
+ Assert.True(Verifier.Verify(proof, statement));
}
[Fact]
@@ -76,21 +83,25 @@ namespace WalletWasabi.Tests.UnitTests.Crypto.ZeroKnowledge
{
var point1 = new Scalar(3) * Generators.G;
var point2 = new Scalar(7) * Generators.G;
+ var generator = Generators.Ga;
var publicPoint = point1;
var nonce = Generators.G;
- var challenge = Challenge.Build(publicPoint, nonce);
- Assert.Equal("secp256k1_scalar = { 0x63CB4683UL, 0xE74DC9A9UL, 0x346534D4UL, 0x247AF71FUL, 0xB49BF19DUL, 0x127658B1UL, 0xE80264F6UL, 0xAE87D410UL }", challenge.ToC(""));
+ var statement = new Statement(publicPoint, generator);
+ var challenge = Challenge.Build(nonce, statement);
+ Assert.Equal("secp256k1_scalar = { 0x8626D370UL, 0x6D18AF98UL, 0xAE71F87DUL, 0x5008741FUL, 0x43515E2BUL, 0x666194D8UL, 0x97CCA524UL, 0x09E82A30UL }", challenge.ToC(""));
publicPoint = Generators.G;
nonce = point2;
- challenge = Challenge.Build(publicPoint, nonce);
- Assert.Equal("secp256k1_scalar = { 0xE2D33BB7UL, 0xA303E090UL, 0x61094B47UL, 0xE689400BUL, 0xE4B97858UL, 0xC92E9B00UL, 0xFE5D6531UL, 0x8F14CF73UL }", challenge.ToC(""));
+ statement = new Statement(publicPoint, generator);
+ challenge = Challenge.Build(nonce, statement);
+ Assert.Equal("secp256k1_scalar = { 0x72CF8E90UL, 0x518E892FUL, 0xA7046699UL, 0x0C21C88FUL, 0xEE3DC26EUL, 0x83F833FBUL, 0x0B21A692UL, 0x404C3D01UL }", challenge.ToC(""));
publicPoint = point1;
nonce = point2;
- challenge = Challenge.Build(publicPoint, nonce);
- Assert.Equal("secp256k1_scalar = { 0xCBF4E2E0UL, 0xCB4E26D0UL, 0xAD167C64UL, 0x3083BA72UL, 0xD74AB657UL, 0xA1F544D6UL, 0x732BFC65UL, 0xF11CEAABUL }", challenge.ToC(""));
+ statement = new Statement(publicPoint, generator);
+ challenge = Challenge.Build(nonce, statement);
+ Assert.Equal("secp256k1_scalar = { 0x333575CAUL, 0x7D400596UL, 0x4C12B718UL, 0xD64F97BBUL, 0x5EBB4EB7UL, 0x2D5DFD8EUL, 0xFB2FE4DCUL, 0x49FD37B6UL }", challenge.ToC(""));
}
[Fact]

MODIFY
WalletWasabi.Tests/UnitTests/Crypto/ZeroKnowledge/KnowledgeOfRepresentationTests.cs
WalletWasabi.Tests/UnitTests/Crypto/ZeroKnowledge/KnowledgeOfRepresentationTests.cs
@@ -26,13 +26,14 @@ namespace WalletWasabi.Tests.UnitTests.Crypto.ZeroKnowledge
var secrets = new[] { new Scalar(scalarSeed1), new Scalar(scalarSeed2) };
var generators = new[] { Generators.G, Generators.Ga };
var publicPoint = GroupElement.Infinity;
- var secretGeneratorPairs = secrets.ZipForceEqualLength(generators);
- foreach (var (secret, generator) in secretGeneratorPairs)
+ foreach (var (secret, generator) in secrets.ZipForceEqualLength(generators))
{
publicPoint += secret * generator;
}
- var proof = Prover.CreateProof(secretGeneratorPairs, publicPoint);
- Assert.True(Verifier.Verify(proof, publicPoint, generators));
+
+ Statement statement = new Statement(publicPoint, generators);
+ var proof = Prover.CreateProof(secrets, statement);
+ Assert.True(Verifier.Verify(proof, statement));
}
[Fact]
@@ -46,13 +47,13 @@ namespace WalletWasabi.Tests.UnitTests.Crypto.ZeroKnowledge
var secrets = new[] { secret1, secret2 };
var generators = new[] { Generators.G, Generators.Ga };
var publicPoint = GroupElement.Infinity;
- var secretGeneratorPairs = secrets.ZipForceEqualLength(generators);
- foreach (var (secret, generator) in secretGeneratorPairs)
+ foreach (var (secret, generator) in secrets.ZipForceEqualLength(generators))
{
publicPoint += secret * generator;
}
- var proof = Prover.CreateProof(secretGeneratorPairs, publicPoint);
- Assert.True(Verifier.Verify(proof, publicPoint, generators));
+ Statement statement = new Statement(publicPoint, generators);
+ var proof = Prover.CreateProof(secrets, statement);
+ Assert.True(Verifier.Verify(proof, statement));
}
}
}

MODIFY
WalletWasabi.Tests/UnitTests/Crypto/ZeroKnowledge/ProverTests.cs
WalletWasabi.Tests/UnitTests/Crypto/ZeroKnowledge/ProverTests.cs
@@ -18,25 +18,19 @@ namespace WalletWasabi.Tests.UnitTests.Crypto.ZeroKnowledge
var two = new Scalar(2);
// Demonstrate when it shouldn't throw.
- Prover.CreateProof(two, two * Generators.G, Generators.G);
+ Prover.CreateProof(two, new Statement(two * Generators.G, Generators.G));
- // Infinity or zero cannot pass through.
- Assert.ThrowsAny<ArgumentException>(() => Prover.CreateProof(Scalar.Zero, two * Generators.G, Generators.G));
- Assert.ThrowsAny<ArgumentException>(() => Prover.CreateProof(two, GroupElement.Infinity, Generators.G));
- Assert.ThrowsAny<ArgumentException>(() => Prover.CreateProof(two, two * Generators.G, GroupElement.Infinity));
- Assert.ThrowsAny<ArgumentException>(() => Prover.CreateProof(Scalar.Zero, GroupElement.Infinity, Generators.G));
- Assert.ThrowsAny<ArgumentException>(() => Prover.CreateProof(Scalar.Zero, two * Generators.G, GroupElement.Infinity));
- Assert.ThrowsAny<ArgumentException>(() => Prover.CreateProof(two, GroupElement.Infinity, GroupElement.Infinity));
- Assert.ThrowsAny<ArgumentException>(() => Prover.CreateProof(Scalar.Zero, GroupElement.Infinity, GroupElement.Infinity));
+ // Zero cannot pass through.
+ Assert.ThrowsAny<ArgumentException>(() => Prover.CreateProof(Scalar.Zero, new Statement(Generators.G, Generators.G)));
// Public point must be generator * secret.
- Assert.ThrowsAny<InvalidOperationException>(() => Prover.CreateProof(two, Generators.G, Generators.G));
- Assert.ThrowsAny<InvalidOperationException>(() => Prover.CreateProof(two, new Scalar(3) * Generators.G, Generators.G));
- Assert.ThrowsAny<InvalidOperationException>(() => Prover.CreateProof(two, Scalar.One * Generators.G, Generators.G));
+ Assert.ThrowsAny<InvalidOperationException>(() => Prover.CreateProof(two, new Statement(Generators.G, Generators.G)));
+ Assert.ThrowsAny<InvalidOperationException>(() => Prover.CreateProof(two, new Statement(new Scalar(3) * Generators.G, Generators.G)));
+ Assert.ThrowsAny<InvalidOperationException>(() => Prover.CreateProof(two, new Statement(Scalar.One * Generators.G, Generators.G)));
// Secret cannot overflow.
- Assert.ThrowsAny<ArgumentException>(() => Prover.CreateProof(EC.N, EC.N * Generators.G, Generators.G));
- Assert.ThrowsAny<ArgumentException>(() => Prover.CreateProof(CryptoHelpers.ScalarLargestOverflow, CryptoHelpers.ScalarLargestOverflow * Generators.G, Generators.G));
+ Assert.ThrowsAny<ArgumentException>(() => Prover.CreateProof(EC.N, new Statement(EC.N * Generators.G, Generators.G)));
+ Assert.ThrowsAny<ArgumentException>(() => Prover.CreateProof(CryptoHelpers.ScalarLargestOverflow, new Statement(CryptoHelpers.ScalarLargestOverflow * Generators.G, Generators.G)));
}
[Fact]
@@ -47,7 +41,7 @@ namespace WalletWasabi.Tests.UnitTests.Crypto.ZeroKnowledge
{
mockRandom.GetScalarResults.Add(scalar);
- Assert.ThrowsAny<InvalidOperationException>(() => Prover.CreateProof(Scalar.One, Scalar.One * Generators.G, Generators.G, mockRandom));
+ Assert.ThrowsAny<InvalidOperationException>(() => Prover.CreateProof(Scalar.One, new Statement(Scalar.One * Generators.G, Generators.G), mockRandom));
}
}
@@ -60,7 +54,7 @@ namespace WalletWasabi.Tests.UnitTests.Crypto.ZeroKnowledge
// Don't tolerate if the second zero scalar random is received.
mockRandom.GetScalarResults.Add(Scalar.Zero);
- Assert.ThrowsAny<InvalidOperationException>(() => Prover.CreateProof(Scalar.One, Scalar.One * Generators.G, Generators.G, mockRandom));
+ Assert.ThrowsAny<InvalidOperationException>(() => Prover.CreateProof(Scalar.One, new Statement(Scalar.One * Generators.G, Generators.G), mockRandom));
}
}
}

ADD
WalletWasabi.Tests/UnitTests/Crypto/ZeroKnowledge/StatementTests.cs
+using System;
+using System.Collections.Generic;
+using System.Text;
+using WalletWasabi.Crypto.Groups;
+using WalletWasabi.Crypto.ZeroKnowledge;
+using Xunit;
+
+namespace WalletWasabi.Tests.UnitTests.Crypto.ZeroKnowledge
+{
+ public class StatementTests
+ {
+ [Fact]
+ public void StatementThrows()
+ {
+ // Demonstrate when it shouldn't throw.
+ new Statement(Generators.G, Generators.Ga);
+ new Statement(Generators.G, Generators.Ga, Generators.Gg);
+
+ // Cannot miss generators.
+ Assert.ThrowsAny<ArgumentException>(() => new Statement(Generators.G));
+
+ // Infinity cannot pass through.
+ Assert.ThrowsAny<ArgumentException>(() => new Statement(GroupElement.Infinity, Generators.Ga));
+ Assert.ThrowsAny<ArgumentException>(() => new Statement(Generators.G, GroupElement.Infinity));
+ Assert.ThrowsAny<ArgumentException>(() => new Statement(GroupElement.Infinity, Generators.Ga, Generators.Gg));
+ Assert.ThrowsAny<ArgumentException>(() => new Statement(Generators.G, GroupElement.Infinity, Generators.Gg));
+ Assert.ThrowsAny<ArgumentException>(() => new Statement(Generators.G, Generators.Ga, GroupElement.Infinity));
+ }
+ }
+}

MODIFY
WalletWasabi.Tests/UnitTests/Crypto/ZeroKnowledge/VerifierTests.cs
WalletWasabi.Tests/UnitTests/Crypto/ZeroKnowledge/VerifierTests.cs
@@ -23,11 +23,12 @@ namespace WalletWasabi.Tests.UnitTests.Crypto.ZeroKnowledge
Scalar randomScalar = new Scalar(14);
var nonce = randomScalar * generator;
- var challenge = Challenge.Build(publicPoint, nonce);
+ var statement = new Statement(publicPoint, generator);
+ var challenge = Challenge.Build(nonce, statement);
var response = randomScalar + (secret + Scalar.One) * challenge;
var proof = new KnowledgeOfDiscreteLog(nonce, response);
- Assert.False(Verifier.Verify(proof, publicPoint, generator));
+ Assert.False(Verifier.Verify(proof, statement));
// Other false verification tests.
var point1 = new Scalar(3) * Generators.G;
@@ -36,7 +37,8 @@ namespace WalletWasabi.Tests.UnitTests.Crypto.ZeroKnowledge
var gen = Generators.G;
proof = new KnowledgeOfDiscreteLog(point1, scalar);
- Assert.False(Verifier.Verify(proof, point2, gen));
+ var statement2 = new Statement(point2, gen);
+ Assert.False(Verifier.Verify(proof, statement2));
}
[Fact]
@@ -46,30 +48,19 @@ namespace WalletWasabi.Tests.UnitTests.Crypto.ZeroKnowledge
var repProof = new KnowledgeOfRepresentation(Generators.G, Scalar.One, CryptoHelpers.ScalarThree);
// Demonstrate when it shouldn't throw.
- Verifier.Verify(dlProof, Generators.Ga, Generators.Gg);
- Verifier.Verify(repProof, Generators.Ga, Generators.Gg, Generators.Ga);
-
- // At least one generator must be provided.
- Assert.ThrowsAny<ArgumentException>(() => Verifier.Verify(dlProof, Generators.Ga));
- Assert.ThrowsAny<ArgumentException>(() => Verifier.Verify(repProof, Generators.Ga));
-
- // Infinity cannot pass through.
- Assert.ThrowsAny<ArgumentException>(() => Verifier.Verify(dlProof, GroupElement.Infinity, Generators.Gg));
- Assert.ThrowsAny<ArgumentException>(() => Verifier.Verify(dlProof, Generators.Ga, GroupElement.Infinity));
- Assert.ThrowsAny<ArgumentException>(() => Verifier.Verify(dlProof, GroupElement.Infinity, GroupElement.Infinity));
-
- Assert.ThrowsAny<ArgumentException>(() => Verifier.Verify(repProof, GroupElement.Infinity, Generators.Gg, Generators.Ga));
- Assert.ThrowsAny<ArgumentException>(() => Verifier.Verify(repProof, Generators.Ga, GroupElement.Infinity, Generators.Ga));
- Assert.ThrowsAny<ArgumentException>(() => Verifier.Verify(repProof, Generators.Ga, Generators.Gg, GroupElement.Infinity));
+ var validStatement1 = new Statement(Generators.Ga, Generators.Gg);
+ var validStatement2 = new Statement(Generators.Ga, Generators.Gg, Generators.Ga);
+ Verifier.Verify(dlProof, validStatement1);
+ Verifier.Verify(repProof, validStatement2);
// Public point should not be equal to the random point of the proof.
- Assert.ThrowsAny<InvalidOperationException>(() => Verifier.Verify(dlProof, Generators.G, Generators.Ga));
- Assert.ThrowsAny<InvalidOperationException>(() => Verifier.Verify(repProof, Generators.G, Generators.Gg, Generators.Ga));
+ Assert.ThrowsAny<InvalidOperationException>(() => Verifier.Verify(dlProof, new Statement(Generators.G, Generators.Ga)));
+ Assert.ThrowsAny<InvalidOperationException>(() => Verifier.Verify(repProof, new Statement(Generators.G, Generators.Gg, Generators.Ga)));
// Same number of generators must be provided as the responses.
- Assert.ThrowsAny<InvalidOperationException>(() => Verifier.Verify(dlProof, Generators.Ga, Generators.Gg, Generators.GV));
- Assert.ThrowsAny<InvalidOperationException>(() => Verifier.Verify(repProof, Generators.Ga, Generators.Gg));
- Assert.ThrowsAny<InvalidOperationException>(() => Verifier.Verify(repProof, Generators.Ga, Generators.Gg, Generators.Ga, Generators.GV));
+ Assert.ThrowsAny<InvalidOperationException>(() => Verifier.Verify(dlProof, new Statement(Generators.Ga, Generators.Gg, Generators.GV)));
+ Assert.ThrowsAny<InvalidOperationException>(() => Verifier.Verify(repProof, new Statement(Generators.Ga, Generators.Gg)));
+ Assert.ThrowsAny<InvalidOperationException>(() => Verifier.Verify(repProof, new Statement(Generators.Ga, Generators.Gg, Generators.Ga, Generators.GV)));
}
}
}

MODIFY
WalletWasabi/Crypto/ZeroKnowledge/Challenge.cs
WalletWasabi/Crypto/ZeroKnowledge/Challenge.cs
@@ -10,26 +10,19 @@ namespace WalletWasabi.Crypto.ZeroKnowledge
{
public static class Challenge
{
- public static Scalar Build(GroupElement publicPoint, GroupElement nonce, IEnumerable<GroupElement> generators)
+ public static Scalar Build(GroupElement nonce, Statement statement)
{
- Guard.False($"{nameof(publicPoint)}.{nameof(publicPoint.IsInfinity)}", publicPoint.IsInfinity);
Guard.False($"{nameof(nonce)}.{nameof(nonce.IsInfinity)}", nonce.IsInfinity);
- foreach (var generator in generators)
- {
- Guard.False($"{nameof(generator)}.{nameof(generator.IsInfinity)}", generator.IsInfinity);
- }
+ var publicPoint = statement.PublicPoint;
if (publicPoint == nonce)
{
throw new InvalidOperationException($"{nameof(publicPoint)} and {nameof(nonce)} should not be equal.");
}
- return HashToScalar(new[] { publicPoint, nonce }.Concat(generators));
+ return HashToScalar(new[] { publicPoint, nonce }.Concat(statement.Generators));
}
- public static Scalar Build(GroupElement publicPoint, GroupElement nonce, params GroupElement[] generators)
- => Build(publicPoint, nonce, generators as IEnumerable<GroupElement>);
-
/// <summary>
/// Fiat Shamir heuristic.
/// </summary>

MODIFY
WalletWasabi/Crypto/ZeroKnowledge/Prover.cs
WalletWasabi/Crypto/ZeroKnowledge/Prover.cs
@@ -11,25 +11,32 @@ namespace WalletWasabi.Crypto.ZeroKnowledge
{
public static class Prover
{
- public static KnowledgeOfDiscreteLog CreateProof(Scalar secret, GroupElement publicPoint, GroupElement generator, WasabiRandom? random = null)
+ public static KnowledgeOfDiscreteLog CreateProof(Scalar secret, Statement statement, WasabiRandom? random = null)
{
- var proof = CreateProof(new[] { (secret, generator) }, publicPoint, random);
+ var proof = CreateProof(new Scalar[] { secret }, statement, random);
return new KnowledgeOfDiscreteLog(proof.Nonce, proof.Responses.First());
}
- public static KnowledgeOfRepresentation CreateProof(IEnumerable<(Scalar secret, GroupElement generator)> secretGeneratorPairs, GroupElement publicPoint, WasabiRandom? random = null)
+ public static KnowledgeOfRepresentation CreateProof(IEnumerable<Scalar> secrets, Statement statement, WasabiRandom? random = null)
{
- Guard.False($"{nameof(publicPoint)}.{nameof(publicPoint.IsInfinity)}", publicPoint.IsInfinity);
+ var secretsCount = secrets.Count();
+ IEnumerable<GroupElement> generators = statement.Generators;
+ var generatorsCount = generators.Count();
+ if (secretsCount != generatorsCount)
+ {
+ const string NameofGenerators = nameof(generators);
+ const string NameofSecrets = nameof(secrets);
+ throw new InvalidOperationException($"Must provide exactly as many {NameofGenerators} as {NameofSecrets}. {NameofGenerators}: {generatorsCount}, {NameofSecrets}: {secretsCount}.");
+ }
var nonce = GroupElement.Infinity;
var randomScalars = new List<Scalar>();
- var publicPointSanity = publicPoint;
- foreach (var (secret, generator) in secretGeneratorPairs)
+ var publicPointSanity = statement.PublicPoint;
+ foreach (var (secret, generator) in secrets.ZipForceEqualLength<Scalar, GroupElement>(generators))
{
Guard.False($"{nameof(secret)}.{nameof(secret.IsOverflow)}", secret.IsOverflow);
Guard.False($"{nameof(secret)}.{nameof(secret.IsZero)}", secret.IsZero);
- Guard.False($"{nameof(generator)}.{nameof(generator.IsInfinity)}", generator.IsInfinity);
publicPointSanity -= secret * generator;
var randomScalar = GetNonZeroRandomScalar(random);
@@ -40,16 +47,13 @@ namespace WalletWasabi.Crypto.ZeroKnowledge
if (publicPointSanity != GroupElement.Infinity)
{
- throw new InvalidOperationException($"{nameof(publicPoint)} was incorrectly constructed.");
+ throw new InvalidOperationException($"{nameof(statement.PublicPoint)} was incorrectly constructed.");
}
- var generators = secretGeneratorPairs.Select(x => x.generator);
- var challenge = Challenge.Build(publicPoint, nonce, generators);
+ var challenge = Challenge.Build(nonce, statement);
var responses = new List<Scalar>();
- foreach (var (secret, randomScalar) in secretGeneratorPairs
- .Select(x => x.secret)
- .ZipForceEqualLength(randomScalars))
+ foreach (var (secret, randomScalar) in secrets.ZipForceEqualLength(randomScalars))
{
var response = randomScalar + secret * challenge;
responses.Add(response);
@@ -58,7 +62,7 @@ namespace WalletWasabi.Crypto.ZeroKnowledge
var proof = new KnowledgeOfRepresentation(nonce, responses);
// Sanity check:
- if (!Verifier.Verify(proof, publicPoint, generators))
+ if (!Verifier.Verify(proof, statement))
{
throw new InvalidOperationException($"{nameof(CreateProof)} or {nameof(Verifier.Verify)} is incorrectly implemented. Proof was built, but verification failed.");
}

ADD
WalletWasabi/Crypto/ZeroKnowledge/Statement.cs
+using System;
+using System.Collections.Generic;
+using System.Text;
+using WalletWasabi.Crypto.Groups;
+using WalletWasabi.Helpers;
+
+namespace WalletWasabi.Crypto.ZeroKnowledge
+{
+ public class Statement
+ {
+ public Statement(GroupElement publicPoint, IEnumerable<GroupElement> generators)
+ {
+ Guard.False($"{nameof(publicPoint)}.{nameof(publicPoint.IsInfinity)}", publicPoint.IsInfinity);
+ foreach (var generator in generators)
+ {
+ Guard.False($"{nameof(generator)}.{nameof(generator.IsInfinity)}", generator.IsInfinity);
+ }
+
+ PublicPoint = publicPoint;
+ Generators = Guard.NotNullOrEmpty(nameof(generators), generators);
+ }
+
+ public Statement(GroupElement publicPoint, params GroupElement[] generators)
+ : this(publicPoint, generators as IEnumerable<GroupElement>)
+ {
+ }
+
+ public GroupElement PublicPoint { get; }
+ public IEnumerable<GroupElement> Generators { get; }
+ }
+}

MODIFY
WalletWasabi/Crypto/ZeroKnowledge/Verifier.cs
WalletWasabi/Crypto/ZeroKnowledge/Verifier.cs
@@ -9,39 +9,29 @@ namespace WalletWasabi.Crypto.ZeroKnowledge
{
public static class Verifier
{
- public static bool Verify(KnowledgeOfRepresentation proof, GroupElement publicPoint, IEnumerable<GroupElement> generators)
+ public static bool Verify(KnowledgeOfRepresentation proof, Statement statement)
{
- Guard.False($"{nameof(publicPoint)}.{nameof(publicPoint.IsInfinity)}", publicPoint.IsInfinity);
- Guard.NotNullOrEmpty(nameof(generators), generators);
-
+ var publicPoint = statement.PublicPoint;
if (publicPoint == proof.Nonce)
{
throw new InvalidOperationException($"{nameof(publicPoint)} and {nameof(proof.Nonce)} should not be equal.");
}
- foreach (var generator in generators)
- {
- Guard.False($"{nameof(generator)}.{nameof(generator.IsInfinity)}", generator.IsInfinity);
- }
-
var nonce = proof.Nonce;
var responses = proof.Responses;
- var challenge = Challenge.Build(publicPoint, nonce, generators);
+ var challenge = Challenge.Build(nonce, statement);
var a = challenge * publicPoint + nonce;
var b = GroupElement.Infinity;
- foreach (var (response, generator) in responses.ZipForceEqualLength(generators))
+ foreach (var (response, generator) in responses.ZipForceEqualLength(statement.Generators))
{
b += response * generator;
}
return a == b;
}
- public static bool Verify(KnowledgeOfDiscreteLog proof, GroupElement publicPoint, GroupElement generator)
- => Verify(proof as KnowledgeOfRepresentation, publicPoint, generator);
-
- public static bool Verify(KnowledgeOfRepresentation proof, GroupElement publicPoint, params GroupElement[] generators)
- => Verify(proof, publicPoint, generators as IEnumerable<GroupElement>);
+ public static bool Verify(KnowledgeOfDiscreteLog proof, Statement statement)
+ => Verify(proof as KnowledgeOfRepresentation, statement);
}
}

Introduce Statement