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 }