382 lines
14 KiB
Go
382 lines
14 KiB
Go
/*
|
|
Copyright IBM Corp. All Rights Reserved.
|
|
|
|
SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
package msp
|
|
|
|
import (
|
|
"encoding/pem"
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/IBM/idemix"
|
|
"github.com/golang/protobuf/proto"
|
|
"github.com/hyperledger/fabric-protos-go/msp"
|
|
"github.com/hyperledger/fabric/bccsp"
|
|
"github.com/hyperledger/fabric/bccsp/factory"
|
|
"github.com/pkg/errors"
|
|
"gopkg.in/yaml.v2"
|
|
)
|
|
|
|
// OrganizationalUnitIdentifiersConfiguration is used to represent an OU
|
|
// and an associated trusted certificate
|
|
type OrganizationalUnitIdentifiersConfiguration struct {
|
|
// Certificate is the path to a root or intermediate certificate
|
|
Certificate string `yaml:"Certificate,omitempty"`
|
|
// OrganizationalUnitIdentifier is the name of the OU
|
|
OrganizationalUnitIdentifier string `yaml:"OrganizationalUnitIdentifier,omitempty"`
|
|
}
|
|
|
|
// NodeOUs contains information on how to tell apart clients, peers and orderers
|
|
// based on OUs. If the check is enforced, by setting Enabled to true,
|
|
// the MSP will consider an identity valid if it is an identity of a client, a peer or
|
|
// an orderer. An identity should have only one of these special OUs.
|
|
type NodeOUs struct {
|
|
// Enable activates the OU enforcement
|
|
Enable bool `yaml:"Enable,omitempty"`
|
|
// ClientOUIdentifier specifies how to recognize clients by OU
|
|
ClientOUIdentifier *OrganizationalUnitIdentifiersConfiguration `yaml:"ClientOUIdentifier,omitempty"`
|
|
// PeerOUIdentifier specifies how to recognize peers by OU
|
|
PeerOUIdentifier *OrganizationalUnitIdentifiersConfiguration `yaml:"PeerOUIdentifier,omitempty"`
|
|
// AdminOUIdentifier specifies how to recognize admins by OU
|
|
AdminOUIdentifier *OrganizationalUnitIdentifiersConfiguration `yaml:"AdminOUIdentifier,omitempty"`
|
|
// OrdererOUIdentifier specifies how to recognize admins by OU
|
|
OrdererOUIdentifier *OrganizationalUnitIdentifiersConfiguration `yaml:"OrdererOUIdentifier,omitempty"`
|
|
}
|
|
|
|
// Configuration represents the accessory configuration an MSP can be equipped with.
|
|
// By default, this configuration is stored in a yaml file
|
|
type Configuration struct {
|
|
// OrganizationalUnitIdentifiers is a list of OUs. If this is set, the MSP
|
|
// will consider an identity valid only it contains at least one of these OUs
|
|
OrganizationalUnitIdentifiers []*OrganizationalUnitIdentifiersConfiguration `yaml:"OrganizationalUnitIdentifiers,omitempty"`
|
|
// NodeOUs enables the MSP to tell apart clients, peers and orderers based
|
|
// on the identity's OU.
|
|
NodeOUs *NodeOUs `yaml:"NodeOUs,omitempty"`
|
|
}
|
|
|
|
func readFile(file string) ([]byte, error) {
|
|
fileCont, err := ioutil.ReadFile(file)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "could not read file %s", file)
|
|
}
|
|
|
|
return fileCont, nil
|
|
}
|
|
|
|
func readPemFile(file string) ([]byte, error) {
|
|
bytes, err := readFile(file)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "reading from file %s failed", file)
|
|
}
|
|
|
|
b, _ := pem.Decode(bytes)
|
|
if b == nil { // TODO: also check that the type is what we expect (cert vs key..)
|
|
return nil, errors.Errorf("no pem content for file %s", file)
|
|
}
|
|
|
|
return bytes, nil
|
|
}
|
|
|
|
func getPemMaterialFromDir(dir string) ([][]byte, error) {
|
|
mspLogger.Debugf("Reading directory %s", dir)
|
|
|
|
_, err := os.Stat(dir)
|
|
if os.IsNotExist(err) {
|
|
return nil, err
|
|
}
|
|
|
|
content := make([][]byte, 0)
|
|
files, err := ioutil.ReadDir(dir)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "could not read directory %s", dir)
|
|
}
|
|
|
|
for _, f := range files {
|
|
fullName := filepath.Join(dir, f.Name())
|
|
|
|
f, err := os.Stat(fullName)
|
|
if err != nil {
|
|
mspLogger.Warningf("Failed to stat %s: %s", fullName, err)
|
|
continue
|
|
}
|
|
if f.IsDir() {
|
|
continue
|
|
}
|
|
|
|
mspLogger.Debugf("Inspecting file %s", fullName)
|
|
|
|
item, err := readPemFile(fullName)
|
|
if err != nil {
|
|
mspLogger.Warningf("Failed reading file %s: %s", fullName, err)
|
|
continue
|
|
}
|
|
|
|
content = append(content, item)
|
|
}
|
|
|
|
return content, nil
|
|
}
|
|
|
|
const (
|
|
cacerts = "cacerts"
|
|
admincerts = "admincerts"
|
|
signcerts = "signcerts"
|
|
keystore = "keystore"
|
|
intermediatecerts = "intermediatecerts"
|
|
crlsfolder = "crls"
|
|
configfilename = "config.yaml"
|
|
tlscacerts = "tlscacerts"
|
|
tlsintermediatecerts = "tlsintermediatecerts"
|
|
)
|
|
|
|
func SetupBCCSPKeystoreConfig(bccspConfig *factory.FactoryOpts, keystoreDir string) *factory.FactoryOpts {
|
|
if bccspConfig == nil {
|
|
bccspConfig = factory.GetDefaultOpts()
|
|
}
|
|
|
|
if bccspConfig.Default == "SW" || bccspConfig.SW != nil {
|
|
if bccspConfig.SW == nil {
|
|
bccspConfig.SW = factory.GetDefaultOpts().SW
|
|
}
|
|
|
|
// Only override the KeyStorePath if it was left empty
|
|
if bccspConfig.SW.FileKeystore == nil ||
|
|
bccspConfig.SW.FileKeystore.KeyStorePath == "" {
|
|
bccspConfig.SW.FileKeystore = &factory.FileKeystoreOpts{KeyStorePath: keystoreDir}
|
|
}
|
|
}
|
|
|
|
return bccspConfig
|
|
}
|
|
|
|
// GetLocalMspConfigWithType returns a local MSP
|
|
// configuration for the MSP in the specified
|
|
// directory, with the specified ID and type
|
|
func GetLocalMspConfigWithType(dir string, bccspConfig *factory.FactoryOpts, ID, mspType string) (*msp.MSPConfig, error) {
|
|
switch mspType {
|
|
case ProviderTypeToString(FABRIC):
|
|
return GetLocalMspConfig(dir, bccspConfig, ID)
|
|
case ProviderTypeToString(IDEMIX):
|
|
return idemix.GetIdemixMspConfig(dir, ID)
|
|
default:
|
|
return nil, errors.Errorf("unknown MSP type '%s'", mspType)
|
|
}
|
|
}
|
|
|
|
func GetLocalMspConfig(dir string, bccspConfig *factory.FactoryOpts, ID string) (*msp.MSPConfig, error) {
|
|
signcertDir := filepath.Join(dir, signcerts)
|
|
keystoreDir := filepath.Join(dir, keystore)
|
|
bccspConfig = SetupBCCSPKeystoreConfig(bccspConfig, keystoreDir)
|
|
|
|
err := factory.InitFactories(bccspConfig)
|
|
if err != nil {
|
|
return nil, errors.WithMessage(err, "could not initialize BCCSP Factories")
|
|
}
|
|
|
|
signcert, err := getPemMaterialFromDir(signcertDir)
|
|
if err != nil || len(signcert) == 0 {
|
|
return nil, errors.Wrapf(err, "could not load a valid signer certificate from directory %s", signcertDir)
|
|
}
|
|
|
|
/* FIXME: for now we're making the following assumptions
|
|
1) there is exactly one signing cert
|
|
2) BCCSP's KeyStore has the private key that matches SKI of
|
|
signing cert
|
|
*/
|
|
|
|
sigid := &msp.SigningIdentityInfo{PublicSigner: signcert[0], PrivateSigner: nil}
|
|
|
|
return getMspConfig(dir, ID, sigid)
|
|
}
|
|
|
|
// GetVerifyingMspConfig returns an MSP config given directory, ID and type
|
|
func GetVerifyingMspConfig(dir, ID, mspType string) (*msp.MSPConfig, error) {
|
|
switch mspType {
|
|
case ProviderTypeToString(FABRIC):
|
|
return getMspConfig(dir, ID, nil)
|
|
case ProviderTypeToString(IDEMIX):
|
|
return idemix.GetIdemixMspConfig(dir, ID)
|
|
default:
|
|
return nil, errors.Errorf("unknown MSP type '%s'", mspType)
|
|
}
|
|
}
|
|
|
|
func getMspConfig(dir string, ID string, sigid *msp.SigningIdentityInfo) (*msp.MSPConfig, error) {
|
|
cacertDir := filepath.Join(dir, cacerts)
|
|
admincertDir := filepath.Join(dir, admincerts)
|
|
intermediatecertsDir := filepath.Join(dir, intermediatecerts)
|
|
crlsDir := filepath.Join(dir, crlsfolder)
|
|
configFile := filepath.Join(dir, configfilename)
|
|
tlscacertDir := filepath.Join(dir, tlscacerts)
|
|
tlsintermediatecertsDir := filepath.Join(dir, tlsintermediatecerts)
|
|
|
|
cacerts, err := getPemMaterialFromDir(cacertDir)
|
|
if err != nil || len(cacerts) == 0 {
|
|
return nil, errors.WithMessagef(err, "could not load a valid ca certificate from directory %s", cacertDir)
|
|
}
|
|
|
|
admincert, err := getPemMaterialFromDir(admincertDir)
|
|
if err != nil && !os.IsNotExist(err) {
|
|
return nil, errors.WithMessagef(err, "could not load a valid admin certificate from directory %s", admincertDir)
|
|
}
|
|
|
|
intermediatecerts, err := getPemMaterialFromDir(intermediatecertsDir)
|
|
if os.IsNotExist(err) {
|
|
mspLogger.Debugf("Intermediate certs folder not found at [%s]. Skipping. [%s]", intermediatecertsDir, err)
|
|
} else if err != nil {
|
|
return nil, errors.WithMessagef(err, "failed loading intermediate ca certs at [%s]", intermediatecertsDir)
|
|
}
|
|
|
|
tlsCACerts, err := getPemMaterialFromDir(tlscacertDir)
|
|
tlsIntermediateCerts := [][]byte{}
|
|
if os.IsNotExist(err) {
|
|
mspLogger.Debugf("TLS CA certs folder not found at [%s]. Skipping and ignoring TLS intermediate CA folder. [%s]", tlsintermediatecertsDir, err)
|
|
} else if err != nil {
|
|
return nil, errors.WithMessagef(err, "failed loading TLS ca certs at [%s]", tlsintermediatecertsDir)
|
|
} else if len(tlsCACerts) != 0 {
|
|
tlsIntermediateCerts, err = getPemMaterialFromDir(tlsintermediatecertsDir)
|
|
if os.IsNotExist(err) {
|
|
mspLogger.Debugf("TLS intermediate certs folder not found at [%s]. Skipping. [%s]", tlsintermediatecertsDir, err)
|
|
} else if err != nil {
|
|
return nil, errors.WithMessagef(err, "failed loading TLS intermediate ca certs at [%s]", tlsintermediatecertsDir)
|
|
}
|
|
} else {
|
|
mspLogger.Debugf("TLS CA certs folder at [%s] is empty. Skipping.", tlsintermediatecertsDir)
|
|
}
|
|
|
|
crls, err := getPemMaterialFromDir(crlsDir)
|
|
if os.IsNotExist(err) {
|
|
mspLogger.Debugf("crls folder not found at [%s]. Skipping. [%s]", crlsDir, err)
|
|
} else if err != nil {
|
|
return nil, errors.WithMessagef(err, "failed loading crls at [%s]", crlsDir)
|
|
}
|
|
|
|
// Load configuration file
|
|
// if the configuration file is there then load it
|
|
// otherwise skip it
|
|
var ouis []*msp.FabricOUIdentifier
|
|
var nodeOUs *msp.FabricNodeOUs
|
|
_, err = os.Stat(configFile)
|
|
if err == nil {
|
|
// load the file, if there is a failure in loading it then
|
|
// return an error
|
|
raw, err := ioutil.ReadFile(configFile)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "failed loading configuration file at [%s]", configFile)
|
|
}
|
|
|
|
configuration := Configuration{}
|
|
err = yaml.Unmarshal(raw, &configuration)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "failed unmarshalling configuration file at [%s]", configFile)
|
|
}
|
|
|
|
// Prepare OrganizationalUnitIdentifiers
|
|
if len(configuration.OrganizationalUnitIdentifiers) > 0 {
|
|
for _, ouID := range configuration.OrganizationalUnitIdentifiers {
|
|
f := filepath.Join(dir, ouID.Certificate)
|
|
raw, err = readFile(f)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "failed loading OrganizationalUnit certificate at [%s]", f)
|
|
}
|
|
|
|
oui := &msp.FabricOUIdentifier{
|
|
Certificate: raw,
|
|
OrganizationalUnitIdentifier: ouID.OrganizationalUnitIdentifier,
|
|
}
|
|
ouis = append(ouis, oui)
|
|
}
|
|
}
|
|
|
|
// Prepare NodeOUs
|
|
if configuration.NodeOUs != nil && configuration.NodeOUs.Enable {
|
|
mspLogger.Debug("Loading NodeOUs")
|
|
nodeOUs = &msp.FabricNodeOUs{
|
|
Enable: true,
|
|
}
|
|
if configuration.NodeOUs.ClientOUIdentifier != nil && len(configuration.NodeOUs.ClientOUIdentifier.OrganizationalUnitIdentifier) != 0 {
|
|
nodeOUs.ClientOuIdentifier = &msp.FabricOUIdentifier{OrganizationalUnitIdentifier: configuration.NodeOUs.ClientOUIdentifier.OrganizationalUnitIdentifier}
|
|
}
|
|
if configuration.NodeOUs.PeerOUIdentifier != nil && len(configuration.NodeOUs.PeerOUIdentifier.OrganizationalUnitIdentifier) != 0 {
|
|
nodeOUs.PeerOuIdentifier = &msp.FabricOUIdentifier{OrganizationalUnitIdentifier: configuration.NodeOUs.PeerOUIdentifier.OrganizationalUnitIdentifier}
|
|
}
|
|
if configuration.NodeOUs.AdminOUIdentifier != nil && len(configuration.NodeOUs.AdminOUIdentifier.OrganizationalUnitIdentifier) != 0 {
|
|
nodeOUs.AdminOuIdentifier = &msp.FabricOUIdentifier{OrganizationalUnitIdentifier: configuration.NodeOUs.AdminOUIdentifier.OrganizationalUnitIdentifier}
|
|
}
|
|
if configuration.NodeOUs.OrdererOUIdentifier != nil && len(configuration.NodeOUs.OrdererOUIdentifier.OrganizationalUnitIdentifier) != 0 {
|
|
nodeOUs.OrdererOuIdentifier = &msp.FabricOUIdentifier{OrganizationalUnitIdentifier: configuration.NodeOUs.OrdererOUIdentifier.OrganizationalUnitIdentifier}
|
|
}
|
|
|
|
// Read certificates, if defined
|
|
|
|
// ClientOU
|
|
if nodeOUs.ClientOuIdentifier != nil {
|
|
nodeOUs.ClientOuIdentifier.Certificate = loadCertificateAt(dir, configuration.NodeOUs.ClientOUIdentifier.Certificate, "ClientOU")
|
|
}
|
|
// PeerOU
|
|
if nodeOUs.PeerOuIdentifier != nil {
|
|
nodeOUs.PeerOuIdentifier.Certificate = loadCertificateAt(dir, configuration.NodeOUs.PeerOUIdentifier.Certificate, "PeerOU")
|
|
}
|
|
// AdminOU
|
|
if nodeOUs.AdminOuIdentifier != nil {
|
|
nodeOUs.AdminOuIdentifier.Certificate = loadCertificateAt(dir, configuration.NodeOUs.AdminOUIdentifier.Certificate, "AdminOU")
|
|
}
|
|
// OrdererOU
|
|
if nodeOUs.OrdererOuIdentifier != nil {
|
|
nodeOUs.OrdererOuIdentifier.Certificate = loadCertificateAt(dir, configuration.NodeOUs.OrdererOUIdentifier.Certificate, "OrdererOU")
|
|
}
|
|
}
|
|
} else {
|
|
mspLogger.Debugf("MSP configuration file not found at [%s]: [%s]", configFile, err)
|
|
}
|
|
|
|
// Set FabricCryptoConfig
|
|
cryptoConfig := &msp.FabricCryptoConfig{
|
|
SignatureHashFamily: bccsp.SHA2,
|
|
IdentityIdentifierHashFunction: bccsp.SHA256,
|
|
}
|
|
|
|
// Compose FabricMSPConfig
|
|
fmspconf := &msp.FabricMSPConfig{
|
|
Admins: admincert,
|
|
RootCerts: cacerts,
|
|
IntermediateCerts: intermediatecerts,
|
|
SigningIdentity: sigid,
|
|
Name: ID,
|
|
OrganizationalUnitIdentifiers: ouis,
|
|
RevocationList: crls,
|
|
CryptoConfig: cryptoConfig,
|
|
TlsRootCerts: tlsCACerts,
|
|
TlsIntermediateCerts: tlsIntermediateCerts,
|
|
FabricNodeOus: nodeOUs,
|
|
}
|
|
|
|
fmpsjs, err := proto.Marshal(fmspconf)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &msp.MSPConfig{Config: fmpsjs, Type: int32(FABRIC)}, nil
|
|
}
|
|
|
|
func loadCertificateAt(dir, certificatePath string, ouType string) []byte {
|
|
if certificatePath == "" {
|
|
mspLogger.Debugf("Specific certificate for %s is not configured", ouType)
|
|
return nil
|
|
}
|
|
|
|
f := filepath.Join(dir, certificatePath)
|
|
raw, err := readFile(f)
|
|
if err != nil {
|
|
mspLogger.Warnf("Failed loading %s certificate at [%s]: [%s]", ouType, f, err)
|
|
} else {
|
|
return raw
|
|
}
|
|
|
|
return nil
|
|
}
|