Skip to content

Commit

Permalink
[FAB-7536] Provide TLS Cert Hash in Channel Header
Browse files Browse the repository at this point in the history
Change-Id: Id6bf5024543b4e7957a511d5e45d3a3f9e7c917f
Signed-off-by: Troy Ronda <[email protected]>
  • Loading branch information
troyronda committed Dec 21, 2017
1 parent 893b60a commit 7c09f2e
Show file tree
Hide file tree
Showing 8 changed files with 109 additions and 10 deletions.
17 changes: 17 additions & 0 deletions pkg/config/comm/comm.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"crypto/tls"

"github.com/hyperledger/fabric-sdk-go/api/apiconfig"
cutil "github.com/hyperledger/fabric-sdk-go/internal/github.com/hyperledger/fabric/common/util"
"github.com/hyperledger/fabric-sdk-go/pkg/errors"
)

Expand All @@ -34,3 +35,19 @@ func TLSConfig(certificate string, serverhostoverride string, config apiconfig.C

return &tls.Config{RootCAs: tlsCaCertPool, Certificates: clientCerts, ServerName: serverhostoverride}, nil
}

// TLSCertHash is a utility method to calculate the SHA256 hash of the configured certificate (for usage in channel headers)
func TLSCertHash(config apiconfig.Config) []byte {
certs, err := config.TLSClientCerts()
if err != nil || len(certs) == 0 {
return nil
}

cert := certs[0]
if len(cert.Certificate) == 0 {
return nil
}

h := cutil.ComputeSHA256(cert.Certificate[0])
return h
}
56 changes: 56 additions & 0 deletions pkg/config/comm/comm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ SPDX-License-Identifier: Apache-2.0
package comm

import (
"bytes"
"crypto/x509"
"encoding/hex"
"testing"

"strings"
Expand Down Expand Up @@ -122,3 +124,57 @@ func TestTLSConfigHappyPath(t *testing.T) {
t.Fatal("Certs do not match")
}
}

func TestNoTlsCertHash(t *testing.T) {
mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()
config := mock_apiconfig.NewMockConfig(mockCtrl)

config.EXPECT().TLSClientCerts().Return([]tls.Certificate{}, nil)

tlsCertHash := TLSCertHash(config)

if len(tlsCertHash) != 0 {
t.Fatal("Unexpected non-empty cert hash")
}
}

func TestEmptyTlsCertHash(t *testing.T) {
mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()
config := mock_apiconfig.NewMockConfig(mockCtrl)

emptyCert := tls.Certificate{}
config.EXPECT().TLSClientCerts().Return([]tls.Certificate{emptyCert}, nil)

tlsCertHash := TLSCertHash(config)

if len(tlsCertHash) != 0 {
t.Fatal("Unexpected non-empty cert hash")
}
}

