/// <summary> /// Helper method for constructing proof. Computes a[index], c[index], r[index] values for when /// the committedValue is not equal to prover.MemberSet[index]. /// </summary> /// <param name="index">Index into prover.MemberSet</param> /// <param name="prover">Prover parameters</param> /// <param name="randomChallengeValue">Value to set c[index]</param> /// <param name="randomResponseValue">Value to set r[index]</param> private void generateFakeProof(int index, ProverSetMembershipParameters prover, FieldZqElement randomChallengeValue, FieldZqElement randomResponseValue) { if (index < this.c.Length) { this.c[index] = randomChallengeValue; } this.r[index] = randomResponseValue; // compute this.a[index] as a multi-exponentiation // a[index] = h^r[index] * g^{memberset[index] + c[index]} * X^{-c[index] GroupElement[] bases = new GroupElement[3] { prover.H, // h prover.G, // g prover.ClosedCommitment // X }; FieldZqElement[] exponents = new FieldZqElement[3] { randomResponseValue, // r[index] prover.MemberSet[index] * randomChallengeValue, // memberset[index] * c randomChallengeValue.Negate() // -c }; this.a[index] = prover.Group.MultiExponentiate(bases, exponents); }
/// <summary> /// Verifies that the given (commitment, challenge, response) values are a /// valid proof for this object. See IOpenEquation for how commitment and response are generated. /// </summary> /// <param name="challenge">The challenge for this proof (second step in Sigma protocol).</param> /// <param name="responseValues">The response (third step in Sigma protocol).</param> /// <param name="commitment">The commitment (first step in Sigma protocol).</param> /// <returns></returns> public override bool Verify(GroupElement commitment, FieldZqElement challenge, FieldZqElement[] response) { if ((response == null) || (response.Length != this.RepresentationLength)) { return(false); } GroupElement[] bases = new GroupElement[this.RepresentationLength + 1]; FieldZqElement[] exponents = new FieldZqElement[this.RepresentationLength + 1]; for (int i = 0; i < response.Length; ++i) { bases[i] = this.BaseAtIndex(i); exponents[i] = response[i]; } exponents[0] = challenge.Negate(); bases[this.RepresentationLength] = this.PublicKey; exponents[this.RepresentationLength] = response[0]; GroupElement verifier = this.Group.MultiExponentiate(bases, exponents); if (commitment != verifier) { return(false); } return(true); }
/// <summary> /// Generates the U-Prove key and tokens. /// </summary> /// <param name="message">The third issuance message.</param> /// <param name="skipTokenValidation">Set to <code>true</code> to skip token validation; /// <code>false</code> otherwise. Token validation SHOULD be performed before use, either /// by setting <code>skipTokenValidation</code> to <code>false</code>, or by later using the /// <code>ProtocolHelper.VerifyTokenSignature</code> method. /// </param> /// <exception cref="InvalidUProveArtifactException">If the token signature is invalid.</exception> /// <returns>An array of U-Prove keys and tokens</returns> public UProveKeyAndToken[] GenerateTokens(ThirdIssuanceMessage message, bool skipTokenValidation = false) { if (state != State.Second) { throw new InvalidOperationException("GenerateSecondMessage must be called first"); } bool doBatchValidation = !skipTokenValidation && batchValidationSecurityLevel > 0; bool doNormalValidation = !skipTokenValidation && batchValidationSecurityLevel <= 0; Group Gq = ip.Gq; FieldZq Zq = ip.Zq; FieldZqElement[] sigmaRPrime = new FieldZqElement[numberOfTokens]; FieldZqElement phoAR = Zq.Zero, phoR = Zq.Zero, phoAC = Zq.Zero, phoC = Zq.Zero; GroupElement[] batchAccBases = new GroupElement[numberOfTokens]; FieldZqElement[] s = null; if (!skipTokenValidation) { s = Zq.GetRandomElements(numberOfTokens, true, batchValidationSecurityLevel); } for (int i = 0; i < numberOfTokens; i++) { sigmaRPrime[i] = message.sigmaR[i] + beta2[i]; if (doBatchValidation) { phoAR += (s[i] * alpha[i] * sigmaRPrime[i]); phoR += (s[i] * sigmaRPrime[i]); phoAC += (s[i] * alpha[i] * sigmaCPrime[i]); phoC += (s[i] * sigmaCPrime[i]); batchAccBases[i] = sigmaAPrime[i] * sigmaBPrime[i]; } else if (doNormalValidation) { if (!(sigmaAPrime[i] * sigmaBPrime[i]).Equals( Gq.MultiExponentiate(new GroupElement[] { Gq.G *h[i], ip.G[0] * sigmaZPrime[i] }, new FieldZqElement[] { sigmaRPrime[i], sigmaCPrime[i].Negate() }))) { throw new InvalidUProveArtifactException("Invalid token signature: " + i); } } ukat[i].Token = new UProveToken(ip.UidP, h[i], TI, PI, sigmaZPrime[i], sigmaCPrime[i], sigmaRPrime[i], isDeviceProtected); } if (doBatchValidation && (Gq.MultiExponentiate(batchAccBases, s) != Gq.MultiExponentiate(new GroupElement[] { Gq.G, gamma, ip.G[0], sigmaZ }, new FieldZqElement[] { phoR, phoAR, phoC.Negate(), phoAC.Negate() }))) // TODO: batch validation with blinded gamma needs to multiply beta0inverse to phoAR { throw new InvalidUProveArtifactException("Invalid token signature"); } state = State.Tokens; return(ukat); }
/// <summary> /// Updates the revocation witness for a user, either adding or removing a revoked value. /// </summary> /// <param name="rap">The Revocation Authority parameters.</param> /// <param name="xid">The revocation attribute value <c>xid</c>.</param> /// <param name="revoked">The attribute value to added to the accumulator, or <c>null</c>.</param> /// <param name="unrevoked">The attribute value to deleted to the accumulator, or <c>null</c>.</param> /// <param name="oldAccumulator">The old accumulator value <c>V</c>. If <c>null</c>, then the accumulator is freshly calculated.</param> /// <param name="updatedAccumulator">The old accumulator value <c>V'</c>.</param> /// <param name="oldWitness">The old witness values. If <c>null</c>, then the witness is freshly calculated.</param> /// <returns></returns> public static RevocationWitness UpdateWitness(RAParameters rap, FieldZqElement xid, FieldZqElement revoked, FieldZqElement unrevoked, GroupElement oldAccumulator, GroupElement updatedAccumulator, RevocationWitness oldWitness) { // TODO: implement batch updates if (revoked != null && unrevoked != null) { throw new ArgumentException("only one of revoked and unrevoked can be non-null"); } FieldZqElement one = rap.group.FieldZq.One; if (oldAccumulator == null) { // set the accumulator value for an empty revocation set oldAccumulator = rap.gt; } if (oldWitness == null) { oldWitness = new RevocationWitness(rap.group.FieldZq.One, rap.group.Identity, rap.group.Identity); } if (revoked == null && unrevoked == null) { // nothing to do return(oldWitness); } FieldZqElement dPrime = null; GroupElement WPrime = null; GroupElement QPrime = null; FieldZqElement xDiff = one; // add values to witness if (revoked != null) { xDiff = (revoked - xid); dPrime = oldWitness.d * xDiff; WPrime = oldAccumulator * oldWitness.W.Exponentiate(xDiff); } xDiff = one; // remove values from witness if (unrevoked != null) { xDiff = (unrevoked - xid).Invert(); dPrime = oldWitness.d * xDiff; WPrime = updatedAccumulator.Exponentiate(one.Negate()) * oldWitness.W.Exponentiate(xDiff); } // update QPrime value QPrime = updatedAccumulator * WPrime.Exponentiate(xid.Negate()) * rap.gt.Exponentiate(dPrime.Negate()); return(new RevocationWitness(dPrime, WPrime, QPrime)); }
private void ConstructorHelper(ProverSetMembershipParameters prover, SetMembershipProofGenerationRandomData smRandom = null) { try { // check that parameters can be used to construct valid proof. if (!prover.Verify()) { throw new ArgumentException("Invalid proof parameters. Cannot create proof"); } // set Group context this.Group = prover.Group; this.IsGroupSerializable = true; // allocate space for proof this.c = new FieldZqElement[prover.MemberSet.Length - 1]; this.a = new GroupElement[prover.MemberSet.Length]; this.r = new FieldZqElement[prover.MemberSet.Length]; // generate random values needed for proof if (smRandom == null) { // generate a pair of random values for each fake proofs, plus the random exponent for the real one smRandom = SetMembershipProofGenerationRandomData.Generate(prover.FieldZq, prover.MemberSet.Length - 1); } // Find index of prover.OpenCommitment.CommittedValue in prover.MemberSet int indexOfCommittedValue = prover.IndexOfCommittedValueInSet; // generate a fake proof for each prover.MemberSet element that is not // equal to CommitedValue. FieldZqElement sumOfSubChallenges = prover.FieldZq.Zero; int randomIndex = 0; for (int index = 0; index < prover.MemberSet.Length; ++index) { if (index != indexOfCommittedValue) { this.generateFakeProof(index, prover, smRandom.c[randomIndex], smRandom.r[randomIndex]); sumOfSubChallenges += smRandom.c[randomIndex]; randomIndex++; } } // generate challenge, and a real proof for committment.CommittedValue this.generateRealA(indexOfCommittedValue, prover, smRandom.w); FieldZqElement challenge = this.ComputeChallenge(prover); this.generateRealProof(indexOfCommittedValue, prover, smRandom.w, sumOfSubChallenges.Negate() + challenge); } catch (Exception e) { throw new Exception("Could not create SetMembershipProof.", e); } }
/// <summary> /// Genererates proof for when prover.CompareToKnownValue=true. /// </summary> /// <param name="prover"></param> private void CreateProofForKnownValue(ProverInequalityProofParameters prover) { // Pedersen Commitment to a random value a // A = g^a h^r FieldZqElement a = prover.FieldZq.GetRandomElement(true); FieldZqElement r = prover.FieldZq.GetRandomElement(true); PedersenCommitment openA = new PedersenCommitment(prover.G, prover.H, a, r, prover.Group); // B = g^(x-value)a DLRepOfGroupElement openB = new DLRepOfGroupElement( new GroupElement[1] { prover.G }, new FieldZqElement[1] { (prover.CommitmentX.CommittedValue - prover.Value) * a }, prover.Group ); // C = (Xg^{-value})^a h^{-ya} = B PedersenCommitment openC = new PedersenCommitment( prover.CommitmentX.Value * prover.G.Exponentiate(prover.Value.Negate()), prover.H, a, a.Negate() * prover.CommitmentX.Opening, prover.Group ); // Create DL equations DLRepOfGroupElement[] equations = new DLRepOfGroupElement[] { prover.CommitmentX, openA, openB, openC }; // generate proof EqualityMap map = this.GetEqualityMap(); ProverEqualityParameters eqProver = new ProverEqualityParameters(equations, map, prover); this.Proof = new EqualityProof(eqProver); this.A = openA.Value; this.B = openB.Value; }
FieldZqElement IDevicePresentationContext.GetDeviceResponse(byte[] messageForDevice, byte[] partialChallengeDigest, string hashOID) { if (this.device == null) { throw new DeviceException("Invalid context."); } if (this.wdPrime == null) { throw new DeviceException("Initial witness not yet calculated."); } HashFunction hashFunction = new HashFunction(GetHashFunctionName(hashOID)); FieldZqElement c = ProtocolHelper.GenerateChallengeForDevice(device.Zq, hashFunction, messageForDevice, partialChallengeDigest); FieldZqElement rdPrime = c.Negate() * device.xd + wdPrime; this.device = null; return(rdPrime); }
/// <summary> /// Returns ProverRangeProofParameters that the committed date to the /// the verifier target date. Range proof will require bit decomposition of approximately 9 + log_2 (maxYear - minYear) bits. /// </summary> /// <param name="crypto">Crypto parameters</param> /// <param name="commitmentToDayOfYear">Commitment value in [0,365]</param> /// <param name="commitmentToYear">Commitment to a year in [minYear, maxYear].</param> /// <param name="rangeProofType">Range proof type.</param> /// <param name="verifierTargetDate">Commitment to a date in [minYear, maxYear +1).</param> /// <param name="minYear">Limits range of proof. </param> /// <param name="maxYear">Limits range of proof.</param> /// <returns></returns> public static ProverRangeProofParameters GetDateTimeProverParameters(CryptoParameters crypto, PedersenCommitment commitmentToYear, PedersenCommitment commitmentToDayOfYear, VerifierRangeProofParameters.ProofType rangeProofType, DateTime verifierTargetDate, int minYear, int maxYear) { //Check crypto parameters and pedersen commitment generators G and H if (!commitmentToYear.AreBasesEqual(commitmentToDayOfYear)) { throw new ArgumentException("PedersenCommitments commitmentToYear and commitmentToDayOfYear have different bases."); } if ((crypto.G != commitmentToYear.BaseAtIndex(0)) || (crypto.H != commitmentToYear.BaseAtIndex(1))) { throw new ArgumentException("PedersenCommitments commitmentToYear and commitmentToDayOfYear should use bases crypto.G and crypto.H."); } FieldZqElement minYearElement = crypto.FieldZq.GetElement((uint)minYear); FieldZqElement daysInOneYear = crypto.FieldZq.GetElement(366); FieldZqElement committedYear = commitmentToYear.ExponentAtIndex(0); FieldZqElement committedDay = commitmentToDayOfYear.ExponentAtIndex(0); FieldZqElement openingYear = commitmentToYear.ExponentAtIndex(1); FieldZqElement openingDay = commitmentToDayOfYear.ExponentAtIndex(1); PedersenCommitment commitmentToYearAndDay = new PedersenCommitment( crypto.G, crypto.H, (committedYear + minYearElement.Negate()) * daysInOneYear + committedDay, openingYear * daysInOneYear + openingDay, crypto.Group); int maxValue = (maxYear - minYear) * 366 + 365; int verifierYearAndDay = EncodeYearAndDay(verifierTargetDate, minYear); return(new ProverRangeProofParameters( crypto, commitmentToYearAndDay, rangeProofType, verifierYearAndDay, 0, maxValue)); }
/// <summary> /// Generates a non-revocation proof. /// </summary> /// <param name="rap">The Revocation Authority parameters.</param> /// <param name="rw">The user's revocation witness.</param> /// <param name="tildeCid">The revocation attribute commitment.</param> /// <param name="xid">The revocation attribute.</param> /// <param name="tildeOid">The revocation attribute commitment's opening value.</param> /// <param name="preGenRandom">The optional pre-generated random values for the proof, or <c>null</c>.</param> /// <returns></returns> public static NonRevocationProof GenerateNonRevocationProof(RAParameters rap, RevocationWitness rw, GroupElement tildeCid, FieldZqElement xid, FieldZqElement tildeOid, NonRevocationProofGenerationRandomData preGenRandom = null) { if (rap == null || rw == null || tildeCid == null || xid == null || tildeOid == null) { throw new ArgumentNullException("null input to GenerateNonRevocationProof"); } Group Gq = rap.group; FieldZq Zq = Gq.FieldZq; NonRevocationProofGenerationRandomData rand = preGenRandom; if (rand == null) { rand = NonRevocationProofGenerationRandomData.Generate(Zq); } GroupElement X = rw.W * rap.g.Exponentiate(rand.t1); GroupElement Y = rw.Q * rap.K.Exponentiate(rand.t1); GroupElement Cd = rap.gt.Exponentiate(rw.d) * rap.g1.Exponentiate(rand.t2); FieldZqElement w = rw.d.Invert(); FieldZqElement z = rand.t1 * tildeOid - rand.t2; FieldZqElement zPrime = rand.t2.Negate() * w; GroupElement T1 = Gq.MultiExponentiate(new GroupElement[] { X, tildeCid * rap.K, rap.g1 }, new FieldZqElement[] { rand.k1, rand.k2.Negate(), rand.k3 }); GroupElement T2 = Gq.MultiExponentiate(new GroupElement[] { rap.g, rap.g1 }, new FieldZqElement[] { rand.k1, rand.k4 }); GroupElement T3 = Gq.MultiExponentiate(new GroupElement[] { Cd, rap.g1 }, new FieldZqElement[] { rand.k5, rand.k6 }); FieldZqElement cPrime = Zq.GetElementFromDigest(rap.ComputeChallenge(tildeCid, X, Y, Cd, T1, T2, T3)); FieldZqElement cPrimeNegate = cPrime.Negate(); FieldZqElement s1 = cPrimeNegate * xid + rand.k1; FieldZqElement s2 = cPrimeNegate * rand.t1 + rand.k2; FieldZqElement s3 = cPrimeNegate * z + rand.k3; FieldZqElement s4 = cPrimeNegate * tildeOid + rand.k4; FieldZqElement s5 = cPrimeNegate * w + rand.k5; FieldZqElement s6 = cPrimeNegate * zPrime + rand.k6; rand.Clear(); return(new NonRevocationProof(cPrime, new FieldZqElement[] { s1, s2, s3, s4, s5, s6 }, X, Y, Cd)); }
public void Verify(IssuerParameters ip, int[] disclosed, int[] committed, int pseudonymAttribIndex, GroupElement gs, byte[] message, byte[] messageD, UProveToken upt) { try { // make sure disclosed list is sorted if (disclosed == null) { // can't be null later, so make it an empty array disclosed = new int[] { }; } Array.Sort(disclosed); Group Gq = ip.Gq; int n = ip.E.Length; bool presentPseudonym = false; if (gs != null && pseudonymAttribIndex != 0) { if (pseudonymAttribIndex < 1 || (pseudonymAttribIndex > n && pseudonymAttribIndex != DeviceAttributeIndex)) { throw new ArgumentException("pseudonymAttribIndex must be between 1 and " + n + " (inclusive)"); } if (disclosed.Contains(pseudonymAttribIndex)) { throw new ArgumentException("pseudonymAttribIndex cannot be in the disclosed attribute array"); } presentPseudonym = true; } else // no nym { pseudonymAttribIndex = 0; } bool verifyCommitments = (committed != null && committed.Length > 0); if (verifyCommitments) { Array.Sort(committed); } ProtocolHelper.VerifyTokenSignature(ip, upt); int dArraySize = disclosed.Length + 2; GroupElement[] dBases = new GroupElement[dArraySize]; FieldZqElement[] dExponents = new FieldZqElement[dArraySize]; dBases[0] = ip.G[0]; dExponents[0] = ip.Zq.One; // g0^1 dBases[1] = ip.G[n + 1]; dExponents[1] = ProtocolHelper.ComputeXt(ip, upt.TI, upt.IsDeviceProtected); // gt^xt FieldZqElement[] disclosedX = new FieldZqElement[disclosedAttributes.Length]; int aPreImageArraySize = 2 + (n - disclosed.Length) + (upt.IsDeviceProtected ? 1 : 0); GroupElement[] aPreImageBases = new GroupElement[aPreImageArraySize]; FieldZqElement[] aPreImageExponents = new FieldZqElement[aPreImageArraySize]; // aPreImage arrays' index 0 values depend on the dArray values; they will be filled out later aPreImageBases[1] = upt.H; aPreImageExponents[1] = this.r[0]; // h^r0 int dIndex = 0; int uIndex = 1; int cIndex = 0; int pseudonymResponseIndex = 0; int[] commitmentResponseIndices = verifyCommitments ? new int[committed.Length] : null; for (int i = 1; i <= n; i++) { if (disclosed.Contains(i)) { disclosedX[dIndex] = ProtocolHelper.ComputeXi(ip, i - 1, disclosedAttributes[dIndex]); dBases[dIndex + 2] = ip.G[i]; dExponents[dIndex + 2] = disclosedX[dIndex]; dIndex++; } else { aPreImageBases[uIndex + 1] = ip.G[i]; aPreImageExponents[uIndex + 1] = this.r[uIndex]; // gi^ri if (presentPseudonym) { if (pseudonymAttribIndex == i) { pseudonymResponseIndex = uIndex; } } if (verifyCommitments) { if (committed.Contains(i)) { // remember which response correspond to which commitment commitmentResponseIndices[cIndex] = uIndex; cIndex++; } } uIndex++; } } if (pseudonymAttribIndex == DeviceAttributeIndex) { pseudonymResponseIndex = this.r.Length - 1; // r_d is the last response in the array } byte[] unused; // verifier doesn't use the returned message for device FieldZqElement c = ProtocolHelper.GenerateChallenge(ip, upt, this.a, pseudonymAttribIndex, this.ap, this.ps, message, messageD, disclosed, disclosedX, committed, this.Commitments, out unused); aPreImageBases[0] = Gq.MultiExponentiate(dBases, dExponents); aPreImageExponents[0] = c.Negate(); // g0.gt^xt.Product[gi^xi]_(for disclosed i) if (upt.IsDeviceProtected) { aPreImageBases[aPreImageArraySize - 1] = ip.Gd; aPreImageExponents[aPreImageArraySize - 1] = this.r[this.r.Length - 1]; // gd^rd } HashFunction hash = ip.HashFunction; hash.Hash(Gq.MultiExponentiate(aPreImageBases, aPreImageExponents)); if (!this.a.SequenceEqual(hash.Digest)) { throw new InvalidUProveArtifactException("Invalid presentation proof"); } if (presentPseudonym) { hash.Hash(Gq.MultiExponentiate(new GroupElement[] { ps, gs }, new FieldZqElement[] { c, r[pseudonymResponseIndex] })); if (!this.ap.SequenceEqual(hash.Digest)) { throw new InvalidUProveArtifactException("Invalid pseudonym"); } } if (verifyCommitments) { GroupElement[] cBases = new GroupElement[3]; FieldZqElement[] cExponents = new FieldZqElement[3]; cBases[1] = Gq.G; cBases[2] = ip.G[1]; cExponents[0] = c; for (int i = 0; i < commitmentResponseIndices.Length; i++) { CommitmentValues commitment = this.Commitments[i]; cBases[0] = commitment.TildeC; cExponents[1] = this.r[commitmentResponseIndices[i]]; cExponents[2] = commitment.TildeR; hash.Hash(Gq.MultiExponentiate(cBases, cExponents)); if (!commitment.TildeA.SequenceEqual(hash.Digest)) { throw new InvalidUProveArtifactException("Invalid commitment " + committed[i]); } } } } catch (ArgumentException) { throw new InvalidUProveArtifactException("Invalid presentation proof"); } catch (IndexOutOfRangeException) { throw new InvalidUProveArtifactException("Invalid presentation proof"); } }
/// <summary> /// Generates a presentation proof including optionally presenting a pseudonym, creating attribute commitments, and passing pre-generated random values. /// </summary> /// <param name="ip">The issuer parameters corresponding to <code>upkt</code>.</param> /// <param name="disclosed">An ordered array of disclosed attribute indices.</param> /// <param name="committed">An ordered array of committed attribute indices.</param> /// <param name="pseudonymAttribIndex">Index of the attribute used to create a scope-exclusive pseudonym, or 0 if no pseudonym is to be presented. The index must not be part of the disclosed attributes.</param> /// <param name="gs">The pseudonym scope element, or null if no pseudonym is to be presented.</param> /// <param name="message">The presentation message.</param> /// <param name="messageD">The message for the Device, or null.</param> /// <param name="deviceContext">The active device context, if token is device-protected, or null.</param> /// <param name="upkt">The U-Proke key and token.</param> /// <param name="attributes">The token attributes.</param> /// <param name="preGenW">Optional pregenerated random data for the proof generation.</param> /// <param name="cpv">Returned commitment private values if commitments are computed.</param> /// <returns>A presentation proof.</returns> internal static PresentationProof Generate(IssuerParameters ip, int[] disclosed, int[] committed, int pseudonymAttribIndex, GroupElement gs, byte[] message, byte[] messageD, IDevicePresentationContext deviceContext, UProveKeyAndToken upkt, byte[][] attributes, ProofGenerationRandomData preGenW, out CommitmentPrivateValues cpv) { if (upkt.Token.IsDeviceProtected && deviceContext == null) { throw new ArgumentNullException("Device context is not initialized"); } bool generateCommitments = (committed != null && committed.Length > 0); FieldZqElement[] tildeO = null; // make sure disclosed and committed lists are sorted if (disclosed == null) { // can't be null later, so make it an empty array disclosed = new int[] { }; } Array.Sort(disclosed); if (generateCommitments) { Array.Sort(committed); } int n = 0; if (ip.E != null) { n = ip.E.Length; if (n != attributes.Length) { throw new ArgumentException("number of attributes is inconsistent with issuer parameters"); } } bool presentPseudonym = false; if (gs != null) { if (pseudonymAttribIndex < 1 || (pseudonymAttribIndex > n && pseudonymAttribIndex != DeviceAttributeIndex)) { throw new ArgumentException("pseudonymAttribIndex must be between 1 and " + n + " (inclusive)"); } if (disclosed.Contains(pseudonymAttribIndex)) { throw new ArgumentException("pseudonymAttribIndex cannot be in the disclosed attribute array"); } presentPseudonym = true; } else if (pseudonymAttribIndex > 0) { throw new ArgumentNullException("gs is null"); } else { pseudonymAttribIndex = 0; } Group Gq = ip.Gq; FieldZq Zq = ip.Zq; FieldZqElement xt = ProtocolHelper.ComputeXt(ip, upkt.Token.TI, upkt.Token.IsDeviceProtected); ProofGenerationRandomData random; if (preGenW == null) { random = ProofGenerationRandomData.Generate(n - disclosed.Length, generateCommitments ? committed.Length : 0, Zq, upkt.Token.IsDeviceProtected); } else { random = preGenW; } // set up the multi-exponentiation arrays, with h^w0 as the first term int multiExpArraySize = 1 + (n - disclosed.Length) + (upkt.Token.IsDeviceProtected ? 1 : 0); GroupElement[] bases = new GroupElement[multiExpArraySize]; FieldZqElement[] exponents = new FieldZqElement[multiExpArraySize]; int multiExpIndex = 0; bases[multiExpIndex] = upkt.Token.H; exponents[multiExpIndex++] = random.W0; FieldZqElement[] x = new FieldZqElement[n]; int uIndex = 0; int dIndex = 0; int cIndex = 0; PresentationProof proof = new PresentationProof(); proof.DisclosedAttributes = new byte[disclosed.Length][]; int pseudonymRandomizerIndex = 0; if (generateCommitments) { proof.Commitments = new CommitmentValues[committed.Length]; tildeO = new FieldZqElement[committed.Length]; } HashFunction hash = ip.HashFunction; GroupElement[] cBases = new GroupElement[2] { Gq.G, ip.G[1] }; for (int i = 0; i < n; i++) { x[i] = ProtocolHelper.ComputeXi(ip, i, attributes[i]); if (!disclosed.Contains(i + 1)) { bases[multiExpIndex] = ip.G[i + 1]; exponents[multiExpIndex++] = random.W[uIndex]; if (presentPseudonym) { if (pseudonymAttribIndex == (i + 1)) { pseudonymRandomizerIndex = uIndex; } } if (generateCommitments && committed.Contains(i + 1)) { GroupElement tildeC = ip.Gq.MultiExponentiate(cBases, new FieldZqElement[2] { x[i], random.TildeO[cIndex] }); tildeO[cIndex] = random.TildeO[cIndex]; GroupElement temp2 = ip.Gq.MultiExponentiate(cBases, new FieldZqElement[2] { random.W[uIndex], random.TildeW[cIndex] }); hash.Hash(temp2); byte[] tildeA = hash.Digest; proof.Commitments[cIndex] = new CommitmentValues(tildeC, tildeA, null); cIndex++; } uIndex++; } else if (generateCommitments && committed.Contains(i + 1)) { throw new ArgumentException("attribute " + (i + 1) + " cannot be both disclosed and committed"); } else { proof.DisclosedAttributes[dIndex] = attributes[i]; dIndex++; } } GroupElement aPreImage; if (upkt.Token.IsDeviceProtected) { GroupElement ad; // pseudonym computed by device if (presentPseudonym && pseudonymAttribIndex == DeviceAttributeIndex) { GroupElement apPrime; GroupElement Ps; ad = deviceContext.GetInitialWitnessesAndPseudonym(gs, out apPrime, out Ps); hash.Hash(apPrime * gs.Exponentiate(random.Wd)); proof.Ap = hash.Digest; proof.Ps = Ps; } else { ad = deviceContext.GetInitialWitness(); } bases[multiExpIndex] = ip.Gd; exponents[multiExpIndex++] = random.Wd; aPreImage = Gq.MultiExponentiate(bases, exponents) * ad; } else { aPreImage = Gq.MultiExponentiate(bases, exponents); } hash.Hash(aPreImage); proof.a = hash.Digest; // pseudonym derived from one token attribute if (presentPseudonym && pseudonymAttribIndex != DeviceAttributeIndex) { hash.Hash(gs.Exponentiate(random.W[pseudonymRandomizerIndex])); proof.Ap = hash.Digest; proof.Ps = gs.Exponentiate(x[pseudonymAttribIndex - 1]); } byte[] mdPrime; FieldZqElement c = ProtocolHelper.GenerateChallenge(ip, upkt.Token, proof.a, pseudonymAttribIndex, proof.ap, proof.Ps, message, messageD, disclosed, GetDisclosedX(disclosed, x), committed, proof.Commitments, out mdPrime); proof.r = new FieldZqElement[1 + n - disclosed.Length + (upkt.Token.IsDeviceProtected ? 1 : 0)]; // r_0, {r_i} for undisclosed i, r_d proof.r[0] = c * upkt.PrivateKey + random.W0; uIndex = 1; for (int i = 1; i <= n; i++) { if (!disclosed.Contains(i)) { proof.r[uIndex] = c.Negate() * x[i - 1] + random.W[uIndex - 1]; uIndex++; } } if (upkt.Token.IsDeviceProtected) { proof.r[proof.r.Length - 1] = deviceContext.GetDeviceResponse(messageD, mdPrime, ip.HashFunctionOID) + random.Wd; } if (generateCommitments) { for (int i = 0; i < committed.Length; i++) { proof.Commitments[i].TildeR = c.Negate() * random.TildeO[i] + random.TildeW[i]; } } random.Clear(); cpv = new CommitmentPrivateValues(tildeO); return(proof); }
/// <summary> /// Generates the U-Prove key and tokens. /// </summary> /// <param name="message">The third issuance message.</param> /// <param name="skipTokenValidation">Set to <code>true</code> to skip token validation; /// <code>false</code> otherwise. Token validation SHOULD be performed before use, either /// by setting <code>skipTokenValidation</code> to <code>false</code>, or by later using the /// <code>ProtocolHelper.VerifyTokenSignature</code> method. /// </param> /// <exception cref="InvalidUProveArtifactException">If the token signature is invalid.</exception> /// <returns>An array of U-Prove keys and tokens</returns> public UProveKeyAndToken[] GenerateTokens(ThirdIssuanceMessage message, bool skipTokenValidation = false) { if (state != State.Second) { throw new InvalidOperationException("GenerateSecondMessage must be called first"); } bool doBatchValidation = !skipTokenValidation && batchValidationSecurityLevel > 0; bool doNormalValidation = !skipTokenValidation && batchValidationSecurityLevel <= 0; Group Gq = ip.Gq; FieldZq Zq = ip.Zq; FieldZqElement[] sigmaRPrime = new FieldZqElement[numberOfTokens]; FieldZqElement phoAR = Zq.Zero, phoR = Zq.Zero, phoAC = Zq.Zero, phoC = Zq.Zero; GroupElement batchAcc = Gq.Identity; for (int i = 0; i < numberOfTokens; i++) { sigmaRPrime[i] = message.sigmaR[i] + beta2[i]; if (doBatchValidation) { FieldZqElement s = Zq.GetRandomElement(true, batchValidationSecurityLevel); phoAR += (s * alpha[i] * sigmaRPrime[i]); phoR += (s * sigmaRPrime[i]); phoAC += (s * alpha[i] * sigmaCPrime[i]); phoC += (s * sigmaCPrime[i]); batchAcc *= (sigmaAPrime[i] * sigmaBPrime[i]).Exponentiate(s); } else if (doNormalValidation) { if (!(sigmaAPrime[i] * sigmaBPrime[i]).Equals( ((Gq.G * h[i]).Exponentiate(sigmaRPrime[i]) * (ip.G[0] * sigmaZPrime[i]).Exponentiate(sigmaCPrime[i].Negate())))) { throw new InvalidUProveArtifactException("Invalid token signature: " + i); } } ukat[i].Token = new UProveToken(ip.UidP, h[i], TI, PI, sigmaZPrime[i], sigmaCPrime[i], sigmaRPrime[i], isDeviceProtected); } if (doBatchValidation && (batchAcc != Gq.G.Exponentiate(phoR) * gamma.Exponentiate(phoAR) * ip.G[0].Exponentiate(phoC.Negate()) * sigmaZ.Exponentiate(phoAC.Negate()))) { throw new InvalidUProveArtifactException("Invalid token signature"); } state = State.Tokens; return(ukat); }
// Verifies the pre-issuance proof, and returns the element gamma needed for the token issuance. /// <summary> /// Verifies a pre-issuance proof and returns the element gamma needed for collaborative issuance. /// </summary> /// <param name="ipip">The pre-issuance proof parameters for the Issuer</param> /// <param name="proof">The proof to be verified</param> /// <param name="message">An optional message to be verified (must match the one signed by the prover)</param> /// <returns>The group element <c>gamma^beta0</c>, a blinded version of the element gamma used during token issuance.</returns> /// <exception cref="InvalidUProveArtifactException">Thrown if the proof is invalid.</exception> public static GroupElement VerifyProof(IssuerPreIssuanceParameters ipip, PreIssuanceProof proof, byte[] message) { // Validate paramters first ipip.Validate(); // extension by Fablei -> need to know the number of attributes involved in this proof -> ip.G.length int ipGLength = proof.na + 2; IssuerParameters ip = ipip.IP; FieldZq Zq = ip.Zq; Group Gq = ip.Gq; List <GroupElement> bases = new List <GroupElement>(); List <FieldZqElement> exponents = new List <FieldZqElement>(); GroupElement[] C = null; GroupElement[] CPrime = null; FieldZqElement c = Zq.GetElementFromDigest(proof.c); if (ipip.HasCarryOverAttributes) { // validate presentation proof int[] disclosed = new int[] {}; VerifierPresentationProtocolParameters vppp = new VerifierPresentationProtocolParameters(ipip.SourceIP, disclosed, message, ipip.Tokens); vppp.Committed = ipip.Corig; try { proof.presentation.Verify(vppp); } catch (InvalidUProveArtifactException e) { throw new InvalidUProveArtifactException("Failed to verify pre-Issuance proof, presentation proof " + 0 + " failed to verify (" + e.ToString() + ")"); } // extract the commitments C = new GroupElement[ipip.C.Length]; CPrime = new GroupElement[ipip.C.Length]; for (int i = 0; i < C.Length; i++) { C[i] = proof.presentation.Commitments[i].TildeC; // Compute the CPrime[i] values. bases = new List <GroupElement>(); exponents = new List <FieldZqElement>(); bases.Add(C[i]); exponents.Add(c); bases.Add(Gq.G); exponents.Add(proof.GetResponse("sx" + ipip.C[i])); bases.Add(ip.G[1]); exponents.Add(proof.GetResponse("sR" + i)); CPrime[i] = Gq.MultiExponentiate(bases.ToArray(), exponents.ToArray()); //Debug.WriteLine("CPrime[i] = " + BitConverter.ToString(CPrime[i].GetEncoded())); } } // Compute D' FieldZqElement sd = proof.GetResponse("sD"); GroupElement DPrime = Gq.MultiExponentiate(proof.Ch0, proof.h0, c, c.Negate()); // TODO: add Inverse() to group element to simplify this DPrime = DPrime.Multiply(Gq.G.Exponentiate(sd)); //Debug.WriteLine("DPrime = " + BitConverter.ToString(DPrime.GetEncoded())); // Compute T' FieldZqElement sBeta0 = proof.GetResponse("sBeta0"); GroupElement TPrime = Gq.MultiExponentiate(proof.Ch0, proof.CGamma, c, sBeta0); //Debug.WriteLine("TPrime = " + BitConverter.ToString(TPrime.GetEncoded())); // Compute gammaK (product of known attributes) bases = new List <GroupElement>(); exponents = new List <FieldZqElement>(); //int t = ip.G.Length-1; // extension by Fablei int t = ipGLength - 1; FieldZqElement xt = ProtocolHelper.ComputeXt(ip, ipip.TI, ipip.DeviceProtected); bases.Add(ip.G[0]); exponents.Add(ip.Zq.One); bases.Add(ip.G[t]); exponents.Add(xt); // gammaK = g0*(gt^xt) for (int i = 1; i < ipGLength - 1; i++) { if (ipip.K.Contains(i)) { FieldZqElement xi = ProtocolHelper.ComputeXi(ip, i - 1, ipip.Attributes[i - 1]); bases.Add(ip.G[i]); exponents.Add(xi); } } GroupElement gammaK = Gq.MultiExponentiate(bases.ToArray(), exponents.ToArray()); // Compute Cgamma' bases = new List <GroupElement>(); exponents = new List <FieldZqElement>(); bases.Add(proof.CGamma); exponents.Add(c); bases.Add(gammaK); exponents.Add(c.Negate()); // TODO: do with one exp; i.e., (CGamma/gammaK)^c for (int i = 1; i < ipGLength - 1; i++) { if (!ipip.K.Contains(i)) { FieldZqElement sxi = proof.GetResponse("sx" + i); bases.Add(ip.G[i]); exponents.Add(sxi); } } bases.Add(Gq.G); exponents.Add(proof.GetResponse("sRho")); GroupElement CgammaPrime = Gq.MultiExponentiate(bases.ToArray(), exponents.ToArray()); // TODO: if deviceprotected multiply device base/response. //Debug.WriteLine("CgammaPrime = " + BitConverter.ToString(CgammaPrime.GetEncoded())); // Recompute challenge byte[] cPrime = ComputeChallenge(ip, proof.h0, proof.CGamma, proof.Ch0, C, DPrime, CgammaPrime, TPrime, CPrime, message); //Debug.WriteLine("c' = " + BitConverter.ToString(cPrime)); if (!cPrime.SequenceEqual <byte>(proof.c)) { throw new InvalidUProveArtifactException("invalid proof"); } return(proof.h0); }
public void Verify(IssuerParameters ip, int[] disclosed, int[] committed, int pseudonymAttribIndex, GroupElement gs, byte[] message, byte[] messageD, UProveToken upt) { try { // make sure disclosed list is sorted if (disclosed == null) { // can't be null later, so make it an empty array disclosed = new int[] { }; } Array.Sort(disclosed); Group Gq = ip.Gq; int n = ip.E.Length; bool presentPseudonym = false; if (gs != null && pseudonymAttribIndex != 0) { if (pseudonymAttribIndex < 1 || (pseudonymAttribIndex > n && pseudonymAttribIndex != DeviceAttributeIndex)) { throw new ArgumentException("pseudonymAttribIndex must be between 1 and " + n + " (inclusive)"); } if (disclosed.Contains(pseudonymAttribIndex)) { throw new ArgumentException("pseudonymAttribIndex cannot be in the disclosed attribute array"); } presentPseudonym = true; } else // no nym { pseudonymAttribIndex = 0; } bool verifyCommitments = (committed != null && committed.Length > 0); if (verifyCommitments) { Array.Sort(committed); } ProtocolHelper.VerifyTokenSignature(ip, upt); FieldZqElement[] disclosedX = new FieldZqElement[disclosedAttributes.Length]; GroupElement dAccumulator = ip.G[0] * ip.G[n + 1].Exponentiate(ProtocolHelper.ComputeXt(ip, upt.TI, upt.IsDeviceProtected)); // g0 * gt^xt GroupElement uAccumulator = upt.H.Exponentiate(this.r[0]); int dIndex = 0; int uIndex = 1; int cIndex = 0; int pseudonymResponseIndex = 0; int[] commitmentResponseIndices = verifyCommitments ? new int[committed.Length] : null; for (int i = 1; i <= n; i++) { if (disclosed.Contains(i)) { disclosedX[dIndex] = ProtocolHelper.ComputeXi(ip, i - 1, disclosedAttributes[dIndex]); dAccumulator = dAccumulator * ip.G[i].Exponentiate(disclosedX[dIndex]); dIndex++; } else { uAccumulator = uAccumulator * ip.G[i].Exponentiate(this.r[uIndex]); if (presentPseudonym) { if (pseudonymAttribIndex == i) { pseudonymResponseIndex = uIndex; } } if (verifyCommitments) { if (committed.Contains(i)) { // remember which response correspond to which commitment commitmentResponseIndices[cIndex] = uIndex; cIndex++; } } uIndex++; } } if (pseudonymAttribIndex == DeviceAttributeIndex) { pseudonymResponseIndex = this.r.Length - 1; // r_d is the last response in the array } byte[] unused; // verifier doesn't use the returned message for device FieldZqElement c = ProtocolHelper.GenerateChallenge(ip, upt, this.a, pseudonymAttribIndex, this.ap, this.ps, message, messageD, disclosed, disclosedX, committed, this.Commitments, out unused); HashFunction hash = ip.HashFunction; hash.Hash((dAccumulator.Exponentiate(c.Negate()) * uAccumulator * (upt.IsDeviceProtected ? ip.Gd.Exponentiate(this.r[this.r.Length - 1]) : Gq.Identity))); if (!this.a.SequenceEqual(hash.Digest)) { throw new InvalidUProveArtifactException("Invalid presentation proof"); } if (presentPseudonym) { hash.Hash(this.ps.Exponentiate(c).Multiply(gs.Exponentiate(this.r[pseudonymResponseIndex]))); if (!this.ap.SequenceEqual(hash.Digest)) { throw new InvalidUProveArtifactException("Invalid pseudonym"); } } if (verifyCommitments) { for (int i = 0; i < commitmentResponseIndices.Length; i++) { CommitmentValues commitment = this.Commitments[i]; hash.Hash(commitment.TildeC.Exponentiate(c).Multiply(ip.Gq.G.Exponentiate(this.r[commitmentResponseIndices[i]])).Multiply(ip.G[1].Exponentiate(commitment.TildeR))); if (!commitment.TildeA.SequenceEqual(hash.Digest)) { throw new InvalidUProveArtifactException("Invalid commitment " + committed[i]); } } } } catch (ArgumentException) { throw new InvalidUProveArtifactException("Invalid presentation proof"); } catch (IndexOutOfRangeException) { throw new InvalidUProveArtifactException("Invalid presentation proof"); } }
/// <summary> /// Computes the revocation witness for a specific user attribute. /// </summary> /// <param name="revoked">Set of revoked values.</param> /// <param name="xid">The user attribute.</param> /// <returns>A revocation witness.</returns> public RevocationWitness ComputeRevocationWitness(HashSet <FieldZqElement> revoked, FieldZqElement xid) { if (revoked.Contains(xid)) { throw new ArgumentException("xid cannot be in revoked set"); } Group Gq = RAParameters.group; FieldZq Zq = Gq.FieldZq; FieldZqElement d = Zq.One; FieldZqElement deltaPlusXProd = Zq.One; foreach (var x in revoked) { if (x + this.PrivateKey == Zq.Zero) { throw new ArgumentException("revocationList cannot contain the negation of the private key"); } d *= (x - xid); deltaPlusXProd *= (this.PrivateKey + x); } GroupElement W = RAParameters.gt.Exponentiate((deltaPlusXProd - d) * (PrivateKey + xid).Invert()); GroupElement Q = Gq.MultiExponentiate(new GroupElement[] { Accumulator, W, RAParameters.gt }, new FieldZqElement[] { Zq.One, xid.Negate(), d.Negate() }); return(new RevocationWitness(d, W, Q)); }