Skip to content

Commit 4e5618f

Browse files
author
Manu Drijvers
committed
[FAB-5672] Adds core idemix crypto package
This commit adds the core idemix crypto package, which supports the issuance of credentials and the creation of anonymous and pseudonymous signatures. Change-Id: I11321e54cca2820dbf5f63ba70254db6b07730f8 Signed-off-by: Manu Drijvers <mdr@zurich.ibm.com> Signed-off-by: Maria Dubovitskaya <mdu@zurich.ibm.com>
1 parent a4b4107 commit 4e5618f

File tree

8 files changed

+1494
-0
lines changed

8 files changed

+1494
-0
lines changed

idemix/credential.go

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
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+
amcl "github.com/manudrijvers/amcl/go"
11+
"github.com/pkg/errors"
12+
)
13+
14+
// Identity Mixer Credential is a list of attributes certified (signed) by the issuer
15+
// A credential also contains a user secret key blindly signed by the issuer
16+
// Without the secret key the credential cannot be used
17+
18+
// Credential issuance is an interactive protocol between a user and an issuer
19+
// The issuer takes its secret and public keys and user attribute values as input
20+
// The user takes the issuer public key and user secret as input
21+
// The issuance protocol consists of the following steps:
22+
// 1) The issuer sends a random nonce to the user
23+
// 2) The user creates a Credential Request using the public key of the issuer, user secret, and the nonce as input
24+
// The request consists of a commitment to the user secret (can be seen as a public key) and a zero-knowledge proof
25+
// of knowledge of the user secret key
26+
// The user sends the credential request to the issuer
27+
// 3) The issuer verifies the credential request by verifying the zero-knowledge proof
28+
// If the request is valid, the issuer issues a credential to the user by signing the commitment to the secret key
29+
// together with the attribute values and sends the credential back to the user
30+
// 4) The user verifies the issuer's signature and stores the credential that consists of
31+
// the signature value, a randomness used to create the signature, the user secret, and the attribute values
32+
33+
// NewCredential issues a new credential, which is the last step of the interactive issuance protocol
34+
// All attribute values are added by the issuer at this step and then signed together with a commitment to
35+
// the user's secret key from a credential request
36+
func NewCredential(key *IssuerKey, m *CredRequest, attrs []*amcl.BIG, rng *amcl.RAND) (*Credential, error) {
37+
// check the credential request that contains
38+
err := m.Check(key.IPk)
39+
if err != nil {
40+
return nil, err
41+
}
42+
43+
if len(attrs) != len(key.IPk.AttributeNames) {
44+
return nil, errors.Errorf("incorrect number of attribute values passed")
45+
}
46+
47+
// Place a BBS+ signature on the user key and the attribute values
48+
// (For BBS+, see e.g. "Constant-Size Dynamic k-TAA" by Man Ho Au, Willy Susilo, Yi Mu)
49+
E := RandModOrder(rng)
50+
S := RandModOrder(rng)
51+
52+
B := amcl.NewECP()
53+
B.Copy(GenG1)
54+
Nym := EcpFromProto(m.Nym)
55+
B.Add(Nym)
56+
B.Add(EcpFromProto(key.IPk.HRand).Mul(S))
57+
58+
// Use Mul2 instead of Mul as much as possible
59+
for i := 0; i < len(attrs)/2; i++ {
60+
B.Add(EcpFromProto(key.IPk.HAttrs[2*i]).Mul2(attrs[2*i], EcpFromProto(key.IPk.HAttrs[2*i+1]), attrs[2*i+1]))
61+
}
62+
if len(attrs)%2 != 0 {
63+
B.Add(EcpFromProto(key.IPk.HAttrs[len(attrs)-1]).Mul(attrs[len(attrs)-1]))
64+
}
65+
66+
Exp := amcl.Modadd(amcl.FromBytes(key.GetISk()), E, GroupOrder)
67+
Exp.Invmodp(GroupOrder)
68+
A := B.Mul(Exp)
69+
70+
CredAttrs := make([][]byte, len(attrs))
71+
for index, attribute := range attrs {
72+
CredAttrs[index] = BigToBytes(attribute)
73+
}
74+
75+
return &Credential{
76+
EcpToProto(A),
77+
EcpToProto(B),
78+
BigToBytes(E),
79+
BigToBytes(S),
80+
CredAttrs}, nil
81+
}
82+
83+
// Complete completes the credential by updating it with the randomness used to generate CredRequest
84+
func (cred *Credential) Complete(credS1 *amcl.BIG) {
85+
cred.S = BigToBytes(amcl.Modadd(amcl.FromBytes(cred.S), credS1, GroupOrder))
86+
}
87+
88+
// Ver cryptographically verifies the credential by verifying the signature
89+
// on the attribute values and user's secret key
90+
func (cred *Credential) Ver(sk *amcl.BIG, ipk *IssuerPublicKey) error {
91+
92+
// parse the credential
93+
A := EcpFromProto(cred.GetA())
94+
B := EcpFromProto(cred.GetB())
95+
E := amcl.FromBytes(cred.GetE())
96+
S := amcl.FromBytes(cred.GetS())
97+
98+
// verify that all attribute values are present
99+
for i := 0; i < len(cred.GetAttrs()); i++ {
100+
if cred.Attrs[i] == nil {
101+
return errors.Errorf("credential has no value for attribute %s", ipk.AttributeNames[i])
102+
}
103+
}
104+
105+
// verify cryptographic signature on the attributes and the user secret key
106+
BPrime := amcl.NewECP()
107+
BPrime.Copy(GenG1)
108+
BPrime.Add(EcpFromProto(ipk.HSk).Mul2(sk, EcpFromProto(ipk.HRand), S))
109+
for i := 0; i < len(cred.Attrs)/2; i++ {
110+
BPrime.Add(EcpFromProto(ipk.HAttrs[2*i]).Mul2(amcl.FromBytes(cred.Attrs[2*i]), EcpFromProto(ipk.HAttrs[2*i+1]), amcl.FromBytes(cred.Attrs[2*i+1])))
111+
}
112+
if len(cred.Attrs)%2 != 0 {
113+
BPrime.Add(EcpFromProto(ipk.HAttrs[len(cred.Attrs)-1]).Mul(amcl.FromBytes(cred.Attrs[len(cred.Attrs)-1])))
114+
}
115+
if !B.Equals(BPrime) {
116+
return errors.Errorf("b-value from credential does not match the attribute values")
117+
}
118+
119+
a := GenG2.Mul(E)
120+
a.Add(Ecp2FromProto(ipk.W))
121+
122+
if !amcl.Fexp(amcl.Ate(a, A)).Equals(amcl.Fexp(amcl.Ate(GenG2, B))) {
123+
return errors.Errorf("credential is not cryptographically valid")
124+
}
125+
return nil
126+
}

