diff --git a/common/cauthdsl/cauthdsl.go b/common/cauthdsl/cauthdsl.go index b5d366ed7fd..be830e2b835 100644 --- a/common/cauthdsl/cauthdsl.go +++ b/common/cauthdsl/cauthdsl.go @@ -25,35 +25,14 @@ import ( // CryptoHelper is used to provide a plugin point for different signature validation types type CryptoHelper interface { - VerifySignature(msg []byte, id []byte, signature []byte) bool -} - -// SignaturePolicyEvaluator is useful for a chain Reader to stream blocks as they are created -type SignaturePolicyEvaluator struct { - compiledAuthenticator func([]byte, [][]byte, [][]byte) bool -} - -// NewSignaturePolicyEvaluator evaluates a protbuf SignaturePolicy to produce a 'compiled' version which can be invoked in code -func NewSignaturePolicyEvaluator(policy *cb.SignaturePolicyEnvelope, ch CryptoHelper) (*SignaturePolicyEvaluator, error) { - if policy.Version != 0 { - return nil, fmt.Errorf("This evaluator only understands messages of version 0, but version was %d", policy.Version) - } - - compiled, err := compile(policy.Policy, policy.Identities, ch) - if err != nil { - return nil, err - } - - return &SignaturePolicyEvaluator{ - compiledAuthenticator: compiled, - }, nil + VerifySignature(signedData *cb.SignedData) error } // compile recursively builds a go evaluatable function corresponding to the policy specified -func compile(policy *cb.SignaturePolicy, identities [][]byte, ch CryptoHelper) (func([]byte, [][]byte, [][]byte) bool, error) { +func compile(policy *cb.SignaturePolicy, identities [][]byte, ch CryptoHelper) (func([]*cb.SignedData) bool, error) { switch t := policy.Type.(type) { case *cb.SignaturePolicy_From: - policies := make([]func([]byte, [][]byte, [][]byte) bool, len(t.From.Policies)) + policies := make([]func([]*cb.SignedData) bool, len(t.From.Policies)) for i, policy := range t.From.Policies { compiledPolicy, err := compile(policy, identities, ch) if err != nil { @@ -62,10 +41,10 @@ func compile(policy *cb.SignaturePolicy, identities [][]byte, ch CryptoHelper) ( policies[i] = compiledPolicy } - return func(msg []byte, ids [][]byte, signatures [][]byte) bool { + return func(signedData []*cb.SignedData) bool { verified := int32(0) for _, policy := range policies { - if policy(msg, ids, signatures) { + if policy(signedData) { verified++ } } @@ -76,10 +55,10 @@ func compile(policy *cb.SignaturePolicy, identities [][]byte, ch CryptoHelper) ( return nil, fmt.Errorf("Identity index out of range, requested %d, but identies length is %d", t.SignedBy, len(identities)) } signedByID := identities[t.SignedBy] - return func(msg []byte, ids [][]byte, signatures [][]byte) bool { - for i, id := range ids { - if bytes.Equal(id, signedByID) { - return ch.VerifySignature(msg, id, signatures[i]) + return func(signedData []*cb.SignedData) bool { + for _, sd := range signedData { + if bytes.Equal(sd.Identity, signedByID) { + return ch.VerifySignature(sd) == nil } } return false @@ -87,10 +66,4 @@ func compile(policy *cb.SignaturePolicy, identities [][]byte, ch CryptoHelper) ( default: return nil, fmt.Errorf("Unknown type: %T:%v", t, t) } - -} - -// Authenticate returns nil if the authentication policy is satisfied, or an error indicating why the authentication failed -func (ape *SignaturePolicyEvaluator) Authenticate(msg []byte, ids [][]byte, signatures [][]byte) bool { - return ape.compiledAuthenticator(msg, ids, signatures) } diff --git a/common/cauthdsl/cauthdsl_test.go b/common/cauthdsl/cauthdsl_test.go index f62f7cb4a78..fb35c413802 100644 --- a/common/cauthdsl/cauthdsl_test.go +++ b/common/cauthdsl/cauthdsl_test.go @@ -18,6 +18,7 @@ package cauthdsl import ( "bytes" + "fmt" "testing" "github.com/golang/protobuf/proto" @@ -27,31 +28,47 @@ import ( var invalidSignature = []byte("badsigned") var validSignature = []byte("signed") var signers = [][]byte{[]byte("signer0"), []byte("signer1")} +var msgs = [][]byte{nil, nil} type mockCryptoHelper struct { } -func (mch *mockCryptoHelper) VerifySignature(msg []byte, id []byte, signature []byte) bool { - return bytes.Equal(signature, validSignature) +func (mch *mockCryptoHelper) VerifySignature(sd *cb.SignedData) error { + if !bytes.Equal(sd.Signature, validSignature) { + return fmt.Errorf("Bad signature") + } + return nil +} + +func toSignedData(data [][]byte, identities [][]byte, signatures [][]byte) []*cb.SignedData { + signedData := make([]*cb.SignedData, len(data)) + for i := range signedData { + signedData[i] = &cb.SignedData{ + Data: data[i], + Identity: identities[i], + Signature: signatures[i], + } + } + return signedData } func TestSimpleSignature(t *testing.T) { mch := &mockCryptoHelper{} policy := Envelope(SignedBy(0), signers) - spe, err := NewSignaturePolicyEvaluator(policy, mch) + spe, err := compile(policy.Policy, policy.Identities, mch) if err != nil { t.Fatalf("Could not create a new SignaturePolicyEvaluator using the given policy, crypto-helper: %s", err) } - if !spe.Authenticate(nil, [][]byte{signers[0]}, [][]byte{validSignature}) { - t.Error("Expected authentication to succeed with valid signatures") + if !spe([]*cb.SignedData{&cb.SignedData{Identity: signers[0], Signature: validSignature}}) { + t.Errorf("Expected authentication to succeed with valid signatures") } - if spe.Authenticate(nil, [][]byte{signers[0]}, [][]byte{invalidSignature}) { - t.Error("Expected authentication to fail given the invalid signature") + if spe([]*cb.SignedData{&cb.SignedData{Identity: signers[0], Signature: invalidSignature}}) { + t.Errorf("Expected authentication to fail given the invalid signature") } - if spe.Authenticate(nil, [][]byte{signers[1]}, [][]byte{validSignature}) { - t.Error("Expected authentication to fail because signers[1] is not authorized in the policy, despite his valid signature") + if spe([]*cb.SignedData{&cb.SignedData{Identity: signers[1], Signature: validSignature}}) { + t.Errorf("Expected authentication to fail because signers[1] is not authorized in the policy, despite his valid signature") } } @@ -59,19 +76,19 @@ func TestMultipleSignature(t *testing.T) { mch := &mockCryptoHelper{} policy := Envelope(And(SignedBy(0), SignedBy(1)), signers) - spe, err := NewSignaturePolicyEvaluator(policy, mch) + spe, err := compile(policy.Policy, policy.Identities, mch) if err != nil { t.Fatalf("Could not create a new SignaturePolicyEvaluator using the given policy, crypto-helper: %s", err) } - if !spe.Authenticate(nil, signers, [][]byte{validSignature, validSignature}) { - t.Error("Expected authentication to succeed with valid signatures") + if !spe(toSignedData(msgs, signers, [][]byte{validSignature, validSignature})) { + t.Errorf("Expected authentication to succeed with valid signatures") } - if spe.Authenticate(nil, signers, [][]byte{validSignature, invalidSignature}) { - t.Error("Expected authentication to fail given one of two invalid signatures") + if spe(toSignedData(msgs, signers, [][]byte{validSignature, invalidSignature})) { + t.Errorf("Expected authentication to fail given one of two invalid signatures") } - if spe.Authenticate(nil, [][]byte{signers[0], signers[0]}, [][]byte{validSignature, validSignature}) { - t.Error("Expected authentication to fail because although there were two valid signatures, one was duplicated") + if spe(toSignedData(msgs, [][]byte{signers[0], signers[0]}, [][]byte{validSignature, validSignature})) { + t.Errorf("Expected authentication to fail because although there were two valid signatures, one was duplicated") } } @@ -79,19 +96,19 @@ func TestComplexNestedSignature(t *testing.T) { mch := &mockCryptoHelper{} policy := Envelope(And(Or(And(SignedBy(0), SignedBy(1)), And(SignedBy(0), SignedBy(0))), SignedBy(0)), signers) - spe, err := NewSignaturePolicyEvaluator(policy, mch) + spe, err := compile(policy.Policy, policy.Identities, mch) if err != nil { t.Fatalf("Could not create a new SignaturePolicyEvaluator using the given policy, crypto-helper: %s", err) } - if !spe.Authenticate(nil, signers, [][]byte{validSignature, validSignature}) { - t.Error("Expected authentication to succeed with valid signatures") + if !spe(toSignedData(msgs, signers, [][]byte{validSignature, validSignature})) { + t.Errorf("Expected authentication to succeed with valid signatures") } - if spe.Authenticate(nil, signers, [][]byte{invalidSignature, validSignature}) { - t.Error("Expected authentication failure as only the signature of signer[1] was valid") + if spe(toSignedData(msgs, signers, [][]byte{invalidSignature, validSignature})) { + t.Errorf("Expected authentication failure as only the signature of signer[1] was valid") } - if !spe.Authenticate(nil, [][]byte{signers[0], signers[0]}, [][]byte{validSignature, validSignature}) { - t.Error("Expected authentication to succeed because the rule allows duplicated signatures for signer[0]") + if !spe(toSignedData(msgs, [][]byte{signers[0], signers[0]}, [][]byte{validSignature, validSignature})) { + t.Errorf("Expected authentication to succeed because the rule allows duplicated signatures for signer[0]") } } @@ -102,7 +119,7 @@ func TestNegatively(t *testing.T) { b, _ := proto.Marshal(rpolicy) policy := &cb.SignaturePolicyEnvelope{} _ = proto.Unmarshal(b, policy) - _, err := NewSignaturePolicyEvaluator(policy, mch) + _, err := compile(policy.Policy, policy.Identities, mch) if err == nil { t.Fatal("Should have errored compiling because the Type field was nil") } diff --git a/common/cauthdsl/policy.go b/common/cauthdsl/policy.go new file mode 100644 index 00000000000..17573fda28a --- /dev/null +++ b/common/cauthdsl/policy.go @@ -0,0 +1,77 @@ +/* +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 cauthdsl + +import ( + "errors" + "fmt" + + "github.com/hyperledger/fabric/common/policies" + cb "github.com/hyperledger/fabric/protos/common" + + "github.com/golang/protobuf/proto" +) + +type provider struct { + helper CryptoHelper +} + +// NewProviderImpl provides a policy generator for cauthdsl type policies +func NewPolicyProvider(helper CryptoHelper) policies.Provider { + return &provider{ + helper: helper, + } +} + +// NewPolicy creates a new policy based on the policy bytes +func (pr *provider) NewPolicy(data []byte) (policies.Policy, error) { + sigPolicy := &cb.SignaturePolicyEnvelope{} + if err := proto.Unmarshal(data, sigPolicy); err != nil { + return nil, fmt.Errorf("Error unmarshaling to SignaturePolicy: %s", err) + } + + if sigPolicy.Version != 0 { + return nil, fmt.Errorf("This evaluator only understands messages of version 0, but version was %d", sigPolicy.Version) + } + + compiled, err := compile(sigPolicy.Policy, sigPolicy.Identities, pr.helper) + if err != nil { + return nil, err + } + + return &policy{ + evaluator: compiled, + }, nil + +} + +type policy struct { + evaluator func([]*cb.SignedData) bool +} + +// Evaluate takes a set of SignedData and evaluates whether this set of signatures satisfies the policy +func (p *policy) Evaluate(signatureSet []*cb.SignedData) error { + if p == nil { + return fmt.Errorf("No such policy") + } + + ok := p.evaluator(signatureSet) + if !ok { + return errors.New("Failed to authenticate policy") + } + return nil +} diff --git a/common/policies/policy_test.go b/common/cauthdsl/policy_test.go similarity index 78% rename from common/policies/policy_test.go rename to common/cauthdsl/policy_test.go index 0f6c43c39c8..508e067bf91 100644 --- a/common/policies/policy_test.go +++ b/common/cauthdsl/policy_test.go @@ -14,24 +14,18 @@ See the License for the specific language governing permissions and limitations under the License. */ -package policies +package cauthdsl import ( "fmt" "testing" - "github.com/hyperledger/fabric/common/cauthdsl" + "github.com/hyperledger/fabric/common/policies" cb "github.com/hyperledger/fabric/protos/common" "github.com/golang/protobuf/proto" ) -type mockCryptoHelper struct{} - -func (mch *mockCryptoHelper) VerifySignature(msg []byte, identity []byte, signature []byte) bool { - return true -} - var acceptAllPolicy []byte var rejectAllPolicy []byte @@ -52,9 +46,9 @@ func marshalOrPanic(msg proto.Message) []byte { func makePolicySource(policyResult bool) []byte { var policyData *cb.SignaturePolicyEnvelope if policyResult { - policyData = cauthdsl.AcceptAllPolicy + policyData = AcceptAllPolicy } else { - policyData = cauthdsl.RejectAllPolicy + policyData = RejectAllPolicy } marshaledPolicy := marshalOrPanic(&cb.Policy{ Type: int32(cb.Policy_SIGNATURE), @@ -63,7 +57,7 @@ func makePolicySource(policyResult bool) []byte { return marshaledPolicy } -func addPolicy(manager *ManagerImpl, id string, policy []byte) { +func addPolicy(manager *policies.ManagerImpl, id string, policy []byte) { manager.BeginConfig() err := manager.ProposeConfig(&cb.ConfigurationItem{ Type: cb.ConfigurationItem_Policy, @@ -76,16 +70,21 @@ func addPolicy(manager *ManagerImpl, id string, policy []byte) { manager.CommitConfig() } +func providerMap() map[int32]policies.Provider { + r := make(map[int32]policies.Provider) + r[int32(cb.Policy_SIGNATURE)] = NewPolicyProvider(&mockCryptoHelper{}) + return r +} + func TestAccept(t *testing.T) { policyID := "policyID" - m := NewManagerImpl(&mockCryptoHelper{}) - t.Logf("%p %x %v", acceptAllPolicy, acceptAllPolicy, acceptAllPolicy) + m := policies.NewManagerImpl(providerMap()) addPolicy(m, policyID, acceptAllPolicy) policy, ok := m.GetPolicy(policyID) if !ok { t.Error("Should have found policy which was just added, but did not") } - err := policy.Evaluate(nil, nil, nil, nil) + err := policy.Evaluate([]*cb.SignedData{}) if err != nil { t.Fatalf("Should not have errored evaluating an acceptAll policy: %s", err) } @@ -93,25 +92,25 @@ func TestAccept(t *testing.T) { func TestReject(t *testing.T) { policyID := "policyID" - m := NewManagerImpl(&mockCryptoHelper{}) + m := policies.NewManagerImpl(providerMap()) addPolicy(m, policyID, rejectAllPolicy) policy, ok := m.GetPolicy(policyID) if !ok { t.Error("Should have found policy which was just added, but did not") } - err := policy.Evaluate(nil, nil, nil, nil) + err := policy.Evaluate([]*cb.SignedData{}) if err == nil { t.Fatal("Should have errored evaluating the rejectAll policy") } } func TestRejectOnUnknown(t *testing.T) { - m := NewManagerImpl(&mockCryptoHelper{}) + m := policies.NewManagerImpl(providerMap()) policy, ok := m.GetPolicy("FakePolicyID") if ok { t.Error("Should not have found policy which was never added, but did") } - err := policy.Evaluate(nil, nil, nil, nil) + err := policy.Evaluate([]*cb.SignedData{}) if err == nil { t.Fatal("Should have errored evaluating the default policy") } diff --git a/common/configtx/manager.go b/common/configtx/manager.go index e18a4720ea9..e2c4dc17efb 100644 --- a/common/configtx/manager.go +++ b/common/configtx/manager.go @@ -59,7 +59,7 @@ const DefaultModificationPolicyID = "DefaultModificationPolicy" type acceptAllPolicy struct{} -func (ap *acceptAllPolicy) Evaluate(headers [][]byte, payload []byte, identities [][]byte, signatures [][]byte) error { +func (ap *acceptAllPolicy) Evaluate(signedData []*cb.SignedData) error { return nil } @@ -212,23 +212,14 @@ func (cm *configurationManager) processConfig(configtx *cb.ConfigurationEnvelope policy = defaultModificationPolicy } - headers := make([][]byte, len(entry.Signatures)) - signatures := make([][]byte, len(entry.Signatures)) - identities := make([][]byte, len(entry.Signatures)) - - for i, configSig := range entry.Signatures { - headers[i] = configSig.Signature - signatures[i] = configSig.SignatureHeader - sigHeader := &cb.SignatureHeader{} - err := proto.Unmarshal(configSig.SignatureHeader, sigHeader) - if err != nil { - return nil, err - } - identities[i] = sigHeader.Creator + // Get signatures + signedData, err := entry.AsSignedData() + if err != nil { + return nil, err } // Ensure the policy is satisfied - if err = policy.Evaluate(headers, entry.ConfigurationItem, identities, signatures); err != nil { + if err = policy.Evaluate(signedData); err != nil { return nil, err } diff --git a/common/configtx/manager_test.go b/common/configtx/manager_test.go index 820003a8ac8..26cc3afe218 100644 --- a/common/configtx/manager_test.go +++ b/common/configtx/manager_test.go @@ -42,7 +42,7 @@ type mockPolicy struct { policyResult error } -func (mp *mockPolicy) Evaluate(headers [][]byte, payload []byte, identities [][]byte, signatures [][]byte) error { +func (mp *mockPolicy) Evaluate(signedData []*cb.SignedData) error { if mp == nil { return errors.New("Invoked nil policy") } diff --git a/common/policies/policy.go b/common/policies/policy.go index 83d962e18af..21a8211fa1d 100644 --- a/common/policies/policy.go +++ b/common/policies/policy.go @@ -19,17 +19,15 @@ package policies import ( "fmt" - "github.com/hyperledger/fabric/common/cauthdsl" cb "github.com/hyperledger/fabric/protos/common" - "errors" "github.com/golang/protobuf/proto" ) // Policy is used to determine if a signature is valid type Policy interface { - // Evaluate returns nil if a digest is properly signed by sigs, or an error indicating why it failed - Evaluate(header [][]byte, payload []byte, identities [][]byte, signatures [][]byte) error + // Evaluate takes a set of SignedData and evaluates whether this set of signatures satisfies the policy + Evaluate(signatureSet []*cb.SignedData) error } // Manager is intended to be the primary accessor of ManagerImpl @@ -40,68 +38,41 @@ type Manager interface { GetPolicy(id string) (Policy, bool) } -type policy struct { - source *cb.Policy - evaluator *cauthdsl.SignaturePolicyEvaluator -} - -func newPolicy(policySource *cb.Policy, ch cauthdsl.CryptoHelper) (*policy, error) { - if policySource.Type != int32(cb.Policy_SIGNATURE) { - return nil, fmt.Errorf("Unknown policy type: %v", policySource.Type) - } - - sigPolicy := &cb.SignaturePolicyEnvelope{} - if err := proto.Unmarshal(policySource.Policy, sigPolicy); err != nil { - return nil, fmt.Errorf("Error unmarshaling to SignaturePolicy: %s", err) - } - - evaluator, err := cauthdsl.NewSignaturePolicyEvaluator(sigPolicy, ch) - if err != nil { - return nil, err - } - - return &policy{ - evaluator: evaluator, - source: policySource, - }, nil -} - -// Evaluate returns nil if a msg is properly signed by sigs, or an error indicating why it failed -// For each identity, it concatenates the corresponding header and the payload together, then -// verifies the corresponding signature. -func (p *policy) Evaluate(header [][]byte, payload []byte, identities [][]byte, signatures [][]byte) error { - if p == nil { - return errors.New("Evaluated default policy, results in reject") - } - - // XXX This is wrong, as the signatures are over the payload envelope, not the message, fix either here, or in cauthdsl once transaction is finalized - if !p.evaluator.Authenticate(payload, identities, signatures) { - return errors.New("Failed to authenticate policy") - } - return nil +// Provider provides the backing implementation of a policy +type Provider interface { + // NewPolicy creates a new policy based on the policy bytes + NewPolicy(data []byte) (Policy, error) } // ManagerImpl is an implementation of Manager and configtx.ConfigHandler // In general, it should only be referenced as an Impl for the configtx.ConfigManager type ManagerImpl struct { - policies map[string]*policy - pendingPolicies map[string]*policy - ch cauthdsl.CryptoHelper + providers map[int32]Provider + policies map[string]Policy + pendingPolicies map[string]Policy } // NewManagerImpl creates a new ManagerImpl with the given CryptoHelper -func NewManagerImpl(ch cauthdsl.CryptoHelper) *ManagerImpl { +func NewManagerImpl(providers map[int32]Provider) *ManagerImpl { return &ManagerImpl{ - ch: ch, - policies: make(map[string]*policy), + providers: providers, + policies: make(map[string]Policy), } } -// GetPolicy returns a policy and true if it was the policy requested, or false if it is the default policy +type rejectPolicy string + +func (rp rejectPolicy) Evaluate(signedData []*cb.SignedData) error { + return fmt.Errorf("No such policy type: %s", rp) +} + +// GetPolicy returns a policy and true if it was the policy requested, or false if it is the default reject policy func (pm *ManagerImpl) GetPolicy(id string) (Policy, bool) { policy, ok := pm.policies[id] - // Note the nil policy evaluates fine - return policy, ok + if !ok { + return rejectPolicy(id), false + } + return policy, true } // BeginConfig is used to start a new configuration proposal @@ -109,7 +80,7 @@ func (pm *ManagerImpl) BeginConfig() { if pm.pendingPolicies != nil { panic("Programming error, cannot call begin in the middle of a proposal") } - pm.pendingPolicies = make(map[string]*policy) + pm.pendingPolicies = make(map[string]Policy) } // RollbackConfig is used to abandon a new configuration proposal @@ -138,7 +109,12 @@ func (pm *ManagerImpl) ProposeConfig(configItem *cb.ConfigurationItem) error { return err } - cPolicy, err := newPolicy(policy, pm.ch) + provider, ok := pm.providers[int32(policy.Type)] + if !ok { + return fmt.Errorf("Unknown policy type: %v", policy.Type) + } + + cPolicy, err := provider.NewPolicy(policy.Policy) if err != nil { return err } diff --git a/orderer/multichain/manager.go b/orderer/multichain/manager.go index a40ac070ebc..fd90fbb6894 100644 --- a/orderer/multichain/manager.go +++ b/orderer/multichain/manager.go @@ -19,6 +19,7 @@ package multichain import ( "fmt" + "github.com/hyperledger/fabric/common/cauthdsl" "github.com/hyperledger/fabric/common/configtx" "github.com/hyperledger/fabric/common/policies" "github.com/hyperledger/fabric/orderer/common/sharedconfig" @@ -36,8 +37,8 @@ var logger = logging.MustGetLogger("orderer/multichain") // it considers all signatures to be valid type xxxCryptoHelper struct{} -func (xxx xxxCryptoHelper) VerifySignature(msg []byte, ids []byte, sigs []byte) bool { - return true +func (xxx xxxCryptoHelper) VerifySignature(sd *cb.SignedData) error { + return nil } func init() { @@ -163,7 +164,19 @@ func (ml *multiLedger) GetChain(chainID string) (ChainSupport, bool) { } func newConfigTxManagerAndHandlers(configEnvelope *cb.ConfigurationEnvelope) (configtx.Manager, policies.Manager, sharedconfig.Manager, error) { - policyManager := policies.NewManagerImpl(xxxCryptoHelper{}) + policyProviderMap := make(map[int32]policies.Provider) + for pType := range cb.Policy_PolicyType_name { + rtype := cb.Policy_PolicyType(pType) + switch rtype { + case cb.Policy_UNKNOWN: + // Do not register a handler + case cb.Policy_SIGNATURE: + policyProviderMap[pType] = cauthdsl.NewPolicyProvider(xxxCryptoHelper{}) + case cb.Policy_MSP: + // Add hook for MSP Handler here + } + } + policyManager := policies.NewManagerImpl(policyProviderMap) sharedConfigManager := sharedconfig.NewManagerImpl() configHandlerMap := make(map[cb.ConfigurationItem_ConfigurationType]configtx.Handler) for ctype := range cb.ConfigurationItem_ConfigurationType_name { diff --git a/orderer/multichain/systemchain_test.go b/orderer/multichain/systemchain_test.go index 29fa54ab60c..23ddf5ccf4e 100644 --- a/orderer/multichain/systemchain_test.go +++ b/orderer/multichain/systemchain_test.go @@ -35,7 +35,7 @@ type mockPolicy struct { err error } -func (mp *mockPolicy) Evaluate(header [][]byte, payload []byte, identities [][]byte, signatures [][]byte) error { +func (mp *mockPolicy) Evaluate(sd []*cb.SignedData) error { return mp.err } diff --git a/protos/common/configuration.proto b/protos/common/configuration.proto index a3ddfb58b93..8c994b25579 100644 --- a/protos/common/configuration.proto +++ b/protos/common/configuration.proto @@ -50,7 +50,7 @@ message ConfigurationEnvelope { // This message may change slightly depending on the finalization of signature schemes for transactions message SignedConfigurationItem { bytes ConfigurationItem = 1; - repeated ConfigurationSignature Signatures = 2; // Signatures over the hash of ConfigurationItem + repeated ConfigurationSignature Signatures = 2; } message ConfigurationItem { @@ -69,12 +69,10 @@ message ConfigurationItem { } message ConfigurationSignature { - bytes signatureHeader = 1; - bytes signature = 2; // Signature over the concatenation of configurationItem bytes and signatureHeader bytes + bytes signatureHeader = 1; // A marshaled SignatureHeader + bytes signature = 2; // Signature over the concatenation of configurationItem bytes and signatureHeader bytes } -// - // Policy expresses a policy which the orderer can evaluate, because there has been some desire expressed to support // multiple policy engines, this is typed as a oneof for now message Policy { diff --git a/protos/common/signed_data.go b/protos/common/signed_data.go new file mode 100644 index 00000000000..f7f87f8f577 --- /dev/null +++ b/protos/common/signed_data.go @@ -0,0 +1,87 @@ +/* +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 common + +import ( + "fmt" + + "github.com/golang/protobuf/proto" +) + +// SignedData is used to represent the general triplet required to verify a signature +// This is intended to be generic across crypto schemes, while most crypto schemes will +// include the signing identity and a nonce within the Data, this is left to the crypto +// implementation +type SignedData struct { + Data []byte + Identity []byte + Signature []byte +} + +// Signable types are those which can map their contents to a set of SignedData +type Signable interface { + // AsSignedData returns the set of signatures for a structure as SignedData or an error indicating why this was not possible + AsSignedData() ([]*SignedData, error) +} + +// AsSignedData returns the set of signatures for the SignedCOnfigurationItem as SignedData or an error indicating why this was not possible +func (sci *SignedConfigurationItem) AsSignedData() ([]*SignedData, error) { + if sci == nil { + return nil, fmt.Errorf("No signatures for nil SignedConfigurationItem") + } + + result := make([]*SignedData, len(sci.Signatures)) + for i, configSig := range sci.Signatures { + sigHeader := &SignatureHeader{} + err := proto.Unmarshal(configSig.SignatureHeader, sigHeader) + if err != nil { + return nil, err + } + + result[i] = &SignedData{ + Data: append(sci.ConfigurationItem, configSig.SignatureHeader...), + Identity: sigHeader.Creator, + Signature: configSig.Signature, + } + + } + + return result, nil +} + +// AsSignedData returns the signatures for the Envelope as SignedData slice of length 1 or an error indicating why this was not possible +func (env *Envelope) AsSignedData() ([]*SignedData, error) { + if env == nil { + return nil, fmt.Errorf("No signatures for nil Envelope") + } + + payload := &Payload{} + err := proto.Unmarshal(env.Payload, payload) + if err != nil { + return nil, err + } + + if payload.Header == nil || payload.Header.SignatureHeader == nil { + return nil, fmt.Errorf("Missing Header or SignatureHeader") + } + + return []*SignedData{&SignedData{ + Data: env.Payload, + Identity: payload.Header.SignatureHeader.Creator, + Signature: env.Signature, + }}, nil +} diff --git a/protos/common/signed_data_test.go b/protos/common/signed_data_test.go new file mode 100644 index 00000000000..a6945ee91a1 --- /dev/null +++ b/protos/common/signed_data_test.go @@ -0,0 +1,121 @@ +/* +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 common + +import ( + "bytes" + "testing" + + "github.com/golang/protobuf/proto" +) + +// More duplicate utility which should go away, but the utils are a bit of a mess right now with import cycles +func marshalOrPanic(msg proto.Message) []byte { + data, err := proto.Marshal(msg) + if err != nil { + panic("Error marshaling") + } + return data +} + +func TestNilSignedConfigurationItemAsSignedData(t *testing.T) { + var sci *SignedConfigurationItem + _, err := sci.AsSignedData() + if err == nil { + t.Fatalf("Should have errored trying to convert a nil signed configuration item to signed data") + } +} + +func TestSignedConfigurationItemAsSignedData(t *testing.T) { + configItemBytes := []byte("Foo") + signatures := [][]byte{[]byte("Signature1"), []byte("Signature2")} + identities := [][]byte{[]byte("Identity1"), []byte("Identity2")} + + configSignatures := make([]*ConfigurationSignature, len(signatures)) + for i := range configSignatures { + configSignatures[i] = &ConfigurationSignature{ + SignatureHeader: marshalOrPanic(&SignatureHeader{ + Creator: identities[i], + }), + Signature: signatures[i], + } + } + + sci := &SignedConfigurationItem{ + ConfigurationItem: configItemBytes, + Signatures: configSignatures, + } + + signedData, err := sci.AsSignedData() + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + for i, sigData := range signedData { + if !bytes.Equal(sigData.Identity, identities[i]) { + t.Errorf("Expected identity to match at index %d", i) + } + if !bytes.Equal(sigData.Data, append(configItemBytes, configSignatures[i].SignatureHeader...)) { + t.Errorf("Expected signature over concatenation of config item bytes and signature header") + } + if !bytes.Equal(sigData.Signature, signatures[i]) { + t.Errorf("Expected signature to match at index %d", i) + } + } +} + +func TestNilEnvelopeAsSignedData(t *testing.T) { + var env *Envelope + _, err := env.AsSignedData() + if err == nil { + t.Fatalf("Should have errored trying to convert a nil envelope") + } +} + +func TestEnvelopeAsSignedData(t *testing.T) { + identity := []byte("Foo") + signature := []byte("Bar") + env := &Envelope{ + Payload: marshalOrPanic(&Payload{ + Header: &Header{ + SignatureHeader: &SignatureHeader{ + Creator: identity, + }, + }, + }), + Signature: signature, + } + + signedData, err := env.AsSignedData() + if err != nil { + t.Fatalf("Unexpected error converting envelope to SignedData: %s", err) + } + + if len(signedData) != 1 { + t.Fatalf("Expected 1 entry of signed data, but got %d", len(signedData)) + } + + if !bytes.Equal(signedData[0].Identity, identity) { + t.Errorf("Wrong identity bytes") + } + if !bytes.Equal(signedData[0].Data, env.Payload) { + t.Errorf("Wrong data bytes") + } + if !bytes.Equal(signedData[0].Signature, signature) { + t.Errorf("Wrong data bytes") + } +}