diff --git a/service/sign.go b/service/sign.go index 50453b9..796a4cd 100644 --- a/service/sign.go +++ b/service/sign.go @@ -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 diff --git a/service/sign_test.go b/service/sign_test.go index 6f1e913..3c54e8c 100644 --- a/service/sign_test.go +++ b/service/sign_test.go @@ -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)) } diff --git a/service/types.go b/service/types.go index 75a5a0a..785a30a 100644 --- a/service/types.go +++ b/service/types.go @@ -262,6 +262,7 @@ type RequestData interface { type RequestSignedData interface { RequestData SignKeyPairAccumulator + SignKeyPairSource } // RequestVerifyData is an interface of request information with signature read access. diff --git a/service/verify.go b/service/verify.go index e1caa06..7691220 100644 --- a/service/verify.go +++ b/service/verify.go @@ -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 +}