Skip to content

Commit

Permalink
Orderer Signer MSP-based implementation
Browse files Browse the repository at this point in the history
This change-set introduces an MSP-based
orderer multichain.signer implementation.

At bootstrap, an orderer loads his local
signing identity that is then used to sign
messages by means of the multichain.Signer interface.

This change-set comes in the context of
https://jira.hyperledger.org/browse/FAB-1268

Also updates the orderer config path stuff to be slightly more
consistent to support referencing the msp sampleconfig both from the dev
env and the docker image.

Change-Id: Ie4e04cb18d0f28ce801a474a3c500d4f61fa9c7f
Signed-off-by: Angelo De Caro <adc@zurich.ibm.com>
Signed-off-by: Jason Yellick <jyellick@us.ibm.com>
  • Loading branch information
Jason Yellick committed Jan 17, 2017
1 parent 1b9bb80 commit 958a66f
Show file tree
Hide file tree
Showing 12 changed files with 282 additions and 48 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ build/image/peer/payload: build/docker/bin/peer \
build/msp-sampleconfig.tar.bz2 \
build/genesis-sampleconfig.tar.bz2
build/image/orderer/payload: build/docker/bin/orderer \
build/msp-sampleconfig.tar.bz2 \
orderer/orderer.yaml
build/image/testenv/payload: build/gotools.tar.bz2
build/image/runtime/payload: build/docker/busybox
Expand Down
28 changes: 28 additions & 0 deletions common/crypto/signer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
Copyright IBM Corp. 2016 All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package crypto

import cb "github.com/hyperledger/fabric/protos/common"

// LocalSigner is a temporary stub interface which will be implemented by the local MSP
type LocalSigner interface {
// NewSignatureHeader creates a SignatureHeader with the correct signing identity and a valid nonce
NewSignatureHeader() (*cb.SignatureHeader, error)

// Sign a message which should embed a signature header created by NewSignatureHeader
Sign(message []byte) ([]byte, error)
}
75 changes: 75 additions & 0 deletions common/localmsp/signer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
Copyright IBM Corp. 2016 All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package localmsp

import (
"fmt"

"github.com/hyperledger/fabric/core/crypto/primitives"
"github.com/hyperledger/fabric/common/crypto"
cb "github.com/hyperledger/fabric/protos/common"
"github.com/hyperledger/fabric/core/peer/msp"
)

type mspSigner struct {
}

// New returns a new instance of the msp-based LocalSigner.
// It assumes that the local msp has been already initialized.
// Look at mspmgmt.LoadLocalMsp for further information.
func NewSigner() crypto.LocalSigner {
return &mspSigner{}
}

// NewSignatureHeader creates a SignatureHeader with the correct signing identity and a valid nonce
func (s *mspSigner) NewSignatureHeader() (*cb.SignatureHeader, error) {
signer, err := mspmgmt.GetLocalMSP().GetDefaultSigningIdentity()
if err != nil {
return nil, fmt.Errorf("Failed getting MSP-based signer [%s]", err)
}

creatorIdentityRaw, err := signer.Serialize()
if err != nil {
return nil, fmt.Errorf("Failed serializing creator public identity [%s]", err)
}

nonce, err := primitives.GetRandomNonce()
if err != nil {
return nil, fmt.Errorf("Failed creating nonce [%s]", err)
}

sh := &cb.SignatureHeader{}
sh.Creator = creatorIdentityRaw
sh.Nonce = nonce

return sh, nil
}

// Sign a message which should embed a signature header created by NewSignatureHeader
func (s *mspSigner) Sign(message []byte) ([]byte, error) {
signer, err := mspmgmt.GetLocalMSP().GetDefaultSigningIdentity()
if err != nil {
return nil, fmt.Errorf("Failed getting MSP-based signer [%s]", err)
}

signature, err := signer.Sign(message)
if err != nil {
return nil, fmt.Errorf("Failed generating signature [%s]", err)
}

return signature, nil
}
84 changes: 84 additions & 0 deletions common/localmsp/signer_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
Copyright IBM Corp. 2016 All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package localmsp

import (
"os"
"testing"

"github.com/hyperledger/fabric/core/crypto/primitives"
"github.com/stretchr/testify/assert"
"github.com/hyperledger/fabric/core/peer/msp"
)

