@@ -66,34 +66,47 @@ export class EncryptionService {
6666 // Decode base64 key
6767 let keyBuffer = Buffer . from ( channel . key , 'base64' ) ;
6868
69+ logger . info ( `Processing channel ${ index } (${ channel . name } ): raw key length = ${ keyBuffer . length } , first byte = 0x${ keyBuffer [ 0 ] ?. toString ( 16 ) . padStart ( 2 , '0' ) } ` ) ;
70+
6971 // Meshtastic uses special 1-byte PSK shortcuts:
7072 // 0x00 = no encryption
7173 // 0x01 = default key (fixed 16-byte AES-128 key)
7274 // 0x02-0x0A = default key with last byte incremented (simple2-simple10)
7375 if ( keyBuffer . length === 1 ) {
7476 const pskByte = keyBuffer [ 0 ] ;
7577
78+ logger . info ( `Detected 1-byte PSK: 0x${ pskByte . toString ( 16 ) . padStart ( 2 , '0' ) } ` ) ;
79+
7680 if ( pskByte === 0x00 ) {
7781 // No encryption - skip this channel
7882 logger . info ( `Channel ${ index } (${ channel . name } ) has no encryption (PSK 0x00), skipping` ) ;
7983 return ;
8084 } else if ( pskByte >= 0x01 && pskByte <= 0x0A ) {
8185 // Meshtastic default key: d4 f1 bb 3a 20 29 07 59 f0 bc ff ab cf 4e 69 01
8286 // For PSK 0x02-0x0A, add (pskByte - 1) to the last byte
83- const defaultKey = Buffer . from ( [
87+ // NOTE: Meshtastic uses AES-256-CTR, so we need to expand to 32 bytes
88+ // The 16-byte default key is repeated/padded to 32 bytes
89+ const defaultKey16 = Buffer . from ( [
8490 0xd4 , 0xf1 , 0xbb , 0x3a , 0x20 , 0x29 , 0x07 , 0x59 ,
8591 0xf0 , 0xbc , 0xff , 0xab , 0xcf , 0x4e , 0x69 , 0x01
8692 ] ) ;
8793
94+ // Expand to 32 bytes by repeating the key
95+ const defaultKey32 = Buffer . alloc ( 32 ) ;
96+ defaultKey16 . copy ( defaultKey32 , 0 ) ;
97+ defaultKey16 . copy ( defaultKey32 , 16 ) ;
98+
8899 if ( pskByte > 0x01 ) {
89100 // For simple2-simple10, increment the last byte
90- defaultKey [ 15 ] = defaultKey [ 15 ] + ( pskByte - 0x01 ) ;
91- logger . info ( `Mapped 1-byte PSK 0x${ pskByte . toString ( 16 ) . padStart ( 2 , '0' ) } to Meshtastic simple${ pskByte } key for channel ${ index } : ${ channel . name } ` ) ;
101+ defaultKey32 [ 15 ] = defaultKey32 [ 15 ] + ( pskByte - 0x01 ) ;
102+ defaultKey32 [ 31 ] = defaultKey32 [ 31 ] + ( pskByte - 0x01 ) ;
103+ logger . info ( `Mapped 1-byte PSK 0x${ pskByte . toString ( 16 ) . padStart ( 2 , '0' ) } to Meshtastic simple${ pskByte } key (32 bytes) for channel ${ index } : ${ channel . name } ` ) ;
92104 } else {
93- logger . info ( `Mapped 1-byte PSK 0x01 to Meshtastic default key for channel ${ index } : ${ channel . name } ` ) ;
105+ logger . info ( `Mapped 1-byte PSK 0x01 to Meshtastic default key (32 bytes) for channel ${ index } : ${ channel . name } ` ) ;
94106 }
95107
96- keyBuffer = defaultKey ;
108+ keyBuffer = defaultKey32 ;
109+ logger . info ( `After expansion: keyBuffer length = ${ keyBuffer . length } ` ) ;
97110 } else {
98111 // Unknown 1-byte PSK, pad with zeros
99112 const paddedKey = Buffer . alloc ( 16 , 0 ) ;
@@ -102,11 +115,28 @@ export class EncryptionService {
102115 logger . warn ( `Unknown 1-byte PSK 0x${ pskByte . toString ( 16 ) . padStart ( 2 , '0' ) } for channel ${ index } : ${ channel . name } , padding with zeros` ) ;
103116 }
104117 } else if ( keyBuffer . length < 16 ) {
105- // For keys shorter than 16 bytes (but not 1 byte), pad with zeros
106- const paddedKey = Buffer . alloc ( 16 , 0 ) ;
118+ // For keys shorter than 16 bytes (but not 1 byte), expand to 32 bytes for AES-256
119+ const expandedKey = Buffer . alloc ( 32 , 0 ) ;
120+ keyBuffer . copy ( expandedKey ) ;
121+ // Repeat the key pattern
122+ for ( let i = keyBuffer . length ; i < 32 ; i ++ ) {
123+ expandedKey [ i ] = keyBuffer [ i % keyBuffer . length ] ;
124+ }
125+ keyBuffer = expandedKey ;
126+ logger . info ( `Expanded encryption key for channel ${ index } : ${ channel . name } (${ Buffer . from ( channel . key , 'base64' ) . length } -> 32 bytes)` ) ;
127+ } else if ( keyBuffer . length === 16 ) {
128+ // Expand 16-byte key to 32 bytes by repeating
129+ const expandedKey = Buffer . alloc ( 32 ) ;
130+ keyBuffer . copy ( expandedKey , 0 ) ;
131+ keyBuffer . copy ( expandedKey , 16 ) ;
132+ keyBuffer = expandedKey ;
133+ logger . info ( `Expanded 16-byte key to 32 bytes for channel ${ index } : ${ channel . name } ` ) ;
134+ } else if ( keyBuffer . length > 16 && keyBuffer . length < 32 ) {
135+ // Pad to 32 bytes
136+ const paddedKey = Buffer . alloc ( 32 , 0 ) ;
107137 keyBuffer . copy ( paddedKey ) ;
108138 keyBuffer = paddedKey ;
109- logger . info ( `Padded encryption key for channel ${ index } : ${ channel . name } (${ Buffer . from ( channel . key , 'base64' ) . length } -> 16 bytes)` ) ;
139+ logger . info ( `Padded encryption key for channel ${ index } : ${ channel . name } (${ Buffer . from ( channel . key , 'base64' ) . length } -> 32 bytes)` ) ;
110140 }
111141
112142 logger . info ( `Loaded encryption key for channel ${ index } : ${ channel . name } (${ keyBuffer . length } bytes, base64: ${ channel . key } )` ) ;
@@ -165,34 +195,55 @@ export class EncryptionService {
165195 return null ;
166196 }
167197
198+ // Log the full encrypted payload for debugging
199+ logger . info ( `=== DECRYPTION DEBUG ===` ) ;
200+ logger . info ( `Encrypted payload length: ${ encryptedPayload . length } bytes` ) ;
201+ logger . info ( `Full encrypted payload (hex): ${ encryptedPayload . toString ( 'hex' ) } ` ) ;
202+ logger . info ( `Packet ID: ${ packetId } ` ) ;
203+ logger . info ( `Channel index: ${ channelIndex } ` ) ;
204+ logger . info ( `Key (hex): ${ key . toString ( 'hex' ) } ` ) ;
205+
168206 // Extract nonce (first 8 bytes) and pad to 16 bytes for CTR mode
169207 const nonce = Buffer . alloc ( 16 , 0 ) ;
170208 encryptedPayload . copy ( nonce , 0 , 0 , 8 ) ;
171209
172210 // Extract ciphertext (everything after the 8-byte nonce)
173211 const ciphertext = encryptedPayload . slice ( 8 ) ;
174212
175- logger . debug ( `Decrypting with nonce: ${ nonce . toString ( 'hex' ) } , channel: ${ channelIndex } , ciphertext length: ${ ciphertext . length } ` ) ;
213+ logger . info ( `Nonce (8 bytes): ${ encryptedPayload . slice ( 0 , 8 ) . toString ( 'hex' ) } ` ) ;
214+ logger . info ( `Nonce padded (16 bytes): ${ nonce . toString ( 'hex' ) } ` ) ;
215+ logger . info ( `Ciphertext length: ${ ciphertext . length } bytes` ) ;
216+ logger . info ( `Ciphertext (first 32 bytes): ${ ciphertext . slice ( 0 , 32 ) . toString ( 'hex' ) } ` ) ;
176217
177218 // Determine the algorithm based on key length
178- // Meshtastic uses AES-128 -CTR (16 -byte key)
219+ // Meshtastic uses AES-256 -CTR (32 -byte key)
179220 let algorithm : string ;
180- if ( key . length === 16 ) {
181- algorithm = 'aes-128-ctr' ;
182- } else if ( key . length === 32 ) {
221+ if ( key . length === 32 ) {
222+ algorithm = 'aes-256-ctr' ;
223+ } else if ( key . length === 16 ) {
224+ // Expand 16-byte key to 32 bytes for AES-256
225+ logger . info ( `Expanding 16-byte key to 32 bytes for AES-256-CTR` ) ;
226+ const expandedKey = Buffer . alloc ( 32 ) ;
227+ key . copy ( expandedKey , 0 ) ;
228+ key . copy ( expandedKey , 16 ) ;
229+ key = expandedKey ;
183230 algorithm = 'aes-256-ctr' ;
184231 } else {
185232 logger . warn ( `Unexpected key length: ${ key . length } bytes, expected 16 or 32` ) ;
186- // Try to use the key as-is with AES-128
187- algorithm = 'aes-128 -ctr' ;
188- if ( key . length < 16 ) {
189- // Pad key to 16 bytes if too short
190- const paddedKey = Buffer . alloc ( 16 , 0 ) ;
233+ // Try to expand/pad to 32 bytes for AES-256
234+ algorithm = 'aes-256 -ctr' ;
235+ if ( key . length < 32 ) {
236+ // Pad key to 32 bytes
237+ const paddedKey = Buffer . alloc ( 32 , 0 ) ;
191238 key . copy ( paddedKey ) ;
239+ // If key is 16 bytes or less, repeat it
240+ if ( key . length <= 16 ) {
241+ key . copy ( paddedKey , 16 , 0 , Math . min ( key . length , 16 ) ) ;
242+ }
192243 key = paddedKey ;
193- } else if ( key . length > 16 && key . length < 32 ) {
194- // Truncate to 16 bytes
195- key = key . slice ( 0 , 16 ) ;
244+ } else {
245+ // Truncate to 32 bytes
246+ key = key . slice ( 0 , 32 ) ;
196247 }
197248 }
198249
@@ -205,7 +256,11 @@ export class EncryptionService {
205256 decipher . final ( )
206257 ] ) ;
207258
208- logger . debug ( `Successfully decrypted payload (${ ciphertext . length } -> ${ decrypted . length } bytes)` ) ;
259+ logger . info ( `Decrypted length: ${ decrypted . length } bytes` ) ;
260+ logger . info ( `Decrypted (first 64 bytes): ${ decrypted . slice ( 0 , Math . min ( 64 , decrypted . length ) ) . toString ( 'hex' ) } ` ) ;
261+ logger . info ( `Decrypted as ASCII: ${ decrypted . slice ( 0 , Math . min ( 64 , decrypted . length ) ) . toString ( 'ascii' ) . replace ( / [ ^ \x20 - \x7E ] / g, '.' ) } ` ) ;
262+ logger . info ( `=== END DECRYPTION DEBUG ===` ) ;
263+
209264 return decrypted ;
210265 } catch ( error ) {
211266 logger . error ( 'Failed to decrypt message:' , error ) ;
0 commit comments