idemix/credrequest.go

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
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+
amcl "github.com/manudrijvers/amcl/go"
11+
"github.com/pkg/errors"
12+
)
13+
14+
// credRequestLabel is the label used in zero-knowledge proof (ZKP) to identify that this ZKP is a credential request
15+
const credRequestLabel = "credRequest"
16+
17+
// Credential issuance is an interactive protocol between a user and an issuer
18+
// The issuer takes its secret and public keys and user attribute values as input
19+
// The user takes the issuer public key and user secret as input
20+
// The issuance protocol consists of the following steps:
21+
// 1) The issuer sends a random nonce to the user
22+
// 2) The user creates a Credential Request using the public key of the issuer, user secret, and the nonce as input
23+
// The request consists of a commitment to the user secret (can be seen as a public key) and a zero-knowledge proof
24+
// of knowledge of the user secret key
25+
// The user sends the credential request to the issuer
26+
// 3) The issuer verifies the credential request by verifying the zero-knowledge proof
27+
// If the request is valid, the issuer issues a credential to the user by signing the commitment to the secret key
28+
// together with the attribute values and sends the credential back to the user
29+
// 4) The user verifies the issuer's signature and stores the credential that consists of
30+
// the signature value, a randomness used to create the signature, the user secret, and the attribute values
31+
32+
// NewCredRequest creates a new Credential Request, the first message of the interactive credential issuance protocol (from user to issuer)
33+
func NewCredRequest(sk *amcl.BIG, credS1 *amcl.BIG, IssuerNonce *amcl.BIG, ipk *IssuerPublicKey, rng *amcl.RAND) *CredRequest {
34+
HSk := EcpFromProto(ipk.HSk)
35+
HRand := EcpFromProto(ipk.HRand)
36+
Nym := HSk.Mul2(sk, HRand, credS1)
37+
38+
// Create ZK Proof
39+
rSk := RandModOrder(rng)
40+
rRand := RandModOrder(rng)
41+
t := HSk.Mul2(rSk, HRand, rRand)
42+
43+
// proofData is the data being hashed, it consists of:
44+
// the credential request label
45+
// 3 elements of G1 each taking 2*FieldBytes+1 bytes
46+
// hash of the issuer public key of length FieldBytes
47+
// issuer nonce of length FieldBytes
48+
proofData := make([]byte, len([]byte(credRequestLabel))+3*(2*FieldBytes+1)+2*FieldBytes)
49+
index := 0
50+
index = appendBytesString(proofData, index, credRequestLabel)
51+
index = appendBytesG1(proofData, index, t)
52+
index = appendBytesG1(proofData, index, HSk)
53+
index = appendBytesG1(proofData, index, Nym)
54+
index = appendBytesBig(proofData, index, IssuerNonce)
55+
copy(proofData[index:], ipk.Hash)
56+
57+
proofC := HashModOrder(proofData)
58+
proofS1 := amcl.Modadd(amcl.Modmul(proofC, sk, GroupOrder), rSk, GroupOrder)
59+
proofS2 := amcl.Modadd(amcl.Modmul(proofC, credS1, GroupOrder), rRand, GroupOrder)
60+
61+
return &CredRequest{EcpToProto(Nym), BigToBytes(IssuerNonce), BigToBytes(proofC), BigToBytes(proofS1), BigToBytes(proofS2)}
62+
}
63+
64+
// Check cryptographically verifies the credential request
65+
func (m *CredRequest) Check(ipk *IssuerPublicKey) error {
66+
Nym := EcpFromProto(m.GetNym())
67+
IssuerNonce := amcl.FromBytes(m.GetIssuerNonce())
68+
ProofC := amcl.FromBytes(m.GetProofC())
69+
ProofS1 := amcl.FromBytes(m.GetProofS1())
70+
ProofS2 := amcl.FromBytes(m.GetProofS2())
71+
72+
HSk := EcpFromProto(ipk.HSk)
73+
HRand := EcpFromProto(ipk.HRand)
74+
75+
if Nym == nil || IssuerNonce == nil || ProofC == nil || ProofS1 == nil || ProofS2 == nil {
76+
return errors.Errorf("one of the proof values is undefined")
77+
}
78+
79+
t := HSk.Mul2(ProofS1, HRand, ProofS2)
80+
t.Sub(Nym.Mul(ProofC))
81+
82+
// proofData is the data being hashed, it consists of:
83+
// the credential request label
84+
// 3 elements of G1 each taking 2*FieldBytes+1 bytes
85+
// hash of the issuer public key of length FieldBytes
86+
// issuer nonce of length FieldBytes
87+
proofData := make([]byte, len([]byte(credRequestLabel))+3*(2*FieldBytes+1)+2*FieldBytes)
88+
index := 0
89+
index = appendBytesString(proofData, index, credRequestLabel)
90+
index = appendBytesG1(proofData, index, t)
91+
index = appendBytesG1(proofData, index, HSk)
92+
index = appendBytesG1(proofData, index, Nym)
93+
index = appendBytesBig(proofData, index, IssuerNonce)
94+
copy(proofData[index:], ipk.Hash)
95+
96+
if !ProofC.Equals(HashModOrder(proofData)) {
97+
return errors.Errorf("zero knowledge proof is invalid")
98+
}
99+
100+
return nil
101+
}

0 commit comments

Comments
 (0)