func TestTlsCertHash(t *testing.T) {
mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()
config := mock_apiconfig.NewMockConfig(mockCtrl)

cert, err := tls.LoadX509KeyPair("testdata/server.crt", "testdata/server.key")
if err != nil {
t.Fatalf("Unexpected error loading cert %v", err)
}

config.EXPECT().TLSClientCerts().Return([]tls.Certificate{cert}, nil)
tlsCertHash := TLSCertHash(config)

// openssl x509 -fingerprint -sha256 -in testdata/server.crt
// SHA256 Fingerprint=0D:D5:90:B8:A5:0E:A6:04:3E:A8:75:16:BF:77:A8:FE:E7:C5:62:2D:4C:B3:CB:99:12:74:72:2A:D8:BA:B8:92
expectedHash, err := hex.DecodeString("0DD590B8A50EA6043EA87516BF77A8FEE7C5622D4CB3CB991274722AD8BAB892")
if err != nil {
t.Fatalf("Unexpected error decoding cert fingerprint %v", err)
}

if bytes.Compare(tlsCertHash, expectedHash) != 0 {
t.Fatal("Cert hash calculated incorrectly")
}
}
15 changes: 15 additions & 0 deletions pkg/config/comm/testdata/server.crt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
-----BEGIN CERTIFICATE-----
MIICZjCCAg2gAwIBAgIQLl+Ug2jLrky26o8cOZE9MjAKBggqhkjOPQQDAjB2MQsw
CQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZy
YW5jaXNjbzEZMBcGA1UEChMQb3JnMS5leGFtcGxlLmNvbTEfMB0GA1UEAxMWdGxz
Y2Eub3JnMS5leGFtcGxlLmNvbTAeFw0xNzA3MjgxNDI3MjBaFw0yNzA3MjYxNDI3
MjBaMFsxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQH
Ew1TYW4gRnJhbmNpc2NvMR8wHQYDVQQDExZwZWVyMC5vcmcxLmV4YW1wbGUuY29t
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEAhDbBctALpd2ushdz8/cng3hnPE9
YFkZa0QIkhBzuPZE7iH8JpB/e5d1zz6IsN0fY5FsvjZsvWkCj8xrl+eePaOBlzCB
lDAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMC
MAwGA1UdEwEB/wQCMAAwKwYDVR0jBCQwIoAgP2onPvGFmShXzjY5WPEJhhDAKM8h
j7l705FH7ynFXNwwKAYDVR0RBCEwH4IWcGVlcjAub3JnMS5leGFtcGxlLmNvbYIF
cGVlcjAwCgYIKoZIzj0EAwIDRwAwRAIgKvld7K1+MukbhULYuhVRZsy4UqpdZ3Iu
+UEU4Vs/CDwCIEuzQaX2N0gnf4UziPBAlrJGArocYA27CpSQX7ycmzJh
-----END CERTIFICATE-----
5 changes: 5 additions & 0 deletions pkg/config/comm/testdata/server.key
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgIHcvk45pDqakyZY6
81ZlopbYflQUbcnYUvjFJSoRJfShRANCAAQCENsFy0Aul3a6yF3Pz9yeDeGc8T1g
WRlrRAiSEHO49kTuIfwmkH97l3XPPoiw3R9jkWy+Nmy9aQKPzGuX5549
-----END PRIVATE KEY-----
7 changes: 5 additions & 2 deletions pkg/fabric-client/channel/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
protos_utils "github.com/hyperledger/fabric-sdk-go/internal/github.com/hyperledger/fabric/protos/utils"
"github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/protos/common"

ccomm "github.com/hyperledger/fabric-sdk-go/pkg/config/comm"
"github.com/hyperledger/fabric-sdk-go/pkg/errors"
fc "github.com/hyperledger/fabric-sdk-go/pkg/fabric-client/internal"
)
Expand Down Expand Up @@ -61,7 +62,8 @@ func (c *Channel) GenesisBlock(request *fab.GenesisBlockRequest) (*common.Block,
Behavior: ab.SeekInfo_BLOCK_UNTIL_READY,
}
protos_utils.MakeChannelHeader(common.HeaderType_DELIVER_SEEK_INFO, 1, c.Name(), 0)
seekInfoHeader, err := BuildChannelHeader(common.HeaderType_DELIVER_SEEK_INFO, c.Name(), request.TxnID.ID, 0, "", time.Now())
tlsCertHash := ccomm.TLSCertHash(c.clientContext.Config())
seekInfoHeader, err := BuildChannelHeader(common.HeaderType_DELIVER_SEEK_INFO, c.Name(), request.TxnID.ID, 0, "", time.Now(), tlsCertHash)
if err != nil {
return nil, errors.Wrap(err, "BuildChannelHeader failed")
}
Expand Down Expand Up @@ -107,7 +109,8 @@ func (c *Channel) block(pos *ab.SeekPosition) (*common.Block, error) {
return nil, errors.Wrap(err, "generating TX ID failed")
}

