Merge pull request #96 from nspcc-dev/feature/matryoshka-signatures

service: sign requests on the principle of Matryoshka
This commit is contained in:
Alex Vanin 2020-06-24 14:33:01 +03:00 committed by GitHub
commit 4fadb5040b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
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) 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( if err := crypto.Verify(
signKey.GetPublicKey(), items[i].GetPublicKey(),
data, data,
signKey.GetSignature(), items[i].GetSignature(),
); err != nil { ); err != nil {
return err return err
} }
@ -213,6 +226,7 @@ func SignRequestData(key *ecdsa.PrivateKey, src RequestSignedData) error {
src.GetBearerToken(), src.GetBearerToken(),
), ),
ExtendedHeadersSignedData(src), ExtendedHeadersSignedData(src),
SignKeyPairsSignedData(src.GetSignKeyPairs()...),
) )
if err != nil { if err != nil {
return err return err

View file

@ -15,13 +15,13 @@ import (
type testSignedDataSrc struct { type testSignedDataSrc struct {
err error err error
data []byte data []byte
sig []byte
key *ecdsa.PublicKey
token SessionToken token SessionToken
bearer BearerToken bearer BearerToken
extHdrs []ExtendedHeader extHdrs []ExtendedHeader
signKeys []SignKeyPair
} }
type testSignedDataReader struct { type testSignedDataReader struct {
@ -29,13 +29,15 @@ type testSignedDataReader struct {
} }
func (s testSignedDataSrc) GetSignature() []byte { 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 { func (s testSignedDataSrc) GetSignKeyPairs() []SignKeyPair {
return []SignKeyPair{ return s.signKeys
newSignatureKeyPair(s.key, s.sig),
}
} }
func (s testSignedDataSrc) SignedData() ([]byte, error) { 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) { func (s *testSignedDataSrc) AddSignKey(sig []byte, key *ecdsa.PublicKey) {
s.key = key s.signKeys = append(s.signKeys,
s.sig = sig newSignatureKeyPair(key, sig),
)
} }
func testData(t *testing.T, sz int) []byte { func testData(t *testing.T, sz int) []byte {
@ -209,23 +212,27 @@ func TestVerifyAccumulatedSignatures(t *testing.T) {
// create test private key // create test private key
sk := test.DecodeKey(0) sk := test.DecodeKey(0)
signKey := new(RequestVerificationHeader_Signature)
signKey.Peer = crypto.MarshalPublicKey(&sk.PublicKey)
// create signature source // create signature source
src := &testSignedDataSrc{ src := &testSignedDataSrc{
data: testData(t, 10), data: testData(t, 10),
key: &sk.PublicKey,
signKeys: []SignKeyPair{signKey},
} }
var err error var err error
// calculate a signature // calculate a signature
src.sig, err = crypto.Sign(sk, src.data) signKey.Sign, err = crypto.Sign(sk, src.data)
require.NoError(t, err) require.NoError(t, err)
// ascertain that verification is passed // ascertain that verification is passed
require.NoError(t, VerifyAccumulatedSignatures(src)) require.NoError(t, VerifyAccumulatedSignatures(src))
// break the signature // break the signature
src.sig[0]++ signKey.Sign[0]++
// ascertain that verification is failed // ascertain that verification is failed
require.Error(t, VerifyAccumulatedSignatures(src)) require.Error(t, VerifyAccumulatedSignatures(src))
@ -238,9 +245,13 @@ func TestVerifySignatureWithKey(t *testing.T) {
ErrEmptyDataWithSignature.Error(), ErrEmptyDataWithSignature.Error(),
) )
signKey := new(RequestVerificationHeader_Signature)
// create test signature source // create test signature source
src := &testSignedDataSrc{ src := &testSignedDataSrc{
data: testData(t, 10), data: testData(t, 10),
signKeys: []SignKeyPair{signKey},
} }
// nil public key // nil public key
@ -255,14 +266,14 @@ func TestVerifySignatureWithKey(t *testing.T) {
var err error var err error
// calculate a signature // calculate a signature
src.sig, err = crypto.Sign(sk, src.data) signKey.Sign, err = crypto.Sign(sk, src.data)
require.NoError(t, err) require.NoError(t, err)
// ascertain that verification is passed // ascertain that verification is passed
require.NoError(t, VerifySignatureWithKey(&sk.PublicKey, src)) require.NoError(t, VerifySignatureWithKey(&sk.PublicKey, src))
// break the signature // break the signature
src.sig[0]++ signKey.Sign[0]++
// ascertain that verification is failed // ascertain that verification is failed
require.Error(t, VerifySignatureWithKey(&sk.PublicKey, src)) require.Error(t, VerifySignatureWithKey(&sk.PublicKey, src))
@ -375,4 +386,15 @@ func TestSignVerifyRequestData(t *testing.T) {
// ascertain that verification is passed // ascertain that verification is passed
require.NoError(t, VerifyRequestData(rdr)) 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 { type RequestSignedData interface {
RequestData RequestData
SignKeyPairAccumulator SignKeyPairAccumulator
SignKeyPairSource
} }
// RequestVerifyData is an interface of request information with signature read access. // RequestVerifyData is an interface of request information with signature read access.

View file

@ -2,11 +2,16 @@ package service
import ( import (
"crypto/ecdsa" "crypto/ecdsa"
"io"
"github.com/nspcc-dev/neofs-api-go/internal" "github.com/nspcc-dev/neofs-api-go/internal"
crypto "github.com/nspcc-dev/neofs-crypto" crypto "github.com/nspcc-dev/neofs-crypto"
) )
type signKeyPairsWrapper struct {
items []SignKeyPair
}
// GetSessionToken returns SessionToken interface of Token field. // GetSessionToken returns SessionToken interface of Token field.
// //
// If token field value is nil, nil returns. // If token field value is nil, nil returns.
@ -114,3 +119,48 @@ func (m RequestVerificationHeader) GetBearerToken() BearerToken {
return nil 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
}