Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Idemix MSP Folder Structure incompatible with what Fabric expects #303 #304

Merged
merged 5 commits into from
Jun 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion api/serverresponses.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ type CAInfoResponseNet struct {
// Base64 encoding of Idemix issuer public key
IssuerPublicKey string
// Base64 encoding of PEM-encoded Idemix issuer revocation public key
IssuerRevocationPublicKey string
RevocationPublicKey string
// Version of the server
Version string
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/fabric-ca-client/command/enroll.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,5 +88,5 @@ func (c *enrollCmd) runEnroll(cmd *cobra.Command, args []string) error {
if err != nil {
return err
}
return storeIssuerRevocationPublicKey(cfg, &resp.CAInfo)
return storeRevocationPublicKey(cfg, &resp.CAInfo)
}
8 changes: 4 additions & 4 deletions cmd/fabric-ca-client/command/getcainfo.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ func (c *getCAInfoCmd) runGetCACert(cmd *cobra.Command, args []string) error {
if err != nil {
return err
}
return storeIssuerRevocationPublicKey(client.Config, si)
return storeRevocationPublicKey(client.Config, si)
}

// Store the CAChain in the CACerts folder of MSP (Membership Service Provider)
Expand Down Expand Up @@ -200,9 +200,9 @@ func storeIssuerPublicKey(config *lib.ClientConfig, si *lib.GetCAInfoResponse) e
return nil
}

