Example #1
0
        /// <summary>
        /// Handles the registration response received from the coordinator.
        /// </summary>
        /// <remarks>
        /// Verifies the registration response message proofs, creates the credentials based on the issued MACs and
        /// finally updates the credentials pool by removing those credentials that were presented and by adding
        /// those that were issued.
        /// </remarks>
        /// <param name="registrationResponse">The registration response message received from the coordinator.</param>
        /// <param name="registrationValidationData">The state data required to validate the issued credentials and the proofs.</param>
        public void HandleResponse(RegistrationResponseMessage registrationResponse, RegistrationValidationData registrationValidationData)
        {
            Guard.NotNull(nameof(registrationResponse), registrationResponse);
            Guard.NotNull(nameof(registrationValidationData), registrationValidationData);

            var issuedCredentialCount    = registrationResponse.IssuedCredentials.Count();
            var requestedCredentialCount = registrationValidationData.Requested.Count();

            if (issuedCredentialCount != NumberOfCredentials)
            {
                throw new WabiSabiException(
                          WabiSabiErrorCode.IssuedCredentialNumberMismatch,
                          $"{issuedCredentialCount} issued but {requestedCredentialCount} were requested.");
            }

            var credentials = Enumerable
                              .Zip(registrationValidationData.Requested, registrationResponse.IssuedCredentials)
                              .Select(x => (Requested: x.First, Issued: x.Second))
                              .ToArray();

            var statements = credentials
                             .Select(x => ProofSystem.IssuerParametersStatement(CoordinatorParameters, x.Issued, x.Requested.Ma));

            var areCorrectlyIssued = ProofSystem.Verify(registrationValidationData.Transcript, statements, registrationResponse.Proofs);

            if (!areCorrectlyIssued)
            {
                throw new WabiSabiException(WabiSabiErrorCode.ClientReceivedInvalidProofs);
            }

            var credentialReceived = credentials.Select(x =>
                                                        new Credential(new Scalar((ulong)x.Requested.Amount.Satoshi), x.Requested.Randomness, x.Issued));

            Credentials.UpdateCredentials(credentialReceived, registrationValidationData.Presented);
        }