func TestMain(m *testing.M) {
// 1. Determine MSP configuration
var mspMgrConfigDir string
var alternativeCfgPath = os.Getenv("ORDERER_CFG_PATH")
if alternativeCfgPath != "" {
mspMgrConfigDir = alternativeCfgPath + "/msp/sampleconfig/"
} else if _, err := os.Stat("./msp/sampleconfig/"); err == nil {
mspMgrConfigDir = "./msp/sampleconfig/"
} else {
mspMgrConfigDir = os.Getenv("GOPATH") + "/src/github.com/hyperledger/fabric/msp/sampleconfig/"
}

if err := mspmgmt.LoadLocalMsp(mspMgrConfigDir); err != nil {
os.Exit(-1)
}

os.Exit(m.Run())
}

func TestNewSigner(t *testing.T) {
signer := NewSigner()
assert.NotNil(t, signer, "Signer must be differentr from nil.")
}

func TestMspSigner_NewSignatureHeader(t *testing.T) {
signer := NewSigner()

sh, err := signer.NewSignatureHeader()
if err != nil {
t.Fatalf("Failed creting signature header [%s]", err)
}

assert.NotNil(t, sh, "SignatureHeader must be different from nil")
assert.Len(t, sh.Nonce, primitives.NonceSize, "SignatureHeader.Nonce must be of length %d", primitives.NonceSize)

mspIdentity, err := mspmgmt.GetLocalMSP().GetDefaultSigningIdentity()
assert.NoError(t, err, "Failed getting default MSP Identity")
publicIdentity := mspIdentity.GetPublicVersion()
assert.NotNil(t, publicIdentity, "Failed getting default public identity. It must be different from nil.")
publicIdentityRaw, err := publicIdentity.Serialize()
assert.NoError(t, err, "Failed serializing default public identity")
assert.Equal(t, publicIdentityRaw, sh.Creator, "Creator must be local default signer identity")
}

func TestMspSigner_Sign(t *testing.T) {
signer := NewSigner()

msg := []byte("Hello World")
sigma, err := signer.Sign(msg)
assert.NoError(t, err, "FAiled generating signature")

// Verify signature
mspIdentity, err := mspmgmt.GetLocalMSP().GetDefaultSigningIdentity()
assert.NoError(t, err, "Failed getting default MSP Identity")
err = mspIdentity.Verify(msg, sigma)
assert.NoError(t, err, "Failed verifiing signature")
}
7 changes: 4 additions & 3 deletions images/orderer/Dockerfile.in
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
FROM hyperledger/fabric-runtime:_TAG_
ENV ORDERER_CFG_PATH /etc/hyperledger/fabric
RUN mkdir -p /var/hyperledger/db /etc/hyperledger/fabric
ENV ORDERER_CFG_PATH /etc/hyperledger/fabric/orderer
RUN mkdir -p /var/hyperledger/db /etc/hyperledger/fabric/orderer
COPY payload/orderer /usr/local/bin
COPY payload/orderer.yaml $ORDERER_CFG_PATH
ADD payload/msp-sampleconfig.tar.bz2 $ORDERER_CFG_PATH/../
COPY payload/orderer.yaml $ORDERER_CFG_PATH/
EXPOSE 7050
CMD orderer
34 changes: 23 additions & 11 deletions orderer/localconfig/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ type General struct {
GenesisFile string
Profile Profile
LogLevel string
LocalMSPDir string
}

