/// <summary> /// Initializes a new instance of the CredentialIssuer class. /// </summary> /// <param name="coordinatorSecretKey">The <see cref="CoordinatorSecretKey">coordinator's secret key</see> used to issue the credentials.</param> /// <param name="numberOfCredentials">The number of credentials that the protocol handles in each request/response.</param> /// <param name="randomNumberGenerator">The random number generator.</param> public CredentialIssuer(CoordinatorSecretKey coordinatorSecretKey, int numberOfCredentials, WasabiRandom randomNumberGenerator) { CoordinatorSecretKey = Guard.NotNull(nameof(coordinatorSecretKey), coordinatorSecretKey); NumberOfCredentials = Guard.InRangeAndNotNull(nameof(numberOfCredentials), numberOfCredentials, 1, 100); CoordinatorParameters = CoordinatorSecretKey.ComputeCoordinatorParameters(); RandomNumberGenerator = Guard.NotNull(nameof(randomNumberGenerator), randomNumberGenerator); }
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 blindingFactor = rnd.GetScalar(); var Ma = amount * Generators.G + blindingFactor * 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 coordinatorStatement = ProofSystem.CreateStatement(coordinatorParameters, mac.V, Ma, t); var proverBuilder = ProofSystem.CreateProver(coordinatorStatement, coordinatorKey); var macProver = proverBuilder(rnd); var proofOfMac = macProver(); // 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.CreateStatement(coordinatorParameters, mac.V, Ma, mac.T); var verifierBuilder = ProofSystem.CreateVerifier(clientStatement); var macVerifier = verifierBuilder(proofOfMac); var isValidProof = macVerifier(); Assert.True(isValidProof); }
public void CannotBuildWrongMac() { var rnd = new SecureRandom(); var sk = new CoordinatorSecretKey(rnd); Assert.Throws <ArgumentNullException>(() => MAC.ComputeMAC(null !, Generators.G, Scalar.One)); Assert.Throws <ArgumentNullException>(() => MAC.ComputeMAC(sk, null !, Scalar.One)); var ex = Assert.Throws <ArgumentException>(() => MAC.ComputeMAC(sk, Generators.G, Scalar.Zero)); Assert.StartsWith("Value cannot be zero.", ex.Message); }
public void CanProduceAndVerifyMAC() { var rnd = new SecureRandom(); var sk = new CoordinatorSecretKey(rnd); var attribute = rnd.GetScalar() * Generators.G; // any random point var t = rnd.GetScalar(); var mac = MAC.ComputeMAC(sk, attribute, t); Assert.True(mac.VerifyMAC(sk, attribute)); }
public void EqualityTests() { var rnd = new SecureRandom(); var sk = new CoordinatorSecretKey(rnd); var attribute = rnd.GetScalar() * Generators.G; // any random point var differentAttribute = rnd.GetScalar() * Generators.G; // any other random point var t = rnd.GetScalar(); var right = (attribute : rnd.GetScalar() * Generators.G, sk : new CoordinatorSecretKey(rnd), t : rnd.GetScalar()); var wrong = (attribute : rnd.GetScalar() * Generators.G, sk : new CoordinatorSecretKey(rnd), t : rnd.GetScalar()); var cases = new[]
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); }
public void RegistrationMessageSerialization() { var converters = new JsonConverter[] { new ScalarJsonConverter(), new GroupElementJsonConverter(), new MoneySatoshiJsonConverter() }; var numberOfCredentials = 2; var rnd = new SecureRandom(); var sk = new CoordinatorSecretKey(rnd); var issuer = new CredentialIssuer(sk, numberOfCredentials, rnd); var client = new WabiSabiClient(sk.ComputeCoordinatorParameters(), numberOfCredentials, rnd); var(credentialRequest, validationData) = client.CreateRequestForZeroAmount(); var credentialResponse = issuer.HandleRequest(credentialRequest); client.HandleResponse(credentialResponse, validationData); var present = client.Credentials.ZeroValue.Take(numberOfCredentials); (credentialRequest, _) = client.CreateRequest(new[] { Money.Coins(1) }, present); // Registration request message var serializedRequestMessage = JsonConvert.SerializeObject(credentialRequest, converters); var deserializedRequestMessage = JsonConvert.DeserializeObject <RegistrationRequestMessage>(serializedRequestMessage, converters); var reserializedRequestMessage = JsonConvert.SerializeObject(deserializedRequestMessage, converters); Assert.Equal(serializedRequestMessage, reserializedRequestMessage); // Registration response message var serializedResponseMessage = JsonConvert.SerializeObject(credentialResponse, converters); var deserializedResponseMessage = JsonConvert.DeserializeObject <RegistrationResponseMessage>(serializedResponseMessage, converters); var reserializedResponseMessage = JsonConvert.SerializeObject(deserializedResponseMessage, converters); Assert.Equal(serializedResponseMessage, reserializedResponseMessage); }
public void CanProveAndVerifyMacShow() { 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.Gg + 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); // The client randomizes the commitments before presenting them to the coordinator proving to // the coordinator that a credential is valid (prover knows a valid MAC on non-randomized attribute) var credential = new Credential(amount, r, mac); var z = rnd.GetScalar(); var randomizedCredential = credential.Present(z); var knowledge = ProofSystem.ShowCredential(randomizedCredential, z, credential, coordinatorParameters); var proofOfMacShow = ProofSystem.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); Assert.True(isValidProof); }
public void CanDetectInvalidMAC() { var rnd = new SecureRandom(); var sk = new CoordinatorSecretKey(rnd); var attribute = rnd.GetScalar() * Generators.G; // any random point var differentAttribute = rnd.GetScalar() * Generators.G; // any other random point var t = rnd.GetScalar(); // Create MAC for realAttribute and verify with fake/wrong attribute var mac = MAC.ComputeMAC(sk, attribute, t); Assert.False(mac.VerifyMAC(sk, differentAttribute)); var differentT = rnd.GetScalar(); var differentMac = MAC.ComputeMAC(sk, attribute, differentT); Assert.NotEqual(mac, differentMac); mac = MAC.ComputeMAC(sk, attribute, differentT); var differentSk = new CoordinatorSecretKey(rnd); Assert.False(mac.VerifyMAC(differentSk, attribute)); }
public void GenerateCoordinatorParameters() { // Coordinator key is (0, 0, 0, 0, 0) var rnd = new MockRandom(); rnd.GetScalarResults.AddRange(Enumerable.Repeat(Scalar.Zero, 5)); var key = new CoordinatorSecretKey(rnd); var ex = Assert.Throws <ArgumentException>(key.ComputeCoordinatorParameters); Assert.StartsWith("Point at infinity is not a valid value.", ex.Message); // Coordinator key is (0, 0, 1, 1, 1) rnd = new MockRandom(); rnd.GetScalarResults.AddRange(Enumerable.Repeat(Scalar.Zero, 2)); rnd.GetScalarResults.AddRange(Enumerable.Repeat(Scalar.One, 3)); key = new CoordinatorSecretKey(rnd); ex = Assert.Throws <ArgumentException>(key.ComputeCoordinatorParameters); Assert.StartsWith("Point at infinity is not a valid value.", ex.Message); // Coordinator key is (1, 1, 0, 0, 0) rnd = new MockRandom(); rnd.GetScalarResults.AddRange(Enumerable.Repeat(Scalar.One, 2)); rnd.GetScalarResults.AddRange(Enumerable.Repeat(Scalar.Zero, 3)); key = new CoordinatorSecretKey(rnd); var iparams = key.ComputeCoordinatorParameters(); Assert.Equal(Generators.GV, iparams.I); // Coordinator key is (1, 1, 1, 1, 1) rnd = new MockRandom(); rnd.GetScalarResults.AddRange(Enumerable.Repeat(Scalar.One, 5)); key = new CoordinatorSecretKey(rnd); iparams = key.ComputeCoordinatorParameters(); Assert.Equal(Generators.Gw + Generators.Gwp, iparams.Cw); Assert.Equal(Generators.GV - Generators.Gx0 - Generators.Gx1 - Generators.Ga, iparams.I); }
public static Knowledge IssuerParameters(MAC mac, GroupElement ma, CoordinatorSecretKey sk) => new Knowledge(IssuerParametersStmt(sk.ComputeCoordinatorParameters(), mac, ma), new ScalarVector(sk.W, sk.Wp, sk.X0, sk.X1, sk.Ya));
private static LinearRelation.Knowledge ToKnowledge(this LinearRelation.Statement statement, CoordinatorSecretKey coordinatorSecretKey) => statement.ToKnowledge(new ScalarVector( coordinatorSecretKey.W, coordinatorSecretKey.Wp, coordinatorSecretKey.X0, coordinatorSecretKey.X1, coordinatorSecretKey.Ya));
public static NonInteractive.FiatShamirTransform.ProverCommitToNonces CreateProver(LinearRelation.Statement statement, CoordinatorSecretKey coordinatorSecretKey) => new NonInteractive.FiatShamirTransform.Prover(statement.ToKnowledge(coordinatorSecretKey)).CommitToStatements(Transcript);
/// <summary> /// Computes the Z element. /// </summary> /// <param name="sk">The coordinator's secret key.</param> /// <returns>The Z element needed to verify that a randomized credential's proof is valid.</returns> public GroupElement ComputeZ(CoordinatorSecretKey sk) => CV - (sk.W * Generators.Gw + sk.X0 * Cx0 + sk.X1 * Cx1 + sk.Ya * Ca);
public void CredentialIssuance() { var numberOfCredentials = 3; var rnd = new SecureRandom(); var sk = new CoordinatorSecretKey(rnd); var client = new WabiSabiClient(sk.ComputeCoordinatorParameters(), numberOfCredentials, rnd); { // Null request. This requests `numberOfCredentials` zero-value credentials. var(credentialRequest, validationData) = client.CreateRequestForZeroAmount(); Assert.True(credentialRequest.IsNullRequest); Assert.Equal(numberOfCredentials, credentialRequest.Requested.Count()); var requested = credentialRequest.Requested.ToArray(); Assert.Empty(requested[0].BitCommitments); Assert.Empty(requested[1].BitCommitments); Assert.Empty(requested[2].BitCommitments); Assert.Equal(Money.Zero, credentialRequest.DeltaAmount); // Issuer part var issuer = new CredentialIssuer(sk, numberOfCredentials, rnd); var credentialResponse = issuer.HandleRequest(credentialRequest); client.HandleResponse(credentialResponse, validationData); Assert.Equal(numberOfCredentials, client.Credentials.ZeroValue.Count()); Assert.Empty(client.Credentials.Valuable); var issuedCredential = client.Credentials.ZeroValue.First(); Assert.True(issuedCredential.Amount.IsZero); } { var present = client.Credentials.ZeroValue.Take(numberOfCredentials); var(credentialRequest, validationData) = client.CreateRequest(new[] { Money.Coins(1) }, present); Assert.False(credentialRequest.IsNullRequest); var credentialRequested = credentialRequest.Requested.ToArray(); Assert.Equal(numberOfCredentials, credentialRequested.Length); Assert.NotEmpty(credentialRequested[0].BitCommitments); Assert.NotEmpty(credentialRequested[1].BitCommitments); // Issuer part var issuer = new CredentialIssuer(sk, numberOfCredentials, rnd); var credentialResponse = issuer.HandleRequest(credentialRequest); client.HandleResponse(credentialResponse, validationData); var issuedCredential = Assert.Single(client.Credentials.Valuable); Assert.Equal(new Scalar(100_000_000), issuedCredential.Amount); Assert.Equal(2, client.Credentials.ZeroValue.Count()); Assert.Equal(3, client.Credentials.All.Count()); } { var valuableCredential = client.Credentials.Valuable.Take(1); var amounts = Enumerable.Repeat(Money.Coins(0.5m), 2); var(credentialRequest, validationData) = client.CreateRequest(amounts, valuableCredential); Assert.False(credentialRequest.IsNullRequest); var requested = credentialRequest.Requested.ToArray(); Assert.Equal(numberOfCredentials, requested.Length); Assert.NotEmpty(requested[0].BitCommitments); Assert.NotEmpty(requested[1].BitCommitments); Assert.Equal(Money.Zero, credentialRequest.DeltaAmount); // Issuer part var issuer = new CredentialIssuer(sk, numberOfCredentials, rnd); var credentialResponse = issuer.HandleRequest(credentialRequest); client.HandleResponse(credentialResponse, validationData); var credentials = client.Credentials.All.ToArray(); Assert.NotEmpty(credentials); Assert.Equal(3, credentials.Length); var valuableCredentials = client.Credentials.Valuable.ToArray(); Assert.Equal(new Scalar(50_000_000), valuableCredentials[0].Amount); Assert.Equal(new Scalar(50_000_000), valuableCredentials[1].Amount); } { var client0 = new WabiSabiClient(sk.ComputeCoordinatorParameters(), numberOfCredentials, rnd); var(credentialRequest, validationData) = client0.CreateRequestForZeroAmount(); var issuer = new CredentialIssuer(sk, numberOfCredentials, rnd); var credentialResponse = issuer.HandleRequest(credentialRequest); client0.HandleResponse(credentialResponse, validationData); (credentialRequest, validationData) = client0.CreateRequest(new[] { Money.Coins(1m) }, Enumerable.Empty <Credential>()); credentialResponse = issuer.HandleRequest(credentialRequest); client0.HandleResponse(credentialResponse, validationData); (credentialRequest, validationData) = client0.CreateRequest(Array.Empty <Money>(), client0.Credentials.Valuable); credentialResponse = issuer.HandleRequest(credentialRequest); client0.HandleResponse(credentialResponse, validationData); Assert.NotEmpty(client0.Credentials.All); Assert.Equal(numberOfCredentials, client0.Credentials.All.Count()); } }
public void InvalidCredentialRequests() { var numberOfCredentials = 3; var rnd = new SecureRandom(); var sk = new CoordinatorSecretKey(rnd); var issuer = new CredentialIssuer(sk, numberOfCredentials, rnd); { var client = new WabiSabiClient(sk.ComputeCoordinatorParameters(), numberOfCredentials, rnd); // Null request. This requests `numberOfCredentials` zero-value credentials. var(credentialRequest, validationData) = client.CreateRequestForZeroAmount(); var credentialResponse = issuer.HandleRequest(credentialRequest); client.HandleResponse(credentialResponse, validationData); var(validCredentialRequest, _) = client.CreateRequest(Array.Empty <Money>(), client.Credentials.ZeroValue.Take(1)); // Test incorrect number of presentations (one instead of 3) var presented = validCredentialRequest.Presented.ToArray(); var invalidCredentialRequest = new RegistrationRequestMessage( validCredentialRequest.DeltaAmount, new[] { presented[0] }, // Should present 3 credentials validCredentialRequest.Requested, validCredentialRequest.Proofs); var ex = Assert.Throws <WabiSabiException>(() => issuer.HandleRequest(invalidCredentialRequest)); Assert.Equal(WabiSabiErrorCode.InvalidNumberOfPresentedCredentials, ex.ErrorCode); Assert.Equal("3 credential presentations were expected but 1 were received.", ex.Message); // Test incorrect number of presentations (0 instead of 3) presented = credentialRequest.Presented.ToArray(); invalidCredentialRequest = new RegistrationRequestMessage( Money.Coins(2), Array.Empty <CredentialPresentation>(), // Should present 3 credentials validCredentialRequest.Requested, validCredentialRequest.Proofs); ex = Assert.Throws <WabiSabiException>(() => issuer.HandleRequest(invalidCredentialRequest)); Assert.Equal(WabiSabiErrorCode.InvalidNumberOfPresentedCredentials, ex.ErrorCode); Assert.Equal("3 credential presentations were expected but 0 were received.", ex.Message); (validCredentialRequest, _) = client.CreateRequest(Array.Empty <Money>(), client.Credentials.All); // Test incorrect number of credential requests invalidCredentialRequest = new RegistrationRequestMessage( validCredentialRequest.DeltaAmount, validCredentialRequest.Presented, validCredentialRequest.Requested.Take(1), validCredentialRequest.Proofs); ex = Assert.Throws <WabiSabiException>(() => issuer.HandleRequest(invalidCredentialRequest)); Assert.Equal(WabiSabiErrorCode.InvalidNumberOfRequestedCredentials, ex.ErrorCode); Assert.Equal("3 credential requests were expected but 1 were received.", ex.Message); // Test incorrect number of credential requests invalidCredentialRequest = new RegistrationRequestMessage( Money.Coins(2), Array.Empty <CredentialPresentation>(), validCredentialRequest.Requested.Take(1), validCredentialRequest.Proofs); ex = Assert.Throws <WabiSabiException>(() => issuer.HandleRequest(invalidCredentialRequest)); Assert.Equal(WabiSabiErrorCode.InvalidNumberOfRequestedCredentials, ex.ErrorCode); Assert.Equal("3 credential requests were expected but 1 were received.", ex.Message); // Test invalid range proof var requested = validCredentialRequest.Requested.ToArray(); invalidCredentialRequest = new RegistrationRequestMessage( validCredentialRequest.DeltaAmount, validCredentialRequest.Presented, new[] { requested[0], requested[1], new IssuanceRequest(requested[2].Ma, new[] { GroupElement.Infinity }) }, validCredentialRequest.Proofs); ex = Assert.Throws <WabiSabiException>(() => issuer.HandleRequest(invalidCredentialRequest)); Assert.Equal(WabiSabiErrorCode.InvalidBitCommitment, ex.ErrorCode); } { var client = new WabiSabiClient(sk.ComputeCoordinatorParameters(), numberOfCredentials, rnd); var(validCredentialRequest, validationData) = client.CreateRequestForZeroAmount(); // Test invalid proofs var proofs = validCredentialRequest.Proofs.ToArray(); proofs[0] = proofs[1]; var invalidCredentialRequest = new RegistrationRequestMessage( validCredentialRequest.DeltaAmount, validCredentialRequest.Presented, validCredentialRequest.Requested, proofs); var ex = Assert.Throws <WabiSabiException>(() => issuer.HandleRequest(invalidCredentialRequest)); Assert.Equal(WabiSabiErrorCode.CoordinatorReceivedInvalidProofs, ex.ErrorCode); } { var client = new WabiSabiClient(sk.ComputeCoordinatorParameters(), numberOfCredentials, rnd); var(validCredentialRequest, validationData) = client.CreateRequestForZeroAmount(); var credentialResponse = issuer.HandleRequest(validCredentialRequest); client.HandleResponse(credentialResponse, validationData); (validCredentialRequest, validationData) = client.CreateRequest(Enumerable.Empty <Money>(), client.Credentials.All); issuer.HandleRequest(validCredentialRequest); var ex = Assert.Throws <WabiSabiException>(() => issuer.HandleRequest(validCredentialRequest)); Assert.Equal(WabiSabiErrorCode.SerialNumberAlreadyUsed, ex.ErrorCode); } }