diff --git a/lora/lorawan/adaptor.go b/lora/lorawan/adaptor.go index 97ef08f..a66954c 100644 --- a/lora/lorawan/adaptor.go +++ b/lora/lorawan/adaptor.go @@ -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 } diff --git a/lora/lorawan/otaa.go b/lora/lorawan/otaa.go index a851bde..4578afd 100644 --- a/lora/lorawan/otaa.go +++ b/lora/lorawan/otaa.go @@ -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 } diff --git a/lora/lorawan/session.go b/lora/lorawan/session.go index 6ff6d1a..ce89604 100644 --- a/lora/lorawan/session.go +++ b/lora/lorawan/session.go @@ -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[:])