Skip to content
This repository was archived by the owner on Apr 22, 2025. It is now read-only.

Commit be173ba

Browse files
Manu DrijversSaad Karim
authored andcommitted
[FAB-6682] Adds identity mixer crypto in java
This commit provides the identity mixer crypto in java, which is equivalent to the functionality provided in the fabric/idemix golang package. This package can be used to integrate anonymous signing using identity mixer in the java sdk. Change-Id: I4a7dc9f1fd319c6aacba50cf84b84c2de7788382 Signed-off-by: Manu Drijvers <mdr@zurich.ibm.com> Signed-off-by: Saad Karim <skarim@us.ibm.com>
1 parent b649868 commit be173ba

File tree

11 files changed

+1654
-39
lines changed

11 files changed

+1654
-39
lines changed

pom.xml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,13 @@
207207
<version>1.23</version>
208208
</dependency>
209209

210+
<!-- https://mvnrepository.com/artifact/org.miracl.milagro.amcl/milagro-crypto-java -->
211+
<dependency>
212+
<groupId>org.miracl.milagro.amcl</groupId>
213+
<artifactId>milagro-crypto-java</artifactId>
214+
<version>0.4.0</version>
215+
</dependency>
216+
210217
</dependencies>
211218

212219

@@ -308,7 +315,7 @@
308315
<version>2.10.4</version>
309316
<configuration>
310317
<excludePackageNames>
311-
org.hyperledger.fabric_ca.sdk.helper:org.hyperledger.fabric.protos.*:org.hyperledger.fabric.sdk.helper:org.hyperledger.fabric.sdk.transaction:org.hyperledger.fabric.sdk.security
318+
org.hyperledger.fabric_ca.sdk.helper:org.hyperledger.fabric.protos.*:org.hyperledger.fabric.sdk.helper:org.hyperledger.fabric.sdk.transaction:org.hyperledger.fabric.sdk.security:org.hyperledger.fabric.sdk.idemix
312319
</excludePackageNames>
313320
<show>public</show>
314321
<doctitle>Hyperledger Fabric Java SDK</doctitle>
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
/*
2+
*
3+
* Copyright 2017, 2018 IBM Corp. All Rights Reserved.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*
15+
*/
16+
17+
package org.hyperledger.fabric.sdk.idemix;
18+
19+
import java.util.ArrayList;
20+
import java.util.Arrays;
21+
22+
import com.google.protobuf.ByteString;
23+
import org.apache.milagro.amcl.FP256BN.BIG;
24+
import org.apache.milagro.amcl.FP256BN.ECP;
25+
import org.apache.milagro.amcl.RAND;
26+
import org.hyperledger.fabric.protos.idemix.Idemix;
27+
28+
/**
29+
* IdemixCredRequest represents the first message of the idemix issuance protocol,
30+
* in which the user requests a credential from the issuer.
31+
*/
32+
public class IdemixCredRequest {
33+
private final ECP nym;
34+
private final BIG issuerNonce;
35+
private final BIG proofC;
36+
private final BIG proofS;
37+
38+
private static final String CREDREQUEST_LABEL = "credRequest";
39+
40+
41+
/**
42+
* Constructor
43+
*
44+
* @param sk the secret key of the user
45+
* @param issuerNonce a nonce
46+
* @param ipk the issuer public key
47+
*/
48+
IdemixCredRequest(BIG sk, BIG issuerNonce, IdemixIssuerPublicKey ipk) {
49+
if (sk == null) {
50+
throw new IllegalArgumentException("Cannot create idemix credrequest from null Secret Key input");
51+
}
52+
53+
if (issuerNonce == null) {
54+
throw new IllegalArgumentException("Cannot create idemix credrequest from null issuer nonce input");
55+
}
56+
57+
if (ipk == null) {
58+
throw new IllegalArgumentException("Cannot create idemix credrequest from null Issuer Public Key input");
59+
}
60+
final RAND rng = IdemixUtils.getRand();
61+
nym = ipk.getHsk().mul(sk);
62+
this.issuerNonce = new BIG(issuerNonce);
63+
64+
// Create Zero Knowledge Proof
65+
BIG rsk = IdemixUtils.randModOrder(rng);
66+
ECP t = ipk.getHsk().mul(rsk);
67+
68+
// Make proofData: total 3 elements of G1, each 2*FIELD_BYTES+1 (ECP),
69+
// plus length of String array,
70+
// plus one BIG
71+
byte[] proofData = new byte[0];
72+
proofData = IdemixUtils.append(proofData, CREDREQUEST_LABEL.getBytes());
73+
proofData = IdemixUtils.append(proofData, IdemixUtils.ecpToBytes(t));
74+
proofData = IdemixUtils.append(proofData, IdemixUtils.ecpToBytes(ipk.getHsk()));
75+
proofData = IdemixUtils.append(proofData, IdemixUtils.ecpToBytes(nym));
76+
proofData = IdemixUtils.append(proofData, IdemixUtils.bigToBytes(issuerNonce));
77+
proofData = IdemixUtils.append(proofData, ipk.getHash());
78+
79+
proofC = IdemixUtils.hashModOrder(proofData);
80+
81+
// Compute proofS = ...
82+
proofS = BIG.modmul(proofC, sk, IdemixUtils.GROUP_ORDER).plus(rsk);
83+
proofS.mod(IdemixUtils.GROUP_ORDER);
84+
}
85+
86+
/**
87+
* Construct a IdemixCredRequest from a serialized credrequest
88+
*
89+
* @param proto a protobuf representation of a credential request
90+
*/
91+
IdemixCredRequest(Idemix.CredRequest proto) {
92+
if (proto == null) {
93+
throw new IllegalArgumentException("Cannot create idemix credrequest from null input");
94+
}
95+
nym = IdemixUtils.transformFromProto(proto.getNym());
96+
proofC = BIG.fromBytes(proto.getProofC().toByteArray());
97+
proofS = BIG.fromBytes(proto.getProofS().toByteArray());
98+
issuerNonce = BIG.fromBytes(proto.getIssuerNonce().toByteArray());
99+
}
100+
101+
/**
102+
* @return a pseudonym of the credential requester
103+
*/
104+
ECP getNym() {
105+
return nym;
106+
}
107+
108+
/**
109+
* @return a proto version of this IdemixCredRequest
110+
*/
111+
Idemix.CredRequest toProto() {
112+
return Idemix.CredRequest.newBuilder()
113+
.setNym(IdemixUtils.transformToProto(nym))
114+
.setProofC(ByteString.copyFrom(IdemixUtils.bigToBytes(proofC)))
115+
.setProofS(ByteString.copyFrom(IdemixUtils.bigToBytes(proofS)))
116+
.setIssuerNonce(ByteString.copyFrom(IdemixUtils.bigToBytes(issuerNonce)))
117+
.build();
118+
}
119+
120+
121+
/**
122+
* Cryptographically verify the IdemixCredRequest
123+
*
124+
* @param ipk the issuer public key
125+
* @return true iff valid
126+
*/
127+
boolean check(IdemixIssuerPublicKey ipk) {
128+
129+
if (nym == null ||
130+
issuerNonce == null ||
131+
proofC == null ||
132+
proofS == null ||
133+
ipk == null) {
134+
return false;
135+
}
136+
137+
ECP t = ipk.getHsk().mul(proofS);
138+
t.sub(nym.mul(proofC));
139+
140+
byte[] proofData = new byte[0];
141+
proofData = IdemixUtils.append(proofData, CREDREQUEST_LABEL.getBytes());
142+
proofData = IdemixUtils.append(proofData, IdemixUtils.ecpToBytes(t));
143+
proofData = IdemixUtils.append(proofData, IdemixUtils.ecpToBytes(ipk.getHsk()));
144+
proofData = IdemixUtils.append(proofData, IdemixUtils.ecpToBytes(nym));
145+
proofData = IdemixUtils.append(proofData, IdemixUtils.bigToBytes(issuerNonce));
146+
proofData = IdemixUtils.append(proofData, ipk.getHash());
147+
148+
149+
// Hash proofData to hproofdata
150+
byte[] hproofdata = IdemixUtils.bigToBytes(IdemixUtils.hashModOrder(proofData));
151+
152+
return Arrays.equals(IdemixUtils.bigToBytes(proofC), hproofdata);
153+
}
154+
}
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
/*
2+
*
3+
* Copyright 2017, 2018 IBM Corp. All Rights Reserved.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*
15+
*/
16+
17+
package org.hyperledger.fabric.sdk.idemix;
18+
19+
import com.google.protobuf.ByteString;
20+
import org.apache.milagro.amcl.FP256BN.BIG;
21+
import org.apache.milagro.amcl.FP256BN.ECP;
22+
import org.apache.milagro.amcl.FP256BN.ECP2;
23+
import org.apache.milagro.amcl.FP256BN.PAIR;
24+
import org.apache.milagro.amcl.RAND;
25+
import org.hyperledger.fabric.protos.idemix.Idemix;
26+
27+
/**
28+
* IdemixCredential represents a user's idemix credential,
29+
* which is a BBS+ signature (see "Constant-Size Dynamic k-TAA" by Man Ho Au, Willy Susilo, Yi Mu)
30+
* on the user's secret key and attribute values.
31+
*/
32+
public class IdemixCredential {
33+
34+
private final ECP A;
35+
private final ECP B;
36+
private final BIG E;
37+
private final BIG S;
38+
private final byte[][] Attrs;
39+
40+
/**
41+
* Constructor creating a new credential
42+
*
43+
* @param key the issuer key pair
44+
* @param m a credential request
45+
* @param attrs an array of attribute values as BIG
46+
*/
47+
IdemixCredential(IdemixIssuerKey key, IdemixCredRequest m, BIG[] attrs) {
48+
if (key == null || key.getIpk() == null || m == null || attrs == null) {
49+
throw new IllegalArgumentException("Cannot create idemix credential from null input");
50+
}
51+
if (attrs.length != key.getIpk().getAttributeNames().length) {
52+
throw new IllegalArgumentException("Amount of attribute values does not match amount of attributes in issuer public key");
53+
}
54+
final RAND rng = IdemixUtils.getRand();
55+
// Place a BBS+ signature on the user key and the attribute values
56+
// (For BBS+, see "Constant-Size Dynamic k-TAA" by Man Ho Au, Willy Susilo, Yi Mu)
57+
E = IdemixUtils.randModOrder(rng);
58+
S = IdemixUtils.randModOrder(rng);
59+
60+
B = new ECP();
61+
B.copy(IdemixUtils.genG1);
62+
B.add(m.getNym());
63+
B.add(key.getIpk().getHRand().mul(S));
64+
65+
for (int i = 0; i < attrs.length / 2; i++) {
66+
B.add(key.getIpk().getHAttrs()[2 * i].mul2(attrs[2 * i], key.getIpk().getHAttrs()[2 * i + 1], attrs[2 * i + 1]));
67+
}
68+
if (attrs.length % 2 != 0) {
69+
B.add(key.getIpk().getHAttrs()[attrs.length - 1].mul(attrs[attrs.length - 1]));
70+
}
71+
72+
BIG exp = new BIG(key.getIsk()).plus(E);
73+
exp.mod(IdemixUtils.GROUP_ORDER);
74+
exp.invmodp(IdemixUtils.GROUP_ORDER);
75+
A = B.mul(exp);
76+
77+
Attrs = new byte[attrs.length][IdemixUtils.FIELD_BYTES];
78+
byte[] b = new byte[IdemixUtils.FIELD_BYTES];
79+
for (int i = 0; i < attrs.length; i++) {
80+
attrs[i].toBytes(b);
81+
System.arraycopy(b, 0, Attrs[i], 0, IdemixUtils.FIELD_BYTES);
82+
}
83+
}
84+
85+
/**
86+
* Construct an IdemixCredential from a serialized credential
87+
*
88+
* @param proto a protobuf representation of a credential
89+
*/
90+
IdemixCredential(Idemix.Credential proto) {
91+
if (proto == null) {
92+
throw new IllegalArgumentException("Cannot create idemix credential from null input");
93+
}
94+
95+
A = IdemixUtils.transformFromProto(proto.getA());
96+
B = IdemixUtils.transformFromProto(proto.getB());
97+
E = BIG.fromBytes(proto.getE().toByteArray());
98+
S = BIG.fromBytes(proto.getS().toByteArray());
99+
Attrs = new byte[proto.getAttrsCount()][];
100+
for (int i = 0; i < proto.getAttrsCount(); i++) {
101+
Attrs[i] = proto.getAttrs(i).toByteArray();
102+
}
103+
}
104+
105+
ECP getA() {
106+
return A;
107+
}
108+
109+
ECP getB() {
110+
return B;
111+
}
112+
113+
BIG getE() {
114+
return E;
115+
}
116+
117+
BIG getS() {
118+
return S;
119+
}
120+
121+
byte[][] getAttrs() {
122+
return Attrs;
123+
}
124+
125+
/**
126+
* verify cryptographically verifies the credential
127+
*
128+
* @param sk the secret key of the user
129+
* @param ipk the public key of the issuer
130+
* @return true iff valid
131+
*/
132+
boolean verify(BIG sk, IdemixIssuerPublicKey ipk) {
133+
if (ipk == null || Attrs.length != ipk.getAttributeNames().length) {
134+
return false;
135+
}
136+
for (byte[] attr : Attrs) {
137+
if (attr == null) {
138+
return false;
139+
}
140+
}
141+
142+
ECP bPrime = new ECP();
143+
bPrime.copy(IdemixUtils.genG1);
144+
bPrime.add(ipk.getHsk().mul2(sk, ipk.getHRand(), S));
145+
for (int i = 0; i < Attrs.length / 2; i++) {
146+
bPrime.add(ipk.getHAttrs()[2 * i].mul2(BIG.fromBytes(Attrs[2 * i]), ipk.getHAttrs()[2 * i + 1], BIG.fromBytes(Attrs[2 * i + 1])));
147+
}
148+
if (Attrs.length % 2 != 0) {
149+
bPrime.add(ipk.getHAttrs()[Attrs.length - 1].mul(BIG.fromBytes(Attrs[Attrs.length - 1])));
150+
}
151+
if (!B.equals(bPrime)) {
152+
return false;
153+
}
154+
155+
ECP2 a = IdemixUtils.genG2.mul(E);
156+
a.add(ipk.getW());
157+
a.affine();
158+
return PAIR.fexp(PAIR.ate(a, A)).equals(PAIR.fexp(PAIR.ate(IdemixUtils.genG2, B)));
159+
}
160+
161+
/**
162+
* @return A proto representation of this credential
163+
*/
164+
Idemix.Credential toProto() {
165+
Idemix.Credential.Builder builder = Idemix.Credential.newBuilder()
166+
.setA(IdemixUtils.transformToProto(A))
167+
.setB(IdemixUtils.transformToProto(B))
168+
.setE(ByteString.copyFrom(IdemixUtils.bigToBytes(E)))
169+
.setS(ByteString.copyFrom(IdemixUtils.bigToBytes(S)));
170+
171+
for (byte[] attr : Attrs) {
172+
builder.addAttrs(ByteString.copyFrom(attr));
173+
}
174+
175+
return builder.build();
176+
}
177+
}

0 commit comments

Comments
 (0)