seekInfoHeader, err := BuildChannelHeader(common.HeaderType_DELIVER_SEEK_INFO, c.Name(), txID, 0, "", time.Now())
tlsCertHash := ccomm.TLSCertHash(c.clientContext.Config())
seekInfoHeader, err := BuildChannelHeader(common.HeaderType_DELIVER_SEEK_INFO, c.Name(), txID, 0, "", time.Now(), tlsCertHash)
if err != nil {
return nil, errors.Wrap(err, "BuildChannelHeader failed")
}
Expand Down
13 changes: 7 additions & 6 deletions pkg/fabric-client/channel/txnsender.go
Original file line number Diff line number Diff line change
Expand Up @@ -390,14 +390,15 @@ func (c *Channel) SendEnvelope(envelope *fab.SignedEnvelope) (*common.Block, err
}

// BuildChannelHeader is a utility method to build a common chain header (TODO refactor)
func BuildChannelHeader(headerType common.HeaderType, channelID string, txID string, epoch uint64, chaincodeID string, timestamp time.Time) (*common.ChannelHeader, error) {
func BuildChannelHeader(headerType common.HeaderType, channelID string, txID string, epoch uint64, chaincodeID string, timestamp time.Time, tlsCertHash []byte) (*common.ChannelHeader, error) {
logger.Debugf("buildChannelHeader - headerType: %s channelID: %s txID: %d epoch: % chaincodeID: %s timestamp: %v", headerType, channelID, txID, epoch, chaincodeID, timestamp)
channelHeader := &common.ChannelHeader{
Type: int32(headerType),
Version: 1,
ChannelId: channelID,
TxId: txID,
Epoch: epoch,
Type: int32(headerType),
Version: 1,
ChannelId: channelID,
TxId: txID,
Epoch: epoch,
TlsCertHash: tlsCertHash,
}

ts, err := ptypes.TimestampProto(timestamp)
Expand Down
2 changes: 1 addition & 1 deletion pkg/fabric-client/channel/txnsender_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -435,7 +435,7 @@ func TestSendTransaction(t *testing.T) {

func TestBuildChannelHeader(t *testing.T) {

header, err := BuildChannelHeader(common.HeaderType_CHAINCODE_PACKAGE, "test", "", 1, "1234", time.Time{})
header, err := BuildChannelHeader(common.HeaderType_CHAINCODE_PACKAGE, "test", "", 1, "1234", time.Time{}, []byte{})

if err != nil || header == nil {
t.Fatalf("Test Build Channel Header failed, cause : '%s'", err.Error())
Expand Down
4 changes: 3 additions & 1 deletion pkg/fabric-client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"github.com/hyperledger/fabric-sdk-go/internal/github.com/hyperledger/fabric/common/crypto"
fcutils "github.com/hyperledger/fabric-sdk-go/internal/github.com/hyperledger/fabric/common/util"
protos_utils "github.com/hyperledger/fabric-sdk-go/internal/github.com/hyperledger/fabric/protos/utils"
ccomm "github.com/hyperledger/fabric-sdk-go/pkg/config/comm"
"github.com/hyperledger/fabric-sdk-go/pkg/errors"
channel "github.com/hyperledger/fabric-sdk-go/pkg/fabric-client/channel"
"github.com/hyperledger/fabric-sdk-go/pkg/fabric-client/identity"
Expand Down Expand Up @@ -386,7 +387,8 @@ func (c *Client) createOrUpdateChannel(request fab.CreateChannelRequest, haveEnv
}

// TODO: Move
channelHeader, err := channel.BuildChannelHeader(common.HeaderType_CONFIG_UPDATE, request.Name, request.TxnID.ID, 0, "", time.Now())
tlsCertHash := ccomm.TLSCertHash(c.config)
channelHeader, err := channel.BuildChannelHeader(common.HeaderType_CONFIG_UPDATE, request.Name, request.TxnID.ID, 0, "", time.Now(), tlsCertHash)
if err != nil {
return errors.WithMessage(err, "BuildChannelHeader failed")
}
Expand Down

0 comments on commit 7c09f2e

Please sign in to comment.