/** * Cryptographically verify the IdemixCredRequest * * @param ipk the issuer public key * @return true iff valid */ public bool Check(IdemixIssuerPublicKey ipk) { if (Nym == null || issuerNonce == null || proofC == null || proofS == null || ipk == null) { return(false); } ECP t = ipk.Hsk.Mul(proofS); t.Sub(Nym.Mul(proofC)); byte[] proofData = new byte[0]; proofData = proofData.Append(CREDREQUEST_LABEL.ToBytes()); proofData = proofData.Append(t.ToBytes()); proofData = proofData.Append(ipk.Hsk.ToBytes()); proofData = proofData.Append(Nym.ToBytes()); proofData = proofData.Append(issuerNonce.ToBytes()); proofData = proofData.Append(ipk.Hash); // Hash proofData to hproofdata byte[] hproofdata = proofData.HashModOrder().ToBytes(); return(Enumerable.SequenceEqual(proofC.ToBytes(), hproofdata)); }
/** * Verify this IdemixPseudonymSignature * * @param nym the pseudonym with respect to which the signature is verified * @param ipk the issuer public key * @param msg the message that should be signed in this signature * @return true iff valid */ public bool Verify(ECP nym, IdemixIssuerPublicKey ipk, byte[] msg) { if (nym == null || ipk == null || msg == null) { return(false); } ECP t = ipk.Hsk.Mul2(proofSSk, ipk.HRand, proofSRNym); t.Sub(nym.Mul(proofC)); // create array for proof data that will contain the sign label, 2 ECPs (each of length 2* FIELD_BYTES + 1), the ipk hash and the message byte[] proofData = new byte[0]; proofData = proofData.Append(NYM_SIGN_LABEL.ToBytes()); proofData = proofData.Append(t.ToBytes()); proofData = proofData.Append(nym.ToBytes()); proofData = proofData.Append(ipk.Hash); proofData = proofData.Append(msg); BIG cvalue = proofData.HashModOrder(); byte[] finalProofData = new byte[0]; finalProofData = finalProofData.Append(cvalue.ToBytes()); finalProofData = finalProofData.Append(nonce.ToBytes()); byte[] hashedProofData = finalProofData.HashModOrder().ToBytes(); return(Enumerable.SequenceEqual(proofC.ToBytes(), hashedProofData)); }
/* Return e.this+f.Q */ public ECP Mul2(BIG e, ECP Q, BIG f) { BIG te = new BIG(); BIG tf = new BIG(); BIG mt = new BIG(); ECP S = new ECP(); ECP T = new ECP(); ECP C = new ECP(); ECP[] W = new ECP[8]; sbyte[] w = new sbyte[1 + (BIG.NLEN * BIG.BASEBITS + 1) / 2]; int i, s, ns, nb; sbyte a, b; //affine(); //Q.affine(); te.Copy(e); tf.Copy(f); // precompute table W[1] = new ECP(); W[1].Copy(this); W[1].Sub(Q); W[2] = new ECP(); W[2].Copy(this); W[2].Add(Q); S.Copy(Q); S.Dbl(); W[0] = new ECP(); W[0].Copy(W[1]); W[0].Sub(S); W[3] = new ECP(); W[3].Copy(W[2]); W[3].Add(S); T.Copy(this); T.Dbl(); W[5] = new ECP(); W[5].Copy(W[1]); W[5].Add(T); W[6] = new ECP(); W[6].Copy(W[2]); W[6].Add(T); W[4] = new ECP(); W[4].Copy(W[5]); W[4].Sub(S); W[7] = new ECP(); W[7].Copy(W[6]); W[7].Add(S); // if multiplier is odd, add 2, else add 1 to multiplier, and add 2P or P to correction s = te.Parity(); te.Inc(1); te.Norm(); ns = te.Parity(); mt.Copy(te); mt.Inc(1); mt.Norm(); te.CMove(mt, s); T.CMove(this, ns); C.Copy(T); s = tf.Parity(); tf.Inc(1); tf.Norm(); ns = tf.Parity(); mt.Copy(tf); mt.Inc(1); mt.Norm(); tf.CMove(mt, s); S.CMove(Q, ns); C.Add(S); mt.Copy(te); mt.Add(tf); mt.Norm(); nb = 1 + (mt.NBits() + 1) / 2; // convert exponent to signed 2-bit window for (i = 0; i < nb; i++) { a = (sbyte)(te.LastBits(3) - 4); te.Dec(a); te.Norm(); te.FShr(2); b = (sbyte)(tf.LastBits(3) - 4); tf.Dec(b); tf.Norm(); tf.FShr(2); w[i] = (sbyte)(4 * a + b); } w[nb] = (sbyte)(4 * te.LastBits(3) + tf.LastBits(3)); S.Copy(W[(w[nb] - 1) / 2]); for (i = nb - 1; i >= 0; i--) { T.Select(W, w[i]); S.Dbl(); S.Dbl(); S.Add(T); } S.Sub(C); // apply correction S.Affine(); return(S); }
/* return e.this */ public ECP Mul(BIG e) { if (e.IsZilch() || IsInfinity()) { return(new ECP()); } ECP P = new ECP(); if (CURVETYPE == MONTGOMERY) { /* use Ladder */ int nb, i, b; ECP D = new ECP(); ECP R0 = new ECP(); R0.Copy(this); ECP R1 = new ECP(); R1.Copy(this); R1.Dbl(); D.Copy(this); D.Affine(); nb = e.NBits(); for (i = nb - 2; i >= 0; i--) { b = e.Bit(i); P.Copy(R1); P.DAdd(R0, D); R0.CSwap(R1, b); R1.Copy(P); R0.Dbl(); R0.CSwap(R1, b); } P.Copy(R0); } else { // fixed size windows int i, nb, s, ns; BIG mt = new BIG(); BIG t = new BIG(); ECP Q = new ECP(); ECP C = new ECP(); ECP[] W = new ECP[8]; sbyte[] w = new sbyte[1 + (BIG.NLEN * BIG.BASEBITS + 3) / 4]; //affine(); // precompute table Q.Copy(this); Q.Dbl(); W[0] = new ECP(); W[0].Copy(this); for (i = 1; i < 8; i++) { W[i] = new ECP(); W[i].Copy(W[i - 1]); W[i].Add(Q); } // make exponent odd - add 2P if even, P if odd t.Copy(e); s = t.Parity(); t.Inc(1); t.Norm(); ns = t.Parity(); mt.Copy(t); mt.Inc(1); mt.Norm(); t.CMove(mt, s); Q.CMove(this, ns); C.Copy(Q); nb = 1 + (t.NBits() + 3) / 4; // convert exponent to signed 4-bit window for (i = 0; i < nb; i++) { w[i] = (sbyte)(t.LastBits(5) - 16); t.Dec(w[i]); t.Norm(); t.FShr(4); } w[nb] = (sbyte)t.LastBits(5); P.Copy(W[(w[nb] - 1) / 2]); for (i = nb - 1; i >= 0; i--) { Q.Select(W, w[i]); P.Dbl(); P.Dbl(); P.Dbl(); P.Dbl(); P.Add(Q); } P.Sub(C); // apply correction } P.Affine(); return(P); }
/** * Create a new IdemixSignature by proving knowledge of a credential * * @param c the credential used to create an idemix signature * @param sk the signer's secret key * @param pseudonym a pseudonym of the signer * @param ipk the issuer public key * @param disclosure a bool-array that steers the disclosure of attributes * @param msg the message to be signed * @param rhIndex the index of the attribute that represents the revocation handle * @param cri the credential revocation information that allows the signer to prove non-revocation */ public IdemixSignature(IdemixCredential c, BIG sk, IdemixPseudonym pseudonym, IdemixIssuerPublicKey ipk, bool[] disclosure, byte[] msg, int rhIndex, CredentialRevocationInformation cri) { if (c == null || sk == null || pseudonym == null || pseudonym.Nym == null || pseudonym.RandNym == null || ipk == null || disclosure == null || msg == null || cri == null) { throw new ArgumentException("Cannot construct idemix signature from null input"); } if (disclosure.Length != c.Attrs.Length) { throw new ArgumentException("Disclosure length must be the same as the number of attributes"); } if (cri.RevocationAlg >= Enum.GetValues(typeof(RevocationAlgorithm)).Length) { throw new ArgumentException("CRI specifies unknown revocation algorithm"); } if (cri.RevocationAlg != (int)RevocationAlgorithm.ALG_NO_REVOCATION && disclosure[rhIndex]) { throw new ArgumentException("Attribute " + rhIndex + " is disclosed but also used a revocation handle attribute, which should remain hidden"); } RevocationAlgorithm revocationAlgorithm = (RevocationAlgorithm)cri.RevocationAlg; int[] hiddenIndices = HiddenIndices(disclosure); RAND rng = IdemixUtils.GetRand(); // Start signature BIG r1 = rng.RandModOrder(); BIG r2 = rng.RandModOrder(); BIG r3 = new BIG(r1); r3.InvModp(IdemixUtils.GROUP_ORDER); nonce = rng.RandModOrder(); aPrime = PAIR.G1Mul(c.A, r1); aBar = PAIR.G1Mul(c.B, r1); aBar.Sub(PAIR.G1Mul(aPrime, c.E)); bPrime = PAIR.G1Mul(c.B, r1); bPrime.Sub(PAIR.G1Mul(ipk.HRand, r2)); BIG sPrime = new BIG(c.S); sPrime.Add(BIG.ModNeg(BIG.ModMul(r2, r3, IdemixUtils.GROUP_ORDER), IdemixUtils.GROUP_ORDER)); sPrime.Mod(IdemixUtils.GROUP_ORDER); //Construct Zero Knowledge Proof BIG rsk = rng.RandModOrder(); BIG re = rng.RandModOrder(); BIG rR2 = rng.RandModOrder(); BIG rR3 = rng.RandModOrder(); BIG rSPrime = rng.RandModOrder(); BIG rRNym = rng.RandModOrder(); BIG[] rAttrs = new BIG[hiddenIndices.Length]; for (int i = 0; i < hiddenIndices.Length; i++) { rAttrs[i] = rng.RandModOrder(); } // Compute non-revoked proof INonRevocationProver prover = NonRevocationProver.GetNonRevocationProver(revocationAlgorithm); int hiddenRHIndex = Array.IndexOf(hiddenIndices, rhIndex); if (hiddenRHIndex < 0) { // rhIndex is not present, set to last index position hiddenRHIndex = hiddenIndices.Length; } byte[] nonRevokedProofHashData = prover.GetFSContribution(BIG.FromBytes(c.Attrs[rhIndex]), rAttrs[hiddenRHIndex], cri); if (nonRevokedProofHashData == null) { throw new Exception("Failed to compute non-revoked proof"); } ECP t1 = aPrime.Mul2(re, ipk.HRand, rR2); ECP t2 = PAIR.G1Mul(ipk.HRand, rSPrime); t2.Add(bPrime.Mul2(rR3, ipk.Hsk, rsk)); for (int i = 0; i < hiddenIndices.Length / 2; i++) { t2.Add(ipk.HAttrs[hiddenIndices[2 * i]].Mul2(rAttrs[2 * i], ipk.HAttrs[hiddenIndices[2 * i + 1]], rAttrs[2 * i + 1])); } if (hiddenIndices.Length % 2 != 0) { t2.Add(PAIR.G1Mul(ipk.HAttrs[hiddenIndices[hiddenIndices.Length - 1]], rAttrs[hiddenIndices.Length - 1])); } ECP t3 = ipk.Hsk.Mul2(rsk, ipk.HRand, rRNym); // create proofData such that it can contain the sign label, 7 elements in G1 (each of size 2*FIELD_BYTES+1), // the ipk hash, the disclosure array, and the message byte[] proofData = new byte[0]; proofData = proofData.Append(SIGN_LABEL.ToBytes()); proofData = proofData.Append(t1.ToBytes()); proofData = proofData.Append(t2.ToBytes()); proofData = proofData.Append(t3.ToBytes()); proofData = proofData.Append(aPrime.ToBytes()); proofData = proofData.Append(aBar.ToBytes()); proofData = proofData.Append(bPrime.ToBytes()); proofData = proofData.Append(pseudonym.Nym.ToBytes()); proofData = proofData.Append(ipk.Hash); proofData = proofData.Append(disclosure); proofData = proofData.Append(msg); BIG cvalue = proofData.HashModOrder(); byte[] finalProofData = new byte[0]; finalProofData = finalProofData.Append(cvalue.ToBytes()); finalProofData = finalProofData.Append(nonce.ToBytes()); proofC = finalProofData.HashModOrder(); proofSSk = rsk.ModAdd(BIG.ModMul(proofC, sk, IdemixUtils.GROUP_ORDER), IdemixUtils.GROUP_ORDER); proofSE = re.ModSub(BIG.ModMul(proofC, c.E, IdemixUtils.GROUP_ORDER), IdemixUtils.GROUP_ORDER); proofSR2 = rR2.ModAdd(BIG.ModMul(proofC, r2, IdemixUtils.GROUP_ORDER), IdemixUtils.GROUP_ORDER); proofSR3 = rR3.ModSub(BIG.ModMul(proofC, r3, IdemixUtils.GROUP_ORDER), IdemixUtils.GROUP_ORDER); proofSSPrime = rSPrime.ModAdd(BIG.ModMul(proofC, sPrime, IdemixUtils.GROUP_ORDER), IdemixUtils.GROUP_ORDER); proofSRNym = rRNym.ModAdd(BIG.ModMul(proofC, pseudonym.RandNym, IdemixUtils.GROUP_ORDER), IdemixUtils.GROUP_ORDER); nym = new ECP(); nym.Copy(pseudonym.Nym); proofSAttrs = new BIG[hiddenIndices.Length]; for (int i = 0; i < hiddenIndices.Length; i++) { proofSAttrs[i] = new BIG(rAttrs[i]); proofSAttrs[i].Add(BIG.ModMul(proofC, BIG.FromBytes(c.Attrs[hiddenIndices[i]]), IdemixUtils.GROUP_ORDER)); proofSAttrs[i].Mod(IdemixUtils.GROUP_ORDER); } // include non-revocation proof in signature revocationPk = cri.EpochPk; revocationPKSig = cri.EpochPkSig.ToByteArray(); epoch = cri.Epoch; nonRevocationProof = prover.GetNonRevocationProof(proofC); }
/** * Verify this signature * * @param disclosure an array indicating which attributes it expects to be disclosed * @param ipk the issuer public key * @param msg the message that should be signed in this signature * @param attributeValues BIG array where attributeValues[i] contains the desired attribute value for the i-th attribute if its disclosed * @param rhIndex index of the attribute that represents the revocation-handle * @param revPk the long term public key used to authenticate CRIs * @param epoch monotonically increasing counter representing a time window * @return true iff valid */ // ReSharper disable once ParameterHidesMember public bool Verify(bool[] disclosure, IdemixIssuerPublicKey ipk, byte[] msg, BIG[] attributeValues, int rhIndex, KeyPair revPk, int epoch) { if (disclosure == null || ipk == null || msg == null || attributeValues == null || attributeValues.Length != ipk.AttributeNames.Length || disclosure.Length != ipk.AttributeNames.Length) { return(false); } for (int i = 0; i < ipk.AttributeNames.Length; i++) { if (disclosure[i] && attributeValues[i] == null) { return(false); } } int[] hiddenIndices = HiddenIndices(disclosure); if (proofSAttrs.Length != hiddenIndices.Length) { return(false); } if (aPrime.IsInfinity()) { return(false); } if (nonRevocationProof.RevocationAlg >= Enum.GetValues(typeof(RevocationAlgorithm)).Length) { throw new ArgumentException("CRI specifies unknown revocation algorithm"); } RevocationAlgorithm revocationAlgorithm = (RevocationAlgorithm)nonRevocationProof.RevocationAlg; if (disclosure[rhIndex]) { throw new ArgumentException("Attribute " + rhIndex + " is disclosed but also used a revocation handle attribute, which should remain hidden"); } // Verify EpochPK if (!RevocationAuthority.VerifyEpochPK(revPk, revocationPk, revocationPKSig, epoch, revocationAlgorithm)) { // Signature is based on an invalid revocation epoch public key return(false); } FP12 temp1 = PAIR.Ate(ipk.W, aPrime); FP12 temp2 = PAIR.Ate(IdemixUtils.GenG2, aBar); temp2.Inverse(); temp1.mul(temp2); if (!PAIR.FExp(temp1).IsUnity()) { return(false); } ECP t1 = aPrime.Mul2(proofSE, ipk.HRand, proofSR2); ECP temp = new ECP(); temp.Copy(aBar); temp.Sub(bPrime); t1.Sub(PAIR.G1Mul(temp, proofC)); ECP t2 = PAIR.G1Mul(ipk.HRand, proofSSPrime); t2.Add(bPrime.Mul2(proofSR3, ipk.Hsk, proofSSk)); for (int i = 0; i < hiddenIndices.Length / 2; i++) { t2.Add(ipk.HAttrs[hiddenIndices[2 * i]].Mul2(proofSAttrs[2 * i], ipk.HAttrs[hiddenIndices[2 * i + 1]], proofSAttrs[2 * i + 1])); } if (hiddenIndices.Length % 2 != 0) { t2.Add(PAIR.G1Mul(ipk.HAttrs[hiddenIndices[hiddenIndices.Length - 1]], proofSAttrs[hiddenIndices.Length - 1])); } temp = new ECP(); temp.Copy(IdemixUtils.GenG1); for (int i = 0; i < disclosure.Length; i++) { if (disclosure[i]) { temp.Add(PAIR.G1Mul(ipk.HAttrs[i], attributeValues[i])); } } t2.Add(PAIR.G1Mul(temp, proofC)); ECP t3 = ipk.Hsk.Mul2(proofSSk, ipk.HRand, proofSRNym); t3.Sub(nym.Mul(proofC)); // Check with non-revoked-verifier INonRevocationVerifier nonRevokedVerifier = NonRevocationVerifier.GetNonRevocationVerifier(revocationAlgorithm); int hiddenRHIndex = Array.IndexOf(hiddenIndices, rhIndex); if (hiddenRHIndex < 0) { // rhIndex is not present, set to last index position hiddenRHIndex = hiddenIndices.Length; } BIG proofSRh = proofSAttrs[hiddenRHIndex]; byte[] nonRevokedProofBytes = nonRevokedVerifier.RecomputeFSContribution(nonRevocationProof, proofC, revocationPk.ToECP2(), proofSRh); if (nonRevokedProofBytes == null) { return(false); } // create proofData such that it can contain the sign label, 7 elements in G1 (each of size 2*FIELD_BYTES+1), // the ipk hash, the disclosure array, and the message byte[] proofData = new byte[0]; proofData = proofData.Append(SIGN_LABEL.ToBytes()); proofData = proofData.Append(t1.ToBytes()); proofData = proofData.Append(t2.ToBytes()); proofData = proofData.Append(t3.ToBytes()); proofData = proofData.Append(aPrime.ToBytes()); proofData = proofData.Append(aBar.ToBytes()); proofData = proofData.Append(bPrime.ToBytes()); proofData = proofData.Append(nym.ToBytes()); proofData = proofData.Append(ipk.Hash); proofData = proofData.Append(disclosure); proofData = proofData.Append(msg); BIG cvalue = proofData.HashModOrder(); byte[] finalProofData = new byte[0]; finalProofData = finalProofData.Append(cvalue.ToBytes()); finalProofData = finalProofData.Append(nonce.ToBytes()); byte[] hashedProofData = finalProofData.HashModOrder().ToBytes(); return(Enumerable.SequenceEqual(proofC.ToBytes(), hashedProofData)); }