Skip to content

Commit

Permalink
Merge pull request #6630 from TheThingsNetwork/feature/additional-data
Browse files Browse the repository at this point in the history
Make forwarder information part of the gateway uplink tokens
  • Loading branch information
adriansmares authored Oct 12, 2023
2 parents 6cfdb8f + f21db91 commit 1840a82
Show file tree
Hide file tree
Showing 6 changed files with 59 additions and 18 deletions.
3 changes: 2 additions & 1 deletion pkg/packetbrokeragent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -691,7 +691,8 @@ func (a *Agent) handleDownlinkMessage(
}
}

uid, msg, err := fromPBDownlink(ctx, down.Message, receivedAt, a.forwarderConfig)
forwarderData := forwarderAdditionalData(down.ForwarderNetId, down.ForwarderTenantId, down.ForwarderClusterId)
uid, msg, err := fromPBDownlink(ctx, down.Message, forwarderData, receivedAt, a.forwarderConfig)
if err != nil {
logger.WithError(err).Warn("Failed to convert incoming downlink message")
return err
Expand Down
3 changes: 2 additions & 1 deletion pkg/packetbrokeragent/agent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -428,8 +428,9 @@ func TestForwarder(t *testing.T) {
}))
tokenNonce := make([]byte, aead.NonceSize())
test.Must(io.ReadFull(rand.Reader, tokenNonce))
forwarderData := []byte("000013:foo-tenant:test")
token := test.Must(proto.Marshal(&ttnpb.PacketBrokerAgentEncryptedPayload{
Ciphertext: aead.Seal(nil, tokenNonce, plainToken, nil),
Ciphertext: aead.Seal(nil, tokenNonce, plainToken, forwarderData),
Nonce: tokenNonce,
}))

Expand Down
21 changes: 21 additions & 0 deletions pkg/packetbrokeragent/crypto.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright © 2023 The Things Network Foundation, The Things Industries B.V.
//
// 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 packetbrokeragent

import "fmt"

