Very bad commit, don't look at it, it's just consolidating cryptographic progress.
MODIFY
WalletWasabi.Tests/UnitTests/Crypto/ZeroKnowledge/ZkExponentTests.cs
WalletWasabi.Tests/UnitTests/Crypto/ZeroKnowledge/ZkExponentTests.cs
@@ -86,7 +86,7 @@ namespace WalletWasabi.Tests.UnitTests.Crypto.ZeroKnowledge
var challenge = ZkChallenge.Build(publicPoint, randomPoint);
var response = randomScalar + (secret + Scalar.One) * challenge;
- var proof = new ZkExponentProof(randomPoint, response);
+ var proof = new ZkKnowledgeOfExponent(randomPoint, response);
Assert.False(ZkVerifier.Verify(proof, publicPoint, generator));
// Other false verification tests.
@@ -95,7 +95,7 @@ namespace WalletWasabi.Tests.UnitTests.Crypto.ZeroKnowledge
var scalar = new Scalar(11);
var gen = GroupElement.G;
- proof = new ZkExponentProof(point1, scalar);
+ proof = new ZkKnowledgeOfExponent(point1, scalar);
Assert.False(ZkVerifier.Verify(proof, point2, gen));
}
@@ -169,12 +169,12 @@ namespace WalletWasabi.Tests.UnitTests.Crypto.ZeroKnowledge
public void ExponentProofThrows()
{
// Demonstrate when it shouldn't throw.
- new ZkExponentProof(GroupElement.G, Scalar.One);
+ new ZkKnowledgeOfExponent(GroupElement.G, Scalar.One);
// Infinity or zero cannot pass through.
- Assert.ThrowsAny<ArgumentException>(() => new ZkExponentProof(GroupElement.G, Scalar.Zero));
- Assert.ThrowsAny<ArgumentException>(() => new ZkExponentProof(GroupElement.Infinity, Scalar.One));
- Assert.ThrowsAny<ArgumentException>(() => new ZkExponentProof(GroupElement.Infinity, Scalar.Zero));
+ Assert.ThrowsAny<ArgumentException>(() => new ZkKnowledgeOfExponent(GroupElement.G, Scalar.Zero));
+ Assert.ThrowsAny<ArgumentException>(() => new ZkKnowledgeOfExponent(GroupElement.Infinity, Scalar.One));
+ Assert.ThrowsAny<ArgumentException>(() => new ZkKnowledgeOfExponent(GroupElement.Infinity, Scalar.Zero));
}
[Fact]
@@ -207,7 +207,7 @@ namespace WalletWasabi.Tests.UnitTests.Crypto.ZeroKnowledge
[Fact]
public void VerifierThrows()
{
- var proof = new ZkExponentProof(GroupElement.G, Scalar.One);
+ var proof = new ZkKnowledgeOfExponent(GroupElement.G, Scalar.One);
// Demonstrate when it shouldn't throw.
ZkVerifier.Verify(proof, GroupElement.Ga, GroupElement.Gg);

ADD
WalletWasabi.Tests/UnitTests/Crypto/ZeroKnowledge/ZkRepresentationTests.cs
+using NBitcoin.Secp256k1;
+using System;
+using System.Collections.Generic;
+using System.Text;
+using WalletWasabi.Crypto;
+using WalletWasabi.Crypto.ZeroKnowledge;
+using Xunit;
+
+namespace WalletWasabi.Tests.UnitTests.Crypto.ZeroKnowledge
+{
+ public class ZkRepresentationTests
+ {
+ [Theory]
+ [InlineData(1, 1)]
+ [InlineData(1, 2)]
+ [InlineData(3, 5)]
+ [InlineData(5, 7)]
+ [InlineData(7, 11)]
+ [InlineData(short.MaxValue, uint.MaxValue)]
+ [InlineData(int.MaxValue, uint.MaxValue)]
+ [InlineData(uint.MaxValue, uint.MaxValue)]
+ public void End2EndVerifiesSimpleProof(uint scalarSeed1, uint scalarSeed2)
+ {
+ var exponents = new[] { new Scalar(scalarSeed1), new Scalar(scalarSeed2) };
+ var generators = new[] { GroupElement.G, GroupElement.Ga };
+ var publicPoint = GroupElement.Infinity;
+ for (int i = 0; i < exponents.Length; i++)
+ {
+ publicPoint += exponents[i] * generators[i];
+ }
+ var proof = ZkProver.CreateProof(exponents, publicPoint, generators);
+ Assert.True(ZkVerifier.Verify(proof, publicPoint, generators));
+ }
+ }
+}

MODIFY
WalletWasabi/Crypto/ZeroKnowledge/ZkChallenge.cs
WalletWasabi/Crypto/ZeroKnowledge/ZkChallenge.cs
using NBitcoin.Secp256k1;
using System;
using System.Collections.Generic;
+using System.Linq;
using System.Text;
using WalletWasabi.Helpers;
@@ -18,9 +19,12 @@ namespace WalletWasabi.Crypto.ZeroKnowledge
throw new InvalidOperationException($"{nameof(publicPoint)} and {nameof(randomPoint)} should not be equal.");
}
- var concatenation = ByteHelpers.Combine(
- publicPoint.ToBytes(),
- randomPoint.ToBytes());
+ return HashToScalar(publicPoint, randomPoint);
+ }
+
+ public static Scalar HashToScalar(params GroupElement[] transcript)
+ {
+ var concatenation = ByteHelpers.Combine(transcript.Select(x => x.ToBytes()));
using var sha256 = System.Security.Cryptography.SHA256.Create();
var hash = sha256.ComputeHash(concatenation);
var challenge = new Scalar(hash);

RENAME
WalletWasabi/Crypto/ZeroKnowledge/ZkExponentProof.cs
WalletWasabi/Crypto/ZeroKnowledge/ZkKnowledgeOfExponent.cs
@@ -6,9 +6,9 @@ using WalletWasabi.Helpers;
namespace WalletWasabi.Crypto.ZeroKnowledge
{
- public class ZkExponentProof
+ public class ZkKnowledgeOfExponent
{
- public ZkExponentProof(GroupElement randomPoint, Scalar response)
+ public ZkKnowledgeOfExponent(GroupElement randomPoint, Scalar response)
{
Guard.False($"{nameof(randomPoint)}.{nameof(randomPoint.IsInfinity)}", randomPoint.IsInfinity);
Guard.False($"{nameof(response)}.{nameof(response.IsZero)}", response.IsZero);

ADD
WalletWasabi/Crypto/ZeroKnowledge/ZkKnowledgeOfRepresentation.cs
+using NBitcoin.Secp256k1;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace WalletWasabi.Crypto.ZeroKnowledge
+{
+ public class ZkKnowledgeOfRepresentation
+ {
+ public ZkKnowledgeOfRepresentation(GroupElement randomness, IEnumerable<Scalar> responses)
+ {
+ Randomness = randomness;
+ Responses = responses;
+ }
+
+ public GroupElement Randomness { get; }
+ public IEnumerable<Scalar> Responses { get; }
+ }
+}

MODIFY
WalletWasabi/Crypto/ZeroKnowledge/ZkProver.cs
WalletWasabi/Crypto/ZeroKnowledge/ZkProver.cs
using NBitcoin.Secp256k1;
using System;
using System.Collections.Generic;
+using System.Linq;
using System.Text;
using WalletWasabi.Crypto.Randomness;
using WalletWasabi.Helpers;
@@ -9,7 +10,7 @@ namespace WalletWasabi.Crypto.ZeroKnowledge
{
public static class ZkProver
{
- public static ZkExponentProof CreateProof(Scalar exponent, GroupElement publicPoint, GroupElement generator, WasabiRandom? random = null)
+ public static ZkKnowledgeOfExponent CreateProof(Scalar exponent, GroupElement publicPoint, GroupElement generator, WasabiRandom? random = null)
{
Guard.False($"{nameof(exponent)}.{nameof(exponent.IsOverflow)}", exponent.IsOverflow);
Guard.False($"{nameof(exponent)}.{nameof(exponent.IsZero)}", exponent.IsZero);
@@ -20,13 +21,53 @@ namespace WalletWasabi.Crypto.ZeroKnowledge
throw new InvalidOperationException($"{nameof(publicPoint)} != {nameof(exponent)} * {nameof(generator)}");
}
+ var proof = CreateProof(new[] { exponent }, publicPoint, new[] { generator }, random);
+
+ return new ZkKnowledgeOfExponent(proof.Randomness, proof.Responses.First());
+ }
+
+ public static ZkKnowledgeOfRepresentation CreateProof(IEnumerable<Scalar> exponents, GroupElement publicPoint, IEnumerable<GroupElement> generators, WasabiRandom? random = null)
+ {
+ Guard.False($"{nameof(publicPoint)}.{nameof(publicPoint.IsInfinity)}", publicPoint.IsInfinity);
+
+ var exponentArray = exponents.ToArray();
+ var generatorArray = generators.ToArray();
+
+ if (exponentArray.Length != generatorArray.Length)
+ {
+ throw new ArgumentException($"Same number of exponents and generators must be provided. Exponents: {exponentArray.Length}, Generators: {generatorArray.Length}");
+ }
+
+ var randomness = GroupElement.Infinity;
+ var randomScalars = new List<Scalar>();
+ for (int i = 0; i < exponentArray.Length; i++)
+ {
+ var exponent = exponentArray[i];
+ var generator = generatorArray[i];
+
+ Guard.False($"{nameof(exponent)}.{nameof(exponent.IsOverflow)}", exponent.IsOverflow);
+ Guard.False($"{nameof(exponent)}.{nameof(exponent.IsZero)}", exponent.IsZero);
+ Guard.False($"{nameof(generator)}.{nameof(generator.IsInfinity)}", generator.IsInfinity);
+
var randomScalar = GetNonZeroRandomScalar(random);
+ randomScalars.Add(randomScalar);
var randomPoint = randomScalar * generator;
- var challenge = ZkChallenge.Build(publicPoint, randomPoint);
+ randomness += randomPoint;
+ }
+
+ var challenge = ZkChallenge.HashToScalar(new[] { publicPoint, randomness }.Concat(generators).ToArray());
+
+ var responses = new List<Scalar>();
+ for (int i = 0; i < exponentArray.Length; i++)
+ {
+ var exponent = exponentArray[i];
+ var randomScalar = randomScalars.ToArray()[i];
var response = randomScalar + exponent * challenge;
+ responses.Add(response);
+ }
- return new ZkExponentProof(randomPoint, response);
+ return new ZkKnowledgeOfRepresentation(randomness, responses);
}
private static Scalar GetNonZeroRandomScalar(WasabiRandom? random = null)

MODIFY
WalletWasabi/Crypto/ZeroKnowledge/ZkVerifier.cs
WalletWasabi/Crypto/ZeroKnowledge/ZkVerifier.cs
using System;
using System.Collections.Generic;
+using System.Linq;
using System.Text;
using WalletWasabi.Helpers;
@@ -7,16 +8,41 @@ namespace WalletWasabi.Crypto.ZeroKnowledge
{
public static class ZkVerifier
{
- public static bool Verify(ZkExponentProof proof, GroupElement publicPoint, GroupElement generator)
+ public static bool Verify(ZkKnowledgeOfExponent proof, GroupElement publicPoint, GroupElement generator)
{
Guard.False($"{nameof(generator)}.{nameof(generator.IsInfinity)}", generator.IsInfinity);
Guard.False($"{nameof(publicPoint)}.{nameof(publicPoint.IsInfinity)}", publicPoint.IsInfinity);
- var randomPoint = proof.RandomPoint;
- var challenge = ZkChallenge.Build(publicPoint, randomPoint);
+ if (publicPoint == proof.RandomPoint)
+ {
+ throw new InvalidOperationException($"{nameof(publicPoint)} and {nameof(proof.RandomPoint)} should not be equal.");
+ }
+
+ var zkor = new ZkKnowledgeOfRepresentation(proof.RandomPoint, new[] { proof.Response });
+ return Verify(zkor, publicPoint, new[] { generator });
+ }
+
+ public static bool Verify(ZkKnowledgeOfRepresentation proof, GroupElement publicPoint, IEnumerable<GroupElement> generators)
+ {
+ foreach (var generator in generators)
+ {
+ Guard.False($"{nameof(generator)}.{nameof(generator.IsInfinity)}", generator.IsInfinity);
+ }
+
+ var randomness = proof.Randomness;
+ var responses = proof.Responses.ToArray();
+
+ var challenge = ZkChallenge.HashToScalar(new[] { publicPoint, randomness }.Concat(generators).ToArray());
+ var a = challenge * publicPoint + randomness;
- var a = challenge * publicPoint + randomPoint;
- var b = proof.Response * generator;
+ var b = GroupElement.Infinity;
+ for (int i = 0; i < responses.Length; i++)
+ {
+ var response = responses[i];
+ var generator = generators.ToArray()[i];
+
+ b += response * generator;
+ }
return a == b;
}
}

Very bad commit, don't look at it, it's just consolidating cryptographic progress.