Example #2
0
        /// <summary>
        /// Process the <see cref="RegistrationRequestMessage">credentials registration requests</see> and
        /// issues the credentials.
        /// </summary>
        /// <param name="registrationRequest">The request containing the credentials presentations, credential requests and the proofs.</param>
        /// <returns>The <see cref="RegistrationResponseMessage">registration response</see> containing the requested credentials and the proofs.</returns>
        /// <exception cref="WabiSabiException">Error code: <see cref="WabiSabiErrorCode">WabiSabiErrorCode</see></exception>
        public RegistrationResponseMessage HandleRequest(RegistrationRequestMessage registrationRequest)
        {
            Guard.NotNull(nameof(registrationRequest), registrationRequest);

            var requested = registrationRequest.Requested ?? Enumerable.Empty <IssuanceRequest>();
            var presented = registrationRequest.Presented ?? Enumerable.Empty <CredentialPresentation>();

            var requestedCount = requested.Count();

            if (requestedCount != NumberOfCredentials)
            {
                throw new WabiSabiException(
                          WabiSabiErrorCode.InvalidNumberOfRequestedCredentials,
                          $"{NumberOfCredentials} credential requests were expected but {requestedCount} were received.");
            }

            var presentedCount = presented.Count();
            var requiredNumberOfPresentations = registrationRequest.IsNullRequest ? 0 : NumberOfCredentials;

            if (presentedCount != requiredNumberOfPresentations)
            {
                throw new WabiSabiException(
                          WabiSabiErrorCode.InvalidNumberOfPresentedCredentials,
                          $"{requiredNumberOfPresentations} credential presentations were expected but {presentedCount} were received.");
            }

            // Don't allow balance to go negative. In case this goes below zero
            // then there is a problem somewhere because this should not be possible.
            if (Balance + registrationRequest.DeltaAmount < Money.Zero)
            {
                throw new WabiSabiException(WabiSabiErrorCode.NegativeBalance);
            }

            // Check that the range proofs are of the appropriate bitwidth
            var rangeProofWidth = registrationRequest.IsNullRequest ? 0 : Constants.RangeProofWidth;
            var allRangeProofsAreCorrectSize = requested.All(x => x.BitCommitments.Count() == rangeProofWidth);

            if (!allRangeProofsAreCorrectSize)
            {
                throw new WabiSabiException(WabiSabiErrorCode.InvalidBitCommitment);
            }

            // Check all the serial numbers are unique. Note that this is checked separately from
            // ensuring that they haven't been used before, because even presenting a previously
            // unused credential more than once in the same request is still a double spend.
            if (registrationRequest.AreThereDuplicatedSerialNumbers)
            {
                throw new WabiSabiException(WabiSabiErrorCode.SerialNumberDuplicated);
            }

            var statements = new List <Statement>();

            foreach (var presentation in presented)
            {
                // Calculate Z using coordinator secret.
                var Z = presentation.ComputeZ(CoordinatorSecretKey);

                // Add the credential presentation to the statements to be verified.
                statements.Add(ProofSystem.ShowCredentialStmt(presentation, Z, CoordinatorParameters));

                // Check if the serial numbers have been used before. Note that
                // the serial numbers have not yet been verified at this point, but a
                // request with an invalid proof and a used serial number should also be
                // rejected.
                if (SerialNumbers.Contains(presentation.S))
                {
                    throw new WabiSabiException(WabiSabiErrorCode.SerialNumberAlreadyUsed, $"Serial number reused {presentation.S}");
                }
            }

            foreach (var credentialRequest in requested)
            {
                statements.Add(registrationRequest.IsNullRequest
                                        ? ProofSystem.ZeroProofStmt(credentialRequest.Ma)
                                        : ProofSystem.RangeProofStmt(credentialRequest.Ma, credentialRequest.BitCommitments));
            }

            // Balance proof
            if (!registrationRequest.IsNullRequest)
            {
                var sumCa = presented.Select(x => x.Ca).Sum();
                var sumMa = requested.Select(x => x.Ma).Sum();

                // A positive Delta_a means the requested credential amounts are larger
                // than the presented ones (i.e. input registration, and a negative
                // balance correspond to output registration). The equation requires a
                // commitment to 0, so the sum of the presented attributes and the
                // negated requested attributes are tweaked by delta_a.
                var absAmountDelta = new Scalar(registrationRequest.DeltaAmount.Abs());
                var deltaA         = registrationRequest.DeltaAmount < Money.Zero ? absAmountDelta.Negate() : absAmountDelta;
                var balanceTweak   = deltaA * Generators.Gg;
                statements.Add(ProofSystem.BalanceProofStmt(balanceTweak + sumCa - sumMa));
            }

            var transcript = BuildTransnscript(registrationRequest.IsNullRequest);

            // Verify all statements.
            var areProofsValid = ProofSystem.Verify(transcript, statements, registrationRequest.Proofs);

            if (!areProofsValid)
            {
                throw new WabiSabiException(WabiSabiErrorCode.CoordinatorReceivedInvalidProofs);
            }

            // Issue credentials.
            var credentials = requested.Select(x => IssueCredential(x.Ma, RandomNumberGenerator.GetScalar())).ToArray();

            // Construct response.
            var proofs   = ProofSystem.Prove(transcript, credentials.Select(x => x.Knowledge), RandomNumberGenerator);
            var macs     = credentials.Select(x => x.Mac);
            var response = new RegistrationResponseMessage(macs, proofs);

            // Register the serial numbers to prevent credential reuse.
            foreach (var presentation in presented)
            {
                SerialNumbers.Add(presentation.S);
            }
            Balance += registrationRequest.DeltaAmount;

            return(response);
        }