2019-11-18 16:22:08 +00:00
|
|
|
package service
|
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/ecdsa"
|
2019-12-20 07:13:16 +00:00
|
|
|
"sync"
|
2019-11-18 16:22:08 +00:00
|
|
|
|
2020-03-31 07:05:26 +00:00
|
|
|
"github.com/nspcc-dev/neofs-api-go/internal"
|
|
|
|
"github.com/nspcc-dev/neofs-api-go/refs"
|
2019-11-18 16:22:08 +00:00
|
|
|
crypto "github.com/nspcc-dev/neofs-crypto"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
)
|
|
|
|
|
|
|
|
type (
|
2019-11-19 16:03:42 +00:00
|
|
|
// VerifiableRequest adds possibility to sign and verify request header.
|
2019-11-18 16:22:08 +00:00
|
|
|
VerifiableRequest interface {
|
2019-12-20 07:13:16 +00:00
|
|
|
Size() int
|
|
|
|
MarshalTo([]byte) (int, error)
|
2019-11-18 16:22:08 +00:00
|
|
|
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)
|
|
|
|
}
|
2020-04-28 13:02:40 +00:00
|
|
|
|
|
|
|
// TokenHeader is an interface of the container of a Token pointer value
|
|
|
|
TokenHeader interface {
|
|
|
|
GetToken() *Token
|
|
|
|
SetToken(*Token)
|
|
|
|
}
|
2019-11-18 16:22:08 +00:00
|
|
|
)
|
|
|
|
|
2020-05-06 09:50:15 +00:00
|
|
|
// GetSessionToken returns SessionToken interface of Token field.
|
|
|
|
//
|
|
|
|
// If token field value is nil, nil returns.
|
|
|
|
func (m RequestVerificationHeader) GetSessionToken() SessionToken {
|
|
|
|
if t := m.GetToken(); t != nil {
|
|
|
|
return t
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// AddSignKey adds new element to Signatures field.
|
|
|
|
//
|
|
|
|
// Sets Sign field to passed sign. Set Peer field to marshaled passed key.
|
|
|
|
func (m *RequestVerificationHeader) AddSignKey(sign []byte, key *ecdsa.PublicKey) {
|
|
|
|
m.SetSignatures(
|
|
|
|
append(
|
|
|
|
m.GetSignatures(),
|
|
|
|
&RequestVerificationHeader_Signature{
|
|
|
|
Sign: sign,
|
|
|
|
Peer: crypto.MarshalPublicKey(key),
|
|
|
|
},
|
|
|
|
),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetSignKeyPairs returns the elements of Signatures field as SignKeyPair slice.
|
|
|
|
func (m RequestVerificationHeader) GetSignKeyPairs() []SignKeyPair {
|
|
|
|
var (
|
|
|
|
signs = m.GetSignatures()
|
|
|
|
res = make([]SignKeyPair, len(signs))
|
|
|
|
)
|
|
|
|
|
|
|
|
for i := range signs {
|
|
|
|
res[i] = signs[i]
|
|
|
|
}
|
|
|
|
|
|
|
|
return res
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetSignature returns the result of a Sign field getter.
|
|
|
|
func (m RequestVerificationHeader_Signature) GetSignature() []byte {
|
|
|
|
return m.GetSign()
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetPublicKey unmarshals and returns the result of a Peer field getter.
|
|
|
|
func (m RequestVerificationHeader_Signature) GetPublicKey() *ecdsa.PublicKey {
|
|
|
|
return crypto.UnmarshalPublicKey(m.GetPeer())
|
|
|
|
}
|
|
|
|
|
2019-11-19 16:03:42 +00:00
|
|
|
// SetSignatures replaces signatures stored in RequestVerificationHeader.
|
2019-11-18 16:22:08 +00:00
|
|
|
func (m *RequestVerificationHeader) SetSignatures(signatures []*RequestVerificationHeader_Signature) {
|
|
|
|
m.Signatures = signatures
|
|
|
|
}
|
|
|
|
|
2019-11-19 16:03:42 +00:00
|
|
|
// AddSignature adds new Signature into RequestVerificationHeader.
|
2019-11-18 16:22:08 +00:00
|
|
|
func (m *RequestVerificationHeader) AddSignature(sig *RequestVerificationHeader_Signature) {
|
|
|
|
if sig == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
m.Signatures = append(m.Signatures, sig)
|
|
|
|
}
|
|
|
|
|
2019-11-26 10:34:16 +00:00
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
2019-11-18 16:22:08 +00:00
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
2020-05-04 10:04:10 +00:00
|
|
|
return nil, ErrInvalidPublicKeyBytes
|
2019-11-18 16:22:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
2020-05-04 10:04:10 +00:00
|
|
|
return nil, ErrInvalidPublicKeyBytes
|
2019-11-18 16:22:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-28 13:02:40 +00:00
|
|
|
// SetToken is a Token field setter.
|
|
|
|
func (m *RequestVerificationHeader) SetToken(token *Token) {
|
|
|
|
m.Token = token
|
|
|
|
}
|
|
|
|
|
2019-11-18 16:22:08 +00:00
|
|
|
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{
|
2020-04-28 10:09:18 +00:00
|
|
|
Sign: sign,
|
|
|
|
Peer: crypto.MarshalPublicKey(&key.PublicKey),
|
2019-11-18 16:22:08 +00:00
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2019-12-20 07:13:16 +00:00
|
|
|
var bytesPool = sync.Pool{New: func() interface{} {
|
|
|
|
return make([]byte, 4.5*1024*1024) // 4.5MB
|
|
|
|
}}
|
|
|
|
|
2019-11-18 16:22:08 +00:00
|
|
|
// 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.
|
2019-11-19 16:03:42 +00:00
|
|
|
func SignRequestHeader(key *ecdsa.PrivateKey, msg VerifiableRequest) error {
|
2019-11-18 16:22:08 +00:00
|
|
|
// ignore meta header
|
2020-05-04 11:37:14 +00:00
|
|
|
if meta, ok := msg.(SeizedRequestMetaContainer); ok {
|
|
|
|
h := meta.CutMeta()
|
2019-11-19 16:03:42 +00:00
|
|
|
|
|
|
|
defer func() {
|
|
|
|
meta.RestoreMeta(h)
|
|
|
|
}()
|
2019-11-18 16:22:08 +00:00
|
|
|
}
|
|
|
|
|
2019-12-20 07:13:16 +00:00
|
|
|
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)
|
2019-11-18 16:22:08 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-12-20 07:13:16 +00:00
|
|
|
signature, err := newSignature(key, data[:size])
|
2019-11-18 16:22:08 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-11-19 16:03:42 +00:00
|
|
|
msg.AddSignature(signature)
|
2019-11-18 16:22:08 +00:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// VerifyRequestHeader receives request with RequestVerificationHeader,
|
2019-11-19 16:03:42 +00:00
|
|
|
// tries to marshal and verify each signature from request.
|
2019-11-18 16:22:08 +00:00
|
|
|
// If something went wrong, returns error.
|
2019-11-19 16:03:42 +00:00
|
|
|
func VerifyRequestHeader(msg VerifiableRequest) error {
|
2019-11-18 16:22:08 +00:00
|
|
|
// ignore meta header
|
2020-05-04 11:37:14 +00:00
|
|
|
if meta, ok := msg.(SeizedRequestMetaContainer); ok {
|
|
|
|
h := meta.CutMeta()
|
2019-11-19 16:03:42 +00:00
|
|
|
|
|
|
|
defer func() {
|
|
|
|
meta.RestoreMeta(h)
|
|
|
|
}()
|
2019-11-18 16:22:08 +00:00
|
|
|
}
|
|
|
|
|
2019-12-20 07:13:16 +00:00
|
|
|
data := bytesPool.Get().([]byte)
|
2019-11-18 16:22:08 +00:00
|
|
|
signatures := msg.GetSignatures()
|
2019-11-19 16:03:42 +00:00
|
|
|
defer func() {
|
2019-12-20 07:13:16 +00:00
|
|
|
bytesPool.Put(data)
|
2019-11-19 16:03:42 +00:00
|
|
|
msg.SetSignatures(signatures)
|
|
|
|
}()
|
2019-11-18 16:22:08 +00:00
|
|
|
|
|
|
|
for i := range signatures {
|
|
|
|
msg.SetSignatures(signatures[:i])
|
|
|
|
peer := signatures[i].GetPeer()
|
|
|
|
sign := signatures[i].GetSign()
|
|
|
|
|
|
|
|
key := crypto.UnmarshalPublicKey(peer)
|
|
|
|
if key == nil {
|
2020-05-04 10:04:10 +00:00
|
|
|
return errors.Wrapf(ErrInvalidPublicKeyBytes, "%d: %02x", i, peer)
|
2019-11-18 16:22:08 +00:00
|
|
|
}
|
|
|
|
|
2019-12-20 07:13:16 +00:00
|
|
|
if size := msg.Size(); size <= cap(data) {
|
|
|
|
data = data[:size]
|
|
|
|
} else {
|
|
|
|
data = make([]byte, size)
|
|
|
|
}
|
|
|
|
|
|
|
|
if size, err := msg.MarshalTo(data); err != nil {
|
2019-11-18 16:22:08 +00:00
|
|
|
return errors.Wrapf(err, "%d: %02x", i, peer)
|
2019-12-20 07:13:16 +00:00
|
|
|
} else if err := crypto.Verify(key, data[:size], sign); err != nil {
|
2019-11-18 16:22:08 +00:00
|
|
|
return errors.Wrapf(err, "%d: %02x", i, peer)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
2019-11-19 16:03:42 +00:00
|
|
|
|
|
|
|
// 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 }
|