From f3112d4ba6fec97b148638f37f415592c92ab92e Mon Sep 17 00:00:00 2001 From: 9seconds Date: Thu, 18 Mar 2021 11:07:51 +0300 Subject: [PATCH] Add timeattack detector --- mtglib/init.go | 5 +++++ mtglib/proxy.go | 13 +++++++------ mtglib/proxy_opts.go | 13 +++++++------ timeattack/detector.go | 36 ++++++++++++++++++++++++++++++++++++ timeattack/detector_test.go | 28 ++++++++++++++++++++++++++++ timeattack/noop.go | 15 +++++++++++++++ timeattack/noop_test.go | 26 ++++++++++++++++++++++++++ 7 files changed, 124 insertions(+), 12 deletions(-) create mode 100644 timeattack/detector.go create mode 100644 timeattack/detector_test.go create mode 100644 timeattack/noop.go create mode 100644 timeattack/noop_test.go diff --git a/mtglib/init.go b/mtglib/init.go index 3ea5e30..826f124 100644 --- a/mtglib/init.go +++ b/mtglib/init.go @@ -5,6 +5,7 @@ import ( "errors" "net" "net/http" + "time" ) var ( @@ -46,6 +47,10 @@ type EventStream interface { Shutdown() } +type TimeAttackDetector interface { + Valid(time.Time) error +} + type Logger interface { Named(name string) Logger diff --git a/mtglib/proxy.go b/mtglib/proxy.go index 7ae99a8..90adaa2 100644 --- a/mtglib/proxy.go +++ b/mtglib/proxy.go @@ -17,12 +17,13 @@ type Proxy struct { streamWaitGroup sync.WaitGroup workerPool *ants.PoolWithFunc - secret Secret - network Network - antiReplayCache AntiReplayCache - ipBlocklist IPBlocklist - eventStream EventStream - logger Logger + secret Secret + network Network + timeAttackDetector TimeAttackDetector + antiReplayCache AntiReplayCache + ipBlocklist IPBlocklist + eventStream EventStream + logger Logger } func (p *Proxy) ServeConn(conn net.Conn) { diff --git a/mtglib/proxy_opts.go b/mtglib/proxy_opts.go index a18763f..9067fb3 100644 --- a/mtglib/proxy_opts.go +++ b/mtglib/proxy_opts.go @@ -3,12 +3,13 @@ package mtglib import "time" type ProxyOpts struct { - Secret Secret - Network Network - AntiReplayCache AntiReplayCache - IPBlocklist IPBlocklist - EventStream EventStream - Logger Logger + Secret Secret + Network Network + AntiReplayCache AntiReplayCache + TimeAttackDetector TimeAttackDetector + IPBlocklist IPBlocklist + EventStream EventStream + Logger Logger BufferSize uint Concurrency uint diff --git a/timeattack/detector.go b/timeattack/detector.go new file mode 100644 index 0000000..ba70d3b --- /dev/null +++ b/timeattack/detector.go @@ -0,0 +1,36 @@ +package timeattack + +import ( + "fmt" + "time" + + "github.com/9seconds/mtg/v2/mtglib" +) + +type detector struct { + time.Duration +} + +func (d detector) Valid(then time.Time) error { + now := time.Now() + + diff := now.Sub(then) + if diff < 0 { + diff = -diff + } + + if diff > d.Duration { + return fmt.Errorf("time is invalid. now=%d, then=%d, diff=%v", + now.Unix(), + then.Unix(), + diff) + } + + return nil +} + +func NewDetector(duration time.Duration) mtglib.TimeAttackDetector { + return detector{ + Duration: duration, + } +} diff --git a/timeattack/detector_test.go b/timeattack/detector_test.go new file mode 100644 index 0000000..dd4bd9f --- /dev/null +++ b/timeattack/detector_test.go @@ -0,0 +1,28 @@ +package timeattack_test + +import ( + "testing" + "time" + + "github.com/9seconds/mtg/v2/timeattack" + "github.com/stretchr/testify/suite" +) + +type DetectorTestSuite struct { + suite.Suite +} + +func (suite *DetectorTestSuite) TestOp() { + d := timeattack.NewDetector(time.Second) + + suite.NoError(d.Valid(time.Now())) + suite.NoError(d.Valid(time.Now().Add(100 * time.Millisecond))) + suite.NoError(d.Valid(time.Now().Add(-100 * time.Millisecond))) + suite.Error(d.Valid(time.Now().Add(time.Hour))) + suite.Error(d.Valid(time.Now().Add(-time.Hour))) +} + +func TestDetector(t *testing.T) { + t.Parallel() + suite.Run(t, &DetectorTestSuite{}) +} diff --git a/timeattack/noop.go b/timeattack/noop.go new file mode 100644 index 0000000..43537c5 --- /dev/null +++ b/timeattack/noop.go @@ -0,0 +1,15 @@ +package timeattack + +import ( + "time" + + "github.com/9seconds/mtg/v2/mtglib" +) + +type noop struct{} + +func (n noop) Valid(_ time.Time) error { return nil } + +func NewNoop() mtglib.TimeAttackDetector { + return noop{} +} diff --git a/timeattack/noop_test.go b/timeattack/noop_test.go new file mode 100644 index 0000000..4b3da79 --- /dev/null +++ b/timeattack/noop_test.go @@ -0,0 +1,26 @@ +package timeattack_test + +import ( + "testing" + "time" + + "github.com/9seconds/mtg/v2/timeattack" + "github.com/stretchr/testify/suite" +) + +type NoopTestSuite struct { + suite.Suite +} + +func (suite *NoopTestSuite) TestOp() { + d := timeattack.NewNoop() + + suite.NoError(d.Valid(time.Now())) + suite.NoError(d.Valid(time.Now().Add(time.Hour))) + suite.NoError(d.Valid(time.Now().Add(-time.Hour))) +} + +func TestNoop(t *testing.T) { + t.Parallel() + suite.Run(t, &NoopTestSuite{}) +}