meshtastic 0.0.39 → 0.0.41

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: aa504f8ade89178504929945de8d426f8a33d59f823148916fccc0f4760fdb6d
4
- data.tar.gz: e0972850468fd4b91badf6295890328c8f469fd0cdf98e17b45ad40617c76dae
3
+ metadata.gz: 94567b95fcf287b1a47b7af7ea3efb4b6f15a5a0daf81137127b6f5dede9a8fd
4
+ data.tar.gz: aa07f4271ac1cb1953492ab508f2f8f4f2c7c0c51fb306195b096b950b917c25
5
5
  SHA512:
6
- metadata.gz: c3910c79e13dc8a84eb5df0e80b96596746f147f417af3f273e33cbed4ea7ee4e1563fd602eedbc97e60d3a7a2b5a41751ac66a67e6437d3027c111c084c5285
7
- data.tar.gz: aaf6deaf8e117a42b9b56503cd15a6c1ade82ce2a0db4101cffcf0d685a8951cbf2b251c8b764e23ea483ba04e1f9a3987162bf345ddd4f52d96545b07e449e2
6
+ metadata.gz: 77093a3977ef63ad7c15d255c679771e5fe02448741147acbc608f19c057d9b9f8f47fe214e0f0a2799cca331726450f978bbda99248fb5d3f592e4d37b09a43
7
+ data.tar.gz: 4c617b139c876db4b3fff9f4357fba3785972e47e88e85a9a9a1656db8c5ced40e42699b737aa7b84f3cc86ced3f66480a3def02e6e1a2272bdd23ac70d82858
data/Gemfile CHANGED
@@ -23,4 +23,5 @@ gem 'rubocop', '1.63.4'
23
23
  gem 'rubocop-rake', '0.6.0'
24
24
  gem 'rubocop-rspec', '2.29.1'
25
25
  gem 'rvm', '1.11.3.9'
26
+ gem 'tty-prompt', '0.23.1'
26
27
  gem 'yard', '0.9.36'
@@ -6,6 +6,7 @@ require 'json'
6
6
  require 'mqtt'
7
7
  require 'openssl'
8
8
  require 'securerandom'
9
+ require 'tty-prompt'
9
10
 
10
11
  # Avoiding Namespace Collisions
11
12
  MQTTClient = MQTT::Client
@@ -42,13 +43,33 @@ module Meshtastic
42
43
  raise e
43
44
  end
44
45
 
46
+ # Supported Method Parameters::
47
+ # Meshtastic::MQQT.get_cipher_keys(
48
+ # psks: 'required - hash of channel / pre-shared key value pairs'
49
+ # )
50
+
51
+ private_class_method def self.get_cipher_keys(opts = {})
52
+ psks = opts[:psks]
53
+
54
+ psks.each_key do |key|
55
+ psk = psks[key]
56
+ padded_psk = psk.ljust(psk.length + ((4 - (psk.length % 4)) % 4), '=')
57
+ replaced_psk = padded_psk.gsub('-', '+').gsub('_', '/')
58
+ psks[key] = replaced_psk
59
+ end
60
+
61
+ psks
62
+ rescue StandardError => e
63
+ raise e
64
+ end
65
+
45
66
  # Supported Method Parameters::
46
67
  # Meshtastic::MQQT.subscribe(
47
68
  # mqtt_obj: 'required - mqtt_obj returned from #connect method'
48
69
  # root_topic: 'optional - root topic (default: msh)',
49
70
  # region: 'optional - region e.g. 'US/VA', etc (default: US)',
50
71
  # channel: 'optional - channel name e.g. "2/stat/#" (default: "2/e/LongFast/#")',
51
- # psk: 'optional - channel pre-shared key (default: AQ==)',
72
+ # psks: 'optional - hash of :channel => psk key value pairs (default: { LongFast: "AQ==" })',
52
73
  # qos: 'optional - quality of service (default: 0)',
53
74
  # filter: 'optional - comma-delimited string(s) to filter on in message (default: nil)',
54
75
  # gps_metadata: 'optional - include GPS metadata in output (default: false)'
@@ -59,34 +80,30 @@ module Meshtastic
59
80
  root_topic = opts[:root_topic] ||= 'msh'
60
81
  region = opts[:region] ||= 'US'
61
82
  channel = opts[:channel] ||= '2/e/LongFast/#'
62
- psk = opts[:psk] ||= 'AQ=='
83
+ # TODO: Support Array of PSKs and attempt each until decrypted
84
+
85
+ public_psk = '1PG7OiApB1nwvP+rz05pAQ=='
86
+ psks = opts[:psks] ||= { LongFast: public_psk }
87
+ raise 'ERROR: psks parameter must be a hash of :channel => psk key value pairs' unless psks.is_a?(Hash)
88
+
89
+ psks[:LongFast] = public_psk if psks[:LongFast] == 'AQ=='
90
+ psks = get_cipher_keys(psks: psks)
91
+
63
92
  qos = opts[:qos] ||= 0
