From 37052f1561923ea3436d1e218b520fd3a78a620a Mon Sep 17 00:00:00 2001 From: Conner Fromknecht Date: Thu, 13 Jun 2019 17:36:27 -0700 Subject: [PATCH] watchtower/wtdb: only accept properly sized blobs Modifies the bbolt and mock tower databases to only accept blobs that are the expected size of the session's blob type. This prevents resource exhaustion attacks where a client may provide disproportionately large encrypted blob, even though all supported blob types are of fixed-size. --- watchtower/wtdb/tower_db.go | 11 +++++++++++ watchtower/wtdb/tower_db_test.go | 28 ++++++++++++++++++++++++++++ watchtower/wtmock/tower_db.go | 5 +++++ 3 files changed, 44 insertions(+) diff --git a/watchtower/wtdb/tower_db.go b/watchtower/wtdb/tower_db.go index 2defce21..8610f04d 100644 --- a/watchtower/wtdb/tower_db.go +++ b/watchtower/wtdb/tower_db.go @@ -46,6 +46,10 @@ var ( // ErrNoSessionHintIndex signals that an active session does not have an // initialized index for tracking its own state updates. ErrNoSessionHintIndex = errors.New("session hint index missing") + + // ErrInvalidBlobSize indicates that the encrypted blob provided by the + // client is not valid according to the blob type of the session. + ErrInvalidBlobSize = errors.New("invalid blob size") ) // TowerDB is single database providing a persistent storage engine for the @@ -233,6 +237,13 @@ func (t *TowerDB) InsertStateUpdate(update *SessionStateUpdate) (uint16, error) return err } + // Assert that the blob is the correct size for the session's + // blob type. + expBlobSize := blob.Size(session.Policy.BlobType) + if len(update.EncryptedBlob) != expBlobSize { + return ErrInvalidBlobSize + } + // Validate the update against the current state of the session. err = session.AcceptUpdateSequence( update.SeqNum, update.LastApplied, diff --git a/watchtower/wtdb/tower_db_test.go b/watchtower/wtdb/tower_db_test.go index a1f75abb..506f9b17 100644 --- a/watchtower/wtdb/tower_db_test.go +++ b/watchtower/wtdb/tower_db_test.go @@ -586,6 +586,30 @@ var stateUpdateRevertLastApplied = stateUpdateTest{ }, } +var stateUpdateInvalidBlobSize = stateUpdateTest{ + session: &wtdb.SessionInfo{ + ID: *id(0), + Policy: wtpolicy.Policy{ + TxPolicy: wtpolicy.TxPolicy{ + BlobType: blob.TypeAltruistCommit, + }, + MaxUpdates: 3, + }, + RewardAddress: []byte{}, + }, + updates: []*wtdb.SessionStateUpdate{ + { + ID: *id(0), + SeqNum: 1, + LastApplied: 0, + EncryptedBlob: []byte{0x01, 0x02, 0x03}, // too $hort + }, + }, + updateErrs: []error{ + wtdb.ErrInvalidBlobSize, + }, +} + func TestTowerDB(t *testing.T) { dbs := []struct { name string @@ -703,6 +727,10 @@ func TestTowerDB(t *testing.T) { name: "state update revert last applied", run: runStateUpdateTest(stateUpdateRevertLastApplied), }, + { + name: "invalid blob size", + run: runStateUpdateTest(stateUpdateInvalidBlobSize), + }, { name: "multiple breach matches", run: testMultipleMatches, diff --git a/watchtower/wtmock/tower_db.go b/watchtower/wtmock/tower_db.go index 27cc25f9..1c7b94c9 100644 --- a/watchtower/wtmock/tower_db.go +++ b/watchtower/wtmock/tower_db.go @@ -37,6 +37,11 @@ func (db *TowerDB) InsertStateUpdate(update *wtdb.SessionStateUpdate) (uint16, e return 0, wtdb.ErrSessionNotFound } + // Assert that the blob is the correct size for the session's blob type. + if len(update.EncryptedBlob) != blob.Size(info.Policy.BlobType) { + return 0, wtdb.ErrInvalidBlobSize + } + err := info.AcceptUpdateSequence(update.SeqNum, update.LastApplied) if err != nil { return info.LastApplied, err