github.com/hyperledger-labs/bdls@v2.1.1+incompatible/core/chaincode/accesscontrol/access.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package accesscontrol
     8  
     9  import (
    10  	"errors"
    11  	"fmt"
    12  
    13  	"github.com/golang/protobuf/proto"
    14  	pb "github.com/hyperledger/fabric-protos-go/peer"
    15  	"github.com/hyperledger/fabric/common/crypto/tlsgen"
    16  	"github.com/hyperledger/fabric/common/flogging"
    17  	"google.golang.org/grpc"
    18  )
    19  
    20  var logger = flogging.MustGetLogger("chaincode.accesscontrol")
    21  
    22  // CertAndPrivKeyPair contains a certificate
    23  // and its corresponding private key in base64 format
    24  type CertAndPrivKeyPair struct {
    25  	// Cert is an x509 certificate
    26  	Cert []byte
    27  	// Key is a private key of the corresponding certificate
    28  	Key []byte
    29  }
    30  
    31  type Authenticator struct {
    32  	mapper *certMapper
    33  }
    34  
    35  func (auth *Authenticator) Wrap(srv pb.ChaincodeSupportServer) pb.ChaincodeSupportServer {
    36  	return newInterceptor(srv, auth.authenticate)
    37  }
    38  
    39  // NewAuthenticator returns a new authenticator that can wrap a chaincode service
    40  func NewAuthenticator(ca tlsgen.CA) *Authenticator {
    41  	return &Authenticator{
    42  		mapper: newCertMapper(ca.NewClientCertKeyPair),
    43  	}
    44  }
    45  
    46  // Generate returns a pair of certificate and private key,
    47  // and associates the hash of the certificate with the given
    48  // chaincode name
    49  func (ac *Authenticator) Generate(ccName string) (*CertAndPrivKeyPair, error) {
    50  	cert, err := ac.mapper.genCert(ccName)
    51  	if err != nil {
    52  		return nil, err
    53  	}
    54  	return &CertAndPrivKeyPair{
    55  		Key:  cert.Key,
    56  		Cert: cert.Cert,
    57  	}, nil
    58  }
    59  
    60  func (ac *Authenticator) authenticate(msg *pb.ChaincodeMessage, stream grpc.ServerStream) error {
    61  	if msg.Type != pb.ChaincodeMessage_REGISTER {
    62  		logger.Warning("Got message", msg, "but expected a ChaincodeMessage_REGISTER message")
    63  		return errors.New("First message needs to be a register")
    64  	}
    65  
    66  	chaincodeID := &pb.ChaincodeID{}
    67  	err := proto.Unmarshal(msg.Payload, chaincodeID)
    68  	if err != nil {
    69  		logger.Warning("Failed unmarshaling message:", err)
    70  		return err
    71  	}
    72  	ccName := chaincodeID.Name
    73  	// Obtain certificate from stream
    74  	hash := extractCertificateHashFromContext(stream.Context())
    75  	if len(hash) == 0 {
    76  		errMsg := fmt.Sprintf("TLS is active but chaincode %s didn't send certificate", ccName)
    77  		logger.Warning(errMsg)
    78  		return errors.New(errMsg)
    79  	}
    80  	// Look it up in the mapper
    81  	registeredName := ac.mapper.lookup(certHash(hash))
    82  	if registeredName == "" {
    83  		errMsg := fmt.Sprintf("Chaincode %s with given certificate hash %v not found in registry", ccName, hash)
    84  		logger.Warning(errMsg)
    85  		return errors.New(errMsg)
    86  	}
    87  	if registeredName != ccName {
    88  		errMsg := fmt.Sprintf("Chaincode %s with given certificate hash %v belongs to a different chaincode", ccName, hash)
    89  		logger.Warning(errMsg)
    90  		return fmt.Errorf(errMsg)
    91  	}
    92  
    93  	logger.Debug("Chaincode", ccName, "'s authentication is authorized")
    94  	return nil
    95  }