fabric源碼分析之六MSP源碼分析

一、介紹

分析MSP其實應該先對權限控制源碼進行分析最好,但是權限控制的主要源碼在CA相關代碼中比較多,所以先把這塊放到後面再分析。
MSP(membership service provider),成員管理服務提供者,它是從1.0引進的一個組件,目的是抽象各個成員間的管理結構關係。它包含:證書的管理、用戶認證、加密和協議等。

二、MSP內容

MSP主要的內容有什麼呢?其實主要就是身份認證。有沒有權利在這個“組織”內工作,此處的組織不是Fabric內的組織。
MSP是基於x.509證書,利用PKI體系爲每個成員分發數字證書。並通過MSP進行身份認證和權限控制。說得挺高大上,其實就是通過根證書,簽發中間CA證書,再利用中間CA簽發客戶端證書。它主要檢查三個方面:
證書的有效性,是否過期或吊銷
證書的路徑的檢查,客戶端證書-根證書
MSP的標識認證,就是身份檢查,因爲MSP中證書和身份是綁定的。
針對實際的MSP管理,有三種方式:
組織和MSP建立1:1關係:推薦這種方式
組織和MSP建立1:N關係:也就說大公司有N個部門進行不同的管理方式。
組織和MSP建立N:1關係:跨組織同步方便,多個公司使用同一套MSP。
在進行MSP設計管理時,要注意將不同的CA證書存儲到不同的路徑,區分管理員和不同的根證書(如CA根證書和TLS根證書),嚴格管理證書的吊銷和更新。

三、源碼

源碼主要位於msp目錄下,其它一些相關的分散在不同的應用目錄下,比如common/config/locamsp,proto/msp,在sampleconfig/msp實現了一個簡單的例子。更具體的目錄結構請看源碼。
1、相關的數據結構

type mspSigner struct {
}
//反序列化ID的接口
type IdentityDeserializer interface {
	// 反序列化身份關聯到相關的MSP。如果不是,將返回錯誤
	DeserializeIdentity(serializedIdentity []byte) (Identity, error)

	// 反序列化的檢查
	IsWellFormed(identity *msp.SerializedIdentity) error
}
type MSPManager interface {

	// 見上面的結構體
	IdentityDeserializer

	//根據配置設置MSPManager 實例
	Setup(msps []MSP) error

	//獲得成員服務提供者列表
	GetMSPs() (map[string]MSP, error)
}

// MSP is the minimal Membership Service Provider Interface to be implemented
// to accommodate peer functionality
type MSP interface {

	// 見上面的結構體
	IdentityDeserializer

	//根據配置信息設置MSP實例
	Setup(config *msp.MSPConfig) error

	// GetVersion returns the version of this MSP
	GetVersion() MSPVersion

	// GetType returns the provider type
	GetType() ProviderType

	// 返回提供者ID
	GetIdentifier() (string, error)

	// 根據提供ID返回簽名身份ID
	GetSigningIdentity(identifier *IdentityIdentifier) (SigningIdentity, error)

	//返回默認簽名身份ID
	GetDefaultSigningIdentity() (SigningIdentity, error)

	// 返回根證書
	GetTLSRootCerts() [][]byte

	// 返回TLS中間根證書
	GetTLSIntermediateCerts() [][]byte

	// 提供的ID是否有效
	Validate(id Identity) error

	// 身份匹配,逐字節或需要MSP驗證
	SatisfiesPrincipal(id Identity, principal *msp.MSPPrincipal) error
}
type Identity interface {

	// 返回ID過期時間
	ExpiresAt() time.Time

	// 返回身份ID
	GetIdentifier() *IdentityIdentifier

	// 獲得MSP的相關實例的ID
	GetMSPIdentifier() string

	// MSP規則驗證
	Validate() error

	// 獲得組織單位如證書頒發機構等
	GetOrganizationalUnits() []*OUIdentifier

	//匿名判定
	Anonymous() bool
	// 驗證消息簽名
	Verify(msg []byte, sig []byte) error
	// 序列化
	Serialize() ([]byte, error)

	// MSPPrincipal檢查驗證,有可能逐字節比對
	SatisfiesPrincipal(principal *msp.MSPPrincipal) error
}
//擴展的ID,包含簽名功能
type SigningIdentity interface {
	// Extends Identity
	Identity
	// Sign the message
	Sign(msg []byte) ([]byte, error)
	// GetPublicVersion returns the public parts of this identity
	GetPublicVersion() Identity
}

// 特殊ID
type IdentityIdentifier struct {

	// MSP提供的標識ID
	Mspid string

	// ID
	Id string
}

另外還有些小的數據結構就不貼上來了。從上面可以看到,其實數據結構也是從小到大(或者說從大到小),從ID到MSP再到MSP管理者。然後再具體到相關數據的反序列化。

