github.com/yimialmonte/fabric@v2.1.1+incompatible/msp/configbuilder.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package msp
     8  
     9  import (
    10  	"encoding/pem"
    11  	"io/ioutil"
    12  	"os"
    13  	"path/filepath"
    14  
    15  	"github.com/golang/protobuf/proto"
    16  	"github.com/hyperledger/fabric-protos-go/msp"
    17  	"github.com/hyperledger/fabric/bccsp"
    18  	"github.com/hyperledger/fabric/bccsp/factory"
    19  	"github.com/pkg/errors"
    20  	"gopkg.in/yaml.v2"
    21  )
    22  
    23  // OrganizationalUnitIdentifiersConfiguration is used to represent an OU
    24  // and an associated trusted certificate
    25  type OrganizationalUnitIdentifiersConfiguration struct {
    26  	// Certificate is the path to a root or intermediate certificate
    27  	Certificate string `yaml:"Certificate,omitempty"`
    28  	// OrganizationalUnitIdentifier is the name of the OU
    29  	OrganizationalUnitIdentifier string `yaml:"OrganizationalUnitIdentifier,omitempty"`
    30  }
    31  
    32  // NodeOUs contains information on how to tell apart clients, peers and orderers
    33  // based on OUs. If the check is enforced, by setting Enabled to true,
    34  // the MSP will consider an identity valid if it is an identity of a client, a peer or
    35  // an orderer. An identity should have only one of these special OUs.
    36  type NodeOUs struct {
    37  	// Enable activates the OU enforcement
    38  	Enable bool `yaml:"Enable,omitempty"`
    39  	// ClientOUIdentifier specifies how to recognize clients by OU
    40  	ClientOUIdentifier *OrganizationalUnitIdentifiersConfiguration `yaml:"ClientOUIdentifier,omitempty"`
    41  	// PeerOUIdentifier specifies how to recognize peers by OU
    42  	PeerOUIdentifier *OrganizationalUnitIdentifiersConfiguration `yaml:"PeerOUIdentifier,omitempty"`
    43  	// AdminOUIdentifier specifies how to recognize admins by OU
    44  	AdminOUIdentifier *OrganizationalUnitIdentifiersConfiguration `yaml:"AdminOUIdentifier,omitempty"`
    45  	// OrdererOUIdentifier specifies how to recognize admins by OU
    46  	OrdererOUIdentifier *OrganizationalUnitIdentifiersConfiguration `yaml:"OrdererOUIdentifier,omitempty"`
    47  }
    48  
    49  // Configuration represents the accessory configuration an MSP can be equipped with.
    50  // By default, this configuration is stored in a yaml file
    51  type Configuration struct {
    52  	// OrganizationalUnitIdentifiers is a list of OUs. If this is set, the MSP
    53  	// will consider an identity valid only it contains at least one of these OUs
    54  	OrganizationalUnitIdentifiers []*OrganizationalUnitIdentifiersConfiguration `yaml:"OrganizationalUnitIdentifiers,omitempty"`
    55  	// NodeOUs enables the MSP to tell apart clients, peers and orderers based
    56  	// on the identity's OU.
    57  	NodeOUs *NodeOUs `yaml:"NodeOUs,omitempty"`
    58  }
    59  
    60  func readFile(file string) ([]byte, error) {
    61  	fileCont, err := ioutil.ReadFile(file)
    62  	if err != nil {
    63  		return nil, errors.Wrapf(err, "could not read file %s", file)
    64  	}
    65  
    66  	return fileCont, nil
    67  }
    68  
    69  func readPemFile(file string) ([]byte, error) {
    70  	bytes, err := readFile(file)
    71  	if err != nil {
    72  		return nil, errors.Wrapf(err, "reading from file %s failed", file)
    73  	}
    74  
    75  	b, _ := pem.Decode(bytes)
    76  	if b == nil { // TODO: also check that the type is what we expect (cert vs key..)
    77  		return nil, errors.Errorf("no pem content for file %s", file)
    78  	}
    79  
    80  	return bytes, nil
    81  }
    82  
    83  func getPemMaterialFromDir(dir string) ([][]byte, error) {
    84  	mspLogger.Debugf("Reading directory %s", dir)
    85  
    86  	_, err := os.Stat(dir)
    87  	if os.IsNotExist(err) {
    88  		return nil, err
    89  	}
    90  
    91  	content := make([][]byte, 0)
    92  	files, err := ioutil.ReadDir(dir)
    93  	if err != nil {
    94  		return nil, errors.Wrapf(err, "could not read directory %s", dir)
    95  	}
    96  
    97  	for _, f := range files {
    98  		fullName := filepath.Join(dir, f.Name())
    99  
   100  		f, err := os.Stat(fullName)
   101  		if err != nil {
   102  			mspLogger.Warningf("Failed to stat %s: %s", fullName, err)
   103  			continue
   104  		}
   105  		if f.IsDir() {
   106  			continue
   107  		}
   108  
   109  		mspLogger.Debugf("Inspecting file %s", fullName)
   110  
   111  		item, err := readPemFile(fullName)
   112  		if err != nil {
   113  			mspLogger.Warningf("Failed reading file %s: %s", fullName, err)
   114  			continue
   115  		}
   116  
   117  		content = append(content, item)
   118  	}
   119  
   120  	return content, nil
   121  }
   122  
   123  const (
   124  	cacerts              = "cacerts"
   125  	admincerts           = "admincerts"
   126  	signcerts            = "signcerts"
   127  	keystore             = "keystore"
   128  	intermediatecerts    = "intermediatecerts"
   129  	crlsfolder           = "crls"
   130  	configfilename       = "config.yaml"
   131  	tlscacerts           = "tlscacerts"
   132  	tlsintermediatecerts = "tlsintermediatecerts"
   133  )
   134  
   135  func SetupBCCSPKeystoreConfig(bccspConfig *factory.FactoryOpts, keystoreDir string) *factory.FactoryOpts {
   136  	if bccspConfig == nil {
   137  		bccspConfig = factory.GetDefaultOpts()
   138  	}
   139  
   140  	if bccspConfig.ProviderName == "SW" || bccspConfig.SwOpts != nil {
   141  		if bccspConfig.SwOpts == nil {
   142  			bccspConfig.SwOpts = factory.GetDefaultOpts().SwOpts
   143  		}
   144  
   145  		// Only override the KeyStorePath if it was left empty
   146  		if bccspConfig.SwOpts.FileKeystore == nil ||
   147  			bccspConfig.SwOpts.FileKeystore.KeyStorePath == "" {
   148  			bccspConfig.SwOpts.Ephemeral = false
   149  			bccspConfig.SwOpts.FileKeystore = &factory.FileKeystoreOpts{KeyStorePath: keystoreDir}
   150  		}
   151  	}
   152  
   153  	return bccspConfig
   154  }
   155  
   156  // GetLocalMspConfigWithType returns a local MSP
   157  // configuration for the MSP in the specified
   158  // directory, with the specified ID and type
   159  func GetLocalMspConfigWithType(dir string, bccspConfig *factory.FactoryOpts, ID, mspType string) (*msp.MSPConfig, error) {
   160  	switch mspType {
   161  	case ProviderTypeToString(FABRIC):
   162  		return GetLocalMspConfig(dir, bccspConfig, ID)
   163  	case ProviderTypeToString(IDEMIX):
   164  		return GetIdemixMspConfig(dir, ID)
   165  	default:
   166  		return nil, errors.Errorf("unknown MSP type '%s'", mspType)
   167  	}
   168  }
   169  
   170  func GetLocalMspConfig(dir string, bccspConfig *factory.FactoryOpts, ID string) (*msp.MSPConfig, error) {
   171  	signcertDir := filepath.Join(dir, signcerts)
   172  	keystoreDir := filepath.Join(dir, keystore)
   173  	bccspConfig = SetupBCCSPKeystoreConfig(bccspConfig, keystoreDir)
   174  
   175  	err := factory.InitFactories(bccspConfig)
   176  	if err != nil {
   177  		return nil, errors.WithMessage(err, "could not initialize BCCSP Factories")
   178  	}
   179  
   180  	signcert, err := getPemMaterialFromDir(signcertDir)
   181  	if err != nil || len(signcert) == 0 {
   182  		return nil, errors.Wrapf(err, "could not load a valid signer certificate from directory %s", signcertDir)
   183  	}
   184  
   185  	/* FIXME: for now we're making the following assumptions
   186  	1) there is exactly one signing cert
   187  	2) BCCSP's KeyStore has the private key that matches SKI of
   188  	   signing cert
   189  	*/
   190  
   191  	sigid := &msp.SigningIdentityInfo{PublicSigner: signcert[0], PrivateSigner: nil}
   192  
   193  	return getMspConfig(dir, ID, sigid)
   194  }
   195  
   196  // GetVerifyingMspConfig returns an MSP config given directory, ID and type
   197  func GetVerifyingMspConfig(dir, ID, mspType string) (*msp.MSPConfig, error) {
   198  	switch mspType {
   199  	case ProviderTypeToString(FABRIC):
   200  		return getMspConfig(dir, ID, nil)
   201  	case ProviderTypeToString(IDEMIX):
   202  		return GetIdemixMspConfig(dir, ID)
   203  	default:
   204  		return nil, errors.Errorf("unknown MSP type '%s'", mspType)
   205  	}
   206  }
   207  
   208  func getMspConfig(dir string, ID string, sigid *msp.SigningIdentityInfo) (*msp.MSPConfig, error) {
   209  	cacertDir := filepath.Join(dir, cacerts)
   210  	admincertDir := filepath.Join(dir, admincerts)
   211  	intermediatecertsDir := filepath.Join(dir, intermediatecerts)
   212  	crlsDir := filepath.Join(dir, crlsfolder)
   213  	configFile := filepath.Join(dir, configfilename)
   214  	tlscacertDir := filepath.Join(dir, tlscacerts)
   215  	tlsintermediatecertsDir := filepath.Join(dir, tlsintermediatecerts)
   216  
   217  	cacerts, err := getPemMaterialFromDir(cacertDir)
   218  	if err != nil || len(cacerts) == 0 {
   219  		return nil, errors.WithMessagef(err, "could not load a valid ca certificate from directory %s", cacertDir)
   220  	}
   221  
   222  	admincert, err := getPemMaterialFromDir(admincertDir)
   223  	if err != nil && !os.IsNotExist(err) {
   224  		return nil, errors.WithMessagef(err, "could not load a valid admin certificate from directory %s", admincertDir)
   225  	}
   226  
   227  	intermediatecerts, err := getPemMaterialFromDir(intermediatecertsDir)
   228  	if os.IsNotExist(err) {
   229  		mspLogger.Debugf("Intermediate certs folder not found at [%s]. Skipping. [%s]", intermediatecertsDir, err)
   230  	} else if err != nil {
   231  		return nil, errors.WithMessagef(err, "failed loading intermediate ca certs at [%s]", intermediatecertsDir)
   232  	}
   233  
   234  	tlsCACerts, err := getPemMaterialFromDir(tlscacertDir)
   235  	tlsIntermediateCerts := [][]byte{}
   236  	if os.IsNotExist(err) {
   237  		mspLogger.Debugf("TLS CA certs folder not found at [%s]. Skipping and ignoring TLS intermediate CA folder. [%s]", tlsintermediatecertsDir, err)
   238  	} else if err != nil {
   239  		return nil, errors.WithMessagef(err, "failed loading TLS ca certs at [%s]", tlsintermediatecertsDir)
   240  	} else if len(tlsCACerts) != 0 {
   241  		tlsIntermediateCerts, err = getPemMaterialFromDir(tlsintermediatecertsDir)
   242  		if os.IsNotExist(err) {
   243  			mspLogger.Debugf("TLS intermediate certs folder not found at [%s]. Skipping. [%s]", tlsintermediatecertsDir, err)
   244  		} else if err != nil {
   245  			return nil, errors.WithMessagef(err, "failed loading TLS intermediate ca certs at [%s]", tlsintermediatecertsDir)
   246  		}
   247  	} else {
   248  		mspLogger.Debugf("TLS CA certs folder at [%s] is empty. Skipping.", tlsintermediatecertsDir)
   249  	}
   250  
   251  	crls, err := getPemMaterialFromDir(crlsDir)
   252  	if os.IsNotExist(err) {
   253  		mspLogger.Debugf("crls folder not found at [%s]. Skipping. [%s]", crlsDir, err)
   254  	} else if err != nil {
   255  		return nil, errors.WithMessagef(err, "failed loading crls at [%s]", crlsDir)
   256  	}
   257  
   258  	// Load configuration file
   259  	// if the configuration file is there then load it
   260  	// otherwise skip it
   261  	var ouis []*msp.FabricOUIdentifier
   262  	var nodeOUs *msp.FabricNodeOUs
   263  	_, err = os.Stat(configFile)
   264  	if err == nil {
   265  		// load the file, if there is a failure in loading it then
   266  		// return an error
   267  		raw, err := ioutil.ReadFile(configFile)
   268  		if err != nil {
   269  			return nil, errors.Wrapf(err, "failed loading configuration file at [%s]", configFile)
   270  		}
   271  
   272  		configuration := Configuration{}
   273  		err = yaml.Unmarshal(raw, &configuration)
   274  		if err != nil {
   275  			return nil, errors.Wrapf(err, "failed unmarshalling configuration file at [%s]", configFile)
   276  		}
   277  
   278  		// Prepare OrganizationalUnitIdentifiers
   279  		if len(configuration.OrganizationalUnitIdentifiers) > 0 {
   280  			for _, ouID := range configuration.OrganizationalUnitIdentifiers {
   281  				f := filepath.Join(dir, ouID.Certificate)
   282  				raw, err = readFile(f)
   283  				if err != nil {
   284  					return nil, errors.Wrapf(err, "failed loading OrganizationalUnit certificate at [%s]", f)
   285  				}
   286  
   287  				oui := &msp.FabricOUIdentifier{
   288  					Certificate:                  raw,
   289  					OrganizationalUnitIdentifier: ouID.OrganizationalUnitIdentifier,
   290  				}
   291  				ouis = append(ouis, oui)
   292  			}
   293  		}
   294  
   295  		// Prepare NodeOUs
   296  		if configuration.NodeOUs != nil && configuration.NodeOUs.Enable {
   297  			mspLogger.Debug("Loading NodeOUs")
   298  			nodeOUs = &msp.FabricNodeOUs{
   299  				Enable: true,
   300  			}
   301  			if configuration.NodeOUs.ClientOUIdentifier != nil && len(configuration.NodeOUs.ClientOUIdentifier.OrganizationalUnitIdentifier) != 0 {
   302  				nodeOUs.ClientOuIdentifier = &msp.FabricOUIdentifier{OrganizationalUnitIdentifier: configuration.NodeOUs.ClientOUIdentifier.OrganizationalUnitIdentifier}
   303  			}
   304  			if configuration.NodeOUs.PeerOUIdentifier != nil && len(configuration.NodeOUs.PeerOUIdentifier.OrganizationalUnitIdentifier) != 0 {
   305  				nodeOUs.PeerOuIdentifier = &msp.FabricOUIdentifier{OrganizationalUnitIdentifier: configuration.NodeOUs.PeerOUIdentifier.OrganizationalUnitIdentifier}
   306  			}
   307  			if configuration.NodeOUs.AdminOUIdentifier != nil && len(configuration.NodeOUs.AdminOUIdentifier.OrganizationalUnitIdentifier) != 0 {
   308  				nodeOUs.AdminOuIdentifier = &msp.FabricOUIdentifier{OrganizationalUnitIdentifier: configuration.NodeOUs.AdminOUIdentifier.OrganizationalUnitIdentifier}
   309  			}
   310  			if configuration.NodeOUs.OrdererOUIdentifier != nil && len(configuration.NodeOUs.OrdererOUIdentifier.OrganizationalUnitIdentifier) != 0 {
   311  				nodeOUs.OrdererOuIdentifier = &msp.FabricOUIdentifier{OrganizationalUnitIdentifier: configuration.NodeOUs.OrdererOUIdentifier.OrganizationalUnitIdentifier}
   312  			}
   313  
   314  			// Read certificates, if defined
   315  
   316  			// ClientOU
   317  			if nodeOUs.ClientOuIdentifier != nil {
   318  				nodeOUs.ClientOuIdentifier.Certificate = loadCertificateAt(dir, configuration.NodeOUs.ClientOUIdentifier.Certificate, "ClientOU")
   319  			}
   320  			// PeerOU
   321  			if nodeOUs.PeerOuIdentifier != nil {
   322  				nodeOUs.PeerOuIdentifier.Certificate = loadCertificateAt(dir, configuration.NodeOUs.PeerOUIdentifier.Certificate, "PeerOU")
   323  			}
   324  			// AdminOU
   325  			if nodeOUs.AdminOuIdentifier != nil {
   326  				nodeOUs.AdminOuIdentifier.Certificate = loadCertificateAt(dir, configuration.NodeOUs.AdminOUIdentifier.Certificate, "AdminOU")
   327  			}
   328  			// OrdererOU
   329  			if nodeOUs.OrdererOuIdentifier != nil {
   330  				nodeOUs.OrdererOuIdentifier.Certificate = loadCertificateAt(dir, configuration.NodeOUs.OrdererOUIdentifier.Certificate, "OrdererOU")
   331  			}
   332  		}
   333  	} else {
   334  		mspLogger.Debugf("MSP configuration file not found at [%s]: [%s]", configFile, err)
   335  	}
   336  
   337  	// Set FabricCryptoConfig
   338  	cryptoConfig := &msp.FabricCryptoConfig{
   339  		SignatureHashFamily:            bccsp.SHA2,
   340  		IdentityIdentifierHashFunction: bccsp.SHA256,
   341  	}
   342  
   343  	// Compose FabricMSPConfig
   344  	fmspconf := &msp.FabricMSPConfig{
   345  		Admins:                        admincert,
   346  		RootCerts:                     cacerts,
   347  		IntermediateCerts:             intermediatecerts,
   348  		SigningIdentity:               sigid,
   349  		Name:                          ID,
   350  		OrganizationalUnitIdentifiers: ouis,
   351  		RevocationList:                crls,
   352  		CryptoConfig:                  cryptoConfig,
   353  		TlsRootCerts:                  tlsCACerts,
   354  		TlsIntermediateCerts:          tlsIntermediateCerts,
   355  		FabricNodeOus:                 nodeOUs,
   356  	}
   357  
   358  	fmpsjs, _ := proto.Marshal(fmspconf)
   359  
   360  	mspconf := &msp.MSPConfig{Config: fmpsjs, Type: int32(FABRIC)}
   361  
   362  	return mspconf, nil
   363  }
   364  
   365  func loadCertificateAt(dir, certificatePath string, ouType string) []byte {
   366  	f := filepath.Join(dir, certificatePath)
   367  	raw, err := readFile(f)
   368  	if err != nil {
   369  		mspLogger.Warnf("Failed loading %s certificate at [%s]: [%s]", ouType, f, err)
   370  	} else {
   371  		return raw
   372  	}
   373  
   374  	return nil
   375  }
   376  
   377  const (
   378  	IdemixConfigDirMsp                  = "msp"
   379  	IdemixConfigDirUser                 = "user"
   380  	IdemixConfigFileIssuerPublicKey     = "IssuerPublicKey"
   381  	IdemixConfigFileRevocationPublicKey = "RevocationPublicKey"
   382  	IdemixConfigFileSigner              = "SignerConfig"
   383  )
   384  
   385  // GetIdemixMspConfig returns the configuration for the Idemix MSP
   386  func GetIdemixMspConfig(dir string, ID string) (*msp.MSPConfig, error) {
   387  	ipkBytes, err := readFile(filepath.Join(dir, IdemixConfigDirMsp, IdemixConfigFileIssuerPublicKey))
   388  	if err != nil {
   389  		return nil, errors.Wrapf(err, "failed to read issuer public key file")
   390  	}
   391  
   392  	revocationPkBytes, err := readFile(filepath.Join(dir, IdemixConfigDirMsp, IdemixConfigFileRevocationPublicKey))
   393  	if err != nil {
   394  		return nil, errors.Wrapf(err, "failed to read revocation public key file")
   395  	}
   396  
   397  	idemixConfig := &msp.IdemixMSPConfig{
   398  		Name:         ID,
   399  		Ipk:          ipkBytes,
   400  		RevocationPk: revocationPkBytes,
   401  	}
   402  
   403  	signerBytes, err := readFile(filepath.Join(dir, IdemixConfigDirUser, IdemixConfigFileSigner))
   404  	if err == nil {
   405  		signerConfig := &msp.IdemixMSPSignerConfig{}
   406  		err = proto.Unmarshal(signerBytes, signerConfig)
   407  		if err != nil {
   408  			return nil, err
   409  		}
   410  		idemixConfig.Signer = signerConfig
   411  	}
   412  
   413  	confBytes, err := proto.Marshal(idemixConfig)
   414  	if err != nil {
   415  		return nil, err
   416  	}
   417  
   418  	return &msp.MSPConfig{Config: confBytes, Type: int32(IDEMIX)}, nil
   419  }