from datetime import datetime from time import sleep from typing import Any import base64 import serial import sys ENDL = bytes([0]) OK = bytes([1]) ERROR = bytes([2]) HANDSHAKE = bytes([3]) SET_TIMESTAMP = bytes([10]) ADD_TOKEN = bytes([20]) DELETE_TOKEN = bytes([30]) GET_TOKENS = bytes([40]) SOFT_WIPE_TOKENS = bytes([50]) HARD_WIPE_TOKENS = bytes([60]) EXIT = bytes([255]) def loop_input(msg: str, valid_values: Any) -> str: valid_values = list(map(str, valid_values)) while True: data = input(msg) if data not in valid_values: print(f"'{data}' isn't a valid value, please enter a value in {valid_values}") else: break return data def b(n: int) -> bytes: return bytes([n]) def process_secret(secret: str) -> bytes: offset = 8 - (len(secret) % 8) if offset != 8: secret += "=" * offset return base64.b32decode(secret, casefold=True) def get_datetime_items() -> dict[str, bytes]: now = datetime.utcnow() return { "year": b(now.year - 2000), "month": b(now.month), "day": b(now.day), "hours": b(now.hour), "minutes": b(now.minute), "seconds": b(now.second) } def main(argv: list[str]): port = argv[-1] conn = serial.Serial(port=port, baudrate=9600) print("UP + Reset to enable the USB connection") input("Press Enter if the device in the USB mode...") conn.write(HANDSHAKE) sleep(0.1) res = conn.read() if res != OK: print("A handshake could not be performed") print("Check the connection with your Arduino") return 1 else: print("Handshake successfully performed") while True: print("What do you want to do?") print("1) Update timestamp") print("2) Add a new token") print("3) Remove a token") print("4) SOFT wipe all tokens") print("5) HARD WIPE ALL THE TOKENS") print("6) EXIT") opt = loop_input(">>> ", range(1, 7)) # Update Timestamp if opt == "1": conn.write(SET_TIMESTAMP) sleep(0.1) resp = conn.read() sleep(0.1) if resp == ERROR: print(f"Error in the communication: Error {resp}") continue date_items = get_datetime_items(); conn.write(date_items["year"]) conn.write(date_items["month"]) conn.write(date_items["day"]) conn.write(date_items["hours"]) conn.write(date_items["minutes"]) conn.write(date_items["seconds"]) resp = conn.read() if resp != OK: print(f"Error in the communication: Error {resp}") else: print("Timestamp updated successfully!") # Add token elif opt == "2": conn.write(ADD_TOKEN) sleep(0.1) resp = conn.read() if resp == ERROR: print("The memory of the device is full") continue name = input("Enter the name of the new token (16 chars max):\n>>> ") name = name.strip()[:16].encode("ascii") key = input("Enter the OTP secret key (32 chars max):\n>>> ") key = process_secret(key.strip()[:32]) for ch in name: conn.write(b(ch)) conn.write(ENDL) for ch in key: conn.write(b(ch)) conn.write(ENDL) resp = conn.read() if resp == ERROR: print("Error trying to add the token, try again") else: print("Token added successfully!") # Soft wipe tokens elif opt == "4": conn.write(SOFT_WIPE_TOKENS) sleep(0.1) _ = conn.read() deleted_tokens = conn.read() resp = conn.read() if resp == OK: print(f"{deleted_tokens} tokens wipped successfully!") else: print("Error!!") # Hard wipe tokens elif opt == "5": resp = loop_input("This will erase all the EEPROM, do you want to continue? [Y/N]", ["y", "Y", "n", "N"]).upper() if resp == "Y": conn.write(HARD_WIPE_TOKENS) sleep(0.1) _ = conn.read() resp = conn.read() if resp == OK: print("All the Eeprom erased successfully!") else: print("Error!!") elif opt == "6": conn.write(EXIT) return 0 if __name__ == "__main__": attrs = sys.argv if len(attrs) == 1: print("You need to specify the Arduino's serial port") print("Example:") print(f"python {attrs[0]} /dev/ttyUSB0") sys.exit(1) sys.exit(main(attrs[1:]))