package service import ( "crypto/ecdsa" "crypto/rand" "errors" "io" "testing" crypto "github.com/nspcc-dev/neofs-crypto" "github.com/nspcc-dev/neofs-crypto/test" "github.com/stretchr/testify/require" ) type testSignedDataSrc struct { err error data []byte token SessionToken bearer BearerToken extHdrs []ExtendedHeader signKeys []SignKeyPair } type testSignedDataReader struct { *testSignedDataSrc } func (s testSignedDataSrc) GetSignature() []byte { if len(s.signKeys) > 0 { return s.signKeys[0].GetSignature() } return nil } func (s testSignedDataSrc) GetSignKeyPairs() []SignKeyPair { return s.signKeys } func (s testSignedDataSrc) SignedData() ([]byte, error) { return s.data, s.err } func (s *testSignedDataSrc) AddSignKey(sig []byte, key *ecdsa.PublicKey) { s.signKeys = append(s.signKeys, newSignatureKeyPair(key, sig), ) } func testData(t *testing.T, sz int) []byte { d := make([]byte, sz) _, err := rand.Read(d) require.NoError(t, err) return d } func (s testSignedDataSrc) GetSessionToken() SessionToken { return s.token } func (s testSignedDataSrc) GetBearerToken() BearerToken { return s.bearer } func (s testSignedDataSrc) ExtendedHeaders() []ExtendedHeader { return s.extHdrs } func (s testSignedDataReader) SignedDataSize() int { return len(s.data) } func (s testSignedDataReader) ReadSignedData(buf []byte) (int, error) { if s.err != nil { return 0, s.err } var err error if len(buf) < len(s.data) { err = io.ErrUnexpectedEOF } return copy(buf, s.data), err } func TestDataSignature(t *testing.T) { var err error // nil private key _, err = DataSignature(nil, nil) require.EqualError(t, err, crypto.ErrEmptyPrivateKey.Error()) // create test private key sk := test.DecodeKey(0) // nil private key _, err = DataSignature(sk, nil) require.EqualError(t, err, ErrNilSignedDataSource.Error()) t.Run("common signed data source", func(t *testing.T) { // create test data source src := &testSignedDataSrc{ data: testData(t, 10), } // create custom error for data source src.err = errors.New("test error for data source") _, err = DataSignature(sk, src) require.EqualError(t, err, src.err.Error()) // reset error to nil src.err = nil // calculate data signature sig, err := DataSignature(sk, src) require.NoError(t, err) // ascertain that the signature passes verification require.NoError(t, crypto.Verify(&sk.PublicKey, src.data, sig)) }) t.Run("signed data reader", func(t *testing.T) { // create test signed data reader src := &testSignedDataSrc{ data: testData(t, 10), } // create custom error for signed data reader src.err = errors.New("test error for signed data reader") sig, err := DataSignature(sk, src) require.EqualError(t, err, src.err.Error()) // reset error to nil src.err = nil // calculate data signature sig, err = DataSignature(sk, src) require.NoError(t, err) // ascertain that the signature passes verification require.NoError(t, crypto.Verify(&sk.PublicKey, src.data, sig)) }) } func TestAddSignatureWithKey(t *testing.T) { require.NoError(t, AddSignatureWithKey( test.DecodeKey(0), &testSignedDataSrc{ data: testData(t, 10), }, ), ) } func TestVerifySignatures(t *testing.T) { // empty signatures require.NoError(t, VerifySignatures(nil)) // create test signature source src := &testSignedDataSrc{ data: testData(t, 10), } // create private key for test sk := test.DecodeKey(0) // calculate a signature of the data sig, err := crypto.Sign(sk, src.data) require.NoError(t, err) // ascertain that verification is passed require.NoError(t, VerifySignatures( src, newSignatureKeyPair(&sk.PublicKey, sig), ), ) // break the signature sig[0]++ require.Error(t, VerifySignatures( src, newSignatureKeyPair(&sk.PublicKey, sig), ), ) // restore the signature sig[0]-- // empty data source require.EqualError(t, VerifySignatures(nil, nil), ErrNilSignedDataSource.Error(), ) } func TestVerifyAccumulatedSignatures(t *testing.T) { // nil signature source require.EqualError(t, VerifyAccumulatedSignatures(nil), ErrNilSignatureKeySource.Error(), ) // 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), signKeys: []SignKeyPair{signKey}, } var err error // calculate a signature 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 signKey.Sign[0]++ // ascertain that verification is failed require.Error(t, VerifyAccumulatedSignatures(src)) } func TestVerifySignatureWithKey(t *testing.T) { // nil signature source require.EqualError(t, VerifySignatureWithKey(nil, nil), ErrEmptyDataWithSignature.Error(), ) signKey := new(RequestVerificationHeader_Signature) // create test signature source src := &testSignedDataSrc{ data: testData(t, 10), signKeys: []SignKeyPair{signKey}, } // nil public key require.EqualError(t, VerifySignatureWithKey(nil, src), crypto.ErrEmptyPublicKey.Error(), ) // create test private key sk := test.DecodeKey(0) var err error // calculate a signature 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 signKey.Sign[0]++ // ascertain that verification is failed require.Error(t, VerifySignatureWithKey(&sk.PublicKey, src)) } func TestSignVerifyRequestData(t *testing.T) { // sign with empty RequestSignedData require.EqualError(t, SignRequestData(nil, nil), ErrNilRequestSignedData.Error(), ) // verify with empty RequestVerifyData require.EqualError(t, VerifyRequestData(nil), ErrNilRequestVerifyData.Error(), ) // create test session token var ( token = new(Token) initVerb = Token_Info_Verb(1) bearer = new(BearerTokenMsg) bearerEpoch = uint64(8) extHdrKey = "key" extHdr = new(RequestExtendedHeader_KV) ) token.SetVerb(initVerb) bearer.SetExpirationEpoch(bearerEpoch) extHdr.SetK(extHdrKey) // create test data with token src := &testSignedDataSrc{ data: testData(t, 10), token: token, bearer: bearer, extHdrs: []ExtendedHeader{ wrapExtendedHeaderKV(extHdr), }, } // create test private key sk := test.DecodeKey(0) // sign with private key require.NoError(t, SignRequestData(sk, src)) // ascertain that verification is passed require.NoError(t, VerifyRequestData(src)) // break the data src.data[0]++ // ascertain that verification is failed require.Error(t, VerifyRequestData(src)) // restore the data src.data[0]-- // break the token token.SetVerb(initVerb + 1) // ascertain that verification is failed require.Error(t, VerifyRequestData(src)) // restore the token token.SetVerb(initVerb) // ascertain that verification is passed require.NoError(t, VerifyRequestData(src)) // break the Bearer token bearer.SetExpirationEpoch(bearerEpoch + 1) // ascertain that verification is failed require.Error(t, VerifyRequestData(src)) // restore the Bearer token bearer.SetExpirationEpoch(bearerEpoch) // ascertain that verification is passed require.NoError(t, VerifyRequestData(src)) // break the extended header extHdr.SetK(extHdrKey + "1") // ascertain that verification is failed require.Error(t, VerifyRequestData(src)) // restore the extended header extHdr.SetK(extHdrKey) // ascertain that verification is passed require.NoError(t, VerifyRequestData(src)) // wrap to data reader rdr := &testSignedDataReader{ testSignedDataSrc: src, } // sign with private key require.NoError(t, SignRequestData(sk, rdr)) // 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)) }