forked from TrueCloudLab/frostfs-api-go
service: remove no longer used Sign/Verify methods and functions
This commit is contained in:
parent
18bd5011f1
commit
877db6be32
3 changed files with 85 additions and 346 deletions
|
@ -2,6 +2,7 @@ package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
|
"sync"
|
||||||
|
|
||||||
crypto "github.com/nspcc-dev/neofs-crypto"
|
crypto "github.com/nspcc-dev/neofs-crypto"
|
||||||
)
|
)
|
||||||
|
@ -11,6 +12,12 @@ type keySign struct {
|
||||||
sign []byte
|
sign []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var bytesPool = sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
return make([]byte, 5<<20)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
// GetSignature is a sign field getter.
|
// GetSignature is a sign field getter.
|
||||||
func (s keySign) GetSignature() []byte {
|
func (s keySign) GetSignature() []byte {
|
||||||
return s.sign
|
return s.sign
|
||||||
|
|
|
@ -2,37 +2,9 @@ package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/nspcc-dev/neofs-api-go/internal"
|
"github.com/nspcc-dev/neofs-api-go/internal"
|
||||||
"github.com/nspcc-dev/neofs-api-go/refs"
|
|
||||||
crypto "github.com/nspcc-dev/neofs-crypto"
|
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.
|
// GetSessionToken returns SessionToken interface of Token field.
|
||||||
|
@ -90,164 +62,11 @@ func (m *RequestVerificationHeader) SetSignatures(signatures []*RequestVerificat
|
||||||
m.Signatures = signatures
|
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.
|
// SetToken is a Token field setter.
|
||||||
func (m *RequestVerificationHeader) SetToken(token *Token) {
|
func (m *RequestVerificationHeader) SetToken(token *Token) {
|
||||||
m.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.
|
// testCustomField for test usage only.
|
||||||
type testCustomField [8]uint32
|
type testCustomField [8]uint32
|
||||||
|
|
||||||
|
|
|
@ -1,194 +1,107 @@
|
||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"encoding/binary"
|
||||||
"log"
|
"io"
|
||||||
"math"
|
"math"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/gogo/protobuf/proto"
|
|
||||||
"github.com/nspcc-dev/neofs-api-go/refs"
|
"github.com/nspcc-dev/neofs-api-go/refs"
|
||||||
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/stretchr/testify/require"
|
"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)
|
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,
|
IntField: math.MaxInt32,
|
||||||
StringField: "TestRequestStringField",
|
StringField: "TestRequestStringField",
|
||||||
BytesField: make([]byte, 1<<22),
|
BytesField: make([]byte, 1<<22),
|
||||||
CustomField: &custom,
|
CustomField: &customField,
|
||||||
RequestMetaHeader: RequestMetaHeader{
|
}
|
||||||
TTL: math.MaxInt32 - 8,
|
|
||||||
Epoch: math.MaxInt64 - 12,
|
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.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
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) {
|
func TestRequestVerificationHeader_SetToken(t *testing.T) {
|
||||||
id, err := refs.NewUUID()
|
id, err := refs.NewUUID()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
Loading…
Reference in a new issue