github.com/hellobchain/third_party@v0.0.0-20230331131523-deb0478a2e52/hyperledger/fabric/idemix/signature.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package idemix 8 9 import ( 10 "github.com/hellobchain/newcryptosm/ecdsa" 11 "github.com/hellobchain/wswlog/wlogging" 12 "sort" 13 14 "github.com/hellobchain/third_party/hyperledger/fabric-amcl/amcl" 15 "github.com/hellobchain/third_party/hyperledger/fabric-amcl/amcl/FP256BN" 16 "github.com/pkg/errors" 17 ) 18 19 var idemixLogger = wlogging.MustGetLoggerWithoutName() 20 21 // signLabel is the label used in zero-knowledge proof (ZKP) to identify that this ZKP is a signature of knowledge 22 const signLabel = "sign" 23 24 // A signature that is produced using an Identity Mixer credential is a so-called signature of knowledge 25 // (for details see C.P.Schnorr "Efficient Identification and Signatures for Smart Cards") 26 // An Identity Mixer signature is a signature of knowledge that signs a message and proves (in zero-knowledge) 27 // the knowledge of the user secret (and possibly attributes) signed inside a credential 28 // that was issued by a certain issuer (referred to with the issuer public key) 29 // The signature is verified using the message being signed and the public key of the issuer 30 // Some of the attributes from the credential can be selectvely disclosed or different statements can be proven about 31 // credential atrributes without diclosing them in the clear 32 // The difference between a standard signature using X.509 certificates and an Identity Mixer signature is 33 // the advanced privacy features provided by Identity Mixer (due to zero-knowledge proofs): 34 // - Unlinkability of the signatures produced with the same credential 35 // - Selective attribute disclosure and predicates over attributes 36 37 // Make a slice of all the attribute indices that will not be disclosed 38 func hiddenIndices(Disclosure []byte) []int { 39 HiddenIndices := make([]int, 0) 40 for index, disclose := range Disclosure { 41 if disclose == 0 { 42 HiddenIndices = append(HiddenIndices, index) 43 } 44 } 45 return HiddenIndices 46 } 47 48 // NewSignature creates a new idemix signature (Schnorr-type signature) 49 // The []byte Disclosure steers which attributes are disclosed: 50 // if Disclosure[i] == 0 then attribute i remains hidden and otherwise it is disclosed. 51 // We require the revocation handle to remain undisclosed (i.e., Disclosure[rhIndex] == 0). 52 // We use the zero-knowledge proof by http://eprint.iacr.org/2016/663.pdf, Sec. 4.5 to prove knowledge of a BBS+ signature 53 func NewSignature(cred *Credential, sk *FP256BN.BIG, Nym *FP256BN.ECP, RNym *FP256BN.BIG, ipk *IssuerPublicKey, Disclosure []byte, msg []byte, rhIndex int, cri *CredentialRevocationInformation, rng *amcl.RAND) (*Signature, error) { 54 // Validate inputs 55 if cred == nil || sk == nil || Nym == nil || RNym == nil || ipk == nil || rng == nil || cri == nil { 56 return nil, errors.Errorf("cannot create idemix signature: received nil input") 57 } 58 59 if rhIndex < 0 || rhIndex >= len(ipk.AttributeNames) || len(Disclosure) != len(ipk.AttributeNames) { 60 return nil, errors.Errorf("cannot create idemix signature: received invalid input") 61 } 62 63 if cri.RevocationAlg != int32(ALG_NO_REVOCATION) && Disclosure[rhIndex] == 1 { 64 return nil, errors.Errorf("Attribute %d is disclosed but also used as revocation handle attribute, which should remain hidden.", rhIndex) 65 } 66 67 // locate the indices of the attributes to hide and sample randomness for them 68 HiddenIndices := hiddenIndices(Disclosure) 69 70 // Generate required randomness r_1, r_2 71 r1 := RandModOrder(rng) 72 r2 := RandModOrder(rng) 73 // Set r_3 as \frac{1}{r_1} 74 r3 := FP256BN.NewBIGcopy(r1) 75 r3.Invmodp(GroupOrder) 76 77 // Sample a nonce 78 Nonce := RandModOrder(rng) 79 80 // Parse credential 81 A := EcpFromProto(cred.A) 82 B := EcpFromProto(cred.B) 83 84 // Randomize credential 85 86 // Compute A' as A^{r_!} 87 APrime := FP256BN.G1mul(A, r1) 88 89 // Compute ABar as A'^{-e} b^{r1} 90 ABar := FP256BN.G1mul(B, r1) 91 ABar.Sub(FP256BN.G1mul(APrime, FP256BN.FromBytes(cred.E))) 92 93 // Compute B' as b^{r1} / h_r^{r2}, where h_r is h_r 94 BPrime := FP256BN.G1mul(B, r1) 95 HRand := EcpFromProto(ipk.HRand) 96 // Parse h_{sk} from ipk 97 HSk := EcpFromProto(ipk.HSk) 98 99 BPrime.Sub(FP256BN.G1mul(HRand, r2)) 100 101 S := FP256BN.FromBytes(cred.S) 102 E := FP256BN.FromBytes(cred.E) 103 104 // Compute s' as s - r_2 \cdot r_3 105 sPrime := Modsub(S, FP256BN.Modmul(r2, r3, GroupOrder), GroupOrder) 106 107 // The rest of this function constructs the non-interactive zero knowledge proof 108 // that links the signature, the non-disclosed attributes and the nym. 109 110 // Sample the randomness used to compute the commitment values (aka t-values) for the ZKP 111 rSk := RandModOrder(rng) 112 re := RandModOrder(rng) 113 rR2 := RandModOrder(rng) 114 rR3 := RandModOrder(rng) 115 rSPrime := RandModOrder(rng) 116 rRNym := RandModOrder(rng) 117 118 rAttrs := make([]*FP256BN.BIG, len(HiddenIndices)) 119 for i := range HiddenIndices { 120 rAttrs[i] = RandModOrder(rng) 121 } 122 123 // First compute the non-revocation proof. 124 // The challenge of the ZKP needs to depend on it, as well. 125 prover, err := getNonRevocationProver(RevocationAlgorithm(cri.RevocationAlg)) 126 if err != nil { 127 return nil, err 128 } 129 nonRevokedProofHashData, err := prover.getFSContribution( 130 FP256BN.FromBytes(cred.Attrs[rhIndex]), 131 rAttrs[sort.SearchInts(HiddenIndices, rhIndex)], 132 cri, 133 rng, 134 ) 135 if err != nil { 136 return nil, errors.Wrap(err, "failed to compute non-revoked proof") 137 } 138 139 // Step 1: First message (t-values) 140 141 // t1 is related to knowledge of the credential (recall, it is a BBS+ signature) 142 t1 := APrime.Mul2(re, HRand, rR2) // A'^{r_E} \cdot h_r^{r_{r2}} 143 144 // t2: is related to knowledge of the non-disclosed attributes that signed in (A,B,S,E) 145 t2 := FP256BN.G1mul(HRand, rSPrime) // h_r^{r_{s'}} 146 t2.Add(BPrime.Mul2(rR3, HSk, rSk)) // B'^{r_{r3}} \cdot h_{sk}^{r_{sk}} 147 for i := 0; i < len(HiddenIndices)/2; i++ { 148 t2.Add( 149 // \cdot h_{2 \cdot i}^{r_{attrs,i} 150 EcpFromProto(ipk.HAttrs[HiddenIndices[2*i]]).Mul2( 151 rAttrs[2*i], 152 EcpFromProto(ipk.HAttrs[HiddenIndices[2*i+1]]), 153 rAttrs[2*i+1], 154 ), 155 ) 156 } 157 if len(HiddenIndices)%2 != 0 { 158 t2.Add(FP256BN.G1mul(EcpFromProto(ipk.HAttrs[HiddenIndices[len(HiddenIndices)-1]]), rAttrs[len(HiddenIndices)-1])) 159 } 160 161 // t3 is related to the knowledge of the secrets behind the pseudonym, which is also signed in (A,B,S,E) 162 t3 := HSk.Mul2(rSk, HRand, rRNym) // h_{sk}^{r_{sk}} \cdot h_r^{r_{rnym}} 163 164 // Step 2: Compute the Fiat-Shamir hash, forming the challenge of the ZKP. 165 166 // Compute the Fiat-Shamir hash, forming the challenge of the ZKP. 167 // proofData is the data being hashed, it consists of: 168 // the signature label 169 // 7 elements of G1 each taking 2*FieldBytes+1 bytes 170 // one bigint (hash of the issuer public key) of length FieldBytes 171 // disclosed attributes 172 // message being signed 173 // the amount of bytes needed for the nonrevocation proof 174 proofData := make([]byte, len([]byte(signLabel))+7*(2*FieldBytes+1)+FieldBytes+len(Disclosure)+len(msg)+ProofBytes[RevocationAlgorithm(cri.RevocationAlg)]) 175 index := 0 176 index = appendBytesString(proofData, index, signLabel) 177 index = appendBytesG1(proofData, index, t1) 178 index = appendBytesG1(proofData, index, t2) 179 index = appendBytesG1(proofData, index, t3) 180 index = appendBytesG1(proofData, index, APrime) 181 index = appendBytesG1(proofData, index, ABar) 182 index = appendBytesG1(proofData, index, BPrime) 183 index = appendBytesG1(proofData, index, Nym) 184 index = appendBytes(proofData, index, nonRevokedProofHashData) 185 copy(proofData[index:], ipk.Hash) 186 index = index + FieldBytes 187 copy(proofData[index:], Disclosure) 188 index = index + len(Disclosure) 189 copy(proofData[index:], msg) 190 c := HashModOrder(proofData) 191 192 // add the previous hash and the nonce and hash again to compute a second hash (C value) 193 index = 0 194 proofData = proofData[:2*FieldBytes] 195 index = appendBytesBig(proofData, index, c) 196 index = appendBytesBig(proofData, index, Nonce) 197 ProofC := HashModOrder(proofData) 198 199 // Step 3: reply to the challenge message (s-values) 200 ProofSSk := Modadd(rSk, FP256BN.Modmul(ProofC, sk, GroupOrder), GroupOrder) // s_sk = rSK + C \cdot sk 201 ProofSE := Modsub(re, FP256BN.Modmul(ProofC, E, GroupOrder), GroupOrder) // s_e = re + C \cdot E 202 ProofSR2 := Modadd(rR2, FP256BN.Modmul(ProofC, r2, GroupOrder), GroupOrder) // s_r2 = rR2 + C \cdot r2 203 ProofSR3 := Modsub(rR3, FP256BN.Modmul(ProofC, r3, GroupOrder), GroupOrder) // s_r3 = rR3 + C \cdot r3 204 ProofSSPrime := Modadd(rSPrime, FP256BN.Modmul(ProofC, sPrime, GroupOrder), GroupOrder) // s_S' = rSPrime + C \cdot sPrime 205 ProofSRNym := Modadd(rRNym, FP256BN.Modmul(ProofC, RNym, GroupOrder), GroupOrder) // s_RNym = rRNym + C \cdot RNym 206 ProofSAttrs := make([][]byte, len(HiddenIndices)) 207 for i, j := range HiddenIndices { 208 ProofSAttrs[i] = BigToBytes( 209 // s_attrsi = rAttrsi + C \cdot cred.Attrs[j] 210 Modadd(rAttrs[i], FP256BN.Modmul(ProofC, FP256BN.FromBytes(cred.Attrs[j]), GroupOrder), GroupOrder), 211 ) 212 } 213 214 // Compute the revocation part 215 nonRevokedProof, err := prover.getNonRevokedProof(ProofC) 216 if err != nil { 217 return nil, err 218 } 219 220 // We are done. Return signature 221 return &Signature{ 222 APrime: EcpToProto(APrime), 223 ABar: EcpToProto(ABar), 224 BPrime: EcpToProto(BPrime), 225 ProofC: BigToBytes(ProofC), 226 ProofSSk: BigToBytes(ProofSSk), 227 ProofSE: BigToBytes(ProofSE), 228 ProofSR2: BigToBytes(ProofSR2), 229 ProofSR3: BigToBytes(ProofSR3), 230 ProofSSPrime: BigToBytes(ProofSSPrime), 231 ProofSAttrs: ProofSAttrs, 232 Nonce: BigToBytes(Nonce), 233 Nym: EcpToProto(Nym), 234 ProofSRNym: BigToBytes(ProofSRNym), 235 RevocationEpochPk: cri.EpochPk, 236 RevocationPkSig: cri.EpochPkSig, 237 Epoch: cri.Epoch, 238 NonRevocationProof: nonRevokedProof}, 239 nil 240 } 241 242 // Ver verifies an idemix signature 243 // Disclosure steers which attributes it expects to be disclosed 244 // attributeValues contains the desired attribute values. 245 // This function will check that if attribute i is disclosed, the i-th attribute equals attributeValues[i]. 246 func (sig *Signature) Ver(Disclosure []byte, ipk *IssuerPublicKey, msg []byte, attributeValues []*FP256BN.BIG, rhIndex int, revPk *ecdsa.PublicKey, epoch int) error { 247 // Validate inputs 248 if ipk == nil || revPk == nil { 249 return errors.Errorf("cannot verify idemix signature: received nil input") 250 } 251 252 if rhIndex < 0 || rhIndex >= len(ipk.AttributeNames) || len(Disclosure) != len(ipk.AttributeNames) { 253 return errors.Errorf("cannot verify idemix signature: received invalid input") 254 } 255 256 if sig.NonRevocationProof.RevocationAlg != int32(ALG_NO_REVOCATION) && Disclosure[rhIndex] == 1 { 257 return errors.Errorf("Attribute %d is disclosed but is also used as revocation handle, which should remain hidden.", rhIndex) 258 } 259 260 HiddenIndices := hiddenIndices(Disclosure) 261 262 // Parse signature 263 APrime := EcpFromProto(sig.GetAPrime()) 264 ABar := EcpFromProto(sig.GetABar()) 265 BPrime := EcpFromProto(sig.GetBPrime()) 266 Nym := EcpFromProto(sig.GetNym()) 267 ProofC := FP256BN.FromBytes(sig.GetProofC()) 268 ProofSSk := FP256BN.FromBytes(sig.GetProofSSk()) 269 ProofSE := FP256BN.FromBytes(sig.GetProofSE()) 270 ProofSR2 := FP256BN.FromBytes(sig.GetProofSR2()) 271 ProofSR3 := FP256BN.FromBytes(sig.GetProofSR3()) 272 ProofSSPrime := FP256BN.FromBytes(sig.GetProofSSPrime()) 273 ProofSRNym := FP256BN.FromBytes(sig.GetProofSRNym()) 274 ProofSAttrs := make([]*FP256BN.BIG, len(sig.GetProofSAttrs())) 275 276 if len(sig.ProofSAttrs) != len(HiddenIndices) { 277 return errors.Errorf("signature invalid: incorrect amount of s-values for AttributeProofSpec") 278 } 279 for i, b := range sig.ProofSAttrs { 280 ProofSAttrs[i] = FP256BN.FromBytes(b) 281 } 282 Nonce := FP256BN.FromBytes(sig.GetNonce()) 283 284 // Parse issuer public key 285 W := Ecp2FromProto(ipk.W) 286 HRand := EcpFromProto(ipk.HRand) 287 HSk := EcpFromProto(ipk.HSk) 288 289 // Verify signature 290 if APrime.Is_infinity() { 291 return errors.Errorf("signature invalid: APrime = 1") 292 } 293 temp1 := FP256BN.Ate(W, APrime) 294 temp2 := FP256BN.Ate(GenG2, ABar) 295 temp2.Inverse() 296 temp1.Mul(temp2) 297 if !FP256BN.Fexp(temp1).Isunity() { 298 return errors.Errorf("signature invalid: APrime and ABar don't have the expected structure") 299 } 300 301 // Verify ZK proof 302 303 // Recover t-values 304 305 // Recompute t1 306 t1 := APrime.Mul2(ProofSE, HRand, ProofSR2) 307 temp := FP256BN.NewECP() 308 temp.Copy(ABar) 309 temp.Sub(BPrime) 310 t1.Sub(FP256BN.G1mul(temp, ProofC)) 311 312 // Recompute t2 313 t2 := FP256BN.G1mul(HRand, ProofSSPrime) 314 t2.Add(BPrime.Mul2(ProofSR3, HSk, ProofSSk)) 315 for i := 0; i < len(HiddenIndices)/2; i++ { 316 t2.Add(EcpFromProto(ipk.HAttrs[HiddenIndices[2*i]]).Mul2(ProofSAttrs[2*i], EcpFromProto(ipk.HAttrs[HiddenIndices[2*i+1]]), ProofSAttrs[2*i+1])) 317 } 318 if len(HiddenIndices)%2 != 0 { 319 t2.Add(FP256BN.G1mul(EcpFromProto(ipk.HAttrs[HiddenIndices[len(HiddenIndices)-1]]), ProofSAttrs[len(HiddenIndices)-1])) 320 } 321 temp = FP256BN.NewECP() 322 temp.Copy(GenG1) 323 for index, disclose := range Disclosure { 324 if disclose != 0 { 325 temp.Add(FP256BN.G1mul(EcpFromProto(ipk.HAttrs[index]), attributeValues[index])) 326 } 327 } 328 t2.Add(FP256BN.G1mul(temp, ProofC)) 329 330 // Recompute t3 331 t3 := HSk.Mul2(ProofSSk, HRand, ProofSRNym) 332 t3.Sub(Nym.Mul(ProofC)) 333 334 // add contribution from the non-revocation proof 335 nonRevokedVer, err := getNonRevocationVerifier(RevocationAlgorithm(sig.NonRevocationProof.RevocationAlg)) 336 if err != nil { 337 return err 338 } 339 340 i := sort.SearchInts(HiddenIndices, rhIndex) 341 proofSRh := ProofSAttrs[i] 342 nonRevokedProofBytes, err := nonRevokedVer.recomputeFSContribution(sig.NonRevocationProof, ProofC, Ecp2FromProto(sig.RevocationEpochPk), proofSRh) 343 if err != nil { 344 return err 345 } 346 347 // Recompute challenge 348 // proofData is the data being hashed, it consists of: 349 // the signature label 350 // 7 elements of G1 each taking 2*FieldBytes+1 bytes 351 // one bigint (hash of the issuer public key) of length FieldBytes 352 // disclosed attributes 353 // message that was signed 354 proofData := make([]byte, len([]byte(signLabel))+7*(2*FieldBytes+1)+FieldBytes+len(Disclosure)+len(msg)+ProofBytes[RevocationAlgorithm(sig.NonRevocationProof.RevocationAlg)]) 355 index := 0 356 index = appendBytesString(proofData, index, signLabel) 357 index = appendBytesG1(proofData, index, t1) 358 index = appendBytesG1(proofData, index, t2) 359 index = appendBytesG1(proofData, index, t3) 360 index = appendBytesG1(proofData, index, APrime) 361 index = appendBytesG1(proofData, index, ABar) 362 index = appendBytesG1(proofData, index, BPrime) 363 index = appendBytesG1(proofData, index, Nym) 364 index = appendBytes(proofData, index, nonRevokedProofBytes) 365 copy(proofData[index:], ipk.Hash) 366 index = index + FieldBytes 367 copy(proofData[index:], Disclosure) 368 index = index + len(Disclosure) 369 copy(proofData[index:], msg) 370 371 c := HashModOrder(proofData) 372 index = 0 373 proofData = proofData[:2*FieldBytes] 374 index = appendBytesBig(proofData, index, c) 375 index = appendBytesBig(proofData, index, Nonce) 376 377 if *ProofC != *HashModOrder(proofData) { 378 // This debug line helps identify where the mismatch happened 379 idemixLogger.Debugf("Signature Verification : \n"+ 380 " [t1:%v]\n,"+ 381 " [t2:%v]\n,"+ 382 " [t3:%v]\n,"+ 383 " [APrime:%v]\n,"+ 384 " [ABar:%v]\n,"+ 385 " [BPrime:%v]\n,"+ 386 " [Nym:%v]\n,"+ 387 " [nonRevokedProofBytes:%v]\n,"+ 388 " [ipk.Hash:%v]\n,"+ 389 " [Disclosure:%v]\n,"+ 390 " [msg:%v]\n,", 391 EcpToBytes(t1), 392 EcpToBytes(t2), 393 EcpToBytes(t3), 394 EcpToBytes(APrime), 395 EcpToBytes(ABar), 396 EcpToBytes(BPrime), 397 EcpToBytes(Nym), 398 nonRevokedProofBytes, 399 ipk.Hash, 400 Disclosure, 401 msg) 402 return errors.Errorf("signature invalid: zero-knowledge proof is invalid") 403 } 404 405 // Signature is valid 406 return nil 407 }