Fix issue with Sign/VerifyRequestHeader proto.Clone

proto.Clone couldn't makes copy for custom fields.
We should reset and restore MetaHeader before/after Sign/Verify.
Add test coverage to check that all works like expected.
This commit is contained in:
Evgeniy Kulikov 2019-11-19 19:03:42 +03:00
parent 24e5497b1d
commit 5c344bfceb
No known key found for this signature in database
GPG key ID: BF6AEE0A2A699BF2
5 changed files with 185 additions and 45 deletions

View file

@ -8,10 +8,11 @@ import (
type ( type (
// MetaHeader contains meta information of request. // MetaHeader contains meta information of request.
// It provides methods to get or set meta information // It provides methods to get or set meta information meta header.
// and reset meta header. // Also contains methods to reset and restore meta header.
MetaHeader interface { MetaHeader interface {
ResetMeta() ResetMeta() RequestMetaHeader
RestoreMeta(RequestMetaHeader)
// TTLRequest to verify and update ttl requests. // TTLRequest to verify and update ttl requests.
GetTTL() uint32 GetTTL() uint32
@ -22,7 +23,7 @@ type (
SetEpoch(v uint64) SetEpoch(v uint64)
} }
// TTLCondition is closure, that allows to validate request with ttl // TTLCondition is closure, that allows to validate request with ttl.
TTLCondition func(ttl uint32) error TTLCondition func(ttl uint32) error
) )
@ -45,14 +46,21 @@ const (
ErrIncorrectTTL = internal.Error("incorrect ttl") ErrIncorrectTTL = internal.Error("incorrect ttl")
) )
// SetTTL sets TTL to RequestMetaHeader // SetTTL sets TTL to RequestMetaHeader.
func (m *RequestMetaHeader) SetTTL(v uint32) { m.TTL = v } func (m *RequestMetaHeader) SetTTL(v uint32) { m.TTL = v }
// SetEpoch sets Epoch to RequestMetaHeader // SetEpoch sets Epoch to RequestMetaHeader.
func (m *RequestMetaHeader) SetEpoch(v uint64) { m.Epoch = v } func (m *RequestMetaHeader) SetEpoch(v uint64) { m.Epoch = v }
// ResetMeta sets RequestMetaHeader to empty value // ResetMeta returns current value and sets RequestMetaHeader to empty value.
func (m *RequestMetaHeader) ResetMeta() { m.Reset() } func (m *RequestMetaHeader) ResetMeta() RequestMetaHeader {
cp := *m
m.Reset()
return cp
}
// RestoreMeta sets current RequestMetaHeader to passed value.
func (m *RequestMetaHeader) RestoreMeta(v RequestMetaHeader) { *m = v }
// IRNonForwarding condition that allows NonForwardingTTL only for IR // IRNonForwarding condition that allows NonForwardingTTL only for IR
func IRNonForwarding(role NodeRole) TTLCondition { func IRNonForwarding(role NodeRole) TTLCondition {

View file

@ -10,7 +10,7 @@ import (
) )
type ( type (
// VerifiableRequest adds possibility to sign and verify request header // VerifiableRequest adds possibility to sign and verify request header.
VerifiableRequest interface { VerifiableRequest interface {
proto.Message proto.Message
Marshal() ([]byte, error) Marshal() ([]byte, error)
@ -30,19 +30,19 @@ type (
) )
const ( const (
// ErrCannotLoadPublicKey is raised when cannot unmarshal public key from RequestVerificationHeader_Sign // ErrCannotLoadPublicKey is raised when cannot unmarshal public key from RequestVerificationHeader_Sign.
ErrCannotLoadPublicKey = internal.Error("cannot load public key") ErrCannotLoadPublicKey = internal.Error("cannot load public key")
// ErrCannotFindOwner is raised when signatures empty in GetOwner // ErrCannotFindOwner is raised when signatures empty in GetOwner.
ErrCannotFindOwner = internal.Error("cannot find owner public key") ErrCannotFindOwner = internal.Error("cannot find owner public key")
) )
// SetSignatures replaces signatures stored in RequestVerificationHeader // SetSignatures replaces signatures stored in RequestVerificationHeader.
func (m *RequestVerificationHeader) SetSignatures(signatures []*RequestVerificationHeader_Signature) { func (m *RequestVerificationHeader) SetSignatures(signatures []*RequestVerificationHeader_Signature) {
m.Signatures = signatures m.Signatures = signatures
} }
// AddSignature adds new Signature into RequestVerificationHeader // AddSignature adds new Signature into RequestVerificationHeader.
func (m *RequestVerificationHeader) AddSignature(sig *RequestVerificationHeader_Signature) { func (m *RequestVerificationHeader) AddSignature(sig *RequestVerificationHeader_Signature) {
if sig == nil { if sig == nil {
return return
@ -123,12 +123,14 @@ func newSignature(key *ecdsa.PrivateKey, data []byte) (*RequestVerificationHeade
// SignRequestHeader receives private key and request with RequestVerificationHeader, // SignRequestHeader receives private key and request with RequestVerificationHeader,
// tries to marshal and sign request with passed PrivateKey, after that adds // tries to marshal and sign request with passed PrivateKey, after that adds
// new signature to headers. If something went wrong, returns error. // new signature to headers. If something went wrong, returns error.
func SignRequestHeader(key *ecdsa.PrivateKey, req VerifiableRequest) error { func SignRequestHeader(key *ecdsa.PrivateKey, msg VerifiableRequest) error {
msg := proto.Clone(req).(VerifiableRequest)
// ignore meta header // ignore meta header
if meta, ok := msg.(MetaHeader); ok { if meta, ok := msg.(MetaHeader); ok {
meta.ResetMeta() h := meta.ResetMeta()
defer func() {
meta.RestoreMeta(h)
}()
} }
data, err := msg.Marshal() data, err := msg.Marshal()
@ -141,22 +143,28 @@ func SignRequestHeader(key *ecdsa.PrivateKey, req VerifiableRequest) error {
return err return err
} }
req.AddSignature(signature) msg.AddSignature(signature)
return nil return nil
} }
// VerifyRequestHeader receives request with RequestVerificationHeader, // VerifyRequestHeader receives request with RequestVerificationHeader,
// tries to marshal and verify each signature from request // tries to marshal and verify each signature from request.
// If something went wrong, returns error. // If something went wrong, returns error.
func VerifyRequestHeader(req VerifiableRequest) error { func VerifyRequestHeader(msg VerifiableRequest) error {
msg := proto.Clone(req).(VerifiableRequest)
// ignore meta header // ignore meta header
if meta, ok := msg.(MetaHeader); ok { if meta, ok := msg.(MetaHeader); ok {
meta.ResetMeta() h := meta.ResetMeta()
defer func() {
meta.RestoreMeta(h)
}()
} }
signatures := msg.GetSignatures() signatures := msg.GetSignatures()
defer func() {
msg.SetSignatures(signatures)
}()
for i := range signatures { for i := range signatures {
msg.SetSignatures(signatures[:i]) msg.SetSignatures(signatures[:i])
@ -177,3 +185,35 @@ func VerifyRequestHeader(req VerifiableRequest) error {
return nil return nil
} }
// testCustomField for test usage only.
type testCustomField [8]uint32
var _ internal.Custom = (*testCustomField)(nil)
// Reset skip, it's for test usage only.
func (t testCustomField) Reset() {}
// ProtoMessage skip, it's for test usage only.
func (t testCustomField) ProtoMessage() {}
// Size skip, it's for test usage only.
func (t testCustomField) Size() int { return 32 }
// String skip, it's for test usage only.
func (t testCustomField) String() string { return "" }
// Bytes skip, it's for test usage only.
func (t testCustomField) Bytes() []byte { return nil }
// Unmarshal skip, it's for test usage only.
func (t testCustomField) Unmarshal(data []byte) error { return nil }
// Empty skip, it's for test usage only.
func (t testCustomField) Empty() bool { return false }
// UnmarshalTo skip, it's for test usage only.
func (t testCustomField) MarshalTo(data []byte) (int, error) { return 0, nil }
// Marshal skip, it's for test usage only.
func (t testCustomField) Marshal() ([]byte, error) { return nil, nil }

View file

@ -1,9 +1,12 @@
package service package service
import ( import (
"bytes"
"log"
"math" "math"
"testing" "testing"
"github.com/gogo/protobuf/proto"
crypto "github.com/nspcc-dev/neofs-crypto" crypto "github.com/nspcc-dev/neofs-crypto"
"github.com/nspcc-dev/neofs-crypto/test" "github.com/nspcc-dev/neofs-crypto/test"
"github.com/pkg/errors" "github.com/pkg/errors"
@ -104,3 +107,37 @@ func TestMaintainableRequest(t *testing.T) {
require.EqualError(t, errors.Cause(err), crypto.ErrInvalidSignature.Error()) require.EqualError(t, errors.Cause(err), crypto.ErrInvalidSignature.Error())
} }
} }
func TestVerifyAndSignRequestHeaderWithoutCloning(t *testing.T) {
key := test.DecodeKey(0)
custom := testCustomField{1, 2, 3, 4, 5, 6, 7, 8}
b := &TestRequest{
IntField: math.MaxInt32,
StringField: "TestRequestStringField",
BytesField: []byte("TestRequestBytesField"),
CustomField: &custom,
RequestMetaHeader: RequestMetaHeader{
TTL: math.MaxInt32 - 8,
Epoch: math.MaxInt64 - 12,
},
}
require.NoError(t, SignRequestHeader(key, b))
require.NoError(t, VerifyRequestHeader(b))
require.Len(t, b.Signatures, 1)
require.Equal(t, custom, *b.CustomField)
require.Equal(t, uint32(math.MaxInt32-8), b.GetTTL())
require.Equal(t, uint64(math.MaxInt64-12), b.GetEpoch())
buf := bytes.NewBuffer(nil)
log.SetOutput(buf)
cp, ok := proto.Clone(b).(*TestRequest)
require.True(t, ok)
require.NotEqual(t, b, cp)
require.Contains(t, buf.String(), "proto: don't know how to copy")
}

View file

@ -24,9 +24,10 @@ var _ = math.Inf
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type TestRequest struct { type TestRequest struct {
IntField int32 `protobuf:"varint,1,opt,name=IntField,proto3" json:"IntField,omitempty"` IntField int32 `protobuf:"varint,1,opt,name=IntField,proto3" json:"IntField,omitempty"`
StringField string `protobuf:"bytes,2,opt,name=StringField,proto3" json:"StringField,omitempty"` StringField string `protobuf:"bytes,2,opt,name=StringField,proto3" json:"StringField,omitempty"`
BytesField []byte `protobuf:"bytes,3,opt,name=BytesField,proto3" json:"BytesField,omitempty"` BytesField []byte `protobuf:"bytes,3,opt,name=BytesField,proto3" json:"BytesField,omitempty"`
CustomField *testCustomField `protobuf:"bytes,4,opt,name=CustomField,proto3,customtype=testCustomField" json:"CustomField,omitempty"`
RequestMetaHeader `protobuf:"bytes,98,opt,name=Meta,proto3,embedded=Meta" json:"Meta"` RequestMetaHeader `protobuf:"bytes,98,opt,name=Meta,proto3,embedded=Meta" json:"Meta"`
RequestVerificationHeader `protobuf:"bytes,99,opt,name=Header,proto3,embedded=Header" json:"Header"` RequestVerificationHeader `protobuf:"bytes,99,opt,name=Header,proto3,embedded=Header" json:"Header"`
XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_NoUnkeyedLiteral struct{} `json:"-"`
@ -91,26 +92,28 @@ func init() {
func init() { proto.RegisterFile("service/verify_test.proto", fileDescriptor_1effa83201a30d75) } func init() { proto.RegisterFile("service/verify_test.proto", fileDescriptor_1effa83201a30d75) }
var fileDescriptor_1effa83201a30d75 = []byte{ var fileDescriptor_1effa83201a30d75 = []byte{
// 296 bytes of a gzipped FileDescriptorProto // 322 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x64, 0x90, 0xbf, 0x4e, 0xeb, 0x30, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x64, 0x90, 0x31, 0x4f, 0xc3, 0x30,
0x14, 0xc6, 0xeb, 0x7b, 0xa1, 0x14, 0x97, 0xc9, 0x62, 0x28, 0x19, 0xdc, 0xa8, 0x53, 0x96, 0x26, 0x10, 0x85, 0xeb, 0x52, 0x4a, 0x71, 0x90, 0x90, 0x0c, 0x43, 0xc9, 0x90, 0x46, 0x9d, 0xb2, 0x34,
0x12, 0x2c, 0x4c, 0x0c, 0x15, 0x42, 0x30, 0xb0, 0x04, 0xc4, 0xc0, 0x82, 0x12, 0xe7, 0x24, 0x58, 0x91, 0x40, 0x48, 0x4c, 0x0c, 0x01, 0x21, 0x18, 0x58, 0x02, 0x62, 0x60, 0x41, 0x49, 0x7a, 0x0d,
0xa2, 0x71, 0x89, 0x4f, 0x22, 0xf5, 0x4d, 0x78, 0xa4, 0x8e, 0x1d, 0x99, 0x2a, 0x14, 0x46, 0x5e, 0x96, 0x68, 0x5c, 0xe2, 0x4b, 0xa5, 0xfe, 0x13, 0x66, 0x7e, 0x4d, 0xc7, 0x8e, 0x88, 0xa1, 0x42,
0x02, 0xd5, 0x36, 0x28, 0xc0, 0x96, 0xef, 0xcf, 0x2f, 0x47, 0xfe, 0xe8, 0x91, 0x86, 0xaa, 0x91, 0xe1, 0x8f, 0xa0, 0xd8, 0x06, 0x05, 0xd8, 0xfc, 0xde, 0xfb, 0x9e, 0xed, 0x3b, 0x7a, 0x20, 0xa1,
0x02, 0xa2, 0x06, 0x2a, 0x99, 0x2f, 0x1f, 0x10, 0x34, 0x86, 0x8b, 0x4a, 0xa1, 0x62, 0x7b, 0x2e, 0x98, 0xf3, 0x14, 0x82, 0x39, 0x14, 0x7c, 0xb2, 0x78, 0x40, 0x90, 0xe8, 0xcf, 0x0a, 0x81, 0x82,
0xf2, 0xd8, 0x57, 0x67, 0x0e, 0x98, 0xd8, 0xd0, 0x3b, 0xfc, 0xc9, 0x39, 0x77, 0x5a, 0x48, 0x7c, 0x6d, 0x99, 0xc8, 0x66, 0xdf, 0xcc, 0x14, 0x30, 0xd6, 0xa1, 0xbd, 0xff, 0xbb, 0x67, 0xdc, 0x51,
0xac, 0xd3, 0x50, 0xa8, 0x79, 0x54, 0xa8, 0x42, 0x45, 0xc6, 0x4e, 0xeb, 0xdc, 0x28, 0x23, 0xcc, 0xc6, 0xf1, 0xb1, 0x4c, 0xfc, 0x54, 0x4c, 0x83, 0x4c, 0x64, 0x22, 0x50, 0x76, 0x52, 0x4e, 0x94,
0x97, 0xad, 0x4f, 0x3e, 0x08, 0x1d, 0xde, 0x82, 0xc6, 0x18, 0x9e, 0x6b, 0xd0, 0xc8, 0x3c, 0x3a, 0x52, 0x42, 0x9d, 0x34, 0x3e, 0x7c, 0x6d, 0x53, 0xeb, 0x16, 0x24, 0x46, 0xf0, 0x5c, 0x82, 0x44,
0xb8, 0x2a, 0xf1, 0x42, 0xc2, 0x53, 0x36, 0x22, 0x3e, 0x09, 0x76, 0xe3, 0x6f, 0xcd, 0x7c, 0x3a, 0x66, 0xd3, 0xde, 0x55, 0x8e, 0x17, 0x1c, 0x9e, 0xc6, 0x7d, 0xe2, 0x12, 0x6f, 0x33, 0xfa, 0xd1,
0xbc, 0xc1, 0x4a, 0x96, 0x85, 0x8d, 0xff, 0xf9, 0x24, 0xd8, 0x8f, 0xbb, 0x16, 0xe3, 0x94, 0xce, 0xcc, 0xa5, 0xd6, 0x0d, 0x16, 0x3c, 0xcf, 0x74, 0xdc, 0x76, 0x89, 0xb7, 0x1d, 0x35, 0x2d, 0xe6,
0x96, 0x08, 0xda, 0x16, 0xfe, 0xfb, 0x24, 0x38, 0x88, 0x3b, 0x0e, 0x3b, 0xa5, 0x3b, 0xd7, 0x80, 0x50, 0x1a, 0x2e, 0x10, 0xa4, 0x06, 0x36, 0x5c, 0xe2, 0xed, 0x44, 0x0d, 0x87, 0x1d, 0x53, 0xeb,
0xc9, 0x28, 0xf5, 0x49, 0x30, 0x3c, 0xf6, 0x42, 0xf7, 0x82, 0xd0, 0x5d, 0xdf, 0x66, 0x97, 0x90, 0xac, 0x94, 0x28, 0xa6, 0x1a, 0xe8, 0xd4, 0x40, 0xb8, 0xf7, 0xbe, 0x1e, 0xec, 0xd6, 0x43, 0x37,
0x64, 0x50, 0xcd, 0x06, 0xab, 0xcd, 0xb8, 0xb7, 0xde, 0x8c, 0x49, 0x6c, 0x08, 0x76, 0x4e, 0xfb, 0xa2, 0xa8, 0xc9, 0xb1, 0x13, 0xda, 0xb9, 0x06, 0x8c, 0xfb, 0x89, 0x4b, 0x3c, 0xeb, 0xd0, 0xf6,
0x36, 0x19, 0x09, 0xc3, 0x4e, 0x7e, 0xb3, 0x77, 0xdb, 0x11, 0xa4, 0x48, 0x50, 0xaa, 0xf2, 0xcf, 0xcd, 0xe0, 0xbe, 0xf9, 0x74, 0x9d, 0x5d, 0x42, 0x3c, 0x86, 0x22, 0xec, 0x2d, 0xd7, 0x83, 0xd6,
0x3f, 0x1c, 0x3b, 0x3b, 0x5b, 0xb5, 0x9c, 0xac, 0x5b, 0x4e, 0x5e, 0x5b, 0x4e, 0xde, 0x5a, 0x4e, 0x6a, 0x3d, 0x20, 0x91, 0x6a, 0xb0, 0x73, 0xda, 0xd5, 0x49, 0x3f, 0x55, 0xdd, 0xe1, 0xdf, 0xee,
0x5e, 0xde, 0x79, 0xef, 0x3e, 0xe8, 0x4c, 0x56, 0xea, 0x85, 0x10, 0xd3, 0x0c, 0x9a, 0xa8, 0x04, 0x5d, 0xbd, 0x3b, 0x9e, 0xc6, 0xc8, 0x45, 0xfe, 0xef, 0x0e, 0xd3, 0x0d, 0x4f, 0x97, 0x95, 0x43,
0x95, 0xeb, 0xa9, 0x1d, 0xcc, 0xdd, 0x4a, 0xfb, 0x46, 0x9e, 0x7c, 0x06, 0x00, 0x00, 0xff, 0xff, 0x56, 0x95, 0x43, 0xde, 0x2a, 0x87, 0x7c, 0x54, 0x0e, 0x79, 0xf9, 0x74, 0x5a, 0xf7, 0x5e, 0x63,
0xa6, 0xdb, 0x5f, 0x7a, 0xb3, 0x01, 0x00, 0x00, 0xd3, 0xb9, 0x9c, 0xa5, 0xe9, 0x68, 0x0c, 0xf3, 0x20, 0x07, 0x31, 0x91, 0x23, 0xbd, 0x67, 0xf3,
0x56, 0xd2, 0x55, 0xf2, 0xe8, 0x2b, 0x00, 0x00, 0xff, 0xff, 0xf7, 0x37, 0x92, 0xd2, 0xea, 0x01,
0x00, 0x00,
} }
func (m *TestRequest) Marshal() (dAtA []byte, err error) { func (m *TestRequest) Marshal() (dAtA []byte, err error) {
@ -161,6 +164,18 @@ func (m *TestRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
dAtA[i] = 0x6 dAtA[i] = 0x6
i-- i--
dAtA[i] = 0x92 dAtA[i] = 0x92
if m.CustomField != nil {
{
size := m.CustomField.Size()
i -= size
if _, err := m.CustomField.MarshalTo(dAtA[i:]); err != nil {
return 0, err
}
i = encodeVarintVerifyTest(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x22
}
if len(m.BytesField) > 0 { if len(m.BytesField) > 0 {
i -= len(m.BytesField) i -= len(m.BytesField)
copy(dAtA[i:], m.BytesField) copy(dAtA[i:], m.BytesField)
@ -211,6 +226,10 @@ func (m *TestRequest) Size() (n int) {
if l > 0 { if l > 0 {
n += 1 + l + sovVerifyTest(uint64(l)) n += 1 + l + sovVerifyTest(uint64(l))
} }
if m.CustomField != nil {
l = m.CustomField.Size()
n += 1 + l + sovVerifyTest(uint64(l))
}
l = m.RequestMetaHeader.Size() l = m.RequestMetaHeader.Size()
n += 2 + l + sovVerifyTest(uint64(l)) n += 2 + l + sovVerifyTest(uint64(l))
l = m.RequestVerificationHeader.Size() l = m.RequestVerificationHeader.Size()
@ -341,6 +360,41 @@ func (m *TestRequest) Unmarshal(dAtA []byte) error {
m.BytesField = []byte{} m.BytesField = []byte{}
} }
iNdEx = postIndex iNdEx = postIndex
case 4:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field CustomField", wireType)
}
var byteLen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowVerifyTest
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
byteLen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if byteLen < 0 {
return ErrInvalidLengthVerifyTest
}
postIndex := iNdEx + byteLen
if postIndex < 0 {
return ErrInvalidLengthVerifyTest
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
var v testCustomField
m.CustomField = &v
if err := m.CustomField.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 98: case 98:
if wireType != 2 { if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field RequestMetaHeader", wireType) return fmt.Errorf("proto: wrong wireType = %d for field RequestMetaHeader", wireType)

View file

@ -12,6 +12,7 @@ message TestRequest {
int32 IntField = 1; int32 IntField = 1;
string StringField = 2; string StringField = 2;
bytes BytesField = 3; bytes BytesField = 3;
bytes CustomField = 4 [(gogoproto.customtype) = "testCustomField"];
RequestMetaHeader Meta = 98 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; RequestMetaHeader Meta = 98 [(gogoproto.embed) = true, (gogoproto.nullable) = false];
RequestVerificationHeader Header = 99 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; RequestVerificationHeader Header = 99 [(gogoproto.embed) = true, (gogoproto.nullable) = false];
} }