service: sign requests on the principle of Matryoshka
This commit changes SignRequestData / VerifyRequestData functions to add the list of previous public keys to a signed message for all requests.
This commit is contained in:
parent
ce4513ce54
commit
5e1e220988
4 changed files with 103 additions and 16 deletions
|
@ -137,11 +137,24 @@ func verifySignatures(src SignedDataSource, items ...SignKeyPair) error {
|
|||
}
|
||||
defer bytesPool.Put(data)
|
||||
|
||||
for _, signKey := range items {
|
||||
for i := range items {
|
||||
if i > 0 {
|
||||
// add previous key bytes to the signed message
|
||||
|
||||
signKeyDataSrc := SignKeyPairsSignedData(items[i-1])
|
||||
|
||||
signKeyData, err := signKeyDataSrc.SignedData()
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "could not get signed data of key-signature #%d", i)
|
||||
}
|
||||
|
||||
data = append(data, signKeyData...)
|
||||
}
|
||||
|
||||
if err := crypto.Verify(
|
||||
signKey.GetPublicKey(),
|
||||
items[i].GetPublicKey(),
|
||||
data,
|
||||
signKey.GetSignature(),
|
||||
items[i].GetSignature(),
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -213,6 +226,7 @@ func SignRequestData(key *ecdsa.PrivateKey, src RequestSignedData) error {
|
|||
src.GetBearerToken(),
|
||||
),
|
||||
ExtendedHeadersSignedData(src),
|
||||
SignKeyPairsSignedData(src.GetSignKeyPairs()...),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -15,13 +15,13 @@ import (
|
|||
type testSignedDataSrc struct {
|
||||
err error
|
||||
data []byte
|
||||
sig []byte
|
||||
key *ecdsa.PublicKey
|
||||
token SessionToken
|
||||
|
||||
bearer BearerToken
|
||||
|
||||
extHdrs []ExtendedHeader
|
||||
|
||||
signKeys []SignKeyPair
|
||||
}
|
||||
|
||||
type testSignedDataReader struct {
|
||||
|
@ -29,13 +29,15 @@ type testSignedDataReader struct {
|
|||
}
|
||||
|
||||
func (s testSignedDataSrc) GetSignature() []byte {
|
||||
return s.sig
|
||||
if len(s.signKeys) > 0 {
|
||||
return s.signKeys[0].GetSignature()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s testSignedDataSrc) GetSignKeyPairs() []SignKeyPair {
|
||||
return []SignKeyPair{
|
||||
newSignatureKeyPair(s.key, s.sig),
|
||||
}
|
||||
return s.signKeys
|
||||
}
|
||||
|
||||
func (s testSignedDataSrc) SignedData() ([]byte, error) {
|
||||
|
@ -43,8 +45,9 @@ func (s testSignedDataSrc) SignedData() ([]byte, error) {
|
|||
}
|
||||
|
||||
func (s *testSignedDataSrc) AddSignKey(sig []byte, key *ecdsa.PublicKey) {
|
||||
s.key = key
|
||||
s.sig = sig
|
||||
s.signKeys = append(s.signKeys,
|
||||
newSignatureKeyPair(key, sig),
|
||||
)
|
||||
}
|
||||
|
||||
func testData(t *testing.T, sz int) []byte {
|
||||
|
@ -209,23 +212,27 @@ func TestVerifyAccumulatedSignatures(t *testing.T) {
|
|||
// create test private key
|
||||
sk := test.DecodeKey(0)
|
||||
|
||||
signKey := new(RequestVerificationHeader_Signature)
|
||||
signKey.Peer = crypto.MarshalPublicKey(&sk.PublicKey)
|
||||
|
||||
// create signature source
|
||||
src := &testSignedDataSrc{
|
||||
data: testData(t, 10),
|
||||
key: &sk.PublicKey,
|
||||
|
||||
signKeys: []SignKeyPair{signKey},
|
||||
}
|
||||
|
||||
var err error
|
||||
|
||||
// calculate a signature
|
||||
src.sig, err = crypto.Sign(sk, src.data)
|
||||
signKey.Sign, err = crypto.Sign(sk, src.data)
|
||||
require.NoError(t, err)
|
||||
|
||||
// ascertain that verification is passed
|
||||
require.NoError(t, VerifyAccumulatedSignatures(src))
|
||||
|
||||
// break the signature
|
||||
src.sig[0]++
|
||||
signKey.Sign[0]++
|
||||
|
||||
// ascertain that verification is failed
|
||||
require.Error(t, VerifyAccumulatedSignatures(src))
|
||||
|
@ -238,9 +245,13 @@ func TestVerifySignatureWithKey(t *testing.T) {
|
|||
ErrEmptyDataWithSignature.Error(),
|
||||
)
|
||||
|
||||
signKey := new(RequestVerificationHeader_Signature)
|
||||
|
||||
// create test signature source
|
||||
src := &testSignedDataSrc{
|
||||
data: testData(t, 10),
|
||||
|
||||
signKeys: []SignKeyPair{signKey},
|
||||
}
|
||||
|
||||
// nil public key
|
||||
|
@ -255,14 +266,14 @@ func TestVerifySignatureWithKey(t *testing.T) {
|
|||
var err error
|
||||
|
||||
// calculate a signature
|
||||
src.sig, err = crypto.Sign(sk, src.data)
|
||||
signKey.Sign, err = crypto.Sign(sk, src.data)
|
||||
require.NoError(t, err)
|
||||
|
||||
// ascertain that verification is passed
|
||||
require.NoError(t, VerifySignatureWithKey(&sk.PublicKey, src))
|
||||
|
||||
// break the signature
|
||||
src.sig[0]++
|
||||
signKey.Sign[0]++
|
||||
|
||||
// ascertain that verification is failed
|
||||
require.Error(t, VerifySignatureWithKey(&sk.PublicKey, src))
|
||||
|
@ -375,4 +386,15 @@ func TestSignVerifyRequestData(t *testing.T) {
|
|||
|
||||
// ascertain that verification is passed
|
||||
require.NoError(t, VerifyRequestData(rdr))
|
||||
|
||||
if len(rdr.GetSignKeyPairs()) < 2 {
|
||||
// add one more signature
|
||||
require.NoError(t, SignRequestData(test.DecodeKey(1), rdr))
|
||||
}
|
||||
|
||||
// change key-signature order
|
||||
rdr.signKeys[0], rdr.signKeys[1] = rdr.signKeys[1], rdr.signKeys[0]
|
||||
|
||||
// ascertain that verification is failed
|
||||
require.Error(t, VerifyRequestData(src))
|
||||
}
|
||||
|
|
|
@ -262,6 +262,7 @@ type RequestData interface {
|
|||
type RequestSignedData interface {
|
||||
RequestData
|
||||
SignKeyPairAccumulator
|
||||
SignKeyPairSource
|
||||
}
|
||||
|
||||
// RequestVerifyData is an interface of request information with signature read access.
|
||||
|
|
|
@ -2,11 +2,16 @@ package service
|
|||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"io"
|
||||
|
||||
"github.com/nspcc-dev/neofs-api-go/internal"
|
||||
crypto "github.com/nspcc-dev/neofs-crypto"
|
||||
)
|
||||
|
||||
type signKeyPairsWrapper struct {
|
||||
items []SignKeyPair
|
||||
}
|
||||
|
||||
// GetSessionToken returns SessionToken interface of Token field.
|
||||
//
|
||||
// If token field value is nil, nil returns.
|
||||
|
@ -114,3 +119,48 @@ func (m RequestVerificationHeader) GetBearerToken() BearerToken {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SignKeyPairsSignedData wraps passed SignKeyPair slice and returns SignedDataSource interface.
|
||||
func SignKeyPairsSignedData(v ...SignKeyPair) SignedDataSource {
|
||||
return &signKeyPairsWrapper{
|
||||
items: v,
|
||||
}
|
||||
}
|
||||
|
||||
// SignedData returns signed SignKeyPair slice in a binary representation.
|
||||
func (s signKeyPairsWrapper) SignedData() ([]byte, error) {
|
||||
return SignedDataFromReader(s)
|
||||
}
|
||||
|
||||
// SignedDataSize returns the length of signed SignKeyPair slice.
|
||||
func (s signKeyPairsWrapper) SignedDataSize() (sz int) {
|
||||
for i := range s.items {
|
||||
// add key length
|
||||
sz += len(
|
||||
crypto.MarshalPublicKey(s.items[i].GetPublicKey()),
|
||||
)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// ReadSignedData copies a binary representation of the signed SignKeyPair slice to passed buffer.
|
||||
//
|
||||
// If buffer length is less than required, io.ErrUnexpectedEOF returns.
|
||||
func (s signKeyPairsWrapper) ReadSignedData(p []byte) (int, error) {
|
||||
sz := s.SignedDataSize()
|
||||
if len(p) < sz {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
|
||||
off := 0
|
||||
|
||||
for i := range s.items {
|
||||
// copy public key bytes
|
||||
off += copy(p[off:], crypto.MarshalPublicKey(
|
||||
s.items[i].GetPublicKey(),
|
||||
))
|
||||
}
|
||||
|
||||
return off, nil
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue