Skip to content

Instantly share code, notes, and snippets.

@netscylla
Last active February 25, 2024 22:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save netscylla/b54ab2fb2f4f5b06d3da86de70093889 to your computer and use it in GitHub Desktop.
Save netscylla/b54ab2fb2f4f5b06d3da86de70093889 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3
##########################################
#
# //\ e s h t /\ s t / c
#
# Meshtastic Channel URL Decoder (c) 2024
# Print Key and Channelname from Meshtastic encoded-Channel URL
#
# License :
# http://www.gnu.org/licenses/agpl-3.0.txt
#
# Author: Andy @ Netscylla
#
###########################################
import argparse
import base64
import urllib.parse
def parse_url_and_decode(url):
# Parse the URL
parsed_url = urllib.parse.urlparse(url)
# Get the last part of the path
element = parsed_url.fragment
padding_needed = len(element) % 5
element += "=" * padding_needed
# Decode the last part using base64
element = element.replace("-", "+").replace("_", "/")
decoded_bytes = base64.b64decode(element.encode("utf-8"))
return decoded_bytes
def interpret_bytes(decoded_bytes):
# Define a lambda function to extract key information
extract_key_info = lambda i: (
decoded_bytes[i + 3],
decoded_bytes[i + 4 : i + 4 + decoded_bytes[i + 3]],
)
# Define a lambda function to extract channel name information
extract_channel_info = lambda i: decoded_bytes[
i + 1 : i + 2 + decoded_bytes[i + 1]
].decode("utf-8")
result = ""
i = 0
while i < len(decoded_bytes):
if decoded_bytes[i] == 0x0A:
# Length of key
length, key = extract_key_info(i)
result += f"Length of key: {length}"
result += "\nKey: "
result += " ".join([format(byte, "02x") for byte in key])
reencoded_key = base64.b64encode(key).decode("utf-8")
result += f"\nRe-encoded key: {reencoded_key}"
i += 3 + length
elif decoded_bytes[i] == 0x1A:
# Channel name follows
channel_name = extract_channel_info(i)
result += f"\nChannel name: {channel_name}"
i += 1 + len(channel_name)
elif decoded_bytes[i] == 0x02:
# Byte after the end of channel name - Primary
result += "\nChannel type: Primary"
i += 1
elif decoded_bytes[i] == 0x04:
# Byte after the end of channel name - Secondary
result += "\nChannel type: Secondary"
i += 1
elif decoded_bytes[i : i + 2] == b"\x00\x28":
# Two bytes after 00 28 = Uplink enabled
result += "\nUplink enabled"
i += 2
elif decoded_bytes[i : i + 2] == b"\x01\x30":
# Two bytes after 01 30 = Downlink enabled
result += "\nDownlink enabled"
i += 2
else:
# Junk bytes
# result += f"\n\nJunk byte: {decoded_bytes[i]}"
i += 1
return result
def main():
parser = argparse.ArgumentParser(description="Decode and interpret a URL")
parser.add_argument("url", help="URL to decode and interpret")
args = parser.parse_args()
url = args.url
print("Found URL: ", url)
decoded_bytes = parse_url_and_decode(url)
interpretation = interpret_bytes(decoded_bytes)
print(interpretation)
if __name__ == "__main__":
print("//\ e s h t /\ s t / c Channel URL Decoder")
print("\nAndy@Netscylla (c) 2024\n\n")
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment