16
16
17
17
package org .hyperledger .fabric .sdk .idemix ;
18
18
19
+ import java .security .PublicKey ;
19
20
import java .util .ArrayList ;
20
21
import java .util .Arrays ;
21
22
import java .util .List ;
22
23
24
+ import com .google .common .primitives .Ints ;
23
25
import com .google .protobuf .ByteString ;
24
26
import org .apache .milagro .amcl .FP256BN .BIG ;
25
27
import org .apache .milagro .amcl .FP256BN .ECP ;
26
28
import org .apache .milagro .amcl .FP256BN .FP12 ;
27
29
import org .apache .milagro .amcl .FP256BN .PAIR ;
28
30
import org .apache .milagro .amcl .RAND ;
29
31
import org .hyperledger .fabric .protos .idemix .Idemix ;
32
+ import org .hyperledger .fabric .sdk .exception .CryptoException ;
30
33
31
34
/**
32
35
* IdemixSignature represents an idemix signature, which is a zero knowledge proof
@@ -47,6 +50,10 @@ public class IdemixSignature {
47
50
private final BIG nonce ;
48
51
private final ECP nym ;
49
52
private final BIG proofSRNym ;
53
+ private Idemix .ECP2 revocationPk ;
54
+ private byte [] revocationPKSig ;
55
+ private long epoch ;
56
+ private Idemix .NonRevocationProof nonRevocationProof ;
50
57
51
58
private static final String SIGN_LABEL = "sign" ;
52
59
@@ -59,11 +66,28 @@ public class IdemixSignature {
59
66
* @param ipk the issuer public key
60
67
* @param disclosure a bool-array that steers the disclosure of attributes
61
68
* @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
62
71
*/
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 ) {
65
74
throw new IllegalArgumentException ("Cannot construct idemix signature from null input" );
66
75
}
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
+
67
91
int [] hiddenIndices = hiddenIndices (disclosure );
68
92
final RAND rng = IdemixUtils .getRand ();
69
93
// Start signature
@@ -96,6 +120,18 @@ public class IdemixSignature {
96
120
rAttrs [i ] = IdemixUtils .randModOrder (rng );
97
121
}
98
122
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
+
99
135
ECP t1 = aPrime .mul2 (re , ipk .getHRand (), rR2 );
100
136
ECP t2 = PAIR .G1mul (ipk .getHRand (), rSPrime );
101
137
t2 .add (bPrime .mul2 (rR3 , ipk .getHsk (), rsk ));
@@ -132,29 +168,12 @@ public class IdemixSignature {
132
168
133
169
proofC = IdemixUtils .hashModOrder (finalProofData );
134
170
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 );
158
177
159
178
nym = new ECP ();
160
179
nym .copy (pseudonym .getNym ());
@@ -166,6 +185,11 @@ public class IdemixSignature {
166
185
proofSAttrs [i ].mod (IdemixUtils .GROUP_ORDER );
167
186
}
168
187
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 );
169
193
}
170
194
171
195
/**
@@ -193,6 +217,11 @@ public class IdemixSignature {
193
217
for (int i = 0 ; i < proto .getProofSAttrsCount (); i ++) {
194
218
proofSAttrs [i ] = BIG .fromBytes (proto .getProofSAttrs (i ).toByteArray ());
195
219
}
220
+
221
+ revocationPk = proto .getRevocationEpochPk ();
222
+ revocationPKSig = proto .getRevocationPkSig ().toByteArray ();
223
+ epoch = proto .getEpoch ();
224
+ nonRevocationProof = proto .getNonRevocationProof ();
196
225
}
197
226
198
227
/**
@@ -201,10 +230,13 @@ public class IdemixSignature {
201
230
* @param disclosure an array indicating which attributes it expects to be disclosed
202
231
* @param ipk the issuer public key
203
232
* @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
205
237
* @return true iff valid
206
238
*/
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 {
208
240
if (disclosure == null || ipk == null || msg == null || attributeValues == null || attributeValues .length != ipk .getAttributeNames ().length || disclosure .length != ipk .getAttributeNames ().length ) {
209
241
return false ;
210
242
}
@@ -215,14 +247,27 @@ boolean verify(boolean[] disclosure, IdemixIssuerPublicKey ipk, byte[] msg, BIG[
215
247
}
216
248
217
249
int [] hiddenIndices = hiddenIndices (disclosure );
218
-
219
250
if (proofSAttrs .length != hiddenIndices .length ) {
220
251
return false ;
221
252
}
222
-
223
253
if (aPrime .is_infinity ()) {
224
254
return false ;
225
255
}
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
+ }
226
271
227
272
FP12 temp1 = PAIR .ate (ipk .getW (), aPrime );
228
273
FP12 temp2 = PAIR .ate (IdemixUtils .genG2 , aBar );
@@ -261,6 +306,19 @@ boolean verify(boolean[] disclosure, IdemixIssuerPublicKey ipk, byte[] msg, BIG[
261
306
ECP t3 = ipk .getHsk ().mul2 (proofSSk , ipk .getHRand (), proofSRNym );
262
307
t3 .sub (nym .mul (proofC ));
263
308
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
+
264
322
// create proofData such that it can contain the sign label, 7 elements in G1 (each of size 2*FIELD_BYTES+1),
265
323
// the ipk hash, the disclosure array, and the message
266
324
byte [] proofData = new byte [0 ];
@@ -304,7 +362,11 @@ Idemix.Signature toProto() {
304
362
.setProofSR3 (ByteString .copyFrom (IdemixUtils .bigToBytes (proofSR3 )))
305
363
.setProofSRNym (ByteString .copyFrom (IdemixUtils .bigToBytes (proofSRNym )))
306
364
.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 );
308
370
309
371
for (BIG attr : proofSAttrs ) {
310
372
builder .addProofSAttrs (ByteString .copyFrom (IdemixUtils .bigToBytes (attr )));
@@ -323,15 +385,15 @@ private int[] hiddenIndices(boolean[] disclosure) {
323
385
if (disclosure == null ) {
324
386
throw new IllegalArgumentException ("cannot compute hidden indices of null disclosure" );
325
387
}
326
- List <Integer > hiddenIndicesList = new ArrayList <Integer >();
388
+ List <Integer > hiddenIndicesList = new ArrayList <>();
327
389
for (int i = 0 ; i < disclosure .length ; i ++) {
328
390
if (!disclosure [i ]) {
329
391
hiddenIndicesList .add (i );
330
392
}
331
393
}
332
394
int [] hiddenIndices = new int [hiddenIndicesList .size ()];
333
395
for (int i = 0 ; i < hiddenIndicesList .size (); i ++) {
334
- hiddenIndices [i ] = hiddenIndicesList .get (i ). intValue () ;
396
+ hiddenIndices [i ] = hiddenIndicesList .get (i );
335
397
}
336
398
337
399
return hiddenIndices ;
0 commit comments