From f1a3675b6712d37d48ee5ee4ed0bb31f4748485d Mon Sep 17 00:00:00 2001 From: Jason Yellick Date: Tue, 4 Oct 2016 17:08:20 -0400 Subject: [PATCH] Add trivial signature validation dsl via protobuf This changeset attempts to make a first pass at having a generic enough to be useful, but not so generic as to be impossible to understand domain specific language (via protobuf) to express cryptographic validation schemes. In particular, the two primitives which comprise a policy are: NOutOf(n, []policies) SignedBy(id) Please note that this DSL is relying on the structure imposed by protobuf, and therefore defines the entire grammar of the DSL in 10 lines. There is an additional envelope message to allow the specification to be versioned. This was developed especially for aiding in specifying bootstrap configuration for signature policies, however, its applicability for other areas such as endorsement seems likely. https://jira.hyperledger.org/browse/FAB-704 Change-Id: I330b0660caf90b09034e5a1c167c08a5c2078e8f Signed-off-by: Jason Yellick --- orderer/atomicbroadcast/ab.pb.go | 258 ++++++++++++++++++++++----- orderer/atomicbroadcast/ab.proto | 27 ++- orderer/cauthdsl/cauthdsl.go | 96 ++++++++++ orderer/cauthdsl/cauthdsl_builder.go | 61 +++++++ orderer/cauthdsl/cauthdsl_test.go | 109 +++++++++++ 5 files changed, 507 insertions(+), 44 deletions(-) create mode 100644 orderer/cauthdsl/cauthdsl.go create mode 100644 orderer/cauthdsl/cauthdsl_builder.go create mode 100644 orderer/cauthdsl/cauthdsl_test.go diff --git a/orderer/atomicbroadcast/ab.pb.go b/orderer/atomicbroadcast/ab.pb.go index 29df299ddc5..c14806cc1e8 100644 --- a/orderer/atomicbroadcast/ab.pb.go +++ b/orderer/atomicbroadcast/ab.pb.go @@ -11,6 +11,8 @@ It is generated from these files: It has these top-level messages: BroadcastResponse BroadcastMessage + SignaturePolicyEnvelope + SignaturePolicy SeekInfo Acknowledgement DeliverUpdate @@ -96,7 +98,7 @@ var SeekInfo_StartType_value = map[string]int32{ func (x SeekInfo_StartType) String() string { return proto.EnumName(SeekInfo_StartType_name, int32(x)) } -func (SeekInfo_StartType) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{2, 0} } +func (SeekInfo_StartType) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{4, 0} } type BroadcastResponse struct { Status Status `protobuf:"varint,1,opt,name=Status,json=status,enum=atomicbroadcast.Status" json:"Status,omitempty"` @@ -116,6 +118,164 @@ func (m *BroadcastMessage) String() string { return proto.CompactText func (*BroadcastMessage) ProtoMessage() {} func (*BroadcastMessage) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } +// SignaturePolicyEnvelope wraps a SignaturePolicy and includes a version for future enhancements +type SignaturePolicyEnvelope struct { + Version int32 `protobuf:"varint,1,opt,name=Version,json=version" json:"Version,omitempty"` + Policy *SignaturePolicy `protobuf:"bytes,2,opt,name=Policy,json=policy" json:"Policy,omitempty"` + Identities [][]byte `protobuf:"bytes,3,rep,name=Identities,json=identities,proto3" json:"Identities,omitempty"` +} + +func (m *SignaturePolicyEnvelope) Reset() { *m = SignaturePolicyEnvelope{} } +func (m *SignaturePolicyEnvelope) String() string { return proto.CompactTextString(m) } +func (*SignaturePolicyEnvelope) ProtoMessage() {} +func (*SignaturePolicyEnvelope) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } + +func (m *SignaturePolicyEnvelope) GetPolicy() *SignaturePolicy { + if m != nil { + return m.Policy + } + return nil +} + +// SignaturePolicy is a recursive message structure which defines a featherweight DSL for describing +// policies which are more complicated than 'exactly this signature'. The NOutOf operator is sufficent +// to express AND as well as OR, as well as of course N out of the following M policies +// SignedBy implies that the signature is from a valid certificate which is signed by the trusted +// authority specified in the bytes. This will be the certificate itself for a self-signed certificate +// and will be the CA for more traditional certificates +type SignaturePolicy struct { + // Types that are valid to be assigned to Type: + // *SignaturePolicy_SignedBy + // *SignaturePolicy_From + Type isSignaturePolicy_Type `protobuf_oneof:"Type"` +} + +func (m *SignaturePolicy) Reset() { *m = SignaturePolicy{} } +func (m *SignaturePolicy) String() string { return proto.CompactTextString(m) } +func (*SignaturePolicy) ProtoMessage() {} +func (*SignaturePolicy) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } + +type isSignaturePolicy_Type interface { + isSignaturePolicy_Type() +} + +type SignaturePolicy_SignedBy struct { + SignedBy int32 `protobuf:"varint,1,opt,name=SignedBy,json=signedBy,oneof"` +} +type SignaturePolicy_From struct { + From *SignaturePolicy_NOutOf `protobuf:"bytes,2,opt,name=From,json=from,oneof"` +} + +func (*SignaturePolicy_SignedBy) isSignaturePolicy_Type() {} +func (*SignaturePolicy_From) isSignaturePolicy_Type() {} + +func (m *SignaturePolicy) GetType() isSignaturePolicy_Type { + if m != nil { + return m.Type + } + return nil +} + +func (m *SignaturePolicy) GetSignedBy() int32 { + if x, ok := m.GetType().(*SignaturePolicy_SignedBy); ok { + return x.SignedBy + } + return 0 +} + +func (m *SignaturePolicy) GetFrom() *SignaturePolicy_NOutOf { + if x, ok := m.GetType().(*SignaturePolicy_From); ok { + return x.From + } + return nil +} + +// XXX_OneofFuncs is for the internal use of the proto package. +func (*SignaturePolicy) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) { + return _SignaturePolicy_OneofMarshaler, _SignaturePolicy_OneofUnmarshaler, _SignaturePolicy_OneofSizer, []interface{}{ + (*SignaturePolicy_SignedBy)(nil), + (*SignaturePolicy_From)(nil), + } +} + +func _SignaturePolicy_OneofMarshaler(msg proto.Message, b *proto.Buffer) error { + m := msg.(*SignaturePolicy) + // Type + switch x := m.Type.(type) { + case *SignaturePolicy_SignedBy: + b.EncodeVarint(1<<3 | proto.WireVarint) + b.EncodeVarint(uint64(x.SignedBy)) + case *SignaturePolicy_From: + b.EncodeVarint(2<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.From); err != nil { + return err + } + case nil: + default: + return fmt.Errorf("SignaturePolicy.Type has unexpected type %T", x) + } + return nil +} + +func _SignaturePolicy_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) { + m := msg.(*SignaturePolicy) + switch tag { + case 1: // Type.SignedBy + if wire != proto.WireVarint { + return true, proto.ErrInternalBadWireType + } + x, err := b.DecodeVarint() + m.Type = &SignaturePolicy_SignedBy{int32(x)} + return true, err + case 2: // Type.From + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(SignaturePolicy_NOutOf) + err := b.DecodeMessage(msg) + m.Type = &SignaturePolicy_From{msg} + return true, err + default: + return false, nil + } +} + +func _SignaturePolicy_OneofSizer(msg proto.Message) (n int) { + m := msg.(*SignaturePolicy) + // Type + switch x := m.Type.(type) { + case *SignaturePolicy_SignedBy: + n += proto.SizeVarint(1<<3 | proto.WireVarint) + n += proto.SizeVarint(uint64(x.SignedBy)) + case *SignaturePolicy_From: + s := proto.Size(x.From) + n += proto.SizeVarint(2<<3 | proto.WireBytes) + n += proto.SizeVarint(uint64(s)) + n += s + case nil: + default: + panic(fmt.Sprintf("proto: unexpected type %T in oneof", x)) + } + return n +} + +type SignaturePolicy_NOutOf struct { + N int32 `protobuf:"varint,1,opt,name=N,json=n" json:"N,omitempty"` + Policies []*SignaturePolicy `protobuf:"bytes,2,rep,name=Policies,json=policies" json:"Policies,omitempty"` +} + +func (m *SignaturePolicy_NOutOf) Reset() { *m = SignaturePolicy_NOutOf{} } +func (m *SignaturePolicy_NOutOf) String() string { return proto.CompactTextString(m) } +func (*SignaturePolicy_NOutOf) ProtoMessage() {} +func (*SignaturePolicy_NOutOf) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3, 0} } + +func (m *SignaturePolicy_NOutOf) GetPolicies() []*SignaturePolicy { + if m != nil { + return m.Policies + } + return nil +} + type SeekInfo struct { Start SeekInfo_StartType `protobuf:"varint,1,opt,name=Start,json=start,enum=atomicbroadcast.SeekInfo_StartType" json:"Start,omitempty"` SpecifiedNumber uint64 `protobuf:"varint,2,opt,name=SpecifiedNumber,json=specifiedNumber" json:"SpecifiedNumber,omitempty"` @@ -125,7 +285,7 @@ type SeekInfo struct { func (m *SeekInfo) Reset() { *m = SeekInfo{} } func (m *SeekInfo) String() string { return proto.CompactTextString(m) } func (*SeekInfo) ProtoMessage() {} -func (*SeekInfo) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } +func (*SeekInfo) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} } type Acknowledgement struct { Number uint64 `protobuf:"varint,1,opt,name=Number,json=number" json:"Number,omitempty"` @@ -134,7 +294,7 @@ type Acknowledgement struct { func (m *Acknowledgement) Reset() { *m = Acknowledgement{} } func (m *Acknowledgement) String() string { return proto.CompactTextString(m) } func (*Acknowledgement) ProtoMessage() {} -func (*Acknowledgement) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } +func (*Acknowledgement) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} } // The update message either causes a seek to a new stream start with a new window, or acknowledges a received block and advances the base of the window type DeliverUpdate struct { @@ -147,7 +307,7 @@ type DeliverUpdate struct { func (m *DeliverUpdate) Reset() { *m = DeliverUpdate{} } func (m *DeliverUpdate) String() string { return proto.CompactTextString(m) } func (*DeliverUpdate) ProtoMessage() {} -func (*DeliverUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} } +func (*DeliverUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} } type isDeliverUpdate_Type interface { isDeliverUpdate_Type() @@ -272,7 +432,7 @@ type Block struct { func (m *Block) Reset() { *m = Block{} } func (m *Block) String() string { return proto.CompactTextString(m) } func (*Block) ProtoMessage() {} -func (*Block) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} } +func (*Block) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} } func (m *Block) GetMessages() []*BroadcastMessage { if m != nil { @@ -291,7 +451,7 @@ type DeliverResponse struct { func (m *DeliverResponse) Reset() { *m = DeliverResponse{} } func (m *DeliverResponse) String() string { return proto.CompactTextString(m) } func (*DeliverResponse) ProtoMessage() {} -func (*DeliverResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} } +func (*DeliverResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{8} } type isDeliverResponse_Type interface { isDeliverResponse_Type() @@ -400,6 +560,9 @@ func _DeliverResponse_OneofSizer(msg proto.Message) (n int) { func init() { proto.RegisterType((*BroadcastResponse)(nil), "atomicbroadcast.BroadcastResponse") proto.RegisterType((*BroadcastMessage)(nil), "atomicbroadcast.BroadcastMessage") + proto.RegisterType((*SignaturePolicyEnvelope)(nil), "atomicbroadcast.SignaturePolicyEnvelope") + proto.RegisterType((*SignaturePolicy)(nil), "atomicbroadcast.SignaturePolicy") + proto.RegisterType((*SignaturePolicy_NOutOf)(nil), "atomicbroadcast.SignaturePolicy.NOutOf") proto.RegisterType((*SeekInfo)(nil), "atomicbroadcast.SeekInfo") proto.RegisterType((*Acknowledgement)(nil), "atomicbroadcast.Acknowledgement") proto.RegisterType((*DeliverUpdate)(nil), "atomicbroadcast.DeliverUpdate") @@ -589,41 +752,50 @@ var _AtomicBroadcast_serviceDesc = grpc.ServiceDesc{ func init() { proto.RegisterFile("ab.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ - // 576 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x84, 0x54, 0x4d, 0x6f, 0xda, 0x40, - 0x10, 0x8d, 0x83, 0xed, 0x38, 0x43, 0x12, 0xbb, 0xdb, 0x2a, 0x75, 0x73, 0x88, 0xa8, 0x2b, 0x55, - 0x69, 0x0f, 0xa4, 0xa2, 0xa7, 0x1e, 0x7a, 0xc0, 0xd8, 0x28, 0x96, 0xa8, 0x21, 0x36, 0x24, 0xbd, - 0x21, 0x03, 0x4b, 0x6a, 0x01, 0x5e, 0xcb, 0xeb, 0x04, 0x35, 0xbf, 0x22, 0x52, 0xab, 0xfe, 0x9c, - 0x5e, 0xfa, 0xa3, 0x7a, 0xed, 0x7a, 0x63, 0x28, 0x60, 0xa1, 0x9c, 0x98, 0xf1, 0xbc, 0x37, 0x1f, - 0x6f, 0x9f, 0x00, 0x25, 0x18, 0x54, 0xe3, 0x84, 0xa4, 0x04, 0xa9, 0x41, 0x4a, 0x66, 0xe1, 0x70, - 0x90, 0x90, 0x60, 0x34, 0x0c, 0x68, 0x6a, 0x58, 0xf0, 0xcc, 0x5c, 0x24, 0x1e, 0xa6, 0x31, 0x89, - 0x28, 0x46, 0xe7, 0x20, 0xfb, 0x69, 0x90, 0xde, 0x52, 0x5d, 0xa8, 0x08, 0x67, 0x47, 0xb5, 0x97, - 0xd5, 0x0d, 0x5a, 0xf5, 0xb1, 0xec, 0xc9, 0x94, 0xff, 0x1a, 0x6f, 0x41, 0x5b, 0x76, 0xf9, 0x82, - 0x29, 0x0d, 0x6e, 0x30, 0x42, 0x20, 0x5a, 0x41, 0x1a, 0xf0, 0x16, 0x07, 0x9e, 0x38, 0x62, 0xb1, - 0xf1, 0x47, 0x00, 0xc5, 0xc7, 0x78, 0xe2, 0x44, 0x63, 0x82, 0x3e, 0x81, 0xc4, 0xda, 0x24, 0x69, - 0x3e, 0xe4, 0x4d, 0x71, 0x48, 0x8e, 0xac, 0x72, 0x58, 0xf7, 0x7b, 0x8c, 0x3d, 0x89, 0x66, 0x21, - 0x3a, 0x03, 0xd5, 0x8f, 0xf1, 0x30, 0x1c, 0x87, 0x78, 0xe4, 0xde, 0xce, 0x06, 0x38, 0xd1, 0x77, - 0x59, 0x13, 0xd1, 0x53, 0xe9, 0xfa, 0x67, 0x74, 0x0a, 0x70, 0x1d, 0x46, 0x23, 0x32, 0xf7, 0xc3, - 0x7b, 0xac, 0x97, 0x38, 0x08, 0xe6, 0xcb, 0x2f, 0x46, 0x0d, 0xf6, 0x97, 0xdd, 0x11, 0x80, 0xec, - 0xda, 0xd7, 0xb6, 0xdf, 0xd5, 0x76, 0xb2, 0xb8, 0xdd, 0xb2, 0xb2, 0x58, 0x40, 0x87, 0x0c, 0xd4, - 0xb1, 0x1b, 0x4e, 0xd3, 0xb1, 0x2d, 0x6d, 0xd7, 0x78, 0x07, 0x6a, 0x7d, 0x38, 0x89, 0xc8, 0x7c, - 0x8a, 0x47, 0x37, 0x78, 0x86, 0xa3, 0x14, 0x1d, 0x33, 0xe6, 0xe3, 0x1e, 0x02, 0x1f, 0x21, 0x47, - 0x3c, 0x33, 0x7e, 0x09, 0x70, 0x68, 0xe1, 0x69, 0x78, 0x87, 0x93, 0x5e, 0xcc, 0x24, 0xc0, 0xa8, - 0x55, 0x20, 0x73, 0x4a, 0xb9, 0x56, 0x29, 0xdc, 0xbf, 0x81, 0xbb, 0xd8, 0xf1, 0xd4, 0x60, 0x63, - 0xee, 0x39, 0x88, 0x99, 0x4a, 0xfc, 0xfa, 0x72, 0xed, 0xd5, 0x56, 0x09, 0x19, 0x57, 0xa4, 0x2c, - 0x36, 0x65, 0x10, 0xb3, 0x53, 0x8d, 0x07, 0x01, 0x24, 0x73, 0x4a, 0x86, 0x93, 0x95, 0xd5, 0x77, - 0x57, 0x57, 0x47, 0x27, 0xa0, 0x74, 0x12, 0x7c, 0x77, 0x11, 0xd0, 0x6f, 0x5c, 0xb7, 0x03, 0x4f, - 0x89, 0xf3, 0x1c, 0xbd, 0x00, 0xa9, 0x93, 0x10, 0x32, 0xd6, 0x45, 0x5e, 0x90, 0xe2, 0x2c, 0x41, - 0x9f, 0x41, 0xc9, 0x1f, 0x9f, 0xea, 0x52, 0xa5, 0xc4, 0x16, 0x7a, 0x5d, 0x58, 0x68, 0xd3, 0x26, - 0x9e, 0x32, 0xcb, 0x29, 0xc6, 0x3d, 0xa8, 0xb9, 0x54, 0x2b, 0x46, 0x94, 0xec, 0x24, 0x21, 0xc9, - 0x13, 0x3e, 0x64, 0xd7, 0x49, 0x38, 0xc3, 0xa1, 0x6a, 0x7e, 0x55, 0x2e, 0xc8, 0x71, 0x71, 0x7e, - 0x56, 0xcd, 0xf0, 0x83, 0x2c, 0x58, 0xc8, 0xf1, 0x3e, 0x58, 0x38, 0x1e, 0x95, 0x61, 0xcf, 0xef, - 0x35, 0x1a, 0xb6, 0xef, 0x33, 0x13, 0x68, 0x50, 0x36, 0xeb, 0x56, 0xdf, 0xb3, 0x2f, 0x7b, 0x99, - 0x13, 0x1e, 0x4a, 0xe8, 0x08, 0xf6, 0x9b, 0x6d, 0xcf, 0x74, 0x2c, 0xcb, 0x76, 0xb5, 0x1f, 0x3c, - 0x77, 0xdb, 0xdd, 0x7e, 0xb3, 0xdd, 0x73, 0x2d, 0xed, 0x67, 0x09, 0xe9, 0xf0, 0xdc, 0xb7, 0xbd, - 0x2b, 0xa7, 0x61, 0xf7, 0x7b, 0x6e, 0xfd, 0xaa, 0xee, 0xb4, 0xea, 0x66, 0xcb, 0xd6, 0xfe, 0x96, - 0x6a, 0xbf, 0x05, 0xf6, 0xf2, 0x7c, 0x9b, 0xa5, 0x06, 0xe8, 0x2b, 0xec, 0xff, 0x4f, 0x9e, 0x16, - 0xeb, 0xc4, 0xd8, 0x0e, 0x59, 0x68, 0x66, 0xec, 0x9c, 0x09, 0x1f, 0x04, 0x74, 0x09, 0x7b, 0xb9, - 0x98, 0xe8, 0xb4, 0x40, 0x5a, 0x73, 0xe4, 0x49, 0x65, 0x5b, 0x7d, 0xbd, 0xe5, 0x40, 0xe6, 0x7f, - 0x21, 0x1f, 0xff, 0x05, 0x00, 0x00, 0xff, 0xff, 0xb0, 0xf0, 0x26, 0x9a, 0x4e, 0x04, 0x00, 0x00, + // 720 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x8c, 0x54, 0xcd, 0x6e, 0xd3, 0x40, + 0x10, 0xae, 0x13, 0xdb, 0x49, 0xa7, 0x3f, 0x09, 0x0b, 0x6a, 0x43, 0x84, 0xaa, 0x60, 0x24, 0x28, + 0x1c, 0x52, 0x14, 0x2e, 0x20, 0xc1, 0x21, 0x89, 0x1d, 0xd5, 0x52, 0x70, 0x5a, 0x3b, 0x69, 0xb9, + 0x55, 0x4e, 0xbc, 0x29, 0x56, 0x13, 0xaf, 0xe5, 0x75, 0x5b, 0xb5, 0xef, 0x80, 0x54, 0x09, 0xc4, + 0xe3, 0x70, 0xe1, 0xca, 0xfb, 0x70, 0x65, 0x77, 0xb3, 0x09, 0x4d, 0x42, 0x55, 0x4e, 0x9e, 0x99, + 0x9d, 0x6f, 0xfc, 0xcd, 0x37, 0xb3, 0x0b, 0x79, 0xbf, 0x5f, 0x8d, 0x13, 0x92, 0x12, 0x54, 0xf0, + 0x53, 0x32, 0x0e, 0x07, 0xfd, 0x84, 0xf8, 0xc1, 0xc0, 0xa7, 0xa9, 0x61, 0xc2, 0x83, 0xc6, 0xd4, + 0x71, 0x31, 0x8d, 0x49, 0x44, 0x31, 0xda, 0x03, 0xdd, 0x4b, 0xfd, 0xf4, 0x9c, 0x96, 0x94, 0x8a, + 0xb2, 0xbb, 0x59, 0xdb, 0xae, 0x2e, 0xc0, 0xaa, 0x93, 0x63, 0x57, 0xa7, 0xe2, 0x6b, 0x3c, 0x87, + 0xe2, 0xac, 0xca, 0x47, 0x4c, 0xa9, 0x7f, 0x8a, 0x11, 0x02, 0xd5, 0xf4, 0x53, 0x5f, 0x94, 0x58, + 0x77, 0xd5, 0x80, 0xd9, 0xc6, 0x17, 0x05, 0xb6, 0xbd, 0xf0, 0x34, 0x62, 0xa0, 0x04, 0x1f, 0x90, + 0x51, 0x38, 0xb8, 0xb2, 0xa2, 0x0b, 0x3c, 0x22, 0x31, 0x46, 0x25, 0xc8, 0x1d, 0xe1, 0x84, 0x86, + 0x24, 0x12, 0x10, 0xcd, 0xcd, 0x5d, 0x4c, 0x5c, 0xf4, 0x16, 0xf4, 0x49, 0x6e, 0x29, 0xc3, 0x0e, + 0xd6, 0x6a, 0x95, 0x65, 0x3a, 0xf3, 0x35, 0x5d, 0x3d, 0x16, 0x5f, 0xb4, 0x03, 0x60, 0x07, 0x38, + 0x4a, 0xc3, 0x34, 0xc4, 0xb4, 0x94, 0xad, 0x64, 0x19, 0x13, 0x08, 0x67, 0x11, 0xe3, 0x97, 0x02, + 0x85, 0x05, 0x2c, 0x7a, 0x02, 0x79, 0x1e, 0xc2, 0x41, 0xe3, 0x6a, 0x42, 0x64, 0x7f, 0xc5, 0xcd, + 0x53, 0x19, 0x41, 0x1f, 0x40, 0x6d, 0x25, 0x64, 0x2c, 0x99, 0xbc, 0xb8, 0x8f, 0x49, 0xd5, 0xe9, + 0x9c, 0xa7, 0x9d, 0x21, 0x2b, 0xa1, 0x0e, 0x19, 0xac, 0xdc, 0x05, 0x7d, 0x12, 0x41, 0xeb, 0xa0, + 0x38, 0xb2, 0x51, 0x25, 0x42, 0xef, 0x21, 0x2f, 0x00, 0x9c, 0x66, 0x86, 0xd1, 0xfc, 0x9f, 0x26, + 0xf3, 0xb1, 0x44, 0x34, 0x74, 0x50, 0xbb, 0x57, 0x31, 0x36, 0x7e, 0x2a, 0x8c, 0x3b, 0xc6, 0x67, + 0x76, 0x34, 0x24, 0xe8, 0x1d, 0x68, 0x6c, 0x4a, 0x49, 0x2a, 0x67, 0xf8, 0x6c, 0xb9, 0x9e, 0xcc, + 0xac, 0x8a, 0x34, 0x5e, 0xc0, 0xd5, 0x28, 0x37, 0xd1, 0x2e, 0x53, 0x25, 0xc6, 0x83, 0x70, 0x18, + 0xe2, 0xc0, 0x39, 0x1f, 0xf7, 0x71, 0x22, 0xfa, 0x55, 0xdd, 0x02, 0x9d, 0x0f, 0x73, 0x81, 0x8f, + 0xc3, 0x28, 0x20, 0x97, 0x5e, 0x78, 0x8d, 0x99, 0xc0, 0x3c, 0x09, 0x2e, 0x67, 0x11, 0xa3, 0x06, + 0xab, 0xb3, 0xea, 0x08, 0x58, 0xf3, 0xd6, 0xb1, 0xe5, 0x75, 0x8b, 0x2b, 0xdc, 0xee, 0xb4, 0x4d, + 0x6e, 0x2b, 0x68, 0x83, 0x25, 0x1d, 0x58, 0x4d, 0xbb, 0x65, 0x5b, 0x66, 0x31, 0x63, 0xbc, 0x84, + 0x42, 0x7d, 0x70, 0x16, 0x91, 0xcb, 0x11, 0x0e, 0x4e, 0xf1, 0x98, 0x0d, 0x0b, 0x6d, 0x31, 0xe4, + 0x84, 0x87, 0x22, 0x7e, 0xa1, 0x47, 0xc2, 0x33, 0xbe, 0x2b, 0xb0, 0x61, 0xe2, 0x51, 0xc8, 0x36, + 0xa5, 0x17, 0xb3, 0x0d, 0xc3, 0xa8, 0xbd, 0x04, 0x16, 0x90, 0x7f, 0xe9, 0xb9, 0x90, 0xc7, 0x66, + 0x54, 0xf0, 0x17, 0xfe, 0xbb, 0x07, 0x2a, 0x57, 0x49, 0x4e, 0xfb, 0xf1, 0x9d, 0x12, 0xf2, 0xf9, + 0x52, 0x66, 0xcf, 0x26, 0x71, 0xa3, 0x80, 0xd6, 0x18, 0x91, 0xc1, 0xd9, 0x2d, 0xea, 0x99, 0xdb, + 0xd4, 0x51, 0x99, 0x4d, 0x3c, 0xc1, 0x17, 0xfb, 0x3e, 0xfd, 0x2c, 0x74, 0x5b, 0x67, 0xf3, 0x94, + 0x3e, 0x7a, 0x04, 0xda, 0x41, 0x42, 0xc8, 0xb0, 0xa4, 0x8a, 0x03, 0x2d, 0xe6, 0x0e, 0x5b, 0xbd, + 0xbc, 0xbc, 0x5b, 0xb4, 0xa4, 0x89, 0x1d, 0x79, 0xba, 0x44, 0x68, 0xf1, 0x16, 0xba, 0xf9, 0xb1, + 0x84, 0x18, 0xd7, 0x50, 0x90, 0x52, 0xdd, 0xba, 0xe7, 0x9a, 0x95, 0x24, 0x24, 0xb9, 0xe7, 0x9a, + 0xb3, 0xee, 0x34, 0xcc, 0xf3, 0x50, 0x55, 0x76, 0x25, 0x05, 0xd9, 0x5a, 0xfe, 0x3f, 0x3f, 0xe5, + 0xf9, 0x7d, 0x6e, 0x4c, 0xe5, 0x78, 0xe5, 0x4f, 0x1f, 0x14, 0xb4, 0x06, 0x39, 0xaf, 0xd7, 0x6c, + 0x5a, 0x9e, 0xc7, 0x96, 0xa0, 0x08, 0x6b, 0x8d, 0xba, 0x79, 0xe2, 0x5a, 0x87, 0x3d, 0xbe, 0x09, + 0x37, 0x59, 0xb4, 0x09, 0xab, 0xad, 0x8e, 0xdb, 0xb0, 0x4d, 0xd3, 0x72, 0x8a, 0x5f, 0x85, 0xef, + 0x74, 0xba, 0x27, 0xad, 0x4e, 0xcf, 0x31, 0x8b, 0xdf, 0xb2, 0xec, 0x91, 0x78, 0xe8, 0x59, 0xee, + 0x91, 0xdd, 0xb4, 0x4e, 0x7a, 0x4e, 0xfd, 0xa8, 0x6e, 0xb7, 0xeb, 0x8d, 0xb6, 0x55, 0xfc, 0x9d, + 0xad, 0xfd, 0x60, 0x57, 0xb9, 0x2e, 0xd8, 0xcc, 0x34, 0x40, 0x9f, 0x60, 0xf5, 0xaf, 0x73, 0xbf, + 0x58, 0x65, 0xe3, 0xee, 0x94, 0xa9, 0x66, 0xc6, 0xca, 0xae, 0xf2, 0x5a, 0x41, 0x87, 0x90, 0x93, + 0x62, 0xa2, 0x9d, 0x25, 0xd0, 0xdc, 0x46, 0x96, 0x2b, 0x77, 0x9d, 0xcf, 0x97, 0xec, 0xeb, 0xe2, + 0x85, 0x7e, 0xf3, 0x27, 0x00, 0x00, 0xff, 0xff, 0xdd, 0xa9, 0x3a, 0x84, 0xad, 0x05, 0x00, 0x00, } diff --git a/orderer/atomicbroadcast/ab.proto b/orderer/atomicbroadcast/ab.proto index e115680debf..05abaf22da6 100644 --- a/orderer/atomicbroadcast/ab.proto +++ b/orderer/atomicbroadcast/ab.proto @@ -35,6 +35,32 @@ message BroadcastMessage { bytes Data = 1; } + +// SignaturePolicyEnvelope wraps a SignaturePolicy and includes a version for future enhancements +message SignaturePolicyEnvelope { + int32 Version = 1; + SignaturePolicy Policy = 2; + repeated bytes Identities = 3; +} + +// SignaturePolicy is a recursive message structure which defines a featherweight DSL for describing +// policies which are more complicated than 'exactly this signature'. The NOutOf operator is sufficent +// to express AND as well as OR, as well as of course N out of the following M policies +// SignedBy implies that the signature is from a valid certificate which is signed by the trusted +// authority specified in the bytes. This will be the certificate itself for a self-signed certificate +// and will be the CA for more traditional certificates +message SignaturePolicy { + message NOutOf { + int32 N = 1; + repeated SignaturePolicy Policies = 2; + } + oneof Type { + int32 SignedBy = 1; + NOutOf From = 2; + } +} + + message SeekInfo { // Start may be specified to a specific block number, or may be request from the newest or oldest available // The start location is always inclusive, so the first reply from NEWEST will contain the newest block at the time @@ -89,4 +115,3 @@ service AtomicBroadcast { // To avoid latency, clients will likely acknowledge before the WindowSize has been exhausted, preventing the server from stopping and waiting for an Acknowledgement rpc Deliver(stream DeliverUpdate) returns (stream DeliverResponse) {} } - diff --git a/orderer/cauthdsl/cauthdsl.go b/orderer/cauthdsl/cauthdsl.go new file mode 100644 index 00000000000..c15d1fcbff4 --- /dev/null +++ b/orderer/cauthdsl/cauthdsl.go @@ -0,0 +1,96 @@ +/* +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 ( + "bytes" + "fmt" + + ab "github.com/hyperledger/fabric/orderer/atomicbroadcast" +) + +// 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 *ab.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 +} + +// compile recursively builds a go evaluatable function corresponding to the policy specified +func compile(policy *ab.SignaturePolicy, identities [][]byte, ch CryptoHelper) (func([]byte, [][]byte, [][]byte) bool, error) { + switch t := policy.Type.(type) { + case *ab.SignaturePolicy_From: + policies := make([]func([]byte, [][]byte, [][]byte) bool, len(t.From.Policies)) + for i, policy := range t.From.Policies { + compiledPolicy, err := compile(policy, identities, ch) + if err != nil { + return nil, err + } + policies[i] = compiledPolicy + + } + return func(msg []byte, ids [][]byte, signatures [][]byte) bool { + verified := int32(0) + for _, policy := range policies { + if policy(msg, ids, signatures) { + verified++ + } + } + return verified >= t.From.N + }, nil + case *ab.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(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 false + }, nil + 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/orderer/cauthdsl/cauthdsl_builder.go b/orderer/cauthdsl/cauthdsl_builder.go new file mode 100644 index 00000000000..6f129d41ef3 --- /dev/null +++ b/orderer/cauthdsl/cauthdsl_builder.go @@ -0,0 +1,61 @@ +/* +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 ( + ab "github.com/hyperledger/fabric/orderer/atomicbroadcast" +) + +// Envelope builds an envelope message embedding a SignaturePolicy +func Envelope(policy *ab.SignaturePolicy, identities [][]byte) *ab.SignaturePolicyEnvelope { + return &ab.SignaturePolicyEnvelope{ + Version: 0, + Policy: policy, + Identities: identities, + } +} + +// SignedBy creates a SignaturePolicy requiring a given signer's signature +func SignedBy(index int32) *ab.SignaturePolicy { + return &ab.SignaturePolicy{ + Type: &ab.SignaturePolicy_SignedBy{ + SignedBy: index, + }, + } +} + +// And is a convenience method which utilizes NOutOf to produce And equivalent behavior +func And(lhs, rhs *ab.SignaturePolicy) *ab.SignaturePolicy { + return NOutOf(2, []*ab.SignaturePolicy{lhs, rhs}) +} + +// Or is a convenience method which utilizes NOutOf to produce Or equivalent behavior +func Or(lhs, rhs *ab.SignaturePolicy) *ab.SignaturePolicy { + return NOutOf(1, []*ab.SignaturePolicy{lhs, rhs}) +} + +// NOutOf creates a policy which requires N out of the slice of policies to evaluate to true +func NOutOf(n int32, policies []*ab.SignaturePolicy) *ab.SignaturePolicy { + return &ab.SignaturePolicy{ + Type: &ab.SignaturePolicy_From{ + From: &ab.SignaturePolicy_NOutOf{ + N: n, + Policies: policies, + }, + }, + } +} diff --git a/orderer/cauthdsl/cauthdsl_test.go b/orderer/cauthdsl/cauthdsl_test.go new file mode 100644 index 00000000000..064ce70b920 --- /dev/null +++ b/orderer/cauthdsl/cauthdsl_test.go @@ -0,0 +1,109 @@ +/* +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 ( + "bytes" + "testing" + + "github.com/golang/protobuf/proto" + ab "github.com/hyperledger/fabric/orderer/atomicbroadcast" +) + +var invalidSignature = []byte("badsigned") +var validSignature = []byte("signed") +var signers = [][]byte{[]byte("signer0"), []byte("signer1")} + +type mockCryptoHelper struct { +} + +func (mch *mockCryptoHelper) VerifySignature(msg []byte, id []byte, signature []byte) bool { + return bytes.Equal(signature, validSignature) +} + +func TestSimpleSignature(t *testing.T) { + mch := &mockCryptoHelper{} + policy := Envelope(SignedBy(0), signers) + + spe, err := NewSignaturePolicyEvaluator(policy, 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.Errorf("Expected authentication to succeed with valid signatures") + } + if spe.Authenticate(nil, [][]byte{signers[0]}, [][]byte{invalidSignature}) { + t.Errorf("Expected authentication to fail given the invalid signature") + } + if spe.Authenticate(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 := NewSignaturePolicyEvaluator(policy, 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.Errorf("Expected authentication to succeed with valid signatures") + } + if spe.Authenticate(nil, 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.Errorf("Expected authentication to fail because although there were two valid signatures, one was duplicated") + } +} + +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) + 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.Errorf("Expected authentication to succeed with valid signatures") + } + if spe.Authenticate(nil, 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.Errorf("Expected authentication to succeed because the rule allows duplicated signatures for signer[0]") + } +} + +func TestNegatively(t *testing.T) { + mch := &mockCryptoHelper{} + rpolicy := Envelope(And(SignedBy(0), SignedBy(1)), signers) + rpolicy.Policy.Type = nil + b, _ := proto.Marshal(rpolicy) + policy := &ab.SignaturePolicyEnvelope{} + _ = proto.Unmarshal(b, policy) + _, err := NewSignaturePolicyEvaluator(policy, mch) + if err == nil { + t.Fatalf("Should have errored compiling because the Type field was nil") + } +}