Skip to content

Commit

Permalink
[FAB-5672] Adds core idemix crypto package
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Manu Drijvers committed Aug 13, 2017
1 parent a4b4107 commit 4e5618f
Show file tree
Hide file tree
Showing 8 changed files with 1,494 additions and 0 deletions.
126 changes: 126 additions & 0 deletions idemix/credential.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package idemix

import (
amcl "github.com/manudrijvers/amcl/go"
"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 []*amcl.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)
E := RandModOrder(rng)
S := RandModOrder(rng)

B := amcl.NewECP()
B.Copy(GenG1)
Nym := EcpFromProto(m.Nym)
B.Add(Nym)
B.Add(EcpFromProto(key.IPk.HRand).Mul(S))

// Use Mul2 instead of Mul as much as possible
for i := 0; i < len(attrs)/2; i++ {
B.Add(EcpFromProto(key.IPk.HAttrs[2*i]).Mul2(attrs[2*i], EcpFromProto(key.IPk.HAttrs[2*i+1]), attrs[2*i+1]))
}
if len(attrs)%2 != 0 {
B.Add(EcpFromProto(key.IPk.HAttrs[len(attrs)-1]).Mul(attrs[len(attrs)-1]))
}

Exp := amcl.Modadd(amcl.FromBytes(key.GetISk()), E, GroupOrder)
Exp.Invmodp(GroupOrder)
A := B.Mul(Exp)

CredAttrs := make([][]byte, len(attrs))
for index, attribute := range attrs {
CredAttrs[index] = BigToBytes(attribute)
}

return &Credential{
EcpToProto(A),
EcpToProto(B),
BigToBytes(E),
BigToBytes(S),
CredAttrs}, nil
}

// Complete completes the credential by updating it with the randomness used to generate CredRequest
func (cred *Credential) Complete(credS1 *amcl.BIG) {
cred.S = BigToBytes(amcl.Modadd(amcl.FromBytes(cred.S), credS1, GroupOrder))
}

// Ver cryptographically verifies the credential by verifying the signature
// on the attribute values and user's secret key
func (cred *Credential) Ver(sk *amcl.BIG, ipk *IssuerPublicKey) error {

// parse the credential
A := EcpFromProto(cred.GetA())
B := EcpFromProto(cred.GetB())
E := amcl.FromBytes(cred.GetE())
S := amcl.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 := amcl.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(amcl.FromBytes(cred.Attrs[2*i]), EcpFromProto(ipk.HAttrs[2*i+1]), amcl.FromBytes(cred.Attrs[2*i+1])))
}
if len(cred.Attrs)%2 != 0 {
BPrime.Add(EcpFromProto(ipk.HAttrs[len(cred.Attrs)-1]).Mul(amcl.FromBytes(cred.Attrs[len(cred.Attrs)-1])))
}
if !B.Equals(BPrime) {
return errors.Errorf("b-value from credential does not match the attribute values")
}

a := GenG2.Mul(E)
a.Add(Ecp2FromProto(ipk.W))

if !amcl.Fexp(amcl.Ate(a, A)).Equals(amcl.Fexp(amcl.Ate(GenG2, B))) {
return errors.Errorf("credential is not cryptographically valid")
}
return nil
}
101 changes: 101 additions & 0 deletions idemix/credrequest.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package idemix

import (
amcl "github.com/manudrijvers/amcl/go"
"github.com/pkg/errors"
)

// credRequestLabel is the label used in zero-knowledge proof (ZKP) to identify that this ZKP is a credential request
const credRequestLabel = "credRequest"

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

// NewCredRequest creates a new Credential Request, the first message of the interactive credential issuance protocol (from user to issuer)
func NewCredRequest(sk *amcl.BIG, credS1 *amcl.BIG, IssuerNonce *amcl.BIG, ipk *IssuerPublicKey, rng *amcl.RAND) *CredRequest {
HSk := EcpFromProto(ipk.HSk)
HRand := EcpFromProto(ipk.HRand)
Nym := HSk.Mul2(sk, HRand, credS1)

// Create ZK Proof
rSk := RandModOrder(rng)
rRand := RandModOrder(rng)
t := HSk.Mul2(rSk, HRand, rRand)

// proofData is the data being hashed, it consists of:
// the credential request label
// 3 elements of G1 each taking 2*FieldBytes+1 bytes
// hash of the issuer public key of length FieldBytes
// issuer nonce of length FieldBytes
proofData := make([]byte, len([]byte(credRequestLabel))+3*(2*FieldBytes+1)+2*FieldBytes)
index := 0
index = appendBytesString(proofData, index, credRequestLabel)
index = appendBytesG1(proofData, index, t)
index = appendBytesG1(proofData, index, HSk)
index = appendBytesG1(proofData, index, Nym)
index = appendBytesBig(proofData, index, IssuerNonce)
copy(proofData[index:], ipk.Hash)

proofC := HashModOrder(proofData)
proofS1 := amcl.Modadd(amcl.Modmul(proofC, sk, GroupOrder), rSk, GroupOrder)
proofS2 := amcl.Modadd(amcl.Modmul(proofC, credS1, GroupOrder), rRand, GroupOrder)

return &CredRequest{EcpToProto(Nym), BigToBytes(IssuerNonce), BigToBytes(proofC), BigToBytes(proofS1), BigToBytes(proofS2)}
}

// Check cryptographically verifies the credential request
func (m *CredRequest) Check(ipk *IssuerPublicKey) error {
Nym := EcpFromProto(m.GetNym())
IssuerNonce := amcl.FromBytes(m.GetIssuerNonce())
ProofC := amcl.FromBytes(m.GetProofC())
ProofS1 := amcl.FromBytes(m.GetProofS1())
ProofS2 := amcl.FromBytes(m.GetProofS2())

HSk := EcpFromProto(ipk.HSk)
HRand := EcpFromProto(ipk.HRand)

if Nym == nil || IssuerNonce == nil || ProofC == nil || ProofS1 == nil || ProofS2 == nil {
return errors.Errorf("one of the proof values is undefined")
}

t := HSk.Mul2(ProofS1, HRand, ProofS2)
t.Sub(Nym.Mul(ProofC))

// proofData is the data being hashed, it consists of:
// the credential request label
// 3 elements of G1 each taking 2*FieldBytes+1 bytes
// hash of the issuer public key of length FieldBytes
// issuer nonce of length FieldBytes
proofData := make([]byte, len([]byte(credRequestLabel))+3*(2*FieldBytes+1)+2*FieldBytes)
index := 0
index = appendBytesString(proofData, index, credRequestLabel)
index = appendBytesG1(proofData, index, t)
index = appendBytesG1(proofData, index, HSk)
index = appendBytesG1(proofData, index, Nym)
index = appendBytesBig(proofData, index, IssuerNonce)
copy(proofData[index:], ipk.Hash)

if !ProofC.Equals(HashModOrder(proofData)) {
return errors.Errorf("zero knowledge proof is invalid")
}

return nil
}
Loading

0 comments on commit 4e5618f

Please sign in to comment.