diff --git a/pkg/accounting/tally/tally_test.go b/pkg/accounting/tally/tally_test.go index 635d9c751..13dbc37fe 100644 --- a/pkg/accounting/tally/tally_test.go +++ b/pkg/accounting/tally/tally_test.go @@ -77,7 +77,7 @@ func TestQueryWithBw(t *testing.T) { func makeBWA(ctx context.Context, t *testing.T, bwDb bwagreement.DB, serialNum string, k *ecdsa.PrivateKey, action pb.PayerBandwidthAllocation_Action) { //generate an agreement with the key - pba, err := test.GeneratePayerBandwidthAllocation(action, k, k, false) + pba, err := test.GeneratePayerBandwidthAllocation(action, k, k, time.Hour) assert.NoError(t, err) rba, err := test.GenerateRenterBandwidthAllocation(pba, teststorj.NodeIDFromString("StorageNodeID"), k) assert.NoError(t, err) diff --git a/pkg/bwagreement/test/common.go b/pkg/bwagreement/test/common.go index 22fcecd6f..f9fe806a9 100644 --- a/pkg/bwagreement/test/common.go +++ b/pkg/bwagreement/test/common.go @@ -20,7 +20,7 @@ import ( ) //GeneratePayerBandwidthAllocation creates a signed PayerBandwidthAllocation from a PayerBandwidthAllocation_Action -func GeneratePayerBandwidthAllocation(action pb.PayerBandwidthAllocation_Action, satelliteKey crypto.PrivateKey, uplinkKey crypto.PrivateKey, expired bool) (*pb.PayerBandwidthAllocation, error) { +func GeneratePayerBandwidthAllocation(action pb.PayerBandwidthAllocation_Action, satelliteKey crypto.PrivateKey, uplinkKey crypto.PrivateKey, expiration time.Duration) (*pb.PayerBandwidthAllocation, error) { satelliteKeyEcdsa, ok := satelliteKey.(*ecdsa.PrivateKey) if !ok { return nil, errs.New("Satellite Private Key is not a valid *ecdsa.PrivateKey") @@ -36,20 +36,12 @@ func GeneratePayerBandwidthAllocation(action pb.PayerBandwidthAllocation_Action, return nil, err } - // generate expiration date - var exp int64 - if expired { - exp = time.Now().AddDate(0, 0, -5).Unix() - } else { - exp = time.Now().Add(time.Hour * 24 * 10).Unix() - } - // Generate PayerBandwidthAllocation_Data data, _ := proto.Marshal( &pb.PayerBandwidthAllocation_Data{ SatelliteId: teststorj.NodeIDFromString("SatelliteID"), UplinkId: teststorj.NodeIDFromString("UplinkID"), - ExpirationUnixSec: exp, + ExpirationUnixSec: time.Now().Add(expiration).Unix(), SerialNumber: serialNum.String(), Action: action, CreatedUnixSec: time.Now().Unix(), diff --git a/pkg/bwagreement/test/server_test.go b/pkg/bwagreement/test/server_test.go index 57fc96814..2ff06abef 100644 --- a/pkg/bwagreement/test/server_test.go +++ b/pkg/bwagreement/test/server_test.go @@ -7,8 +7,10 @@ import ( "context" "crypto/ecdsa" "testing" + "time" - //"github.com/gogo/protobuf/proto" + "github.com/gogo/protobuf/proto" + "github.com/gtank/cryptopasta" "github.com/stretchr/testify/assert" "go.uber.org/zap" @@ -21,19 +23,21 @@ import ( "storj.io/storj/satellite/satellitedb/satellitedbtest" ) -func TestSameSerialNumberBandwidthAgreements(t *testing.T) { +func TestBandwidthAgreement(t *testing.T) { satellitedbtest.Run(t, func(t *testing.T, db satellite.DB) { ctx := testcontext.New(t) defer ctx.Cleanup() - /* More than one storage node can submit bwagreements with the same serial number. - Uplink would like to download a file from 2 storage nodes. - Uplink requests a PayerBandwidthAllocation from the satellite. One serial number for all storage nodes. - Uplink signes 2 RenterBandwidthAllocation for both storage node. */ - satellitePubKey, satellitePrivKey, uplinkPrivKey := generateKeys(ctx, t) - server := bwagreement.NewServer(db.BandwidthAgreement(), zap.NewNop(), satellitePubKey) + testDatabase(ctx, t, db.BandwidthAgreement()) + }) +} - pbaFile1, err := GeneratePayerBandwidthAllocation(pb.PayerBandwidthAllocation_GET, satellitePrivKey, uplinkPrivKey, false) +func testDatabase(ctx context.Context, t *testing.T, bwdb bwagreement.DB) { + satellitePubKey, satellitePrivKey, uplinkPrivKey := generateKeys(ctx, t) + satellite := bwagreement.NewServer(bwdb, zap.NewNop(), satellitePubKey) + + { // TestSameSerialNumberBandwidthAgreements + pbaFile1, err := GeneratePayerBandwidthAllocation(pb.PayerBandwidthAllocation_GET, satellitePrivKey, uplinkPrivKey, time.Hour) assert.NoError(t, err) rbaNode1, err := GenerateRenterBandwidthAllocation(pbaFile1, teststorj.NodeIDFromString("Storage node 1"), uplinkPrivKey) @@ -42,109 +46,261 @@ func TestSameSerialNumberBandwidthAgreements(t *testing.T) { rbaNode2, err := GenerateRenterBandwidthAllocation(pbaFile1, teststorj.NodeIDFromString("Storage node 2"), uplinkPrivKey) assert.NoError(t, err) - reply, err := server.BandwidthAgreements(ctx, rbaNode1) - assert.NoError(t, err) - assert.Equal(t, pb.AgreementsSummary_OK, reply.Status) + /* More than one storage node can submit bwagreements with the same serial number. + Uplink would like to download a file from 2 storage nodes. + Uplink requests a PayerBandwidthAllocation from the satellite. One serial number for all storage nodes. + Uplink signes 2 RenterBandwidthAllocation for both storage node. */ + { + reply, err := satellite.BandwidthAgreements(ctx, rbaNode1) + assert.NoError(t, err) + assert.Equal(t, pb.AgreementsSummary_OK, reply.Status) - reply, err = server.BandwidthAgreements(ctx, rbaNode2) - assert.NoError(t, err) - assert.Equal(t, pb.AgreementsSummary_OK, reply.Status) + reply, err = satellite.BandwidthAgreements(ctx, rbaNode2) + assert.NoError(t, err) + assert.Equal(t, pb.AgreementsSummary_OK, reply.Status) + } /* Storage node can submit a second bwagreement with a different sequence value. Uplink downloads another file. New PayerBandwidthAllocation with a new sequence. */ - pbaFile2, err := GeneratePayerBandwidthAllocation(pb.PayerBandwidthAllocation_GET, satellitePrivKey, uplinkPrivKey, false) - assert.NoError(t, err) + { + pbaFile2, err := GeneratePayerBandwidthAllocation(pb.PayerBandwidthAllocation_GET, satellitePrivKey, uplinkPrivKey, time.Hour) + assert.NoError(t, err) - rbaNode1, err = GenerateRenterBandwidthAllocation(pbaFile2, teststorj.NodeIDFromString("Storage node 1"), uplinkPrivKey) - assert.NoError(t, err) + rbaNode1, err := GenerateRenterBandwidthAllocation(pbaFile2, teststorj.NodeIDFromString("Storage node 1"), uplinkPrivKey) + assert.NoError(t, err) - reply, err = server.BandwidthAgreements(ctx, rbaNode1) - assert.NoError(t, err) - assert.Equal(t, pb.AgreementsSummary_OK, reply.Status) + reply, err := satellite.BandwidthAgreements(ctx, rbaNode1) + assert.NoError(t, err) + assert.Equal(t, pb.AgreementsSummary_OK, reply.Status) + } /* Storage nodes can't submit a second bwagreement with the same sequence. */ - rbaNode1, err = GenerateRenterBandwidthAllocation(pbaFile1, teststorj.NodeIDFromString("Storage node 1"), uplinkPrivKey) - assert.NoError(t, err) + { + rbaNode1, err := GenerateRenterBandwidthAllocation(pbaFile1, teststorj.NodeIDFromString("Storage node 1"), uplinkPrivKey) + assert.NoError(t, err) - reply, err = server.BandwidthAgreements(ctx, rbaNode1) - assert.EqualError(t, err, "bwagreement error: SerialNumber already exists in the PayerBandwidthAllocation") - assert.Equal(t, pb.AgreementsSummary_REJECTED, reply.Status) + reply, err := satellite.BandwidthAgreements(ctx, rbaNode1) + assert.EqualError(t, err, "bwagreement error: SerialNumber already exists in the PayerBandwidthAllocation") + assert.Equal(t, pb.AgreementsSummary_REJECTED, reply.Status) + } /* Storage nodes can't submit the same bwagreement twice. This test is kind of duplicate cause it will most likely trigger the same sequence error. For safety we will try it anyway to make sure nothing strange will happen */ - reply, err = server.BandwidthAgreements(ctx, rbaNode2) - assert.EqualError(t, err, "bwagreement error: SerialNumber already exists in the PayerBandwidthAllocation") - assert.Equal(t, pb.AgreementsSummary_REJECTED, reply.Status) - }) -} + { + reply, err := satellite.BandwidthAgreements(ctx, rbaNode2) + assert.EqualError(t, err, "bwagreement error: SerialNumber already exists in the PayerBandwidthAllocation") + assert.Equal(t, pb.AgreementsSummary_REJECTED, reply.Status) + } + } -func TestManipulatedBandwidthAgreements(t *testing.T) { - satellitedbtest.Run(t, func(t *testing.T, db satellite.DB) { - ctx := testcontext.New(t) - defer ctx.Cleanup() + { // TestExpiredBandwidthAgreements + { // storage nodes can submit a bwagreement that will expire in one second + pba, err := GeneratePayerBandwidthAllocation(pb.PayerBandwidthAllocation_GET, satellitePrivKey, uplinkPrivKey, time.Second) + assert.NoError(t, err) - satellitePubKey, satellitePrivKey, uplinkPrivKey := generateKeys(ctx, t) - server := bwagreement.NewServer(db.BandwidthAgreement(), zap.NewNop(), satellitePubKey) + rba, err := GenerateRenterBandwidthAllocation(pba, teststorj.NodeIDFromString("Storage node 1"), uplinkPrivKey) + assert.NoError(t, err) - pba, err := GeneratePayerBandwidthAllocation(pb.PayerBandwidthAllocation_GET, satellitePrivKey, uplinkPrivKey, false) + reply, err := satellite.BandwidthAgreements(ctx, rba) + assert.NoError(t, err) + assert.Equal(t, pb.AgreementsSummary_OK, reply.Status) + } + + { // storage nodes can't submit a bwagreement that expires right now + pba, err := GeneratePayerBandwidthAllocation(pb.PayerBandwidthAllocation_GET, satellitePrivKey, uplinkPrivKey, 0*time.Second) + assert.NoError(t, err) + + rba, err := GenerateRenterBandwidthAllocation(pba, teststorj.NodeIDFromString("Storage node 1"), uplinkPrivKey) + assert.NoError(t, err) + + reply, err := satellite.BandwidthAgreements(ctx, rba) + assert.Error(t, err) + assert.Equal(t, pb.AgreementsSummary_REJECTED, reply.Status) + } + + { // storage nodes can't submit a bwagreement that expires yesterday + pba, err := GeneratePayerBandwidthAllocation(pb.PayerBandwidthAllocation_GET, satellitePrivKey, uplinkPrivKey, -23*time.Hour-55*time.Second) + assert.NoError(t, err) + + rba, err := GenerateRenterBandwidthAllocation(pba, teststorj.NodeIDFromString("Storage node 1"), uplinkPrivKey) + assert.NoError(t, err) + + reply, err := satellite.BandwidthAgreements(ctx, rba) + assert.Error(t, err) + assert.Equal(t, pb.AgreementsSummary_REJECTED, reply.Status) + } + } + + { // TestManipulatedBandwidthAgreements + pba, err := GeneratePayerBandwidthAllocation(pb.PayerBandwidthAllocation_GET, satellitePrivKey, uplinkPrivKey, time.Hour) assert.NoError(t, err) rba, err := GenerateRenterBandwidthAllocation(pba, teststorj.NodeIDFromString("Storage node 1"), uplinkPrivKey) assert.NoError(t, err) - // Make sure the bwagreement we are using as blueprint is valid and avoid false positives that way. - reply, err := server.BandwidthAgreements(ctx, rba) - assert.NoError(t, err) - assert.Equal(t, pb.AgreementsSummary_OK, reply.Status) - - // storage nodes can't submit an expired bwagreement - expPBA, err := GeneratePayerBandwidthAllocation(pb.PayerBandwidthAllocation_GET, satellitePrivKey, uplinkPrivKey, true) + // Unmarschal Renter and Payer bwagreements + rbaData := &pb.RenterBandwidthAllocation_Data{} + err = proto.Unmarshal(rba.GetData(), rbaData) assert.NoError(t, err) - rba, err = GenerateRenterBandwidthAllocation(expPBA, teststorj.NodeIDFromString("Storage node 1"), uplinkPrivKey) + pbaData := &pb.PayerBandwidthAllocation_Data{} + err = proto.Unmarshal(pba.GetData(), pbaData) assert.NoError(t, err) - reply, err = server.BandwidthAgreements(ctx, rba) - assert.Error(t, err) - assert.Equal(t, pb.AgreementsSummary_REJECTED, reply.Status) + // Storage node manipulates the bwagreement + rbaData.Total = 1337 - /* Todo: Add more tests for bwagreement manipulations - - /* copy and unmarshal pba and rba to manipulate it without overwriting it */ - - /* manipulate PayerBandwidthAllocation -> invalid signature */ - - /* self signed. Storage node sends a self signed bwagreement to get a higher payout */ - }) -} - -func TestInvalidBandwidthAgreements(t *testing.T) { - satellitedbtest.Run(t, func(t *testing.T, db satellite.DB) { - ctx := testcontext.New(t) - defer ctx.Cleanup() - - satellitePubKey, satellitePrivKey, uplinkPrivKey := generateKeys(ctx, t) - server := bwagreement.NewServer(db.BandwidthAgreement(), zap.NewNop(), satellitePubKey) - - pba, err := GeneratePayerBandwidthAllocation(pb.PayerBandwidthAllocation_GET, satellitePrivKey, uplinkPrivKey, false) + // Marschal the manipulated bwagreement + maniprba, err := proto.Marshal(rbaData) assert.NoError(t, err) - rba, err := GenerateRenterBandwidthAllocation(pba, teststorj.NodeIDFromString("Storage node 1"), uplinkPrivKey) + // Generate a new keypair for self signing bwagreements + _, manipPrivKey, _ := generateKeys(ctx, t) + manipPubKey, err := getUplinkPubKey(manipPrivKey) assert.NoError(t, err) - /* Make sure the bwagreement we are using as bluleprint is valid and avoid false positives that way. */ - reply, err := server.BandwidthAgreements(ctx, rba) + /* Storage node can't manipulate the bwagreement size (or any other field) + Satellite will verify Renter's Signature */ + { + // Using uplink signature + reply, err := satellite.BandwidthAgreements(ctx, &pb.RenterBandwidthAllocation{ + Signature: rba.GetSignature(), + Data: maniprba, + }) + + assert.EqualError(t, err, "bwagreement error: Failed to verify Renter's Signature") + assert.Equal(t, pb.AgreementsSummary_REJECTED, reply.Status) + } + + /* Storage node can't sign the manipulated bwagreement + Satellite will verify Renter's Signature */ + { + manipSignature, err := cryptopasta.Sign(maniprba, manipPrivKey) + assert.NoError(t, err) + + // Using self created signature + reply, err := satellite.BandwidthAgreements(ctx, &pb.RenterBandwidthAllocation{ + Signature: manipSignature, + Data: maniprba, + }) + + assert.EqualError(t, err, "bwagreement error: Failed to verify Renter's Signature") + assert.Equal(t, pb.AgreementsSummary_REJECTED, reply.Status) + } + + /* Storage node can't replace uplink PubKey + Satellite will verify Payer's Signature */ + { + // Overwrite the uplink public key with our own keypair + pbaData.PubKey = manipPubKey + + manippba, err := proto.Marshal(pbaData) + assert.NoError(t, err) + + rbaData.PayerAllocation = &pb.PayerBandwidthAllocation{ + Signature: pba.GetSignature(), + Data: manippba, + } + + maniprba, err := proto.Marshal(rbaData) + assert.NoError(t, err) + + manipSignature, err := cryptopasta.Sign(maniprba, manipPrivKey) + assert.NoError(t, err) + + // Using self created signature + public key + reply, err := satellite.BandwidthAgreements(ctx, &pb.RenterBandwidthAllocation{ + Signature: manipSignature, + Data: maniprba, + }) + + assert.EqualError(t, err, "bwagreement error: Failed to verify Payer's Signature") + assert.Equal(t, pb.AgreementsSummary_REJECTED, reply.Status) + } + + /* Storage node can't self sign the PayerBandwidthAllocation. + Satellite will verify the Payer's Signature with his own public key. */ + { + // Overwrite the uplink public key with our own keypair + pbaData.PubKey = manipPubKey + + manippba, err := proto.Marshal(pbaData) + assert.NoError(t, err) + + manipSignature, err := cryptopasta.Sign(manippba, manipPrivKey) + assert.NoError(t, err) + + rbaData.PayerAllocation = &pb.PayerBandwidthAllocation{ + Signature: manipSignature, + Data: manippba, + } + + maniprba, err := proto.Marshal(rbaData) + assert.NoError(t, err) + + manipSignature, err = cryptopasta.Sign(maniprba, manipPrivKey) + assert.NoError(t, err) + + // Using self created Payer and Renter bwagreement signatures + reply, err := satellite.BandwidthAgreements(ctx, &pb.RenterBandwidthAllocation{ + Signature: manipSignature, + Data: maniprba, + }) + + assert.EqualError(t, err, "bwagreement error: Failed to verify Payer's Signature") + assert.Equal(t, pb.AgreementsSummary_REJECTED, reply.Status) + } + } + + { //TestInvalidBandwidthAgreements + pba, err := GeneratePayerBandwidthAllocation(pb.PayerBandwidthAllocation_GET, satellitePrivKey, uplinkPrivKey, time.Hour) assert.NoError(t, err) - assert.Equal(t, pb.AgreementsSummary_OK, reply.Status) - /* Storage node sends an corrupted signuature to force a satellite crash */ - rba.Signature = []byte("invalid") + { // Storage node sends an corrupted signuature to force a satellite crash + rba, err := GenerateRenterBandwidthAllocation(pba, teststorj.NodeIDFromString("Storage node 1"), uplinkPrivKey) + assert.NoError(t, err) - reply, err = server.BandwidthAgreements(ctx, rba) - assert.EqualError(t, err, "bwagreement error: Invalid Renter's Signature Length") - assert.Equal(t, pb.AgreementsSummary_REJECTED, reply.Status) - }) + rba.Signature = []byte("invalid") + + reply, err := satellite.BandwidthAgreements(ctx, rba) + assert.EqualError(t, err, "bwagreement error: Invalid Renter's Signature Length") + assert.Equal(t, pb.AgreementsSummary_REJECTED, reply.Status) + } + + { // Storage node sends an corrupted uplink pubkey to force a crash + rba, err := GenerateRenterBandwidthAllocation(pba, teststorj.NodeIDFromString("Storage node 2"), uplinkPrivKey) + assert.NoError(t, err) + + rbaData := &pb.RenterBandwidthAllocation_Data{} + err = proto.Unmarshal(rba.GetData(), rbaData) + assert.NoError(t, err) + + pbaData := &pb.PayerBandwidthAllocation_Data{} + err = proto.Unmarshal(pba.GetData(), pbaData) + assert.NoError(t, err) + + pbaData.PubKey = nil + + invalidpba, err := proto.Marshal(pbaData) + assert.NoError(t, err) + + rbaData.PayerAllocation = &pb.PayerBandwidthAllocation{ + Signature: pba.GetSignature(), + Data: invalidpba, + } + + invalidrba, err := proto.Marshal(rbaData) + assert.NoError(t, err) + + reply, err := satellite.BandwidthAgreements(ctx, &pb.RenterBandwidthAllocation{ + Signature: rba.GetSignature(), + Data: invalidrba, + }) + assert.EqualError(t, err, "bwagreement error: Failed to extract Public Key from RenterBandwidthAllocation: asn1: syntax error: sequence truncated") + assert.Equal(t, pb.AgreementsSummary_REJECTED, reply.Status) + } + } } func generateKeys(ctx context.Context, t *testing.T) (satellitePubKey *ecdsa.PublicKey, satellitePrivKey *ecdsa.PrivateKey, uplinkPrivKey *ecdsa.PrivateKey) {