-
-
Notifications
You must be signed in to change notification settings - Fork 545
/
rx_info.go
140 lines (116 loc) · 4.14 KB
/
rx_info.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
package gateway
import (
"context"
"crypto/aes"
"encoding/binary"
"fmt"
"time"
"github.com/golang/protobuf/ptypes"
"github.com/jmoiron/sqlx"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
"github.com/brocaar/chirpstack-api/go/v3/common"
"github.com/brocaar/chirpstack-api/go/v3/gw"
"github.com/brocaar/chirpstack-network-server/v3/internal/helpers"
"github.com/brocaar/chirpstack-network-server/v3/internal/logging"
"github.com/brocaar/chirpstack-network-server/v3/internal/models"
"github.com/brocaar/chirpstack-network-server/v3/internal/storage"
"github.com/brocaar/lorawan"
)
// UpdateMetaDataInRXPacket updates the gateway meta-data in the
// given RXPacket. It will:
// - add the gateway location
// - set the FPGA id if available
// - decrypt the fine-timestamp (if available and AES key is set)
func UpdateMetaDataInRXPacket(ctx context.Context, db sqlx.Queryer, rxPacket *models.RXPacket) error {
var rxInfoSet []*gw.UplinkRXInfo
for i := range rxPacket.RXInfoSet {
rxInfo := rxPacket.RXInfoSet[i]
id := helpers.GetGatewayID(rxInfo)
g, err := storage.GetAndCacheGatewayMeta(ctx, db, id)
if err != nil {
if errors.Cause(err) == storage.ErrDoesNotExist {
log.WithFields(log.Fields{
"ctx_id": ctx.Value(logging.ContextIDKey),
"gateway_id": id,
}).Warning("uplink received by unknown gateway")
} else {
log.WithFields(log.Fields{
"ctx_id": ctx.Value(logging.ContextIDKey),
"gateway_id": id,
}).WithError(err).Error("get gateway error")
}
continue
}
// set gateway location
rxInfo.Location = &common.Location{
Latitude: g.Location.Latitude,
Longitude: g.Location.Longitude,
Altitude: g.Altitude,
}
var board storage.GatewayBoard
if int(rxInfo.Board) < len(g.Boards) {
board = g.Boards[int(rxInfo.Board)]
}
// set FPGA ID
// this is useful when the AES decryption key is not set as it
// indicates which key to use for decryption
if tsInfo := rxInfo.GetEncryptedFineTimestamp(); tsInfo != nil && board.FPGAID != nil {
if len(tsInfo.FpgaId) == 0 {
tsInfo.FpgaId = board.FPGAID[:]
}
}
// decrypt fine-timestamp when the AES key is known
if tsInfo := rxInfo.GetEncryptedFineTimestamp(); tsInfo != nil && board.FineTimestampKey != nil {
rxTime, err := ptypes.Timestamp(rxInfo.GetTime())
if err != nil {
log.WithFields(log.Fields{
"ctx_id": ctx.Value(logging.ContextIDKey),
"gateway_id": id,
}).WithError(err).Error("get timestamp error")
}
plainTS, err := decryptFineTimestamp(*board.FineTimestampKey, rxTime, *tsInfo)
if err != nil {
log.WithFields(log.Fields{
"ctx_id": ctx.Value(logging.ContextIDKey),
"gateway_id": id,
}).WithError(err).Error("decrypt fine-timestamp error")
continue
}
rxInfo.FineTimestampType = gw.FineTimestampType_PLAIN
rxInfo.FineTimestamp = &gw.UplinkRXInfo_PlainFineTimestamp{
PlainFineTimestamp: &plainTS,
}
}
rxInfoSet = append(rxInfoSet, rxInfo)
rxPacket.GatewayIsPrivate[g.GatewayID] = g.IsPrivate
if g.ServiceProfileID != nil {
rxPacket.GatewayServiceProfile[g.GatewayID] = *g.ServiceProfileID
}
}
rxPacket.RXInfoSet = rxInfoSet
return nil
}
func decryptFineTimestamp(key lorawan.AES128Key, rxTime time.Time, ts gw.EncryptedFineTimestamp) (gw.PlainFineTimestamp, error) {
var plainTS gw.PlainFineTimestamp
block, err := aes.NewCipher(key[:])
if err != nil {
return plainTS, errors.Wrap(err, "new cipher error")
}
if len(ts.EncryptedNs) != block.BlockSize() {
return plainTS, fmt.Errorf("invalid block-size (%d) or ciphertext length (%d)", block.BlockSize(), len(ts.EncryptedNs))
}
ct := make([]byte, block.BlockSize())
block.Decrypt(ct, ts.EncryptedNs)
nanoSec := binary.BigEndian.Uint64(ct[len(ct)-8:])
nanoSec = nanoSec / 32
if time.Duration(nanoSec) >= time.Second {
return plainTS, errors.New("expected fine-timestamp nanosecond remainder must be < 1 second, did you set the correct decryption key?")
}
rxTime = rxTime.Add(time.Duration(nanoSec) * time.Nanosecond)
plainTS.Time, err = ptypes.TimestampProto(rxTime)
if err != nil {
return plainTS, errors.Wrap(err, "timestamp proto error")
}
return plainTS, nil
}