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

     1  package lorawan
     2  
     3  import (
     4  	"crypto/aes"
     5  	"encoding/binary"
     6  	"encoding/hex"
     7  	"math"
     8  )
     9  
    10  // Session is used to store session data of a LoRaWAN session
    11  type Session struct {
    12  	NwkSKey    [16]uint8
    13  	AppSKey    [16]uint8
    14  	DevAddr    [4]uint8
    15  	FCntDown   uint32
    16  	FCntUp     uint32
    17  	CFList     [16]uint8
    18  	RXDelay    uint8
    19  	DLSettings uint8
    20  }
    21  
    22  // SetDevAddr configures the Session DevAddr
    23  func (s *Session) SetDevAddr(devAddr []uint8) error {
    24  	if len(devAddr) != 4 {
    25  		return ErrInvalidDevAddrLength
    26  	}
    27  
    28  	copy(s.DevAddr[:], devAddr)
    29  
    30  	return nil
    31  }
    32  
    33  // GetDevAddr returns the Session DevAddr
    34  func (s *Session) GetDevAddr() string {
    35  	return hex.EncodeToString(s.DevAddr[:])
    36  }
    37  
    38  // SetNwkSKey configures the Session NwkSKey
    39  func (s *Session) SetNwkSKey(nwkSKey []uint8) error {
    40  	if len(nwkSKey) != 16 {
    41  		return ErrInvalidNwkSKeyLength
    42  	}
    43  
    44  	copy(s.NwkSKey[:], nwkSKey)
    45  
    46  	return nil
    47  }
    48  
    49  // GetNwkSKey returns the Session NwkSKey
    50  func (s *Session) GetNwkSKey() string {
    51  	return hex.EncodeToString(s.NwkSKey[:])
    52  }
    53  
    54  // SetAppSKey configures the Session AppSKey
    55  func (s *Session) SetAppSKey(appSKey []uint8) error {
    56  	if len(appSKey) != 16 {
    57  		return ErrInvalidAppSKeyLength
    58  	}
    59  
    60  	copy(s.AppSKey[:], appSKey)
    61  
    62  	return nil
    63  }
    64  
    65  // GetAppSKey returns the Session AppSKey
    66  func (s *Session) GetAppSKey() string {
    67  	return hex.EncodeToString(s.AppSKey[:])
    68  }
    69  
    70  // GenMessage generates an uplink message.
    71  func (s *Session) GenMessage(dir uint8, payload []uint8) ([]uint8, error) {
    72  	var buf []uint8
    73  	buf = append(buf, 0b01000000) // FHDR Unconfirmed up
    74  	buf = append(buf, s.DevAddr[:]...)
    75  
    76  	// FCtl : No ADR, No RFU, No ACK, No FPending, No FOpt
    77  	buf = append(buf, 0x00)
    78  
    79  	// FCnt Up
    80  	buf = append(buf, uint8(s.FCntUp&0xFF), uint8((s.FCntUp>>8)&0xFF))
    81  
    82  	// FPort=1
    83  	buf = append(buf, 0x01)
    84  
    85  	fCnt := uint32(0)
    86  	if dir == 0 {
    87  		fCnt = s.FCntUp
    88  		s.FCntUp++
    89  	} else {
    90  		fCnt = s.FCntDown
    91  	}
    92  	data, err := s.genFRMPayload(dir, fCnt, payload, false)
    93  	if err != nil {
    94  		return nil, err
    95  	}
    96  	buf = append(buf, data[:]...)
    97  
    98  	mic := calcMessageMIC(buf, s.NwkSKey, dir, s.DevAddr[:], fCnt, uint8(len(buf)))
    99  	buf = append(buf, mic[:]...)
   100  
   101  	return buf, nil
   102  }
   103  
   104  func (s *Session) genFRMPayload(dir uint8, fCnt uint32, payload []byte, isFOpts bool) ([]byte, error) {
   105  	k := len(payload) / aes.BlockSize
   106  	if len(payload)%aes.BlockSize != 0 {
   107  		k++
   108  	}
   109  	if k > math.MaxUint8 {
   110  		return nil, ErrFrmPayloadTooLarge
   111  	}
   112  	encrypted := make([]byte, 0, k*16)
   113  	cipher, err := aes.NewCipher(s.AppSKey[:])
   114  	if err != nil {
   115  		panic(err)
   116  	}
   117  
   118  	var a [aes.BlockSize]byte
   119  	a[0] = 0x01
   120  	a[5] = dir
   121  	copy(a[6:10], s.DevAddr[:])
   122  	binary.LittleEndian.PutUint32(a[10:14], fCnt)
   123  	var ss [aes.BlockSize]byte
   124  	var b [aes.BlockSize]byte
   125  	for i := uint8(0); i < uint8(k); i++ {
   126  		copy(b[:], payload[i*aes.BlockSize:])
   127  		if !isFOpts {
   128  			a[15] = i + 1
   129  		}
   130  		cipher.Encrypt(ss[:], a[:])
   131  		for j := 0; j < aes.BlockSize; j++ {
   132  			b[j] = b[j] ^ ss[j]
   133  		}
   134  		encrypted = append(encrypted, b[:]...)
   135  	}
   136  	return encrypted[:len(payload)], nil
   137  }