-
Notifications
You must be signed in to change notification settings - Fork 707
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[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>
- Loading branch information
Manu Drijvers
authored and
Saad Karim
committed
Sep 4, 2018
1 parent
b649868
commit be173ba
Showing
11 changed files
with
1,654 additions
and
39 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
154 changes: 154 additions & 0 deletions
154
src/main/java/org/hyperledger/fabric/sdk/idemix/IdemixCredRequest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
/* | ||
* | ||
* Copyright 2017, 2018 IBM Corp. All Rights Reserved. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
* | ||
*/ | ||
|
||
package org.hyperledger.fabric.sdk.idemix; | ||
|
||
import java.util.ArrayList; | ||
import java.util.Arrays; | ||
|
||
import com.google.protobuf.ByteString; | ||
import org.apache.milagro.amcl.FP256BN.BIG; | ||
import org.apache.milagro.amcl.FP256BN.ECP; | ||
import org.apache.milagro.amcl.RAND; | ||
import org.hyperledger.fabric.protos.idemix.Idemix; | ||
|
||
/** | ||
* IdemixCredRequest represents the first message of the idemix issuance protocol, | ||
* in which the user requests a credential from the issuer. | ||
*/ | ||
public class IdemixCredRequest { | ||
private final ECP nym; | ||
private final BIG issuerNonce; | ||
private final BIG proofC; | ||
private final BIG proofS; | ||
|
||
private static final String CREDREQUEST_LABEL = "credRequest"; | ||
|
||
|
||
/** | ||
* Constructor | ||
* | ||
* @param sk the secret key of the user | ||
* @param issuerNonce a nonce | ||
* @param ipk the issuer public key | ||
*/ | ||
IdemixCredRequest(BIG sk, BIG issuerNonce, IdemixIssuerPublicKey ipk) { | ||
if (sk == null) { | ||
throw new IllegalArgumentException("Cannot create idemix credrequest from null Secret Key input"); | ||
} | ||
|
||
if (issuerNonce == null) { | ||
throw new IllegalArgumentException("Cannot create idemix credrequest from null issuer nonce input"); | ||
} | ||
|
||
if (ipk == null) { | ||
throw new IllegalArgumentException("Cannot create idemix credrequest from null Issuer Public Key input"); | ||
} | ||
final RAND rng = IdemixUtils.getRand(); | ||
nym = ipk.getHsk().mul(sk); | ||
this.issuerNonce = new BIG(issuerNonce); | ||
|
||
// Create Zero Knowledge Proof | ||
BIG rsk = IdemixUtils.randModOrder(rng); | ||
ECP t = ipk.getHsk().mul(rsk); | ||
|
||
// Make proofData: total 3 elements of G1, each 2*FIELD_BYTES+1 (ECP), | ||
// plus length of String array, | ||
// plus one BIG | ||
byte[] proofData = new byte[0]; | ||
proofData = IdemixUtils.append(proofData, CREDREQUEST_LABEL.getBytes()); | ||
proofData = IdemixUtils.append(proofData, IdemixUtils.ecpToBytes(t)); | ||
proofData = IdemixUtils.append(proofData, IdemixUtils.ecpToBytes(ipk.getHsk())); | ||
proofData = IdemixUtils.append(proofData, IdemixUtils.ecpToBytes(nym)); | ||
proofData = IdemixUtils.append(proofData, IdemixUtils.bigToBytes(issuerNonce)); | ||
proofData = IdemixUtils.append(proofData, ipk.getHash()); | ||
|
||
proofC = IdemixUtils.hashModOrder(proofData); | ||
|
||
// Compute proofS = ... | ||
proofS = BIG.modmul(proofC, sk, IdemixUtils.GROUP_ORDER).plus(rsk); | ||
proofS.mod(IdemixUtils.GROUP_ORDER); | ||
} | ||
|
||
/** | ||
* Construct a IdemixCredRequest from a serialized credrequest | ||
* | ||
* @param proto a protobuf representation of a credential request | ||
*/ | ||
IdemixCredRequest(Idemix.CredRequest proto) { | ||
if (proto == null) { | ||
throw new IllegalArgumentException("Cannot create idemix credrequest from null input"); | ||
} | ||
nym = IdemixUtils.transformFromProto(proto.getNym()); | ||
proofC = BIG.fromBytes(proto.getProofC().toByteArray()); | ||
proofS = BIG.fromBytes(proto.getProofS().toByteArray()); | ||
issuerNonce = BIG.fromBytes(proto.getIssuerNonce().toByteArray()); | ||
} | ||
|
||
/** | ||
* @return a pseudonym of the credential requester | ||
*/ | ||
ECP getNym() { | ||
return nym; | ||
} | ||
|
||
/** | ||
* @return a proto version of this IdemixCredRequest | ||
*/ | ||
Idemix.CredRequest toProto() { | ||
return Idemix.CredRequest.newBuilder() | ||
.setNym(IdemixUtils.transformToProto(nym)) | ||
.setProofC(ByteString.copyFrom(IdemixUtils.bigToBytes(proofC))) | ||
.setProofS(ByteString.copyFrom(IdemixUtils.bigToBytes(proofS))) | ||
.setIssuerNonce(ByteString.copyFrom(IdemixUtils.bigToBytes(issuerNonce))) | ||
.build(); | ||
} | ||
|
||
|
||
/** | ||
* Cryptographically verify the IdemixCredRequest | ||
* | ||
* @param ipk the issuer public key | ||
* @return true iff valid | ||
*/ | ||
boolean check(IdemixIssuerPublicKey ipk) { | ||
|
||
if (nym == null || | ||
issuerNonce == null || | ||
proofC == null || | ||
proofS == null || | ||
ipk == null) { | ||
return false; | ||
} | ||
|
||
ECP t = ipk.getHsk().mul(proofS); | ||
t.sub(nym.mul(proofC)); | ||
|
||
byte[] proofData = new byte[0]; | ||
proofData = IdemixUtils.append(proofData, CREDREQUEST_LABEL.getBytes()); | ||
proofData = IdemixUtils.append(proofData, IdemixUtils.ecpToBytes(t)); | ||
proofData = IdemixUtils.append(proofData, IdemixUtils.ecpToBytes(ipk.getHsk())); | ||
proofData = IdemixUtils.append(proofData, IdemixUtils.ecpToBytes(nym)); | ||
proofData = IdemixUtils.append(proofData, IdemixUtils.bigToBytes(issuerNonce)); | ||
proofData = IdemixUtils.append(proofData, ipk.getHash()); | ||
|
||
|
||
// Hash proofData to hproofdata | ||
byte[] hproofdata = IdemixUtils.bigToBytes(IdemixUtils.hashModOrder(proofData)); | ||
|
||
return Arrays.equals(IdemixUtils.bigToBytes(proofC), hproofdata); | ||
} | ||
} |
177 changes: 177 additions & 0 deletions
177
src/main/java/org/hyperledger/fabric/sdk/idemix/IdemixCredential.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
/* | ||
* | ||
* Copyright 2017, 2018 IBM Corp. All Rights Reserved. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
* | ||
*/ | ||
|
||
package org.hyperledger.fabric.sdk.idemix; | ||
|
||
import com.google.protobuf.ByteString; | ||
import org.apache.milagro.amcl.FP256BN.BIG; | ||
import org.apache.milagro.amcl.FP256BN.ECP; | ||
import org.apache.milagro.amcl.FP256BN.ECP2; | ||
import org.apache.milagro.amcl.FP256BN.PAIR; | ||
import org.apache.milagro.amcl.RAND; | ||
import org.hyperledger.fabric.protos.idemix.Idemix; | ||
|
||
/** | ||
* IdemixCredential represents a user's idemix credential, | ||
* which is a BBS+ signature (see "Constant-Size Dynamic k-TAA" by Man Ho Au, Willy Susilo, Yi Mu) | ||
* on the user's secret key and attribute values. | ||
*/ | ||
public class IdemixCredential { | ||
|
||
private final ECP A; | ||
private final ECP B; | ||
private final BIG E; | ||
private final BIG S; | ||
private final byte[][] Attrs; | ||
|
||
/** | ||
* Constructor creating a new credential | ||
* | ||
* @param key the issuer key pair | ||
* @param m a credential request | ||
* @param attrs an array of attribute values as BIG | ||
*/ | ||
IdemixCredential(IdemixIssuerKey key, IdemixCredRequest m, BIG[] attrs) { | ||
if (key == null || key.getIpk() == null || m == null || attrs == null) { | ||
throw new IllegalArgumentException("Cannot create idemix credential from null input"); | ||
} | ||
if (attrs.length != key.getIpk().getAttributeNames().length) { | ||
throw new IllegalArgumentException("Amount of attribute values does not match amount of attributes in issuer public key"); | ||
} | ||
final RAND rng = IdemixUtils.getRand(); | ||
// Place a BBS+ signature on the user key and the attribute values | ||
// (For BBS+, see "Constant-Size Dynamic k-TAA" by Man Ho Au, Willy Susilo, Yi Mu) | ||
E = IdemixUtils.randModOrder(rng); | ||
S = IdemixUtils.randModOrder(rng); | ||
|
||
B = new ECP(); | ||
B.copy(IdemixUtils.genG1); | ||
B.add(m.getNym()); | ||
B.add(key.getIpk().getHRand().mul(S)); | ||
|
||
for (int i = 0; i < attrs.length / 2; i++) { | ||
B.add(key.getIpk().getHAttrs()[2 * i].mul2(attrs[2 * i], key.getIpk().getHAttrs()[2 * i + 1], attrs[2 * i + 1])); | ||
} | ||
if (attrs.length % 2 != 0) { | ||
B.add(key.getIpk().getHAttrs()[attrs.length - 1].mul(attrs[attrs.length - 1])); | ||
} | ||
|
||
BIG exp = new BIG(key.getIsk()).plus(E); | ||
exp.mod(IdemixUtils.GROUP_ORDER); | ||
exp.invmodp(IdemixUtils.GROUP_ORDER); | ||
A = B.mul(exp); | ||
|
||
Attrs = new byte[attrs.length][IdemixUtils.FIELD_BYTES]; | ||
byte[] b = new byte[IdemixUtils.FIELD_BYTES]; | ||
for (int i = 0; i < attrs.length; i++) { | ||
attrs[i].toBytes(b); | ||
System.arraycopy(b, 0, Attrs[i], 0, IdemixUtils.FIELD_BYTES); | ||
} | ||
} | ||
|
||
/** | ||
* Construct an IdemixCredential from a serialized credential | ||
* | ||
* @param proto a protobuf representation of a credential | ||
*/ | ||
IdemixCredential(Idemix.Credential proto) { | ||
if (proto == null) { | ||
throw new IllegalArgumentException("Cannot create idemix credential from null input"); | ||
} | ||
|
||
A = IdemixUtils.transformFromProto(proto.getA()); | ||
B = IdemixUtils.transformFromProto(proto.getB()); | ||
E = BIG.fromBytes(proto.getE().toByteArray()); | ||
S = BIG.fromBytes(proto.getS().toByteArray()); | ||
Attrs = new byte[proto.getAttrsCount()][]; | ||
for (int i = 0; i < proto.getAttrsCount(); i++) { | ||
Attrs[i] = proto.getAttrs(i).toByteArray(); | ||
} | ||
} | ||
|
||
ECP getA() { | ||
return A; | ||
} | ||
|
||
ECP getB() { | ||
return B; | ||
} | ||
|
||
BIG getE() { | ||
return E; | ||
} | ||
|
||
BIG getS() { | ||
return S; | ||
} | ||
|
||
byte[][] getAttrs() { | ||
return Attrs; | ||
} | ||
|
||
/** | ||
* verify cryptographically verifies the credential | ||
* | ||
* @param sk the secret key of the user | ||
* @param ipk the public key of the issuer | ||
* @return true iff valid | ||
*/ | ||
boolean verify(BIG sk, IdemixIssuerPublicKey ipk) { | ||
if (ipk == null || Attrs.length != ipk.getAttributeNames().length) { | ||
return false; | ||
} | ||
for (byte[] attr : Attrs) { | ||
if (attr == null) { | ||
return false; | ||
} | ||
} | ||
|
||
ECP bPrime = new ECP(); | ||
bPrime.copy(IdemixUtils.genG1); | ||
bPrime.add(ipk.getHsk().mul2(sk, ipk.getHRand(), S)); | ||
for (int i = 0; i < Attrs.length / 2; i++) { | ||
bPrime.add(ipk.getHAttrs()[2 * i].mul2(BIG.fromBytes(Attrs[2 * i]), ipk.getHAttrs()[2 * i + 1], BIG.fromBytes(Attrs[2 * i + 1]))); | ||
} | ||
if (Attrs.length % 2 != 0) { | ||
bPrime.add(ipk.getHAttrs()[Attrs.length - 1].mul(BIG.fromBytes(Attrs[Attrs.length - 1]))); | ||
} | ||
if (!B.equals(bPrime)) { | ||
return false; | ||
} | ||
|
||
ECP2 a = IdemixUtils.genG2.mul(E); | ||
a.add(ipk.getW()); | ||
a.affine(); | ||
return PAIR.fexp(PAIR.ate(a, A)).equals(PAIR.fexp(PAIR.ate(IdemixUtils.genG2, B))); | ||
} | ||
|
||
/** | ||
* @return A proto representation of this credential | ||
*/ | ||
Idemix.Credential toProto() { | ||
Idemix.Credential.Builder builder = Idemix.Credential.newBuilder() | ||
.setA(IdemixUtils.transformToProto(A)) | ||
.setB(IdemixUtils.transformToProto(B)) | ||
.setE(ByteString.copyFrom(IdemixUtils.bigToBytes(E))) | ||
.setS(ByteString.copyFrom(IdemixUtils.bigToBytes(S))); | ||
|
||
for (byte[] attr : Attrs) { | ||
builder.addAttrs(ByteString.copyFrom(attr)); | ||
} | ||
|
||
return builder.build(); | ||
} | ||
} |
Oops, something went wrong.