Files
drivers/lora/lorawan/session.go
deadprogram 80b2a4a569 lora/lorawan: set functions for keys
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-01-16 09:33:13 +01:00

138 lines
2.9 KiB
Go

package lorawan
import (
"crypto/aes"
"encoding/binary"
"encoding/hex"
"math"
)
// Session is used to store session data of a LoRaWAN session
type Session struct {
NwkSKey [16]uint8
AppSKey [16]uint8
DevAddr [4]uint8
FCntDown uint32
FCntUp uint32
CFList [16]uint8
RXDelay uint8
DLSettings uint8
}
// SetDevAddr configures the Session DevAddr
func (s *Session) SetDevAddr(devAddr []uint8) error {
if len(devAddr) != 4 {
return ErrInvalidDevAddrLength
}
copy(s.DevAddr[:], devAddr)
return nil
}
// GetDevAddr returns the Session DevAddr
func (s *Session) GetDevAddr() string {
return hex.EncodeToString(s.DevAddr[:])
}
// SetNwkSKey configures the Session NwkSKey
func (s *Session) SetNwkSKey(nwkSKey []uint8) error {
if len(nwkSKey) != 16 {
return ErrInvalidNwkSKeyLength
}
copy(s.NwkSKey[:], nwkSKey)
return nil
}
// GetNwkSKey returns the Session NwkSKey
func (s *Session) GetNwkSKey() string {
return hex.EncodeToString(s.NwkSKey[:])
}
// SetAppSKey configures the Session AppSKey
func (s *Session) SetAppSKey(appSKey []uint8) error {
if len(appSKey) != 16 {
return ErrInvalidAppSKeyLength
}
copy(s.AppSKey[:], appSKey)
return nil
}
// GetAppSKey returns the Session AppSKey
func (s *Session) GetAppSKey() string {
return hex.EncodeToString(s.AppSKey[:])
}
// GenMessage generates an uplink message.
func (s *Session) GenMessage(dir uint8, payload []uint8) ([]uint8, error) {
var buf []uint8
buf = append(buf, 0b01000000) // FHDR Unconfirmed up
buf = append(buf, s.DevAddr[:]...)
// FCtl : No ADR, No RFU, No ACK, No FPending, No FOpt
buf = append(buf, 0x00)
// FCnt Up
buf = append(buf, uint8(s.FCntUp&0xFF), uint8((s.FCntUp>>8)&0xFF))
// FPort=1
buf = append(buf, 0x01)
fCnt := uint32(0)
if dir == 0 {
fCnt = s.FCntUp
s.FCntUp++
} else {
fCnt = s.FCntDown
}
data, err := s.genFRMPayload(dir, fCnt, payload, false)
if err != nil {
return nil, err
}
buf = append(buf, data[:]...)
mic := calcMessageMIC(buf, s.NwkSKey, dir, s.DevAddr[:], fCnt, uint8(len(buf)))
buf = append(buf, mic[:]...)
return buf, nil
}
func (s *Session) genFRMPayload(dir uint8, fCnt uint32, payload []byte, isFOpts bool) ([]byte, error) {
k := len(payload) / aes.BlockSize
if len(payload)%aes.BlockSize != 0 {
k++
}
if k > math.MaxUint8 {
return nil, ErrFrmPayloadTooLarge
}
encrypted := make([]byte, 0, k*16)
cipher, err := aes.NewCipher(s.AppSKey[:])
if err != nil {
panic(err)
}
var a [aes.BlockSize]byte
a[0] = 0x01
a[5] = dir
copy(a[6:10], s.DevAddr[:])
binary.LittleEndian.PutUint32(a[10:14], fCnt)
var ss [aes.BlockSize]byte
var b [aes.BlockSize]byte
for i := uint8(0); i < uint8(k); i++ {
copy(b[:], payload[i*aes.BlockSize:])
if !isFOpts {
a[15] = i + 1
}
cipher.Encrypt(ss[:], a[:])
for j := 0; j < aes.BlockSize; j++ {
b[j] = b[j] ^ ss[j]
}
encrypted = append(encrypted, b[:]...)
}
return encrypted[:len(payload)], nil
}