meshtastic 0.0.40 → 0.0.41

Sign up to get free protection for your applications and to get access to all the features.
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