func forwarderAdditionalData(netID uint32, tenantID string, clusterID string) []byte {
return []byte(fmt.Sprintf("%06x:%s:%s", netID, tenantID, clusterID))
}
3 changes: 2 additions & 1 deletion pkg/packetbrokeragent/grpc_gspba.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@ func (s *gsPbaServer) PublishUplink(ctx context.Context, up *ttnpb.GatewayUplink
ctx = appendUplinkCorrelationID(ctx)
up.Message.CorrelationIds = events.CorrelationIDsFromContext(ctx)

msg, err := toPBUplink(ctx, up, s.config)
forwarderData := forwarderAdditionalData(s.netID.MarshalNumber(), s.tenantIDExtractor(ctx), s.clusterID)
msg, err := toPBUplink(ctx, up, forwarderData, s.config)
if err != nil {
log.FromContext(ctx).WithError(err).Warn("Failed to convert outgoing uplink message")
return nil, err
Expand Down
36 changes: 24 additions & 12 deletions pkg/packetbrokeragent/translation.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,27 +215,27 @@ type legacyGatewayUplinkToken struct {
Token []byte `json:"t"`
}

func encryptPlaintext(plaintext []byte, aead cipher.AEAD) ([]byte, error) {
func encryptPlaintext(plaintext, additionalData []byte, aead cipher.AEAD) ([]byte, error) {
nonce := make([]byte, aead.NonceSize())
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
return nil, err
}
return proto.Marshal(&ttnpb.PacketBrokerAgentEncryptedPayload{
Ciphertext: aead.Seal(nil, nonce, plaintext, nil),
Ciphertext: aead.Seal(nil, nonce, plaintext, additionalData),
Nonce: nonce,
})
}

func decryptCiphertext(payload []byte, aead cipher.AEAD) ([]byte, error) {
func decryptCiphertext(payload, additionalData []byte, aead cipher.AEAD) ([]byte, error) {
var encrypted ttnpb.PacketBrokerAgentEncryptedPayload
if err := proto.Unmarshal(payload, &encrypted); err != nil {
return nil, err
}
return aead.Open(nil, encrypted.Nonce, encrypted.Ciphertext, nil)
return aead.Open(nil, encrypted.Nonce, encrypted.Ciphertext, additionalData)
}

func wrapGatewayUplinkToken(
ctx context.Context, ids *ttnpb.GatewayIdentifiers, ulToken []byte, aead cipher.AEAD,
ctx context.Context, ids *ttnpb.GatewayIdentifiers, ulToken, forwarderData []byte, aead cipher.AEAD,
) ([]byte, error) {
plaintext, err := proto.Marshal(&ttnpb.PacketBrokerAgentGatewayUplinkToken{
GatewayUid: unique.ID(ctx, ids),
Expand All @@ -244,7 +244,7 @@ func wrapGatewayUplinkToken(
if err != nil {
return nil, err
}
return encryptPlaintext(plaintext, aead)
return encryptPlaintext(plaintext, forwarderData, aead)
}

func unwrapLegacyGatewayUplinkToken(token, key []byte) (string, []byte, error) {
Expand All @@ -263,11 +263,13 @@ func unwrapLegacyGatewayUplinkToken(token, key []byte) (string, []byte, error) {
return t.GatewayUID, t.Token, nil
}

func unwrapGatewayUplinkToken(token []byte, aead cipher.AEAD, legacyKey []byte) (string, []byte, error) {
func unwrapGatewayUplinkToken(
token, forwarderData []byte, aead cipher.AEAD, legacyKey []byte,
) (string, []byte, error) {
if uid, token, err := unwrapLegacyGatewayUplinkToken(token, legacyKey); err == nil {
return uid, token, nil
}
plaintext, err := decryptCiphertext(token, aead)
plaintext, err := decryptCiphertext(token, forwarderData, aead)
if err != nil {
return "", nil, err
}
Expand Down Expand Up @@ -320,7 +322,9 @@ var (
errWrapGatewayUplinkToken = errors.DefineAborted("wrap_gateway_uplink_token", "wrap gateway uplink token")
)

func toPBUplink(ctx context.Context, msg *ttnpb.GatewayUplinkMessage, config ForwarderConfig) (*packetbroker.UplinkMessage, error) {
func toPBUplink(
ctx context.Context, msg *ttnpb.GatewayUplinkMessage, forwarderData []byte, config ForwarderConfig,
) (*packetbroker.UplinkMessage, error) {
msg.Message.Payload = &ttnpb.Message{}
if err := lorawan.UnmarshalMessage(msg.Message.RawPayload, msg.Message.Payload); err != nil {
return nil, errDecodePayload.WithCause(err)
Expand Down Expand Up @@ -454,7 +458,9 @@ func toPBUplink(ctx context.Context, msg *ttnpb.GatewayUplinkMessage, config For

if len(gatewayUplinkToken) == 0 {
var err error
gatewayUplinkToken, err = wrapGatewayUplinkToken(ctx, md.GatewayIds, md.UplinkToken, config.TokenAEAD)
gatewayUplinkToken, err = wrapGatewayUplinkToken(
ctx, md.GatewayIds, md.UplinkToken, forwarderData, config.TokenAEAD,
)
if err != nil {
return nil, errWrapGatewayUplinkToken.WithCause(err)
}
Expand Down Expand Up @@ -738,8 +744,14 @@ var (
errInvalidRx1Delay = errors.DefineInvalidArgument("invalid_rx1_delay", "invalid Rx1 delay")
)

func fromPBDownlink(ctx context.Context, msg *packetbroker.DownlinkMessage, receivedAt time.Time, conf ForwarderConfig) (uid string, res *ttnpb.DownlinkMessage, err error) {
uid, token, err := unwrapGatewayUplinkToken(msg.GatewayUplinkToken, conf.TokenAEAD, conf.TokenKey)
func fromPBDownlink(
ctx context.Context,
msg *packetbroker.DownlinkMessage,
forwarderData []byte,
receivedAt time.Time,
conf ForwarderConfig,
) (uid string, res *ttnpb.DownlinkMessage, err error) {
uid, token, err := unwrapGatewayUplinkToken(msg.GatewayUplinkToken, forwarderData, conf.TokenAEAD, conf.TokenKey)
if err != nil {
return "", nil, errUnwrapGatewayUplinkToken.WithCause(err)
}
Expand Down
11 changes: 8 additions & 3 deletions pkg/packetbrokeragent/translation_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ func WrapUplinkTokens(gateway, forwarder []byte, agent *ttnpb.PacketBrokerAgentU
func TestWrapGatewayUplinkToken(t *testing.T) {
a, ctx := test.New(t)
key := bytes.Repeat([]byte{0x42}, 16)
forwarderData := []byte("000013:tnt:eu1")
blockCipher, err := aes.NewCipher(key)
if !a.So(err, should.BeNil) {
t.FailNow()
Expand All @@ -43,15 +44,19 @@ func TestWrapGatewayUplinkToken(t *testing.T) {
t.FailNow()
}

wrappedToken, err := wrapGatewayUplinkToken(ctx, &ttnpb.GatewayIdentifiers{GatewayId: "test-gateway"},
[]byte{0x1, 0x2, 0x3}, aead,
wrappedToken, err := wrapGatewayUplinkToken(
ctx,
&ttnpb.GatewayIdentifiers{GatewayId: "test-gateway"},
[]byte{0x1, 0x2, 0x3},
forwarderData,
aead,
)
if !a.So(err, should.BeNil) {
t.FailNow()
}
t.Logf("Wrapped token: %q", base64.RawStdEncoding.EncodeToString(wrappedToken))

uid, gtwToken, err := unwrapGatewayUplinkToken(wrappedToken, aead, nil)
uid, gtwToken, err := unwrapGatewayUplinkToken(wrappedToken, forwarderData, aead, key)
if !a.So(err, should.BeNil) {
t.FailNow()
}
Expand Down

0 comments on commit 1840a82

Please sign in to comment.