/// <summary>
        /// Creates a <see cref="CredentialsRequest">credential registration request messages</see>
        /// for requesting `k` zero-value credentials.
        /// </summary>
        /// <remarks>
        /// The request messages created by CreateRequestForZeroAmount are called null requests. The first
        /// registration request message that has to be sent to the coordinator is a null request, in this
        /// way the coordinator issues `k` zero-value credentials that can be used in following requests.
        /// </remarks>
        public ZeroCredentialsRequestData CreateRequestForZeroAmount()
        {
            var credentialsToRequest = new IssuanceRequest[NumberOfCredentials];
            var knowledge            = new Knowledge[NumberOfCredentials];
            var validationData       = new IssuanceValidationData[NumberOfCredentials];

            for (var i = 0; i < NumberOfCredentials; i++)
            {
                var randomness = RandomNumberGenerator.GetScalar(allowZero: false);
                var ma         = randomness * Generators.Gh;

                knowledge[i]            = ProofSystem.ZeroProofKnowledge(ma, randomness);
                credentialsToRequest[i] = new IssuanceRequest(ma, Enumerable.Empty <GroupElement>());
                validationData[i]       = new IssuanceValidationData(0, randomness, ma);
            }

            var transcript = BuildTransnscript(isNullRequest: true);

            return(new(
                       new ZeroCredentialsRequest(
                           credentialsToRequest,
                           ProofSystem.Prove(transcript, knowledge, RandomNumberGenerator)),
                       new CredentialsResponseValidation(
                           transcript,
                           Enumerable.Empty <Credential>(),
                           validationData)));
        }
        /// <summary>
        /// Creates a <see cref="RealCredentialsRequest">credential registration request messages</see>
        /// for requesting `k` non-zero-value credentials.
        /// </summary>
        /// <param name="amountsToRequest">List of amounts requested in credentials.</param>
        /// <param name="credentialsToPresent">List of credentials to be presented to the coordinator.</param>
        /// <param name="cancellationToken">The cancellation token to be used in case shut down is in progress..</param>
        /// <returns>
        /// A tuple containing the registration request message instance and the registration validation data
        /// to be used to validate the coordinator response message (the issued credentials).
        /// </returns>
        private RealCredentialsRequestData InternalCreateRequest(
            IEnumerable <long> amountsToRequest,
            IEnumerable <Credential> credentialsToPresent,
            CancellationToken cancellationToken)
        {
            // Make sure we request always the same number of credentials
            var credentialAmountsToRequest = amountsToRequest.ToList();

            var macsToPresent = credentialsToPresent.Select(x => x.Mac);

            if (macsToPresent.Distinct().Count() < macsToPresent.Count())
            {
                throw new WabiSabiCryptoException(WabiSabiCryptoErrorCode.CredentialToPresentDuplicated);
            }

            var zs = new List <Scalar>();
            var knowledgeToProve = new List <Knowledge>();
            var presentations    = new List <CredentialPresentation>();

            foreach (var credential in credentialsToPresent)
            {
                var z            = RandomNumberGenerator.GetScalar();
                var presentation = credential.Present(z);
                presentations.Add(presentation);
                knowledgeToProve.Add(ProofSystem.ShowCredentialKnowledge(presentation, z, credential, CredentialIssuerParameters));
                zs.Add(z);
            }

            // Generate RangeProofs (or ZeroProof) for each requested credential
            var expectedNumberOfCredentials = credentialAmountsToRequest.Count;
            var credentialsToRequest        = new IssuanceRequest[expectedNumberOfCredentials];
            var validationData = new IssuanceValidationData[expectedNumberOfCredentials];

            for (var i = 0; i < expectedNumberOfCredentials; i++)
            {
                var value  = credentialAmountsToRequest[i];
                var scalar = new Scalar((ulong)value);

                var randomness = RandomNumberGenerator.GetScalar(allowZero: false);
                var ma         = ProofSystem.PedersenCommitment(scalar, randomness);

                var(rangeKnowledge, bitCommitments) = ProofSystem.RangeProofKnowledge(scalar, randomness, RangeProofWidth, RandomNumberGenerator);
                knowledgeToProve.Add(rangeKnowledge);

                var credentialRequest = new IssuanceRequest(ma, bitCommitments);
                credentialsToRequest[i] = credentialRequest;
                validationData[i]       = new IssuanceValidationData(value, randomness, ma);
            }

            // Generate Balance Proof
            var sumOfZ = zs.Sum();
            var cr     = credentialsToPresent.Select(x => x.Randomness).Sum();
            var r      = validationData.Select(x => x.Randomness).Sum();
            var deltaR = cr + r.Negate();

            var balanceKnowledge = ProofSystem.BalanceProofKnowledge(sumOfZ, deltaR);

            knowledgeToProve.Add(balanceKnowledge);

            var transcript = BuildTransnscript(isNullRequest: false);

            return(new(
                       new RealCredentialsRequest(
                           amountsToRequest.Sum() - credentialsToPresent.Sum(x => x.Value),
                           presentations,
                           credentialsToRequest,
                           ProofSystem.Prove(transcript, knowledgeToProve, RandomNumberGenerator)),
                       new CredentialsResponseValidation(
                           transcript,
                           credentialsToPresent,
                           validationData)));
        }