// Genesis contains config which is used by the provisional bootstrapper
Expand Down Expand Up @@ -121,7 +122,8 @@ var defaults = TopLevel{
Enabled: false,
Address: "0.0.0.0:6060",
},
LogLevel: "INFO",
LogLevel: "INFO",
LocalMSPDir: "../msp/sampleconfig/",
},
RAMLedger: RAMLedger{
HistorySize: 10000,
Expand Down Expand Up @@ -180,6 +182,12 @@ func (c *TopLevel) completeInitialization() {
case c.General.Profile.Enabled && (c.General.Profile.Address == ""):
logger.Infof("Profiling enabled and General.Profile.Address unset, setting to %s", defaults.General.Profile.Address)
c.General.Profile.Address = defaults.General.Profile.Address
case c.General.LocalMSPDir == "":
logger.Infof("General.LocalMSPDir unset, setting to %s", defaults.General.LocalMSPDir)
// Note, this is a bit of a weird one, the orderer may set the ORDERER_CFG_PATH after
// the file is initialized, so we cannot initialize this in the structure, so we
// deference the env portion here
c.General.LocalMSPDir = filepath.Join(os.Getenv("ORDERER_CFG_PATH"), defaults.General.LocalMSPDir)
case c.FileLedger.Prefix == "":
logger.Infof("FileLedger.Prefix unset, setting to %s", defaults.FileLedger.Prefix)
c.FileLedger.Prefix = defaults.FileLedger.Prefix
Expand Down Expand Up @@ -221,22 +229,26 @@ func Load() *TopLevel {
config := viper.New()

config.SetConfigName("orderer")
alternativeCfgPath := os.Getenv("ORDERER_CFG_PATH")
if alternativeCfgPath != "" {
logger.Infof("User defined config file path: %s", alternativeCfgPath)
config.AddConfigPath(alternativeCfgPath) // Path to look for the config file in
} else {
config.AddConfigPath("./")
config.AddConfigPath("../../.")
config.AddConfigPath("../orderer/")
config.AddConfigPath("../../orderer/")
cfgPath := os.Getenv("ORDERER_CFG_PATH")
if cfgPath == "" {
logger.Infof("No orderer cfg path set, assuming development environment, deriving from go path")
// Path to look for the config file in based on GOPATH
gopath := os.Getenv("GOPATH")
for _, p := range filepath.SplitList(gopath) {
ordererPath := filepath.Join(p, "src/github.com/hyperledger/fabric/orderer/")
config.AddConfigPath(ordererPath)
if _, err := os.Stat(filepath.Join(ordererPath, "orderer.yaml")); err != nil {
// The yaml file does not exist in this component of the go src
continue
}
cfgPath = ordererPath
}
if cfgPath == "" {
logger.Fatalf("Could not find orderer.yaml, try setting ORDERER_CFG_PATH or GOPATH correctly")
}
logger.Infof("Setting ORDERER_CFG_PATH to: %s", cfgPath)
os.Setenv("ORDERER_CFG_PATH", cfgPath)
}
config.AddConfigPath(cfgPath) // Path to look for the config file in

// for environment variables
config.SetEnvPrefix(Prefix)
Expand Down
12 changes: 11 additions & 1 deletion orderer/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,17 @@ import (
"github.com/hyperledger/fabric/protos/utils"

"github.com/Shopify/sarama"
"github.com/hyperledger/fabric/common/localmsp"
"github.com/hyperledger/fabric/core/peer/msp"
logging "github.com/op/go-logging"
"google.golang.org/grpc"
)

var logger = logging.MustGetLogger("orderer/main")

func main() {
// Temporarilly set logging level until config is read
logging.SetLevel(logging.INFO, "")
conf := config.Load()
flogging.InitFromSpec(conf.General.LogLevel)

Expand All @@ -67,6 +71,12 @@ func main() {
return
}

// Load local MSP
err = mspmgmt.LoadLocalMsp(conf.General.LocalMSPDir)
if err != nil { // Handle errors reading the config file
panic(fmt.Errorf("Failed initializing crypto [%s]", err))
}

var lf ordererledger.Factory
switch conf.General.LedgerType {
case "file":
Expand Down Expand Up @@ -127,7 +137,7 @@ func main() {
consenters["solo"] = solo.New()
consenters["kafka"] = kafka.New(conf.Kafka.Version, conf.Kafka.Retry)

manager := multichain.NewManagerImpl(lf, consenters)
manager := multichain.NewManagerImpl(lf, consenters, localmsp.NewSigner())

server := NewServer(
manager,
Expand Down
8 changes: 4 additions & 4 deletions orderer/mocks/multichain/multichain.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,11 @@ func (mcs *ConsenterSupport) ChainID() string {
}

// Sign returns the bytes passed in
func (mcs *ConsenterSupport) Sign(message []byte) []byte {
return message
func (mcs *ConsenterSupport) Sign(message []byte) ([]byte, error) {
return message, nil
}

// NewSignatureHeader returns an empty signature header
func (mcs *ConsenterSupport) NewSignatureHeader() *cb.SignatureHeader {
return &cb.SignatureHeader{}
func (mcs *ConsenterSupport) NewSignatureHeader() (*cb.SignatureHeader, error) {
return &cb.SignatureHeader{}, nil
}
Loading

0 comments on commit 958a66f

Please sign in to comment.