Beispiel #1
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 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);
        }
Beispiel #2
0
 /// <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 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);
        }
Beispiel #6
0
        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);
        }
Beispiel #7
0
 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));
Beispiel #8
0
        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());
            }
        }
Beispiel #9
0
        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);
            }
        }