Skip to content

Commit

Permalink
Integration of MSP into cauthdsl
Browse files Browse the repository at this point in the history
This change set introduces a more flexible way of describing the identity
associated to a policy. So far we had support for serializing the
certificate associated to the identity. We introduce a new structure that
supports that and 3 other ways of listing identities: i) the admin of an
MSP, ii) the CA of an MSP and iii) a valid certificate for an MSP.

Furthermore, policy evaluation is now performed using the MSP infrastructure:
cauthdsl receives a policy principal and an Identity instance and then it can
use the interfaces offered by the MSP to check whether the identity satisfies
the principal and whether the signature verifies. The semantics of policy
verification has somewhat changed: an identity (and its signature) can be used
to satisfy only a single principal. This has the benefit of better dealing
with the policy "two signatures from org0", but it has the downside that a
single identity can no longer be used to satisfy two principals (e.g. if
we need signatures from an identity with attribute A and one with attribute
B, a single signature from an identity with both attributes would not be
sufficient).

Change-Id: Id18a5933e341781334080965b5d04dc07d4f1b99
Signed-off-by: Alessandro Sorniotti <ale.linux@sopit.net>
  • Loading branch information
ale-linux committed Jan 12, 2017
1 parent bbe8e8c commit bf2fd1d
Show file tree
Hide file tree
Showing 14 changed files with 314 additions and 136 deletions.
127 changes: 113 additions & 14 deletions common/cauthdsl/cauthdsl.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,53 +17,152 @@ limitations under the License.
package cauthdsl

import (
"bytes"
"fmt"

"bytes"

"github.com/hyperledger/fabric/msp"
cb "github.com/hyperledger/fabric/protos/common"
"github.com/op/go-logging"
"github.com/syndtr/goleveldb/leveldb/errors"
)

// CryptoHelper is used to provide a plugin point for different signature validation types
type CryptoHelper interface {
VerifySignature(signedData *cb.SignedData) error
}
var cauthdslLogger = logging.MustGetLogger("cauthdsl")

