frostfs-api-go/service/sign_test.go
Leonard Lyubich 5e1e220988 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.
2020-06-22 17:26:59 +03:00

400 lines
8.2 KiB
Go

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