diff --git a/accounting/sign_test.go b/accounting/sign_test.go index dd7a819..ebc683b 100644 --- a/accounting/sign_test.go +++ b/accounting/sign_test.go @@ -13,7 +13,7 @@ func TestSignBalanceRequest(t *testing.T) { sk := test.DecodeKey(0) type sigType interface { - service.SignedDataWithToken + service.RequestData service.SignKeyPairAccumulator service.SignKeyPairSource SetToken(*service.Token) @@ -159,26 +159,26 @@ func TestSignBalanceRequest(t *testing.T) { token := new(service.Token) v.SetToken(token) - require.NoError(t, service.SignDataWithSessionToken(sk, v)) + require.NoError(t, service.SignRequestData(sk, v)) - require.NoError(t, service.VerifyAccumulatedSignaturesWithToken(v)) + require.NoError(t, service.VerifyRequestData(v)) token.SetSessionKey(append(token.GetSessionKey(), 1)) - require.Error(t, service.VerifyAccumulatedSignaturesWithToken(v)) + require.Error(t, service.VerifyRequestData(v)) } { // payload corruptions for _, corruption := range item.payloadCorrupt { v := item.constructor() - require.NoError(t, service.SignDataWithSessionToken(sk, v)) + require.NoError(t, service.SignRequestData(sk, v)) - require.NoError(t, service.VerifyAccumulatedSignaturesWithToken(v)) + require.NoError(t, service.VerifyRequestData(v)) corruption(v) - require.Error(t, service.VerifyAccumulatedSignaturesWithToken(v)) + require.Error(t, service.VerifyRequestData(v)) } } } diff --git a/bootstrap/sign_test.go b/bootstrap/sign_test.go index 2c76117..3812130 100644 --- a/bootstrap/sign_test.go +++ b/bootstrap/sign_test.go @@ -12,7 +12,7 @@ func TestRequestSign(t *testing.T) { sk := test.DecodeKey(0) type sigType interface { - service.SignedDataWithToken + service.RequestData service.SignKeyPairAccumulator service.SignKeyPairSource SetToken(*service.Token) @@ -56,26 +56,26 @@ func TestRequestSign(t *testing.T) { token := new(service.Token) v.SetToken(token) - require.NoError(t, service.SignDataWithSessionToken(sk, v)) + require.NoError(t, service.SignRequestData(sk, v)) - require.NoError(t, service.VerifyAccumulatedSignaturesWithToken(v)) + require.NoError(t, service.VerifyRequestData(v)) token.SetSessionKey(append(token.GetSessionKey(), 1)) - require.Error(t, service.VerifyAccumulatedSignaturesWithToken(v)) + require.Error(t, service.VerifyRequestData(v)) } { // payload corruptions for _, corruption := range item.payloadCorrupt { v := item.constructor() - require.NoError(t, service.SignDataWithSessionToken(sk, v)) + require.NoError(t, service.SignRequestData(sk, v)) - require.NoError(t, service.VerifyAccumulatedSignaturesWithToken(v)) + require.NoError(t, service.VerifyRequestData(v)) corruption(v) - require.Error(t, service.VerifyAccumulatedSignaturesWithToken(v)) + require.Error(t, service.VerifyRequestData(v)) } } } diff --git a/container/sign_test.go b/container/sign_test.go index e469399..d9b7d26 100644 --- a/container/sign_test.go +++ b/container/sign_test.go @@ -12,7 +12,7 @@ func TestRequestSign(t *testing.T) { sk := test.DecodeKey(0) type sigType interface { - service.SignedDataWithToken + service.RequestData service.SignKeyPairAccumulator service.SignKeyPairSource SetToken(*service.Token) @@ -117,26 +117,26 @@ func TestRequestSign(t *testing.T) { token := new(service.Token) v.SetToken(token) - require.NoError(t, service.SignDataWithSessionToken(sk, v)) + require.NoError(t, service.SignRequestData(sk, v)) - require.NoError(t, service.VerifyAccumulatedSignaturesWithToken(v)) + require.NoError(t, service.VerifyRequestData(v)) token.SetSessionKey(append(token.GetSessionKey(), 1)) - require.Error(t, service.VerifyAccumulatedSignaturesWithToken(v)) + require.Error(t, service.VerifyRequestData(v)) } { // payload corruptions for _, corruption := range item.payloadCorrupt { v := item.constructor() - require.NoError(t, service.SignDataWithSessionToken(sk, v)) + require.NoError(t, service.SignRequestData(sk, v)) - require.NoError(t, service.VerifyAccumulatedSignaturesWithToken(v)) + require.NoError(t, service.VerifyRequestData(v)) corruption(v) - require.Error(t, service.VerifyAccumulatedSignaturesWithToken(v)) + require.Error(t, service.VerifyRequestData(v)) } } } diff --git a/object/sign_test.go b/object/sign_test.go index 9480fda..dcfbd7e 100644 --- a/object/sign_test.go +++ b/object/sign_test.go @@ -13,7 +13,7 @@ func TestSignVerifyRequests(t *testing.T) { sk := test.DecodeKey(0) type sigType interface { - service.SignedDataWithToken + service.RequestData service.SignKeyPairAccumulator service.SignKeyPairSource SetToken(*Token) @@ -164,26 +164,26 @@ func TestSignVerifyRequests(t *testing.T) { token := new(Token) v.SetToken(token) - require.NoError(t, service.SignDataWithSessionToken(sk, v)) + require.NoError(t, service.SignRequestData(sk, v)) - require.NoError(t, service.VerifyAccumulatedSignaturesWithToken(v)) + require.NoError(t, service.VerifyRequestData(v)) token.SetSessionKey(append(token.GetSessionKey(), 1)) - require.Error(t, service.VerifyAccumulatedSignaturesWithToken(v)) + require.Error(t, service.VerifyRequestData(v)) } { // payload corruptions for _, corruption := range item.payloadCorrupt { v := item.constructor() - require.NoError(t, service.SignDataWithSessionToken(sk, v)) + require.NoError(t, service.SignRequestData(sk, v)) - require.NoError(t, service.VerifyAccumulatedSignaturesWithToken(v)) + require.NoError(t, service.VerifyRequestData(v)) corruption(v) - require.Error(t, service.VerifyAccumulatedSignaturesWithToken(v)) + require.Error(t, service.VerifyRequestData(v)) } } } diff --git a/service/errors.go b/service/errors.go index f3a0dfc..e1c4900 100644 --- a/service/errors.go +++ b/service/errors.go @@ -36,14 +36,18 @@ const ErrEmptyDataWithSignature = internal.Error("empty data with signature") // negative length for slice allocation. const ErrNegativeLength = internal.Error("negative slice length") -// ErrNilDataWithTokenSignAccumulator is returned by functions that expect -// a non-nil DataWithTokenSignAccumulator, but received nil. -const ErrNilDataWithTokenSignAccumulator = internal.Error("signed data with token is nil") +// ErrNilRequestSignedData is returned by functions that expect +// a non-nil RequestSignedData, but received nil. +const ErrNilRequestSignedData = internal.Error("request signed data is nil") -// ErrNilSignatureKeySourceWithToken is returned by functions that expect -// a non-nil SignatureKeySourceWithToken, but received nil. -const ErrNilSignatureKeySourceWithToken = internal.Error("key-signature source with token is nil") +// ErrNilRequestVerifyData is returned by functions that expect +// a non-nil RequestVerifyData, but received nil. +const ErrNilRequestVerifyData = internal.Error("request verification data is nil") // ErrNilSignedDataReader is returned by functions that expect // a non-nil SignedDataReader, but received nil. const ErrNilSignedDataReader = internal.Error("signed data reader is nil") + +// ErrNilSignKeyPairAccumulator is returned by functions that expect +// a non-nil SignKeyPairAccumulator, but received nil. +const ErrNilSignKeyPairAccumulator = internal.Error("signature-key pair accumulator is nil") diff --git a/service/sign.go b/service/sign.go index 5b1548f..eb1c16d 100644 --- a/service/sign.go +++ b/service/sign.go @@ -2,9 +2,11 @@ package service import ( "crypto/ecdsa" + "io" "sync" crypto "github.com/nspcc-dev/neofs-crypto" + "github.com/pkg/errors" ) type keySign struct { @@ -12,6 +14,20 @@ type keySign struct { sign []byte } +type signSourceGroup struct { + SignKeyPairSource + SignKeyPairAccumulator + + sources []SignedDataSource +} + +type signReadersGroup struct { + SignKeyPairSource + SignKeyPairAccumulator + + readers []SignedDataReader +} + var bytesPool = sync.Pool{ New: func() interface{} { return make([]byte, 5<<20) @@ -176,54 +192,191 @@ func VerifySignatureWithKey(key *ecdsa.PublicKey, src DataWithSignature) error { ) } -// SignDataWithSessionToken calculates data with token signature and adds it to accumulator. +// SignRequestData calculates request data signature and adds it to accumulator. // -// Any change of data or session token info provoke signature breakdown. +// Any change of request data provoke signature breakdown. // // If passed private key is nil, crypto.ErrEmptyPrivateKey returns. -// If passed DataWithTokenSignAccumulator is nil, ErrNilDataWithTokenSignAccumulator returns. -func SignDataWithSessionToken(key *ecdsa.PrivateKey, src DataWithTokenSignAccumulator) error { +// If passed RequestSignedData is nil, ErrNilRequestSignedData returns. +func SignRequestData(key *ecdsa.PrivateKey, src RequestSignedData) error { if src == nil { - return ErrNilDataWithTokenSignAccumulator - } else if r, ok := src.(SignedDataReader); ok { - return AddSignatureWithKey(key, &signDataReaderWithToken{ - SignedDataSource: src, - SignKeyPairAccumulator: src, - - rdr: r, - token: src.GetSessionToken(), - }, - ) + return ErrNilRequestSignedData } - return AddSignatureWithKey(key, &signAccumWithToken{ - SignedDataSource: src, - SignKeyPairAccumulator: src, + sigSrc, err := GroupSignedPayloads( + src, + src, + NewSignedSessionToken( + src.GetSessionToken(), + ), + ) + if err != nil { + return err + } - token: src.GetSessionToken(), - }) + return AddSignatureWithKey(key, sigSrc) } -// VerifyAccumulatedSignaturesWithToken checks if accumulated key-signature pairs of data with token are valid. +// VerifyRequestData checks if accumulated key-signature pairs of data with token are valid. // -// If passed DataWithTokenSignSource is nil, ErrNilSignatureKeySourceWithToken returns. -func VerifyAccumulatedSignaturesWithToken(src DataWithTokenSignSource) error { +// If passed RequestVerifyData is nil, ErrNilRequestVerifyData returns. +func VerifyRequestData(src RequestVerifyData) error { if src == nil { - return ErrNilSignatureKeySourceWithToken - } else if r, ok := src.(SignedDataReader); ok { - return VerifyAccumulatedSignatures(&signDataReaderWithToken{ - SignedDataSource: src, - SignKeyPairSource: src, - - rdr: r, - token: src.GetSessionToken(), - }) + return ErrNilRequestVerifyData } - return VerifyAccumulatedSignatures(&signAccumWithToken{ - SignedDataSource: src, - SignKeyPairSource: src, + verSrc, err := GroupVerifyPayloads( + src, + src, + NewVerifiedSessionToken( + src.GetSessionToken(), + ), + ) + if err != nil { + return err + } - token: src.GetSessionToken(), - }) + return VerifyAccumulatedSignatures(verSrc) +} + +// SignedData returns payload bytes concatenation from all sources keeping order. +func (s signSourceGroup) SignedData() ([]byte, error) { + chunks := make([][]byte, 0, len(s.sources)) + sz := 0 + + for i := range s.sources { + data, err := s.sources[i].SignedData() + if err != nil { + return nil, errors.Wrapf(err, "could not get signed payload of element #%d", i) + } + + chunks = append(chunks, data) + + sz += len(data) + } + + res := make([]byte, sz) + off := 0 + + for i := range chunks { + off += copy(res[off:], chunks[i]) + } + + return res, nil +} + +// SignedData returns payload bytes concatenation from all readers. +func (s signReadersGroup) SignedData() ([]byte, error) { + return SignedDataFromReader(s) +} + +// SignedDataSize returns the sum of sizes of all readers. +func (s signReadersGroup) SignedDataSize() (sz int) { + for i := range s.readers { + sz += s.readers[i].SignedDataSize() + } + + return +} + +// ReadSignedData reads data from all readers to passed buffer keeping order. +// +// If the buffer size is insufficient, io.ErrUnexpectedEOF returns. +func (s signReadersGroup) ReadSignedData(p []byte) (int, error) { + sz := s.SignedDataSize() + if len(p) < sz { + return 0, io.ErrUnexpectedEOF + } + + off := 0 + + for i := range s.readers { + n, err := s.readers[i].ReadSignedData(p[off:]) + off += n + if err != nil { + return off, errors.Wrapf(err, "could not read signed payload of element #%d", i) + } + } + + return off, nil +} + +// GroupSignedPayloads groups SignKeyPairAccumulator and SignedDataSource list to DataWithSignKeyAccumulator. +// +// If passed SignKeyPairAccumulator is nil, ErrNilSignKeyPairAccumulator returns. +// +// Signed payload of the result is a concatenation of payloads of list elements keeping order. +// Nil elements in list are ignored. +// +// If all elements implement SignedDataReader, result implements it too. +func GroupSignedPayloads(acc SignKeyPairAccumulator, sources ...SignedDataSource) (DataWithSignKeyAccumulator, error) { + if acc == nil { + return nil, ErrNilSignKeyPairAccumulator + } + + return groupPayloads(acc, nil, sources...), nil +} + +// GroupVerifyPayloads groups SignKeyPairSource and SignedDataSource list to DataWithSignKeySource. +// +// If passed SignKeyPairSource is nil, ErrNilSignatureKeySource returns. +// +// Signed payload of the result is a concatenation of payloads of list elements keeping order. +// Nil elements in list are ignored. +// +// If all elements implement SignedDataReader, result implements it too. +func GroupVerifyPayloads(src SignKeyPairSource, sources ...SignedDataSource) (DataWithSignKeySource, error) { + if src == nil { + return nil, ErrNilSignatureKeySource + } + + return groupPayloads(nil, src, sources...), nil +} + +func groupPayloads(acc SignKeyPairAccumulator, src SignKeyPairSource, sources ...SignedDataSource) interface { + SignedDataSource + SignKeyPairSource + SignKeyPairAccumulator +} { + var allReaders bool + + for i := range sources { + if sources[i] == nil { + continue + } else if _, allReaders = sources[i].(SignedDataReader); !allReaders { + break + } + } + + if !allReaders { + res := &signSourceGroup{ + SignKeyPairSource: src, + SignKeyPairAccumulator: acc, + + sources: make([]SignedDataSource, 0, len(sources)), + } + + for i := range sources { + if sources[i] != nil { + res.sources = append(res.sources, sources[i]) + } + } + + return res + } + + res := &signReadersGroup{ + SignKeyPairSource: src, + SignKeyPairAccumulator: acc, + + readers: make([]SignedDataReader, 0, len(sources)), + } + + for i := range sources { + if sources[i] != nil { + res.readers = append(res.readers, sources[i].(SignedDataReader)) + } + } + + return res } diff --git a/service/sign_test.go b/service/sign_test.go index 5cb7c40..ca469b8 100644 --- a/service/sign_test.go +++ b/service/sign_test.go @@ -257,16 +257,16 @@ func TestVerifySignatureWithKey(t *testing.T) { } func TestSignVerifyDataWithSessionToken(t *testing.T) { - // sign with empty DataWithTokenSignAccumulator + // sign with empty RequestSignedData require.EqualError(t, - SignDataWithSessionToken(nil, nil), - ErrNilDataWithTokenSignAccumulator.Error(), + SignRequestData(nil, nil), + ErrNilRequestSignedData.Error(), ) - // verify with empty DataWithTokenSignSource + // verify with empty RequestVerifyData require.EqualError(t, - VerifyAccumulatedSignaturesWithToken(nil), - ErrNilSignatureKeySourceWithToken.Error(), + VerifyRequestData(nil), + ErrNilRequestVerifyData.Error(), ) // create test session token @@ -287,16 +287,16 @@ func TestSignVerifyDataWithSessionToken(t *testing.T) { sk := test.DecodeKey(0) // sign with private key - require.NoError(t, SignDataWithSessionToken(sk, src)) + require.NoError(t, SignRequestData(sk, src)) // ascertain that verification is passed - require.NoError(t, VerifyAccumulatedSignaturesWithToken(src)) + require.NoError(t, VerifyRequestData(src)) // break the data src.data[0]++ // ascertain that verification is failed - require.Error(t, VerifyAccumulatedSignaturesWithToken(src)) + require.Error(t, VerifyRequestData(src)) // restore the data src.data[0]-- @@ -305,13 +305,13 @@ func TestSignVerifyDataWithSessionToken(t *testing.T) { token.SetVerb(initVerb + 1) // ascertain that verification is failed - require.Error(t, VerifyAccumulatedSignaturesWithToken(src)) + require.Error(t, VerifyRequestData(src)) // restore the token token.SetVerb(initVerb) // ascertain that verification is passed - require.NoError(t, VerifyAccumulatedSignaturesWithToken(src)) + require.NoError(t, VerifyRequestData(src)) // wrap to data reader rdr := &testSignedDataReader{ @@ -319,8 +319,8 @@ func TestSignVerifyDataWithSessionToken(t *testing.T) { } // sign with private key - require.NoError(t, SignDataWithSessionToken(sk, rdr)) + require.NoError(t, SignRequestData(sk, rdr)) // ascertain that verification is passed - require.NoError(t, VerifyAccumulatedSignaturesWithToken(rdr)) + require.NoError(t, VerifyRequestData(rdr)) } diff --git a/service/types.go b/service/types.go index 66582f5..257b0ca 100644 --- a/service/types.go +++ b/service/types.go @@ -250,20 +250,20 @@ type DataWithSignKeySource interface { SignKeyPairSource } -// SignedDataWithToken is an interface of data-token pair with read access. -type SignedDataWithToken interface { +// RequestData is an interface of the request information with read access. +type RequestData interface { SignedDataSource SessionTokenSource } -// DataWithTokenSignAccumulator is an interface of data-token pair with signature write access. -type DataWithTokenSignAccumulator interface { - SignedDataWithToken +// RequestSignedData is an interface of request information with signature write access. +type RequestSignedData interface { + RequestData SignKeyPairAccumulator } -// DataWithTokenSignSource is an interface of data-token pair with signature read access. -type DataWithTokenSignSource interface { - SignedDataWithToken +// RequestVerifyData is an interface of request information with signature read access. +type RequestVerifyData interface { + RequestData SignKeyPairSource } diff --git a/service/verify_test.go b/service/verify_test.go index c6e4d61..e13f316 100644 --- a/service/verify_test.go +++ b/service/verify_test.go @@ -69,7 +69,7 @@ func BenchmarkSignDataWithSessionToken(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { - require.NoError(b, SignDataWithSessionToken(key, req)) + require.NoError(b, SignRequestData(key, req)) } } @@ -91,14 +91,14 @@ func BenchmarkVerifyAccumulatedSignaturesWithToken(b *testing.B) { for i := 0; i < 10; i++ { key := test.DecodeKey(i) - require.NoError(b, SignDataWithSessionToken(key, req)) + require.NoError(b, SignRequestData(key, req)) } b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { - require.NoError(b, VerifyAccumulatedSignaturesWithToken(req)) + require.NoError(b, VerifyRequestData(req)) } } diff --git a/session/create.go b/session/create.go index 35d0540..412d1fd 100644 --- a/session/create.go +++ b/session/create.go @@ -53,7 +53,7 @@ func (s gRPCCreator) Create(ctx context.Context, p CreateParamsSource) (CreateRe req.SetExpirationEpoch(p.ExpirationEpoch()) // sign with private key - if err := service.SignDataWithSessionToken(s.key, req); err != nil { + if err := service.SignRequestData(s.key, req); err != nil { return nil, err } diff --git a/session/create_test.go b/session/create_test.go index 732d4fd..943c5da 100644 --- a/session/create_test.go +++ b/session/create_test.go @@ -84,7 +84,7 @@ func TestGRPCCreator_Create(t *testing.T) { require.Equal(t, ownerID, req.GetOwnerID()) require.Equal(t, created, req.CreationEpoch()) require.Equal(t, expired, req.ExpirationEpoch()) - require.NoError(t, service.VerifyAccumulatedSignaturesWithToken(req)) + require.NoError(t, service.VerifyRequestData(req)) }, resp: &CreateResponse{ ID: TokenID{1, 2, 3}, diff --git a/state/sign_test.go b/state/sign_test.go index 9b2bca9..05af654 100644 --- a/state/sign_test.go +++ b/state/sign_test.go @@ -12,7 +12,7 @@ func TestRequestSign(t *testing.T) { sk := test.DecodeKey(0) type sigType interface { - service.SignedDataWithToken + service.RequestData service.SignKeyPairAccumulator service.SignKeyPairSource SetToken(*service.Token) @@ -68,26 +68,26 @@ func TestRequestSign(t *testing.T) { token := new(service.Token) v.SetToken(token) - require.NoError(t, service.SignDataWithSessionToken(sk, v)) + require.NoError(t, service.SignRequestData(sk, v)) - require.NoError(t, service.VerifyAccumulatedSignaturesWithToken(v)) + require.NoError(t, service.VerifyRequestData(v)) token.SetSessionKey(append(token.GetSessionKey(), 1)) - require.Error(t, service.VerifyAccumulatedSignaturesWithToken(v)) + require.Error(t, service.VerifyRequestData(v)) } { // payload corruptions for _, corruption := range item.payloadCorrupt { v := item.constructor() - require.NoError(t, service.SignDataWithSessionToken(sk, v)) + require.NoError(t, service.SignRequestData(sk, v)) - require.NoError(t, service.VerifyAccumulatedSignaturesWithToken(v)) + require.NoError(t, service.VerifyRequestData(v)) corruption(v) - require.Error(t, service.VerifyAccumulatedSignaturesWithToken(v)) + require.Error(t, service.VerifyRequestData(v)) } } }