mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-07-26 10:38:41 +00:00
lora/lorawan: completed functions needed for Join OTAA process
Signed-off-by: deadprogram <ron@hybridgroup.com>
This commit is contained in:
+15
-10
@@ -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
@@ -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
@@ -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[:])
|
||||
|
||||
Reference in New Issue
Block a user