public void HandleResponse(RegistrationResponse 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.IssuerParametersStmt(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); }
public RegistrationResponse HandleRequest(RegistrationRequest 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 RegistrationResponse(macs, proofs); // Register the serial numbers to prevent credential reuse. foreach (var presentation in presented) { SerialNumbers.Add(presentation.S); } Balance += registrationRequest.DeltaAmount; return(response); }