Skip to content

Commit

Permalink
load idemix as produced by fabric-ca (#480)
Browse files Browse the repository at this point in the history
Signed-off-by: Angelo De Caro <[email protected]>
  • Loading branch information
adecaro authored Aug 11, 2023
1 parent 26c35e4 commit 40af3ef
Show file tree
Hide file tree
Showing 7 changed files with 282 additions and 0 deletions.
123 changes: 123 additions & 0 deletions platform/fabric/core/generic/msp/idemix/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package idemix

import (
"encoding/json"
"os"
"path/filepath"

"github.com/hyperledger/fabric/bccsp/factory"
"github.com/hyperledger/fabric/msp"

im "github.com/IBM/idemix/idemixmsp"
"github.com/hyperledger-labs/fabric-smart-client/pkg/utils/proto"
m "github.com/hyperledger/fabric-protos-go/msp"
"github.com/pkg/errors"
)

// SignerConfig contains the crypto material to set up an idemix signing identity
type SignerConfig struct {
// Cred represents the serialized idemix credential of the default signer
Cred []byte `protobuf:"bytes,1,opt,name=Cred,proto3" json:"Cred,omitempty"`
// Sk is the secret key of the default signer, corresponding to credential Cred
Sk []byte `protobuf:"bytes,2,opt,name=Sk,proto3" json:"Sk,omitempty"`
// OrganizationalUnitIdentifier defines the organizational unit the default signer is in
OrganizationalUnitIdentifier string `protobuf:"bytes,3,opt,name=organizational_unit_identifier,json=organizationalUnitIdentifier" json:"organizational_unit_identifier,omitempty"`
// Role defines whether the default signer is admin, member, peer, or client
Role int `protobuf:"varint,4,opt,name=role,json=role" json:"role,omitempty"`
// EnrollmentID contains the enrollment id of this signer
EnrollmentID string `protobuf:"bytes,5,opt,name=enrollment_id,json=enrollmentId" json:"enrollment_id,omitempty"`
// CRI contains a serialized Credential Revocation Information
CredentialRevocationInformation []byte `protobuf:"bytes,6,opt,name=credential_revocation_information,json=credentialRevocationInformation,proto3" json:"credential_revocation_information,omitempty"`
// RevocationHandle is the handle used to single out this credential and determine its revocation status
RevocationHandle string `protobuf:"bytes,7,opt,name=revocation_handle,json=revocationHandle,proto3" json:"revocation_handle,omitempty"`
// CurveID specifies the name of the Idemix curve to use, defaults to 'amcl.Fp256bn'
CurveID string `protobuf:"bytes,8,opt,name=curve_id,json=curveID" json:"curveID,omitempty"`
}

const (
ConfigDirUser = "user"
ConfigFileIssuerPublicKey = "IssuerPublicKey"
IdemixConfigFileRevocationPublicKey = "IssuerRevocationPublicKey"
ConfigFileSigner = "SignerConfig"
)

func readFile(file string) ([]byte, error) {
fileCont, err := os.ReadFile(file)
if err != nil {
return nil, errors.Wrapf(err, "could not read file %s", file)
}

return fileCont, nil
}

func GetLocalMspConfigWithType(dir string, bccspConfig *factory.FactoryOpts, id string) (*m.MSPConfig, error) {
mspConfig, err := msp.GetLocalMspConfigWithType(dir, bccspConfig, id, msp.ProviderTypeToString(msp.IDEMIX))
if err != nil {
// load it using the fabric-ca format
mspConfig2, err2 := GetFabricCAIdemixMspConfig(dir, id)
if err2 != nil {
return nil, errors.Wrapf(err2, "cannot get idemix msp config from [%s]: [%s]", dir, err)
}
mspConfig = mspConfig2
}
return mspConfig, nil
}

// GetFabricCAIdemixMspConfig returns the configuration for the Idemix MSP generated by Fabric-CA
func GetFabricCAIdemixMspConfig(dir string, ID string) (*m.MSPConfig, error) {
path := filepath.Join(dir, ConfigFileIssuerPublicKey)
ipkBytes, err := readFile(path)
if err != nil {
return nil, errors.Wrapf(err, "failed to read issuer public key file at [%s]", path)
}

path = filepath.Join(dir, IdemixConfigFileRevocationPublicKey)
revocationPkBytes, err := readFile(path)
if err != nil {
return nil, errors.Wrapf(err, "failed to read revocation public key file at [%s]", path)
}

idemixConfig := &im.IdemixMSPConfig{
Name: ID,
Ipk: ipkBytes,
RevocationPk: revocationPkBytes,
}

path = filepath.Join(dir, ConfigDirUser, ConfigFileSigner)
signerBytes, err := readFile(path)
if err == nil {
// signerBytes is a json structure, convert it to protobuf
si := &SignerConfig{}
if err := json.Unmarshal(signerBytes, si); err != nil {
return nil, errors.Wrapf(err, "failed to json unmarshal signer config read at [%s]", path)
}

signerConfig := &im.IdemixMSPSignerConfig{
Cred: si.Cred,
Sk: si.Sk,
OrganizationalUnitIdentifier: si.OrganizationalUnitIdentifier,
Role: int32(si.Role),
EnrollmentId: si.EnrollmentID,
CredentialRevocationInformation: si.CredentialRevocationInformation,
RevocationHandle: si.RevocationHandle,
}
idemixConfig.Signer = signerConfig
} else {
if !os.IsNotExist(errors.Cause(err)) {
return nil, errors.Wrapf(err, "failed to read the content of signer config at [%s]", path)
}
}

confBytes, err := proto.Marshal(idemixConfig)
if err != nil {
return nil, err
}

return &m.MSPConfig{Config: confBytes, Type: int32(msp.IDEMIX)}, nil
}
4 changes: 4 additions & 0 deletions platform/fabric/core/generic/msp/idemix/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@ func NewProviderWithSigType(conf1 *m.MSPConfig, sp view2.ServiceProvider, sigTyp
return NewProvider(conf1, sp, sigType, math.FP256BN_AMCL)
}

func NewProviderWithSigTypeAncCurve(conf1 *m.MSPConfig, sp view2.ServiceProvider, sigType bccsp.SignatureType, curveID math.CurveID) (*Provider, error) {
return NewProvider(conf1, sp, sigType, curveID)
}

func NewProvider(conf1 *m.MSPConfig, sp view2.ServiceProvider, sigType bccsp.SignatureType, curveID math.CurveID) (*Provider, error) {
logger.Debugf("Setting up Idemix-based MSP instance")

Expand Down
135 changes: 135 additions & 0 deletions platform/fabric/core/generic/msp/idemix/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"testing"

bccsp "github.com/IBM/idemix/bccsp/schemes"
math "github.com/IBM/mathlib"
idemix2 "github.com/hyperledger-labs/fabric-smart-client/platform/fabric/core/generic/msp/idemix"
driver2 "github.com/hyperledger-labs/fabric-smart-client/platform/fabric/driver"
sig2 "github.com/hyperledger-labs/fabric-smart-client/platform/view/core/sig"
Expand Down Expand Up @@ -277,3 +278,137 @@ func TestProvider_DeserializeSigner(t *testing.T) {
assert.NoError(t, err)
assert.NoError(t, verifier.Verify(msg, sigma))
}

func TestIdentityFromFabricCA(t *testing.T) {
registry := registry2.New()

kvss, err := kvs.NewWithConfig(registry, "memory", "", &mock.ConfigProvider{})
assert.NoError(t, err)
assert.NoError(t, registry.RegisterService(kvss))
sigService := sig2.NewSignService(registry, nil, kvss)
assert.NoError(t, registry.RegisterService(sigService))

config, err := idemix2.GetLocalMspConfigWithType("./testdata/charlie.ExtraId2", nil, "charlie.ExtraId2")
assert.NoError(t, err)

p, err := idemix2.NewProviderWithSigTypeAncCurve(config, registry, bccsp.Standard, math.BN254)
assert.NoError(t, err)
assert.NotNil(t, p)

id, audit, err := p.Identity(nil)
assert.NoError(t, err)
assert.NotNil(t, id)
assert.Nil(t, audit)

signer, err := p.DeserializeSigner(id)
assert.NoError(t, err)
verifier, err := p.DeserializeVerifier(id)
assert.NoError(t, err)

sigma, err := signer.Sign([]byte("hello world!!!"))
assert.NoError(t, err)
assert.NoError(t, verifier.Verify([]byte("hello world!!!"), sigma))

p, err = idemix2.NewProviderWithSigTypeAncCurve(config, registry, bccsp.Standard, math.BN254)
assert.NoError(t, err)
assert.NotNil(t, p)

id, audit, err = p.Identity(nil)
assert.NoError(t, err)
assert.NotNil(t, id)
assert.Nil(t, audit)

signer, err = p.DeserializeSigner(id)
assert.NoError(t, err)
verifier, err = p.DeserializeVerifier(id)
assert.NoError(t, err)

sigma, err = signer.Sign([]byte("hello world!!!"))
assert.NoError(t, err)
assert.NoError(t, verifier.Verify([]byte("hello world!!!"), sigma))

p, err = idemix2.NewProviderWithSigTypeAncCurve(config, registry, idemix2.Any, math.BN254)
assert.NoError(t, err)
assert.NotNil(t, p)

id, audit, err = p.Identity(nil)
assert.NoError(t, err)
assert.NotNil(t, id)
assert.Nil(t, audit)

signer, err = p.DeserializeSigner(id)
assert.NoError(t, err)
verifier, err = p.DeserializeVerifier(id)
assert.NoError(t, err)

sigma, err = signer.Sign([]byte("hello world!!!"))
assert.NoError(t, err)
assert.NoError(t, verifier.Verify([]byte("hello world!!!"), sigma))
}

func TestIdentityFromFabricCAWithEidRhNymPolicy(t *testing.T) {
registry := registry2.New()

kvss, err := kvs.NewWithConfig(registry, "memory", "", &mock.ConfigProvider{})
assert.NoError(t, err)
assert.NoError(t, registry.RegisterService(kvss))
sigService := sig2.NewSignService(registry, nil, kvss)
assert.NoError(t, registry.RegisterService(sigService))

config, err := idemix2.GetLocalMspConfigWithType("./testdata/charlie.ExtraId2", nil, "charlie.ExtraId2")
assert.NoError(t, err)

p, err := idemix2.NewProviderWithSigTypeAncCurve(config, registry, bccsp.EidNymRhNym, math.BN254)
assert.NoError(t, err)
assert.NotNil(t, p)

// get an identity with its own audit info from the provider
// id is in its serialized form
id, audit, err := p.Identity(nil)
assert.NoError(t, err)
assert.NotNil(t, id)
assert.NotNil(t, audit)
info, err := p.Info(id, audit)
assert.NoError(t, err)
assert.True(t, strings.HasPrefix(info, "MSP.Idemix: [charlie.ExtraId2]"))
assert.True(t, strings.HasSuffix(info, "[charlie.ExtraId2][][MEMBER]"))

auditInfo, err := p.DeserializeAuditInfo(audit)
assert.NoError(t, err)
assert.NoError(t, auditInfo.Match(id))

signer, err := p.DeserializeSigner(id)
assert.NoError(t, err)
verifier, err := p.DeserializeVerifier(id)
assert.NoError(t, err)

sigma, err := signer.Sign([]byte("hello world!!!"))
assert.NoError(t, err)
assert.NoError(t, verifier.Verify([]byte("hello world!!!"), sigma))

p, err = idemix2.NewProviderWithAnyPolicyAndCurve(config, registry, math.BN254)
assert.NoError(t, err)
assert.NotNil(t, p)

id, audit, err = p.Identity(&driver2.IdentityOptions{EIDExtension: true})
assert.NoError(t, err)
assert.NotNil(t, id)
assert.NotNil(t, audit)
info, err = p.Info(id, audit)
assert.NoError(t, err)
assert.True(t, strings.HasPrefix(info, "MSP.Idemix: [charlie.ExtraId2]"))
assert.True(t, strings.HasSuffix(info, "[charlie.ExtraId2][][MEMBER]"))

auditInfo, err = p.DeserializeAuditInfo(audit)
assert.NoError(t, err)
assert.NoError(t, auditInfo.Match(id))

signer, err = p.DeserializeSigner(id)
assert.NoError(t, err)
verifier, err = p.DeserializeVerifier(id)
assert.NoError(t, err)

sigma, err = signer.Sign([]byte("hello world!!!"))
assert.NoError(t, err)
assert.NoError(t, verifier.Verify([]byte("hello world!!!"), sigma))
}
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-----BEGIN PUBLIC KEY-----
MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEC8Li6cAeTbbm4EDn7WW6jadtm1SxyuZN
VxO4b+lWIj1fM5q2ZQOfGhvX6tamjjqAsWcPlB9pExtqwNZYkmy/p08vEDjdcJdw
/Zm+DxBgy/V0RqnOQoikUEY2HW1CiyOt
-----END PUBLIC KEY-----
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
-----BEGIN CERTIFICATE-----
MIICFzCCAb2gAwIBAgIUG8VbHeXmzg6NP1p2keeEKLktU7swCgYIKoZIzj0EAwIw
aDELMAkGA1UEBhMCVVMxFzAVBgNVBAgTDk5vcnRoIENhcm9saW5hMRQwEgYDVQQK
EwtIeXBlcmxlZGdlcjEPMA0GA1UECxMGRmFicmljMRkwFwYDVQQDExBmYWJyaWMt
Y2Etc2VydmVyMB4XDTIzMDYyOTE0MDUwMFoXDTM4MDYyNTE0MDUwMFowaDELMAkG
A1UEBhMCVVMxFzAVBgNVBAgTDk5vcnRoIENhcm9saW5hMRQwEgYDVQQKEwtIeXBl
cmxlZGdlcjEPMA0GA1UECxMGRmFicmljMRkwFwYDVQQDExBmYWJyaWMtY2Etc2Vy
dmVyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEzaCNErKmgyfKOI6+ZpySBWeR
Ar/lC6xz3qMJfng43bwP8lh/itvs30bykj5rXV72SgKonzxpBg6Jmo33WRoMAaNF
MEMwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYE
FHOGW/tJ64Z6Ey5YpPa2TKBbgad/MAoGCCqGSM49BAMCA0gAMEUCIQDrrplKe4+E
kBWINrIeunCsfmLdRElCK32i0vhJKwoK8AIgWbqPYqahMWBFw0kjzcVhEo2nnluN
/E50lRakHY0g7GY=
-----END CERTIFICATE-----
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"Cred":"CkQKIBgoychRH352qH5kn2PgsDqYy7YV4fIIpNTPy3jKaqs0EiAUTfWTT3HERcQOH6SqiMsNvEOJZG67WgVZvZD38sy6jhJECiAZkx52wMXtHpAIyi2QVgQEtBvRADhuVSsbbnIezqqfehIgAWgT5QS+dXuI/roShhYnn8bHLHngTvTaAc3CjM4b7yYaIAjk8NdMhGHOnuQfg8Xt+r2E9F9bldx+CdWwK37KCePjIiAv/sSDk+3fTv2KP2cg7APGE3KGV7hggmNFZ8+Nw88SmyogIh+KdxQ1m225ut3uk2pXr4beoMJ9tdEHlQ3Cy7hSuFEqIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABKiAZf1sC4JEoVA9LcdmW7C/6O3/EluHH9kXv4h9IzY95zyogCr4VjjzRvI4syvTh/FeOnPdF1FmuvDwnOFpntdeHW0k=","Sk":"DpK+mjLmMq56x6HQFtksSyyaqhlVONwC2KcdWbK1hso=","enrollment_id":"charlie.ExtraId2","credential_revocation_information":"CAESiAEKIBmOk5OSDUg6cmC/tzH7XSXxqkkzNannEpfkhbeu8xLCEiAYAN7vEh8edkJqAGZeXER5Z0Mi1Pde2t1G3r1c2ZL27RogCQaJ0Fhf8HXsnpmtaQwzlbxLMTNws47zVaza3NEil1siIBLIXqXbjG3rSqtxgI3LQI/j0edpDEPTe0zmzAFm+n2qGmcwZQIwLNKDW/OpTOeREehIcKTqQZy55wHAmUwsBdOnGyAotrgRhBy5WV+QcbOnEH5QCXycAjEA23xRDoXD22WjgubK22tvf1uh6JZ+O5vGSD91TZq5OOKRTSIopomictjB+pLOk5Z7","revocation_handle":"1","curveID":"gurvy.Bn254"}

0 comments on commit 40af3ef

Please sign in to comment.