Skip to content

Commit

Permalink
[FAB-7638] Block expired x509 identities in bcast
Browse files Browse the repository at this point in the history
This change set blocks expired x509 identities from succeeding
broadcast, via a new envelope filter.

Change-Id: I6d864b9ed3f525355e38de5c2b7b05105d079aa8
Signed-off-by: yacovm <[email protected]>
  • Loading branch information
yacovm committed Jan 8, 2018
1 parent d783a0d commit 07cbf6e
Show file tree
Hide file tree
Showing 10 changed files with 242 additions and 0 deletions.
6 changes: 6 additions & 0 deletions common/capabilities/orderer.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,9 @@ func (cp *OrdererProvider) SetChannelModPolicyDuringCreate() bool {
func (cp *OrdererProvider) Resubmission() bool {
return cp.v11BugFixes
}

// ExpirationCheck specifies whether the orderer checks for identity expiration checks
// when validating messages
func (cp *OrdererProvider) ExpirationCheck() bool {
return cp.v11BugFixes
}
4 changes: 4 additions & 0 deletions common/channelconfig/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,10 @@ type OrdererCapabilities interface {

// Supported returns an error if there are unknown capabilities in this channel which are required
Supported() error

// ExpirationCheck specifies whether the orderer checks for identity expiration checks
// when validating messages
ExpirationCheck() bool
}

// Resources is the common set of config resources for all channels
Expand Down
9 changes: 9 additions & 0 deletions common/mocks/config/orderer.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ type OrdererCapabilities struct {

// ResubmissionVal is returned by Resubmission()
ResubmissionVal bool

// ExpirationVal is returned by ExpirationCheck()
ExpirationVal bool
}

// Supported returns SupportedErr
Expand All @@ -92,3 +95,9 @@ func (oc *OrdererCapabilities) SetChannelModPolicyDuringCreate() bool {
func (oc *OrdererCapabilities) Resubmission() bool {
return oc.ResubmissionVal
}

// ExpirationCheck specifies whether the orderer checks for identity expiration checks
// when validating messages
func (oc *OrdererCapabilities) ExpirationCheck() bool {
return oc.ExpirationVal
}
54 changes: 54 additions & 0 deletions orderer/common/msgprocessor/expiration.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package msgprocessor

import (
"time"

"github.com/hyperledger/fabric/common/channelconfig"
"github.com/hyperledger/fabric/common/crypto"
"github.com/hyperledger/fabric/protos/common"
"github.com/pkg/errors"
)

type resources interface {
// OrdererConfig returns the config.Orderer for the channel
// and whether the Orderer config exists
OrdererConfig() (channelconfig.Orderer, bool)
}

// NewExpirationRejectRule returns a rule that rejects messages signed by identities
// who's identities have expired, given the capability is active
func NewExpirationRejectRule(filterSupport resources) Rule {
return &expirationRejectRule{filterSupport: filterSupport}
}

type expirationRejectRule struct {
filterSupport resources
}

// Apply checks whether the identity that created the envelope has expired
func (exp *expirationRejectRule) Apply(message *common.Envelope) error {
ordererConf, ok := exp.filterSupport.OrdererConfig()
if !ok {
logger.Panic("Programming error: orderer config not found")
}
if !ordererConf.Capabilities().ExpirationCheck() {
return nil
}
signedData, err := message.AsSignedData()

if err != nil {
return errors.Errorf("could not convert message to signedData: %s", err)
}
expirationTime := crypto.ExpiresAt(signedData[0].Identity)
// Identity cannot expire, or identity has not expired yet
if expirationTime.IsZero() || time.Now().Before(expirationTime) {
return nil
}
return errors.New("identity expired")
}
128 changes: 128 additions & 0 deletions orderer/common/msgprocessor/expiration_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package msgprocessor

import (
"io/ioutil"
"path/filepath"
"testing"

"github.com/golang/protobuf/proto"
"github.com/hyperledger/fabric/common/channelconfig"
"github.com/hyperledger/fabric/common/mocks/config"
"github.com/hyperledger/fabric/protos/common"
"github.com/hyperledger/fabric/protos/msp"
"github.com/hyperledger/fabric/protos/utils"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)

func createEnvelope(t *testing.T, serializedIdentity []byte) *common.Envelope {
sHdr := utils.MakeSignatureHeader(serializedIdentity, nil)
hdr := utils.MakePayloadHeader(&common.ChannelHeader{}, sHdr)
payload := &common.Payload{
Header: hdr,
}
payloadBytes, err := proto.Marshal(payload)
assert.NoError(t, err)
return &common.Envelope{
Payload: payloadBytes,
Signature: []byte{1, 2, 3},
}
}

func createX509Identity(t *testing.T, certFileName string) []byte {
certBytes, err := ioutil.ReadFile(filepath.Join("testdata", certFileName))
assert.NoError(t, err)
sId := &msp.SerializedIdentity{
IdBytes: certBytes,
}
idBytes, err := proto.Marshal(sId)
assert.NoError(t, err)
return idBytes
}

func createIdemixIdentity(t *testing.T) []byte {
idemixId := &msp.SerializedIdemixIdentity{
NymX: []byte{1, 2, 3},
NymY: []byte{1, 2, 3},
OU: []byte("OU1"),
}
idemixBytes, err := proto.Marshal(idemixId)
assert.NoError(t, err)
sId := &msp.SerializedIdentity{
IdBytes: idemixBytes,
}
idBytes, err := proto.Marshal(sId)
assert.NoError(t, err)
return idBytes
}

type resourcesMock struct {
mock.Mock
}

func (r *resourcesMock) OrdererConfig() (channelconfig.Orderer, bool) {
args := r.Called()
if args.Get(1).(bool) {
return args.Get(0).(channelconfig.Orderer), true
}
return nil, false
}

func TestExpirationRejectRule(t *testing.T) {
activeCapability := &config.Orderer{CapabilitiesVal: &config.OrdererCapabilities{
ExpirationVal: true,
}}
inActiveCapability := &config.Orderer{CapabilitiesVal: &config.OrdererCapabilities{
ExpirationVal: false,
}}
resources := &resourcesMock{}
setupMock := func() {
// Odd invocations return active capability
resources.On("OrdererConfig").Return(activeCapability, true).Once()
// Even invocations return inactive capability
resources.On("OrdererConfig").Return(inActiveCapability, true).Once()
}
t.Run("NoOrdererConfig", func(t *testing.T) {
resources.On("OrdererConfig").Return(nil, false).Once()
assert.Panics(t, func() {
NewExpirationRejectRule(resources).Apply(&common.Envelope{})
})
})
t.Run("BadEnvelope", func(t *testing.T) {
setupMock()
err := NewExpirationRejectRule(resources).Apply(&common.Envelope{})
assert.Error(t, err)
assert.Contains(t, err.Error(), "could not convert message to signedData")

err = NewExpirationRejectRule(resources).Apply(&common.Envelope{})
assert.NoError(t, err)
})
t.Run("ExpiredX509Identity", func(t *testing.T) {
setupMock()
env := createEnvelope(t, createX509Identity(t, "expiredCert.pem"))
err := NewExpirationRejectRule(resources).Apply(env)
assert.Error(t, err)
assert.Equal(t, err.Error(), "identity expired")

err = NewExpirationRejectRule(resources).Apply(env)
assert.NoError(t, err)
})
t.Run("IdemixIdentity", func(t *testing.T) {
setupMock()
env := createEnvelope(t, createIdemixIdentity(t))
assert.Nil(t, NewExpirationRejectRule(resources).Apply(env))
assert.Nil(t, NewExpirationRejectRule(resources).Apply(env))
})
t.Run("NoneExpiredX509Identity", func(t *testing.T) {
setupMock()
env := createEnvelope(t, createX509Identity(t, "cert.pem"))
assert.Nil(t, NewExpirationRejectRule(resources).Apply(env))
assert.Nil(t, NewExpirationRejectRule(resources).Apply(env))
})
}
1 change: 1 addition & 0 deletions orderer/common/msgprocessor/standardchannel.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ func CreateStandardChannelFilters(filterSupport channelconfig.Resources) *RuleSe
}
return NewRuleSet([]Rule{
EmptyRejectRule,
NewExpirationRejectRule(filterSupport),
NewSizeFilter(ordererConfig),
NewSigFilter(policies.ChannelWriters, filterSupport),
})
Expand Down
1 change: 1 addition & 0 deletions orderer/common/msgprocessor/systemchannel.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ func CreateSystemChannelFilters(chainCreator ChainCreator, ledgerResources chann
}
return NewRuleSet([]Rule{
EmptyRejectRule,
NewExpirationRejectRule(ledgerResources),
NewSizeFilter(ordererConfig),
NewSigFilter(policies.ChannelWriters, ledgerResources),
NewSystemChannelFilter(ledgerResources, chainCreator),
Expand Down
12 changes: 12 additions & 0 deletions orderer/common/msgprocessor/testdata/badCert.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
-----BEGIN CERTIFICATE-----
MIICCDCCAa6gAwIBAgIRANLH5Ue5a6tHuzCQtap1BP8wCgYIKoZIzj0EAwIwZzEL
MAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBG
cmFuY2lzY28xEzARBgNVBAoTCmhybC5pYm0uaWwxFjAUBgNVBAMTDWNhLmhybC5p
EwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZyYW5jaXNj
bzEZMBcGA1UEAwwQVXNlcjFAaHJsLmlibS5pbDBZMBMGByqGSM49AgEGCCqGSM49
AwEHA0IABE7fF65KsF0nxNgIBFVA2x/QU0LuAyuTsRaSWc/ycQAuLQfCti5bYp4W
WaQUc5sBaKAmVbFQTm9RhmOhtIz7PL6jTTBLMA4GA1UdDwEB/wQEAwIHgDAMBgNV
HRMBAf8EAjAAMCsGA1UdIwQkMCKAIMjiBsyFZlbO6pRxo7VgoqKhl78Ujd9sdWUk
epB05fodMAoGCCqGSM49BAMCA0gAMEUCIQCiOzbaApF46NVobwh3wqHf8ID1zxja
j23HPXR3FjjFZgIgXLujyDGETptNrELaytjG+dxO3Kzq/SM07K2zPUg4368=
-----END CERTIFICATE-----
13 changes: 13 additions & 0 deletions orderer/common/msgprocessor/testdata/cert.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
-----BEGIN CERTIFICATE-----
MIICCDCCAa6gAwIBAgIRANLH5Ue5a6tHuzCQtap1BP8wCgYIKoZIzj0EAwIwZzEL
MAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBG
cmFuY2lzY28xEzARBgNVBAoTCmhybC5pYm0uaWwxFjAUBgNVBAMTDWNhLmhybC5p
Ym0uaWwwHhcNMTcwODE5MTIxOTQ4WhcNMjcwODE3MTIxOTQ4WjBVMQswCQYDVQQG
EwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZyYW5jaXNj
bzEZMBcGA1UEAwwQVXNlcjFAaHJsLmlibS5pbDBZMBMGByqGSM49AgEGCCqGSM49
AwEHA0IABE7fF65KsF0nxNgIBFVA2x/QU0LuAyuTsRaSWc/ycQAuLQfCti5bYp4W
WaQUc5sBaKAmVbFQTm9RhmOhtIz7PL6jTTBLMA4GA1UdDwEB/wQEAwIHgDAMBgNV
HRMBAf8EAjAAMCsGA1UdIwQkMCKAIMjiBsyFZlbO6pRxo7VgoqKhl78Ujd9sdWUk
epB05fodMAoGCCqGSM49BAMCA0gAMEUCIQCiOzbaApF46NVobwh3wqHf8ID1zxja
j23HPXR3FjjFZgIgXLujyDGETptNrELaytjG+dxO3Kzq/SM07K2zPUg4368=
-----END CERTIFICATE-----
14 changes: 14 additions & 0 deletions orderer/common/msgprocessor/testdata/expiredCert.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
-----BEGIN CERTIFICATE-----
MIICGTCCAcCgAwIBAgIRAOazAZAhdFlzFdQYiWYwy3cwCgYIKoZIzj0EAwIwczEL
MAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBG
cmFuY2lzY28xGTAXBgNVBAoTEG9yZzEuZXhhbXBsZS5jb20xHDAaBgNVBAMTE2Nh
Lm9yZzEuZXhhbXBsZS5jb20wHhcNMTgwMTA3MTA0NDAzWhcNMTgwMTA3MTAzOTAz
WjBbMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMN
U2FuIEZyYW5jaXNjbzEfMB0GA1UEAwwWVXNlcjFAb3JnMS5leGFtcGxlLmNvbTBZ
MBMGByqGSM49AgEGCCqGSM49AwEHA0IABF6gDOVUGphkUaQj301wSXitdSpngXv8
Dee1b79s38WdZGrxL+7EOYxY1UhJBwKbNJqOvRcwSGRFkJthLpSSL32jTTBLMA4G
A1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMCsGA1UdIwQkMCKAIC9+Pq7Gi7uv
MCWUVvVIuOQPAqCWXT+5MYKe4Z+b2vy1MAoGCCqGSM49BAMCA0cAMEQCIBMh5fwh
GFHAscjF04rVooiJ/o6tDrmPl8NBEbzdaNWoAiAe1/7LeS8DkQ+tfsBAoK/OECpu
bDEvOYkljiKIpabYiQ==
-----END CERTIFICATE-----

0 comments on commit 07cbf6e

Please sign in to comment.