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:
Leonard Lyubich 2020-06-22 17:26:59 +03:00
parent ce4513ce54
commit 5e1e220988
4 changed files with 103 additions and 16 deletions

View file

@ -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

View file

@ -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))
}

View file

@ -262,6 +262,7 @@ type RequestData interface {
type RequestSignedData interface {
RequestData
SignKeyPairAccumulator
SignKeyPairSource
}
// RequestVerifyData is an interface of request information with signature read access.

View file

@ -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
}