1616
1717package org .hyperledger .fabric .sdk .idemix ;
1818
19+ import java .security .PublicKey ;
1920import java .util .ArrayList ;
2021import java .util .Arrays ;
2122import java .util .List ;
2223
24+ import com .google .common .primitives .Ints ;
2325import com .google .protobuf .ByteString ;
2426import org .apache .milagro .amcl .FP256BN .BIG ;
2527import org .apache .milagro .amcl .FP256BN .ECP ;
2628import org .apache .milagro .amcl .FP256BN .FP12 ;
2729import org .apache .milagro .amcl .FP256BN .PAIR ;
2830import org .apache .milagro .amcl .RAND ;
2931import org .hyperledger .fabric .protos .idemix .Idemix ;
32+ import org .hyperledger .fabric .sdk .exception .CryptoException ;
3033
3134/**
3235 * IdemixSignature represents an idemix signature, which is a zero knowledge proof
@@ -47,6 +50,10 @@ public class IdemixSignature {
4750 private final BIG nonce ;
4851 private final ECP nym ;
4952 private final BIG proofSRNym ;
53+ private Idemix .ECP2 revocationPk ;
54+ private byte [] revocationPKSig ;
55+ private long epoch ;
56+ private Idemix .NonRevocationProof nonRevocationProof ;
5057
5158 private static final String SIGN_LABEL = "sign" ;
5259
@@ -59,11 +66,28 @@ public class IdemixSignature {
5966 * @param ipk the issuer public key
6067 * @param disclosure a bool-array that steers the disclosure of attributes
6168 * @param msg the message to be signed
69+ * @param rhIndex the index of the attribute that represents the revocation handle
70+ * @param cri the credential revocation information that allows the signer to prove non-revocation
6271 */
63- IdemixSignature (IdemixCredential c , BIG sk , IdemixPseudonym pseudonym , IdemixIssuerPublicKey ipk , boolean [] disclosure , byte [] msg ) {
64- if (c == null || sk == null || pseudonym == null || pseudonym .getNym () == null || pseudonym .getRandNym () == null || ipk == null || disclosure == null || msg == null ) {
72+ IdemixSignature (IdemixCredential c , BIG sk , IdemixPseudonym pseudonym , IdemixIssuerPublicKey ipk , boolean [] disclosure , byte [] msg , int rhIndex , Idemix . CredentialRevocationInformation cri ) {
73+ if (c == null || sk == null || pseudonym == null || pseudonym .getNym () == null || pseudonym .getRandNym () == null || ipk == null || disclosure == null || msg == null || cri == null ) {
6574 throw new IllegalArgumentException ("Cannot construct idemix signature from null input" );
6675 }
76+
77+ if (disclosure .length != c .getAttrs ().length ) {
78+ throw new IllegalArgumentException ("Disclosure length must be the same as the number of attributes" );
79+ }
80+
81+ if (cri .getRevocationAlg () >= RevocationAlgorithm .values ().length ) {
82+ throw new IllegalArgumentException ("CRI specifies unknown revocation algorithm" );
83+ }
84+
85+ if (cri .getRevocationAlg () != RevocationAlgorithm .ALG_NO_REVOCATION .ordinal () && disclosure [rhIndex ]) {
86+ throw new IllegalArgumentException ("Attribute " + rhIndex + " is disclosed but also used a revocation handle attribute, which should remain hidden" );
87+ }
88+
89+ RevocationAlgorithm revocationAlgorithm = RevocationAlgorithm .values ()[cri .getRevocationAlg ()];
90+
6791 int [] hiddenIndices = hiddenIndices (disclosure );
6892 final RAND rng = IdemixUtils .getRand ();
6993 // Start signature
@@ -96,6 +120,18 @@ public class IdemixSignature {
96120 rAttrs [i ] = IdemixUtils .randModOrder (rng );
97121 }
98122
123+ // Compute non-revoked proof
124+ NonRevocationProver prover = NonRevocationProver .getNonRevocationProver (revocationAlgorithm );
125+ int hiddenRHIndex = Ints .indexOf (hiddenIndices , rhIndex );
126+ if (hiddenRHIndex < 0 ) {
127+ // rhIndex is not present, set to last index position
128+ hiddenRHIndex = hiddenIndices .length ;
129+ }
130+ byte [] nonRevokedProofHashData = prover .getFSContribution (BIG .fromBytes (c .getAttrs ()[rhIndex ]), rAttrs [hiddenRHIndex ], cri );
131+ if (nonRevokedProofHashData == null ) {
132+ throw new RuntimeException ("Failed to compute non-revoked proof" );
133+ }
134+
99135 ECP t1 = aPrime .mul2 (re , ipk .getHRand (), rR2 );
100136 ECP t2 = PAIR .G1mul (ipk .getHRand (), rSPrime );
101137 t2 .add (bPrime .mul2 (rR3 , ipk .getHsk (), rsk ));
@@ -132,29 +168,12 @@ public class IdemixSignature {
132168
133169 proofC = IdemixUtils .hashModOrder (finalProofData );
134170
135- proofSSk = new BIG (rsk );
136- proofSSk .add (BIG .modmul (proofC , sk , IdemixUtils .GROUP_ORDER ));
137- proofSSk .mod (IdemixUtils .GROUP_ORDER );
138-
139- proofSE = new BIG (re );
140- proofSE .add (BIG .modneg (BIG .modmul (proofC , c .getE (), IdemixUtils .GROUP_ORDER ), IdemixUtils .GROUP_ORDER ));
141- proofSE .mod (IdemixUtils .GROUP_ORDER );
142-
143- proofSR2 = new BIG (rR2 );
144- proofSR2 .add (BIG .modmul (proofC , r2 , IdemixUtils .GROUP_ORDER ));
145- proofSR2 .mod (IdemixUtils .GROUP_ORDER );
146-
147- proofSR3 = new BIG (rR3 );
148- proofSR3 .add (BIG .modneg (BIG .modmul (proofC , r3 , IdemixUtils .GROUP_ORDER ), IdemixUtils .GROUP_ORDER ));
149- proofSR3 .mod (IdemixUtils .GROUP_ORDER );
150-
151- proofSSPrime = new BIG (rSPrime );
152- proofSSPrime .add (BIG .modmul (proofC , sPrime , IdemixUtils .GROUP_ORDER ));
153- proofSSPrime .mod (IdemixUtils .GROUP_ORDER );
154-
155- proofSRNym = new BIG (rRNym );
156- proofSRNym .add (BIG .modmul (proofC , pseudonym .getRandNym (), IdemixUtils .GROUP_ORDER ));
157- proofSRNym .mod (IdemixUtils .GROUP_ORDER );
171+ proofSSk = IdemixUtils .modAdd (rsk , BIG .modmul (proofC , sk , IdemixUtils .GROUP_ORDER ), IdemixUtils .GROUP_ORDER );
172+ proofSE = IdemixUtils .modSub (re , BIG .modmul (proofC , c .getE (), IdemixUtils .GROUP_ORDER ), IdemixUtils .GROUP_ORDER );
173+ proofSR2 = IdemixUtils .modAdd (rR2 , BIG .modmul (proofC , r2 , IdemixUtils .GROUP_ORDER ), IdemixUtils .GROUP_ORDER );
174+ proofSR3 = IdemixUtils .modSub (rR3 , BIG .modmul (proofC , r3 , IdemixUtils .GROUP_ORDER ), IdemixUtils .GROUP_ORDER );
175+ proofSSPrime = IdemixUtils .modAdd (rSPrime , BIG .modmul (proofC , sPrime , IdemixUtils .GROUP_ORDER ), IdemixUtils .GROUP_ORDER );
176+ proofSRNym = IdemixUtils .modAdd (rRNym , BIG .modmul (proofC , pseudonym .getRandNym (), IdemixUtils .GROUP_ORDER ), IdemixUtils .GROUP_ORDER );
158177
159178 nym = new ECP ();
160179 nym .copy (pseudonym .getNym ());
@@ -166,6 +185,11 @@ public class IdemixSignature {
166185 proofSAttrs [i ].mod (IdemixUtils .GROUP_ORDER );
167186 }
168187
188+ // include non-revocation proof in signature
189+ this .revocationPk = cri .getEpochPk ();
190+ this .revocationPKSig = cri .getEpochPkSig ().toByteArray ();
191+ this .epoch = cri .getEpoch ();
192+ this .nonRevocationProof = prover .getNonRevocationProof (this .proofC );
169193 }
170194
171195 /**
@@ -193,6 +217,11 @@ public class IdemixSignature {
193217 for (int i = 0 ; i < proto .getProofSAttrsCount (); i ++) {
194218 proofSAttrs [i ] = BIG .fromBytes (proto .getProofSAttrs (i ).toByteArray ());
195219 }
220+
221+ revocationPk = proto .getRevocationEpochPk ();
222+ revocationPKSig = proto .getRevocationPkSig ().toByteArray ();
223+ epoch = proto .getEpoch ();
224+ nonRevocationProof = proto .getNonRevocationProof ();
196225 }
197226
198227 /**
@@ -201,10 +230,13 @@ public class IdemixSignature {
201230 * @param disclosure an array indicating which attributes it expects to be disclosed
202231 * @param ipk the issuer public key
203232 * @param msg the message that should be signed in this signature
204- * @param attributeValues BIG array with attributeValues[i] contains the desired attribute value for the i-th undisclosed attribute in disclosure
233+ * @param attributeValues BIG array where attributeValues[i] contains the desired attribute value for the i-th attribute if its disclosed
234+ * @param rhIndex index of the attribute that represents the revocation-handle
235+ * @param revPk the long term public key used to authenticate CRIs
236+ * @param epoch monotonically increasing counter representing a time window
205237 * @return true iff valid
206238 */
207- boolean verify (boolean [] disclosure , IdemixIssuerPublicKey ipk , byte [] msg , BIG [] attributeValues ) {
239+ boolean verify (boolean [] disclosure , IdemixIssuerPublicKey ipk , byte [] msg , BIG [] attributeValues , int rhIndex , PublicKey revPk , int epoch ) throws CryptoException {
208240 if (disclosure == null || ipk == null || msg == null || attributeValues == null || attributeValues .length != ipk .getAttributeNames ().length || disclosure .length != ipk .getAttributeNames ().length ) {
209241 return false ;
210242 }
@@ -215,14 +247,27 @@ boolean verify(boolean[] disclosure, IdemixIssuerPublicKey ipk, byte[] msg, BIG[
215247 }
216248
217249 int [] hiddenIndices = hiddenIndices (disclosure );
218-
219250 if (proofSAttrs .length != hiddenIndices .length ) {
220251 return false ;
221252 }
222-
223253 if (aPrime .is_infinity ()) {
224254 return false ;
225255 }
256+ if (nonRevocationProof .getRevocationAlg () >= RevocationAlgorithm .values ().length ) {
257+ throw new IllegalArgumentException ("CRI specifies unknown revocation algorithm" );
258+ }
259+
260+ RevocationAlgorithm revocationAlgorithm = RevocationAlgorithm .values ()[nonRevocationProof .getRevocationAlg ()];
261+
262+ if (disclosure [rhIndex ]) {
263+ throw new IllegalArgumentException ("Attribute " + rhIndex + " is disclosed but also used a revocation handle attribute, which should remain hidden" );
264+ }
265+
266+ // Verify EpochPK
267+ if (!RevocationAuthority .verifyEpochPK (revPk , this .revocationPk , this .revocationPKSig , epoch , revocationAlgorithm )) {
268+ // Signature is based on an invalid revocation epoch public key
269+ return false ;
270+ }
226271
227272 FP12 temp1 = PAIR .ate (ipk .getW (), aPrime );
228273 FP12 temp2 = PAIR .ate (IdemixUtils .genG2 , aBar );
@@ -261,6 +306,19 @@ boolean verify(boolean[] disclosure, IdemixIssuerPublicKey ipk, byte[] msg, BIG[
261306 ECP t3 = ipk .getHsk ().mul2 (proofSSk , ipk .getHRand (), proofSRNym );
262307 t3 .sub (nym .mul (proofC ));
263308
309+ // Check with non-revoked-verifier
310+ NonRevocationVerifier nonRevokedVerifier = NonRevocationVerifier .getNonRevocationVerifier (revocationAlgorithm );
311+ int hiddenRHIndex = Ints .indexOf (hiddenIndices , rhIndex );
312+ if (hiddenRHIndex < 0 ) {
313+ // rhIndex is not present, set to last index position
314+ hiddenRHIndex = hiddenIndices .length ;
315+ }
316+ BIG proofSRh = proofSAttrs [hiddenRHIndex ];
317+ byte [] nonRevokedProofBytes = nonRevokedVerifier .recomputeFSContribution (this .nonRevocationProof , proofC , IdemixUtils .transformFromProto (this .revocationPk ), proofSRh );
318+ if (nonRevokedProofBytes == null ) {
319+ return false ;
320+ }
321+
264322 // create proofData such that it can contain the sign label, 7 elements in G1 (each of size 2*FIELD_BYTES+1),
265323 // the ipk hash, the disclosure array, and the message
266324 byte [] proofData = new byte [0 ];
@@ -304,7 +362,11 @@ Idemix.Signature toProto() {
304362 .setProofSR3 (ByteString .copyFrom (IdemixUtils .bigToBytes (proofSR3 )))
305363 .setProofSRNym (ByteString .copyFrom (IdemixUtils .bigToBytes (proofSRNym )))
306364 .setProofSSPrime (ByteString .copyFrom (IdemixUtils .bigToBytes (proofSSPrime )))
307- .setNonce (ByteString .copyFrom (IdemixUtils .bigToBytes (nonce )));
365+ .setNonce (ByteString .copyFrom (IdemixUtils .bigToBytes (nonce )))
366+ .setRevocationEpochPk (revocationPk )
367+ .setRevocationPkSig (ByteString .copyFrom (revocationPKSig ))
368+ .setEpoch (epoch )
369+ .setNonRevocationProof (nonRevocationProof );
308370
309371 for (BIG attr : proofSAttrs ) {
310372 builder .addProofSAttrs (ByteString .copyFrom (IdemixUtils .bigToBytes (attr )));
@@ -323,15 +385,15 @@ private int[] hiddenIndices(boolean[] disclosure) {
323385 if (disclosure == null ) {
324386 throw new IllegalArgumentException ("cannot compute hidden indices of null disclosure" );
325387 }
326- List <Integer > hiddenIndicesList = new ArrayList <Integer >();
388+ List <Integer > hiddenIndicesList = new ArrayList <>();
327389 for (int i = 0 ; i < disclosure .length ; i ++) {
328390 if (!disclosure [i ]) {
329391 hiddenIndicesList .add (i );
330392 }
331393 }
332394 int [] hiddenIndices = new int [hiddenIndicesList .size ()];
333395 for (int i = 0 ; i < hiddenIndicesList .size (); i ++) {
334- hiddenIndices [i ] = hiddenIndicesList .get (i ). intValue () ;
396+ hiddenIndices [i ] = hiddenIndicesList .get (i );
335397 }
336398
337399 return hiddenIndices ;
0 commit comments