/ emu-encrypt.py
emu-encrypt.py
  1  #!/usr/bin/env python3
  2  
  3  import sys, itertools
  4  from mcu import Operation, Microcontroller
  5  
  6  getc = [ord(c) for c in reversed("5\n23\n42\n13\nY\n")]
  7  
  8  def putc(c):
  9      c = int(mcu.mem.a)
 10      if c & 0x80: # unescaping in case we come from putc_raw
 11          c &= 0x3f
 12          c = (0x40 | c ) if c<27 else c
 13      #print(f"putc({repr(chr(c))})")
 14      print(chr(c), end="")
 15  
 16  mcu = Microcontroller()
 17  def run(mcu, end, trace):
 18      isns = 0
 19      while mcu.pc not in end:
 20          isns+=1
 21          op = Operation(mcu.rom[int(mcu.pc)])
 22          op.args = mcu.rom[int(mcu.pc + 1):int(mcu.pc + len(op))]
 23  
 24          if trace:
 25              print(f"{isns:5} {int(mcu.pc):04x}   {str(op):<30} | "
 26                    f"{int(mcu.mem.a):02x} "
 27                    f"{int(mcu.mem.r0):02x} "
 28                    f"{int(mcu.mem.r1):02x} "
 29                    f"{int(mcu.mem.r2):02x} "
 30                    f"{int(mcu.mem.dptr):04x} "
 31                    f"{int(mcu.mem[0xd0]) & 0xfe:02x} "
 32                    f"{int(mcu.mem[0xd1]):02x} ", end='')
 33              for i in range(23):
 34                  if i>0 and i % 4==0: print(" ", end='')
 35                  print(f"{mcu.mem._data[0x25+i]:02x}", end='')
 36              print()
 37  
 38          if op.opcode == 0x12: # lcall
 39              if op.args == [0x03,0x7a]: # putc
 40                  putc(mcu.mem.a)
 41                  mcu.pc += len(op)
 42                  continue
 43              elif op.args == [0x05,0x65]: # getc
 44                  if trace:
 45                      print("getc()")
 46                  mcu.pc += len(op)
 47                  mcu.mem.a = getc.pop()
 48                  continue
 49          if op.opcode == 0x2: # jmp
 50              if op.args == [0x03,0x7a]: # putc
 51                  putc(mcu.mem.a)
 52                  mcu._exec_34() # ret
 53                  continue
 54              elif op.args == [0x05,0x65]: # getc
 55                  if trace:
 56                      print("getc()")
 57                  mcu.mem.a = getc.pop()
 58                  mcu._exec_34() # ret
 59                  continue
 60  
 61          # catch putc if not jmp/call
 62          if int(mcu.pc)==0x37a:
 63              putc(mcu.mem.a)
 64              mcu._exec_34() # ret
 65              continue
 66  
 67          mcu.next_cycle()
 68      if trace:
 69          print()
 70          print("end, total insn", isns)
 71      return isns
 72  
 73  def usage(ret):
 74    print("sbt encrypt")
 75    print("\t-k=<15chars>\t\t - key, only A-Z0-9,.-")
 76    print("\t-n=<5chars>\t\t - previous nonce, only letters")
 77    print("\t-p=<text>\t\t - plaintext, only A-Z0-9,.-")
 78    exit(ret)
 79  
 80  def is_valid_str(s):
 81    valid = set(c if isinstance(c,str) else chr(c) for c in itertools.chain(range(ord('A'), ord('Z')+1), range(ord('0'), ord('9')+1), [',', '.', '-', ' ']))
 82    return (set(s.upper()) - valid) == set()
 83  
 84  prev_nonce="ABCDE"
 85  plaintext="HELLO WORLD"
 86  key="AAAAAAAAAAAAAAA"
 87  
 88  for arg in sys.argv[1:]:
 89    if arg == '-h':
 90        usage(0)
 91    if arg.startswith("-k="):
 92      if not is_valid_str(arg[3:]) or len(arg[3:])!=15: usage(1)
 93      key=arg[3:].upper()
 94      continue
 95    if arg.startswith("-n="):
 96      if not arg[3:].isalpha() or len(arg[3:])!=5: usage(1)
 97      prev_nonce=arg[3:].upper()
 98      continue
 99    if arg.startswith("-p="):
100      if not is_valid_str(arg[3:]): usage(1)
101      plaintext=arg[3:].upper()
102      continue
103  
104  # reset
105  mcu.reset_ram()
106  
107  # load rom image
108  with open('SBT.rom','rb') as fd:
109      code = fd.read()
110  for i, b in enumerate(code):
111     mcu.rom[i] = b
112  
113  # init key
114  for i, c in enumerate(key): mcu.rom[0x72d7+i] = ord(c) & 0x3f
115  mcu.pc = 0x1aad
116  steps = run(mcu, [0x1ac9], False)
117  print()
118  
119  mcu.pc = 0x1ba8 # we skip the query to print the output at 1aed
120  for i,c in enumerate(plaintext + "\xfe"):
121     mcu.xmem[0x3000+i]=(ord(c) & 0x3f) if c!='\xfe' else 0xfe
122  mcu.mem[0x42]=0x30
123  mcu.mem[0x43]=0x00
124  mcu.mem.r5=1
125  
126  mcu.mem[0x4a]=0x00
127  mcu.mem[0x4b]=0x00
128  mcu.mem[0x4c]=0x00
129  
130  # previous nonce
131  for i,c in enumerate(prev_nonce):
132     mcu.xmem[0x72d2+i]=ord(c) & 0x3f
133  
134  steps = run(mcu, [0x1c25], False)
135  print()