tinygo.org/x/drivers@v0.27.1-0.20240509133757-7dbca2a54349/lora/lorawan/otaa.go (about)

     1  package lorawan
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/aes"
     6  	"encoding/hex"
     7  )
     8  
     9  // Otaa is used to store Over The Air Activation data of a LoRaWAN session
    10  type Otaa struct {
    11  	DevEUI   [8]uint8
    12  	AppEUI   [8]uint8
    13  	AppKey   [16]uint8
    14  	devNonce [2]uint8
    15  	appNonce [3]uint8
    16  	NetID    [3]uint8
    17  	buf      []uint8
    18  }
    19  
    20  // Initialize DevNonce
    21  func (o *Otaa) Init() {
    22  	o.buf = make([]uint8, 0)
    23  	o.generateDevNonce()
    24  }
    25  
    26  func (o *Otaa) generateDevNonce() {
    27  	// TODO: handle error
    28  	rnd, _ := GetRand16()
    29  	o.devNonce[0] = rnd[0]
    30  	o.devNonce[1] = rnd[1]
    31  }
    32  
    33  func (o *Otaa) incrementDevNonce() {
    34  	nonce := uint16(o.devNonce[1])<<8 | uint16(o.devNonce[0]) + 1
    35  	o.devNonce[0] = uint8(nonce)
    36  	o.devNonce[1] = uint8((nonce >> 8))
    37  }
    38  
    39  // Set configures the Otaa AppEUI, DevEUI, AppKey for the device
    40  func (o *Otaa) Set(appEUI []uint8, devEUI []uint8, appKey []uint8) {
    41  	o.SetAppEUI(appEUI)
    42  	o.SetDevEUI(devEUI)
    43  	o.SetAppKey(appKey)
    44  }
    45  
    46  // SetAppEUI configures the Otaa AppEUI
    47  func (o *Otaa) SetAppEUI(appEUI []uint8) error {
    48  	if len(appEUI) != 8 {
    49  		return ErrInvalidEuiLength
    50  	}
    51  
    52  	copy(o.AppEUI[:], appEUI)
    53  
    54  	return nil
    55  }
    56  
    57  func (o *Otaa) GetAppEUI() string {
    58  	return hex.EncodeToString(o.AppEUI[:])
    59  }
    60  
    61  // SetDevEUI configures the Otaa DevEUI
    62  func (o *Otaa) SetDevEUI(devEUI []uint8) error {
    63  	if len(devEUI) != 8 {
    64  		return ErrInvalidEuiLength
    65  	}
    66  
    67  	copy(o.DevEUI[:], devEUI)
    68  
    69  	return nil
    70  }
    71  
    72  func (o *Otaa) GetDevEUI() string {
    73  	return hex.EncodeToString(o.DevEUI[:])
    74  }
    75  
    76  // SetAppKey configures the Otaa AppKey
    77  func (o *Otaa) SetAppKey(appKey []uint8) error {
    78  	if len(appKey) != 16 {
    79  		return ErrInvalidAppKeyLength
    80  	}
    81  
    82  	copy(o.AppKey[:], appKey)
    83  
    84  	return nil
    85  }
    86  
    87  func (o *Otaa) GetAppKey() string {
    88  	return hex.EncodeToString(o.AppKey[:])
    89  }
    90  
    91  func (o *Otaa) GetNetID() string {
    92  	return hex.EncodeToString(o.NetID[:])
    93  }
    94  
    95  func (o *Otaa) SetNetID(netID []uint8) error {
    96  	if len(netID) != 3 {
    97  		return ErrInvalidNetIDLength
    98  	}
    99  
   100  	copy(o.NetID[:], netID)
   101  
   102  	return nil
   103  }
   104  
   105  // GenerateJoinRequest Generates a LoraWAN Join request
   106  func (o *Otaa) GenerateJoinRequest() ([]uint8, error) {
   107  	o.incrementDevNonce()
   108  
   109  	// TODO: Add checks
   110  	o.buf = o.buf[:0]
   111  	o.buf = append(o.buf, 0x00)
   112  	o.buf = append(o.buf, reverseBytes(o.AppEUI[:])...)
   113  	o.buf = append(o.buf, reverseBytes(o.DevEUI[:])...)
   114  	o.buf = append(o.buf, o.devNonce[:]...)
   115  	mic := genPayloadMIC(o.buf, o.AppKey)
   116  	o.buf = append(o.buf, mic[:]...)
   117  
   118  	return o.buf, nil
   119  }
   120  
   121  // DecodeJoinAccept Decodes a Lora Join Accept packet
   122  func (o *Otaa) DecodeJoinAccept(phyPload []uint8, s *Session) error {
   123  	if len(phyPload) < 12 {
   124  		return ErrInvalidPacketLength
   125  	}
   126  	data := phyPload[1:] // Remove trailing 0x20
   127  
   128  	// Prepare AES Cipher
   129  	block, err := aes.NewCipher(o.AppKey[:])
   130  	if err != nil {
   131  		return err
   132  	}
   133  	buf := make([]byte, len(data))
   134  	for k := 0; k < len(data)/aes.BlockSize; k++ {
   135  		block.Encrypt(buf[k*aes.BlockSize:], data[k*aes.BlockSize:])
   136  	}
   137  
   138  	copy(o.appNonce[:], buf[0:3])
   139  	copy(o.NetID[:], buf[3:6])
   140  	copy(s.DevAddr[:], buf[6:10])
   141  	s.DLSettings = buf[10]
   142  	s.RXDelay = buf[11]
   143  
   144  	if len(buf) > 16 {
   145  		copy(s.CFList[:], buf[12:28])
   146  	}
   147  	rxMic := buf[len(buf)-4:]
   148  
   149  	dataMic := []byte{}
   150  	dataMic = append(dataMic, phyPload[0])
   151  	dataMic = append(dataMic, o.appNonce[:]...)
   152  	dataMic = append(dataMic, o.NetID[:]...)
   153  	dataMic = append(dataMic, s.DevAddr[:]...)
   154  	dataMic = append(dataMic, s.DLSettings)
   155  	dataMic = append(dataMic, s.RXDelay)
   156  	dataMic = append(dataMic, s.CFList[:]...)
   157  	computedMic := genPayloadMIC(dataMic[:], o.AppKey)
   158  	if !bytes.Equal(computedMic[:], rxMic[:]) {
   159  		return ErrInvalidMic
   160  	}
   161  	// Generate NwkSKey
   162  	// NwkSKey = aes128_encrypt(AppKey, 0x01|AppNonce|NetID|DevNonce|pad16)
   163  	sKey := []byte{}
   164  	sKey = append(sKey, 0x01)
   165  	sKey = append(sKey, o.appNonce[:]...)
   166  	sKey = append(sKey, o.NetID[:]...)
   167  	sKey = append(sKey, o.devNonce[:]...)
   168  	for i := 0; i < 7; i++ {
   169  		sKey = append(sKey, 0x00) // PAD to 16
   170  	}
   171  	block.Encrypt(buf, sKey)
   172  	copy(s.NwkSKey[:], buf[0:16])
   173  
   174  	// Generate AppSKey
   175  	// AppSKey = aes128_encrypt(AppKey, 0x02|AppNonce|NetID|DevNonce|pad16)
   176  	sKey[0] = 0x02
   177  	block.Encrypt(buf, sKey)
   178  	copy(s.AppSKey[:], buf[0:16])
   179  
   180  	// Reset counters
   181  	s.FCntDown = 0
   182  	s.FCntUp = 0
   183  
   184  	return nil
   185  }