From 40af3ef59cb39682cd68012103dac8e0464cd115 Mon Sep 17 00:00:00 2001 From: Angelo De Caro Date: Fri, 11 Aug 2023 10:33:54 +0200 Subject: [PATCH] load idemix as produced by fabric-ca (#480) Signed-off-by: Angelo De Caro --- .../fabric/core/generic/msp/idemix/config.go | 123 ++++++++++++++++ .../core/generic/msp/idemix/provider.go | 4 + .../core/generic/msp/idemix/provider_test.go | 135 ++++++++++++++++++ .../testdata/charlie.ExtraId2/IssuerPublicKey | Bin 0 -> 843 bytes .../IssuerRevocationPublicKey | 5 + ...estchannel-token-chaincode-example-com.pem | 14 ++ .../charlie.ExtraId2/user/SignerConfig | 1 + 7 files changed, 282 insertions(+) create mode 100644 platform/fabric/core/generic/msp/idemix/config.go create mode 100644 platform/fabric/core/generic/msp/idemix/testdata/charlie.ExtraId2/IssuerPublicKey create mode 100644 platform/fabric/core/generic/msp/idemix/testdata/charlie.ExtraId2/IssuerRevocationPublicKey create mode 100644 platform/fabric/core/generic/msp/idemix/testdata/charlie.ExtraId2/cacerts/localhost-7054-default-testchannel-token-chaincode-example-com.pem create mode 100644 platform/fabric/core/generic/msp/idemix/testdata/charlie.ExtraId2/user/SignerConfig diff --git a/platform/fabric/core/generic/msp/idemix/config.go b/platform/fabric/core/generic/msp/idemix/config.go new file mode 100644 index 000000000..b6daa69e5 --- /dev/null +++ b/platform/fabric/core/generic/msp/idemix/config.go @@ -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 +} diff --git a/platform/fabric/core/generic/msp/idemix/provider.go b/platform/fabric/core/generic/msp/idemix/provider.go index bbedee4bb..68e30b8cc 100644 --- a/platform/fabric/core/generic/msp/idemix/provider.go +++ b/platform/fabric/core/generic/msp/idemix/provider.go @@ -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") diff --git a/platform/fabric/core/generic/msp/idemix/provider_test.go b/platform/fabric/core/generic/msp/idemix/provider_test.go index b80869c75..66e84ab4a 100644 --- a/platform/fabric/core/generic/msp/idemix/provider_test.go +++ b/platform/fabric/core/generic/msp/idemix/provider_test.go @@ -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" @@ -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)) +} diff --git a/platform/fabric/core/generic/msp/idemix/testdata/charlie.ExtraId2/IssuerPublicKey b/platform/fabric/core/generic/msp/idemix/testdata/charlie.ExtraId2/IssuerPublicKey new file mode 100644 index 0000000000000000000000000000000000000000..12682ebdbab2af9d49f49e316f6799fe19251eec GIT binary patch literal 843 zcmV-R1GM}K0#8*61X6EoWeN;MZgOvIY;9$3bV)=C5K?7!Z)0I}X>V>wVQyq>WfDXR zAOmd_7Y-4Hk;+VsoCZhDBvyH58Ky_c6jG>o?CFmrXA&R^_W`R~hPo~I(GlwTv2^A* z0ZVLsZ|gX!b#et&y~0HrL<%4P{7RE2iLCALGAutWPq;g!EMPo##_X_@x@5J?^#hO+ zAQRWvlC|X;)WvIuvz+3i6MUO=#G z7*R?8sMqx!n#X{OYUB{Jyb>TU>FN4$SV2=NaOU0vX@5NB3IJ6i6z5~7l=dqD1(qiw zL<%4P!5^s^FHs%Lvrs#1k(}z^%MsW|tPK}wdX~1zBw<1IeASbbl%MiBB%Zo`x?jx(LHPQB}zgrqujy?H_kxAF+ zZ4w|S_Mh%tks?8)wZM8_fLTcI-6b8Xzd$gT2jzlf zLxE$o5oEuDl5{9^Mfo~A@eBGAAR}Is9kPF48A?;zA{kdQ_!JyJ!yU-Ax8uvU8%BAK zAS#Ff3Lq0)tJ#&}2Oc<7K6``#{Otf*`8lEowlp|}KxOAMU+xkhAUdHlRO{6TNC>U) zta3XQj(^2GW^2NG^A#VwgNq3v8XzibpJTzDE`A1~+K40?k0`g2O?yfJ{}=b!EjbB5 zLMb93A$9~&CcKT9!_7C%&Mo}OHE?W*zndb%zi}+HXY|QpGDHd>8simvFoaxb$JWca zKX|JjX&8;peWbe)k>PdfB5$M>5+D_}I$+)3bxspr9)ZiBqsmGh&e-BmXLc0P6Mxs( zthhQv3Lq{i8-azbufonaNN-X42|sP0Z`u#E13qI4eE~vsudWgx4z(Y)0^x?tl#R-t z^p^KO+Qu@vGPM)Pcv~8GqZF7nLLeUuo;dvRhB_irR=@T5rl}6tAKAB-Yz9hMfrtTF z7T`)CC#{l