From c604d8ea9b0e9f5a60b44c8885d523d835d78682 Mon Sep 17 00:00:00 2001 From: Jason Yellick Date: Mon, 9 Jan 2017 13:25:35 -0500 Subject: [PATCH] [FAB-1565] Add signature filter https://jira.hyperledger.org/browse/FAB-1565 This is the second in the three changesets in a series to filter incoming broadcast transactions by signature. This changeset adds a broadcast filter (but does not enable it) to filter messages based on a policy name. Change-Id: Ib314f0f07c87029c0d8f8f7fe2a60969ead29bd9 Signed-off-by: Jason Yellick --- orderer/common/sigfilter/sigfilter.go | 70 ++++++++++++++++++ orderer/common/sigfilter/sigfilter_test.go | 83 ++++++++++++++++++++++ 2 files changed, 153 insertions(+) create mode 100644 orderer/common/sigfilter/sigfilter.go create mode 100644 orderer/common/sigfilter/sigfilter_test.go diff --git a/orderer/common/sigfilter/sigfilter.go b/orderer/common/sigfilter/sigfilter.go new file mode 100644 index 00000000000..241c8e61928 --- /dev/null +++ b/orderer/common/sigfilter/sigfilter.go @@ -0,0 +1,70 @@ +/* +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 sigfilter + +import ( + "github.com/hyperledger/fabric/common/policies" + "github.com/hyperledger/fabric/orderer/common/filter" + cb "github.com/hyperledger/fabric/protos/common" + + "github.com/op/go-logging" +) + +var logger = logging.MustGetLogger("orderer/common/sigfilter") + +type sigFilter struct { + policySource func() string + policyManager policies.Manager +} + +// New creates a new signature filter, at every evaluation, the policySource is called +// just before evaluation to get the policy name to use when evaluating the filter +func New(policySource func() string, policyManager policies.Manager) filter.Rule { + return &sigFilter{ + policySource: policySource, + policyManager: policyManager, + } +} + +// Apply applies the policy given, resulting in Reject or Forward, never Accept and always with nil Committer +func (sf *sigFilter) Apply(message *cb.Envelope) (filter.Action, filter.Committer) { + signedData, err := message.AsSignedData() + + if err != nil { + if logger.IsEnabledFor(logging.DEBUG) { + logger.Debugf("Rejecting because of err: %s", err) + } + return filter.Reject, nil + } + + policy, ok := sf.policyManager.GetPolicy(sf.policySource()) + if !ok { + logger.Debugf("Rejecting because policy was not found") + return filter.Reject, nil + } + + err = policy.Evaluate(signedData) + + if err != nil { + if logger.IsEnabledFor(logging.DEBUG) { + logger.Debugf("Rejecting because policy did not evaluate without error: %s", err) + } + return filter.Reject, nil + } + + return filter.Forward, nil +} diff --git a/orderer/common/sigfilter/sigfilter_test.go b/orderer/common/sigfilter/sigfilter_test.go new file mode 100644 index 00000000000..aaca48010e9 --- /dev/null +++ b/orderer/common/sigfilter/sigfilter_test.go @@ -0,0 +1,83 @@ +/* +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 sigfilter + +import ( + "fmt" + "testing" + + "github.com/hyperledger/fabric/orderer/common/filter" + mockpolicies "github.com/hyperledger/fabric/orderer/mocks/policies" + cb "github.com/hyperledger/fabric/protos/common" + "github.com/hyperledger/fabric/protos/utils" + + "github.com/op/go-logging" +) + +func init() { + logging.SetLevel(logging.DEBUG, "") +} + +func makeEnvelope() *cb.Envelope { + return &cb.Envelope{ + Payload: utils.MarshalOrPanic(&cb.Payload{ + Header: &cb.Header{ + SignatureHeader: &cb.SignatureHeader{}, + }, + }), + } +} + +func fooSource() string { + return "foo" +} + +func TestAccept(t *testing.T) { + mpm := &mockpolicies.Manager{Policy: &mockpolicies.Policy{}} + sf := New(fooSource, mpm) + result, _ := sf.Apply(makeEnvelope()) + if result != filter.Forward { + t.Fatalf("Should have accepted envelope") + } +} + +func TestMissingPolicy(t *testing.T) { + mpm := &mockpolicies.Manager{} + sf := New(fooSource, mpm) + result, _ := sf.Apply(makeEnvelope()) + if result != filter.Reject { + t.Fatalf("Should have rejected when missing policy") + } +} + +func TestEmptyPayload(t *testing.T) { + mpm := &mockpolicies.Manager{Policy: &mockpolicies.Policy{}} + sf := New(fooSource, mpm) + result, _ := sf.Apply(&cb.Envelope{}) + if result != filter.Reject { + t.Fatalf("Should have rejected when payload empty") + } +} + +func TestErrorOnPolicy(t *testing.T) { + mpm := &mockpolicies.Manager{Policy: &mockpolicies.Policy{Err: fmt.Errorf("Error")}} + sf := New(fooSource, mpm) + result, _ := sf.Apply(makeEnvelope()) + if result != filter.Reject { + t.Fatalf("Should have rejected when policy evaluated to err") + } +}