Login with a serial RFID / NFC reader under Windows and Linux

The script I had posted above works, but it didn’t always work reliably. As I was trying to figure out why, I realized something: Rohos Logon Key really expects UIDs to come in specific data formats from the serial readers it supports nominally. The script however simply passes it raw LF-terminated ASCII UIDs.

So I modified it a bit: now it simulates an Easyident FS-2044 RFID reader (one of the readers Rohos Logon Key explicitely supports). With that new version, Rohos unlocks each and every time without fail.

Here it is:

#!/usr/bin/python3

# This program emulates an Easyident FS-2044 RFID reader with UIDs read from a
# generic, 10-digit UID, 9600 baud serial RFID reader, for use as an input
# device by Rohos Logon Key.
#
# This program requires a virtual null modem cable such as com0com, with
# Rohos Logon Key listening to one end of the null modem cable, and this
# program outputting fake Easyident frames to the other end.
#
# Additionally, this program can kill a running Windows screensaver whenever a
# UID is read, so that the Windows logon screen is automatically brought up
# before passing the UID to Rohos Logon Key, thereby alleviating the need to
# press a key to activate the logon screen.



# Parameters
incomport="COM2"		# Input COM port for the generic serial reader
outcomport="COM4"		# Output COM port for the virtual null modem
				# Rohos Logon Key is connected to the
				# other end of
screensaver="ribbons.scr"	# Set to None to bypass killing the screensaver



# Modules
import os
import time
import serial



# Main program
incomport_fd=None
outcomport_fd=None

hexdigits = "0123456789ABCDEF"

while True:

  uid=None

  if not incomport_fd:
    try:
      incomport_fd = serial.Serial(incomport)
    except:
      print("Error opening input COM port {}".format(incomport))
      incomport_fd = None
      time.sleep(0.2)
      continue

  # Read a UID from the serial RFID reader
  try:
    uid=incomport_fd.readline().strip().decode("ascii")
  except:
    print("Read error")
    incomport_fd = None
    time.sleep(0.2)
    continue

  if not outcomport_fd:
    try:
      outcomport_fd = serial.Serial(outcomport)
    except:
      print("Error opening output COM port {}".format(outcomport))
      outcomport_fd = None
      time.sleep(0.2)
      continue

  uid = "".join([c for c in uid.upper() if c in hexdigits])

  print("UID read in:", uid)

  if len(uid) != 10:
    print("  Incompatible UID (not 10 digits) - giving up")
    continue

  # Kill the screensaver
  if screensaver:
    try:
      os.system("taskkill /f /t /im {}".format(screensaver))
    except:
      pass

  # Create a simulated Easyident data frame
  csum = sum([ord(c) - ord("0" if c.isdigit() else "7") for c in uid]) & 0xf
  easyident_frame = b"\x8a" + (uid + hexdigits[csum]).encode("ascii") + b"\r\n"

  # Pass on the fake Easyident frame to Rohos Logon Key
  try:
    outcomport_fd.write(easyident_frame)
    outcomport_fd.flush()
    print("  UID sent out")
  except:
    print("  Write error")
    incomport_fd = None
    time.sleep(0.2)
2 Likes