Skip to content
This repository was archived by the owner on Apr 25, 2025. It is now read-only.

Commit 6721a4c

Browse files
author
Baha Shaaban
committed
[FABG-723] Support distributed signing identities
Initial commit with new functionality and unit tests. Another commit will be pushed later to include integration tests Change-Id: If34333e8cc2a6c267f579b956a03452e6fbd92a5 Signed-off-by: Baha Shaaban <baha.shaaban@securekey.com>
1 parent 8113dec commit 6721a4c

File tree

4 files changed

+480
-34
lines changed

4 files changed

+480
-34
lines changed

pkg/client/resmgmt/opts.go

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@ package resmgmt
88

99
import (
1010
reqContext "context"
11+
"io"
1112
"time"
1213

1314
"github.com/hyperledger/fabric-sdk-go/pkg/common/errors/retry"
1415
"github.com/hyperledger/fabric-sdk-go/pkg/common/providers/context"
1516
"github.com/hyperledger/fabric-sdk-go/pkg/common/providers/fab"
1617
"github.com/hyperledger/fabric-sdk-go/pkg/fab/comm"
18+
"github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/protos/common"
1719
"github.com/pkg/errors"
1820
)
1921

@@ -68,7 +70,7 @@ func WithTargetFilter(targetFilter fab.TargetFilter) RequestOption {
6870
}
6971
}
7072

