Skip to content

Commit

Permalink
[FAB-8810] Extend idemix cred with enrollment IDs
Browse files Browse the repository at this point in the history
This commit extends the idemix certificates with attributes
enrollment id and revocation handle, to allow for auditing
and revocation features that are planned.

Change-Id: I921de0a383d76132fe07d49f2179161a40f97810
Signed-off-by: Manu Drijvers <mdr@zurich.ibm.com>
  • Loading branch information
Manu Drijvers committed May 1, 2018
1 parent 9015790 commit 8cbae04
Show file tree
Hide file tree
Showing 25 changed files with 147 additions and 87 deletions.
33 changes: 21 additions & 12 deletions common/tools/idemixgen/idemixca/idemixca.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,19 @@ import (
"github.com/pkg/errors"
)

// GenerateIssuerKey invokes Idemix library to generate an issuer (CA) signing key pair
// currently two attributes are supported by the issuer:
// GenerateIssuerKey invokes Idemix library to generate an issuer (CA) signing key pair.
// Currently four attributes are supported by the issuer:
// AttributeNameOU is the organization unit name
// AttributeNameRole is the role (member or admin) name
// Generated keys are serialized to bytes
// AttributeNameEnrollmentId is the enrollment id
// AttributeNameRevocationHandle contains the revocation handle, which can be used to revoke this user
// Generated keys are serialized to bytes.
func GenerateIssuerKey() ([]byte, []byte, error) {
rng, err := idemix.GetRand()
if err != nil {
return nil, nil, err
}
AttributeNames := []string{msp.AttributeNameOU, msp.AttributeNameRole}
AttributeNames := []string{msp.AttributeNameOU, msp.AttributeNameRole, msp.AttributeNameEnrollmentId, msp.AttributeNameRevocationHandle}
key, err := idemix.NewIssuerKey(AttributeNames, rng)
if err != nil {
return nil, nil, errors.WithMessage(err, "cannot generate CA key")
Expand All @@ -35,29 +37,35 @@ func GenerateIssuerKey() ([]byte, []byte, error) {
return key.ISk, ipkSerialized, err
}

// GenerateMSPConfig creates a new MSP config
// GenerateMSPConfig creates a new MSP config.
// If the new MSP config contains a signer then
// it generates a fresh user secret and issues a credential
// with two attributes (described above)
// using the CA's key pair from the file
// with four attributes (described above)
// using the CA's key pair from the file.
// If the new MSP config does not contain a signer
// (meaning it is used only for verification)
// then only a public key of the CA (issuer) is added to the MSP config (besides the name)
func GenerateSignerConfig(isAdmin bool, ouString string, key *idemix.IssuerKey) ([]byte, error) {
attrs := make([]*FP256BN.BIG, 2)
// then only a public key of the CA (issuer) is added to the MSP config (besides the name).
func GenerateSignerConfig(isAdmin bool, ouString string, enrollmentId string, revocationHandle int, key *idemix.IssuerKey) ([]byte, error) {
attrs := make([]*FP256BN.BIG, 4)

if ouString == "" {
return nil, errors.Errorf("the OU attribute value is empty")
}

if enrollmentId == "" {
return nil, errors.Errorf("the enrollment id value is empty")
}

role := m.MSPRole_MEMBER

if isAdmin {
role = m.MSPRole_ADMIN
}

attrs[0] = idemix.HashModOrder([]byte(ouString))
attrs[1] = FP256BN.NewBIGint(int(role))
attrs[msp.AttributeIndexOU] = idemix.HashModOrder([]byte(ouString))
attrs[msp.AttributeIndexRole] = FP256BN.NewBIGint(int(role))
attrs[msp.AttributeIndexEnrollmentId] = idemix.HashModOrder([]byte(enrollmentId))
attrs[msp.AttributeIndexRevocationHandle] = FP256BN.NewBIGint(revocationHandle)

rng, err := idemix.GetRand()
if err != nil {
Expand All @@ -81,6 +89,7 @@ func GenerateSignerConfig(isAdmin bool, ouString string, key *idemix.IssuerKey)
idemix.BigToBytes(sk),
ouString,
isAdmin,
enrollmentId,
}
return proto.Marshal(signer)
}
9 changes: 6 additions & 3 deletions common/tools/idemixgen/idemixca/idemixca_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,13 @@ func TestIdemixCa(t *testing.T) {

key := &idemix.IssuerKey{isk, ipk}

conf, err := GenerateSignerConfig(false, "OU1", key)
conf, err := GenerateSignerConfig(false, "OU1", "enrollmentid1", 1, key)
assert.NoError(t, err)
cleanupSigner()
assert.NoError(t, writeSignerToFile(conf))
assert.NoError(t, setupMSP())

conf, err = GenerateSignerConfig(true, "OU1", key)
conf, err = GenerateSignerConfig(true, "OU1", "enrollmentid2", 1234, key)
assert.NoError(t, err)
cleanupSigner()
assert.NoError(t, writeSignerToFile(conf))
Expand All @@ -52,8 +52,11 @@ func TestIdemixCa(t *testing.T) {
cleanupVerifier()
assert.Error(t, setupMSP())

_, err = GenerateSignerConfig(true, "", key)
_, err = GenerateSignerConfig(true, "", "enrollmentid", 1, key)
assert.EqualError(t, err, "the OU attribute value is empty")

_, err = GenerateSignerConfig(true, "OU1", "", 1, key)
assert.EqualError(t, err, "the enrollment id value is empty")
}

func cleanup() error {
Expand Down
12 changes: 7 additions & 5 deletions common/tools/idemixgen/idemixgen.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,12 @@ const (
var (
app = kingpin.New("idemixgen", "Utility for generating key material to be used with the Identity Mixer MSP in Hyperledger Fabric")

genIssuerKey = app.Command("ca-keygen", "Generate CA key material")
genSignerConfig = app.Command("signerconfig", "Generate a default signer for this Idemix MSP")
genCredOU = genSignerConfig.Flag("org-unit", "The Organizational Unit of the default signer").Short('u').String()
genCredIsAdmin = genSignerConfig.Flag("admin", "Make the default signer admin").Short('a').Bool()
genIssuerKey = app.Command("ca-keygen", "Generate CA key material")
genSignerConfig = app.Command("signerconfig", "Generate a default signer for this Idemix MSP")
genCredOU = genSignerConfig.Flag("org-unit", "The Organizational Unit of the default signer").Short('u').String()
genCredIsAdmin = genSignerConfig.Flag("admin", "Make the default signer admin").Short('a').Bool()
genCredEnrollmentId = genSignerConfig.Flag("enrollmentId", "The enrollment id of the default signer").Short('e').String()
genCredRevocationHandle = genSignerConfig.Flag("revocationHandle", "The handle used to revoke this signer").Short('r').Int()

version = app.Command("version", "Show version information")
)
Expand Down Expand Up @@ -67,7 +69,7 @@ func main() {
writeFile(filepath.Join(msp.IdemixConfigDirMsp, msp.IdemixConfigFileIssuerPublicKey), ipk)

case genSignerConfig.FullCommand():
config, err := idemixca.GenerateSignerConfig(*genCredIsAdmin, *genCredOU, readIssuerKey())
config, err := idemixca.GenerateSignerConfig(*genCredIsAdmin, *genCredOU, *genCredEnrollmentId, *genCredRevocationHandle, readIssuerKey())
handleError(err)

path := msp.IdemixConfigDirUser
Expand Down
67 changes: 50 additions & 17 deletions msp/idemixmsp.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,38 @@ import (
"github.com/pkg/errors"
)

const (
// AttributeIndexOU contains the index of the OU attribute in the idemix credential attributes
AttributeIndexOU = iota

// AttributeIndexRole contains the index of the Role attribute in the idemix credential attributes
AttributeIndexRole

// AttributeIndexEnrollmentId contains the index of the Enrollment ID attribute in the idemix credential attributes
AttributeIndexEnrollmentId

// AttributeIndexRevocationHandle contains the index of the Revocation Handle attribute in the idemix credential attributes
AttributeIndexRevocationHandle
)

const (
// AttributeNameOU is the attribute name of the Organization Unit attribute
AttributeNameOU = "OU"

// AttributeNameRole is the attribute name of the Role attribute
AttributeNameRole = "Role"

// AttributeNameEnrollmentId is the attribute name of the Enrollment ID attribute
AttributeNameEnrollmentId = "EnrollmentID"

// AttributeNameRevocationHandle is the attribute name of the revocation handle attribute
AttributeNameRevocationHandle = "RevocationHandle"
)

// discloseFlags will be passed to the idemix signing and verification routines.
// It informs idemix to disclose both attributes (OU and Role) when signing.
var discloseFlags = []byte{1, 1}
// It informs idemix to disclose both attributes (OU and Role) when signing,
// while hiding attributes EnrollmentID and RevocationHandle.
var discloseFlags = []byte{1, 1, 0, 0}

type idemixmsp struct {
ipk *idemix.IssuerPublicKey
Expand Down Expand Up @@ -77,8 +98,12 @@ func (msp *idemixmsp) Setup(conf1 *m.MSPConfig) error {
return errors.WithMessage(err, "setting the hash of the issuer public key failed")
}

if len(ipk.AttributeNames) < 2 || ipk.AttributeNames[0] != AttributeNameOU || ipk.AttributeNames[1] != AttributeNameRole {
return errors.Errorf("ipk must have have attributes OU and Role")
if len(ipk.AttributeNames) < 4 ||
ipk.AttributeNames[AttributeIndexOU] != AttributeNameOU ||
ipk.AttributeNames[AttributeIndexRole] != AttributeNameRole ||
ipk.AttributeNames[AttributeIndexEnrollmentId] != AttributeNameEnrollmentId ||
ipk.AttributeNames[AttributeIndexRevocationHandle] != AttributeNameRevocationHandle {
return errors.Errorf("issuer public key must have have attributes OU, Role, EnrollmentId, and RevocationHandle")
}

err = ipk.Check()
Expand Down Expand Up @@ -124,22 +149,29 @@ func (msp *idemixmsp) Setup(conf1 *m.MSPConfig) error {
CertifiersIdentifier: ipk.Hash,
}

// Check if credential contains the right amount of attribute values (Role and OU)
if len(cred.Attrs) != 2 {
return errors.Errorf("Credential contains %d attribute values, but expected 2", len(cred.Attrs))
enrollmentId := conf.Signer.EnrollmentId

// Check if credential contains the right amount of attribute values (Role, OU, EnrollmentId, RevocationHandle)
if len(cred.Attrs) != 4 {
return errors.Errorf("Credential contains %d attribute values, but expected 4", len(cred.Attrs))
}

// Check if credential contains the correct OU attribute value
ouBytes := []byte(conf.Signer.OrganizationalUnitIdentifier)
if !bytes.Equal(idemix.BigToBytes(idemix.HashModOrder(ouBytes)), cred.Attrs[0]) {
if !bytes.Equal(idemix.BigToBytes(idemix.HashModOrder(ouBytes)), cred.Attrs[AttributeIndexOU]) {
return errors.New("Credential does not contain the correct OU attribute value")
}

// Check if credential contains the correct OU attribute value
if !bytes.Equal(idemix.BigToBytes(FP256BN.NewBIGint(int(role.Role))), cred.Attrs[1]) {
// Check if credential contains the correct Role attribute value
if !bytes.Equal(idemix.BigToBytes(FP256BN.NewBIGint(int(role.Role))), cred.Attrs[AttributeIndexRole]) {
return errors.New("Credential does not contain the correct Role attribute value")
}

// Check if credential contains the correct Enrollment ID attribute value
if !bytes.Equal(idemix.BigToBytes(idemix.HashModOrder([]byte(enrollmentId))), cred.Attrs[AttributeIndexEnrollmentId]) {
return errors.New("Credential does not contain the correct enrollment id attribute value")
}

// Verify that the credential is cryptographically valid
err = cred.Ver(sk, msp.ipk)
if err != nil {
Expand All @@ -153,7 +185,7 @@ func (msp *idemixmsp) Setup(conf1 *m.MSPConfig) error {
}

// Set up default signer
msp.signer = &idemixSigningIdentity{newIdemixIdentity(msp, Nym, role, ou, proof), rng, cred, sk, RandNym}
msp.signer = &idemixSigningIdentity{newIdemixIdentity(msp, Nym, role, ou, proof), rng, cred, sk, RandNym, enrollmentId}

return nil
}
Expand Down Expand Up @@ -295,8 +327,8 @@ func (msp *idemixmsp) SatisfiesPrincipal(id Identity, principal *m.MSPPrincipal)
default:
return errors.Errorf("invalid MSP role type %d", int32(mspRole.Role))
}
// in this case we have to serialize this instance
// and compare it byte-by-byte with Principal
// in this case we have to serialize this instance
// and compare it byte-by-byte with Principal
case m.MSPPrincipal_IDENTITY:
mspLogger.Debugf("Checking if identity satisfies IDENTITY principal")
idBytes, err := id.Serialize()
Expand Down Expand Up @@ -473,10 +505,11 @@ func (id *idemixidentity) Serialize() ([]byte, error) {

type idemixSigningIdentity struct {
*idemixidentity
rng *amcl.RAND
Cred *idemix.Credential
Sk *FP256BN.BIG
RandNym *FP256BN.BIG
rng *amcl.RAND
Cred *idemix.Credential
Sk *FP256BN.BIG
RandNym *FP256BN.BIG
enrollmentId string
}

func (id *idemixSigningIdentity) Sign(msg []byte) ([]byte, error) {
Expand Down
Binary file modified msp/testdata/idemix/MSP1OU1/ca/IssuerPublicKey
Binary file not shown.
Binary file modified msp/testdata/idemix/MSP1OU1/ca/IssuerSecretKey
Binary file not shown.
Binary file modified msp/testdata/idemix/MSP1OU1/msp/IssuerPublicKey
Binary file not shown.
Binary file modified msp/testdata/idemix/MSP1OU1/user/SignerConfig
Binary file not shown.
Binary file modified msp/testdata/idemix/MSP1OU1Admin/ca/IssuerPublicKey
Binary file not shown.
Binary file modified msp/testdata/idemix/MSP1OU1Admin/ca/IssuerSecretKey
Binary file not shown.
Binary file modified msp/testdata/idemix/MSP1OU1Admin/msp/IssuerPublicKey
Binary file not shown.
Binary file modified msp/testdata/idemix/MSP1OU1Admin/user/SignerConfig
Binary file not shown.
Binary file modified msp/testdata/idemix/MSP1OU2/ca/IssuerPublicKey
Binary file not shown.
Binary file modified msp/testdata/idemix/MSP1OU2/ca/IssuerSecretKey
Binary file not shown.
Binary file modified msp/testdata/idemix/MSP1OU2/msp/IssuerPublicKey
Binary file not shown.
Binary file modified msp/testdata/idemix/MSP1OU2/user/SignerConfig
Binary file not shown.
Binary file modified msp/testdata/idemix/MSP1Verifier/ca/IssuerPublicKey
Binary file not shown.
Binary file modified msp/testdata/idemix/MSP1Verifier/ca/IssuerSecretKey
Binary file not shown.
Binary file modified msp/testdata/idemix/MSP1Verifier/msp/IssuerPublicKey
Binary file not shown.
Binary file modified msp/testdata/idemix/MSP2OU1/ca/IssuerPublicKey
Binary file not shown.
2 changes: 1 addition & 1 deletion msp/testdata/idemix/MSP2OU1/ca/IssuerSecretKey
Original file line number Diff line number Diff line change
@@ -1 +1 @@
��z1�~��(q>�7(UQ�������11�
.H[��N��B�.X���� ��B}e��_�O
Binary file modified msp/testdata/idemix/MSP2OU1/msp/IssuerPublicKey
Binary file not shown.
Binary file modified msp/testdata/idemix/MSP2OU1/user/SignerConfig
Binary file not shown.
Loading

0 comments on commit 8cbae04

Please sign in to comment.