// compile recursively builds a go evaluatable function corresponding to the policy specified
func compile(policy *cb.SignaturePolicy, identities [][]byte, ch CryptoHelper) (func([]*cb.SignedData) bool, error) {
func compile(policy *cb.SignaturePolicy, identities []*cb.MSPPrincipal, deserializer msp.Common) (func([]*cb.SignedData, []bool) bool, error) {
switch t := policy.Type.(type) {
case *cb.SignaturePolicy_From:
policies := make([]func([]*cb.SignedData) bool, len(t.From.Policies))
policies := make([]func([]*cb.SignedData, []bool) bool, len(t.From.Policies))
for i, policy := range t.From.Policies {
compiledPolicy, err := compile(policy, identities, ch)
compiledPolicy, err := compile(policy, identities, deserializer)
if err != nil {
return nil, err
}
policies[i] = compiledPolicy

}
return func(signedData []*cb.SignedData) bool {
return func(signedData []*cb.SignedData, used []bool) bool {
cauthdslLogger.Debug("Gate evaluation starts: (%s)", t)
verified := int32(0)
_used := make([]bool, len(used))
for _, policy := range policies {
if policy(signedData) {
copy(_used, used)
if policy(signedData, _used) {
verified++
copy(used, _used)
}
}

if verified >= t.From.N {
cauthdslLogger.Debug("Gate evaluation succeeds: (%s)", t)
} else {
cauthdslLogger.Debug("Gate evaluation fails: (%s)", t)
}

return verified >= t.From.N
}, nil
case *cb.SignaturePolicy_SignedBy:
if t.SignedBy < 0 || t.SignedBy >= int32(len(identities)) {
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(signedData []*cb.SignedData) bool {
for _, sd := range signedData {
if bytes.Equal(sd.Identity, signedByID) {
return ch.VerifySignature(sd) == nil
return func(signedData []*cb.SignedData, used []bool) bool {
cauthdslLogger.Debug("Principal evaluation starts: (%s) (used %s)", t, used)
for i, sd := range signedData {
if used[i] {
continue
}
// FIXME: what should I do with the error below?
identity, _ := deserializer.DeserializeIdentity(sd.Identity)
err := identity.SatisfiesPrincipal(signedByID)
if err == nil {
err := identity.Verify(sd.Data, sd.Signature)
if err == nil {
cauthdslLogger.Debug("Principal evaluation succeeds: (%s)", t, used)
used[i] = true
return true
}
}
}
cauthdslLogger.Debug("Principal evaluation fails: (%s)", t, used)
return false
}, nil
default:
return nil, fmt.Errorf("Unknown type: %T:%v", t, t)
}
}

// FIXME: remove the code below as soon as we can use MSP from the policy manager code
var invalidSignature = []byte("badsigned")

type mockIdentity struct {
idBytes []byte
}

func (id *mockIdentity) SatisfiesPrincipal(p *cb.MSPPrincipal) error {
if bytes.Compare(id.idBytes, p.Principal) == 0 {
return nil
} else {
return errors.New("Principals do not match")
}
}

func (id *mockIdentity) GetIdentifier() *msp.IdentityIdentifier {
return &msp.IdentityIdentifier{Mspid: "Mock", Id: "Bob"}
}

func (id *mockIdentity) GetMSPIdentifier() string {
return "Mock"
}

func (id *mockIdentity) Validate() error {
return nil
}

func (id *mockIdentity) GetOrganizationUnits() string {
return "dunno"
}

func (id *mockIdentity) Verify(msg []byte, sig []byte) error {
if bytes.Compare(sig, invalidSignature) == 0 {
return errors.New("Invalid signature")
} else {
return nil
}
}

func (id *mockIdentity) VerifyOpts(msg []byte, sig []byte, opts msp.SignatureOpts) error {
return nil
}

func (id *mockIdentity) VerifyAttributes(proof [][]byte, spec *msp.AttributeProofSpec) error {
return nil
}

func (id *mockIdentity) Serialize() ([]byte, error) {
return id.idBytes, nil
}

func toSignedData(data [][]byte, identities [][]byte, signatures [][]byte) ([]*cb.SignedData, []bool) {
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, make([]bool, len(signedData))
}

type mockDeserializer struct {
}

func NewMockDeserializer() msp.Common {
return &mockDeserializer{}
}

func (md *mockDeserializer) DeserializeIdentity(serializedIdentity []byte) (msp.Identity, error) {
return &mockIdentity{idBytes: serializedIdentity}, nil
}
7 changes: 6 additions & 1 deletion common/cauthdsl/cauthdsl_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,15 @@ func init() {

// Envelope builds an envelope message embedding a SignaturePolicy
func Envelope(policy *cb.SignaturePolicy, identities [][]byte) *cb.SignaturePolicyEnvelope {
ids := make([]*cb.MSPPrincipal, len(identities))
for i, _ := range ids {
ids[i] = &cb.MSPPrincipal{PrincipalClassification: cb.MSPPrincipal_ByIdentity, Principal: identities[i]}
}

return &cb.SignaturePolicyEnvelope{
Version: 0,
Policy: policy,
Identities: identities,
Identities: ids,
}
}

Expand Down
62 changes: 20 additions & 42 deletions common/cauthdsl/cauthdsl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,66 +17,40 @@ limitations under the License.
package cauthdsl

import (
"bytes"
"fmt"
"testing"

"github.com/golang/protobuf/proto"
cb "github.com/hyperledger/fabric/protos/common"
)

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(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
}
var moreMsgs = [][]byte{nil, nil, nil}

func TestSimpleSignature(t *testing.T) {
mch := &mockCryptoHelper{}
policy := Envelope(SignedBy(0), signers)

spe, err := compile(policy.Policy, policy.Identities, mch)
spe, err := compile(policy.Policy, policy.Identities, &mockDeserializer{})
if err != nil {
t.Fatalf("Could not create a new SignaturePolicyEvaluator using the given policy, crypto-helper: %s", err)
}

if !spe([]*cb.SignedData{&cb.SignedData{Identity: signers[0], Signature: validSignature}}) {
t.Errorf("Expected authentication to succeed with valid signatures")
if !spe(toSignedData([][]byte{nil}, [][]byte{signers[0]}, [][]byte{validSignature})) {
t.Errorf("Expected authentication to succeed with valid signatures")
}
if spe([]*cb.SignedData{&cb.SignedData{Identity: signers[0], Signature: invalidSignature}}) {
if spe(toSignedData([][]byte{nil}, [][]byte{signers[0]}, [][]byte{invalidSignature})) {
t.Errorf("Expected authentication to fail given the invalid signature")
}
if spe([]*cb.SignedData{&cb.SignedData{Identity: signers[1], Signature: validSignature}}) {
if spe(toSignedData([][]byte{nil}, [][]byte{signers[1]}, [][]byte{validSignature})) {
t.Errorf("Expected authentication to fail because signers[1] is not authorized in the policy, despite his valid signature")
}
}

func TestMultipleSignature(t *testing.T) {
mch := &mockCryptoHelper{}
policy := Envelope(And(SignedBy(0), SignedBy(1)), signers)

spe, err := compile(policy.Policy, policy.Identities, mch)
spe, err := compile(policy.Policy, policy.Identities, &mockDeserializer{})
if err != nil {
t.Fatalf("Could not create a new SignaturePolicyEvaluator using the given policy, crypto-helper: %s", err)
}
Expand All @@ -93,33 +67,37 @@ func TestMultipleSignature(t *testing.T) {
}

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 := compile(policy.Policy, policy.Identities, mch)
spe, err := compile(policy.Policy, policy.Identities, &mockDeserializer{})
if err != nil {
t.Fatalf("Could not create a new SignaturePolicyEvaluator using the given policy, crypto-helper: %s", err)
}

if !spe(toSignedData(msgs, signers, [][]byte{validSignature, validSignature})) {
if !spe(toSignedData(moreMsgs, append(signers, [][]byte{[]byte("signer0")}...), [][]byte{validSignature, validSignature, validSignature})) {
t.Errorf("Expected authentication to succeed with valid signatures")
}
if spe(toSignedData(msgs, signers, [][]byte{invalidSignature, validSignature})) {
t.Errorf("Expected authentication failure as only the signature of signer[1] was valid")
if !spe(toSignedData(moreMsgs, [][]byte{[]byte("signer0"), []byte("signer0"), []byte("signer0")}, [][]byte{validSignature, validSignature, validSignature})) {
t.Errorf("Expected authentication to succeed with valid signatures")
}
if spe(toSignedData(msgs, signers, [][]byte{validSignature, validSignature})) {
t.Errorf("Expected authentication to fail with too few signatures")
}
if spe(toSignedData(moreMsgs, append(signers, [][]byte{[]byte("signer0")}...), [][]byte{validSignature, invalidSignature, validSignature})) {
t.Errorf("Expected authentication failure as the signature of signer[1] was invalid")
}
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]")
if spe(toSignedData(moreMsgs, append(signers, [][]byte{[]byte("signer1")}...), [][]byte{validSignature, validSignature, validSignature})) {
t.Errorf("Expected authentication failure as there was a signature from signer[0] missing")
}
}

func TestNegatively(t *testing.T) {
mch := &mockCryptoHelper{}
rpolicy := Envelope(And(SignedBy(0), SignedBy(1)), signers)
rpolicy.Policy.Type = nil
b, _ := proto.Marshal(rpolicy)
policy := &cb.SignaturePolicyEnvelope{}
_ = proto.Unmarshal(b, policy)
_, err := compile(policy.Policy, policy.Identities, mch)
_, err := compile(policy.Policy, policy.Identities, &mockDeserializer{})
if err == nil {
t.Fatal("Should have errored compiling because the Type field was nil")
}
Expand Down
13 changes: 7 additions & 6 deletions common/cauthdsl/policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,17 @@ import (
cb "github.com/hyperledger/fabric/protos/common"

"github.com/golang/protobuf/proto"
"github.com/hyperledger/fabric/msp"
)

type provider struct {
helper CryptoHelper
deserializer msp.Common
}

// NewProviderImpl provides a policy generator for cauthdsl type policies
func NewPolicyProvider(helper CryptoHelper) policies.Provider {
func NewPolicyProvider(deserializer msp.Common) policies.Provider {
return &provider{
helper: helper,
deserializer: deserializer,
}
}

Expand All @@ -48,7 +49,7 @@ func (pr *provider) NewPolicy(data []byte) (policies.Policy, error) {
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)
compiled, err := compile(sigPolicy.Policy, sigPolicy.Identities, pr.deserializer)
if err != nil {
return nil, err
}
Expand All @@ -60,7 +61,7 @@ func (pr *provider) NewPolicy(data []byte) (policies.Policy, error) {
}

type policy struct {
evaluator func([]*cb.SignedData) bool
evaluator func([]*cb.SignedData, []bool) bool
}

// Evaluate takes a set of SignedData and evaluates whether this set of signatures satisfies the policy
Expand All @@ -69,7 +70,7 @@ func (p *policy) Evaluate(signatureSet []*cb.SignedData) error {
return fmt.Errorf("No such policy")
}

ok := p.evaluator(signatureSet)
ok := p.evaluator(signatureSet, make([]bool, len(signatureSet)))
if !ok {
return errors.New("Failed to authenticate policy")
}
Expand Down
2 changes: 1 addition & 1 deletion common/cauthdsl/policy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ func addPolicy(manager *policies.ManagerImpl, id string, policy []byte) {

func providerMap() map[int32]policies.Provider {
r := make(map[int32]policies.Provider)
r[int32(cb.Policy_SIGNATURE)] = NewPolicyProvider(&mockCryptoHelper{})
r[int32(cb.Policy_SIGNATURE)] = NewPolicyProvider(&mockDeserializer{})
return r
}

Expand Down
6 changes: 6 additions & 0 deletions msp/identities.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"github.com/golang/protobuf/proto"
"github.com/hyperledger/fabric/bccsp"
"github.com/hyperledger/fabric/bccsp/signer"
"github.com/hyperledger/fabric/protos/common"
)

type identity struct {
Expand All @@ -49,6 +50,11 @@ func newIdentity(id *IdentityIdentifier, cert *x509.Certificate, pk bccsp.Key, m
return &identity{id: id, cert: cert, pk: pk, msp: msp}
}

// SatisfiesPrincipal returns null if this instance matches the supplied principal or an error otherwise
func (id *identity) SatisfiesPrincipal(principal *common.MSPPrincipal) error {
return id.msp.SatisfiesPrincipal(id, principal)
}

// GetIdentifier returns the identifier (MSPID/IDID) for this instance
func (id *identity) GetIdentifier() *IdentityIdentifier {
return id.id
Expand Down
Loading

0 comments on commit bf2fd1d

Please sign in to comment.