forked from TrueCloudLab/frostfs-api-go
Merge pull request #79 from nspcc-dev/remove-old-sign-verify-functions
service: remove no longer used Sign/Verify methods and functions
This commit is contained in:
commit
f85f8c5830
3 changed files with 85 additions and 346 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue