Merge pull request #78 from nspcc-dev/request-signed-payload

Implement SignedDataSource on all request messages
This commit is contained in:
Leonard Lyubich 2020-05-12 10:35:01 +03:00 committed by GitHub
commit 18bd5011f1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 1516 additions and 71 deletions

167
accounting/sign.go Normal file
View file

@ -0,0 +1,167 @@
package accounting
import (
"encoding/binary"
"io"
"github.com/nspcc-dev/neofs-api-go/service"
)
// SignedData returns payload bytes of the request.
func (m BalanceRequest) SignedData() ([]byte, error) {
return service.SignedDataFromReader(m)
}
// SignedDataSize returns payload size of the request.
func (m BalanceRequest) SignedDataSize() int {
return m.GetOwnerID().Size()
}
// ReadSignedData copies payload bytes to passed buffer.
//
// If the buffer size is insufficient, io.ErrUnexpectedEOF returns.
func (m BalanceRequest) ReadSignedData(p []byte) (int, error) {
sz := m.SignedDataSize()
if len(p) < sz {
return 0, io.ErrUnexpectedEOF
}
copy(p, m.GetOwnerID().Bytes())
return sz, nil
}
// SignedData returns payload bytes of the request.
func (m GetRequest) SignedData() ([]byte, error) {
return service.SignedDataFromReader(m)
}
// SignedDataSize returns payload size of the request.
func (m GetRequest) SignedDataSize() int {
return m.GetID().Size() + m.GetOwnerID().Size()
}
// ReadSignedData copies payload bytes to passed buffer.
//
// If the buffer size is insufficient, io.ErrUnexpectedEOF returns.
func (m GetRequest) ReadSignedData(p []byte) (int, error) {
sz := m.SignedDataSize()
if len(p) < sz {
return 0, io.ErrUnexpectedEOF
}
var off int
off += copy(p[off:], m.GetID().Bytes())
copy(p[off:], m.GetOwnerID().Bytes())
return sz, nil
}
// SignedData returns payload bytes of the request.
func (m PutRequest) SignedData() ([]byte, error) {
return service.SignedDataFromReader(m)
}
// SignedDataSize returns payload size of the request.
func (m PutRequest) SignedDataSize() (sz int) {
sz += m.GetOwnerID().Size()
sz += m.GetMessageID().Size()
sz += 8
if amount := m.GetAmount(); amount != nil {
sz += amount.Size()
}
return
}
// ReadSignedData copies payload bytes to passed buffer.
//
// If the buffer size is insufficient, io.ErrUnexpectedEOF returns.
func (m PutRequest) ReadSignedData(p []byte) (int, error) {
if len(p) < m.SignedDataSize() {
return 0, io.ErrUnexpectedEOF
}
var off int
off += copy(p[off:], m.GetOwnerID().Bytes())
off += copy(p[off:], m.GetMessageID().Bytes())
binary.BigEndian.PutUint64(p[off:], m.GetHeight())
off += 8
if amount := m.GetAmount(); amount != nil {
n, err := amount.MarshalTo(p[off:])
off += n
if err != nil {
return off + n, err
}
}
return off, nil
}
// SignedData returns payload bytes of the request.
func (m ListRequest) SignedData() ([]byte, error) {
return service.SignedDataFromReader(m)
}
// SignedDataSize returns payload size of the request.
func (m ListRequest) SignedDataSize() int {
return m.GetOwnerID().Size()
}
// ReadSignedData copies payload bytes to passed buffer.
//
// If the buffer size is insufficient, io.ErrUnexpectedEOF returns.
func (m ListRequest) ReadSignedData(p []byte) (int, error) {
sz := m.SignedDataSize()
if len(p) < sz {
return 0, io.ErrUnexpectedEOF
}
copy(p, m.GetOwnerID().Bytes())
return sz, nil
}
// SignedData returns payload bytes of the request.
func (m DeleteRequest) SignedData() ([]byte, error) {
return service.SignedDataFromReader(m)
}
// SignedDataSize returns payload size of the request.
func (m DeleteRequest) SignedDataSize() (sz int) {
sz += m.GetID().Size()
sz += m.GetOwnerID().Size()
sz += m.GetMessageID().Size()
return
}
// ReadSignedData copies payload bytes to passed buffer.
//
// If the buffer size is insufficient, io.ErrUnexpectedEOF returns.
func (m DeleteRequest) ReadSignedData(p []byte) (int, error) {
if len(p) < m.SignedDataSize() {
return 0, io.ErrUnexpectedEOF
}
var off int
off += copy(p[off:], m.GetID().Bytes())
off += copy(p[off:], m.GetOwnerID().Bytes())
off += copy(p[off:], m.GetMessageID().Bytes())
return off, nil
}

185
accounting/sign_test.go Normal file
View file

@ -0,0 +1,185 @@
package accounting
import (
"testing"
"github.com/nspcc-dev/neofs-api-go/decimal"
"github.com/nspcc-dev/neofs-api-go/service"
"github.com/nspcc-dev/neofs-crypto/test"
"github.com/stretchr/testify/require"
)
func TestSignBalanceRequest(t *testing.T) {
sk := test.DecodeKey(0)
type sigType interface {
service.SignedDataWithToken
service.SignKeyPairAccumulator
service.SignKeyPairSource
SetToken(*service.Token)
}
items := []struct {
constructor func() sigType
payloadCorrupt []func(sigType)
}{
{ // BalanceRequest
constructor: func() sigType {
return new(BalanceRequest)
},
payloadCorrupt: []func(sigType){
func(s sigType) {
req := s.(*BalanceRequest)
owner := req.GetOwnerID()
owner[0]++
req.SetOwnerID(owner)
},
},
},
{ // GetRequest
constructor: func() sigType {
return new(GetRequest)
},
payloadCorrupt: []func(sigType){
func(s sigType) {
req := s.(*GetRequest)
id, err := NewChequeID()
require.NoError(t, err)
req.SetID(id)
},
func(s sigType) {
req := s.(*GetRequest)
id := req.GetOwnerID()
id[0]++
req.SetOwnerID(id)
},
},
},
{ // PutRequest
constructor: func() sigType {
req := new(PutRequest)
amount := decimal.New(1)
req.SetAmount(amount)
return req
},
payloadCorrupt: []func(sigType){
func(s sigType) {
req := s.(*PutRequest)
owner := req.GetOwnerID()
owner[0]++
req.SetOwnerID(owner)
},
func(s sigType) {
req := s.(*PutRequest)
mid := req.GetMessageID()
mid[0]++
req.SetMessageID(mid)
},
func(s sigType) {
req := s.(*PutRequest)
req.SetHeight(req.GetHeight() + 1)
},
func(s sigType) {
req := s.(*PutRequest)
amount := req.GetAmount()
if amount == nil {
req.SetAmount(decimal.New(0))
} else {
req.SetAmount(amount.Add(decimal.New(amount.GetValue())))
}
},
},
},
{ // ListRequest
constructor: func() sigType {
return new(ListRequest)
},
payloadCorrupt: []func(sigType){
func(s sigType) {
req := s.(*ListRequest)
owner := req.GetOwnerID()
owner[0]++
req.SetOwnerID(owner)
},
},
},
{
constructor: func() sigType {
return new(DeleteRequest)
},
payloadCorrupt: []func(sigType){
func(s sigType) {
req := s.(*DeleteRequest)
id, err := NewChequeID()
require.NoError(t, err)
req.SetID(id)
},
func(s sigType) {
req := s.(*DeleteRequest)
owner := req.GetOwnerID()
owner[0]++
req.SetOwnerID(owner)
},
func(s sigType) {
req := s.(*DeleteRequest)
mid := req.GetMessageID()
mid[0]++
req.SetMessageID(mid)
},
},
},
}
for _, item := range items {
{ // token corruptions
v := item.constructor()
token := new(service.Token)
v.SetToken(token)
require.NoError(t, service.SignDataWithSessionToken(sk, v))
require.NoError(t, service.VerifyAccumulatedSignaturesWithToken(v))
token.SetSessionKey(append(token.GetSessionKey(), 1))
require.Error(t, service.VerifyAccumulatedSignaturesWithToken(v))
}
{ // payload corruptions
for _, corruption := range item.payloadCorrupt {
v := item.constructor()
require.NoError(t, service.SignDataWithSessionToken(sk, v))
require.NoError(t, service.VerifyAccumulatedSignaturesWithToken(v))
corruption(v)
require.Error(t, service.VerifyAccumulatedSignaturesWithToken(v))
}
}
}
}

View file

@ -351,3 +351,103 @@ func (m *Settlement) Equal(s *Settlement) bool {
} }
return len(m.Transactions) == 0 || reflect.DeepEqual(m.Transactions, s.Transactions) return len(m.Transactions) == 0 || reflect.DeepEqual(m.Transactions, s.Transactions)
} }
// GetOwnerID is an OwnerID field getter.
func (m BalanceRequest) GetOwnerID() OwnerID {
return m.OwnerID
}
// SetOwnerID is an OwnerID field setter.
func (m *BalanceRequest) SetOwnerID(owner OwnerID) {
m.OwnerID = owner
}
// GetID is an ID field getter.
func (m GetRequest) GetID() ChequeID {
return m.ID
}
// SetID is an ID field setter.
func (m *GetRequest) SetID(id ChequeID) {
m.ID = id
}
// GetOwnerID is an OwnerID field getter.
func (m GetRequest) GetOwnerID() OwnerID {
return m.OwnerID
}
// SetOwnerID is an OwnerID field setter.
func (m *GetRequest) SetOwnerID(id OwnerID) {
m.OwnerID = id
}
// GetOwnerID is an OwnerID field getter.
func (m PutRequest) GetOwnerID() OwnerID {
return m.OwnerID
}
// SetOwnerID is an OwnerID field setter.
func (m *PutRequest) SetOwnerID(id OwnerID) {
m.OwnerID = id
}
// GetMessageID is a MessageID field getter.
func (m PutRequest) GetMessageID() MessageID {
return m.MessageID
}
// SetMessageID is a MessageID field setter.
func (m *PutRequest) SetMessageID(id MessageID) {
m.MessageID = id
}
// SetAmount is an Amount field setter.
func (m *PutRequest) SetAmount(amount *decimal.Decimal) {
m.Amount = amount
}
// SetHeight is a Height field setter.
func (m *PutRequest) SetHeight(h uint64) {
m.Height = h
}
// GetOwnerID is an OwnerID field getter.
func (m ListRequest) GetOwnerID() OwnerID {
return m.OwnerID
}
// SetOwnerID is an OwnerID field setter.
func (m *ListRequest) SetOwnerID(id OwnerID) {
m.OwnerID = id
}
// GetID is an ID field getter.
func (m DeleteRequest) GetID() ChequeID {
return m.ID
}
// SetID is an ID field setter.
func (m *DeleteRequest) SetID(id ChequeID) {
m.ID = id
}
// GetOwnerID is an OwnerID field getter.
func (m DeleteRequest) GetOwnerID() OwnerID {
return m.OwnerID
}
// SetOwnerID is an OwnerID field setter.
func (m *DeleteRequest) SetOwnerID(id OwnerID) {
m.OwnerID = id
}
// GetMessageID is a MessageID field getter.
func (m DeleteRequest) GetMessageID() MessageID {
return m.MessageID
}
// SetMessageID is a MessageID field setter.
func (m *DeleteRequest) SetMessageID(id MessageID) {
m.MessageID = id
}

View file

@ -84,3 +84,110 @@ func TestCheque(t *testing.T) {
require.Equal(t, cheque.Amount, decimal.NewGAS(42)) require.Equal(t, cheque.Amount, decimal.NewGAS(42))
}) })
} }
func TestBalanceRequest_SetOwnerID(t *testing.T) {
ownerID := OwnerID{1, 2, 3}
m := new(BalanceRequest)
m.SetOwnerID(ownerID)
require.Equal(t, ownerID, m.GetOwnerID())
}
func TestGetRequestGettersSetters(t *testing.T) {
t.Run("id", func(t *testing.T) {
id := ChequeID("test id")
m := new(GetRequest)
m.SetID(id)
require.Equal(t, id, m.GetID())
})
t.Run("owner", func(t *testing.T) {
id := OwnerID{1, 2, 3}
m := new(GetRequest)
m.SetOwnerID(id)
require.Equal(t, id, m.GetOwnerID())
})
}
func TestPutRequestGettersSetters(t *testing.T) {
t.Run("owner", func(t *testing.T) {
id := OwnerID{1, 2, 3}
m := new(PutRequest)
m.SetOwnerID(id)
require.Equal(t, id, m.GetOwnerID())
})
t.Run("message ID", func(t *testing.T) {
id, err := refs.NewUUID()
require.NoError(t, err)
m := new(PutRequest)
m.SetMessageID(id)
require.Equal(t, id, m.GetMessageID())
})
t.Run("amount", func(t *testing.T) {
amount := decimal.New(1)
m := new(PutRequest)
m.SetAmount(amount)
require.Equal(t, amount, m.GetAmount())
})
t.Run("height", func(t *testing.T) {
h := uint64(3)
m := new(PutRequest)
m.SetHeight(h)
require.Equal(t, h, m.GetHeight())
})
}
func TestListRequestGettersSetters(t *testing.T) {
ownerID := OwnerID{1, 2, 3}
m := new(ListRequest)
m.SetOwnerID(ownerID)
require.Equal(t, ownerID, m.GetOwnerID())
}
func TestDeleteRequestGettersSetters(t *testing.T) {
t.Run("id", func(t *testing.T) {
id := ChequeID("test id")
m := new(DeleteRequest)
m.SetID(id)
require.Equal(t, id, m.GetID())
})
t.Run("owner", func(t *testing.T) {
id := OwnerID{1, 2, 3}
m := new(DeleteRequest)
m.SetOwnerID(id)
require.Equal(t, id, m.GetOwnerID())
})
t.Run("message ID", func(t *testing.T) {
id, err := refs.NewUUID()
require.NoError(t, err)
m := new(DeleteRequest)
m.SetMessageID(id)
require.Equal(t, id, m.GetMessageID())
})
}

46
bootstrap/sign.go Normal file
View file

@ -0,0 +1,46 @@
package bootstrap
import (
"io"
"github.com/nspcc-dev/neofs-api-go/service"
)
// SignedData returns payload bytes of the request.
func (m Request) SignedData() ([]byte, error) {
return service.SignedDataFromReader(m)
}
// SignedDataSize returns payload size of the request.
func (m Request) SignedDataSize() (sz int) {
sz += m.GetType().Size()
sz += m.GetState().Size()
info := m.GetInfo()
sz += info.Size()
return
}
// ReadSignedData copies payload bytes to passed buffer.
//
// If the Request size is insufficient, io.ErrUnexpectedEOF returns.
func (m Request) ReadSignedData(p []byte) (int, error) {
if len(p) < m.SignedDataSize() {
return 0, io.ErrUnexpectedEOF
}
var off int
off += copy(p[off:], m.GetType().Bytes())
off += copy(p[off:], m.GetState().Bytes())
info := m.GetInfo()
// FIXME: implement and use stable functions
n, err := info.MarshalTo(p[off:])
off += n
return off, err
}

82
bootstrap/sign_test.go Normal file
View file

@ -0,0 +1,82 @@
package bootstrap
import (
"testing"
"github.com/nspcc-dev/neofs-api-go/service"
"github.com/nspcc-dev/neofs-crypto/test"
"github.com/stretchr/testify/require"
)
func TestRequestSign(t *testing.T) {
sk := test.DecodeKey(0)
type sigType interface {
service.SignedDataWithToken
service.SignKeyPairAccumulator
service.SignKeyPairSource
SetToken(*service.Token)
}
items := []struct {
constructor func() sigType
payloadCorrupt []func(sigType)
}{
{ // Request
constructor: func() sigType {
return new(Request)
},
payloadCorrupt: []func(sigType){
func(s sigType) {
req := s.(*Request)
req.SetType(req.GetType() + 1)
},
func(s sigType) {
req := s.(*Request)
req.SetState(req.GetState() + 1)
},
func(s sigType) {
req := s.(*Request)
info := req.GetInfo()
info.Address += "1"
req.SetInfo(info)
},
},
},
}
for _, item := range items {
{ // token corruptions
v := item.constructor()
token := new(service.Token)
v.SetToken(token)
require.NoError(t, service.SignDataWithSessionToken(sk, v))
require.NoError(t, service.VerifyAccumulatedSignaturesWithToken(v))
token.SetSessionKey(append(token.GetSessionKey(), 1))
require.Error(t, service.VerifyAccumulatedSignaturesWithToken(v))
}
{ // payload corruptions
for _, corruption := range item.payloadCorrupt {
v := item.constructor()
require.NoError(t, service.SignDataWithSessionToken(sk, v))
require.NoError(t, service.VerifyAccumulatedSignaturesWithToken(v))
corruption(v)
require.Error(t, service.VerifyAccumulatedSignaturesWithToken(v))
}
}
}
}

View file

@ -2,6 +2,7 @@ package bootstrap
import ( import (
"bytes" "bytes"
"encoding/binary"
"encoding/hex" "encoding/hex"
"strconv" "strconv"
"strings" "strings"
@ -27,6 +28,8 @@ var (
_ proto.Message = (*SpreadMap)(nil) _ proto.Message = (*SpreadMap)(nil)
) )
var requestEndianness = binary.BigEndian
// Equals checks whether two NodeInfo has same address. // Equals checks whether two NodeInfo has same address.
func (m NodeInfo) Equals(n1 NodeInfo) bool { func (m NodeInfo) Equals(n1 NodeInfo) bool {
return m.Address == n1.Address && bytes.Equal(m.PubKey, n1.PubKey) return m.Address == n1.Address && bytes.Equal(m.PubKey, n1.PubKey)
@ -98,3 +101,37 @@ func (m SpreadMap) String() string {
", " + ", " +
"Netmap: [" + strings.Join(result, ",") + "]>" "Netmap: [" + strings.Join(result, ",") + "]>"
} }
// GetType is a Type field getter.
func (m Request) GetType() NodeType {
return m.Type
}
// SetType is a Type field setter.
func (m *Request) SetType(t NodeType) {
m.Type = t
}
// SetState is a State field setter.
func (m *Request) SetState(state Request_State) {
m.State = state
}
// SetInfo is an Info field getter.
func (m *Request) SetInfo(info NodeInfo) {
m.Info = info
}
// Size returns the size necessary for a binary representation of the state.
func (x Request_State) Size() int {
return 4
}
// Bytes returns a binary representation of the state.
func (x Request_State) Bytes() []byte {
data := make([]byte, x.Size())
requestEndianness.PutUint32(data, uint32(x))
return data
}

39
bootstrap/types_test.go Normal file
View file

@ -0,0 +1,39 @@
package bootstrap
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestRequestGettersSetters(t *testing.T) {
t.Run("type", func(t *testing.T) {
rt := NodeType(1)
m := new(Request)
m.SetType(rt)
require.Equal(t, rt, m.GetType())
})
t.Run("state", func(t *testing.T) {
st := Request_State(1)
m := new(Request)
m.SetState(st)
require.Equal(t, st, m.GetState())
})
t.Run("info", func(t *testing.T) {
info := NodeInfo{
Address: "some address",
}
m := new(Request)
m.SetInfo(info)
require.Equal(t, info, m.GetInfo())
})
}

137
container/sign.go Normal file
View file

@ -0,0 +1,137 @@
package container
import (
"encoding/binary"
"io"
service "github.com/nspcc-dev/neofs-api-go/service"
)
var requestEndianness = binary.BigEndian
// SignedData returns payload bytes of the request.
func (m PutRequest) SignedData() ([]byte, error) {
return service.SignedDataFromReader(m)
}
// SignedDataSize returns payload size of the request.
func (m PutRequest) SignedDataSize() (sz int) {
sz += m.GetMessageID().Size()
sz += 8
sz += m.GetOwnerID().Size()
rules := m.GetRules()
sz += rules.Size()
sz += 4
return
}
// ReadSignedData copies payload bytes to passed buffer.
//
// If the Request size is insufficient, io.ErrUnexpectedEOF returns.
func (m PutRequest) ReadSignedData(p []byte) (int, error) {
if len(p) < m.SignedDataSize() {
return 0, io.ErrUnexpectedEOF
}
var off int
off += copy(p[off:], m.GetMessageID().Bytes())
requestEndianness.PutUint64(p[off:], m.GetCapacity())
off += 8
off += copy(p[off:], m.GetOwnerID().Bytes())
rules := m.GetRules()
// FIXME: implement and use stable functions
n, err := rules.MarshalTo(p[off:])
off += n
if err != nil {
return off, err
}
requestEndianness.PutUint32(p[off:], m.GetBasicACL())
off += 4
return off, nil
}
// SignedData returns payload bytes of the request.
func (m DeleteRequest) SignedData() ([]byte, error) {
return service.SignedDataFromReader(m)
}
// SignedDataSize returns payload size of the request.
func (m DeleteRequest) SignedDataSize() int {
return m.GetCID().Size()
}
// ReadSignedData copies payload bytes to passed buffer.
//
// If the Request size is insufficient, io.ErrUnexpectedEOF returns.
func (m DeleteRequest) ReadSignedData(p []byte) (int, error) {
if len(p) < m.SignedDataSize() {
return 0, io.ErrUnexpectedEOF
}
var off int
off += copy(p[off:], m.GetCID().Bytes())
return off, nil
}
// SignedData returns payload bytes of the request.
func (m GetRequest) SignedData() ([]byte, error) {
return service.SignedDataFromReader(m)
}
// SignedDataSize returns payload size of the request.
func (m GetRequest) SignedDataSize() int {
return m.GetCID().Size()
}
// ReadSignedData copies payload bytes to passed buffer.
//
// If the Request size is insufficient, io.ErrUnexpectedEOF returns.
func (m GetRequest) ReadSignedData(p []byte) (int, error) {
if len(p) < m.SignedDataSize() {
return 0, io.ErrUnexpectedEOF
}
var off int
off += copy(p[off:], m.GetCID().Bytes())
return off, nil
}
// SignedData returns payload bytes of the request.
func (m ListRequest) SignedData() ([]byte, error) {
return service.SignedDataFromReader(m)
}
// SignedDataSize returns payload size of the request.
func (m ListRequest) SignedDataSize() int {
return m.GetOwnerID().Size()
}
// ReadSignedData copies payload bytes to passed buffer.
//
// If the Request size is insufficient, io.ErrUnexpectedEOF returns.
func (m ListRequest) ReadSignedData(p []byte) (int, error) {
if len(p) < m.SignedDataSize() {
return 0, io.ErrUnexpectedEOF
}
var off int
off += copy(p[off:], m.GetOwnerID().Bytes())
return off, nil
}

143
container/sign_test.go Normal file
View file

@ -0,0 +1,143 @@
package container
import (
"testing"
"github.com/nspcc-dev/neofs-api-go/service"
"github.com/nspcc-dev/neofs-crypto/test"
"github.com/stretchr/testify/require"
)
func TestRequestSign(t *testing.T) {
sk := test.DecodeKey(0)
type sigType interface {
service.SignedDataWithToken
service.SignKeyPairAccumulator
service.SignKeyPairSource
SetToken(*service.Token)
}
items := []struct {
constructor func() sigType
payloadCorrupt []func(sigType)
}{
{ // PutRequest
constructor: func() sigType {
return new(PutRequest)
},
payloadCorrupt: []func(sigType){
func(s sigType) {
req := s.(*PutRequest)
id := req.GetMessageID()
id[0]++
req.SetMessageID(id)
},
func(s sigType) {
req := s.(*PutRequest)
req.SetCapacity(req.GetCapacity() + 1)
},
func(s sigType) {
req := s.(*PutRequest)
owner := req.GetOwnerID()
owner[0]++
req.SetOwnerID(owner)
},
func(s sigType) {
req := s.(*PutRequest)
rules := req.GetRules()
rules.ReplFactor++
req.SetRules(rules)
},
func(s sigType) {
req := s.(*PutRequest)
req.SetBasicACL(req.GetBasicACL() + 1)
},
},
},
{ // DeleteRequest
constructor: func() sigType {
return new(DeleteRequest)
},
payloadCorrupt: []func(sigType){
func(s sigType) {
req := s.(*DeleteRequest)
cid := req.GetCID()
cid[0]++
req.SetCID(cid)
},
},
},
{ // GetRequest
constructor: func() sigType {
return new(GetRequest)
},
payloadCorrupt: []func(sigType){
func(s sigType) {
req := s.(*GetRequest)
cid := req.GetCID()
cid[0]++
req.SetCID(cid)
},
},
},
{ // ListRequest
constructor: func() sigType {
return new(ListRequest)
},
payloadCorrupt: []func(sigType){
func(s sigType) {
req := s.(*ListRequest)
owner := req.GetOwnerID()
owner[0]++
req.SetOwnerID(owner)
},
},
},
}
for _, item := range items {
{ // token corruptions
v := item.constructor()
token := new(service.Token)
v.SetToken(token)
require.NoError(t, service.SignDataWithSessionToken(sk, v))
require.NoError(t, service.VerifyAccumulatedSignaturesWithToken(v))
token.SetSessionKey(append(token.GetSessionKey(), 1))
require.Error(t, service.VerifyAccumulatedSignaturesWithToken(v))
}
{ // payload corruptions
for _, corruption := range item.payloadCorrupt {
v := item.constructor()
require.NoError(t, service.SignDataWithSessionToken(sk, v))
require.NoError(t, service.VerifyAccumulatedSignaturesWithToken(v))
corruption(v)
require.Error(t, service.VerifyAccumulatedSignaturesWithToken(v))
}
}
}
}

View file

@ -93,3 +93,68 @@ func NewTestContainer() (*Container, error) {
}, },
}) })
} }
// GetMessageID is a MessageID field getter.
func (m PutRequest) GetMessageID() MessageID {
return m.MessageID
}
// SetMessageID is a MessageID field getter.
func (m *PutRequest) SetMessageID(id MessageID) {
m.MessageID = id
}
// SetCapacity is a Capacity field setter.
func (m *PutRequest) SetCapacity(c uint64) {
m.Capacity = c
}
// GetOwnerID is an OwnerID field getter.
func (m PutRequest) GetOwnerID() OwnerID {
return m.OwnerID
}
// SetOwnerID is an OwnerID field setter.
func (m *PutRequest) SetOwnerID(owner OwnerID) {
m.OwnerID = owner
}
// SetRules is a Rules field setter.
func (m *PutRequest) SetRules(rules netmap.PlacementRule) {
m.Rules = rules
}
// SetBasicACL is a BasicACL field setter.
func (m *PutRequest) SetBasicACL(acl uint32) {
m.BasicACL = acl
}
// GetCID is a CID field getter.
func (m DeleteRequest) GetCID() CID {
return m.CID
}
// SetCID is a CID field setter.
func (m *DeleteRequest) SetCID(cid CID) {
m.CID = cid
}
// GetCID is a CID field getter.
func (m GetRequest) GetCID() CID {
return m.CID
}
// SetCID is a CID field setter.
func (m *GetRequest) SetCID(cid CID) {
m.CID = cid
}
// GetOwnerID is an OwnerID field getter.
func (m ListRequest) GetOwnerID() OwnerID {
return m.OwnerID
}
// SetOwnerID is an OwnerID field setter.
func (m *ListRequest) SetOwnerID(owner OwnerID) {
m.OwnerID = owner
}

View file

@ -55,3 +55,88 @@ func TestCID(t *testing.T) {
require.Equal(t, cid1, cid2) require.Equal(t, cid1, cid2)
}) })
} }
func TestPutRequestGettersSetters(t *testing.T) {
t.Run("owner", func(t *testing.T) {
owner := OwnerID{1, 2, 3}
m := new(PutRequest)
m.SetOwnerID(owner)
require.Equal(t, owner, m.GetOwnerID())
})
t.Run("capacity", func(t *testing.T) {
cp := uint64(3)
m := new(PutRequest)
m.SetCapacity(cp)
require.Equal(t, cp, m.GetCapacity())
})
t.Run("message ID", func(t *testing.T) {
id, err := refs.NewUUID()
require.NoError(t, err)
m := new(PutRequest)
m.SetMessageID(id)
require.Equal(t, id, m.GetMessageID())
})
t.Run("rules", func(t *testing.T) {
rules := netmap.PlacementRule{
ReplFactor: 1,
}
m := new(PutRequest)
m.SetRules(rules)
require.Equal(t, rules, m.GetRules())
})
t.Run("basic ACL", func(t *testing.T) {
bACL := uint32(5)
m := new(PutRequest)
m.SetBasicACL(bACL)
require.Equal(t, bACL, m.GetBasicACL())
})
}
func TestDeleteRequestGettersSetters(t *testing.T) {
t.Run("cid", func(t *testing.T) {
cid := CID{1, 2, 3}
m := new(DeleteRequest)
m.SetCID(cid)
require.Equal(t, cid, m.GetCID())
})
}
func TestGetRequestGettersSetters(t *testing.T) {
t.Run("cid", func(t *testing.T) {
cid := CID{1, 2, 3}
m := new(GetRequest)
m.SetCID(cid)
require.Equal(t, cid, m.GetCID())
})
}
func TestListRequestGettersSetters(t *testing.T) {
t.Run("owner", func(t *testing.T) {
owner := OwnerID{1, 2, 3}
m := new(PutRequest)
m.SetOwnerID(owner)
require.Equal(t, owner, m.GetOwnerID())
})
}

View file

@ -3,34 +3,27 @@ package object
import ( import (
"encoding/binary" "encoding/binary"
"io" "io"
"github.com/nspcc-dev/neofs-api-go/service"
) )
// SignedData returns payload bytes of the request. // SignedData returns payload bytes of the request.
// //
// If payload is nil, ErrHeaderNotFound returns. // If payload is nil, ErrHeaderNotFound returns.
func (m PutRequest) SignedData() ([]byte, error) { func (m PutRequest) SignedData() ([]byte, error) {
sz := m.SignedDataSize() return service.SignedDataFromReader(m)
if sz < 0 {
return nil, ErrHeaderNotFound
}
data := make([]byte, sz)
return data, m.ReadSignedData(data)
} }
// ReadSignedData copies payload bytes to passed buffer. // ReadSignedData copies payload bytes to passed buffer.
// //
// If the buffer size is insufficient, io.ErrUnexpectedEOF returns. // If the buffer size is insufficient, io.ErrUnexpectedEOF returns.
func (m PutRequest) ReadSignedData(p []byte) error { func (m PutRequest) ReadSignedData(p []byte) (int, error) {
r := m.GetR() r := m.GetR()
if r == nil { if r == nil {
return ErrHeaderNotFound return 0, ErrHeaderNotFound
} }
_, err := r.MarshalTo(p) return r.MarshalTo(p)
return err
} }
// SignedDataSize returns the size of payload of the Put request. // SignedDataSize returns the size of payload of the Put request.
@ -47,26 +40,26 @@ func (m PutRequest) SignedDataSize() int {
// SignedData returns payload bytes of the request. // SignedData returns payload bytes of the request.
func (m GetRequest) SignedData() ([]byte, error) { func (m GetRequest) SignedData() ([]byte, error) {
data := make([]byte, m.SignedDataSize()) return service.SignedDataFromReader(m)
return data, m.ReadSignedData(data)
} }
// ReadSignedData copies payload bytes to passed buffer. // ReadSignedData copies payload bytes to passed buffer.
// //
// If the buffer size is insufficient, io.ErrUnexpectedEOF returns. // If the buffer size is insufficient, io.ErrUnexpectedEOF returns.
func (m GetRequest) ReadSignedData(p []byte) error { func (m GetRequest) ReadSignedData(p []byte) (int, error) {
addr := m.GetAddress() addr := m.GetAddress()
if len(p) < m.SignedDataSize() { if len(p) < m.SignedDataSize() {
return io.ErrUnexpectedEOF return 0, io.ErrUnexpectedEOF
} }
off := copy(p, addr.CID.Bytes()) var off int
copy(p[off:], addr.ObjectID.Bytes()) off += copy(p[off:], addr.CID.Bytes())
return nil off += copy(p[off:], addr.ObjectID.Bytes())
return off, nil
} }
// SignedDataSize returns payload size of the request. // SignedDataSize returns payload size of the request.
@ -76,28 +69,28 @@ func (m GetRequest) SignedDataSize() int {
// SignedData returns payload bytes of the request. // SignedData returns payload bytes of the request.
func (m HeadRequest) SignedData() ([]byte, error) { func (m HeadRequest) SignedData() ([]byte, error) {
data := make([]byte, m.SignedDataSize()) return service.SignedDataFromReader(m)
return data, m.ReadSignedData(data)
} }
// ReadSignedData copies payload bytes to passed buffer. // ReadSignedData copies payload bytes to passed buffer.
// //
// If the buffer size is insufficient, io.ErrUnexpectedEOF returns. // If the buffer size is insufficient, io.ErrUnexpectedEOF returns.
func (m HeadRequest) ReadSignedData(p []byte) error { func (m HeadRequest) ReadSignedData(p []byte) (int, error) {
if len(p) < m.SignedDataSize() { if len(p) < m.SignedDataSize() {
return io.ErrUnexpectedEOF return 0, io.ErrUnexpectedEOF
} }
if m.GetFullHeaders() { if m.GetFullHeaders() {
p[0] = 1 p[0] = 1
} }
off := 1 + copy(p[1:], m.Address.CID.Bytes()) off := 1
copy(p[off:], m.Address.ObjectID.Bytes()) off += copy(p[off:], m.Address.CID.Bytes())
return nil off += copy(p[off:], m.Address.ObjectID.Bytes())
return off, nil
} }
// SignedDataSize returns payload size of the request. // SignedDataSize returns payload size of the request.
@ -107,24 +100,24 @@ func (m HeadRequest) SignedDataSize() int {
// SignedData returns payload bytes of the request. // SignedData returns payload bytes of the request.
func (m DeleteRequest) SignedData() ([]byte, error) { func (m DeleteRequest) SignedData() ([]byte, error) {
data := make([]byte, m.SignedDataSize()) return service.SignedDataFromReader(m)
return data, m.ReadSignedData(data)
} }
// ReadSignedData copies payload bytes to passed buffer. // ReadSignedData copies payload bytes to passed buffer.
// //
// If the buffer size is insufficient, io.ErrUnexpectedEOF returns. // If the buffer size is insufficient, io.ErrUnexpectedEOF returns.
func (m DeleteRequest) ReadSignedData(p []byte) error { func (m DeleteRequest) ReadSignedData(p []byte) (int, error) {
if len(p) < m.SignedDataSize() { if len(p) < m.SignedDataSize() {
return io.ErrUnexpectedEOF return 0, io.ErrUnexpectedEOF
} }
off := copy(p, m.OwnerID.Bytes()) var off int
copy(p[off:], addressBytes(m.Address)) off += copy(p[off:], m.OwnerID.Bytes())
return nil off += copy(p[off:], addressBytes(m.Address))
return off, nil
} }
// SignedDataSize returns payload size of the request. // SignedDataSize returns payload size of the request.
@ -134,27 +127,25 @@ func (m DeleteRequest) SignedDataSize() int {
// SignedData returns payload bytes of the request. // SignedData returns payload bytes of the request.
func (m GetRangeRequest) SignedData() ([]byte, error) { func (m GetRangeRequest) SignedData() ([]byte, error) {
data := make([]byte, m.SignedDataSize()) return service.SignedDataFromReader(m)
return data, m.ReadSignedData(data)
} }
// ReadSignedData copies payload bytes to passed buffer. // ReadSignedData copies payload bytes to passed buffer.
// //
// If the buffer size is insufficient, io.ErrUnexpectedEOF returns. // If the buffer size is insufficient, io.ErrUnexpectedEOF returns.
func (m GetRangeRequest) ReadSignedData(p []byte) error { func (m GetRangeRequest) ReadSignedData(p []byte) (int, error) {
if len(p) < m.SignedDataSize() { if len(p) < m.SignedDataSize() {
return io.ErrUnexpectedEOF return 0, io.ErrUnexpectedEOF
} }
n, err := (&m.Range).MarshalTo(p) n, err := (&m.Range).MarshalTo(p)
if err != nil { if err != nil {
return err return 0, err
} }
copy(p[n:], addressBytes(m.GetAddress())) n += copy(p[n:], addressBytes(m.GetAddress()))
return nil return n, nil
} }
// SignedDataSize returns payload size of the request. // SignedDataSize returns payload size of the request.
@ -164,17 +155,15 @@ func (m GetRangeRequest) SignedDataSize() int {
// SignedData returns payload bytes of the request. // SignedData returns payload bytes of the request.
func (m GetRangeHashRequest) SignedData() ([]byte, error) { func (m GetRangeHashRequest) SignedData() ([]byte, error) {
data := make([]byte, m.SignedDataSize()) return service.SignedDataFromReader(m)
return data, m.ReadSignedData(data)
} }
// ReadSignedData copies payload bytes to passed buffer. // ReadSignedData copies payload bytes to passed buffer.
// //
// If the buffer size is insufficient, io.ErrUnexpectedEOF returns. // If the buffer size is insufficient, io.ErrUnexpectedEOF returns.
func (m GetRangeHashRequest) ReadSignedData(p []byte) error { func (m GetRangeHashRequest) ReadSignedData(p []byte) (int, error) {
if len(p) < m.SignedDataSize() { if len(p) < m.SignedDataSize() {
return io.ErrUnexpectedEOF return 0, io.ErrUnexpectedEOF
} }
var off int var off int
@ -185,7 +174,7 @@ func (m GetRangeHashRequest) ReadSignedData(p []byte) error {
off += copy(p[off:], m.GetSalt()) off += copy(p[off:], m.GetSalt())
return nil return off, nil
} }
// SignedDataSize returns payload size of the request. // SignedDataSize returns payload size of the request.
@ -203,17 +192,15 @@ func (m GetRangeHashRequest) SignedDataSize() int {
// SignedData returns payload bytes of the request. // SignedData returns payload bytes of the request.
func (m SearchRequest) SignedData() ([]byte, error) { func (m SearchRequest) SignedData() ([]byte, error) {
data := make([]byte, m.SignedDataSize()) return service.SignedDataFromReader(m)
return data, m.ReadSignedData(data)
} }
// ReadSignedData copies payload bytes to passed buffer. // ReadSignedData copies payload bytes to passed buffer.
// //
// If the buffer size is insufficient, io.ErrUnexpectedEOF returns. // If the buffer size is insufficient, io.ErrUnexpectedEOF returns.
func (m SearchRequest) ReadSignedData(p []byte) error { func (m SearchRequest) ReadSignedData(p []byte) (int, error) {
if len(p) < m.SignedDataSize() { if len(p) < m.SignedDataSize() {
return io.ErrUnexpectedEOF return 0, io.ErrUnexpectedEOF
} }
var off int var off int
@ -223,9 +210,9 @@ func (m SearchRequest) ReadSignedData(p []byte) error {
binary.BigEndian.PutUint32(p[off:], m.GetQueryVersion()) binary.BigEndian.PutUint32(p[off:], m.GetQueryVersion())
off += 4 off += 4
copy(p[off:], m.GetQuery()) off += copy(p[off:], m.GetQuery())
return nil return off, nil
} }
// SignedDataSize returns payload size of the request. // SignedDataSize returns payload size of the request.

View file

@ -43,3 +43,7 @@ const ErrNilDataWithTokenSignAccumulator = internal.Error("signed data with toke
// ErrNilSignatureKeySourceWithToken is returned by functions that expect // ErrNilSignatureKeySourceWithToken is returned by functions that expect
// a non-nil SignatureKeySourceWithToken, but received nil. // a non-nil SignatureKeySourceWithToken, but received nil.
const ErrNilSignatureKeySourceWithToken = internal.Error("key-signature source with token is nil") const ErrNilSignatureKeySourceWithToken = internal.Error("key-signature source with token is nil")
// ErrNilSignedDataReader is returned by functions that expect
// a non-nil SignedDataReader, but received nil.
const ErrNilSignedDataReader = internal.Error("signed data reader is nil")

View file

@ -1,5 +1,7 @@
package service package service
import "encoding/binary"
const ( const (
_ NodeRole = iota _ NodeRole = iota
// InnerRingNode that work like IR node. // InnerRingNode that work like IR node.
@ -19,3 +21,17 @@ func (nt NodeRole) String() string {
return "Unknown" return "Unknown"
} }
} }
// Size returns the size necessary for a binary representation of the NodeRole.
func (nt NodeRole) Size() int {
return 4
}
// Bytes returns a binary representation of the NodeRole.
func (nt NodeRole) Bytes() []byte {
data := make([]byte, nt.Size())
binary.BigEndian.PutUint32(data, uint32(nt))
return data
}

View file

@ -123,11 +123,7 @@ func (m *Token) AddSignKey(sig []byte, _ *ecdsa.PublicKey) {
// SignedData returns token information in a binary representation. // SignedData returns token information in a binary representation.
func (m *Token) SignedData() ([]byte, error) { func (m *Token) SignedData() ([]byte, error) {
data := make([]byte, m.SignedDataSize()) return SignedDataFromReader(m)
copyTokenSignedData(data, m)
return data, nil
} }
// ReadSignedData copies a binary representation of the token information to passed buffer. // ReadSignedData copies a binary representation of the token information to passed buffer.

18
service/utils.go Normal file
View file

@ -0,0 +1,18 @@
package service
// SignedDataFromReader allocates buffer and reads bytes from passed reader to it.
//
// If passed SignedDataReader is nil, ErrNilSignedDataReader returns.
func SignedDataFromReader(r SignedDataReader) ([]byte, error) {
if r == nil {
return nil, ErrNilSignedDataReader
}
data := make([]byte, r.SignedDataSize())
if _, err := r.ReadSignedData(data); err != nil {
return nil, err
}
return data, nil
}

34
service/utils_test.go Normal file
View file

@ -0,0 +1,34 @@
package service
import (
"testing"
"github.com/pkg/errors"
"github.com/stretchr/testify/require"
)
func TestSignedDataFromReader(t *testing.T) {
// nil SignedDataReader
_, err := SignedDataFromReader(nil)
require.EqualError(t, err, ErrNilSignedDataReader.Error())
rdr := &testSignedDataReader{
testSignedDataSrc: new(testSignedDataSrc),
}
// make reader to return an error
rdr.err = errors.New("test error")
_, err = SignedDataFromReader(rdr)
require.EqualError(t, err, rdr.err.Error())
// remove the error
rdr.err = nil
// fill the data
rdr.data = testData(t, 10)
res, err := SignedDataFromReader(rdr)
require.NoError(t, err)
require.Equal(t, rdr.data, res)
}

View file

@ -5,6 +5,7 @@ import (
"io" "io"
"github.com/nspcc-dev/neofs-api-go/refs" "github.com/nspcc-dev/neofs-api-go/refs"
"github.com/nspcc-dev/neofs-api-go/service"
) )
const signedRequestDataSize = 0 + const signedRequestDataSize = 0 +
@ -31,14 +32,7 @@ func (m *CreateRequest) SetOwnerID(id OwnerID) {
// SignedData returns payload bytes of the request. // SignedData returns payload bytes of the request.
func (m CreateRequest) SignedData() ([]byte, error) { func (m CreateRequest) SignedData() ([]byte, error) {
data := make([]byte, m.SignedDataSize()) return service.SignedDataFromReader(m)
_, err := m.ReadSignedData(data)
if err != nil {
return nil, err
}
return data, nil
} }
// SignedDataSize returns payload size of the request. // SignedDataSize returns payload size of the request.

67
state/sign.go Normal file
View file

@ -0,0 +1,67 @@
package state
import (
"io"
"github.com/nspcc-dev/neofs-api-go/service"
)
// SignedData returns payload bytes of the request.
//
// Always returns an empty slice.
func (m NetmapRequest) SignedData() ([]byte, error) {
return make([]byte, 0), nil
}
// SignedData returns payload bytes of the request.
//
// Always returns an empty slice.
func (m MetricsRequest) SignedData() ([]byte, error) {
return make([]byte, 0), nil
}
// SignedData returns payload bytes of the request.
//
// Always returns an empty slice.
func (m HealthRequest) SignedData() ([]byte, error) {
return make([]byte, 0), nil
}
// SignedData returns payload bytes of the request.
//
// Always returns an empty slice.
func (m DumpRequest) SignedData() ([]byte, error) {
return make([]byte, 0), nil
}
// SignedData returns payload bytes of the request.
//
// Always returns an empty slice.
func (m DumpVarsRequest) SignedData() ([]byte, error) {
return make([]byte, 0), nil
}
// SignedData returns payload bytes of the request.
func (m ChangeStateRequest) SignedData() ([]byte, error) {
return service.SignedDataFromReader(m)
}
// SignedDataSize returns payload size of the request.
func (m ChangeStateRequest) SignedDataSize() int {
return m.GetState().Size()
}
// ReadSignedData copies payload bytes to passed buffer.
//
// If the Request size is insufficient, io.ErrUnexpectedEOF returns.
func (m ChangeStateRequest) ReadSignedData(p []byte) (int, error) {
if len(p) < m.SignedDataSize() {
return 0, io.ErrUnexpectedEOF
}
var off int
off += copy(p[off:], m.GetState().Bytes())
return off, nil
}

94
state/sign_test.go Normal file
View file

@ -0,0 +1,94 @@
package state
import (
"testing"
"github.com/nspcc-dev/neofs-api-go/service"
"github.com/nspcc-dev/neofs-crypto/test"
"github.com/stretchr/testify/require"
)
func TestRequestSign(t *testing.T) {
sk := test.DecodeKey(0)
type sigType interface {
service.SignedDataWithToken
service.SignKeyPairAccumulator
service.SignKeyPairSource
SetToken(*service.Token)
}
items := []struct {
constructor func() sigType
payloadCorrupt []func(sigType)
}{
{ // NetmapRequest
constructor: func() sigType {
return new(NetmapRequest)
},
},
{ // MetricsRequest
constructor: func() sigType {
return new(MetricsRequest)
},
},
{ // HealthRequest
constructor: func() sigType {
return new(HealthRequest)
},
},
{ // DumpRequest
constructor: func() sigType {
return new(DumpRequest)
},
},
{ // DumpVarsRequest
constructor: func() sigType {
return new(DumpVarsRequest)
},
},
{
constructor: func() sigType {
return new(ChangeStateRequest)
},
payloadCorrupt: []func(sigType){
func(s sigType) {
req := s.(*ChangeStateRequest)
req.SetState(req.GetState() + 1)
},
},
},
}
for _, item := range items {
{ // token corruptions
v := item.constructor()
token := new(service.Token)
v.SetToken(token)
require.NoError(t, service.SignDataWithSessionToken(sk, v))
require.NoError(t, service.VerifyAccumulatedSignaturesWithToken(v))
token.SetSessionKey(append(token.GetSessionKey(), 1))
require.Error(t, service.VerifyAccumulatedSignaturesWithToken(v))
}
{ // payload corruptions
for _, corruption := range item.payloadCorrupt {
v := item.constructor()
require.NoError(t, service.SignDataWithSessionToken(sk, v))
require.NoError(t, service.VerifyAccumulatedSignaturesWithToken(v))
corruption(v)
require.Error(t, service.VerifyAccumulatedSignaturesWithToken(v))
}
}
}
}

24
state/types.go Normal file
View file

@ -0,0 +1,24 @@
package state
import (
"encoding/binary"
)
// SetState is a State field setter.
func (m *ChangeStateRequest) SetState(st ChangeStateRequest_State) {
m.State = st
}
// Size returns the size of the state binary representation.
func (ChangeStateRequest_State) Size() int {
return 4
}
// Bytes returns the state binary representation.
func (x ChangeStateRequest_State) Bytes() []byte {
data := make([]byte, x.Size())
binary.BigEndian.PutUint32(data, uint32(x))
return data
}

18
state/types_test.go Normal file
View file

@ -0,0 +1,18 @@
package state
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestChangeStateRequestGettersSetters(t *testing.T) {
t.Run("state", func(t *testing.T) {
st := ChangeStateRequest_State(1)
m := new(ChangeStateRequest)
m.SetState(st)
require.Equal(t, st, m.GetState())
})
}