func storeIssuerRevocationPublicKey(config *lib.ClientConfig, si *lib.GetCAInfoResponse) error {
if len(si.IssuerRevocationPublicKey) > 0 {
err := storeToFile("Issuer revocation public key", config.MSPDir, "IssuerRevocationPublicKey", si.IssuerRevocationPublicKey)
func storeRevocationPublicKey(config *lib.ClientConfig, si *lib.GetCAInfoResponse) error {
if len(si.RevocationPublicKey) > 0 {
err := storeToFile("Issuer revocation public key", config.MSPDir, "RevocationPublicKey", si.RevocationPublicKey)
if err != nil {
return err
}
Expand Down
4 changes: 2 additions & 2 deletions cmd/fabric-ca-client/command/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2189,7 +2189,7 @@ func TestCleanUp(t *testing.T) {
os.Remove(filepath.Join(tdDir, "ca-key.pem"))
os.Remove(filepath.Join(tdDir, "IssuerPublicKey"))
os.Remove(filepath.Join(tdDir, "IssuerSecretKey"))
os.Remove(filepath.Join(tdDir, "IssuerRevocationPublicKey"))
os.Remove(filepath.Join(tdDir, "RevocationPublicKey"))
os.Remove(testYaml)
os.Remove(fabricCADB)
os.RemoveAll(moptionDir)
Expand All @@ -2200,7 +2200,7 @@ func cleanMultiCADir() {
caFolder := filepath.Join(tdDir, "ca/rootca")
nestedFolders := []string{"ca1", "ca2"}
removeFiles := []string{"msp", "ca-cert.pem",
"fabric-ca-server.db", "fabric-ca2-server.db", "ca-chain.pem", "IssuerPublicKey", "IssuerSecretKey", "IssuerRevocationPublicKey"}
"fabric-ca-server.db", "fabric-ca2-server.db", "ca-chain.pem", "IssuerPublicKey", "IssuerSecretKey", "RevocationPublicKey"}

for _, nestedFolder := range nestedFolders {
path := filepath.Join(caFolder, nestedFolder)
Expand Down
6 changes: 3 additions & 3 deletions cmd/fabric-ca-server/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -435,7 +435,7 @@ func TestClean(t *testing.T) {
os.Remove("ca-cert.pem")
os.Remove("IssuerSecretKey")
os.Remove("IssuerPublicKey")
os.Remove("IssuerRevocationPublicKey")
os.Remove("RevocationPublicKey")
os.Remove("fabric-ca-server.db")
os.RemoveAll("keystore")
os.RemoveAll("msp")
Expand All @@ -444,7 +444,7 @@ func TestClean(t *testing.T) {
os.Remove("../../testdata/ca-cert.pem")
os.Remove("../../testdata/IssuerSecretKey")
os.Remove("../../testdata/IssuerPublicKey")
os.Remove("../../testdata/IssuerRevocationPublicKey")
os.Remove("../../testdata/RevocationPublicKey")
os.RemoveAll(ldapTestDir)
os.RemoveAll("testregattr")
}
Expand All @@ -453,7 +453,7 @@ func cleanUpMultiCAFiles() {
caFolder := "../../testdata/ca/rootca"
nestedFolders := []string{"ca1", "ca2"}
removeFiles := []string{"msp", "ca-cert.pem", "ca-key.pem", "fabric-ca-server.db",
"fabric-ca2-server.db", "IssuerSecretKey", "IssuerPublicKey", "IssuerRevocationPublicKey"}
"fabric-ca2-server.db", "IssuerSecretKey", "IssuerPublicKey", "RevocationPublicKey"}

for _, nestedFolder := range nestedFolders {
path := filepath.Join(caFolder, nestedFolder)
Expand Down
6 changes: 3 additions & 3 deletions docs/source/deployguide/cadeploy.md
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ Because you've already registered and enrolled your organization CA bootstrap id
├── rcaadmin
├── msp
      ├── IssuerPublicKey
      ├── IssuerRevocationPublicKey
      ├── RevocationPublicKey
      ├── cacerts
   ├── keystore
     └── key.pem
Expand Down Expand Up @@ -412,7 +412,7 @@ The folder structure we are using for these commands is:
└── cert.pem
├── user
├── IssuerPublicKey
└── IssuerRevocationPublicKey
└── RevocationPublicKey
```

Where:
Expand Down Expand Up @@ -473,7 +473,7 @@ The resulting folder structure is similar to the following structure. (Some fold
├── tlscacerts
├── user
├── IssuerPublicKey
     └── IssuerRevocationPublicKey
     └── RevocationPublicKey
fabric-ca-server-int-ca
└── tls
└── tls-ca-cert.pem
Expand Down
2 changes: 1 addition & 1 deletion docs/source/operations_guide.rst
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ issued form the CA. You will see files such as the ones below:
├── fabric-ca-client-config.yaml
└── msp
├── IssuerPublicKey
├── IssuerRevocationPublicKey
├── RevocationPublicKey
├── cacerts
│ └── 0-0-0-0-7053.pem
├── keystore
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ require (
github.com/grantae/certinfo v0.0.0-20170412194111-59d56a35515b
github.com/hyperledger/fabric v1.4.11
github.com/hyperledger/fabric-lib-go v1.0.0
github.com/hyperledger/fabric-protos-go v0.0.0-20210911123859-041d13f0980c
github.com/jinzhu/copier v0.3.5
github.com/jmoiron/sqlx v1.2.0
github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46
Expand Down
1 change: 1 addition & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ github.com/hyperledger/fabric-amcl v0.0.0-20210603140002-2670f91851c8 h1:BCR8ZlO
github.com/hyperledger/fabric-amcl v0.0.0-20210603140002-2670f91851c8/go.mod h1:X+DIyUsaTmalOpmpQfIvFZjKHQedrURQ5t4YqquX7lE=
github.com/hyperledger/fabric-lib-go v1.0.0 h1:UL1w7c9LvHZUSkIvHTDGklxFv2kTeva1QI2emOVc324=
github.com/hyperledger/fabric-lib-go v1.0.0/go.mod h1:H362nMlunurmHwkYqR5uHL2UDWbQdbfz74n8kbCFsqc=
github.com/hyperledger/fabric-protos-go v0.0.0-20210911123859-041d13f0980c h1:QPhSriw6EzMOj/d7gcGiKEvozVvQ5HLk9UWie4KAvSs=
github.com/hyperledger/fabric-protos-go v0.0.0-20210911123859-041d13f0980c/go.mod h1:xVYTjK4DtZRBxZ2D9aE4y6AbLaPwue2o/criQyQbVD0=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
Expand Down
2 changes: 1 addition & 1 deletion lib/ca.go
Original file line number Diff line number Diff line change
Expand Up @@ -1014,7 +1014,7 @@ func (ca *CA) fillCAInfo(info *api.CAInfoResponseNet) error {
return err
}
info.IssuerPublicKey = util.B64Encode(ipkBytes)
info.IssuerRevocationPublicKey = util.B64Encode(rpkBytes)
info.RevocationPublicKey = util.B64Encode(rpkBytes)
return nil
}

Expand Down
25 changes: 18 additions & 7 deletions lib/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"encoding/hex"
"encoding/json"
"fmt"
"github.com/hyperledger/fabric-protos-go/msp"
"io/ioutil"
"net"
"net/http"
Expand Down Expand Up @@ -73,7 +74,7 @@ type GetCAInfoResponse struct {
// Idemix issuer public key of the CA
IssuerPublicKey []byte
// Idemix issuer revocation public key of the CA
IssuerRevocationPublicKey []byte
RevocationPublicKey []byte
// Version of the server
Version string
}
Expand Down Expand Up @@ -124,7 +125,7 @@ func (c *Client) Init() error {
c.ipkFile = filepath.Join(mspDir, "IssuerPublicKey")

// Idemix credentials directory
c.idemixCredsDir = path.Join(mspDir, "user")
c.idemixCredsDir = path.Join(c.HomeDir, "user")
err = os.MkdirAll(c.idemixCredsDir, 0o755)
if err != nil {
return errors.Wrap(err, "Failed to create Idemix credentials directory 'user'")
Expand Down Expand Up @@ -308,12 +309,12 @@ func (c *Client) net2LocalCAInfo(net *api.CAInfoResponseNet, local *GetCAInfoRes
}
local.IssuerPublicKey = ipk
}
if net.IssuerRevocationPublicKey != "" {
rpk, err := util.B64Decode(net.IssuerRevocationPublicKey)
if net.RevocationPublicKey != "" {
rpk, err := util.B64Decode(net.RevocationPublicKey)
if err != nil {
return errors.WithMessage(err, "Failed to decode issuer revocation key")
}
local.IssuerRevocationPublicKey = rpk
local.RevocationPublicKey = rpk
}
local.CAName = net.CAName
local.CAChain = caChain
Expand Down Expand Up @@ -517,7 +518,6 @@ func (c *Client) newIdemixEnrollmentResponse(identity *Identity, result *api.Ide
ou, _ := result.Attrs["OU"].(string)
enrollmentID, _ := result.Attrs["EnrollmentID"].(string)
signerConfig := &idemixcred.SignerConfig{
CurveID: cidemix.Curves.ByID(c.curveID),
Cred: credBytes,
Sk: sk.Bytes(),
Role: role,
Expand Down Expand Up @@ -945,7 +945,18 @@ func (c *Client) verifyIdemixCredential() error {
signerConfig := &idemixcred.SignerConfig{}
err = json.Unmarshal(credfileBytes, signerConfig)
if err != nil {
return errors.Wrapf(err, "Failed to unmarshal signer config from %s", c.idemixCredFile)
// try proto
idemixSignerConfig := &msp.IdemixMSPSignerConfig{}
err = proto.Unmarshal(credfileBytes, idemixSignerConfig)
if err != nil {
return errors.Wrapf(err, "Failed to unmarshal signer config from %s", c.idemixCredFile)
}
signerConfig.Cred = idemixSignerConfig.Cred
signerConfig.Sk = idemixSignerConfig.Sk
signerConfig.CredentialRevocationInformation = idemixSignerConfig.CredentialRevocationInformation
signerConfig.EnrollmentID = idemixSignerConfig.EnrollmentId
signerConfig.Role = int(idemixSignerConfig.Role)
signerConfig.OrganizationalUnitIdentifier = idemixSignerConfig.OrganizationalUnitIdentifier
}

cred := new(idemix.Credential)
Expand Down
43 changes: 36 additions & 7 deletions lib/client/credential/idemix/credential.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/hyperledger/fabric-ca/api"
idemix4 "github.com/hyperledger/fabric-ca/lib/common/idemix"
"github.com/hyperledger/fabric-ca/util"
m "github.com/hyperledger/fabric-protos-go/msp"
"github.com/hyperledger/fabric/bccsp"
"github.com/pkg/errors"
)
Expand Down Expand Up @@ -88,7 +89,18 @@ func (cred *Credential) Store() error {
if err != nil {
return err
}
signerConfigBytes, err := json.Marshal(val)
caSignerConfig := val.(*SignerConfig)

// Store the MSP signer config as Fabric expects
mspSignerConfig := &m.IdemixMSPSignerConfig{
Cred: caSignerConfig.Cred,
Sk: caSignerConfig.Sk,
OrganizationalUnitIdentifier: caSignerConfig.OrganizationalUnitIdentifier,
Role: int32(caSignerConfig.Role),
EnrollmentId: caSignerConfig.EnrollmentID,
CredentialRevocationInformation: caSignerConfig.CredentialRevocationInformation,
}
signerConfigBytes, err := proto.Marshal(mspSignerConfig)
if err != nil {
return errors.Wrapf(err, "Failed to marshal SignerConfig")
}
Expand All @@ -108,14 +120,31 @@ func (cred *Credential) Load() error {
log.Debugf("No credential found at %s: %s", cred.signerConfigFile, err.Error())
return err
}
val := SignerConfig{}
err = json.Unmarshal(signerConfigBytes, &val)
if err != nil {
return errors.Wrapf(err, fmt.Sprintf("Failed to unmarshal SignerConfig bytes from %s", cred.signerConfigFile))

// Load the MSP signer config
var val SignerConfig
mspSignerConfig := &m.IdemixMSPSignerConfig{}
err = proto.Unmarshal(signerConfigBytes, mspSignerConfig)
if err == nil {
val = SignerConfig{
Cred: mspSignerConfig.Cred,
Sk: mspSignerConfig.Sk,
OrganizationalUnitIdentifier: mspSignerConfig.OrganizationalUnitIdentifier,
Role: int(mspSignerConfig.Role),
EnrollmentID: mspSignerConfig.EnrollmentId,
CredentialRevocationInformation: mspSignerConfig.CredentialRevocationInformation,
}
}
if val.CurveID == "" {
val.CurveID = idemix4.DefaultIdemixCurve
Comment on lines -116 to -117
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't we still have to handle the blank val.CurveID?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that field is not needed anymore


if err != nil {
// try to unmarshal via json
val = SignerConfig{}
err = json.Unmarshal(signerConfigBytes, &val)
if err != nil {
return errors.Wrapf(err, fmt.Sprintf("Failed to unmarshal SignerConfig bytes from %s", cred.signerConfigFile))
}
}

cred.val = &val
return nil
}
Expand Down
2 changes: 0 additions & 2 deletions lib/client/credential/idemix/signerconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ type SignerConfig struct {
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"`
// CurveID specifies the name of the Idemix curve to use, defaults to 'amcl.Fp256bn'
CurveID string `protobuf:"bytes,7,opt,name=curve_id,json=curveID" json:"curveID,omitempty"`
Comment on lines -23 to -24
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CurveID was recently added by Yacov, don't we need it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but we don't need this field. It is never used and does not offer any advantage at the current stage.
The SignerConfig struct here is just a copy of the same struct in the Idemix lib.

}

// GetCred returns credential associated with this signer config
Expand Down
4 changes: 2 additions & 2 deletions lib/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,10 +208,10 @@ func TestIdemixEnroll(t *testing.T) {
t.Fatalf("Failed to store identity: %s", err.Error())
}

_, err = client.LoadIdentity("", filepath.Join(clientHome, "msp/signcerts/cert.pem"), filepath.Join(clientHome, "msp/user/SignerConfig"))
_, err = client.LoadIdentity("", filepath.Join(clientHome, "msp/signcerts/cert.pem"), filepath.Join(clientHome, "user/SignerConfig"))
assert.NoError(t, err, "Failed to load identity that has both X509 and Idemix credentials")

_, err = client.LoadIdentity("", "", filepath.Join(clientHome, "msp/user/SignerConfig"))
_, err = client.LoadIdentity("", "", filepath.Join(clientHome, "user/SignerConfig"))
assert.NoError(t, err, "Failed to load identity that only has Idemix credential")

// Error case, invalid x509 and Idemix credential
Expand Down
8 changes: 4 additions & 4 deletions lib/server/idemix/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ const (
// DefaultIssuerSecretKeyFile is the default name of the file that contains issuer secret key
DefaultIssuerSecretKeyFile = "IssuerSecretKey"
// DefaultRevocationPublicKeyFile is the name of the file where revocation public key is stored
DefaultRevocationPublicKeyFile = "IssuerRevocationPublicKey"
DefaultRevocationPublicKeyFile = "RevocationPublicKey"
// DefaultRevocationPrivateKeyFile is the name of the file where revocation private key is stored
DefaultRevocationPrivateKeyFile = "IssuerRevocationPrivateKey"
DefaultRevocationPrivateKeyFile = "RevocationPrivateKey"
// KeystoreDir is the keystore directory where all keys are stored. It is relative to the server home directory.
KeystoreDir = "msp/keystore"
)
Expand All @@ -30,8 +30,8 @@ type Config struct {
Curve string `def:"amcl.Fp256bn" help:"Name of the curve among {'amcl.Fp256bn', 'gurvy.Bn254', 'amcl.Fp256Miraclbn'}, defaults to 'amcl.Fp256bn'"`
IssuerPublicKeyfile string `def:"IssuerPublicKey" skip:"true" help:"Name of the file that contains marshalled bytes of CA's Idemix issuer public key"`
IssuerSecretKeyfile string `def:"IssuerSecretKey" skip:"true" help:"Name of the file that contains CA's Idemix issuer secret key"`
RevocationPublicKeyfile string `def:"IssuerRevocationPublicKey" skip:"true" help:"Name of the file that contains Idemix issuer revocation public key"`
RevocationPrivateKeyfile string `def:"IssuerRevocationPrivateKey" skip:"true" help:"Name of the file that contains Idemix issuer revocation private key"`
RevocationPublicKeyfile string `def:"RevocationPublicKey" skip:"true" help:"Name of the file that contains Idemix issuer revocation public key"`
RevocationPrivateKeyfile string `def:"RevocationPrivateKey" skip:"true" help:"Name of the file that contains Idemix issuer revocation private key"`
RHPoolSize int `def:"100" help:"Specifies revocation handle pool size"`
NonceExpiration string `def:"15s" help:"Duration after which a nonce expires"`
NonceSweepInterval string `def:"15m" help:"Interval at which expired nonces are deleted"`
Expand Down
4 changes: 2 additions & 2 deletions lib/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1238,7 +1238,7 @@ func TestSRVMultiCAWithIntermediate(t *testing.T) {
if err != nil {
t.Errorf("RemoveAll failed: %s", err)
}
err = os.RemoveAll("../testdata/IssuerRevocationPublicKey")
err = os.RemoveAll("../testdata/RevocationPublicKey")
if err != nil {
t.Errorf("RemoveAll failed: %s", err)
}
Expand Down Expand Up @@ -2399,7 +2399,7 @@ func cleanMultiCADir(t *testing.T) {
toplevelFolders := []string{"intermediateca", "rootca"}
nestedFolders := []string{"ca1", "ca2", "ca3"}
removeFiles := []string{"ca-cert.pem", "ca-key.pem", "fabric-ca-server.db",
"fabric-ca2-server.db", "ca-chain.pem", "IssuerPublicKey", "IssuerSecretKey", "IssuerRevocationPublicKey"}
"fabric-ca2-server.db", "ca-chain.pem", "IssuerPublicKey", "IssuerSecretKey", "RevocationPublicKey"}

for _, topFolder := range toplevelFolders {
for _, nestedFolder := range nestedFolders {
Expand Down
2 changes: 1 addition & 1 deletion scripts/fvt/idemix_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ function getIdemixCred() {
$FABRIC_CA_CLIENTEXEC enroll -u "${PROTO}${USERNAME}:$USERPSWD@$CA_HOST_ADDRESS:$PROXY_PORT" -H $CA_CFG_PATH/$USERNAME --enrollment.type idemix -d $TLSOPT
test $? -eq 0 || ErrorMsg "Failed to complete 'enroll' command"

CLIENTCERT="$CA_CFG_PATH/$USERNAME/msp/user/SignerConfig"
CLIENTCERT="$CA_CFG_PATH/$USERNAME/user/SignerConfig"
if [ ! -f $CLIENTCERT ]; then
ErrorMsg "Idemix credential was not stored in the correct location"
fi
Expand Down
Loading