meshtastic 0.0.40 → 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: 9dc432f90a8545cad1027f5514f0676a60d776f617db87b394436e1657693e98
4
- data.tar.gz: 5a461b32da2516b164ab789f2a27653b5f46a4f7d36020ddca1ba161931162e0
3
+ metadata.gz: 94567b95fcf287b1a47b7af7ea3efb4b6f15a5a0daf81137127b6f5dede9a8fd
4
+ data.tar.gz: aa07f4271ac1cb1953492ab508f2f8f4f2c7c0c51fb306195b096b950b917c25
5
5
  SHA512:
6
- metadata.gz: 1094355139b2e703dda03bf60ff7034aeb94e1d7d335ba8cd73aae0d9c3bae4c64e61cfb3b8e2dcbdd8894174ab5e3f77b29fde04617fedc3f578b5e469a2678
7
- data.tar.gz: d5677d5b337b4f1b8cbff41da88eafaeab8377f3235085f6ba2df384b7c19dd24cbece877d1d6c39f4cf3c4b9d6674f93ed6cb783bc62bd739e12379f3a8a552
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)'
@@ -60,34 +81,29 @@ module Meshtastic
60
81
  region = opts[:region] ||= 'US'
61
82
  channel = opts[:channel] ||= '2/e/LongFast/#'
62
83
  # TODO: Support Array of PSKs and attempt each until decrypted
63
- psk = opts[:psk] ||= 'AQ=='
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
+
64
92
  qos = opts[:qos] ||= 0
65
93
  json = opts[:json] ||= false
66
94
  filter = opts[:filter]
67
95
  gps_metadata = opts[:gps_metadata] ||= false
68
96
 
69
- # TODO: Find JSON URI for this
97
+ # NOTE: Use MQTT Explorer for topic discovery
70
98
  full_topic = "#{root_topic}/#{region}/#{channel}"
71
99
  puts "Subscribing to: #{full_topic}"
72
100
  mqtt_obj.subscribe(full_topic, qos)
73
101
 
74
- # Decrypt the message
75
- # Our AES key is 128 or 256 bits, shared as part of the 'Channel' specification.
76
-
77
- # Actual pre-shared key for LongFast channel
78
- psk = '1PG7OiApB1nwvP+rz05pAQ==' if psk == 'AQ=='
79
- padded_psk = psk.ljust(psk.length + ((4 - (psk.length % 4)) % 4), '=')
80
- replaced_psk = padded_psk.gsub('-', '+').gsub('_', '/')
81
- psk = replaced_psk
82
- dec_psk = Base64.strict_decode64(psk)
83
-
84
- # cipher = OpenSSL::Cipher.new('AES-256-CTR')
85
- cipher = OpenSSL::Cipher.new('AES-128-CTR')
86
102
  filter_arr = filter.to_s.split(',').map(&:strip)
87
103
  mqtt_obj.get_packet do |packet_bytes|
88
104
  # raw_packet = packet_bytes.to_s.b
89
105
  raw_topic = packet_bytes.topic ||= ''
90
- raw_payload = packet_bytes.payload
106
+ raw_payload = packet_bytes.payload ||= ''
91
107
 
92
108
  begin
93
109
  disp = false
@@ -110,15 +126,21 @@ module Meshtastic
110
126
  encrypted_message = message[:encrypted]
111
127
  # If encrypted_message is not nil, then decrypt the message
112
128
  if encrypted_message.to_s.length.positive?
129
+
113
130
  packet_id = message[:id]
114
131
  packet_from = message[:from]
132
+
115
133
  nonce_packet_id = [packet_id].pack('V').ljust(8, "\x00")
116
134
  nonce_from_node = [packet_from].pack('V').ljust(8, "\x00")
117
135
  nonce = "#{nonce_packet_id}#{nonce_from_node}".b
118
136
 
119
- # Decrypt the message
120
- # Key must be 32 bytes
121
- # 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
122
144
  cipher.decrypt
123
145
  cipher.key = dec_psk
124
146
  cipher.iv = nonce
@@ -320,7 +342,7 @@ module Meshtastic
320
342
  root_topic: 'optional - root topic (default: msh)',
321
343
  region: 'optional - region e.g. 'US/VA', etc (default: US)',
322
344
  channel: 'optional - channel name e.g. '2/stat/#' (default: '2/e/LongFast/#')',
323
- psk: 'optional - channel pre-shared key (default: AQ==)',
345
+ psks: 'optional - hash of :channel => psk key value pairs (default: { LongFast: 'AQ==' })',
324
346
  qos: 'optional - quality of service (default: 0)',
325
347
  json: 'optional - JSON output (default: false)',
326
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.40'
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.40
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