Flashing morse code on a LED implant

Since not everybody has a Proxmark3, here’s a version of the script that uses an ACR122U reader instead. The performance is similarly craptastic, but at least it work. really good! It goes up to 135 WPM on an ordinary PC [EDIT: I thought it was crap because I coded it in a virtual machine, and it won’t go over 10 WPM without timing overruns in that kind of environment]

Tested on Linux running PCSC-Lite. It ought to work in Windows with very minor modifications, but I haven’t tried.

#!/usr/bin/python3

### Parameters
default_wpm     = 10 #words per minute
msg_start_pause = "   "
msg_end_pause   = "   "



### Modules
import sys
import argparse
from time import sleep
from datetime import datetime
from smartcard.scard import *



### Defines
# Morse code dictionary
morsecode = {
	"A":     ".-",
	"B":     "-...",
	"C":     "-.-.",
	"D":     "-..",
	"E":     ".",
	"F":     "..-.",
	"G":     "--.",
	"H":     "....",
	"I":     "..",
	"J":     ".---",
	"K":     "-.-",
	"L":     ".-..",
	"M":     "--",
	"N":     "-.",
	"O":     "---",
	"P":     ".--.",
	"Q":     "--.-",
	"R":     ".-.",
	"S":     "...",
	"T":     "-",
	"U":     "..-",
	"V":     "...-",
	"W":     ".--",
	"X":     "-..-",
	"Y":     "-.--",
	"Z":     "--..",
	"1":     ".----",
	"2":     "..---",
	"3":     "...--",
	"4":     "....-",
	"5":     ".....",
	"6":     "-....",
	"7":     "--...",
	"8":     "---..",
	"9":     "----.",
	"0":     "-----",
	"=":     "-...-",
	"/":     "-..-.",
	"?":     "..--..",
	",":     "--..--",
	".":     ".-.-.-",
	":":     "---...",
	"'":     ".----.",
	'"':     ".-..-.",
	"_":     "..--.-",
	"(":     "-.--.",
	")":     "-.--.-",
	"#":     "-.---",
	"-":     "-....-",
	"|":     "...-..",
	"\\":    "-.....",
	"*":     "-----.",
	";":     "-.-.-.",
	"@":     ".--.-.",
	"^":     "....--.-.",
	"$":     "...-..-",
	"!":     "....-.",
	">":     "....---.",
	"]":     "....-....",
	"[":     "....-..",
	"<":     "....-.-..",
	"&":     "....--.",
	"%":     "....-.--.",
	"~":     "....--",
	"+":     ".-.-.",
	"{":     "....-.--",
	"}":     "....--..-",
	"[AR]":  ".-.-.",
	"[AS]":  ".-...",
	"[BK]":  "-...-.-",
	"[BT]":  "-...-",
	"[KA]":  "-.-.-",
	"[CL]":  "-.-..-..",
	"[KN]":  "-.--.",
	"[VA]":  "...-.-",
	"[VE]":  "...-.",
	"[GR]":  "--..-.",
	"[HM]":  "....--",
	"[IX]":  "..-..-",
	"[IMI]": "..--..",
	"[INT]": "..-.-",
	"[SOS]": "...---..."}

# CCID escape command
ioctl_ccid_escape = SCARD_CTL_CODE(1)	# For PCSC-Lite. Use 3500 for Windows

# ACR122U pseudo-APDU commands
cmd_get_fw_revision = [0xff, 0x00, 0x48, 0x00, 0x00]
cmd_disable_polling = [0xff, 0x00, 0x51, 0x00, 0x00]
cmd_enable_polling  = [0xff, 0x00, 0x51, 0xff, 0x00]

# PN53x commands, wrapped in ACR122U direct transmit pseudo-APDUs
cmd_rf_field_off    = [0xff, 0x00, 0x00, 0x00, 0x04, 0xd4, 0x32, 0x01, 0x00]
cmd_rf_field_on     = [0xff, 0x00, 0x00, 0x00, 0x04, 0xd4, 0x32, 0x01, 0x01]



### Routines
def send_acr122u_control_command(cmd, hcard):
  """Send the ACR122U a control command, return the raw result and raise an
  exception in case of error
  """

  hresult, response = SCardControl(hcard, ioctl_ccid_escape, cmd)
  if hresult != SCARD_S_SUCCESS:
     raise SystemError("Failure to control: {}".format(
			SCardGetErrorMessage(hresult)))
  return(response)
  


