forked from hyperledger/fabric
-
Notifications
You must be signed in to change notification settings - Fork 3
/
credential.go
160 lines (137 loc) · 5.73 KB
/
credential.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
/*
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"
)
// Identity Mixer Credential is a list of attributes certified (signed) by the issuer
// A credential also contains a user secret key blindly signed by the issuer
// Without the secret key the credential cannot be used
// Credential issuance is an interactive protocol between a user and an issuer
// The issuer takes its secret and public keys and user attribute values as input
// The user takes the issuer public key and user secret as input
// The issuance protocol consists of the following steps:
// 1) The issuer sends a random nonce to the user
// 2) The user creates a Credential Request using the public key of the issuer, user secret, and the nonce as input
// The request consists of a commitment to the user secret (can be seen as a public key) and a zero-knowledge proof
// of knowledge of the user secret key
// The user sends the credential request to the issuer
// 3) The issuer verifies the credential request by verifying the zero-knowledge proof
// If the request is valid, the issuer issues a credential to the user by signing the commitment to the secret key
// together with the attribute values and sends the credential back to the user
// 4) The user verifies the issuer's signature and stores the credential that consists of
// the signature value, a randomness used to create the signature, the user secret, and the attribute values
// NewCredential issues a new credential, which is the last step of the interactive issuance protocol
// All attribute values are added by the issuer at this step and then signed together with a commitment to
// the user's secret key from a credential request
func NewCredential(key *IssuerKey, m *CredRequest, attrs []*FP256BN.BIG, rng *amcl.RAND) (*Credential, error) {
// check the credential request that contains
err := m.Check(key.Ipk)
if err != nil {
return nil, err
}
if len(attrs) != len(key.Ipk.AttributeNames) {
return nil, errors.Errorf("incorrect number of attribute values passed")
}
// Place a BBS+ signature on the user key and the attribute values
// (For BBS+, see e.g. "Constant-Size Dynamic k-TAA" by Man Ho Au, Willy Susilo, Yi Mu)
// or http://eprint.iacr.org/2016/663.pdf, Sec. 4.3.
// For a credential, a BBS+ signature consists of the following three elements:
// 1. E, random value in the proper group
// 2. S, random value in the proper group
// 3. A as B^Exp where B=g_1 \cdot h_r^s \cdot h_sk^sk \cdot \prod_{i=1}^L h_i^{m_i} and Exp = \frac{1}{e+x}
// Notice that:
// h_r is h_0 in http://eprint.iacr.org/2016/663.pdf, Sec. 4.3.
// Pick randomness E and S
E := RandModOrder(rng)
S := RandModOrder(rng)
// Set B as g_1 \cdot h_r^s \cdot h_sk^sk \cdot \prod_{i=1}^L h_i^{m_i} and Exp = \frac{1}{e+x}
B := FP256BN.NewECP()
B.Copy(GenG1) // g_1
Nym := EcpFromProto(m.Nym)
B.Add(Nym) // in this case, recall Nym=h_sk^sk
B.Add(EcpFromProto(key.Ipk.HRand).Mul(S)) // h_r^s
// Append attributes
// Use Mul2 instead of Mul as much as possible for efficiency reasones
for i := 0; i < len(attrs)/2; i++ {
B.Add(
// Add two attributes in one shot
EcpFromProto(key.Ipk.HAttrs[2*i]).Mul2(
attrs[2*i],
EcpFromProto(key.Ipk.HAttrs[2*i+1]),
attrs[2*i+1],
),
)
}
// Check for residue in case len(attrs)%2 is odd
if len(attrs)%2 != 0 {
B.Add(EcpFromProto(key.Ipk.HAttrs[len(attrs)-1]).Mul(attrs[len(attrs)-1]))
}
// Set Exp as \frac{1}{e+x}
Exp := Modadd(FP256BN.FromBytes(key.GetIsk()), E, GroupOrder)
Exp.Invmodp(GroupOrder)
// Finalise A as B^Exp
A := B.Mul(Exp)
// The signature is now generated.
// Notice that here we release also B, this does not harm security cause
// it can be compute publicly from the BBS+ signature itself.
CredAttrs := make([][]byte, len(attrs))
for index, attribute := range attrs {
CredAttrs[index] = BigToBytes(attribute)
}
return &Credential{
A: EcpToProto(A),
B: EcpToProto(B),
E: BigToBytes(E),
S: BigToBytes(S),
Attrs: CredAttrs}, nil
}
// Ver cryptographically verifies the credential by verifying the signature
// on the attribute values and user's secret key
func (cred *Credential) Ver(sk *FP256BN.BIG, ipk *IssuerPublicKey) error {
// Validate Input
// - parse the credential
A := EcpFromProto(cred.GetA())
B := EcpFromProto(cred.GetB())
E := FP256BN.FromBytes(cred.GetE())
S := FP256BN.FromBytes(cred.GetS())
// - verify that all attribute values are present
for i := 0; i < len(cred.GetAttrs()); i++ {
if cred.Attrs[i] == nil {
return errors.Errorf("credential has no value for attribute %s", ipk.AttributeNames[i])
}
}
// - verify cryptographic signature on the attributes and the user secret key
BPrime := FP256BN.NewECP()
BPrime.Copy(GenG1)
BPrime.Add(EcpFromProto(ipk.HSk).Mul2(sk, EcpFromProto(ipk.HRand), S))
for i := 0; i < len(cred.Attrs)/2; i++ {
BPrime.Add(
EcpFromProto(ipk.HAttrs[2*i]).Mul2(
FP256BN.FromBytes(cred.Attrs[2*i]),
EcpFromProto(ipk.HAttrs[2*i+1]),
FP256BN.FromBytes(cred.Attrs[2*i+1]),
),
)
}
if len(cred.Attrs)%2 != 0 {
BPrime.Add(EcpFromProto(ipk.HAttrs[len(cred.Attrs)-1]).Mul(FP256BN.FromBytes(cred.Attrs[len(cred.Attrs)-1])))
}
if !B.Equals(BPrime) {
return errors.Errorf("b-value from credential does not match the attribute values")
}
// Verify BBS+ signature. Namely: e(w \cdot g_2^e, A) =? e(g_2, B)
a := GenG2.Mul(E)
a.Add(Ecp2FromProto(ipk.W))
a.Affine()
left := FP256BN.Fexp(FP256BN.Ate(a, A))
right := FP256BN.Fexp(FP256BN.Ate(GenG2, B))
if !left.Equals(right) {
return errors.Errorf("credential is not cryptographically valid")
}
return nil
}