2、MSP的初始化代碼
無論是在前面的Peer還是Orderer啓動都在分析時看到了相關的MSP的代碼的初始化部分,下面看一下相關代碼:

//Peer
func serve(args []string) error {
	//
	mspType := mgmt.GetLocalMSP().GetType()
  ......
  identityDeserializerFactory := func(chainID string) msp.IdentityDeserializer {
    return mgmt.GetManagerForChain(chainID)
  }
......
  membershipInfoProvider := privdata.NewMembershipInfoProvider(createSelfSignedData(), identityDeserializerFactory)
......
  signingIdentity := mgmt.GetLocalSigningIdentityOrPanic()
  serializedIdentity, err := signingIdentity.Serialize()
  ......
}
//Orderer
func Main() {
......
	initializeLocalMsp(conf)
......
}

找到的相關的部分,下面看一下具體實現的代碼有什麼異同。

Orderer部分:

func initializeLocalMsp(conf *localconfig.TopLevel) {
	// Load local MSP
	err := mspmgmt.LoadLocalMsp(conf.General.LocalMSPDir, conf.General.BCCSP, conf.General.LocalMSPID)
	if err != nil { // Handle errors reading the config file
		logger.Fatal("Failed to initialize local MSP:", err)
	}
}
func LoadLocalMsp(dir string, bccspConfig *factory.FactoryOpts, mspID string) error {
	if mspID == "" {
		return errors.New("the local MSP must have an ID")
	}
  //獲得本地配置,就是提供的.yaml文件
	conf, err := msp.GetLocalMspConfig(dir, bccspConfig, mspID)
	if err != nil {
		return err
	}
  //這個在前面有,取得當前MSP並配置
	return GetLocalMSP().Setup(conf)
}
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")
	}

  //從指定路徑獲得PEM證書
	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)
	}

  //正面會和BCCSP有關係了
	/* 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)
}
func loadLocaMSP() msp.MSP {
	// determine the type of MSP (by default, we'll use bccspMSP)
	mspType := viper.GetString("peer.localMspType")
	if mspType == "" {
		mspType = msp.ProviderTypeToString(msp.FABRIC)
	}

	var mspOpts = map[string]msp.NewOpts{
		msp.ProviderTypeToString(msp.FABRIC): &msp.BCCSPNewOpts{NewBaseOpts: msp.NewBaseOpts{Version: msp.MSPv1_4_3}},
		msp.ProviderTypeToString(msp.IDEMIX): &msp.IdemixNewOpts{NewBaseOpts: msp.NewBaseOpts{Version: msp.MSPv1_1}},
	}
	newOpts, found := mspOpts[mspType]
	if !found {
		mspLogger.Panicf("msp type " + mspType + " unknown")
	}

	mspInst, err := msp.New(newOpts)
	if err != nil {
		mspLogger.Fatalf("Failed to initialize local MSP, received err %+v", err)
	}
	switch mspType {
	case msp.ProviderTypeToString(msp.FABRIC):
		mspInst, err = cache.New(mspInst)
		if err != nil {
			mspLogger.Fatalf("Failed to initialize local MSP, received err %+v", err)
		}
	case msp.ProviderTypeToString(msp.IDEMIX):
		// Do nothing
	default:
		panic("msp type " + mspType + " unknown")
	}

	mspLogger.Debugf("Created new local MSP")

	return mspInst
}
//在New函數裏會調用生成MSP的相關函數,不同的版本人會有不同的創建方法,這裏取了其中一種
func newBccspMsp(version MSPVersion) (MSP, error) {
	mspLogger.Debugf("Creating BCCSP-based MSP instance")

	bccsp := factory.GetDefault()
	theMsp := &bccspmsp{}
	theMsp.version = version
	theMsp.bccsp = bccsp
	switch version {
	case MSPv1_0:
		theMsp.internalSetupFunc = theMsp.setupV1
		theMsp.internalValidateIdentityOusFunc = theMsp.validateIdentityOUsV1
		theMsp.internalSatisfiesPrincipalInternalFunc = theMsp.satisfiesPrincipalInternalPreV13
		theMsp.internalSetupAdmin = theMsp.setupAdminsPreV143
	case MSPv1_1:
		theMsp.internalSetupFunc = theMsp.setupV11
		theMsp.internalValidateIdentityOusFunc = theMsp.validateIdentityOUsV11
		theMsp.internalSatisfiesPrincipalInternalFunc = theMsp.satisfiesPrincipalInternalPreV13
		theMsp.internalSetupAdmin = theMsp.setupAdminsPreV143
	case MSPv1_3:
		theMsp.internalSetupFunc = theMsp.setupV11
		theMsp.internalValidateIdentityOusFunc = theMsp.validateIdentityOUsV11
		theMsp.internalSatisfiesPrincipalInternalFunc = theMsp.satisfiesPrincipalInternalV13
		theMsp.internalSetupAdmin = theMsp.setupAdminsPreV143
	case MSPv1_4_3:
		theMsp.internalSetupFunc = theMsp.setupV143
		theMsp.internalValidateIdentityOusFunc = theMsp.validateIdentityOUsV143
		theMsp.internalSatisfiesPrincipalInternalFunc = theMsp.satisfiesPrincipalInternalV143
		theMsp.internalSetupAdmin = theMsp.setupAdminsV143
	default:
		return nil, errors.Errorf("Invalid MSP version [%v]", version)
	}

	return theMsp, nil
}
//最後Setup
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)
}

這一系列的動作,有點小複雜。
Peer部分:
GetLoclaMSP和Orderer完全一樣的代碼,看一下下面的反序列化註冊函數:

func GetManagerForChain(chainID string) msp.MSPManager {
	m.Lock()
	defer m.Unlock()

	mspMgr, ok := mspMap[chainID]
	if !ok {
		mspLogger.Debugf("Created new msp manager for channel `%s`", chainID)
		mspMgmtMgr := &mspMgmtMgr{msp.NewMSPManager(), false}
		mspMap[chainID] = mspMgmtMgr
		mspMgr = mspMgmtMgr
	} else {
		// 類型匹配檢查
		if !(reflect.TypeOf(mspMgr).Elem().Name() == "mspManagerImpl" || reflect.TypeOf(mspMgr).Elem().Name() == "mspMgmtMgr") {
			panic("Found unexpected MSPManager type.")
		}
		mspLogger.Debugf("Returning existing manager for channel '%s'", chainID)
	}
	return mspMgr
}
//直到Setup方法後纔會初始化,這裏只生成一個空實例
func NewMSPManager() MSPManager {
	return &mspManagerImpl{}
}
//再看membershipInfoProvider :=
//privdata.NewMembershipInfoProvider(createSelfSignedData(), identityDeserializerFactory)
func createSelfSignedData() common2.SignedData {
	sId := mgmt.GetLocalSigningIdentityOrPanic()
	msg := make([]byte, 32)
	sig, err := sId.Sign(msg)
	if err != nil {
		logger.Panicf("Failed creating self signed data because message signing failed: %v", err)
	}
	peerIdentity, err := sId.Serialize()
	if err != nil {
		logger.Panicf("Failed creating self signed data because peer identity couldn't be serialized: %v", err)
	}
	return common2.SignedData{
		Data:      msg,
		Signature: sig,
		Identity:  peerIdentity,
	}
}
//這個函數生成一個新的相關MSP實例交將上面得到相關序列化函數指針代入
func NewMembershipInfoProvider(selfSignedData common.SignedData, identityDeserializerFunc func(chainID string) msp.IdentityDeserializer) *MembershipProvider {
	return &MembershipProvider{selfSignedData: selfSignedData, IdentityDeserializerFactory: identityDeserializerFunc}
}
func GetLocalSigningIdentityOrPanic() msp.SigningIdentity {
	id, err := GetLocalMSP().GetDefaultSigningIdentity()
	if err != nil {
		mspLogger.Panicf("Failed getting local signing identity [%+v]", err)
	}
	return id
}
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
}

3、交易驗證
交易的驗證在背書中首先會被用到,看一下代碼:
背書的除了Proto部分外,主要在core/endorser這個文件夾下,重點看一下MSP相關的驗證:

func (e *Endorser) ProcessProposal(ctx context.Context, signedProp *pb.SignedProposal) (*pb.ProposalResponse, error) {
	// start time for computing elapsed time metric for successfully endorsed proposals
	startTime := time.Now()
......

	// 0 -- check and validate
	vr, err := e.preProcess(signedProp)
  ......
}

這裏可以看看這個簽名的定義:

// This structure is necessary to sign the proposal which contains the header
// and the payload. Without this structure, we would have to concatenate the
// header and the payload to verify the signature, which could be expensive
// with large payload
//
// When an endorser receives a SignedProposal message, it should verify the
// signature over the proposal bytes. This verification requires the following
// steps:
// 1. Verification of the validity of the certificate that was used to produce
//    the signature.  The certificate will be available once proposalBytes has
//    been unmarshalled to a Proposal message, and Proposal.header has been
//    unmarshalled to a Header message. While this unmarshalling-before-verifying
//    might not be ideal, it is unavoidable because i) the signature needs to also
//    protect the signing certificate; ii) it is desirable that Header is created
//    once by the client and never changed (for the sake of accountability and
//    non-repudiation). Note also that it is actually impossible to conclusively
//    verify the validity of the certificate included in a Proposal, because the
//    proposal needs to first be endorsed and ordered with respect to certificate
//    expiration transactions. Still, it is useful to pre-filter expired
//    certificates at this stage.
// 2. Verification that the certificate is trusted (signed by a trusted CA) and
//    that it is allowed to transact with us (with respect to some ACLs);
// 3. Verification that the signature on proposalBytes is valid;
// 4. Detect replay attacks;
type SignedProposal struct {
	// The bytes of Proposal
	ProposalBytes []byte `protobuf:"bytes,1,opt,name=proposal_bytes,json=proposalBytes,proto3" json:"proposal_bytes,omitempty"`
	// Signaure over proposalBytes; this signature is to be verified against
	// the creator identity contained in the header of the Proposal message
	// marshaled as proposalBytes
	Signature            []byte   `protobuf:"bytes,2,opt,name=signature,proto3" json:"signature,omitempty"`
	XXX_NoUnkeyedLiteral struct{} `json:"-"`
	XXX_unrecognized     []byte   `json:"-"`
	XXX_sizecache        int32    `json:"-"`
}

註釋寫的很清楚,接着往下看:

func (e *Endorser) preProcess(signedProp *pb.SignedProposal) (*validateResult, error) {
	vr := &validateResult{}
	// at first, we check whether the message is valid
	prop, hdr, hdrExt, err := validation.ValidateProposalMessage(signedProp)

	if err != nil {
		e.Metrics.ProposalValidationFailed.Add(1)
		vr.resp = &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}
		return vr, err
	}
  ......
}
func ValidateProposalMessage(signedProp *pb.SignedProposal) (*pb.Proposal, *common.Header, *pb.ChaincodeHeaderExtension, error) {
......
  //得到Proposal的比特流
  prop, err := utils.GetProposal(signedProp.ProposalBytes)
  if err != nil {
  return nil, nil, nil, err
  }
	// 1) look at the ProposalHeader
	hdr, err := utils.GetHeader(prop.Header)
	if err != nil {
		return nil, nil, nil, err
	}

	// validate the header
	chdr, shdr, err := validateCommonHeader(hdr)
	if err != nil {
		return nil, nil, nil, err
	}

	// validate the signature
	err = checkSignatureFromCreator(shdr.Creator, signedProp.Signature, signedProp.ProposalBytes, chdr.ChannelId)
......
}
//把相關的數據結構取出用來傳遞給下面的驗證
func validateCommonHeader(hdr *common.Header) (*common.ChannelHeader, *common.SignatureHeader, error) {
	if hdr == nil {
		return nil, nil, errors.New("nil header")
	}

	chdr, err := utils.UnmarshalChannelHeader(hdr.ChannelHeader)
	if err != nil {
		return nil, nil, err
	}

	shdr, err := utils.GetSignatureHeader(hdr.SignatureHeader)
	if err != nil {
		return nil, nil, err
	}

	err = validateChannelHeader(chdr)
	if err != nil {
		return nil, nil, err
	}

	err = validateSignatureHeader(shdr)
	if err != nil {
		return nil, nil, err
	}

	return chdr, shdr, nil
}
//下面的驗證就順理成章--基本可以看到相關的基礎接口的實現的函數定義
func checkSignatureFromCreator(creatorBytes []byte, sig []byte, msg []byte, ChainID string) error {
	putilsLogger.Debugf("begin")

	// check for nil argument
	if creatorBytes == nil || sig == nil || msg == nil {
		return errors.New("nil arguments")
	}

	mspObj := mspmgmt.GetIdentityDeserializer(ChainID)
	if mspObj == nil {
		return errors.Errorf("could not get msp for channel [%s]", ChainID)
	}

	// get the identity of the creator
	creator, err := mspObj.DeserializeIdentity(creatorBytes)
	if err != nil {
		return errors.WithMessage(err, "MSP error")
	}

	putilsLogger.Debugf("creator is %s", creator.GetIdentifier())

	// ensure that creator is a valid certificate
	err = creator.Validate()
	if err != nil {
		return errors.WithMessage(err, "creator certificate is not valid")
	}

	putilsLogger.Debugf("creator is valid")

	// validate the signature
	err = creator.Verify(msg, sig)
	if err != nil {
		return errors.WithMessage(err, "creator's signature over the proposal is not valid")
	}

	putilsLogger.Debugf("exits successfully")

	return nil
}

其它的地方同樣還有這種驗證,基本類似就不再一一分析,以後遇到逐一說明。

四、總結

MSP只是Fabric中的一個基本的模塊,其實他的重要性更在於聯盟鏈的許可性。也正是通過它來完成了Channel的多鍊形態的保障。所以說,一個系統中,每個模塊都不是孤立的,是有機的和其它模塊或者系統融合在一起的。在分析代碼時,要注意這一點,要放到更廣泛的系統中去看代碼,這樣纔會有更大的收穫。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章