71-
//WithTimeout encapsulates key value pairs of timeout type, timeout duration to Options
73+
// WithTimeout encapsulates key value pairs of timeout type, timeout duration to Options
7274
//if not provided, default timeout configuration from config will be used
7375
func WithTimeout(timeoutType fab.TimeoutType, timeout time.Duration) RequestOption {
7476
return func(ctx context.Client, o *requestOptions) error {
@@ -109,6 +111,29 @@ func WithOrderer(orderer fab.Orderer) RequestOption {
109111
}
110112
}
111113

114+
// WithConfigSignatures allows to provide pre defined signatures for resmgmt client's SaveChannel call
115+
func WithConfigSignatures(signatures ...*common.ConfigSignature) RequestOption {
116+
return func(ctx context.Client, opts *requestOptions) error {
117+
opts.Signatures = signatures
118+
return nil
119+
}
120+
}
121+
122+
// withConfigSignature allows to provide a pre defined signature reader for resmgmt client's SaveChannel call
123+
// The r reader must provide marshaled ConfigSignature content built using either one of the following calls:
124+
// * CreateConfigSignature call for a signature created internally by the SDK
125+
// * CreateConfigSignatureData call with signingBytes used for creating a signature by external tool (ex: Openssl)
126+
//
127+
// Note: call this function for as many times as there are signatures required for the channel update.
128+
// This option appends 1 ConfigSignature read from r to requestOptions.Signatures.
129+
//
130+
// Note : function not exported for now TODO: double check how to export this
131+
func withConfigSignature(r io.Reader) RequestOption { // nolint
132+
return func(ctx context.Client, opts *requestOptions) error {
133+
return createConfigSignatureOption(r, opts)
134+
}
135+
}
136+
112137
//WithParentContext encapsulates grpc parent context.
113138
func WithParentContext(parentContext reqContext.Context) RequestOption {
114139
return func(ctx context.Client, o *requestOptions) error {

pkg/client/resmgmt/resmgmt.go

Lines changed: 163 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@ import (
2727
"os"
2828
"time"
2929

30-
"github.com/hyperledger/fabric-sdk-go/pkg/common/errors/retry"
31-
30+
"github.com/golang/protobuf/proto"
3231
"github.com/hyperledger/fabric-sdk-go/pkg/client/common/verifier"
32+
"github.com/hyperledger/fabric-sdk-go/pkg/common/errors/retry"
3333
"github.com/hyperledger/fabric-sdk-go/pkg/common/providers/fab"
3434
"github.com/hyperledger/fabric-sdk-go/pkg/common/providers/msp"
3535
"github.com/hyperledger/fabric-sdk-go/pkg/fab/channel"
@@ -48,6 +48,8 @@ import (
4848
"github.com/pkg/errors"
4949
)
5050

51+
const bufferSize = 1024
52+
5153
// InstallCCRequest contains install chaincode request parameters
5254
type InstallCCRequest struct {
5355
Name string
@@ -101,6 +103,8 @@ type requestOptions struct {
101103
Timeouts map[fab.TimeoutType]time.Duration //timeout options for resmgmt operations
102104
ParentContext reqContext.Context //parent grpc context for resmgmt operations
103105
Retry retry.Opts
106+
// signatures for channel configurations, if set, this option will take precedence over signatures of SaveChannelRequest.SigningIdentities
107+
Signatures []*common.ConfigSignature
104108
}
105109

106110
//SaveChannelRequest holds parameters for save channel request
@@ -109,7 +113,6 @@ type SaveChannelRequest struct {
109113
ChannelConfig io.Reader // ChannelConfig data source
110114
ChannelConfigPath string // Convenience option to use the named file as ChannelConfig reader
111115
SigningIdentities []msp.SigningIdentity // Users that sign channel configuration
112-
// TODO: support pre-signed signature blocks
113116
}
114117

115118
// SaveChannelResponse contains response parameters for save channel
@@ -832,6 +835,9 @@ func peersToTxnProcessors(peers []fab.Peer) []fab.ProposalProcessor {
832835
// Parameters:
833836
// req holds info about mandatory channel name and configuration
834837
// options holds optional request options
838+
// if options have signatures (WithConfigSignatures() or 1 or more WithConfigSignature() calls), then SaveChannel will
839+
// use these signatures instead of creating ones for the SigningIdentities found in req.
840+
// Make sure that req.ChannelConfigPath/req.ChannelConfig have the channel config matching these signatures.
835841
//
836842
// Returns:
837843
// save channel response with transaction ID
@@ -858,24 +864,24 @@ func (rc *Client) SaveChannel(req SaveChannelRequest, options ...RequestOption)
858864

859865
logger.Debugf("saving channel: %s", req.ChannelID)
860866

861-
configTx, err := ioutil.ReadAll(req.ChannelConfig)
867+
chConfig, err := extractChConfigData(req.ChannelConfig)
862868
if err != nil {
863-
return SaveChannelResponse{}, errors.WithMessage(err, "reading channel config file failed")
864-
}
865-
866-
chConfig, err := resource.ExtractChannelConfig(configTx)
867-
if err != nil {
868-
return SaveChannelResponse{}, errors.WithMessage(err, "extracting channel config failed")
869+
return SaveChannelResponse{}, errors.WithMessage(err, "extracting channel config from ConfigTx failed")
869870
}
870871

871872
orderer, err := rc.requestOrderer(&opts, req.ChannelID)
872873
if err != nil {
873874
return SaveChannelResponse{}, errors.WithMessage(err, "failed to find orderer for request")
874875
}
875876

876-
configSignatures, err := rc.getConfigSignatures(req, chConfig)
877-
if err != nil {
878-
return SaveChannelResponse{}, err
877+
var configSignatures []*common.ConfigSignature
878+
if opts.Signatures != nil {
879+
configSignatures = opts.Signatures
880+
} else {
881+
configSignatures, err = rc.getConfigSignatures(req, chConfig)
882+
if err != nil {
883+
return SaveChannelResponse{}, err
884+
}
879885
}
880886

881887
request := resource.CreateChannelRequest{
@@ -905,7 +911,6 @@ func (rc *Client) validateSaveChannelRequest(req SaveChannelRequest) error {
905911
}
906912

907913
func (rc *Client) getConfigSignatures(req SaveChannelRequest, chConfig []byte) ([]*common.ConfigSignature, error) {
908-
909914
// Signing user has to belong to one of configured channel organisations
910915
// In case that order org is one of channel orgs we can use context user
911916
var signers []msp.SigningIdentity
@@ -922,6 +927,150 @@ func (rc *Client) getConfigSignatures(req SaveChannelRequest, chConfig []byte) (
922927
return nil, errors.New("must provide signing user")
923928
}
924929

930+
return rc.createCfgSigFromIDs(chConfig, signers...)
931+
}
932+
933+
func readChConfigData(channelConfigPath string) ([]byte, error) {
934+
if channelConfigPath == "" {
935+
return nil, errors.New("must provide a channel config path")
936+
}
937+
938+
configReader, err := os.Open(channelConfigPath) // nolint
939+
if err != nil {
940+
return nil, errors.Wrapf(err, "opening channel config file failed")
941+
}
942+
defer loggedClose(configReader)
943+
944+
chConfig, err := extractChConfigData(configReader)
945+
if err != nil {
946+
return nil, errors.WithMessage(err, "extracting channel config from channel config reader of channelConfigPath failed")
947+
}
948+
return chConfig, nil
949+
}
950+
951+
func extractChConfigData(channelConfigReader io.Reader) ([]byte, error) {
952+
if channelConfigReader == nil {
953+
return nil, errors.New("must provide a non empty channel config file")
954+
}
955+
configTx, err := ioutil.ReadAll(channelConfigReader)
956+
if err != nil {
957+
return nil, errors.WithMessage(err, "reading channel config file failed")
958+
}
959+
960+
chConfig, err := resource.ExtractChannelConfig(configTx)
961+
if err != nil {
962+
return nil, errors.WithMessage(err, "extracting channel config from ConfigTx failed")
963+
}
964+
965+
return chConfig, nil
966+
}
967+
968+
// CreateConfigSignature creates a signature for the given client, custom signers and chConfig from channelConfigPath argument
969+
// return ConfigSignature will be signed internally by the SDK. It can be passed to WithConfigSignatures() option
970+
func (rc *Client) CreateConfigSignature(signer msp.SigningIdentity, channelConfigPath string) (*common.ConfigSignature, error) {
971+
chConfig, err := readChConfigData(channelConfigPath)
972+
if err != nil {
973+
return nil, err
974+
}
975+
976+
sigs, err := rc.createCfgSigFromIDs(chConfig, signer)
977+
if err != nil {
978+
return nil, err
979+
}
980+
981+
if len(sigs) != 1 {
982+
return nil, errors.New("creating a config signature for 1 identity did not return 1 signature")
983+
}
984+
985+
return sigs[0], nil
986+
}
987+
988+
// CreateConfigSignatureData will prepare a SignatureHeader and the full signing []byte (signingBytes) to be used for signing a Channel Config
989+
// Once SigningBytes have been signed externally (signing signatureHeaderData.SigningBytes using an external tool like OpenSSL), do the following:
990+
// 1. create a common.ConfigSignature{} instance
991+
// 2. assign its SignatureHeader field with the returned field 'signatureHeaderData.signatureHeader'
992+
// 3. assign its Signature field with the generated signature of 'signatureHeaderData.signingBytes' from the external tool
993+
// Then use WithConfigSignatures() option to pass this new instance for channel updates
994+
func (rc *Client) CreateConfigSignatureData(signer msp.SigningIdentity, channelConfigPath string) (signatureHeaderData resource.ConfigSignatureData, e error) {
995+
chConfig, err := readChConfigData(channelConfigPath)
996+
if err != nil {
997+
e = err
998+
return
999+
}
1000+
sigCtx := contextImpl.Client{
1001+
SigningIdentity: signer,
1002+
Providers: rc.ctx,
1003+
}
1004+
1005+
return resource.GetConfigSignatureData(&sigCtx, chConfig)
1006+
}
1007+
1008+
// MarshalConfigSignature marshals 1 ConfigSignature for the given client concatenated as []byte
1009+
func MarshalConfigSignature(signature *common.ConfigSignature) ([]byte, error) {
1010+
mSig, err := proto.Marshal(signature)
1011+
if err != nil {
1012+
return nil, errors.WithMessage(err, "failed to marshal signature")
1013+
}
1014+
return mSig, nil
1015+
}
1016+
1017+
// UnmarshalConfigSignature reads 1 ConfigSignature as []byte from reader and unmarshals it
1018+
func UnmarshalConfigSignature(reader io.Reader) (*common.ConfigSignature, error) {
1019+
arr, err := readConfigSignatureArray(reader)
1020+
if err != nil {
1021+
return nil, errors.Wrap(err, "reading ConfigSiganture array failed")
1022+
}
1023+
1024+
configSignature := &common.ConfigSignature{}
1025+
err = proto.Unmarshal(arr, configSignature)
1026+
if err != nil {
1027+
return nil, errors.WithMessage(err, "Failed to unmarshal config signature")
1028+
}
1029+
return configSignature, nil
1030+
}
1031+
1032+
func createConfigSignatureOption(r io.Reader, opts *requestOptions) error {
1033+
arr, err := readConfigSignatureArray(r)
1034+
if err != nil {
1035+
logger.Warnf("Failed to read channel config signature from bytes array: %s .. ignoring", err)
1036+
return err
1037+
}
1038+
1039+
singleSig := &common.ConfigSignature{}
1040+
1041+
err = proto.Unmarshal(arr, singleSig)
1042+
if err != nil {
1043+
logger.Warnf("Failed to unmarshal channel config signature from bytes array: %s .. ignoring signature", err)
1044+
return err
1045+
}
1046+
1047+
opts.Signatures = append(opts.Signatures, singleSig)
1048+
1049+
return nil
1050+
}
1051+
1052+
func readConfigSignatureArray(reader io.Reader) ([]byte, error) {
1053+
buff := make([]byte, bufferSize)
1054+
arr := []byte{}
1055+
for {
1056+
n, err := reader.Read(buff)
1057+
if err != nil && err != io.EOF {
1058+
logger.Warnf("Failed to read config signature data from reader: %s", err)
1059+
return nil, errors.WithMessage(err, "Failed to read config signature data from reader")
1060+
}
1061+
1062+
if n == 0 {
1063+
break
1064+
} else if n < bufferSize {
1065+
arr = append(arr, buff[:n]...)
1066+
} else {
1067+
arr = append(arr, buff...)
1068+
}
1069+
}
1070+
return arr, nil
1071+
}
1072+
1073+
func (rc *Client) createCfgSigFromIDs(chConfig []byte, signers ...msp.SigningIdentity) ([]*common.ConfigSignature, error) {
9251074
var configSignatures []*common.ConfigSignature
9261075
for _, signer := range signers {
9271076

@@ -938,7 +1087,6 @@ func (rc *Client) getConfigSignatures(req SaveChannelRequest, chConfig []byte) (
9381087
}
9391088

9401089
return configSignatures, nil
941-
9421090
}
9431091

9441092
func loggedClose(c io.Closer) {

0 commit comments

Comments
 (0)