lora/lorawan: completed functions needed for Join OTAA process

Signed-off-by: deadprogram <ron@hybridgroup.com>
This commit is contained in:
deadprogram
2023-01-12 13:19:18 +01:00
committed by Ron Evans
parent 999f42dc03
commit 4bc8ecdbdd
3 changed files with 44 additions and 29 deletions
+15 -10
View File
@@ -6,10 +6,22 @@ import (
"tinygo.org/x/drivers/lora"
)
var (
ErrNoJoinAcceptReceived = errors.New("no JoinAccept packet received")
ErrNoRadioAttached = errors.New("no LoRa radio attached")
ErrInvalidEuiLength = errors.New("invalid EUI length")
ErrInvalidAppKeyLength = errors.New("invalid AppKey length")
ErrInvalidPacketLength = errors.New("invalid packet length")
ErrInvalidDevAddrLength = errors.New("invalid DevAddr length")
ErrInvalidMic = errors.New("invalid Mic")
ErrFrmPayloadTooLarge = errors.New("FRM payload too large")
)
const LORA_RXTX_TIMEOUT = 1000
var (
ActiveRadio lora.Radio
Retries = 15
)
func UseRadio(r lora.Radio) {
@@ -23,7 +35,7 @@ func Join(otaa *Otaa, session *Session) error {
var resp []uint8
if ActiveRadio == nil {
return errors.New("no LoRa radio attached")
return ErrNoRadioAttached
}
otaa.Init()
@@ -42,9 +54,8 @@ func Join(otaa *Otaa, session *Session) error {
}
// Wait for JoinAccept
//println("lorawan: Wait for JOINACCEPT for 10s")
ActiveRadio.SetIqMode(1) // IQ Inverted
for i := 0; i < 15; i++ {
for i := 0; i < Retries; i++ {
resp, err = ActiveRadio.Rx(LORA_RXTX_TIMEOUT)
if err != nil {
return err
@@ -54,19 +65,13 @@ func Join(otaa *Otaa, session *Session) error {
}
}
if resp == nil {
return errors.New("no JoinAccept packet received")
return ErrNoJoinAcceptReceived
}
//println("lorawan: Received packet: len=", len(resp), "payload=", bytesToHexString(resp))
err = otaa.DecodeJoinAccept(resp, session)
if err != nil {
return err
}
// println("lorawan: Valid JOINACCEPT, now connected")
// println("lorawan: | DevAddr: ", bytesToHexString(r.Session.DevAddr[:]), " (LSB)")
// println("lorawan: | NetID : ", bytesToHexString(r.Otaa.NetID[:]))
// println("lorawan: | NwkSKey: ", bytesToHexString(r.Session.NwkSKey[:]))
// println("lorawan: | AppSKey: ", bytesToHexString(r.Session.AppSKey[:]))
return nil
}
+19 -16
View File
@@ -4,7 +4,6 @@ import (
"bytes"
"crypto/aes"
"encoding/hex"
"errors"
)
// Otaa is used to store Over The Air Activation data of a LoRaWAN session
@@ -12,8 +11,8 @@ type Otaa struct {
DevEUI [8]uint8
AppEUI [8]uint8
AppKey [16]uint8
DevNonce [2]uint8
AppNonce [3]uint8
devNonce [2]uint8
appNonce [3]uint8
NetID [3]uint8
buf []uint8
}
@@ -24,8 +23,8 @@ func (o *Otaa) Init() {
// TODO: handle error
rnd, _ := GetRand16()
o.DevNonce[0] = rnd[0]
o.DevNonce[1] = rnd[1]
o.devNonce[0] = rnd[0]
o.devNonce[1] = rnd[1]
}
// Set configures the Otaa AppEUI, DevEUI, AppKey for the device
@@ -38,7 +37,7 @@ func (o *Otaa) Set(appEUI []uint8, devEUI []uint8, appKey []uint8) {
// SetAppEUI configures the Otaa AppEUI
func (o *Otaa) SetAppEUI(appEUI []uint8) error {
if len(appEUI) != 8 {
return errors.New("invalid length")
return ErrInvalidEuiLength
}
copy(o.AppEUI[:], appEUI)
@@ -53,7 +52,7 @@ func (o *Otaa) GetAppEUI() string {
// SetDevEUI configures the Otaa DevEUI
func (o *Otaa) SetDevEUI(devEUI []uint8) error {
if len(devEUI) != 8 {
return errors.New("invalid length")
return ErrInvalidEuiLength
}
copy(o.DevEUI[:], devEUI)
@@ -68,7 +67,7 @@ func (o *Otaa) GetDevEUI() string {
// SetAppKey configures the Otaa AppKey
func (o *Otaa) SetAppKey(appKey []uint8) error {
if len(appKey) != 16 {
return errors.New("invalid length")
return ErrInvalidAppKeyLength
}
copy(o.AppKey[:], appKey)
@@ -80,6 +79,10 @@ func (o *Otaa) GetAppKey() string {
return hex.EncodeToString(o.AppKey[:])
}
func (o *Otaa) GetNetID() string {
return hex.EncodeToString(o.NetID[:])
}
// GenerateJoinRequest Generates a LoraWAN Join request
func (o *Otaa) GenerateJoinRequest() ([]uint8, error) {
// TODO: Add checks
@@ -87,7 +90,7 @@ func (o *Otaa) GenerateJoinRequest() ([]uint8, error) {
o.buf = append(o.buf, 0x00)
o.buf = append(o.buf, reverseBytes(o.AppEUI[:])...)
o.buf = append(o.buf, reverseBytes(o.DevEUI[:])...)
o.buf = append(o.buf, o.DevNonce[:]...)
o.buf = append(o.buf, o.devNonce[:]...)
mic := genPayloadMIC(o.buf, o.AppKey)
o.buf = append(o.buf, mic[:]...)
@@ -97,21 +100,21 @@ func (o *Otaa) GenerateJoinRequest() ([]uint8, error) {
// DecodeJoinAccept Decodes a Lora Join Accept packet
func (o *Otaa) DecodeJoinAccept(phyPload []uint8, s *Session) error {
if len(phyPload) < 12 {
return errors.New("Bad packet")
return ErrInvalidPacketLength
}
data := phyPload[1:] // Remove trailing 0x20
// Prepare AES Cipher
block, err := aes.NewCipher(o.AppKey[:])
if err != nil {
return errors.New("Lora Cipher error 1")
return err
}
buf := make([]byte, len(data))
for k := 0; k < len(data)/aes.BlockSize; k++ {
block.Encrypt(buf[k*aes.BlockSize:], data[k*aes.BlockSize:])
}
copy(o.AppNonce[:], buf[0:3])
copy(o.appNonce[:], buf[0:3])
copy(o.NetID[:], buf[3:6])
copy(s.DevAddr[:], buf[6:10])
s.DLSettings = buf[10]
@@ -124,7 +127,7 @@ func (o *Otaa) DecodeJoinAccept(phyPload []uint8, s *Session) error {
dataMic := []byte{}
dataMic = append(dataMic, phyPload[0])
dataMic = append(dataMic, o.AppNonce[:]...)
dataMic = append(dataMic, o.appNonce[:]...)
dataMic = append(dataMic, o.NetID[:]...)
dataMic = append(dataMic, s.DevAddr[:]...)
dataMic = append(dataMic, s.DLSettings)
@@ -132,15 +135,15 @@ func (o *Otaa) DecodeJoinAccept(phyPload []uint8, s *Session) error {
dataMic = append(dataMic, s.CFList[:]...)
computedMic := genPayloadMIC(dataMic[:], o.AppKey)
if !bytes.Equal(computedMic[:], rxMic[:]) {
return errors.New("invalid Mic")
return ErrInvalidMic
}
// Generate NwkSKey
// NwkSKey = aes128_encrypt(AppKey, 0x01|AppNonce|NetID|DevNonce|pad16)
sKey := []byte{}
sKey = append(sKey, 0x01)
sKey = append(sKey, o.AppNonce[:]...)
sKey = append(sKey, o.appNonce[:]...)
sKey = append(sKey, o.NetID[:]...)
sKey = append(sKey, o.DevNonce[:]...)
sKey = append(sKey, o.devNonce[:]...)
for i := 0; i < 7; i++ {
sKey = append(sKey, 0x00) // PAD to 16
}
+10 -3
View File
@@ -4,7 +4,6 @@ import (
"crypto/aes"
"encoding/binary"
"encoding/hex"
"errors"
"math"
)
@@ -23,7 +22,7 @@ type Session struct {
// SetDevAddr configures the Session DevAddr
func (s *Session) SetDevAddr(devAddr []uint8) error {
if len(devAddr) != 4 {
return errors.New("invalid length")
return ErrInvalidDevAddrLength
}
copy(s.DevAddr[:], devAddr)
@@ -35,6 +34,14 @@ func (s *Session) GetDevAddr() string {
return hex.EncodeToString(s.DevAddr[:])
}
func (s *Session) GetNwkSKey() string {
return hex.EncodeToString(s.NwkSKey[:])
}
func (s *Session) GetAppSKey() string {
return hex.EncodeToString(s.AppSKey[:])
}
// GenMessage Forge an uplink message
func (s *Session) GenMessage(dir uint8, payload []uint8) ([]uint8, error) {
var buf []uint8
@@ -75,7 +82,7 @@ func (s *Session) genFRMPayload(dir uint8, fCnt uint32, payload []byte, isFOpts
k++
}
if k > math.MaxUint8 {
return nil, errors.New("Payload too big !")
return nil, ErrFrmPayloadTooLarge
}
encrypted := make([]byte, 0, k*16)
cipher, err := aes.NewCipher(s.AppSKey[:])