/* Copyright IBM Corp. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */ package api import ( "crypto/x509" "encoding/base64" "encoding/json" "encoding/pem" "fmt" "time" "github.com/golang/protobuf/proto" cb "github.com/hyperledger/fabric-protos-go/common" "github.com/hyperledger/fabric-protos-go/msp" "github.com/hyperledger/fabric/gossip/common" "google.golang.org/grpc" ) // MessageCryptoService is the contract between the gossip component and the // peer's cryptographic layer and is used by the gossip component to verify, // and authenticate remote peers and data they send, as well as to verify // received blocks from the ordering service. type MessageCryptoService interface { // GetPKIidOfCert returns the PKI-ID of a peer's identity // If any error occurs, the method return nil // This method does not validate peerIdentity. // This validation is supposed to be done appropriately during the execution flow. GetPKIidOfCert(peerIdentity PeerIdentityType) common.PKIidType // VerifyBlock returns nil if the block is properly signed, and the claimed seqNum is the // sequence number that the block's header contains. // else returns error VerifyBlock(channelID common.ChannelID, seqNum uint64, block *cb.Block) error // VerifyBlockAttestation does the same as VerifyBlock, except it assumes block.Data = nil. It therefore does not // compute the block.Data.Hash() and compare it to the block.Header.DataHash. This is used when the orderer // delivers a block with header & metadata only, as an attestation of block existence. VerifyBlockAttestation(channelID string, block *cb.Block) error // Sign signs msg with this peer's signing key and outputs // the signature if no error occurred. Sign(msg []byte) ([]byte, error) // Verify checks that signature is a valid signature of message under a peer's verification key. // If the verification succeeded, Verify returns nil meaning no error occurred. // If peerIdentity is nil, then the verification fails. Verify(peerIdentity PeerIdentityType, signature, message []byte) error // VerifyByChannel checks that signature is a valid signature of message // under a peer's verification key, but also in the context of a specific channel. // If the verification succeeded, Verify returns nil meaning no error occurred. // If peerIdentity is nil, then the verification fails. VerifyByChannel(channelID common.ChannelID, peerIdentity PeerIdentityType, signature, message []byte) error // ValidateIdentity validates the identity of a remote peer. // If the identity is invalid, revoked, expired it returns an error. // Else, returns nil ValidateIdentity(peerIdentity PeerIdentityType) error // Expiration returns: // - The time when the identity expires, nil // In case it can expire // - A zero value time.Time, nil // in case it cannot expire // - A zero value, error in case it cannot be // determined if the identity can expire or not Expiration(peerIdentity PeerIdentityType) (time.Time, error) } // PeerIdentityInfo aggregates a peer's identity, // and also additional metadata about it type PeerIdentityInfo struct { PKIId common.PKIidType Identity PeerIdentityType Organization OrgIdentityType } // PeerIdentitySet aggregates a PeerIdentityInfo slice type PeerIdentitySet []PeerIdentityInfo // PeerIdentityFilter defines predicate function used to filter // peer identities type PeerIdentityFilter func(info PeerIdentityInfo) bool // ByOrg sorts the PeerIdentitySet by organizations of its peers func (pis PeerIdentitySet) ByOrg() map[string]PeerIdentitySet { m := make(map[string]PeerIdentitySet) for _, id := range pis { m[string(id.Organization)] = append(m[string(id.Organization)], id) } return m } // ByID sorts the PeerIdentitySet by PKI-IDs of its peers func (pis PeerIdentitySet) ByID() map[string]PeerIdentityInfo { m := make(map[string]PeerIdentityInfo) for _, id := range pis { m[string(id.PKIId)] = id } return m } // Filter filters identities based on predicate, returns new PeerIdentitySet // with filtered ids. func (pis PeerIdentitySet) Filter(filter PeerIdentityFilter) PeerIdentitySet { var result PeerIdentitySet for _, id := range pis { if filter(id) { result = append(result, id) } } return result } // PeerIdentityType is the peer's certificate type PeerIdentityType []byte // String returns a string representation of this PeerIdentityType func (pit PeerIdentityType) String() string { base64Representation := base64.StdEncoding.EncodeToString(pit) sID := &msp.SerializedIdentity{} err := proto.Unmarshal(pit, sID) if err != nil { return fmt.Sprintf("non SerializedIdentity: %s", base64Representation) } bl, _ := pem.Decode(sID.IdBytes) if bl == nil { return fmt.Sprintf("non PEM encoded identity: %s", base64Representation) } cert, _ := x509.ParseCertificate(bl.Bytes) if cert == nil { return fmt.Sprintf("non x509 identity: %s", base64Representation) } m := make(map[string]interface{}) m["MSP"] = sID.Mspid s := cert.Subject m["CN"] = s.CommonName m["OU"] = s.OrganizationalUnit m["L-ST-C"] = fmt.Sprintf("%s-%s-%s", s.Locality, s.StreetAddress, s.Country) i := cert.Issuer m["Issuer-CN"] = i.CommonName m["Issuer-OU"] = i.OrganizationalUnit m["Issuer-L-ST-C"] = fmt.Sprintf("%s-%s-%s", i.Locality, i.StreetAddress, i.Country) rawJSON, err := json.Marshal(m) if err != nil { return base64Representation } return string(rawJSON) } // PeerSuspector returns whether a peer with a given identity is suspected // as being revoked, or its CA is revoked type PeerSuspector func(identity PeerIdentityType) bool // PeerSecureDialOpts returns the gRPC DialOptions to use for connection level // security when communicating with remote peer endpoints type PeerSecureDialOpts func() []grpc.DialOption // PeerSignature defines a signature of a peer // on a given message type PeerSignature struct { Signature []byte Message []byte PeerIdentity PeerIdentityType }