/**
         * check whether the issuer public key is correct
         *
         * @return true iff valid
         */
        public bool Check()
        {
            // check formalities of IdemixIssuerPublicKey
            if (AttributeNames == null || Hsk == null || HRand == null || HAttrs == null || BarG1 == null || BarG1.IsInfinity() || BarG2 == null || HAttrs.Length < AttributeNames.Length)
            {
                return(false);
            }

            for (int i = 0; i < AttributeNames.Length; i++)
            {
                if (HAttrs[i] == null)
                {
                    return(false);
                }
            }

            // check proofs
            ECP2 t1 = IdemixUtils.GenG2.Mul(ProofS);
            ECP  t2 = BarG1.Mul(ProofS);

            t1.Add(W.Mul(BIG.ModNeg(ProofC, IdemixUtils.GROUP_ORDER)));
            t2.Add(BarG2.Mul(BIG.ModNeg(ProofC, IdemixUtils.GROUP_ORDER)));

            // Generating proofData that will contain 3 elements in G1 (of size 2*FIELD_BYTES+1)and 3 elements in G2 (of size 4 * FIELD_BYTES)
            byte[] proofData = new byte[0];
            proofData = proofData.Append(t1.ToBytes());
            proofData = proofData.Append(t2.ToBytes());
            proofData = proofData.Append(IdemixUtils.GenG2.ToBytes());
            proofData = proofData.Append(BarG1.ToBytes());
            proofData = proofData.Append(W.ToBytes());
            proofData = proofData.Append(BarG2.ToBytes());

            // Hash proofData to hproofdata and compare with proofC
            return(Enumerable.SequenceEqual(proofData.HashModOrder().ToBytes(), ProofC.ToBytes()));
        }
        /**
         * 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));
        }
        /**
         * Constructor creating a new credential
         *
         * @param key   the issuer key pair
         * @param m     a credential request
         * @param attrs an array of attribute values as BIG
         */
        public IdemixCredential(IdemixIssuerKey key, IdemixCredRequest m, BIG[] attrs)
        {
            if (key == null || key.Ipk == null || m == null || attrs == null)
            {
                throw new ArgumentException("Cannot create idemix credential from null input");
            }
            if (attrs.Length != key.Ipk.AttributeNames.Length)
            {
                throw new ArgumentException("Amount of attribute values does not match amount of attributes in issuer public key");
            }
            RAND rng = IdemixUtils.GetRand();

            // Place a BBS+ signature on the user key and the attribute values
            // (For BBS+, see "Constant-Size Dynamic k-TAA" by Man Ho Au, Willy Susilo, Yi Mu)
            E = rng.RandModOrder();
            S = rng.RandModOrder();

            B = new ECP();
            B.Copy(IdemixUtils.GenG1);
            B.Add(m.Nym);
            B.Add(key.Ipk.HRand.Mul(S));

            for (int i = 0; i < attrs.Length / 2; i++)
            {
                B.Add(key.Ipk.HAttrs[2 * i].Mul2(attrs[2 * i], key.Ipk.HAttrs[2 * i + 1], attrs[2 * i + 1]));
            }

            if (attrs.Length % 2 != 0)
            {
                B.Add(key.Ipk.HAttrs[attrs.Length - 1].Mul(attrs[attrs.Length - 1]));
            }

            BIG exp = new BIG(key.Isk).Plus(E);

            exp.Mod(IdemixUtils.GROUP_ORDER);
            exp.InvModp(IdemixUtils.GROUP_ORDER);
            A = B.Mul(exp);

            Attrs = new byte[attrs.Length][];
            for (int i = 0; i < attrs.Length; i++)
            {
                byte[] b = new byte[IdemixUtils.FIELD_BYTES];
                attrs[i].ToBytes(b);
                Attrs[i] = b;
            }
        }
        /**
         * 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));
        }
        /**
         * Constructor
         *
         * @param attributeNames the names of attributes as String array (must not contain duplicates)
         * @param isk            the issuer secret key
         */
        public IdemixIssuerPublicKey(string[] attributeNames, BIG isk)
        {
            // check null input
            if (attributeNames == null || isk == null)
            {
                throw new ArgumentException("Cannot create IdemixIssuerPublicKey from null input");
            }

            // Checking if attribute names are unique
            HashSet <string> map = new HashSet <string>();

            foreach (string item in attributeNames)
            {
                if (map.Contains(item))
                {
                    throw new ArgumentException("Attribute " + item + " appears multiple times in attributeNames");
                }
                map.Add(item);
            }

            RAND rng = IdemixUtils.GetRand();

            // Attaching Attribute Names array correctly
            AttributeNames = attributeNames;

            // Computing W value
            W = IdemixUtils.GenG2.Mul(isk);

            // Filling up HAttributes correctly in Issuer Public Key, length
            // preserving
            HAttrs = new ECP[attributeNames.Length];

            for (int i = 0; i < attributeNames.Length; i++)
            {
                HAttrs[i] = IdemixUtils.GenG1.Mul(rng.RandModOrder());
            }

            // Generating Hsk value
            Hsk = IdemixUtils.GenG1.Mul(rng.RandModOrder());

            // Generating HRand value
            HRand = IdemixUtils.GenG1.Mul(rng.RandModOrder());

            // Generating BarG1 value
            BarG1 = IdemixUtils.GenG1.Mul(rng.RandModOrder());

            // Generating BarG2 value
            BarG2 = BarG1.Mul(isk);

            // Zero Knowledge Proofs

            // Computing t1 and t2 values with random local variable r for later use
            BIG  r  = rng.RandModOrder();
            ECP2 t1 = IdemixUtils.GenG2.Mul(r);
            ECP  t2 = BarG1.Mul(r);

            // Generating proofData that will contain 3 elements in G1 (of size 2*FIELD_BYTES+1)and 3 elements in G2 (of size 4 * FIELD_BYTES)
            byte[] proofData = new byte[0];
            proofData = proofData.Append(t1.ToBytes());
            proofData = proofData.Append(t2.ToBytes());
            proofData = proofData.Append(IdemixUtils.GenG2.ToBytes());
            proofData = proofData.Append(BarG1.ToBytes());
            proofData = proofData.Append(W.ToBytes());
            proofData = proofData.Append(BarG2.ToBytes());

            // Hashing proofData to proofC
            ProofC = proofData.HashModOrder();

            // Computing ProofS = (ProofC*isk) + r mod GROUP_ORDER
            ProofS = BIG.ModMul(ProofC, isk, IdemixUtils.GROUP_ORDER).Plus(r);
            ProofS.Mod(IdemixUtils.GROUP_ORDER);

            // Compute Hash of IdemixIssuerPublicKey
            byte[] serializedIpk = ToProto().ToByteArray();
            Hash = serializedIpk.HashModOrder().ToBytes();
        }