From 9030f86144a7eff5f8ad4d3f7879a754f77ebe29 Mon Sep 17 00:00:00 2001 From: JordiSubira Date: Mon, 12 Jun 2023 12:56:25 +0200 Subject: [PATCH] new ts spec implementation fix tests --- control/config/drkey.go | 9 ++ pkg/slayers/pkt_auth.go | 69 +++++--------- pkg/slayers/pkt_auth_test.go | 51 ++++------- pkg/spao/BUILD.bazel | 2 +- pkg/spao/mac.go | 11 +-- pkg/spao/mac_test.go | 57 ++++++------ pkg/spao/timestamp.go | 27 +++--- private/drkey/BUILD.bazel | 14 +++ private/drkey/provider.go | 110 +++++++++++++++++++++++ router/BUILD.bazel | 2 + router/dataplane.go | 103 +++++++++++---------- tools/braccept/BUILD.bazel | 1 + tools/braccept/cases/BUILD.bazel | 2 + tools/braccept/cases/scmp.go | 19 ++-- tools/braccept/cases/scmp_expired_hop.go | 53 ++++++++++- tools/braccept/cases/scmp_invalid_mac.go | 3 +- tools/braccept/cases/scmp_traceroute.go | 45 +++++++--- tools/braccept/main.go | 8 ++ 18 files changed, 383 insertions(+), 203 deletions(-) create mode 100644 private/drkey/BUILD.bazel create mode 100644 private/drkey/provider.go diff --git a/control/config/drkey.go b/control/config/drkey.go index 27df174c26..9e57e0978a 100644 --- a/control/config/drkey.go +++ b/control/config/drkey.go @@ -31,6 +31,15 @@ const ( DefaultEpochDuration = 24 * time.Hour DefaultPrefetchEntries = 10000 EnvVarEpochDuration = "SCION_TESTING_DRKEY_EPOCH_DURATION" + // DefaultAcceptanceWindowOffset is the time width for accepting incoming packets. The + // acceptance widown is then compute as: + // aw := [T-a, T+a) + // where aw:= acceptance window, T := time instant and a := acceptanceWindowOffset + // + // Picking the value equal or shorter than half of the drkey Grace Period ensures + // that we accept packets for active keys only. + DefaultAcceptanceWindowOffset = 2*time.Second + 500*time.Millisecond + EnvVarAccpetanceWindow = "SCION_TESTING_ACCEPTANCE_WINDOW" ) var _ (config.Config) = (*DRKeyConfig)(nil) diff --git a/pkg/slayers/pkt_auth.go b/pkg/slayers/pkt_auth.go index eb3db8115c..57f098eb81 100644 --- a/pkg/slayers/pkt_auth.go +++ b/pkg/slayers/pkt_auth.go @@ -21,9 +21,9 @@ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | Security Parameter Index | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// | Algorithm | Timestamp | -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// | RSV | Sequence Number | +// | Algorithm | RSV | | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | +// | Timestamp / Sequence Number | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | | // + + @@ -52,17 +52,12 @@ const ( PacketAuthReceiverSide ) -const ( - PacketAuthLater uint8 = iota - PacketAuthEarlier -) - const ( // PacketAuthOptionMetadataLen is the size of the SPAO Metadata and // corresponds the minimum size of the SPAO OptData. // The SPAO header contains the following fixed-length fields: - // SPI (4 Bytes), Algorithm (1 Byte), Timestamp (3 Bytes), - // RSV (1 Byte) and Sequence Number (3 Bytes). + // SPI (4 Bytes), Algorithm (1 Byte), RSV (1 Byte) and + // Timestamp / Sequence Number (6 Bytes). PacketAuthOptionMetadataLen = 12 ) @@ -72,26 +67,19 @@ const ( type PacketAuthSPI uint32 func (p PacketAuthSPI) Type() uint8 { - if p&(1<<18) == 0 { + if p&(1<<17) == 0 { return PacketAuthASHost } return PacketAuthHostHost } func (p PacketAuthSPI) Direction() uint8 { - if p&(1<<17) == 0 { + if p&(1<<16) == 0 { return PacketAuthSenderSide } return PacketAuthReceiverSide } -func (p PacketAuthSPI) Epoch() uint8 { - if p&(1<<16) == 0 { - return PacketAuthLater - } - return PacketAuthEarlier -} - func (p PacketAuthSPI) DRKeyProto() uint16 { return uint16(p) } @@ -104,7 +92,6 @@ func MakePacketAuthSPIDRKey( proto uint16, drkeyType uint8, dir uint8, - epoch uint8, ) (PacketAuthSPI, error) { if proto < 1 { @@ -116,12 +103,8 @@ func MakePacketAuthSPIDRKey( if dir > 1 { return 0, serrors.New("Invalid DRKeyDirection value") } - if epoch > 1 { - return 0, serrors.New("Invalid DRKeyEpochType value") - } - spi := uint32((drkeyType & 0x1)) << 18 - spi |= uint32((dir & 0x1)) << 17 - spi |= uint32((epoch & 0x1)) << 16 + spi := uint32((drkeyType & 0x1)) << 17 + spi |= uint32((dir & 0x1)) << 16 spi |= uint32(proto) return PacketAuthSPI(spi), nil @@ -137,11 +120,10 @@ const ( ) type PacketAuthOptionParams struct { - SPI PacketAuthSPI - Algorithm PacketAuthAlg - Timestamp uint32 - SequenceNumber uint32 - Auth []byte + SPI PacketAuthSPI + Algorithm PacketAuthAlg + TimestampSN uint64 + Auth []byte } // PacketAuthOption wraps an EndToEndOption of OptTypeAuthenticator. @@ -185,12 +167,9 @@ func (o PacketAuthOption) Reset( p PacketAuthOptionParams, ) error { - if p.Timestamp >= (1 << 24) { + if p.TimestampSN >= (1 << 48) { return serrors.New("Timestamp value should be smaller than 2^24") } - if p.SequenceNumber >= (1 << 24) { - return serrors.New("Sequence number should be smaller than 2^24") - } o.OptType = OptTypeAuthenticator @@ -202,13 +181,10 @@ func (o PacketAuthOption) Reset( } binary.BigEndian.PutUint32(o.OptData[:4], uint32(p.SPI)) o.OptData[4] = byte(p.Algorithm) - o.OptData[5] = byte(p.Timestamp >> 16) - o.OptData[6] = byte(p.Timestamp >> 8) - o.OptData[7] = byte(p.Timestamp) - o.OptData[8] = byte(0) - o.OptData[9] = byte(p.SequenceNumber >> 16) - o.OptData[10] = byte(p.SequenceNumber >> 8) - o.OptData[11] = byte(p.SequenceNumber) + o.OptData[5] = byte(0) + o.OptData[6] = byte(p.TimestampSN >> 40) + o.OptData[7] = byte(p.TimestampSN >> 32) + binary.BigEndian.PutUint32(o.OptData[8:12], uint32(p.TimestampSN)) copy(o.OptData[12:], p.Auth) o.OptAlign = [2]uint8{4, 2} @@ -229,13 +205,8 @@ func (o PacketAuthOption) Algorithm() PacketAuthAlg { } // Timestamp returns the value set in the homonym field in the extension. -func (o PacketAuthOption) Timestamp() uint32 { - return uint32(o.OptData[5])<<16 + uint32(o.OptData[6])<<8 + uint32(o.OptData[7]) -} - -// SequenceNumber returns the value set in the homonym field in the extension. -func (o PacketAuthOption) SequenceNumber() uint32 { - return uint32(o.OptData[9])<<16 + uint32(o.OptData[10])<<8 + uint32(o.OptData[11]) +func (o PacketAuthOption) TimestampSN() uint64 { + return uint64(o.OptData[6])<<40 + uint64(o.OptData[7])<<32 + uint64(binary.BigEndian.Uint32(o.OptData[8:12])) } // Authenticator returns slice of the underlying auth buffer. diff --git a/pkg/slayers/pkt_auth_test.go b/pkg/slayers/pkt_auth_test.go index 1b102192e3..acd182b642 100644 --- a/pkg/slayers/pkt_auth_test.go +++ b/pkg/slayers/pkt_auth_test.go @@ -27,17 +27,16 @@ import ( var ( algo = slayers.PacketAuthSHA1_AES_CBC - ts = uint32(0x030201) - sn = uint32(0x060504) + ts = uint64(0x060504030201) optAuthMAC = []byte("16byte_mac_foooo") ) var rawE2EOptAuth = append( []byte{ 0x11, 0x7, 0x2, 0x1c, - 0x0, 0x2, 0x0, 0x1, - 0x1, 0x3, 0x2, 0x1, - 0x0, 0x6, 0x5, 0x4, + 0x0, 0x1, 0x0, 0x1, + 0x1, 0x0, 0x6, 0x5, + 0x4, 0x3, 0x2, 0x1, }, optAuthMAC..., ) @@ -47,8 +46,7 @@ func TestOptAuthenticatorSerialize(t *testing.T) { name string spiFunc func(t *testing.T) slayers.PacketAuthSPI algo slayers.PacketAuthAlg - ts uint32 - sn uint32 + ts uint64 optAuth []byte errorFunc assert.ErrorAssertionFunc }{ @@ -57,25 +55,16 @@ func TestOptAuthenticatorSerialize(t *testing.T) { spiFunc: initSPI, algo: algo, ts: ts, - sn: sn, optAuth: optAuthMAC, errorFunc: assert.NoError, }, { - name: "bad_ts", - spiFunc: initSPI, - algo: algo, - ts: binary.LittleEndian.Uint32([]byte{0, 0, 0, 1}), - sn: sn, - optAuth: optAuthMAC, - errorFunc: assert.Error, - }, - { - name: "bad_sn", - spiFunc: initSPI, - algo: algo, - ts: ts, - sn: binary.LittleEndian.Uint32([]byte{0, 0, 0, 1}), + name: "bad_ts", + spiFunc: initSPI, + algo: algo, + ts: binary.LittleEndian.Uint64( + []byte{0, 0, 0, 0, 0, 0, 0, 1}, + ), optAuth: optAuthMAC, errorFunc: assert.Error, }, @@ -84,11 +73,10 @@ func TestOptAuthenticatorSerialize(t *testing.T) { t.Run(c.name, func(t *testing.T) { spao, err := slayers.NewPacketAuthOption(slayers.PacketAuthOptionParams{ - SPI: c.spiFunc(t), - Algorithm: c.algo, - Timestamp: c.ts, - SequenceNumber: c.sn, - Auth: c.optAuth, + SPI: c.spiFunc(t), + Algorithm: c.algo, + TimestampSN: c.ts, + Auth: c.optAuth, }) c.errorFunc(t, err) if err != nil { @@ -122,17 +110,15 @@ func TestOptAuthenticatorDeserialize(t *testing.T) { assert.Equal(t, initSPI(t), auth.SPI(), "SPI") assert.Equal(t, slayers.PacketAuthASHost, auth.SPI().Type()) assert.Equal(t, slayers.PacketAuthReceiverSide, auth.SPI().Direction()) - assert.Equal(t, slayers.PacketAuthLater, auth.SPI().Epoch()) assert.Equal(t, true, auth.SPI().IsDRKey()) assert.Equal(t, algo, auth.Algorithm(), "Algorithm Type") - assert.Equal(t, ts, auth.Timestamp(), "Timestamp") - assert.Equal(t, sn, auth.SequenceNumber(), "Sequence Number") + assert.Equal(t, ts, auth.TimestampSN(), "TimestampSN") assert.Equal(t, optAuthMAC, auth.Authenticator(), "Authenticator data (MAC)") } func TestMakePacketAuthSPIDrkey(t *testing.T) { spi := initSPI(t) - assert.EqualValues(t, binary.BigEndian.Uint32([]byte{0, 2, 0, 1}), spi) + assert.EqualValues(t, binary.BigEndian.Uint32([]byte{0, 1, 0, 1}), spi) } func TestOptAuthenticatorDeserializeCorrupt(t *testing.T) { @@ -159,8 +145,7 @@ func initSPI(t *testing.T) slayers.PacketAuthSPI { spi, err := slayers.MakePacketAuthSPIDRKey( 1, slayers.PacketAuthASHost, - slayers.PacketAuthReceiverSide, - slayers.PacketAuthLater) + slayers.PacketAuthReceiverSide) require.NoError(t, err) return spi } diff --git a/pkg/spao/BUILD.bazel b/pkg/spao/BUILD.bazel index 390c3f26c2..e91e0d5158 100644 --- a/pkg/spao/BUILD.bazel +++ b/pkg/spao/BUILD.bazel @@ -10,8 +10,8 @@ go_library( importpath = "github.com/scionproto/scion/pkg/spao", visibility = ["//visibility:public"], deps = [ + "//pkg/drkey:go_default_library", "//pkg/private/serrors:go_default_library", - "//pkg/private/util:go_default_library", "//pkg/slayers:go_default_library", "//pkg/slayers/path:go_default_library", "//pkg/slayers/path/empty:go_default_library", diff --git a/pkg/spao/mac.go b/pkg/spao/mac.go index d594ef88dc..781e2ba3da 100644 --- a/pkg/spao/mac.go +++ b/pkg/spao/mac.go @@ -127,13 +127,10 @@ func serializeAuthenticatedData( buf[1] = byte(pldType) binary.BigEndian.PutUint16(buf[2:], uint16(len(pld))) buf[4] = byte(opt.Algorithm()) - buf[5] = byte(opt.Timestamp() >> 16) - buf[6] = byte(opt.Timestamp() >> 8) - buf[7] = byte(opt.Timestamp()) - buf[8] = byte(0) - buf[9] = byte(opt.SequenceNumber() >> 16) - buf[10] = byte(opt.SequenceNumber() >> 8) - buf[11] = byte(opt.SequenceNumber()) + buf[5] = byte(0) + buf[6] = byte(opt.TimestampSN() >> 40) + buf[7] = byte(opt.TimestampSN() >> 32) + binary.BigEndian.PutUint32(buf[8:12], uint32(opt.TimestampSN())) firstHdrLine := uint32(s.Version&0xF)<<28 | uint32(s.TrafficClass&0x3f)<<20 | s.FlowID&0xFFFFF binary.BigEndian.PutUint32(buf[12:], firstHdrLine) buf[16] = byte(s.PathType) diff --git a/pkg/spao/mac_test.go b/pkg/spao/mac_test.go index 08e6e8c167..3b74398ab7 100644 --- a/pkg/spao/mac_test.go +++ b/pkg/spao/mac_test.go @@ -39,7 +39,6 @@ func TestComputeAuthMac(t *testing.T) { srcIA := xtest.MustParseIA("1-ff00:0:111") dstIA := xtest.MustParseIA("1-ff00:0:112") authKey := drkey.Key{0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7} - sn := uint32(0x060504) ts := uint32(0x030201) fooPayload := []byte("some payload") decodedPath := &scion.Decoded{ @@ -108,11 +107,10 @@ func TestComputeAuthMac(t *testing.T) { }{ "empty": { optionParameter: slayers.PacketAuthOptionParams{ - SPI: slayers.PacketAuthSPI(0x1), - Algorithm: slayers.PacketAuthCMAC, - Timestamp: 0x3e8, - SequenceNumber: sn, - Auth: make([]byte, 16), + SPI: slayers.PacketAuthSPI(0x1), + Algorithm: slayers.PacketAuthCMAC, + TimestampSN: 0x060504030201, + Auth: make([]byte, 16), }, scionL: slayers.SCION{ FlowID: binary.BigEndian.Uint32([]byte{0x00, 0x00, 0x12, 0x34}), @@ -131,8 +129,9 @@ func TestComputeAuthMac(t *testing.T) { rawMACInput: append([]byte{ // 1. Authenticator Option Metadata 0x9, 0xca, 0x0, 0xc, // HdrLen | Upper Layer | Upper-Layer Packet Length - 0x0, 0x0, 0x3, 0xe8, // Algorithm | Timestamp - 0x0, 0x6, 0x5, 0x4, // RSV | Sequence Number + 0x0, 0x0, // Algorithm | RSV + 0x6, 0x5, 0x4, 0x3, // Timestamp / Sequence Number + 0x2, 0x1, // 2. SCION Common Header 0x3, 0xf0, 0x12, 0x34, // Version | TrafficClass | FlowID 0x0, 0x0, 0x0, 0x0, // PathType |DT |DL |ST |SL | RSV @@ -143,11 +142,10 @@ func TestComputeAuthMac(t *testing.T) { }, "decoded": { optionParameter: slayers.PacketAuthOptionParams{ - SPI: slayers.PacketAuthSPI(0x1), - Algorithm: slayers.PacketAuthCMAC, - Timestamp: 0x3e8, - SequenceNumber: sn, - Auth: make([]byte, 16), + SPI: slayers.PacketAuthSPI(0x1), + Algorithm: slayers.PacketAuthCMAC, + TimestampSN: 0x060504030201, + Auth: make([]byte, 16), }, scionL: slayers.SCION{ FlowID: binary.BigEndian.Uint32([]byte{0x00, 0x00, 0x12, 0x34}), @@ -166,8 +164,9 @@ func TestComputeAuthMac(t *testing.T) { rawMACInput: append([]byte{ // 1. Authenticator Option Metadata 0x1c, 0xca, 0x0, 0xc, // HdrLen | Upper Layer | Upper-Layer Packet Length - 0x0, 0x0, 0x3, 0xe8, // Algorithm | Timestamp - 0x0, 0x6, 0x5, 0x4, // RSV | Sequence Number + 0x0, 0x0, // Algorithm | RSV + 0x6, 0x5, 0x4, 0x3, // Timestamp / Sequence Number + 0x2, 0x1, // 2. SCION Common Header 0x3, 0xf0, 0x12, 0x34, // Version | TrafficClass | FlowID 0x1, 0x43, 0x0, 0x0, // PathType |DT |DL |ST |SL | RSV @@ -198,11 +197,10 @@ func TestComputeAuthMac(t *testing.T) { }, "one hop": { optionParameter: slayers.PacketAuthOptionParams{ - SPI: slayers.PacketAuthSPI(0x1), - Algorithm: slayers.PacketAuthCMAC, - Timestamp: 0x3e8, - SequenceNumber: sn, - Auth: make([]byte, 16), + SPI: slayers.PacketAuthSPI(0x1), + Algorithm: slayers.PacketAuthCMAC, + TimestampSN: 0x060504030201, + Auth: make([]byte, 16), }, scionL: slayers.SCION{ FlowID: binary.BigEndian.Uint32([]byte{0x00, 0x00, 0x12, 0x34}), @@ -242,8 +240,9 @@ func TestComputeAuthMac(t *testing.T) { rawMACInput: append([]byte{ // 1. Authenticator Option Metadata 0x11, 0xca, 0x0, 0xc, // HdrLen | Upper Layer | Upper-Layer Packet Length - 0x0, 0x0, 0x3, 0xe8, // Algorithm | Timestamp - 0x0, 0x6, 0x5, 0x4, // RSV | Sequence Number + 0x0, 0x0, // Algorithm | RSV + 0x6, 0x5, 0x4, 0x3, // Timestamp / Sequence Number + 0x2, 0x1, // 2. SCION Common Header 0x3, 0xf0, 0x12, 0x34, // Version | TrafficClass | FlowID 0x2, 0x0, 0x0, 0x0, // PathType |DT |DL |ST |SL | RSV @@ -263,11 +262,10 @@ func TestComputeAuthMac(t *testing.T) { }, "epic": { optionParameter: slayers.PacketAuthOptionParams{ - SPI: slayers.PacketAuthSPI(2 ^ 21 - 1), - Algorithm: slayers.PacketAuthCMAC, - Timestamp: 0x3e8, - SequenceNumber: sn, - Auth: make([]byte, 16), + SPI: slayers.PacketAuthSPI(2 ^ 21 - 1), + Algorithm: slayers.PacketAuthCMAC, + TimestampSN: 0x060504030201, + Auth: make([]byte, 16), }, scionL: slayers.SCION{ FlowID: binary.BigEndian.Uint32([]byte{0x00, 0x00, 0x12, 0x34}), @@ -298,8 +296,9 @@ func TestComputeAuthMac(t *testing.T) { // 1. Authenticator Option Metadata 0x1d, 0xca, 0x0, 0xc, // HdrLen | Upper Layer | Upper-Layer Packet Length - 0x0, 0x0, 0x3, 0xe8, // Algorithm | Timestamp - 0x0, 0x6, 0x5, 0x4, // RSV | Sequence Number + 0x0, 0x0, // Algorithm | RSV + 0x6, 0x5, 0x4, 0x3, // Timestamp / Sequence Number + 0x2, 0x1, // 2. SCION Common Header 0x3, 0xf0, 0x12, 0x34, // Version | TrafficClass | FlowID 0x3, 0x0, 0x0, 0x0, // PathType |DT |DL |ST |SL | RSV diff --git a/pkg/spao/timestamp.go b/pkg/spao/timestamp.go index 0298340e2e..c7375841a0 100644 --- a/pkg/spao/timestamp.go +++ b/pkg/spao/timestamp.go @@ -17,24 +17,23 @@ package spao import ( "time" + "github.com/scionproto/scion/pkg/drkey" "github.com/scionproto/scion/pkg/private/serrors" - "github.com/scionproto/scion/pkg/private/util" ) -// RelativeTimestamp computes the relative timestamp (spaoTS) where: -// now = ts+spaoTSā‹…š¯‘˛, (where q := 6 ms and ts = info[0].Timestamp, i.e., -// the timestamp field in the first InfoField). -func RelativeTimestamp(ts uint32, now time.Time) (uint32, error) { - timestamp := now.Sub(util.SecsToTime(ts)).Milliseconds() / 6 - if timestamp >= (1 << 24) { - return 0, serrors.New("relative timestamp is bigger than 2^24-1") +// RelativeTimestamp returns the relative timestamp (RelTime) as the time diference from +// time instant t to the begining of the drkey epoch. +func RelativeTimestamp(key drkey.ASHostKey, t time.Time) (uint64, error) { + relTime := t.Sub(key.Epoch.NotBefore).Nanoseconds() + if relTime >= (1 << 48) { + return 0, serrors.New("relative timestamp is bigger than 2^48-1") } - return uint32(timestamp), nil + return uint64(relTime), nil } -// Time computes the time instant (then) where: -// then = ts + spaoTSā‹…š¯‘˛, (where q := 6 ms and ts = info[0].Timestamp, i.e., -// the timestamp field in the first InfoField). -func Time(ts uint32, spaoTS uint32) time.Time { - return util.SecsToTime(ts).Add(time.Millisecond * time.Duration(spaoTS) * 6) +// AbsoluteTimestamp returns the absolute timestamp (AbsTime) based on the +// relTime (Timestamp / Sequence Number field in SPAO hedaer) and the DRKey +// information. +func AbsoluteTimestamp(key drkey.ASHostKey, relTime uint64) time.Time { + return key.Epoch.NotBefore.Add(time.Duration(relTime)) } diff --git a/private/drkey/BUILD.bazel b/private/drkey/BUILD.bazel new file mode 100644 index 0000000000..4e1c76d4dc --- /dev/null +++ b/private/drkey/BUILD.bazel @@ -0,0 +1,14 @@ +load("//tools/lint:go.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = ["provider.go"], + importpath = "github.com/scionproto/scion/private/drkey", + visibility = ["//visibility:public"], + deps = [ + "//pkg/addr:go_default_library", + "//pkg/drkey:go_default_library", + "//pkg/private/serrors:go_default_library", + "//pkg/spao:go_default_library", + ], +) diff --git a/private/drkey/provider.go b/private/drkey/provider.go new file mode 100644 index 0000000000..29844e4c95 --- /dev/null +++ b/private/drkey/provider.go @@ -0,0 +1,110 @@ +// Copyright 2023 ETH Zurich +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package drkey + +import ( + "time" + + "github.com/scionproto/scion/pkg/addr" + "github.com/scionproto/scion/pkg/drkey" + "github.com/scionproto/scion/pkg/private/serrors" + "github.com/scionproto/scion/pkg/spao" +) + +type FakeProvider struct { + KeyDuration time.Duration + AcceptanceWindow time.Duration +} + +func (p *FakeProvider) GetASHostKey( + validTime time.Time, + _ addr.IA, + _ addr.Host, +) (drkey.ASHostKey, error) { + + duration := int64(p.KeyDuration / time.Second) + idxCurrent := validTime.Unix() / duration + epochCurrent := newEpoch(idxCurrent, duration) + return drkey.ASHostKey{ + Key: drkey.Key{}, + Epoch: epochCurrent, + }, nil +} + +func (p *FakeProvider) GetKeyWithinAcceptanceWindow( + t time.Time, + timestamp uint64, + dstIA addr.IA, + dstAddr addr.Host, +) (drkey.ASHostKey, error) { + + keys, err := p.getASHostTreble(t, dstIA, dstAddr) + if err != nil { + return drkey.ASHostKey{}, err + } + + awBegin := t.Add(-(p.AcceptanceWindow + time.Nanosecond)) + awEnd := t.Add(p.AcceptanceWindow) + + absTimePrevious := spao.AbsoluteTimestamp(keys[0], timestamp) + absTimeCurrent := spao.AbsoluteTimestamp(keys[1], timestamp) + absTimeNext := spao.AbsoluteTimestamp(keys[2], timestamp) + switch { + case absTimeCurrent.After(awBegin) && absTimeCurrent.Before(awEnd): + return keys[1], nil + case absTimePrevious.After(awBegin) && absTimePrevious.Before(awEnd): + return keys[0], nil + case absTimeNext.After(awBegin) && absTimeNext.Before(awEnd): + return keys[2], nil + default: + return drkey.ASHostKey{}, serrors.New("no absTime falls into the acceptance window", + "awBegin", awBegin, "awEnd", awEnd, "absTimePrevious", absTimePrevious, + "absTimeCurrent", absTimeCurrent, "absTimeNext", absTimeNext) + } +} + +func (p *FakeProvider) getASHostTreble( + validTime time.Time, + _ addr.IA, + _ addr.Host, +) ([]drkey.ASHostKey, error) { + + duration := int64(p.KeyDuration / time.Second) + idxCurrent := validTime.Unix() / duration + idxPrevious, idxNext := idxCurrent-1, idxCurrent+1 + epochPrevious := newEpoch(idxPrevious, duration) + epochCurrent := newEpoch(idxCurrent, duration) + epochNext := newEpoch(idxNext, duration) + return []drkey.ASHostKey{ + { + Epoch: epochPrevious, + Key: drkey.Key{}, + }, + { + Epoch: epochCurrent, + Key: drkey.Key{}, + }, + { + Epoch: epochNext, + Key: drkey.Key{}, + }, + }, nil +} + +func newEpoch(idx int64, duration int64) drkey.Epoch { + begin := uint32(idx * duration) + end := begin + uint32(duration) + return drkey.NewEpoch(begin, end) +} diff --git a/router/BUILD.bazel b/router/BUILD.bazel index 57fae70cda..4e1d0256a3 100644 --- a/router/BUILD.bazel +++ b/router/BUILD.bazel @@ -11,6 +11,7 @@ go_library( importpath = "github.com/scionproto/scion/router", visibility = ["//visibility:public"], deps = [ + "//control/config:go_default_library", "//pkg/addr:go_default_library", "//pkg/drkey:go_default_library", "//pkg/experimental/epic:go_default_library", @@ -26,6 +27,7 @@ go_library( "//pkg/slayers/path/onehop:go_default_library", "//pkg/slayers/path/scion:go_default_library", "//pkg/spao:go_default_library", + "//private/drkey:go_default_library", "//private/topology:go_default_library", "//private/underlay/conn:go_default_library", "//router/bfd:go_default_library", diff --git a/router/dataplane.go b/router/dataplane.go index 81592f3cd3..2fcfac2b98 100644 --- a/router/dataplane.go +++ b/router/dataplane.go @@ -25,6 +25,7 @@ import ( "math/big" "net" "net/netip" + "os" "strconv" "sync" "syscall" @@ -34,6 +35,7 @@ import ( "github.com/google/gopacket/layers" "github.com/prometheus/client_golang/prometheus" + "github.com/scionproto/scion/control/config" "github.com/scionproto/scion/pkg/addr" "github.com/scionproto/scion/pkg/drkey" libepic "github.com/scionproto/scion/pkg/experimental/epic" @@ -48,6 +50,7 @@ import ( "github.com/scionproto/scion/pkg/slayers/path/onehop" "github.com/scionproto/scion/pkg/slayers/path/scion" "github.com/scionproto/scion/pkg/spao" + drkeytools "github.com/scionproto/scion/private/drkey" "github.com/scionproto/scion/private/topology" "github.com/scionproto/scion/private/underlay/conn" underlayconn "github.com/scionproto/scion/private/underlay/conn" @@ -133,7 +136,13 @@ var ( ) type drkeyProvider interface { - GetAuthKey(validTime time.Time, dstIA addr.IA, dstAddr addr.Host) (drkey.Key, error) + GetASHostKey(validTime time.Time, dstIA addr.IA, dstAddr addr.Host) (drkey.ASHostKey, error) + GetKeyWithinAcceptanceWindow( + validTime time.Time, + timestamp uint64, + dstIA addr.IA, + dstAddr addr.Host, + ) (drkey.ASHostKey, error) } type scmpError struct { @@ -590,9 +599,13 @@ func newPacketProcessor(d *DataPlane, ingressID uint16) *scionPacketProcessor { drkeyInput: make([]byte, spao.MACBufferSize), }, // TODO(JordiSubira): Replace this with a useful implementation. - drkeyProvider: &fakeProvider{}, - optAuth: slayers.PacketAuthOption{EndToEndOption: new(slayers.EndToEndOption)}, - validAuthBuf: make([]byte, 16), + + drkeyProvider: &drkeytools.FakeProvider{ + KeyDuration: loadEpochDuration(), + AcceptanceWindow: loadAcceptanceWindow(), + }, + optAuth: slayers.PacketAuthOption{EndToEndOption: new(slayers.EndToEndOption)}, + validAuthBuf: make([]byte, 16), } p.scionLayer.RecyclePaths() return p @@ -1659,7 +1672,7 @@ func (p *scionPacketProcessor) prepareSCMP( // if SCMPTypeTracerouteReply to create the response. needsAuth := cause != nil || (scmpH.TypeCode.Type() == slayers.SCMPTypeTracerouteReply && - p.hasValidAuth()) + p.hasValidAuth(time.Now())) var quote []byte if cause != nil { @@ -1704,11 +1717,11 @@ func (p *scionPacketProcessor) prepareSCMP( now := time.Now() // srcA == scionL.DstAddr - key, err := p.drkeyProvider.GetAuthKey(now, scionL.DstIA, srcA) + key, err := p.drkeyProvider.GetASHostKey(now, scionL.DstIA, srcA) if err != nil { return nil, serrors.Wrap(cannotRoute, err, "details", "retrieving DRKey") } - if err := p.resetSPAOMetadata(now); err != nil { + if err := p.resetSPAOMetadata(key, now); err != nil { return nil, serrors.Wrap(cannotRoute, err, "details", "resetting SPAO header") } @@ -1716,7 +1729,7 @@ func (p *scionPacketProcessor) prepareSCMP( e2e.NextHdr = slayers.L4SCMP _, err = spao.ComputeAuthCMAC( spao.MACInput{ - Key: key[:], + Key: key.Key[:], Header: p.optAuth, ScionLayer: &scionL, PldType: slayers.L4SCMP, @@ -1741,43 +1754,30 @@ func (p *scionPacketProcessor) prepareSCMP( return p.buffer.Bytes(), scmpError{TypeCode: scmpH.TypeCode, Cause: cause} } -func (p *scionPacketProcessor) resetSPAOMetadata(now time.Time) error { +func (p *scionPacketProcessor) resetSPAOMetadata(key drkey.ASHostKey, now time.Time) error { // For creating SCMP responses we use sender side. dir := slayers.PacketAuthSenderSide - // TODO(JordiSubira): We assume the later epoch at the moment. - // If the authentication stems from an authenticated request, we want to use - // the same key as the one used by the request sender. - epoch := slayers.PacketAuthLater drkeyType := slayers.PacketAuthASHost - spi, err := slayers.MakePacketAuthSPIDRKey(uint16(drkey.SCMP), drkeyType, dir, epoch) - if err != nil { - return err - } - - firstInfo, err := p.path.GetInfoField(0) + spi, err := slayers.MakePacketAuthSPIDRKey(uint16(drkey.SCMP), drkeyType, dir) if err != nil { return err } - timestamp, err := spao.RelativeTimestamp(firstInfo.Timestamp, now) + timestamp, err := spao.RelativeTimestamp(key, now) if err != nil { return err } - // XXX(JordiSubira): Assume that send rate is low so that combination - // with timestamp is always unique - sn := uint32(0) return p.optAuth.Reset(slayers.PacketAuthOptionParams{ - SPI: spi, - Algorithm: slayers.PacketAuthCMAC, - Timestamp: timestamp, - SequenceNumber: sn, - Auth: zeroBuffer, + SPI: spi, + Algorithm: slayers.PacketAuthCMAC, + TimestampSN: timestamp, + Auth: zeroBuffer, }) } -func (p *scionPacketProcessor) hasValidAuth() bool { +func (p *scionPacketProcessor) hasValidAuth(t time.Time) bool { // Check if e2eLayer was parsed for this packet if !p.lastLayer.CanDecode().Contains(slayers.LayerTypeEndToEndExtn) { return false @@ -1799,25 +1799,22 @@ func (p *scionPacketProcessor) hasValidAuth() bool { return false } // Computing authField - firstInfo, err := p.path.GetInfoField(0) - if err != nil { - return false - } - then := spao.Time(firstInfo.Timestamp, authOption.Timestamp()) + // the sender should have used the receiver side key, i.e., K_{localIA-remoteIA:remoteHost} + // where remoteIA == p.scionLayer.SrcIA and remoteHost == srcAddr + // (for the incoming packet). srcAddr, err := p.scionLayer.SrcAddr() if err != nil { return false } - // the sender should have used the receiver side key, i.e., K_{localIA-remoteIA:remoteHost} - // where remoteIA == p.scionLayer.SrcIA and remoteHost == srcAddr - // (for the incoming packet). - key, err := p.drkeyProvider.GetAuthKey(then, p.scionLayer.SrcIA, srcAddr) + key, err := p.drkeyProvider.GetKeyWithinAcceptanceWindow(t, authOption.TimestampSN(), p.scionLayer.SrcIA, srcAddr) if err != nil { + log.Error("Selecting key to authenticate the incoming packet", "err", err) return false } + _, err = spao.ComputeAuthCMAC( spao.MACInput{ - Key: key[:], + Key: key.Key[:], Header: authOption, ScionLayer: &p.scionLayer, PldType: slayers.L4SCMP, @@ -1920,12 +1917,26 @@ func serviceMetricLabels(localIA addr.IA, svc addr.SVC) prometheus.Labels { } } -type fakeProvider struct{} +func loadEpochDuration() time.Duration { + s := os.Getenv(config.EnvVarEpochDuration) + if s == "" { + return config.DefaultEpochDuration + } + duration, err := util.ParseDuration(s) + if err != nil { + return config.DefaultEpochDuration + } + return duration +} -func (p *fakeProvider) GetAuthKey( - _ time.Time, - _ addr.IA, - _ addr.Host, -) (drkey.Key, error) { - return drkey.Key{}, nil +func loadAcceptanceWindow() time.Duration { + s := os.Getenv(config.EnvVarAccpetanceWindow) + if s == "" { + return config.DefaultAcceptanceWindowOffset + } + duration, err := util.ParseDuration(s) + if err != nil { + return config.DefaultAcceptanceWindowOffset + } + return duration } diff --git a/tools/braccept/BUILD.bazel b/tools/braccept/BUILD.bazel index 972054c646..6003bcf1c7 100644 --- a/tools/braccept/BUILD.bazel +++ b/tools/braccept/BUILD.bazel @@ -7,6 +7,7 @@ go_library( importpath = "github.com/scionproto/scion/tools/braccept", visibility = ["//visibility:private"], deps = [ + "//control/config:go_default_library", "//pkg/log:go_default_library", "//pkg/scrypto:go_default_library", "//pkg/slayers:go_default_library", diff --git a/tools/braccept/cases/BUILD.bazel b/tools/braccept/cases/BUILD.bazel index c25dd33e91..360fb305fc 100644 --- a/tools/braccept/cases/BUILD.bazel +++ b/tools/braccept/cases/BUILD.bazel @@ -29,6 +29,7 @@ go_library( importpath = "github.com/scionproto/scion/tools/braccept/cases", visibility = ["//visibility:public"], deps = [ + "//control/config:go_default_library", "//pkg/addr:go_default_library", "//pkg/drkey:go_default_library", "//pkg/private/util:go_default_library", @@ -39,6 +40,7 @@ go_library( "//pkg/slayers/path/onehop:go_default_library", "//pkg/slayers/path/scion:go_default_library", "//pkg/spao:go_default_library", + "//private/drkey:go_default_library", "//tools/braccept/runner:go_default_library", "@com_github_google_gopacket//:go_default_library", "@com_github_google_gopacket//layers:go_default_library", diff --git a/tools/braccept/cases/scmp.go b/tools/braccept/cases/scmp.go index 28f6d8c6f3..d90b480b3e 100644 --- a/tools/braccept/cases/scmp.go +++ b/tools/braccept/cases/scmp.go @@ -51,11 +51,10 @@ func normalizePacketAuthOption(pkt gopacket.Packet) { 0, 0, 0, 0, 0, 0, 0, 0} _ = optAuth.Reset(slayers.PacketAuthOptionParams{ - SPI: spi, - Algorithm: alg, - Timestamp: uint32(0), - SequenceNumber: uint32(0), - Auth: auth, + SPI: spi, + Algorithm: alg, + TimestampSN: uint64(0), + Auth: auth, }) } @@ -64,17 +63,15 @@ func normalizedSCMPPacketAuthEndToEndExtn() *slayers.EndToEndExtn { uint16(drkey.SCMP), slayers.PacketAuthASHost, slayers.PacketAuthSenderSide, - slayers.PacketAuthLater, ) if err != nil { panic(err) } packAuthOpt, err := slayers.NewPacketAuthOption(slayers.PacketAuthOptionParams{ - SPI: spi, - Algorithm: slayers.PacketAuthCMAC, - Timestamp: uint32(0), - SequenceNumber: uint32(0), - Auth: make([]byte, 16), + SPI: spi, + Algorithm: slayers.PacketAuthCMAC, + TimestampSN: uint64(0), + Auth: make([]byte, 16), }) if err != nil { panic(err) diff --git a/tools/braccept/cases/scmp_expired_hop.go b/tools/braccept/cases/scmp_expired_hop.go index 02a886cea5..faa48cbb82 100644 --- a/tools/braccept/cases/scmp_expired_hop.go +++ b/tools/braccept/cases/scmp_expired_hop.go @@ -119,7 +119,10 @@ func SCMPExpiredHop(artifactsDir string, mac hash.Hash) runner.Case { scionudp.DstPort = 40222 scionudp.SetNetworkLayerForChecksum(scionL) + pointer := slayers.CmnHdrLen + scionL.AddrHdrLen() + + (4 + 8*sp.NumINF + 12*int(sp.PathMeta.CurrHF)) payload := []byte("actualpayloadbytes") + // Prepare input packet input := gopacket.NewSerializeBuffer() if err := gopacket.SerializeLayers(input, options, @@ -129,13 +132,61 @@ func SCMPExpiredHop(artifactsDir string, mac hash.Hash) runner.Case { } // Prepare want packet + want := gopacket.NewSerializeBuffer() + // Ethernet: SrcMAC=f0:0d:ca:fe:00:13 DstMAC=f0:0d:ca:fe:be:ef + ethernet.SrcMAC = net.HardwareAddr{0xf0, 0x0d, 0xca, 0xfe, 0x00, 0x13} + ethernet.DstMAC = net.HardwareAddr{0xf0, 0x0d, 0xca, 0xfe, 0xbe, 0xef} + // IP4: Src=192.168.14.2 Dst=192.168.13.3 Checksum=0 + ip.SrcIP = net.IP{192, 168, 13, 2} + ip.DstIP = net.IP{192, 168, 13, 3} + // UDP: Src=50000 Dst=40000 + udp.SrcPort, udp.DstPort = udp.DstPort, udp.SrcPort + + scionL.DstIA = scionL.SrcIA + scionL.SrcIA = xtest.MustParseIA("1-ff00:0:1") + if err := scionL.SetDstAddr(srcA); err != nil { + panic(err) + } + intlA := addr.MustParseHost("192.168.0.11") + if err := scionL.SetSrcAddr(intlA); err != nil { + panic(err) + } + + p, err := sp.Reverse() + if err != nil { + panic(err) + } + sp = p.(*scion.Decoded) + if err := sp.IncPath(); err != nil { + panic(err) + } + scionL.NextHdr = slayers.End2EndClass + e2e := normalizedSCMPPacketAuthEndToEndExtn() + e2e.NextHdr = slayers.L4SCMP + scmpH := &slayers.SCMP{ + TypeCode: slayers.CreateSCMPTypeCode(slayers.SCMPTypeParameterProblem, + slayers.SCMPCodePathExpired), + } + scmpH.SetNetworkLayerForChecksum(scionL) + scmpP := &slayers.SCMPParameterProblem{ + Pointer: uint16(pointer), + } + + // Skip Ethernet + IPv4 + UDP + quoteStart := 14 + 20 + 8 + quote := input.Bytes()[quoteStart:] + if err := gopacket.SerializeLayers(want, options, + ethernet, ip, udp, scionL, e2e, scmpH, scmpP, gopacket.Payload(quote), + ); err != nil { + panic(err) + } return runner.Case{ Name: "SCMPExpiredHop", WriteTo: "veth_131_host", ReadFrom: "veth_131_host", Input: input.Bytes(), - Want: nil, + Want: want.Bytes(), StoreDir: filepath.Join(artifactsDir, "SCMPExpiredHop"), NormalizePacket: scmpNormalizePacket, } diff --git a/tools/braccept/cases/scmp_invalid_mac.go b/tools/braccept/cases/scmp_invalid_mac.go index ce94c1c20a..3a5fbd7f56 100644 --- a/tools/braccept/cases/scmp_invalid_mac.go +++ b/tools/braccept/cases/scmp_invalid_mac.go @@ -306,11 +306,10 @@ func SCMPBadMACInternal(artifactsDir string, mac hash.Hash) runner.Case { panic(err) } - p, err := sp.Reverse() + _, err := sp.Reverse() if err != nil { panic(err) } - sp = p.(*scion.Decoded) scionL.NextHdr = slayers.End2EndClass e2e := normalizedSCMPPacketAuthEndToEndExtn() e2e.NextHdr = slayers.L4SCMP diff --git a/tools/braccept/cases/scmp_traceroute.go b/tools/braccept/cases/scmp_traceroute.go index 6242efe1ef..4a7e51b6da 100644 --- a/tools/braccept/cases/scmp_traceroute.go +++ b/tools/braccept/cases/scmp_traceroute.go @@ -17,12 +17,14 @@ package cases import ( "hash" "net" + "os" "path/filepath" "time" "github.com/google/gopacket" "github.com/google/gopacket/layers" + "github.com/scionproto/scion/control/config" "github.com/scionproto/scion/pkg/addr" "github.com/scionproto/scion/pkg/drkey" "github.com/scionproto/scion/pkg/private/util" @@ -31,6 +33,7 @@ import ( "github.com/scionproto/scion/pkg/slayers/path" "github.com/scionproto/scion/pkg/slayers/path/scion" "github.com/scionproto/scion/pkg/spao" + drkeytools "github.com/scionproto/scion/private/drkey" "github.com/scionproto/scion/tools/braccept/runner" ) @@ -256,8 +259,21 @@ func SCMPTracerouteIngressWithSPAO(artifactsDir string, mac hash.Hash) runner.Ca } e2e := &slayers.EndToEndExtn{} + + sendTime := time.Now() + key, err := (&drkeytools.FakeProvider{ + KeyDuration: loadEpochDuration(), + }).GetASHostKey(sendTime, xtest.MustParseIA("1-ff00:0:4"), srcA) + if err != nil { + panic(err) + } + timestamp, err := spao.RelativeTimestamp(key, sendTime) + if err != nil { + panic(err) + } optAuth, err := slayers.NewPacketAuthOption(slayers.PacketAuthOptionParams{ - Auth: make([]byte, 16), + TimestampSN: timestamp, + Auth: make([]byte, 16), }) if err != nil { panic(err) @@ -279,7 +295,7 @@ func SCMPTracerouteIngressWithSPAO(artifactsDir string, mac hash.Hash) runner.Ca } _, err = spao.ComputeAuthCMAC( spao.MACInput{ - Key: (&drkey.Key{})[:], + Key: key.Key[:], Header: optAuth, ScionLayer: scionL, PldType: slayers.L4SCMP, @@ -331,17 +347,15 @@ func SCMPTracerouteIngressWithSPAO(artifactsDir string, mac hash.Hash) runner.Ca uint16(drkey.SCMP), slayers.PacketAuthASHost, slayers.PacketAuthSenderSide, - slayers.PacketAuthLater, ) if err != nil { panic(err) } packAuthOpt, err := slayers.NewPacketAuthOption(slayers.PacketAuthOptionParams{ - SPI: spi, - Algorithm: slayers.PacketAuthCMAC, - Timestamp: uint32(0), - SequenceNumber: uint32(0), - Auth: make([]byte, 16), + SPI: spi, + Algorithm: slayers.PacketAuthCMAC, + TimestampSN: uint64(0), + Auth: make([]byte, 16), }) if err != nil { panic(err) @@ -1068,11 +1082,10 @@ func SCMPTracerouteInternal(artifactsDir string, mac hash.Hash) runner.Case { } sp.HopFields[0].EgressRouterAlert = false - p, err := sp.Reverse() + _, err := sp.Reverse() if err != nil { panic(err) } - sp = p.(*scion.Decoded) scionL.NextHdr = slayers.L4SCMP scmpH = &slayers.SCMP{ TypeCode: slayers.CreateSCMPTypeCode(slayers.SCMPTypeTracerouteReply, 0), @@ -1100,3 +1113,15 @@ func SCMPTracerouteInternal(artifactsDir string, mac hash.Hash) runner.Case { StoreDir: filepath.Join(artifactsDir, "SCMPTracerouteInternal"), } } + +func loadEpochDuration() time.Duration { + s := os.Getenv(config.EnvVarEpochDuration) + if s == "" { + return config.DefaultEpochDuration + } + duration, err := util.ParseDuration(s) + if err != nil { + return config.DefaultEpochDuration + } + return duration +} diff --git a/tools/braccept/main.go b/tools/braccept/main.go index 9a54104d56..270db7c31d 100644 --- a/tools/braccept/main.go +++ b/tools/braccept/main.go @@ -23,6 +23,7 @@ import ( "github.com/google/gopacket/layers" + "github.com/scionproto/scion/control/config" "github.com/scionproto/scion/pkg/log" "github.com/scionproto/scion/pkg/scrypto" "github.com/scionproto/scion/pkg/slayers" @@ -74,6 +75,13 @@ func realMain() int { return 1 } + // Set acceptance window to longer than default value to handle with test time fluctuation + err = os.Setenv(config.EnvVarAccpetanceWindow, "1m") + if err != nil { + fmt.Fprintf(os.Stderr, "Setting acceptance window: %v\n", err) + return 1 + } + registerScionPorts() log.Info("BR V2 acceptance tests:")