64
93
  json = opts[:json] ||= false
65
94
  filter = opts[:filter]
66
95
  gps_metadata = opts[:gps_metadata] ||= false
67
96
 
68
- # TODO: Find JSON URI for this
97
+ # NOTE: Use MQTT Explorer for topic discovery
69
98
  full_topic = "#{root_topic}/#{region}/#{channel}"
70
99
  puts "Subscribing to: #{full_topic}"
71
100
  mqtt_obj.subscribe(full_topic, qos)
72
101
 
73
- # Decrypt the message
74
- # Our AES key is 128 or 256 bits, shared as part of the 'Channel' specification.
75
-
76
- # Actual pre-shared key for LongFast channel
77
- psk = '1PG7OiApB1nwvP+rz05pAQ==' if channel.include?('LongFast')
78
- padded_psk = psk.ljust(psk.length + ((4 - (psk.length % 4)) % 4), '=')
79
- replaced_psk = padded_psk.gsub('-', '+').gsub('_', '/')
80
- psk = replaced_psk
81
- dec_psk = Base64.strict_decode64(psk)
82
-
83
- # cipher = OpenSSL::Cipher.new('AES-256-CTR')
84
- cipher = OpenSSL::Cipher.new('AES-128-CTR')
85
102
  filter_arr = filter.to_s.split(',').map(&:strip)
86
103
  mqtt_obj.get_packet do |packet_bytes|
87
104
  # raw_packet = packet_bytes.to_s.b
88
105
  raw_topic = packet_bytes.topic ||= ''
89
- raw_payload = packet_bytes.payload
106
+ raw_payload = packet_bytes.payload ||= ''
90
107
 
91
108
  begin
92
109
  disp = false
@@ -109,15 +126,21 @@ module Meshtastic
109
126
  encrypted_message = message[:encrypted]
110
127
  # If encrypted_message is not nil, then decrypt the message
111
128
  if encrypted_message.to_s.length.positive?
129
+
112
130
  packet_id = message[:id]
113
131
  packet_from = message[:from]
132
+
114
133
  nonce_packet_id = [packet_id].pack('V').ljust(8, "\x00")
115
134
  nonce_from_node = [packet_from].pack('V').ljust(8, "\x00")
116
135
  nonce = "#{nonce_packet_id}#{nonce_from_node}".b
117
136
 
118
- # Decrypt the message
119
- # Key must be 32 bytes
120
- # IV mustr be 16 bytes
137
+ psk = psks[:LongFast]
138
+ target_chanel = decoded_payload_hash[:channel_id].to_s.to_sym
139
+ psk = psks[target_chanel] if psks.keys.include?(target_chanel)
140
+ dec_psk = Base64.strict_decode64(psk)
141
+
142
+ cipher = OpenSSL::Cipher.new('AES-128-CTR')
143
+ cipher = OpenSSL::Cipher.new('AES-256-CTR') if dec_psk.length == 32
121
144
  cipher.decrypt
122
145
  cipher.key = dec_psk
123
146
  cipher.iv = nonce
@@ -319,7 +342,7 @@ module Meshtastic
319
342
  root_topic: 'optional - root topic (default: msh)',
320
343
  region: 'optional - region e.g. 'US/VA', etc (default: US)',
321
344
  channel: 'optional - channel name e.g. '2/stat/#' (default: '2/e/LongFast/#')',
322
- psk: 'optional - channel pre-shared key (default: AQ==)',
345
+ psks: 'optional - hash of :channel => psk key value pairs (default: { LongFast: 'AQ==' })',
323
346
  qos: 'optional - quality of service (default: 0)',
324
347
  json: 'optional - JSON output (default: false)',
325
348
  filter: 'optional - comma-delimited string(s) to filter on in message (default: nil)',
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Meshtastic
4
- VERSION = '0.0.39'
4
+ VERSION = '0.0.41'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: meshtastic
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.39
4
+ version: 0.0.41
5
5
  platform: ruby
6
6
  authors:
7
7
  - 0day Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-05-01 00:00:00.000000000 Z
11
+ date: 2024-05-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -178,6 +178,20 @@ dependencies:
178
178
  - - '='
179
179
  - !ruby/object:Gem::Version
180
180
  version: 1.11.3.9
181
+ - !ruby/object:Gem::Dependency
182
+ name: tty-prompt
183
+ requirement: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - '='
186
+ - !ruby/object:Gem::Version
187
+ version: 0.23.1
188
+ type: :runtime
189
+ prerelease: false
190
+ version_requirements: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - '='
193
+ - !ruby/object:Gem::Version
194
+ version: 0.23.1
181
195
  - !ruby/object:Gem::Dependency
182
196
  name: yard
183
197
  requirement: !ruby/object:Gem::Requirement