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

Commit 814bf79

Browse files
Maria DubovitskayaSaad Karim
authored andcommitted
[FAB-6836] Implement Idemix Signing Identity
This commit adds the Identity and SigningIdentity interfaces and their implementation with Idemix crypto library for transaction signing and further integration with the sdk. The tests use crypto material (configs) that is generated by the idemixgen tool from fabric, so the crypto implementations in Go and Java are fully compatible. Change-Id: I13d43bac6e87be36fd46fa77b6762aa56b9b1e0d Signed-off-by: Maria Dubovitskaya <mdu@zurich.ibm.com> Signed-off-by: Manu Drijvers <mdr@zurich.ibm.com> Signed-off-by: Saad Karim <skarim@us.ibm.com> Signed-off-by: Rafa Torres <rtm@zurich.ibm.com>
1 parent 68faf09 commit 814bf79

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+1106
-23
lines changed

src/main/java/org/hyperledger/fabric/sdk/idemix/IdemixCredential.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ public class IdemixCredential {
8787
*
8888
* @param proto a protobuf representation of a credential
8989
*/
90-
IdemixCredential(Idemix.Credential proto) {
90+
public IdemixCredential(Idemix.Credential proto) {
9191
if (proto == null) {
9292
throw new IllegalArgumentException("Cannot create idemix credential from null input");
9393
}
@@ -118,7 +118,7 @@ BIG getS() {
118118
return S;
119119
}
120120

121-
byte[][] getAttrs() {
121+
public byte[][] getAttrs() {
122122
return Attrs;
123123
}
124124

@@ -129,7 +129,7 @@ byte[][] getAttrs() {
129129
* @param ipk the public key of the issuer
130130
* @return true iff valid
131131
*/
132-
boolean verify(BIG sk, IdemixIssuerPublicKey ipk) {
132+
public boolean verify(BIG sk, IdemixIssuerPublicKey ipk) {
133133
if (ipk == null || Attrs.length != ipk.getAttributeNames().length) {
134134
return false;
135135
}

src/main/java/org/hyperledger/fabric/sdk/idemix/IdemixIssuerPublicKey.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ public class IdemixIssuerPublicKey {
122122
*
123123
* @param proto a protobuf representation of an issuer public key
124124
*/
125-
IdemixIssuerPublicKey(Idemix.IssuerPublicKey proto) {
125+
public IdemixIssuerPublicKey(Idemix.IssuerPublicKey proto) {
126126
// check for bad input
127127
if (proto == null) {
128128
throw new IllegalArgumentException("Cannot create IdemixIssuerPublicKey from null input");
@@ -160,7 +160,7 @@ public class IdemixIssuerPublicKey {
160160
*
161161
* @return true iff valid
162162
*/
163-
boolean check() {
163+
public boolean check() {
164164
// check formalities of IdemixIssuerPublicKey
165165
if (AttributeNames == null || Hsk == null || HRand == null || HAttrs == null
166166
|| BarG1 == null || BarG1.is_infinity() || BarG2 == null
@@ -221,7 +221,7 @@ Idemix.IssuerPublicKey toProto() {
221221
/**
222222
* @return The names of the attributes certified with this issuer public key
223223
*/
224-
String[] getAttributeNames() {
224+
public String[] getAttributeNames() {
225225
return AttributeNames;
226226
}
227227

@@ -244,7 +244,7 @@ protected ECP2 getW() {
244244
/**
245245
* @return A digest of this issuer public key
246246
*/
247-
byte[] getHash() {
247+
public byte[] getHash() {
248248
return Hash;
249249
}
250250
}

src/main/java/org/hyperledger/fabric/sdk/idemix/IdemixPseudonym.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public class IdemixPseudonym {
3535
* @param sk the secret key of the user
3636
* @param ipk the public key of the issuer
3737
*/
38-
IdemixPseudonym(BIG sk, IdemixIssuerPublicKey ipk) {
38+
public IdemixPseudonym(BIG sk, IdemixIssuerPublicKey ipk) {
3939
if (sk == null || ipk == null) {
4040
throw new IllegalArgumentException("Cannot construct idemix pseudonym from null input");
4141
}
@@ -47,7 +47,7 @@ public class IdemixPseudonym {
4747
/**
4848
* @return the value of the pseudonym as an ECP
4949
*/
50-
ECP getNym() {
50+
public ECP getNym() {
5151
return Nym;
5252
}
5353

src/main/java/org/hyperledger/fabric/sdk/idemix/IdemixPseudonymSignature.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public class IdemixPseudonymSignature {
4343
* @param ipk the issuer public key
4444
* @param msg the message to be signed
4545
*/
46-
IdemixPseudonymSignature(BIG sk, IdemixPseudonym pseudonym, IdemixIssuerPublicKey ipk, byte[] msg) {
46+
public IdemixPseudonymSignature(BIG sk, IdemixPseudonym pseudonym, IdemixIssuerPublicKey ipk, byte[] msg) {
4747
if (sk == null || pseudonym == null || pseudonym.getNym() == null || pseudonym.getRandNym() == null || ipk == null || msg == null) {
4848
throw new IllegalArgumentException("Cannot create IdemixPseudonymSignature from null input");
4949
}
@@ -84,7 +84,7 @@ public class IdemixPseudonymSignature {
8484
*
8585
* @param proto a protobuf object representing an IdemixPseudonymSignature
8686
*/
87-
IdemixPseudonymSignature(Idemix.NymSignature proto) {
87+
public IdemixPseudonymSignature(Idemix.NymSignature proto) {
8888
if (proto == null) {
8989
throw new IllegalArgumentException("Cannot create idemix nym signature from null input");
9090
}
@@ -102,7 +102,7 @@ public class IdemixPseudonymSignature {
102102
* @param msg the message that should be signed in this signature
103103
* @return true iff valid
104104
*/
105-
boolean verify(ECP nym, IdemixIssuerPublicKey ipk, byte[] msg) {
105+
public boolean verify(ECP nym, IdemixIssuerPublicKey ipk, byte[] msg) {
106106
if (nym == null || ipk == null || msg == null) {
107107
return false;
108108
}
@@ -131,7 +131,7 @@ boolean verify(ECP nym, IdemixIssuerPublicKey ipk, byte[] msg) {
131131
/**
132132
* @return A proto object representing this IdemixPseudonymSignature
133133
*/
134-
Idemix.NymSignature toProto() {
134+
public Idemix.NymSignature toProto() {
135135
return Idemix.NymSignature.newBuilder()
136136
.setProofC(ByteString.copyFrom(IdemixUtils.bigToBytes(proofC)))
137137
.setProofSSk(ByteString.copyFrom(IdemixUtils.bigToBytes(proofSSk)))

src/main/java/org/hyperledger/fabric/sdk/idemix/IdemixSignature.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ public class IdemixSignature {
6969
* @param rhIndex the index of the attribute that represents the revocation handle
7070
* @param cri the credential revocation information that allows the signer to prove non-revocation
7171
*/
72-
IdemixSignature(IdemixCredential c, BIG sk, IdemixPseudonym pseudonym, IdemixIssuerPublicKey ipk, boolean[] disclosure, byte[] msg, int rhIndex, Idemix.CredentialRevocationInformation cri) {
72+
public IdemixSignature(IdemixCredential c, BIG sk, IdemixPseudonym pseudonym, IdemixIssuerPublicKey ipk, boolean[] disclosure, byte[] msg, int rhIndex, Idemix.CredentialRevocationInformation cri) {
7373
if (c == null || sk == null || pseudonym == null || pseudonym.getNym() == null || pseudonym.getRandNym() == null || ipk == null || disclosure == null || msg == null || cri == null) {
7474
throw new IllegalArgumentException("Cannot construct idemix signature from null input");
7575
}
@@ -197,7 +197,7 @@ public class IdemixSignature {
197197
*
198198
* @param proto a protobuf object representing an IdemixSignature
199199
*/
200-
IdemixSignature(Idemix.Signature proto) {
200+
public IdemixSignature(Idemix.Signature proto) {
201201
if (proto == null) {
202202
throw new IllegalArgumentException("Cannot construct idemix signature from null input");
203203
}
@@ -236,7 +236,7 @@ public class IdemixSignature {
236236
* @param epoch monotonically increasing counter representing a time window
237237
* @return true iff valid
238238
*/
239-
boolean verify(boolean[] disclosure, IdemixIssuerPublicKey ipk, byte[] msg, BIG[] attributeValues, int rhIndex, PublicKey revPk, int epoch) throws CryptoException {
239+
public boolean verify(boolean[] disclosure, IdemixIssuerPublicKey ipk, byte[] msg, BIG[] attributeValues, int rhIndex, PublicKey revPk, int epoch) throws CryptoException {
240240
if (disclosure == null || ipk == null || msg == null || attributeValues == null || attributeValues.length != ipk.getAttributeNames().length || disclosure.length != ipk.getAttributeNames().length) {
241241
return false;
242242
}
@@ -349,7 +349,7 @@ boolean verify(boolean[] disclosure, IdemixIssuerPublicKey ipk, byte[] msg, BIG[
349349
*
350350
* @return a protobuf object representing this IdemixSignature
351351
*/
352-
Idemix.Signature toProto() {
352+
public Idemix.Signature toProto() {
353353
Idemix.Signature.Builder builder = Idemix.Signature.newBuilder()
354354
.setAPrime(IdemixUtils.transformToProto(aPrime))
355355
.setABar(IdemixUtils.transformToProto(aBar))

src/main/java/org/hyperledger/fabric/sdk/idemix/IdemixUtils.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ public final class IdemixUtils {
5252
static final FP12 genGT = PAIR.fexp(PAIR.ate(genG2, genG1));
5353
static final BIG GROUP_ORDER = new BIG(ROM.CURVE_Order);
5454
static final int FIELD_BYTES = BIG.MODBYTES;
55-
private static final RAND RNG = getRand();
5655

5756
private IdemixUtils() {
5857
// private constructor as there shouldn't be instances of this utility class
@@ -95,7 +94,7 @@ public static BIG randModOrder(RAND rng) {
9594
* @param data the data to be hashed
9695
* @return a BIG in 0, ..., GROUP_ORDER-1 that is the hash of the data
9796
*/
98-
static BIG hashModOrder(byte[] data) {
97+
public static BIG hashModOrder(byte[] data) {
9998
HASH256 hash = new HASH256();
10099
for (byte b : data) {
101100
hash.process(b);
@@ -115,7 +114,7 @@ static BIG hashModOrder(byte[] data) {
115114
* @param big the BIG to turn into bytes
116115
* @return a byte array representation of the BIG
117116
*/
118-
static byte[] bigToBytes(BIG big) {
117+
public static byte[] bigToBytes(BIG big) {
119118
byte[] ret = new byte[IdemixUtils.FIELD_BYTES];
120119
big.toBytes(ret);
121120
return ret;
@@ -256,7 +255,7 @@ static Idemix.ECP transformToProto(ECP w) {
256255
* @param m the modulus
257256
* @return Returns a+b (mod m)
258257
*/
259-
public static BIG modAdd(BIG a, BIG b, BIG m) {
258+
static BIG modAdd(BIG a, BIG b, BIG m) {
260259
BIG c = a.plus(b);
261260
c.mod(m);
262261
return c;
@@ -270,7 +269,7 @@ public static BIG modAdd(BIG a, BIG b, BIG m) {
270269
* @param m the modulus
271270
* @return returns a-b (mod m)
272271
*/
273-
public static BIG modSub(BIG a, BIG b, BIG m) {
272+
static BIG modSub(BIG a, BIG b, BIG m) {
274273
return modAdd(a, BIG.modneg(b, m), m);
275274
}
276275
}
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
/*
2+
*
3+
* Copyright 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.identity;
18+
19+
import java.util.Arrays;
20+
21+
import com.google.protobuf.ByteString;
22+
import com.google.protobuf.InvalidProtocolBufferException;
23+
import org.apache.commons.logging.Log;
24+
import org.apache.commons.logging.LogFactory;
25+
import org.apache.milagro.amcl.FP256BN.BIG;
26+
import org.apache.milagro.amcl.FP256BN.ECP;
27+
import org.hyperledger.fabric.protos.common.MspPrincipal;
28+
import org.hyperledger.fabric.protos.idemix.Idemix;
29+
import org.hyperledger.fabric.protos.msp.Identities;
30+
import org.hyperledger.fabric.sdk.exception.CryptoException;
31+
import org.hyperledger.fabric.sdk.exception.InvalidArgumentException;
32+
import org.hyperledger.fabric.sdk.idemix.IdemixIssuerPublicKey;
33+
import org.hyperledger.fabric.sdk.idemix.IdemixSignature;
34+
import org.hyperledger.fabric.sdk.idemix.IdemixUtils;
35+
36+
/**
37+
* IdemixIdentity is a public serializable part of the IdemixSigningIdentity.
38+
* It contains an (un)linkable pseudonym, revealed attribute values, and a
39+
* corresponding proof of possession of an Idemix credential
40+
*/
41+
public class IdemixIdentity implements Identity {
42+
43+
private static final Log logger = LogFactory.getLog(IdemixIdentity.class);
44+
45+
// MSP identifier
46+
private final String mspId;
47+
48+
private final byte[] ipkHash;
49+
50+
// Idemix Pseudonym
51+
private final ECP pseudonym;
52+
53+
// Organization Unit attribute
54+
private final String ou;
55+
56+
// Role attribute
57+
private final boolean role;
58+
59+
// Proof of possession of Idemix credential
60+
// with respect to the pseudonym (nym)
61+
// and the corresponding attributes (ou, role)
62+
private final IdemixSignature associationProof;
63+
64+
/**
65+
* Create Idemix Identity from a Serialized Identity
66+
*
67+
* @param proto
68+
*/
69+
public IdemixIdentity(Identities.SerializedIdentity proto) throws CryptoException, InvalidArgumentException {
70+
if (proto == null) {
71+
throw new InvalidArgumentException("Input must not be null");
72+
}
73+
74+
this.mspId = proto.getMspid();
75+
76+
try {
77+
logger.trace("Fetching Idemix Proto");
78+
Identities.SerializedIdemixIdentity idemixProto = Identities.SerializedIdemixIdentity.parseFrom(proto.getIdBytes());
79+
80+
if (idemixProto == null) {
81+
throw new IllegalArgumentException("The identity does not contain a serialized idemix identity");
82+
}
83+
logger.trace("Deserializing Nym and attribute values");
84+
this.pseudonym = new ECP(BIG.fromBytes(idemixProto.getNymX().toByteArray()),
85+
BIG.fromBytes(idemixProto.getNymY().toByteArray()));
86+
87+
MspPrincipal.OrganizationUnit ou = MspPrincipal.OrganizationUnit.parseFrom(idemixProto.getOu());
88+
MspPrincipal.MSPRole role = MspPrincipal.MSPRole.parseFrom(idemixProto.getRole());
89+
90+
this.ou = ou.getOrganizationalUnitIdentifier();
91+
this.role = role.getRole().getNumber() == 1;
92+
this.ipkHash = ou.getCertifiersIdentifier().toByteArray();
93+
94+
logger.trace("Deserializing Proof");
95+
this.associationProof = new IdemixSignature(Idemix.Signature.parseFrom(idemixProto.getProof().toByteArray()));
96+
97+
} catch (InvalidProtocolBufferException e) {
98+
throw new CryptoException("Cannot deserialize MSP ID", e);
99+
}
100+
}
101+
102+
/**
103+
* Create Idemix Identity from the following inputs:
104+
*
105+
* @param mspId is MSP ID sting
106+
* @param nym is Identity Mixer Pseudonym
107+
* @param ou is OU attribute
108+
* @param role is Role attribute
109+
* @param proof is Proof
110+
*/
111+
public IdemixIdentity(String mspId, IdemixIssuerPublicKey ipk, ECP nym, String ou, boolean role, IdemixSignature proof)
112+
throws InvalidArgumentException {
113+
114+
if (mspId == null) {
115+
throw new InvalidArgumentException("MSP ID must not be null");
116+
}
117+
118+
if (mspId.isEmpty()) {
119+
throw new InvalidArgumentException("MSP ID must not be empty");
120+
}
121+
122+
if (ipk == null) {
123+
throw new InvalidArgumentException("Issuer Public Key must not be empty");
124+
}
125+
126+
if (nym == null) {
127+
throw new InvalidArgumentException("Identity Mixer Pseudonym (nym) must not be null");
128+
}
129+
130+
if (ou == null) {
131+
throw new InvalidArgumentException("OU attribute must not be null");
132+
}
133+
134+
if (ou.isEmpty()) {
135+
throw new InvalidArgumentException("OU attribute must not be empty");
136+
}
137+
138+
if (proof == null) {
139+
throw new InvalidArgumentException("Proof must not be null");
140+
}
141+
142+
143+
this.mspId = mspId;
144+
this.ipkHash = ipk.getHash();
145+
this.pseudonym = nym;
146+
this.ou = ou;
147+
this.role = role;
148+
this.associationProof = proof;
149+
}
150+
151+
/**
152+
* Serialize Idemix Identity
153+
*/
154+
@Override
155+
public Identities.SerializedIdentity createSerializedIdentity() {
156+
MspPrincipal.OrganizationUnit ou = MspPrincipal.OrganizationUnit.newBuilder()
157+
.setCertifiersIdentifier(ByteString.copyFrom(this.ipkHash))
158+
.setMspIdentifier(this.mspId)
159+
.setOrganizationalUnitIdentifier(this.ou)
160+
.build();
161+
162+
MspPrincipal.MSPRole role = MspPrincipal.MSPRole.newBuilder()
163+
.setRole(this.role ? MspPrincipal.MSPRole.MSPRoleType.ADMIN : MspPrincipal.MSPRole.MSPRoleType.MEMBER)
164+
.setMspIdentifier(this.mspId)
165+
.build();
166+
167+
Identities.SerializedIdemixIdentity serializedIdemixIdentity = Identities.SerializedIdemixIdentity.newBuilder()
168+
.setProof(ByteString.copyFrom(this.associationProof.toProto().toByteArray()))
169+
.setOu(ByteString.copyFrom(ou.toByteArray()))
170+
.setRole(ByteString.copyFrom(role.toByteArray()))
171+
.setNymY(ByteString.copyFrom(IdemixUtils.bigToBytes(this.pseudonym.getY())))
172+
.setNymX(ByteString.copyFrom(IdemixUtils.bigToBytes(this.pseudonym.getX())))
173+
.build();
174+
175+
return Identities.SerializedIdentity.newBuilder()
176+
.setIdBytes(ByteString.copyFrom(serializedIdemixIdentity.toByteArray()))
177+
.setMspid(this.mspId)
178+
.build();
179+
}
180+
181+
public String getOuValue() {
182+
return this.ou;
183+
}
184+
185+
public boolean getRoleValue() {
186+
return this.role;
187+
}
188+
189+
@Override
190+
public String toString() {
191+
return "IdemixIdentity" +
192+
" [ MSP ID: " + this.mspId +
193+
" Issuer Public Key Hash: " + Arrays.toString(this.ipkHash) +
194+
" Pseudonym: " + this.pseudonym.toRawString() +
195+
" OU: " + this.ou +
196+
" Role: " + this.role +
197+
" Association Proof: " + this.associationProof.toProto().toString() +
198+
" ]";
199+
}
200+
}

0 commit comments

Comments
 (0)