982 lines
33 KiB
Go
982 lines
33 KiB
Go
/*
|
|
Copyright IBM Corp. All Rights Reserved.
|
|
|
|
SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
package msp
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/x509"
|
|
"crypto/x509/pkix"
|
|
"encoding/asn1"
|
|
"encoding/hex"
|
|
"encoding/pem"
|
|
"strings"
|
|
|
|
"github.com/golang/protobuf/proto"
|
|
m "github.com/hyperledger/fabric-protos-go/msp"
|
|
"github.com/hyperledger/fabric/bccsp"
|
|
"github.com/hyperledger/fabric/bccsp/factory"
|
|
"github.com/hyperledger/fabric/bccsp/signer"
|
|
"github.com/hyperledger/fabric/bccsp/sw"
|
|
"github.com/hyperledger/fabric/bccsp/utils"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
// mspSetupFuncType is the prototype of the setup function
|
|
type mspSetupFuncType func(config *m.FabricMSPConfig) error
|
|
|
|
// validateIdentityOUsFuncType is the prototype of the function to validate identity's OUs
|
|
type validateIdentityOUsFuncType func(id *identity) error
|
|
|
|
// satisfiesPrincipalInternalFuncType is the prototype of the function to check if principals are satisfied
|
|
type satisfiesPrincipalInternalFuncType func(id Identity, principal *m.MSPPrincipal) error
|
|
|
|
// setupAdminInternalFuncType is a prototype of the function to setup the admins
|
|
type setupAdminInternalFuncType func(conf *m.FabricMSPConfig) error
|
|
|
|
// This is an instantiation of an MSP that
|
|
// uses BCCSP for its cryptographic primitives.
|
|
type bccspmsp struct {
|
|
// version specifies the behaviour of this msp
|
|
version MSPVersion
|
|
// The following function pointers are used to change the behaviour
|
|
// of this MSP depending on its version.
|
|
// internalSetupFunc is the pointer to the setup function
|
|
internalSetupFunc mspSetupFuncType
|
|
|
|
// internalValidateIdentityOusFunc is the pointer to the function to validate identity's OUs
|
|
internalValidateIdentityOusFunc validateIdentityOUsFuncType
|
|
|
|
// internalSatisfiesPrincipalInternalFunc is the pointer to the function to check if principals are satisfied
|
|
internalSatisfiesPrincipalInternalFunc satisfiesPrincipalInternalFuncType
|
|
|
|
// internalSetupAdmin is the pointer to the function that setup the administrators of this msp
|
|
internalSetupAdmin setupAdminInternalFuncType
|
|
|
|
// list of CA certs we trust
|
|
rootCerts []Identity
|
|
|
|
// list of intermediate certs we trust
|
|
intermediateCerts []Identity
|
|
|
|
// list of CA TLS certs we trust
|
|
tlsRootCerts [][]byte
|
|
|
|
// list of intermediate TLS certs we trust
|
|
tlsIntermediateCerts [][]byte
|
|
|
|
// certificationTreeInternalNodesMap whose keys correspond to the raw material
|
|
// (DER representation) of a certificate casted to a string, and whose values
|
|
// are boolean. True means that the certificate is an internal node of the certification tree.
|
|
// False means that the certificate corresponds to a leaf of the certification tree.
|
|
certificationTreeInternalNodesMap map[string]bool
|
|
|
|
// list of signing identities
|
|
signer SigningIdentity
|
|
|
|
// list of admin identities
|
|
admins []Identity
|
|
|
|
// the crypto provider
|
|
bccsp bccsp.BCCSP
|
|
|
|
// the provider identifier for this MSP
|
|
name string
|
|
|
|
// verification options for MSP members
|
|
opts *x509.VerifyOptions
|
|
|
|
// list of certificate revocation lists
|
|
CRL []*pkix.CertificateList
|
|
|
|
// list of OUs
|
|
ouIdentifiers map[string][][]byte
|
|
|
|
// cryptoConfig contains
|
|
cryptoConfig *m.FabricCryptoConfig
|
|
|
|
// NodeOUs configuration
|
|
ouEnforcement bool
|
|
// These are the OUIdentifiers of the clients, peers, admins and orderers.
|
|
// They are used to tell apart these entities
|
|
clientOU, peerOU, adminOU, ordererOU *OUIdentifier
|
|
}
|
|
|
|
// newBccspMsp returns an MSP instance backed up by a BCCSP
|
|
// crypto provider. It handles x.509 certificates and can
|
|
// generate identities and signing identities backed by
|
|
// certificates and keypairs
|
|
func newBccspMsp(version MSPVersion, defaultBCCSP bccsp.BCCSP) (MSP, error) {
|
|
mspLogger.Debugf("Creating BCCSP-based MSP instance")
|
|
|
|
theMsp := &bccspmsp{}
|
|
theMsp.version = version
|
|
theMsp.bccsp = defaultBCCSP
|
|
switch version {
|
|
case MSPv1_0:
|
|
theMsp.internalSetupFunc = theMsp.setupV1
|
|
theMsp.internalValidateIdentityOusFunc = theMsp.validateIdentityOUsV1
|
|
theMsp.internalSatisfiesPrincipalInternalFunc = theMsp.satisfiesPrincipalInternalPreV13
|
|
theMsp.internalSetupAdmin = theMsp.setupAdminsPreV142
|
|
case MSPv1_1:
|
|
theMsp.internalSetupFunc = theMsp.setupV11
|
|
theMsp.internalValidateIdentityOusFunc = theMsp.validateIdentityOUsV11
|
|
theMsp.internalSatisfiesPrincipalInternalFunc = theMsp.satisfiesPrincipalInternalPreV13
|
|
theMsp.internalSetupAdmin = theMsp.setupAdminsPreV142
|
|
case MSPv1_3:
|
|
theMsp.internalSetupFunc = theMsp.setupV11
|
|
theMsp.internalValidateIdentityOusFunc = theMsp.validateIdentityOUsV11
|
|
theMsp.internalSatisfiesPrincipalInternalFunc = theMsp.satisfiesPrincipalInternalV13
|
|
theMsp.internalSetupAdmin = theMsp.setupAdminsPreV142
|
|
case MSPv1_4_3:
|
|
theMsp.internalSetupFunc = theMsp.setupV142
|
|
theMsp.internalValidateIdentityOusFunc = theMsp.validateIdentityOUsV142
|
|
theMsp.internalSatisfiesPrincipalInternalFunc = theMsp.satisfiesPrincipalInternalV142
|
|
theMsp.internalSetupAdmin = theMsp.setupAdminsV142
|
|
default:
|
|
return nil, errors.Errorf("Invalid MSP version [%v]", version)
|
|
}
|
|
|
|
return theMsp, nil
|
|
}
|
|
|
|
// NewBccspMspWithKeyStore allows to create a BCCSP-based MSP whose underlying
|
|
// crypto material is available through the passed keystore
|
|
func NewBccspMspWithKeyStore(version MSPVersion, keyStore bccsp.KeyStore, bccsp bccsp.BCCSP) (MSP, error) {
|
|
thisMSP, err := newBccspMsp(version, bccsp)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
csp, err := sw.NewWithParams(
|
|
factory.GetDefaultOpts().SW.Security,
|
|
factory.GetDefaultOpts().SW.Hash,
|
|
keyStore)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
thisMSP.(*bccspmsp).bccsp = csp
|
|
|
|
return thisMSP, nil
|
|
}
|
|
|
|
func (msp *bccspmsp) getCertFromPem(idBytes []byte) (*x509.Certificate, error) {
|
|
if idBytes == nil {
|
|
return nil, errors.New("getCertFromPem error: nil idBytes")
|
|
}
|
|
|
|
// Decode the pem bytes
|
|
pemCert, _ := pem.Decode(idBytes)
|
|
if pemCert == nil {
|
|
return nil, errors.Errorf("getCertFromPem error: could not decode pem bytes [%v]", idBytes)
|
|
}
|
|
|
|
// get a cert
|
|
var cert *x509.Certificate
|
|
cert, err := x509.ParseCertificate(pemCert.Bytes)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "getCertFromPem error: failed to parse x509 cert")
|
|
}
|
|
|
|
return cert, nil
|
|
}
|
|
|
|
func (msp *bccspmsp) getIdentityFromConf(idBytes []byte) (Identity, bccsp.Key, error) {
|
|
// get a cert
|
|
cert, err := msp.getCertFromPem(idBytes)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
// get the public key in the right format
|
|
certPubK, err := msp.bccsp.KeyImport(cert, &bccsp.X509PublicKeyImportOpts{Temporary: true})
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
mspId, err := newIdentity(cert, certPubK, msp)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
return mspId, certPubK, nil
|
|
}
|
|
|
|
func (msp *bccspmsp) getSigningIdentityFromConf(sidInfo *m.SigningIdentityInfo) (SigningIdentity, error) {
|
|
if sidInfo == nil {
|
|
return nil, errors.New("getIdentityFromBytes error: nil sidInfo")
|
|
}
|
|
|
|
// Extract the public part of the identity
|
|
idPub, pubKey, err := msp.getIdentityFromConf(sidInfo.PublicSigner)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Find the matching private key in the BCCSP keystore
|
|
privKey, err := msp.bccsp.GetKey(pubKey.SKI())
|
|
// Less Secure: Attempt to import Private Key from KeyInfo, if BCCSP was not able to find the key
|
|
if err != nil {
|
|
mspLogger.Debugf("Could not find SKI [%s], trying KeyMaterial field: %+v\n", hex.EncodeToString(pubKey.SKI()), err)
|
|
if sidInfo.PrivateSigner == nil || sidInfo.PrivateSigner.KeyMaterial == nil {
|
|
return nil, errors.New("KeyMaterial not found in SigningIdentityInfo")
|
|
}
|
|
|
|
pemKey, _ := pem.Decode(sidInfo.PrivateSigner.KeyMaterial)
|
|
if pemKey == nil {
|
|
return nil, errors.Errorf("%s: wrong PEM encoding", sidInfo.PrivateSigner.KeyIdentifier)
|
|
}
|
|
privKey, err = msp.bccsp.KeyImport(pemKey.Bytes, &bccsp.ECDSAPrivateKeyImportOpts{Temporary: true})
|
|
if err != nil {
|
|
return nil, errors.WithMessage(err, "getIdentityFromBytes error: Failed to import EC private key")
|
|
}
|
|
}
|
|
|
|
// get the peer signer
|
|
peerSigner, err := signer.New(msp.bccsp, privKey)
|
|
if err != nil {
|
|
return nil, errors.WithMessage(err, "getIdentityFromBytes error: Failed initializing bccspCryptoSigner")
|
|
}
|
|
|
|
return newSigningIdentity(idPub.(*identity).cert, idPub.(*identity).pk, peerSigner, msp)
|
|
}
|
|
|
|
// Setup sets up the internal data structures
|
|
// for this MSP, given an MSPConfig ref; it
|
|
// returns nil in case of success or an error otherwise
|
|
func (msp *bccspmsp) Setup(conf1 *m.MSPConfig) error {
|
|
if conf1 == nil {
|
|
return errors.New("Setup error: nil conf reference")
|
|
}
|
|
|
|
// given that it's an msp of type fabric, extract the MSPConfig instance
|
|
conf := &m.FabricMSPConfig{}
|
|
err := proto.Unmarshal(conf1.Config, conf)
|
|
if err != nil {
|
|
return errors.Wrap(err, "failed unmarshalling fabric msp config")
|
|
}
|
|
|
|
// set the name for this msp
|
|
msp.name = conf.Name
|
|
mspLogger.Debugf("Setting up MSP instance %s", msp.name)
|
|
|
|
// setup
|
|
return msp.internalSetupFunc(conf)
|
|
}
|
|
|
|
// GetVersion returns the version of this MSP
|
|
func (msp *bccspmsp) GetVersion() MSPVersion {
|
|
return msp.version
|
|
}
|
|
|
|
// GetType returns the type for this MSP
|
|
func (msp *bccspmsp) GetType() ProviderType {
|
|
return FABRIC
|
|
}
|
|
|
|
// GetIdentifier returns the MSP identifier for this instance
|
|
func (msp *bccspmsp) GetIdentifier() (string, error) {
|
|
return msp.name, nil
|
|
}
|
|
|
|
// GetTLSRootCerts returns the root certificates for this MSP
|
|
func (msp *bccspmsp) GetTLSRootCerts() [][]byte {
|
|
return msp.tlsRootCerts
|
|
}
|
|
|
|
// GetTLSIntermediateCerts returns the intermediate root certificates for this MSP
|
|
func (msp *bccspmsp) GetTLSIntermediateCerts() [][]byte {
|
|
return msp.tlsIntermediateCerts
|
|
}
|
|
|
|
// GetDefaultSigningIdentity returns the
|
|
// default signing identity for this MSP (if any)
|
|
func (msp *bccspmsp) GetDefaultSigningIdentity() (SigningIdentity, error) {
|
|
mspLogger.Debugf("Obtaining default signing identity")
|
|
|
|
if msp.signer == nil {
|
|
return nil, errors.New("this MSP does not possess a valid default signing identity")
|
|
}
|
|
|
|
return msp.signer, nil
|
|
}
|
|
|
|
// Validate attempts to determine whether
|
|
// the supplied identity is valid according
|
|
// to this MSP's roots of trust; it returns
|
|
// nil in case the identity is valid or an
|
|
// error otherwise
|
|
func (msp *bccspmsp) Validate(id Identity) error {
|
|
mspLogger.Debugf("MSP %s validating identity", msp.name)
|
|
|
|
switch id := id.(type) {
|
|
// If this identity is of this specific type,
|
|
// this is how I can validate it given the
|
|
// root of trust this MSP has
|
|
case *identity:
|
|
return msp.validateIdentity(id)
|
|
default:
|
|
return errors.New("identity type not recognized")
|
|
}
|
|
}
|
|
|
|
// hasOURole checks that the identity belongs to the organizational unit
|
|
// associated to the specified MSPRole.
|
|
// This function does not check the certifiers identifier.
|
|
// Appropriate validation needs to be enforced before.
|
|
func (msp *bccspmsp) hasOURole(id Identity, mspRole m.MSPRole_MSPRoleType) error {
|
|
// Check NodeOUs
|
|
if !msp.ouEnforcement {
|
|
return errors.New("NodeOUs not activated. Cannot tell apart identities.")
|
|
}
|
|
|
|
mspLogger.Debugf("MSP %s checking if the identity is a client", msp.name)
|
|
|
|
switch id := id.(type) {
|
|
// If this identity is of this specific type,
|
|
// this is how I can validate it given the
|
|
// root of trust this MSP has
|
|
case *identity:
|
|
return msp.hasOURoleInternal(id, mspRole)
|
|
default:
|
|
return errors.New("Identity type not recognized")
|
|
}
|
|
}
|
|
|
|
func (msp *bccspmsp) hasOURoleInternal(id *identity, mspRole m.MSPRole_MSPRoleType) error {
|
|
var nodeOU *OUIdentifier
|
|
switch mspRole {
|
|
case m.MSPRole_CLIENT:
|
|
nodeOU = msp.clientOU
|
|
case m.MSPRole_PEER:
|
|
nodeOU = msp.peerOU
|
|
case m.MSPRole_ADMIN:
|
|
nodeOU = msp.adminOU
|
|
case m.MSPRole_ORDERER:
|
|
nodeOU = msp.ordererOU
|
|
default:
|
|
return errors.New("Invalid MSPRoleType. It must be CLIENT, PEER, ADMIN or ORDERER")
|
|
}
|
|
|
|
if nodeOU == nil {
|
|
return errors.Errorf("cannot test for classification, node ou for type [%s], not defined, msp: [%s]", mspRole, msp.name)
|
|
}
|
|
|
|
for _, OU := range id.GetOrganizationalUnits() {
|
|
if OU.OrganizationalUnitIdentifier == nodeOU.OrganizationalUnitIdentifier {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
return errors.Errorf("The identity does not contain OU [%s], MSP: [%s]", mspRole, msp.name)
|
|
}
|
|
|
|
// DeserializeIdentity returns an Identity given the byte-level
|
|
// representation of a SerializedIdentity struct
|
|
func (msp *bccspmsp) DeserializeIdentity(serializedID []byte) (Identity, error) {
|
|
mspLogger.Debug("Obtaining identity")
|
|
|
|
// We first deserialize to a SerializedIdentity to get the MSP ID
|
|
sId := &m.SerializedIdentity{}
|
|
err := proto.Unmarshal(serializedID, sId)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "could not deserialize a SerializedIdentity")
|
|
}
|
|
|
|
if sId.Mspid != msp.name {
|
|
return nil, errors.Errorf("expected MSP ID %s, received %s", msp.name, sId.Mspid)
|
|
}
|
|
|
|
return msp.deserializeIdentityInternal(sId.IdBytes)
|
|
}
|
|
|
|
// deserializeIdentityInternal returns an identity given its byte-level representation
|
|
func (msp *bccspmsp) deserializeIdentityInternal(serializedIdentity []byte) (Identity, error) {
|
|
// This MSP will always deserialize certs this way
|
|
bl, _ := pem.Decode(serializedIdentity)
|
|
if bl == nil {
|
|
return nil, errors.New("could not decode the PEM structure")
|
|
}
|
|
cert, err := x509.ParseCertificate(bl.Bytes)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "parseCertificate failed")
|
|
}
|
|
|
|
// Now we have the certificate; make sure that its fields
|
|
// (e.g. the Issuer.OU or the Subject.OU) match with the
|
|
// MSP id that this MSP has; otherwise it might be an attack
|
|
// TODO!
|
|
// We can't do it yet because there is no standardized way
|
|
// (yet) to encode the MSP ID into the x.509 body of a cert
|
|
|
|
pub, err := msp.bccsp.KeyImport(cert, &bccsp.X509PublicKeyImportOpts{Temporary: true})
|
|
if err != nil {
|
|
return nil, errors.WithMessage(err, "failed to import certificate's public key")
|
|
}
|
|
|
|
return newIdentity(cert, pub, msp)
|
|
}
|
|
|
|
// SatisfiesPrincipal returns nil if the identity matches the principal or an error otherwise
|
|
func (msp *bccspmsp) SatisfiesPrincipal(id Identity, principal *m.MSPPrincipal) error {
|
|
principals, err := collectPrincipals(principal, msp.GetVersion())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, principal := range principals {
|
|
err = msp.internalSatisfiesPrincipalInternalFunc(id, principal)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// collectPrincipals collects principals from combined principals into a single MSPPrincipal slice.
|
|
func collectPrincipals(principal *m.MSPPrincipal, mspVersion MSPVersion) ([]*m.MSPPrincipal, error) {
|
|
switch principal.PrincipalClassification {
|
|
case m.MSPPrincipal_COMBINED:
|
|
// Combined principals are not supported in MSP v1.0 or v1.1
|
|
if mspVersion <= MSPv1_1 {
|
|
return nil, errors.Errorf("invalid principal type %d", int32(principal.PrincipalClassification))
|
|
}
|
|
// Principal is a combination of multiple principals.
|
|
principals := &m.CombinedPrincipal{}
|
|
err := proto.Unmarshal(principal.Principal, principals)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "could not unmarshal CombinedPrincipal from principal")
|
|
}
|
|
// Return an error if there are no principals in the combined principal.
|
|
if len(principals.Principals) == 0 {
|
|
return nil, errors.New("No principals in CombinedPrincipal")
|
|
}
|
|
// Recursively call msp.collectPrincipals for all combined principals.
|
|
// There is no limit for the levels of nesting for the combined principals.
|
|
var principalsSlice []*m.MSPPrincipal
|
|
for _, cp := range principals.Principals {
|
|
internalSlice, err := collectPrincipals(cp, mspVersion)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
principalsSlice = append(principalsSlice, internalSlice...)
|
|
}
|
|
// All the combined principals have been collected into principalsSlice
|
|
return principalsSlice, nil
|
|
default:
|
|
return []*m.MSPPrincipal{principal}, nil
|
|
}
|
|
}
|
|
|
|
// satisfiesPrincipalInternalPreV13 takes as arguments the identity and the principal.
|
|
// The function returns an error if one occurred.
|
|
// The function implements the behavior of an MSP up to and including v1.1.
|
|
func (msp *bccspmsp) satisfiesPrincipalInternalPreV13(id Identity, principal *m.MSPPrincipal) error {
|
|
switch principal.PrincipalClassification {
|
|
// in this case, we have to check whether the
|
|
// identity has a role in the msp - member or admin
|
|
case m.MSPPrincipal_ROLE:
|
|
// Principal contains the msp role
|
|
mspRole := &m.MSPRole{}
|
|
err := proto.Unmarshal(principal.Principal, mspRole)
|
|
if err != nil {
|
|
return errors.Wrap(err, "could not unmarshal MSPRole from principal")
|
|
}
|
|
|
|
// at first, we check whether the MSP
|
|
// identifier is the same as that of the identity
|
|
if mspRole.MspIdentifier != msp.name {
|
|
return errors.Errorf("the identity is a member of a different MSP (expected %s, got %s)", mspRole.MspIdentifier, id.GetMSPIdentifier())
|
|
}
|
|
|
|
// now we validate the different msp roles
|
|
switch mspRole.Role {
|
|
case m.MSPRole_MEMBER:
|
|
// in the case of member, we simply check
|
|
// whether this identity is valid for the MSP
|
|
mspLogger.Debugf("Checking if identity satisfies MEMBER role for %s", msp.name)
|
|
return msp.Validate(id)
|
|
case m.MSPRole_ADMIN:
|
|
mspLogger.Debugf("Checking if identity satisfies ADMIN role for %s", msp.name)
|
|
// in the case of admin, we check that the
|
|
// id is exactly one of our admins
|
|
if msp.isInAdmins(id.(*identity)) {
|
|
return nil
|
|
}
|
|
return errors.New("This identity is not an admin")
|
|
case m.MSPRole_CLIENT:
|
|
fallthrough
|
|
case m.MSPRole_PEER:
|
|
mspLogger.Debugf("Checking if identity satisfies role [%s] for %s", m.MSPRole_MSPRoleType_name[int32(mspRole.Role)], msp.name)
|
|
if err := msp.Validate(id); err != nil {
|
|
return errors.Wrapf(err, "The identity is not valid under this MSP [%s]", msp.name)
|
|
}
|
|
|
|
if err := msp.hasOURole(id, mspRole.Role); err != nil {
|
|
return errors.Wrapf(err, "The identity is not a [%s] under this MSP [%s]", m.MSPRole_MSPRoleType_name[int32(mspRole.Role)], msp.name)
|
|
}
|
|
return nil
|
|
default:
|
|
return errors.Errorf("invalid MSP role type %d", int32(mspRole.Role))
|
|
}
|
|
case m.MSPPrincipal_IDENTITY:
|
|
// in this case we have to deserialize the principal's identity
|
|
// and compare it byte-by-byte with our cert
|
|
principalId, err := msp.DeserializeIdentity(principal.Principal)
|
|
if err != nil {
|
|
return errors.WithMessage(err, "invalid identity principal, not a certificate")
|
|
}
|
|
|
|
if bytes.Equal(id.(*identity).cert.Raw, principalId.(*identity).cert.Raw) {
|
|
return principalId.Validate()
|
|
}
|
|
|
|
return errors.New("The identities do not match")
|
|
case m.MSPPrincipal_ORGANIZATION_UNIT:
|
|
// Principal contains the OrganizationUnit
|
|
OU := &m.OrganizationUnit{}
|
|
err := proto.Unmarshal(principal.Principal, OU)
|
|
if err != nil {
|
|
return errors.Wrap(err, "could not unmarshal OrganizationUnit from principal")
|
|
}
|
|
|
|
// at first, we check whether the MSP
|
|
// identifier is the same as that of the identity
|
|
if OU.MspIdentifier != msp.name {
|
|
return errors.Errorf("the identity is a member of a different MSP (expected %s, got %s)", OU.MspIdentifier, id.GetMSPIdentifier())
|
|
}
|
|
|
|
// we then check if the identity is valid with this MSP
|
|
// and fail if it is not
|
|
err = msp.Validate(id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// now we check whether any of this identity's OUs match the requested one
|
|
for _, ou := range id.GetOrganizationalUnits() {
|
|
if ou.OrganizationalUnitIdentifier == OU.OrganizationalUnitIdentifier &&
|
|
bytes.Equal(ou.CertifiersIdentifier, OU.CertifiersIdentifier) {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// if we are here, no match was found, return an error
|
|
return errors.New("The identities do not match")
|
|
default:
|
|
return errors.Errorf("invalid principal type %d", int32(principal.PrincipalClassification))
|
|
}
|
|
}
|
|
|
|
// satisfiesPrincipalInternalV13 takes as arguments the identity and the principal.
|
|
// The function returns an error if one occurred.
|
|
// The function implements the additional behavior expected of an MSP starting from v1.3.
|
|
// For pre-v1.3 functionality, the function calls the satisfiesPrincipalInternalPreV13.
|
|
func (msp *bccspmsp) satisfiesPrincipalInternalV13(id Identity, principal *m.MSPPrincipal) error {
|
|
switch principal.PrincipalClassification {
|
|
case m.MSPPrincipal_COMBINED:
|
|
return errors.New("SatisfiesPrincipalInternal shall not be called with a CombinedPrincipal")
|
|
case m.MSPPrincipal_ANONYMITY:
|
|
anon := &m.MSPIdentityAnonymity{}
|
|
err := proto.Unmarshal(principal.Principal, anon)
|
|
if err != nil {
|
|
return errors.Wrap(err, "could not unmarshal MSPIdentityAnonymity from principal")
|
|
}
|
|
switch anon.AnonymityType {
|
|
case m.MSPIdentityAnonymity_ANONYMOUS:
|
|
return errors.New("Principal is anonymous, but X.509 MSP does not support anonymous identities")
|
|
case m.MSPIdentityAnonymity_NOMINAL:
|
|
return nil
|
|
default:
|
|
return errors.Errorf("Unknown principal anonymity type: %d", anon.AnonymityType)
|
|
}
|
|
|
|
default:
|
|
// Use the pre-v1.3 function to check other principal types
|
|
return msp.satisfiesPrincipalInternalPreV13(id, principal)
|
|
}
|
|
}
|
|
|
|
// satisfiesPrincipalInternalV142 takes as arguments the identity and the principal.
|
|
// The function returns an error if one occurred.
|
|
// The function implements the additional behavior expected of an MSP starting from v2.0.
|
|
// For v1.3 functionality, the function calls the satisfiesPrincipalInternalPreV13.
|
|
func (msp *bccspmsp) satisfiesPrincipalInternalV142(id Identity, principal *m.MSPPrincipal) error {
|
|
_, okay := id.(*identity)
|
|
if !okay {
|
|
return errors.New("invalid identity type, expected *identity")
|
|
}
|
|
|
|
switch principal.PrincipalClassification {
|
|
case m.MSPPrincipal_ROLE:
|
|
if !msp.ouEnforcement {
|
|
break
|
|
}
|
|
|
|
// Principal contains the msp role
|
|
mspRole := &m.MSPRole{}
|
|
err := proto.Unmarshal(principal.Principal, mspRole)
|
|
if err != nil {
|
|
return errors.Wrap(err, "could not unmarshal MSPRole from principal")
|
|
}
|
|
|
|
// at first, we check whether the MSP
|
|
// identifier is the same as that of the identity
|
|
if mspRole.MspIdentifier != msp.name {
|
|
return errors.Errorf("the identity is a member of a different MSP (expected %s, got %s)", mspRole.MspIdentifier, id.GetMSPIdentifier())
|
|
}
|
|
|
|
// now we validate the admin role only, the other roles are left to the v1.3 function
|
|
switch mspRole.Role {
|
|
case m.MSPRole_ADMIN:
|
|
mspLogger.Debugf("Checking if identity has been named explicitly as an admin for %s", msp.name)
|
|
// in the case of admin, we check that the
|
|
// id is exactly one of our admins
|
|
if msp.isInAdmins(id.(*identity)) {
|
|
return nil
|
|
}
|
|
|
|
// or it carries the Admin OU, in this case check that the identity is valid as well.
|
|
mspLogger.Debugf("Checking if identity carries the admin ou for %s", msp.name)
|
|
if err := msp.Validate(id); err != nil {
|
|
return errors.Wrapf(err, "The identity is not valid under this MSP [%s]", msp.name)
|
|
}
|
|
|
|
if err := msp.hasOURole(id, m.MSPRole_ADMIN); err != nil {
|
|
return errors.Wrapf(err, "The identity is not an admin under this MSP [%s]", msp.name)
|
|
}
|
|
|
|
return nil
|
|
case m.MSPRole_ORDERER:
|
|
mspLogger.Debugf("Checking if identity satisfies role [%s] for %s", m.MSPRole_MSPRoleType_name[int32(mspRole.Role)], msp.name)
|
|
if err := msp.Validate(id); err != nil {
|
|
return errors.Wrapf(err, "The identity is not valid under this MSP [%s]", msp.name)
|
|
}
|
|
|
|
if err := msp.hasOURole(id, mspRole.Role); err != nil {
|
|
return errors.Wrapf(err, "The identity is not a [%s] under this MSP [%s]", m.MSPRole_MSPRoleType_name[int32(mspRole.Role)], msp.name)
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// Use the v1.3 function to check other principal types
|
|
return msp.satisfiesPrincipalInternalV13(id, principal)
|
|
}
|
|
|
|
func (msp *bccspmsp) isInAdmins(id *identity) bool {
|
|
for _, admincert := range msp.admins {
|
|
if bytes.Equal(id.cert.Raw, admincert.(*identity).cert.Raw) {
|
|
// we do not need to check whether the admin is a valid identity
|
|
// according to this MSP, since we already check this at Setup time
|
|
// if there is a match, we can just return
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// getCertificationChain returns the certification chain of the passed identity within this msp
|
|
func (msp *bccspmsp) getCertificationChain(id Identity) ([]*x509.Certificate, error) {
|
|
mspLogger.Debugf("MSP %s getting certification chain", msp.name)
|
|
|
|
switch id := id.(type) {
|
|
// If this identity is of this specific type,
|
|
// this is how I can validate it given the
|
|
// root of trust this MSP has
|
|
case *identity:
|
|
return msp.getCertificationChainForBCCSPIdentity(id)
|
|
default:
|
|
return nil, errors.New("identity type not recognized")
|
|
}
|
|
}
|
|
|
|
// getCertificationChainForBCCSPIdentity returns the certification chain of the passed bccsp identity within this msp
|
|
func (msp *bccspmsp) getCertificationChainForBCCSPIdentity(id *identity) ([]*x509.Certificate, error) {
|
|
if id == nil {
|
|
return nil, errors.New("Invalid bccsp identity. Must be different from nil.")
|
|
}
|
|
|
|
// we expect to have a valid VerifyOptions instance
|
|
if msp.opts == nil {
|
|
return nil, errors.New("Invalid msp instance")
|
|
}
|
|
|
|
// CAs cannot be directly used as identities..
|
|
if id.cert.IsCA {
|
|
return nil, errors.New("An X509 certificate with Basic Constraint: " +
|
|
"Certificate Authority equals true cannot be used as an identity")
|
|
}
|
|
|
|
return msp.getValidationChain(id.cert, false)
|
|
}
|
|
|
|
func (msp *bccspmsp) getUniqueValidationChain(cert *x509.Certificate, opts x509.VerifyOptions) ([]*x509.Certificate, error) {
|
|
// ask golang to validate the cert for us based on the options that we've built at setup time
|
|
if msp.opts == nil {
|
|
return nil, errors.New("the supplied identity has no verify options")
|
|
}
|
|
validationChains, err := cert.Verify(opts)
|
|
if err != nil {
|
|
return nil, errors.WithMessage(err, "the supplied identity is not valid")
|
|
}
|
|
|
|
// we only support a single validation chain;
|
|
// if there's more than one then there might
|
|
// be unclarity about who owns the identity
|
|
if len(validationChains) != 1 {
|
|
return nil, errors.Errorf("this MSP only supports a single validation chain, got %d", len(validationChains))
|
|
}
|
|
|
|
// Make the additional verification checks that were done in Go 1.14.
|
|
err = verifyLegacyNameConstraints(validationChains[0])
|
|
if err != nil {
|
|
return nil, errors.WithMessage(err, "the supplied identity is not valid")
|
|
}
|
|
|
|
return validationChains[0], nil
|
|
}
|
|
|
|
var (
|
|
oidExtensionSubjectAltName = asn1.ObjectIdentifier{2, 5, 29, 17}
|
|
oidExtensionNameConstraints = asn1.ObjectIdentifier{2, 5, 29, 30}
|
|
)
|
|
|
|
// verifyLegacyNameConstraints exercises the name constraint validation rules
|
|
// that were part of the certificate verification process in Go 1.14.
|
|
//
|
|
// If a signing certificate contains a name constratint, the leaf certificate
|
|
// does not include SAN extensions, and the leaf's common name looks like a
|
|
// host name, the validation would fail with an x509.CertificateInvalidError
|
|
// and a rason of x509.NameConstraintsWithoutSANs.
|
|
func verifyLegacyNameConstraints(chain []*x509.Certificate) error {
|
|
if len(chain) < 2 {
|
|
return nil
|
|
}
|
|
|
|
// Leaf certificates with SANs are fine.
|
|
if oidInExtensions(oidExtensionSubjectAltName, chain[0].Extensions) {
|
|
return nil
|
|
}
|
|
// Leaf certificates without a hostname in CN are fine.
|
|
if !validHostname(chain[0].Subject.CommonName) {
|
|
return nil
|
|
}
|
|
// If an intermediate or root have a name constraint, validation
|
|
// would fail in Go 1.14.
|
|
for _, c := range chain[1:] {
|
|
if oidInExtensions(oidExtensionNameConstraints, c.Extensions) {
|
|
return x509.CertificateInvalidError{Cert: chain[0], Reason: x509.NameConstraintsWithoutSANs}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func oidInExtensions(oid asn1.ObjectIdentifier, exts []pkix.Extension) bool {
|
|
for _, ext := range exts {
|
|
if ext.Id.Equal(oid) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// validHostname reports whether host is a valid hostname that can be matched or
|
|
// matched against according to RFC 6125 2.2, with some leniency to accommodate
|
|
// legacy values.
|
|
//
|
|
// This implementation is sourced from the standard library.
|
|
func validHostname(host string) bool {
|
|
host = strings.TrimSuffix(host, ".")
|
|
|
|
if len(host) == 0 {
|
|
return false
|
|
}
|
|
|
|
for i, part := range strings.Split(host, ".") {
|
|
if part == "" {
|
|
// Empty label.
|
|
return false
|
|
}
|
|
if i == 0 && part == "*" {
|
|
// Only allow full left-most wildcards, as those are the only ones
|
|
// we match, and matching literal '*' characters is probably never
|
|
// the expected behavior.
|
|
continue
|
|
}
|
|
for j, c := range part {
|
|
if 'a' <= c && c <= 'z' {
|
|
continue
|
|
}
|
|
if '0' <= c && c <= '9' {
|
|
continue
|
|
}
|
|
if 'A' <= c && c <= 'Z' {
|
|
continue
|
|
}
|
|
if c == '-' && j != 0 {
|
|
continue
|
|
}
|
|
if c == '_' || c == ':' {
|
|
// Not valid characters in hostnames, but commonly
|
|
// found in deployments outside the WebPKI.
|
|
continue
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func (msp *bccspmsp) getValidationChain(cert *x509.Certificate, isIntermediateChain bool) ([]*x509.Certificate, error) {
|
|
validationChain, err := msp.getUniqueValidationChain(cert, msp.getValidityOptsForCert(cert))
|
|
if err != nil {
|
|
return nil, errors.WithMessage(err, "failed getting validation chain")
|
|
}
|
|
|
|
// we expect a chain of length at least 2
|
|
if len(validationChain) < 2 {
|
|
return nil, errors.Errorf("expected a chain of length at least 2, got %d", len(validationChain))
|
|
}
|
|
|
|
// check that the parent is a leaf of the certification tree
|
|
// if validating an intermediate chain, the first certificate will the parent
|
|
parentPosition := 1
|
|
if isIntermediateChain {
|
|
parentPosition = 0
|
|
}
|
|
if msp.certificationTreeInternalNodesMap[string(validationChain[parentPosition].Raw)] {
|
|
return nil, errors.Errorf("invalid validation chain. Parent certificate should be a leaf of the certification tree [%v]", cert.Raw)
|
|
}
|
|
return validationChain, nil
|
|
}
|
|
|
|
// getCertificationChainIdentifier returns the certification chain identifier of the passed identity within this msp.
|
|
// The identifier is computes as the SHA256 of the concatenation of the certificates in the chain.
|
|
func (msp *bccspmsp) getCertificationChainIdentifier(id Identity) ([]byte, error) {
|
|
chain, err := msp.getCertificationChain(id)
|
|
if err != nil {
|
|
return nil, errors.WithMessagef(err, "failed getting certification chain for [%v]", id)
|
|
}
|
|
|
|
// chain[0] is the certificate representing the identity.
|
|
// It will be discarded
|
|
return msp.getCertificationChainIdentifierFromChain(chain[1:])
|
|
}
|
|
|
|
func (msp *bccspmsp) getCertificationChainIdentifierFromChain(chain []*x509.Certificate) ([]byte, error) {
|
|
// Hash the chain
|
|
// Use the hash of the identity's certificate as id in the IdentityIdentifier
|
|
hashOpt, err := bccsp.GetHashOpt(msp.cryptoConfig.IdentityIdentifierHashFunction)
|
|
if err != nil {
|
|
return nil, errors.WithMessage(err, "failed getting hash function options")
|
|
}
|
|
|
|
hf, err := msp.bccsp.GetHash(hashOpt)
|
|
if err != nil {
|
|
return nil, errors.WithMessage(err, "failed getting hash function when computing certification chain identifier")
|
|
}
|
|
for i := 0; i < len(chain); i++ {
|
|
hf.Write(chain[i].Raw)
|
|
}
|
|
return hf.Sum(nil), nil
|
|
}
|
|
|
|
// sanitizeCert ensures that x509 certificates signed using ECDSA
|
|
// do have signatures in Low-S. If this is not the case, the certificate
|
|
// is regenerated to have a Low-S signature.
|
|
func (msp *bccspmsp) sanitizeCert(cert *x509.Certificate) (*x509.Certificate, error) {
|
|
var err error
|
|
if isECDSASignedCert(cert) {
|
|
isRootCACert := false
|
|
validityOpts := msp.getValidityOptsForCert(cert)
|
|
if cert.IsCA && cert.CheckSignatureFrom(cert) == nil {
|
|
// this is a root CA we can already sanitize it
|
|
cert, err = sanitizeECDSASignedCert(cert, cert)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
isRootCACert = true
|
|
validityOpts.Roots = x509.NewCertPool()
|
|
validityOpts.Roots.AddCert(cert)
|
|
}
|
|
// Lookup for a parent certificate to perform the sanitization
|
|
// run cert validation at any rate, if this is a root CA
|
|
// we will validate already sanitized cert
|
|
chain, err := msp.getUniqueValidationChain(cert, validityOpts)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// once we finish validation and this is already
|
|
// sanitized certificate, there is no need to
|
|
// sanitize it once again hence we can just return it
|
|
if isRootCACert {
|
|
return cert, nil
|
|
}
|
|
|
|
// ok, this is no a root CA cert, and now we
|
|
// then we have chain of certs and can get parent
|
|
// to sanitize the cert whenever it's intermediate or leaf certificate
|
|
parentCert := chain[1]
|
|
|
|
// Sanitize
|
|
return sanitizeECDSASignedCert(cert, parentCert)
|
|
}
|
|
return cert, nil
|
|
}
|
|
|
|
// IsWellFormed checks if the given identity can be deserialized into its provider-specific form.
|
|
// In this MSP implementation, well formed means that the PEM has a Type which is either
|
|
// the string 'CERTIFICATE' or the Type is missing altogether.
|
|
func (msp *bccspmsp) IsWellFormed(identity *m.SerializedIdentity) error {
|
|
bl, rest := pem.Decode(identity.IdBytes)
|
|
if bl == nil {
|
|
return errors.New("PEM decoding resulted in an empty block")
|
|
}
|
|
if len(rest) > 0 {
|
|
return errors.Errorf("identity %s for MSP %s has trailing bytes", string(identity.IdBytes), identity.Mspid)
|
|
}
|
|
|
|
// Important: This method looks very similar to getCertFromPem(idBytes []byte) (*x509.Certificate, error)
|
|
// But we:
|
|
// 1) Must ensure PEM block is of type CERTIFICATE or is empty
|
|
// 2) Must not replace getCertFromPem with this method otherwise we will introduce
|
|
// a change in validation logic which will result in a chain fork.
|
|
if bl.Type != "CERTIFICATE" && bl.Type != "" {
|
|
return errors.Errorf("pem type is %s, should be 'CERTIFICATE' or missing", bl.Type)
|
|
}
|
|
cert, err := x509.ParseCertificate(bl.Bytes)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if !isECDSASignedCert(cert) {
|
|
return nil
|
|
}
|
|
|
|
return isIdentitySignedInCanonicalForm(cert.Signature, identity.Mspid, identity.IdBytes)
|
|
}
|
|
|
|
func isIdentitySignedInCanonicalForm(sig []byte, mspID string, pemEncodedIdentity []byte) error {
|
|
r, s, err := utils.UnmarshalECDSASignature(sig)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
expectedSig, err := utils.MarshalECDSASignature(r, s)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if !bytes.Equal(expectedSig, sig) {
|
|
return errors.Errorf("identity %s for MSP %s has a non canonical signature",
|
|
string(pemEncodedIdentity), mspID)
|
|
}
|
|
|
|
return nil
|
|
}
|