forked from hyperledger/fabric
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsignature.go
265 lines (227 loc) · 9.71 KB
/
signature.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package idemix
import (
"github.com/hyperledger/fabric-amcl/amcl"
"github.com/hyperledger/fabric-amcl/amcl/FP256BN"
"github.com/pkg/errors"
)
// signLabel is the label used in zero-knowledge proof (ZKP) to identify that this ZKP is a signature of knowledge
const signLabel = "sign"
// A signature that is produced using an Identity Mixer credential is a so-called signature of knowledge
// (for details see C.P.Schnorr "Efficient Identification and Signatures for Smart Cards")
// An Identity Mixer signature is a signature of knowledge that signs a message and proves (in zero-knowledge)
// the knowledge of the user secret (and possibly attributes) signed inside a credential
// that was issued by a certain issuer (referred to with the issuer public key)
// The signature is verified using the message being signed and the public key of the issuer
// Some of the attributes from the credential can be selectvely disclosed or different statements can be proven about
// credential atrributes without diclosing them in the clear
// The difference between a standard signature using X.509 certificates and an Identity Mixer signature is
// the advanced privacy features provided by Identity Mixer (due to zero-knowledge proofs):
// - Unlinkability of the signatures produced with the same credential
// - Selective attribute disclosure and predicates over attributes
// Make a slice of all the attribute indices that will not be disclosed
func hiddenIndices(Disclosure []byte) []int {
HiddenIndices := make([]int, 0)
for index, disclose := range Disclosure {
if disclose == 0 {
HiddenIndices = append(HiddenIndices, index)
}
}
return HiddenIndices
}
// NewSignature creates a new idemix signature (Schnorr-type signature)
// The []byte Disclosure steers which attributes are disclosed:
// if Disclosure[i] == 0 then attribute i remains hidden and otherwise it is disclosed.
// We use the zero-knowledge proof by http://eprint.iacr.org/2016/663.pdf to prove knowledge of a BBS+ signature
func NewSignature(cred *Credential, sk *FP256BN.BIG, Nym *FP256BN.ECP, RNym *FP256BN.BIG, ipk *IssuerPublicKey, Disclosure []byte, msg []byte, rng *amcl.RAND) (*Signature, error) {
if cred == nil || sk == nil || Nym == nil || RNym == nil || ipk == nil || rng == nil {
return nil, errors.Errorf("cannot create idemix signature: received nil input")
}
HiddenIndices := hiddenIndices(Disclosure)
// Start sig
r1 := RandModOrder(rng)
r2 := RandModOrder(rng)
r3 := FP256BN.NewBIGcopy(r1)
r3.Invmodp(GroupOrder)
Nonce := RandModOrder(rng)
A := EcpFromProto(cred.A)
B := EcpFromProto(cred.B)
APrime := FP256BN.G1mul(A, r1) // A' = A^{r1}
ABar := FP256BN.G1mul(B, r1)
ABar.Sub(FP256BN.G1mul(APrime, FP256BN.FromBytes(cred.E))) // barA = A'^{-e} b^{r1}
BPrime := FP256BN.G1mul(B, r1)
HRand := EcpFromProto(ipk.HRand)
HSk := EcpFromProto(ipk.HSk)
BPrime.Sub(FP256BN.G1mul(HRand, r2)) // b' = b^{r1} h_r^{-r2}
S := FP256BN.FromBytes(cred.S)
E := FP256BN.FromBytes(cred.E)
sPrime := Modsub(S, FP256BN.Modmul(r2, r3, GroupOrder), GroupOrder)
// Construct ZK proof
rSk := RandModOrder(rng)
re := RandModOrder(rng)
rR2 := RandModOrder(rng)
rR3 := RandModOrder(rng)
rSPrime := RandModOrder(rng)
rRNym := RandModOrder(rng)
rAttrs := make([]*FP256BN.BIG, len(HiddenIndices))
for i := range HiddenIndices {
rAttrs[i] = RandModOrder(rng)
}
t1 := APrime.Mul2(re, HRand, rR2)
t2 := FP256BN.G1mul(HRand, rSPrime)
t2.Add(BPrime.Mul2(rR3, HSk, rSk))
for i := 0; i < len(HiddenIndices)/2; i++ {
t2.Add(EcpFromProto(ipk.HAttrs[HiddenIndices[2*i]]).Mul2(rAttrs[2*i], EcpFromProto(ipk.HAttrs[HiddenIndices[2*i+1]]), rAttrs[2*i+1]))
}
if len(HiddenIndices)%2 != 0 {
t2.Add(FP256BN.G1mul(EcpFromProto(ipk.HAttrs[HiddenIndices[len(HiddenIndices)-1]]), rAttrs[len(HiddenIndices)-1]))
}
t3 := HSk.Mul2(rSk, HRand, rRNym)
// proofData is the data being hashed, it consists of:
// the signature label
// 7 elements of G1 each taking 2*FieldBytes+1 bytes
// one bigint (hash of the issuer public key) of length FieldBytes
// disclosed attributes
// message being signed
proofData := make([]byte, len([]byte(signLabel))+7*(2*FieldBytes+1)+FieldBytes+len(Disclosure)+len(msg))
index := 0
index = appendBytesString(proofData, index, signLabel)
index = appendBytesG1(proofData, index, t1)
index = appendBytesG1(proofData, index, t2)
index = appendBytesG1(proofData, index, t3)
index = appendBytesG1(proofData, index, APrime)
index = appendBytesG1(proofData, index, ABar)
index = appendBytesG1(proofData, index, BPrime)
index = appendBytesG1(proofData, index, Nym)
copy(proofData[index:], ipk.Hash)
index = index + FieldBytes
copy(proofData[index:], Disclosure)
index = index + len(Disclosure)
copy(proofData[index:], msg)
c := HashModOrder(proofData)
// add the previous hash and the nonce and hash again to compute a second hash (C value)
index = 0
proofData = proofData[:2*FieldBytes]
index = appendBytesBig(proofData, index, c)
index = appendBytesBig(proofData, index, Nonce)
ProofC := HashModOrder(proofData)
ProofSSk := Modadd(rSk, FP256BN.Modmul(ProofC, sk, GroupOrder), GroupOrder)
ProofSE := Modsub(re, FP256BN.Modmul(ProofC, E, GroupOrder), GroupOrder)
ProofSR2 := Modadd(rR2, FP256BN.Modmul(ProofC, r2, GroupOrder), GroupOrder)
ProofSR3 := Modsub(rR3, FP256BN.Modmul(ProofC, r3, GroupOrder), GroupOrder)
ProofSSPrime := Modadd(rSPrime, FP256BN.Modmul(ProofC, sPrime, GroupOrder), GroupOrder)
ProofSRNym := Modadd(rRNym, FP256BN.Modmul(ProofC, RNym, GroupOrder), GroupOrder)
ProofSAttrs := make([][]byte, len(HiddenIndices))
for i, j := range HiddenIndices {
ProofSAttrs[i] = BigToBytes(Modadd(rAttrs[i], FP256BN.Modmul(ProofC, FP256BN.FromBytes(cred.Attrs[j]), GroupOrder), GroupOrder))
}
return &Signature{
EcpToProto(APrime),
EcpToProto(ABar),
EcpToProto(BPrime),
BigToBytes(ProofC),
BigToBytes(ProofSSk),
BigToBytes(ProofSE),
BigToBytes(ProofSR2),
BigToBytes(ProofSR3),
BigToBytes(ProofSSPrime),
ProofSAttrs,
BigToBytes(Nonce),
EcpToProto(Nym),
BigToBytes(ProofSRNym)},
nil
}
// Ver verifies an idemix signature
// Disclosure steers which attributes it expects to be disclosed
// attributeValues[i] contains the desired attribute value for the i-th undisclosed attribute in Disclosure
func (sig *Signature) Ver(Disclosure []byte, ipk *IssuerPublicKey, msg []byte, attributeValues []*FP256BN.BIG) error {
HiddenIndices := hiddenIndices(Disclosure)
APrime := EcpFromProto(sig.GetAPrime())
ABar := EcpFromProto(sig.GetABar())
BPrime := EcpFromProto(sig.GetBPrime())
Nym := EcpFromProto(sig.GetNym())
ProofC := FP256BN.FromBytes(sig.GetProofC())
ProofSSk := FP256BN.FromBytes(sig.GetProofSSk())
ProofSE := FP256BN.FromBytes(sig.GetProofSE())
ProofSR2 := FP256BN.FromBytes(sig.GetProofSR2())
ProofSR3 := FP256BN.FromBytes(sig.GetProofSR3())
ProofSSPrime := FP256BN.FromBytes(sig.GetProofSSPrime())
ProofSRNym := FP256BN.FromBytes(sig.GetProofSRNym())
ProofSAttrs := make([]*FP256BN.BIG, len(sig.GetProofSAttrs()))
if len(sig.ProofSAttrs) != len(HiddenIndices) {
return errors.Errorf("signature invalid: incorrect amount of s-values for AttributeProofSpec")
}
for i, b := range sig.ProofSAttrs {
ProofSAttrs[i] = FP256BN.FromBytes(b)
}
Nonce := FP256BN.FromBytes(sig.GetNonce())
W := Ecp2FromProto(ipk.W)
HRand := EcpFromProto(ipk.HRand)
HSk := EcpFromProto(ipk.HSk)
if APrime.Is_infinity() {
return errors.Errorf("signature invalid: APrime = 1")
}
temp1 := FP256BN.Ate(W, APrime)
temp2 := FP256BN.Ate(GenG2, ABar)
temp2.Inverse()
temp1.Mul(temp2)
if !FP256BN.Fexp(temp1).Isunity() {
return errors.Errorf("signature invalid: APrime and ABar don't have the expected structure")
}
t1 := APrime.Mul2(ProofSE, HRand, ProofSR2)
temp := FP256BN.NewECP()
temp.Copy(ABar)
temp.Sub(BPrime)
t1.Sub(FP256BN.G1mul(temp, ProofC))
t2 := FP256BN.G1mul(HRand, ProofSSPrime)
t2.Add(BPrime.Mul2(ProofSR3, HSk, ProofSSk))
for i := 0; i < len(HiddenIndices)/2; i++ {
t2.Add(EcpFromProto(ipk.HAttrs[HiddenIndices[2*i]]).Mul2(ProofSAttrs[2*i], EcpFromProto(ipk.HAttrs[HiddenIndices[2*i+1]]), ProofSAttrs[2*i+1]))
}
if len(HiddenIndices)%2 != 0 {
t2.Add(FP256BN.G1mul(EcpFromProto(ipk.HAttrs[HiddenIndices[len(HiddenIndices)-1]]), ProofSAttrs[len(HiddenIndices)-1]))
}
temp = FP256BN.NewECP()
temp.Copy(GenG1)
for index, disclose := range Disclosure {
if disclose != 0 {
temp.Add(FP256BN.G1mul(EcpFromProto(ipk.HAttrs[index]), attributeValues[index]))
}
}
t2.Add(FP256BN.G1mul(temp, ProofC))
t3 := HSk.Mul2(ProofSSk, HRand, ProofSRNym)
t3.Sub(Nym.Mul(ProofC))
// proofData is the data being hashed, it consists of:
// the signature label
// 7 elements of G1 each taking 2*FieldBytes+1 bytes
// one bigint (hash of the issuer public key) of length FieldBytes
// disclosed attributes
// message that was signed
proofData := make([]byte, len([]byte(signLabel))+7*(2*FieldBytes+1)+FieldBytes+len(Disclosure)+len(msg))
index := 0
index = appendBytesString(proofData, index, signLabel)
index = appendBytesG1(proofData, index, t1)
index = appendBytesG1(proofData, index, t2)
index = appendBytesG1(proofData, index, t3)
index = appendBytesG1(proofData, index, APrime)
index = appendBytesG1(proofData, index, ABar)
index = appendBytesG1(proofData, index, BPrime)
index = appendBytesG1(proofData, index, Nym)
copy(proofData[index:], ipk.Hash)
index = index + FieldBytes
copy(proofData[index:], Disclosure)
index = index + len(Disclosure)
copy(proofData[index:], msg)
c := HashModOrder(proofData)
index = 0
proofData = proofData[:2*FieldBytes]
index = appendBytesBig(proofData, index, c)
index = appendBytesBig(proofData, index, Nonce)
if *ProofC != *HashModOrder(proofData) {
return errors.Errorf("signature invalid: zero-knowledge proof is invalid")
}
return nil
}