Skip to content

Commit

Permalink
Merge "[FAB-5406] Mutual TLS in chaincode service-P2"
Browse files Browse the repository at this point in the history
  • Loading branch information
C0rWin authored and Gerrit Code Review committed Aug 6, 2017
2 parents dcd0d1b + 2459f93 commit 5101b9e
Show file tree
Hide file tree
Showing 4 changed files with 562 additions and 2 deletions.
121 changes: 121 additions & 0 deletions core/chaincode/accesscontrol/access.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package accesscontrol

import (
"errors"
"fmt"

"github.com/golang/protobuf/proto"
"github.com/hyperledger/fabric/common/flogging"
pb "github.com/hyperledger/fabric/protos/peer"
"google.golang.org/grpc"
)

var logger = flogging.MustGetLogger("accessControl")

// Authenticator wraps a chaincode service and authenticates
// chaincode shims (containers)
type Authenticator interface {
// DisableAccessCheck disables the access control
// enforcement of the Authenticator
DisableAccessCheck()

// Generate returns a pair of certificate and private key,
// and associates the hash of the certificate with the given
// chaincode name
Generate(ccName string) (*CertAndPrivKeyPair, error)

// ChaincodeSupportServer - The Authenticator is registered
// as a chaincode service
pb.ChaincodeSupportServer
}

// CertAndPrivKeyPair contains a certificate
// and its corresponding private key in base64 format
type CertAndPrivKeyPair struct {
// Cert - an x509 certificate encoded in base64
Cert string
// Key - a private key of the corresponding certificate
Key string
}

type authenticator struct {
bypass bool
mapper *certMapper
pb.ChaincodeSupportServer
}

// NewAuthenticator returns a new authenticator that would wrap the given chaincode service
func NewAuthenticator(srv pb.ChaincodeSupportServer, ca CA) Authenticator {
auth := &authenticator{
mapper: newCertMapper(ca.newCertKeyPair),
}
auth.ChaincodeSupportServer = newInterceptor(srv, auth.authenticate)
return auth
}

// DisableAccessCheck disables the access control
// enforcement of the Authenticator
func (ac *authenticator) DisableAccessCheck() {
ac.bypass = true
}

// Generate returns a pair of certificate and private key,
// and associates the hash of the certificate with the given
// chaincode name
func (ac *authenticator) Generate(ccName string) (*CertAndPrivKeyPair, error) {
cert, err := ac.mapper.genCert(ccName)
if err != nil {
return nil, err
}
return &CertAndPrivKeyPair{
Key: cert.privKeyString(),
Cert: cert.pubKeyString(),
}, nil
}

func (ac *authenticator) authenticate(msg *pb.ChaincodeMessage, stream grpc.ServerStream) error {
if ac.bypass {
return nil
}

if msg.Type != pb.ChaincodeMessage_REGISTER {
logger.Warning("Got message", msg, "but expected a ChaincodeMessage_REGISTER message")
return errors.New("First message needs to be a register")
}

chaincodeID := &pb.ChaincodeID{}
err := proto.Unmarshal(msg.Payload, chaincodeID)
if err != nil {
logger.Warning("Failed unmarshaling message:", err)
return err
}
ccName := chaincodeID.Name
// Obtain certificate from stream
hash := extractCertificateHashFromContext(stream.Context())
if len(hash) == 0 {
errMsg := fmt.Sprintf("TLS is active but chaincode %s didn't send certificate", ccName)
logger.Warning(errMsg)
return errors.New(errMsg)
}
// Look it up in the mapper
registeredName := ac.mapper.lookup(certHash(hash))
if registeredName == "" {
errMsg := fmt.Sprintf("Chaincode %s with given certificate hash %v not found in registry", ccName, hash)
logger.Warning(errMsg)
return errors.New(errMsg)
}
if registeredName != ccName {
errMsg := fmt.Sprintf("Chaincode %s with given certificate hash %v belongs to a different chaincode", ccName, hash)
logger.Warning(errMsg)
return fmt.Errorf(errMsg)
}

logger.Debug("Chaincode", ccName, "'s authentication is authorized")
return nil
}
Loading

0 comments on commit 5101b9e

Please sign in to comment.