|
| 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