### Main routine
def main():
  """Main routine
  """

  # Read the command line arguments
  argparser = argparse.ArgumentParser()
  argparser.add_argument(
	  "-w", "--words-per-minute",
	  type = int,
	  help = "Rate of the morse code (default: {})".format(default_wpm),
	  default = default_wpm,
          required = False
	)
  argparser.add_argument(
	  "message",
	  type = str,
	  help = "Text to output in morse code on LED implant",
	)
  args = argparser.parse_args()

  wpm = args.words_per_minute
  msg = args.message.upper()

  if not msg:
    print("Nothing to do")
    return(0)



  # Add a pause at the beginning and at the end of the message
  msg = msg_start_pause + msg + msg_end_pause

  # Turn the message into a sequence of field-on durations (positive) and
  # field-off durations (negative)
  morsechars = []
  i = 0
  while i < len(msg):

    if msg[i] == "[":
      j = msg.find("]", i + 1)
      if j > 0 and msg[i : j + 1] in morsecode:
        morsechars.append(morsecode[msg[i : j + 1]])
        i = j + 1
        continue

    if msg[i] == " " or msg[i] == "\t":
      morsechars.append(" ")

    elif msg[i] in morsecode:
      morsechars.append(morsecode[msg[i]])

    else:
      print("Untranslatable in morse code: {} - dropped".format(msg[i]))

    i += 1

  ditlen = 1.2 / wpm 
      
  morseseq = []
  for mc in morsechars:

    if mc == " ":
      if morseseq:
        morseseq[-1] = -ditlen * 7
      else:
        morseseq.append(-ditlen * 7)
      continue

    for c in mc:
      if c == ".":
        morseseq.append(ditlen)
      else:
        morseseq.append(ditlen * 3)
      morseseq.append(-ditlen)

    morseseq[-1] = -ditlen * 3



  # Establish context and connect to the reader
  try:
    hresult, hcontext = SCardEstablishContext(SCARD_SCOPE_USER)
    hresult, hcard, dwActiveProtocol = SCardConnect(hcontext,
					'ACS ACR122U PICC Interface 00 00',
					SCARD_SHARE_DIRECT, SCARD_PROTOCOL_T0)
  except:
    print("Cannot connect to ACR122U")
    return(-1)

  # Get the ACR122U's firmware revision number, to test that CCID escape has
  # been enabled and to double-check that the reader is indeed an ACR122U
  try:
    fwrev = "".join([chr(v) for v in send_acr122u_control_command(
		cmd_get_fw_revision, hcard)])
  except:
    print("Error getting ACR122U firmware revision number.")
    print("Is the CCID exchange command allowed in /etc/libccid_Info.plist")
    return(-2)
  
  if fwrev[:7].upper() != "ACR122U":
    print('Error: "{}" does not appear to be an ACR122U'.format(fwrev))
    return(-3)

  # Disable polling so the reader won't try to read the implant when the field
  # is turned on
  try:
    send_acr122u_control_command(cmd_disable_polling, hcard)
  except:
    print("Error disabling polling")
    return(-4)

  # Play the morse code sequence
  warn_if_too_fast = True
  for d in morseseq:

    # Flip the RF field on or off
    cmd_start_tstamp = datetime.now().timestamp()
    try:
      send_acr122u_control_command(cmd_rf_field_off if d < 0 \
					else cmd_rf_field_on, hcard)
    except:
      print("Error switching the RF field {}".format("off" if d < 0 else "on"))
      return(-5)
    cmd_duration = datetime.now().timestamp() - cmd_start_tstamp

    # Wait for however long it takes to match the duration in the sequence
    remaining_wait = abs(d) - cmd_duration
    if remaining_wait > 0:
      sleep(remaining_wait)
    else:
      if warn_if_too_fast:
        print("Warning: {} WPM is too fast for the ACR122U and/or your " \
		"computer.".format(wpm))
        print("Morse code timing may appear incorrect")
        warn_if_too_fast = False        

  # Re-enable the RF field and re-enable polling so the reader can work again
  # as a regular reader
  try:
    send_acr122u_control_command(cmd_rf_field_on, hcard)
  except:
    print("Error re-enabling the RF field")
    return(-5)

  try:
    send_acr122u_control_command(cmd_enable_polling, hcard)
  except:
    print("Error re-enabling polling")
    return(-6)

      

### Jump to the main routine
if __name__ == "__main__":
  sys.exit(main())
1 Like