service: remove no longer used Sign/Verify methods and functions

This commit is contained in:
Leonard Lyubich 2020-05-12 10:40:48 +03:00
parent 18bd5011f1
commit 877db6be32
3 changed files with 85 additions and 346 deletions

View file

@ -2,6 +2,7 @@ package service
import (
"crypto/ecdsa"
"sync"
crypto "github.com/nspcc-dev/neofs-crypto"
)
@ -11,6 +12,12 @@ type keySign struct {
sign []byte
}
var bytesPool = sync.Pool{
New: func() interface{} {
return make([]byte, 5<<20)
},
}
// GetSignature is a sign field getter.
func (s keySign) GetSignature() []byte {
return s.sign

View file

@ -2,37 +2,9 @@ package service
import (
"crypto/ecdsa"
"sync"
"github.com/nspcc-dev/neofs-api-go/internal"
"github.com/nspcc-dev/neofs-api-go/refs"
crypto "github.com/nspcc-dev/neofs-crypto"
"github.com/pkg/errors"
)
type (
// VerifiableRequest adds possibility to sign and verify request header.
VerifiableRequest interface {
Size() int
MarshalTo([]byte) (int, error)
AddSignature(*RequestVerificationHeader_Signature)
GetSignatures() []*RequestVerificationHeader_Signature
SetSignatures([]*RequestVerificationHeader_Signature)
}
// MaintainableRequest adds possibility to set and get (+validate)
// owner (client) public key from RequestVerificationHeader.
MaintainableRequest interface {
GetOwner() (*ecdsa.PublicKey, error)
SetOwner(*ecdsa.PublicKey, []byte)
GetLastPeer() (*ecdsa.PublicKey, error)
}
// TokenHeader is an interface of the container of a Token pointer value
TokenHeader interface {
GetToken() *Token
SetToken(*Token)
}
)
// GetSessionToken returns SessionToken interface of Token field.
@ -90,164 +62,11 @@ func (m *RequestVerificationHeader) SetSignatures(signatures []*RequestVerificat
m.Signatures = signatures
}
// AddSignature adds new Signature into RequestVerificationHeader.
func (m *RequestVerificationHeader) AddSignature(sig *RequestVerificationHeader_Signature) {
if sig == nil {
return
}
m.Signatures = append(m.Signatures, sig)
}
// CheckOwner validates, that passed OwnerID is equal to present PublicKey of owner.
func (m *RequestVerificationHeader) CheckOwner(owner refs.OwnerID) error {
if key, err := m.GetOwner(); err != nil {
return err
} else if user, err := refs.NewOwnerID(key); err != nil {
return err
} else if !user.Equal(owner) {
return ErrWrongOwner
}
return nil
}
// GetOwner tries to get owner (client) public key from signatures.
// If signatures contains not empty Origin, we should try to validate,
// that session key was signed by owner (client), otherwise return error.
func (m *RequestVerificationHeader) GetOwner() (*ecdsa.PublicKey, error) {
if len(m.Signatures) == 0 {
return nil, ErrCannotFindOwner
} else if key := crypto.UnmarshalPublicKey(m.Signatures[0].Peer); key != nil {
return key, nil
}
return nil, ErrInvalidPublicKeyBytes
}
// GetLastPeer tries to get last peer public key from signatures.
// If signatures has zero length, returns ErrCannotFindOwner.
// If signatures has length equal to one, uses GetOwner.
// Otherwise tries to unmarshal last peer public key.
func (m *RequestVerificationHeader) GetLastPeer() (*ecdsa.PublicKey, error) {
switch ln := len(m.Signatures); ln {
case 0:
return nil, ErrCannotFindOwner
case 1:
return m.GetOwner()
default:
if key := crypto.UnmarshalPublicKey(m.Signatures[ln-1].Peer); key != nil {
return key, nil
}
return nil, ErrInvalidPublicKeyBytes
}
}
// SetToken is a Token field setter.
func (m *RequestVerificationHeader) SetToken(token *Token) {
m.Token = token
}
func newSignature(key *ecdsa.PrivateKey, data []byte) (*RequestVerificationHeader_Signature, error) {
sign, err := crypto.Sign(key, data)
if err != nil {
return nil, err
}
return &RequestVerificationHeader_Signature{
Sign: sign,
Peer: crypto.MarshalPublicKey(&key.PublicKey),
}, nil
}
var bytesPool = sync.Pool{New: func() interface{} {
return make([]byte, 4.5*1024*1024) // 4.5MB
}}
// SignRequestHeader receives private key and request with RequestVerificationHeader,
// tries to marshal and sign request with passed PrivateKey, after that adds
// new signature to headers. If something went wrong, returns error.
func SignRequestHeader(key *ecdsa.PrivateKey, msg VerifiableRequest) error {
// ignore meta header
if meta, ok := msg.(SeizedRequestMetaContainer); ok {
h := meta.CutMeta()
defer func() {
meta.RestoreMeta(h)
}()
}
data := bytesPool.Get().([]byte)
defer func() {
bytesPool.Put(data)
}()
if size := msg.Size(); size <= cap(data) {
data = data[:size]
} else {
data = make([]byte, size)
}
size, err := msg.MarshalTo(data)
if err != nil {
return err
}
signature, err := newSignature(key, data[:size])
if err != nil {
return err
}
msg.AddSignature(signature)
return nil
}
// VerifyRequestHeader receives request with RequestVerificationHeader,
// tries to marshal and verify each signature from request.
// If something went wrong, returns error.
func VerifyRequestHeader(msg VerifiableRequest) error {
// ignore meta header
if meta, ok := msg.(SeizedRequestMetaContainer); ok {
h := meta.CutMeta()
defer func() {
meta.RestoreMeta(h)
}()
}
data := bytesPool.Get().([]byte)
signatures := msg.GetSignatures()
defer func() {
bytesPool.Put(data)
msg.SetSignatures(signatures)
}()
for i := range signatures {
msg.SetSignatures(signatures[:i])
peer := signatures[i].GetPeer()
sign := signatures[i].GetSign()
key := crypto.UnmarshalPublicKey(peer)
if key == nil {
return errors.Wrapf(ErrInvalidPublicKeyBytes, "%d: %02x", i, peer)
}
if size := msg.Size(); size <= cap(data) {
data = data[:size]
} else {
data = make([]byte, size)
}
if size, err := msg.MarshalTo(data); err != nil {
return errors.Wrapf(err, "%d: %02x", i, peer)
} else if err := crypto.Verify(key, data[:size], sign); err != nil {
return errors.Wrapf(err, "%d: %02x", i, peer)
}
}
return nil
}
// testCustomField for test usage only.
type testCustomField [8]uint32

View file

@ -1,194 +1,107 @@
package service
import (
"bytes"
"log"
"encoding/binary"
"io"
"math"
"testing"
"github.com/gogo/protobuf/proto"
"github.com/nspcc-dev/neofs-api-go/refs"
crypto "github.com/nspcc-dev/neofs-crypto"
"github.com/nspcc-dev/neofs-crypto/test"
"github.com/pkg/errors"
"github.com/stretchr/testify/require"
)
func BenchmarkSignRequestHeader(b *testing.B) {
func (m TestRequest) SignedData() ([]byte, error) {
return SignedDataFromReader(m)
}
func (m TestRequest) SignedDataSize() (sz int) {
sz += 4
sz += len(m.StringField)
sz += len(m.BytesField)
sz += m.CustomField.Size()
return
}
func (m TestRequest) ReadSignedData(p []byte) (int, error) {
if len(p) < m.SignedDataSize() {
return 0, io.ErrUnexpectedEOF
}
var off int
binary.BigEndian.PutUint32(p[off:], uint32(m.IntField))
off += 4
off += copy(p[off:], []byte(m.StringField))
off += copy(p[off:], m.BytesField)
n, err := m.CustomField.MarshalTo(p[off:])
off += n
return off, err
}
func BenchmarkSignDataWithSessionToken(b *testing.B) {
key := test.DecodeKey(0)
custom := testCustomField{1, 2, 3, 4, 5, 6, 7, 8}
customField := testCustomField{1, 2, 3, 4, 5, 6, 7, 8}
some := &TestRequest{
token := new(Token)
req := &TestRequest{
IntField: math.MaxInt32,
StringField: "TestRequestStringField",
BytesField: make([]byte, 1<<22),
CustomField: &custom,
RequestMetaHeader: RequestMetaHeader{
TTL: math.MaxInt32 - 8,
Epoch: math.MaxInt64 - 12,
},
CustomField: &customField,
}
req.SetTTL(math.MaxInt32 - 8)
req.SetEpoch(math.MaxInt64 - 12)
req.SetToken(token)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
require.NoError(b, SignDataWithSessionToken(key, req))
}
}
func BenchmarkVerifyAccumulatedSignaturesWithToken(b *testing.B) {
customField := testCustomField{1, 2, 3, 4, 5, 6, 7, 8}
token := new(Token)
req := &TestRequest{
IntField: math.MaxInt32,
StringField: "TestRequestStringField",
BytesField: make([]byte, 1<<22),
CustomField: &customField,
}
req.SetTTL(math.MaxInt32 - 8)
req.SetEpoch(math.MaxInt64 - 12)
req.SetToken(token)
for i := 0; i < 10; i++ {
key := test.DecodeKey(i)
require.NoError(b, SignDataWithSessionToken(key, req))
}
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
require.NoError(b, SignRequestHeader(key, some))
require.NoError(b, VerifyAccumulatedSignaturesWithToken(req))
}
}
func BenchmarkVerifyRequestHeader(b *testing.B) {
custom := testCustomField{1, 2, 3, 4, 5, 6, 7, 8}
some := &TestRequest{
IntField: math.MaxInt32,
StringField: "TestRequestStringField",
BytesField: make([]byte, 1<<22),
CustomField: &custom,
RequestMetaHeader: RequestMetaHeader{
TTL: math.MaxInt32 - 8,
Epoch: math.MaxInt64 - 12,
},
}
for i := 0; i < 10; i++ {
key := test.DecodeKey(i)
require.NoError(b, SignRequestHeader(key, some))
}
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
require.NoError(b, VerifyRequestHeader(some))
}
}
func TestSignRequestHeader(t *testing.T) {
req := &TestRequest{
IntField: math.MaxInt32,
StringField: "TestRequestStringField",
BytesField: []byte("TestRequestBytesField"),
}
key := test.DecodeKey(0)
peer := crypto.MarshalPublicKey(&key.PublicKey)
data, err := req.Marshal()
require.NoError(t, err)
require.NoError(t, SignRequestHeader(key, req))
require.Len(t, req.Signatures, 1)
for i := range req.Signatures {
sign := req.Signatures[i].GetSign()
require.Equal(t, peer, req.Signatures[i].GetPeer())
require.NoError(t, crypto.Verify(&key.PublicKey, data, sign))
}
}
func TestVerifyRequestHeader(t *testing.T) {
req := &TestRequest{
IntField: math.MaxInt32,
StringField: "TestRequestStringField",
BytesField: []byte("TestRequestBytesField"),
RequestMetaHeader: RequestMetaHeader{TTL: 10},
}
for i := 0; i < 10; i++ {
req.TTL--
require.NoError(t, SignRequestHeader(test.DecodeKey(i), req))
}
require.NoError(t, VerifyRequestHeader(req))
}
func TestMaintainableRequest(t *testing.T) {
req := &TestRequest{
IntField: math.MaxInt32,
StringField: "TestRequestStringField",
BytesField: []byte("TestRequestBytesField"),
RequestMetaHeader: RequestMetaHeader{TTL: 10},
}
count := 10
owner := test.DecodeKey(count + 1)
for i := 0; i < count; i++ {
req.TTL--
key := test.DecodeKey(i)
// sign first key (session key) by owner key
if i == 0 {
key = owner
}
require.NoError(t, SignRequestHeader(key, req))
}
{ // Validate owner
user, err := refs.NewOwnerID(&owner.PublicKey)
require.NoError(t, err)
require.NoError(t, req.CheckOwner(user))
}
{ // Good case:
require.NoError(t, VerifyRequestHeader(req))
// validate, that first key (session key) was signed with owner
signatures := req.GetSignatures()
require.Len(t, signatures, count)
pub, err := req.GetOwner()
require.NoError(t, err)
require.Equal(t, &owner.PublicKey, pub)
}
{ // Wrong signatures:
copy(req.Signatures[count-1].Sign, req.Signatures[count-2].Sign)
err := VerifyRequestHeader(req)
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")
}
func TestRequestVerificationHeader_SetToken(t *testing.T) {
id, err := refs.NewUUID()
require.NoError(t, err)