Example #3
0
        /// <summary>
        /// Creates a <see cref="RealCredentialsRequest">credential registration request messages</see>
        /// for requesting `k` non-zero-value credentials.
        /// </summary>
        /// <param name="amountsToRequest">List of amounts requested in credentials.</param>
        /// <param name="credentialsToPresent">List of credentials to be presented to the coordinator.</param>
        /// <returns>
        /// A tuple containing the registration request message instance and the registration validation data
        /// to be used to validate the coordinator response message (the issued credentials).
        /// </returns>
        public RealCredentialsRequestData CreateRequest(
            IEnumerable <long> amountsToRequest,
            IEnumerable <Credential> credentialsToPresent)
        {
            // Make sure we request always the same number of credentials
            var credentialAmountsToRequest = amountsToRequest.ToList();
            var missingCredentialRequests  = NumberOfCredentials - amountsToRequest.Count();

            for (var i = 0; i < missingCredentialRequests; i++)
            {
                credentialAmountsToRequest.Add(0);
            }

            // Make sure we present always the same number of credentials (except for Null requests)
            var missingCredentialPresent = NumberOfCredentials - credentialsToPresent.Count();

            var alreadyPresentedZeroCredentials = credentialsToPresent.Where(x => x.Amount.IsZero);
            var availableZeroCredentials        = Credentials.ZeroValue.Except(alreadyPresentedZeroCredentials);

            // This should not be possible
            var availableZeroCredentialCount = availableZeroCredentials.Count();

            if (availableZeroCredentialCount < missingCredentialPresent)
            {
                throw new WabiSabiCryptoException(
                          WabiSabiCryptoErrorCode.NotEnoughZeroCredentialToFillTheRequest,
                          $"{missingCredentialPresent} credentials are missing but there are only {availableZeroCredentialCount} zero-value credentials available.");
            }

            credentialsToPresent = credentialsToPresent.Concat(availableZeroCredentials.Take(missingCredentialPresent)).ToList();
            var macsToPresent = credentialsToPresent.Select(x => x.Mac);

            if (macsToPresent.Distinct().Count() < macsToPresent.Count())
            {
                throw new WabiSabiCryptoException(WabiSabiCryptoErrorCode.CredentialToPresentDuplicated);
            }

            var zs = new List <Scalar>();
            var knowledgeToProve = new List <Knowledge>();
            var presentations    = new List <CredentialPresentation>();

            foreach (var credential in credentialsToPresent)
            {
                var z            = RandomNumberGenerator.GetScalar();
                var presentation = credential.Present(z);
                presentations.Add(presentation);
                knowledgeToProve.Add(ProofSystem.ShowCredentialKnowledge(presentation, z, credential, CredentialIssuerParameters));
                zs.Add(z);
            }

            // Generate RangeProofs (or ZeroProof) for each requested credential
            var credentialsToRequest = new IssuanceRequest[NumberOfCredentials];
            var validationData       = new IssuanceValidationData[NumberOfCredentials];

            for (var i = 0; i < NumberOfCredentials; i++)
            {
                var amount       = credentialAmountsToRequest[i];
                var scalarAmount = new Scalar((ulong)amount);

                var randomness = RandomNumberGenerator.GetScalar(allowZero: false);
                var ma         = ProofSystem.PedersenCommitment(scalarAmount, randomness);

                var(rangeKnowledge, bitCommitments) = ProofSystem.RangeProofKnowledge(scalarAmount, randomness, RangeProofWidth, RandomNumberGenerator);
                knowledgeToProve.Add(rangeKnowledge);

                var credentialRequest = new IssuanceRequest(ma, bitCommitments);
                credentialsToRequest[i] = credentialRequest;
                validationData[i]       = new IssuanceValidationData(amount, randomness, ma);
            }

            // Generate Balance Proof
            var sumOfZ = zs.Sum();
            var cr     = credentialsToPresent.Select(x => x.Randomness).Sum();
            var r      = validationData.Select(x => x.Randomness).Sum();
            var deltaR = cr + r.Negate();

            var balanceKnowledge = ProofSystem.BalanceProofKnowledge(sumOfZ, deltaR);

            knowledgeToProve.Add(balanceKnowledge);

            var transcript = BuildTransnscript(isNullRequest: false);

            return(new (
                       new RealCredentialsRequest(
                           amountsToRequest.Sum() - credentialsToPresent.Sum(x => (long)x.Amount.ToUlong()),
                           presentations,
                           credentialsToRequest,
                           ProofSystem.Prove(transcript, knowledgeToProve, RandomNumberGenerator)),
                       new CredentialsResponseValidation(
                           transcript,
                           credentialsToPresent,
                           validationData)));
        }