docs: public boolean verify(boolean[] disclosure, IdemixIssuerPublicKey ipk, byte[] msg, BIG[] attributeValues, int rhIndex, PublicKey revPk, int epoch) throws CryptoException {
          if (disclosure == null || ipk == null || msg == null || attributeValues == null || attributeValues.length != ipk.getAttributeNames().length || disclosure.length != ipk.getAttributeNames().length) {
              return false;
          }
          for (int i = 0; i < ipk.getAttributeNames().length; i++) {
              if (disclosure[i] && attributeValues[i] == null) {
                  return false;
              }
          }
  
          int[] hiddenIndices = hiddenIndices(disclosure);
          if (proofSAttrs.length != hiddenIndices.length) {
              return false;
          }
          if (aPrime.is_infinity()) {
              return false;
          }
          if (nonRevocationProof.getRevocationAlg() >= RevocationAlgorithm.values().length) {
              throw new IllegalArgumentException("CRI specifies unknown revocation algorithm");
          }
  
          RevocationAlgorithm revocationAlgorithm = RevocationAlgorithm.values()[nonRevocationProof.getRevocationAlg()];
  
          if (disclosure[rhIndex]) {
              throw new IllegalArgumentException("Attribute " + rhIndex + " is disclosed but also used a revocation handle attribute, which should remain hidden");
          }
  
          // Verify EpochPK
          if (!RevocationAuthority.verifyEpochPK(revPk, this.revocationPk, this.revocationPKSig, epoch, revocationAlgorithm)) {
              // Signature is based on an invalid revocation epoch public key
              return false;
          }
  
          FP12 temp1 = PAIR.ate(ipk.getW(), 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.getHRand(), proofSR2);
          ECP temp = new ECP();
          temp.copy(aBar);
          temp.sub(bPrime);
          t1.sub(PAIR.G1mul(temp, proofC));
  
          ECP t2 = PAIR.G1mul(ipk.getHRand(), proofSSPrime);
          t2.add(bPrime.mul2(proofSR3, ipk.getHsk(), proofSSk));
  
          for (int i = 0; i < hiddenIndices.length / 2; i++) {
              t2.add(ipk.getHAttrs()[hiddenIndices[2 * i]].mul2(proofSAttrs[2 * i], ipk.getHAttrs()[hiddenIndices[2 * i + 1]], proofSAttrs[2 * i + 1]));
          }
          if (hiddenIndices.length % 2 != 0) {
              t2.add(PAIR.G1mul(ipk.getHAttrs()[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.getHAttrs()[i], attributeValues[i]));
              }
          }
          t2.add(PAIR.G1mul(temp, proofC));
  
          ECP t3 = ipk.getHsk().mul2(proofSSk, ipk.getHRand(), proofSRNym);
          t3.sub(nym.mul(proofC));
  
          // Check with non-revoked-verifier
          NonRevocationVerifier nonRevokedVerifier = NonRevocationVerifier.getNonRevocationVerifier(revocationAlgorithm);
          int hiddenRHIndex = Ints.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(this.nonRevocationProof, proofC, IdemixUtils.transformFromProto(this.revocationPk), 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 = IdemixUtils.append(proofData, SIGN_LABEL.getBytes());
          proofData = IdemixUtils.append(proofData, IdemixUtils.ecpToBytes(t1));
          proofData = IdemixUtils.append(proofData, IdemixUtils.ecpToBytes(t2));
          proofData = IdemixUtils.append(proofData, IdemixUtils.ecpToBytes(t3));
          proofData = IdemixUtils.append(proofData, IdemixUtils.ecpToBytes(aPrime));
          proofData = IdemixUtils.append(proofData, IdemixUtils.ecpToBytes(aBar));
          proofData = IdemixUtils.append(proofData, IdemixUtils.ecpToBytes(bPrime));
          proofData = IdemixUtils.append(proofData, IdemixUtils.ecpToBytes(nym));
          proofData = IdemixUtils.append(proofData, ipk.getHash());
          proofData = IdemixUtils.append(proofData, disclosure);
          proofData = IdemixUtils.append(proofData, msg);
  
          BIG cvalue = IdemixUtils.hashModOrder(proofData);
  
          byte[] finalProofData = new byte[0];
          finalProofData = IdemixUtils.append(finalProofData, IdemixUtils.bigToBytes(cvalue));
          finalProofData = IdemixUtils.append(finalProofData, IdemixUtils.bigToBytes(nonce));
  
          byte[] hashedProofData = IdemixUtils.bigToBytes(IdemixUtils.hashModOrder(finalProofData));
          return Arrays.equals(IdemixUtils.bigToBytes(proofC), hashedProofData);
      }