A bit of refactoring

This commit is contained in:
Viktor Dragomiretskyy 2019-08-26 23:05:42 +12:00
parent 859f163520
commit 269fd1955e
23 changed files with 304 additions and 701 deletions

View file

@ -1,110 +0,0 @@
from hashlib import sha256
from binascii import hexlify, unhexlify
from os.path import isfile
from threading import Thread
from struct import unpack, pack
from tls97 import tls
from usb97 import usb
from time import ctime
from sensor import write_hw_reg32, read_hw_reg32, identify_sensor
from flash import erase_flash, read_flash, get_fw_info, write_flash_all
from util import assert_status
from blobs import calibrate_prg
#usb.trace_enabled=True
#tls.trace_enabled=True
usb.open()
tls.parseTlsFlash(read_flash(1, 0, 0x1000))
tls.open()
# no idea what this is:
write_hw_reg32(0x8000205c, 7)
if read_hw_reg32(0x80002080) != 2:
raise Exception('Unexpected register value')
dev=identify_sensor()
print('Sensor: %s' % dev.name)
# ^ TODO -- what is the real reason to detect HW at this stage? -- likely it is required to construct calibrate_prg
fwi=get_fw_info(2)
if fwi == None:
raise Exception('No firmware detected')
print('FWExt version %d.%d (%s), %d modules' % (fwi.major, fwi.minor, ctime(fwi.buildtime), len(fwi.modules)))
def wait_82():
calib_data=usb.read_82()
print('len=%d' % len(calib_data))
with open('calib-data.bin', 'wb') as f:
f.write(calib_data)
if isfile('calib-data.bin'):
with open('calib-data.bin', 'rb') as f:
calib_data=f.read()
print('Calibration data loaded from a file.')
else:
# TODO Properly construct calibrate_prg.
# >>> 6f 000e 000000000000
# <<< 0000 880d 0000 07000000
# 0800 0000 9400 0e00 0300 0080 07000000 7e7f807f808080808080808080808080808080808080818081808180818080808080818081808080818081808180818081808180818081808180808081808180808081807f80808180808081808180818080808180818081808180818081808080818081808180818081808180818081808180818081808180818081808080808080808080807f807f807f807f7f7e7e
# a400 0000 0800 0e00 0200 0000 00000000 0d007100
# b400 0000 0800 0e00 0800 0080 db000000 00000000
# c400 0000 0400 0e00 0500 0080 1c6f0400
# d000 0000 9400 0e00 0700 0080 07000000 2b23203c2d182e1e30182e1c321d341d341e321c301e1e241e201f201d1c321a301e1c211e21341f1e202024201f1e20201f212221221d221e23341e1d1e1d20341f1d193b341c1d1e35201e201c20221f341c1e1e1c221f201d21201e1c1f34242221201f20221f201e241e241d2020221e2420231d221e211e1f1e1e341c321e3220301d2d302f2d2c2b23223a211c
# 6c01 0000 1400 0e00 0f00 0080 05550007 7701002805720000080100020811e107
# 8801 0000 0c00 0e00 1200 0080 07000000 7002 7800 7002 7800
#
# Empty reply:
# >>> 6f 000a 000000000000
# <<< 0000 880d 0000 00000000
#Thread(target=wait_82).start()
rsp=tls.cmd(calibrate_prg)
assert_status(rsp)
print(rsp.hex())
# ^ TODO check what the rest of the rsp means
calib_data=usb.read_82()
print('len=%d' % len(calib_data))
with open('calib-data.bin', 'wb') as f:
f.write(calib_data)
class Line():
def __init__(self, blob):
# what's with the rest of fields?
self.u0, self.u1, self.line, self.frame, self.u2, self.u3, self.u4, self.u5 = unpack('<BBBBBBBB', blob[:8])
self.data = blob[8:]
def serialize(self):
return pack('<BBBBBBBB', self.u0, self.u1, self.line, self.frame, self.u2, self.u3, self.u4, self.u5) + self.data
def __repr__(self):
return 'Line(line=%d, frame=%d)' % (self.line, self.frame)
lines=[calib_data[i:i+0x90+8] for i in range(0, len(calib_data), 0x90+8)] # TODO work out where "bytes per line" constant is comming from
lines=[Line(i) for i in lines]
frame4=[i.serialize() for i in lines if i.frame == 4] # why 4?
frame4=b''.join(frame4)
calib_data=pack('<H', len(frame4)) + frame4 + pack('<H', 0) # what's that 00000 in the end?
calib_data=pack('<H', len(calib_data)) + sha256(calib_data).digest() + b'\0'*0x20 + calib_data
calib_data=unhexlify('0250') + calib_data
def persist_calib_data():
start=read_flash(6, 0, 0x44)
if start != b'\xff' * 0x44:
if calib_data[:0x44] == start:
print('Calibration data already matches the data on flash.')
return
else:
print('Calibration flash already written. Erasing.')
erase_flash(6)
write_flash_all(6, 0, calib_data)
persist_calib_data()

View file

@ -6,8 +6,8 @@ from pydbus.generic import signal
import pkg_resources
from time import sleep
from prototype import *
from db97 import subtype_to_string
from sensor import cancel_capture
from proto97.db import subtype_to_string
from proto97.sensor import cancel_capture
import pwd
print("Starting up")

View file

@ -1,11 +1,6 @@
from usb97 import usb
from util import unhex, assert_status
from blobs import reset_blob
from sensor import reboot
from proto97.usb import usb
from proto97.sensor import factory_reset
#usb.trace_enabled=True
usb.open()
assert_status(usb.cmd(reset_blob))
assert_status(usb.cmd(b'\x10' + b'\0'*0x61))
reboot()
factory_reset()

View file

@ -1,49 +0,0 @@
from struct import pack, unpack
from db97 import db
from usb97 import usb
from tls97 import tls
from flash import read_flash
machine_guid='e7260876-58db-4d27-8c40-8d13110d6a71'
#usb.trace_enabled=True
#tls.trace_enabled=True
usb.open()
tls.parseTlsFlash(read_flash(1, 0, 0x1000))
tls.open()
stg=db.get_user_storage(name='StgWindsor')
if stg == None:
stgid=db.new_record(1, 4, 3, b'StgWindsor\0')
stg=db.get_user_storage(stgid)
if stg == None:
raise Exception('Failed to create StgWindsor')
def stg_data_records():
rc = db.get_record_children(stg.dbid).children
return [i['dbid'] for i in rc if i['type'] == 8] # 8 == "data" type
def machine_id_rec_value(b):
b = b.encode('utf-16le')
b = b + b'\0' * (0x94 - len(b))
return pack('<HH', 0x102, len(b)) + b # 0x102 = Machine ID/GUID?
rc = stg_data_records()
if rc == []:
db.new_record(stg.dbid, 0x8, stg.dbid, machine_id_rec_value(machine_guid))
rc = stg_data_records()
rc = db.get_record_value(rc[0]).value
if rc != machine_id_rec_value(machine_guid):
u0, l = unpack('<HH', rc[:4])
b=rc[4:4+l]
b=b.decode('utf-16le')
raise Exception('Machine GUID does not match the DB flash ownership record (%s).' % b)
print('That''s it, pairing''s finished')

36
pair.py Normal file
View file

@ -0,0 +1,36 @@
from time import sleep
from proto97.usb import usb
from proto97.tls import tls
from proto97.flash import read_flash
from proto97.init_flash import init_flash
from proto97.upload_fwext import upload_fwext
from proto97.calibrate import calibrate
from proto97.init_db import init_db
def restart():
sleep(2)
tls.reset()
usb.open()
usb.send_init()
tls.parseTlsFlash(read_flash(1, 0, 0x1000))
tls.open()
usb.open()
print('Initializing flash...')
init_flash()
restart()
print('Uploading firmware...')
upload_fwext()
restart()
print('Calibrating...')
calibrate()
print('Init database...')
init_db()
print('That\'s it, pairing\'s finished')

View file

@ -1,109 +0,0 @@
from prototype import *
from tls97 import hs_key
from hashlib import sha256
from fastecdsa.encoding.der import DEREncoder
from fastecdsa.curve import P256
from fastecdsa.ecdsa import verify
from fastecdsa.point import Point
from fastecdsa.keys import get_public_key
blob4f=unhex('''
4f0000000000000c000000100000100000000020000100f000010407000010000000100000000000009da
6bb3eb1d4d1641811b0a2402ba5010d6d0f2f7ffd8debc7e7e68c31473333020102000020000000e00300
00000000840acfea4e998b5302df52db54b7654b164a1057681149fcbb3ec7a559c80db20505030000000
40000800000000000005c70f7ccce2fc2d7620f69da63548fc392877c88393e9c45327a10c21d46267f06
0603000080040000800000000000005a5eeb284ee05ddae47ee27dc75fafaf09bc8b112a652bde4eab8af
c61f2df75040305000000050000000300000000000e828eaff826a0454f6a50f60a36d5014372aaaf2621
41e5ced9d0a0056907c20500bc011700000020000000ebfaf1f684cd4e4739e56588695ba19b515be4c16
f3e89bfe6b33f59b209a26b00000000000000000000000000000000000000000000000000000000000000
00000000003870b373e2a0c3b7b09c16cdb8bd5f04bfdfdd55583c67db236c5b522ad6b49000000000000
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000460000003044022036a452cef79a1
f146ca26dcf06eb58b333f95fab2d49eb89abbcb07c39dbe4c602200dc95d991f7f89e66ad8ee546619dc
4262617fb813e6e53f4333bbe21fe705d3000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000000300a40117000000000
1000001000000fcffffffffffffffffffffff00000000000000000000000001000000ffffffff00000000
00000000000000000000000000000000000000000000000000000000000000004b60d2273e3cce3bf6b05
3ccb0061d65bc86987655bdebb3e7933aaad835c65a000000000000000000000000000000000000000000
00000000000000000000000000000096c298d84539a1f4a033eb2d817d0377f240a463e5e6bcf847422ce
1f2d1176b000000000000000000000000000000000000000000000000000000000000000000000000f551
bf376840b6cbce5e316b5733ce2b169e0f7c4aebe78e9b7f1afee242e34f0000000000000000000000000
00000000000000000000000000000000000000000000000512563fcc2cab9f3849e17a7adfae6bcffffff
ffffffffff00000000ffffffff00000000000000000000000000000000000000000000000000000000000
0000000000000ffffffffffffffffffffffff00000000000000000000000001000000ffffffff00000000
0000000000000000000000000000000000000000000000000000000000000000
''')
#skip 4f 0000 0000
blob4f=blob4f[5:]
while len(blob4f) > 0:
hdr, blob4f = blob4f[:4], blob4f[4:]
id, l = unpack('<HH', hdr)
p, blob4f = blob4f[:l], blob4f[l:]
print('block %04x: %s' % (id, hexlify(p).decode()))
if id == 1:
while len(p) > 0:
d, align, hsh, p = p[:12], p[12:12+4], p[12+4:12+4+32], p[12+4+32:]
if align != b'\0' * 4:
raise Exception('align should be blank')
m=sha256()
m.update(d)
if m.digest() != hsh:
raise Exception('Hash mismatch')
print('block 1, blob: %s' % hexlify(d).decode())
elif id == 5:
hdr, p = p[:4+4], p[4+4:]
magic, keysz = unpack('<LL', hdr)
if magic != 0x17:
raise Exception('Unexpected magic')
if keysz != 0x20:
raise Exception('Unexpected key size')
x, p = p[:0x20], p[0x20:]
x=int(hexlify(x[::-1]).decode(), 16)
print('block 5, x=%x' % x)
nulls, p = p[:0x24], p[0x24:]
if nulls != b'\0' * len(nulls):
raise Exception('Nulls expected')
y, p = p[:0x20], p[0x20:]
y=int(hexlify(y[::-1]).decode(), 16)
print('block 5, y=%x' % y)
if not P256.is_point_on_curve( (x, y) ):
raise Exception('pub key point is not on curve')
nulls, p = p[:0x4c], p[0x4c:]
if nulls != b'\0' * len(nulls):
raise Exception('Nulls expected')
ssz, p = p[:4], p[4:]
ssz, = unpack('<L', ssz)
signature, nulls = p[:ssz], p[ssz:]
if nulls != b'\0' * len(nulls):
raise Exception('Nulls expected')
signature = DEREncoder().decode_signature(signature)
msg=(pack('<LL', 0x17, 0x20) +
unhexlify('%064x' % x)[::-1] +
(b'\0'*0x24) +
unhexlify('%064x' % y)[::-1] +
(b'\0'*0x4c))
pub=get_public_key(hs_key(), P256)
if not verify(signature, msg, pub):
raise Exception('Signature validation failed')

View file

@ -1,5 +1,5 @@
from util import unhex
from .util import unhex
reset_blob=unhex('''
06020000013920c0cdd8e7e68d6ef897ee686fbf657f83b9514341e1d3c0835a28c9cd1ccd0016644f74d

96
proto97/calibrate.py Normal file
View file

@ -0,0 +1,96 @@
from hashlib import sha256
from binascii import hexlify, unhexlify
from os.path import isfile
from threading import Thread
from struct import unpack, pack
from .tls import tls
from .usb import usb
from time import ctime
from .sensor import write_hw_reg32, read_hw_reg32, identify_sensor
from .flash import erase_flash, read_flash, get_fw_info, write_flash_all
from .util import assert_status
from .blobs import calibrate_prg
class Line():
def __init__(self, blob):
# what's with the rest of fields?
self.u0, self.u1, self.line, self.frame, self.u2, self.u3, self.u4, self.u5 = unpack('<BBBBBBBB', blob[:8])
self.data = blob[8:]
def serialize(self):
return pack('<BBBBBBBB', self.u0, self.u1, self.line, self.frame, self.u2, self.u3, self.u4, self.u5) + self.data
def __repr__(self):
return 'Line(line=%d, frame=%d)' % (self.line, self.frame)
def persist_calib_data(calib_data):
start=read_flash(6, 0, 0x44)
if start != b'\xff' * 0x44:
if calib_data[:0x44] == start:
print('Calibration data already matches the data on the flash.')
return
else:
print('Calibration flash already written. Erasing.')
erase_flash(6)
write_flash_all(6, 0, calib_data)
def calibrate():
# no idea what this is:
write_hw_reg32(0x8000205c, 7)
if read_hw_reg32(0x80002080) != 2:
raise Exception('Unexpected register value')
dev=identify_sensor()
print('Sensor: %s' % dev.name)
# ^ TODO -- what is the real reason to detect HW at this stage? -- likely it is required to construct calibrate_prg
fwi=get_fw_info(2)
if fwi == None:
raise Exception('No firmware detected')
print('FWExt version %d.%d (%s), %d modules' % (fwi.major, fwi.minor, ctime(fwi.buildtime), len(fwi.modules)))
if isfile('calib-data.bin'):
with open('calib-data.bin', 'rb') as f:
calib_data=f.read()
print('Calibration data loaded from the file.')
else:
# TODO Properly construct calibrate_prg.
# >>> 6f 000e 000000000000
# <<< 0000 880d 0000 07000000
# 0800 0000 9400 0e00 0300 0080 07000000 7e7f807f808080808080808080808080808080808080818081808180818080808080818081808080818081808180818081808180818081808180808081808180808081807f80808180808081808180818080808180818081808180818081808080818081808180818081808180818081808180818081808180818081808080808080808080807f807f807f807f7f7e7e
# a400 0000 0800 0e00 0200 0000 00000000 0d007100
# b400 0000 0800 0e00 0800 0080 db000000 00000000
# c400 0000 0400 0e00 0500 0080 1c6f0400
# d000 0000 9400 0e00 0700 0080 07000000 2b23203c2d182e1e30182e1c321d341d341e321c301e1e241e201f201d1c321a301e1c211e21341f1e202024201f1e20201f212221221d221e23341e1d1e1d20341f1d193b341c1d1e35201e201c20221f341c1e1e1c221f201d21201e1c1f34242221201f20221f201e241e241d2020221e2420231d221e211e1f1e1e341c321e3220301d2d302f2d2c2b23223a211c
# 6c01 0000 1400 0e00 0f00 0080 05550007 7701002805720000080100020811e107
# 8801 0000 0c00 0e00 1200 0080 07000000 7002 7800 7002 7800
#
# Empty reply:
# >>> 6f 000a 000000000000
# <<< 0000 880d 0000 00000000
rsp=tls.cmd(calibrate_prg)
assert_status(rsp)
print(rsp.hex())
# ^ TODO check what the rest of the rsp means
calib_data=usb.read_82()
print('len=%d' % len(calib_data))
with open('calib-data.bin', 'wb') as f:
f.write(calib_data)
lines=[calib_data[i:i+0x90+8] for i in range(0, len(calib_data), 0x90+8)] # TODO work out where "bytes per line" constant is comming from
lines=[Line(i) for i in lines]
frame4=[i.serialize() for i in lines if i.frame == 4] # why 4?
frame4=b''.join(frame4)
calib_data=pack('<H', len(frame4)) + frame4 + pack('<H', 0) # what's that 00000 in the end?
calib_data=pack('<H', len(calib_data)) + sha256(calib_data).digest() + b'\0'*0x20 + calib_data
calib_data=unhexlify('0250') + calib_data
persist_calib_data(calib_data)
# no need to reboot

View file

@ -1,12 +1,12 @@
from util import unhex
from tls97 import tls
from util import assert_status
from .util import unhex
from .tls import tls
from .util import assert_status
from struct import pack, unpack
from binascii import hexlify, unhexlify
from blobs import db_write_enable
from flash import flush_changes
from sid import *
from .blobs import db_write_enable
from .flash import flush_changes
from .sid import *
class UserStorage():
def __init__(self, dbid, name):
@ -132,6 +132,14 @@ class Db():
return parse_user_storage(tls.cmd(pack('<BHH', 0x4b, dbid, len(name)) + name))
def new_user_storate(self):
db.new_record(1, 4, 3, b'StgWindsor\0')
def get_storage_data(self):
stg = self.get_user_storage(name='StgWindsor')
rc = self.get_record_children(stg.dbid).children
return [i['dbid'] for i in rc if i['type'] == 8] # 8 == "data" type
def get_user(self, dbid):
return parse_user(tls.cmd(pack('<BHHH', 0x4a, dbid, 0, 0)))

View file

@ -1,10 +1,10 @@
from tls97 import tls
from .tls import tls
from struct import pack, unpack
from binascii import hexlify, unhexlify
from util import assert_status, unhex
from blobs import db_write_enable
from hw_tables import flash_ic_table_lookup
from .util import assert_status, unhex
from .blobs import db_write_enable
from .hw_tables import flash_ic_table_lookup
class FlashInfo():
def __init__(self, ic, blocks, unknown0, blocksize, unknown1, partitions):
@ -120,7 +120,7 @@ def write_flash_all(partition, ptr, buf):
def read_flash_all(partition, start, end):
bs = 0x1000
blocks = [db.read_flash(partition, addr, bs) for addr in range(start, end, bs)]
blocks = [read_flash(partition, addr, bs) for addr in range(start, end, bs)]
return b''.join(blocks)
def write_fw_signature(partition, signature):

35
proto97/init_db.py Normal file
View file

@ -0,0 +1,35 @@
from struct import pack, unpack
from .db import db
from .usb import usb
from .tls import tls
from .flash import read_flash
def machine_id_rec_value(b):
b = b.encode('utf-16le')
b = b + b'\0' * (0x94 - len(b))
return pack('<HH', 0x102, len(b)) + b # 0x102 = Machine ID/GUID?
def init_db(machine_guid='e7260876-58db-4d27-8c40-8d13110d6a71'):
stg=db.get_user_storage(name='StgWindsor')
if stg == None:
print('Creating new user storage object')
db.new_user_storate()
rc = db.get_storage_data()
if rc == []:
print('Creating host machine GUID record')
stg=db.get_user_storage(name='StgWindsor')
db.new_record(stg.dbid, 0x8, stg.dbid, machine_id_rec_value(machine_guid))
rc = db.get_storage_data()
rc = db.get_record_value(rc[0]).value
if rc != machine_id_rec_value(machine_guid):
u0, l = unpack('<HH', rc[:4])
b=rc[4:4+l]
b=b.decode('utf-16le')
raise Exception('Machine GUID does not match the DB flash ownership record (%s).' % b)

View file

@ -1,28 +1,26 @@
from tls97 import tls, psk_encryption_key, psk_validation_key, hs_key, crt_hardcoded
from .tls import tls, psk_encryption_key, psk_validation_key, hs_key, crt_hardcoded
from hashlib import sha256
import hmac
from struct import pack, unpack
from binascii import hexlify, unhexlify
from usb97 import usb
from flash import write_flash, erase_flash, flush_changes, PartitionInfo, get_flash_info
from sensor import reboot
from .usb import usb
from .flash import write_flash, erase_flash, flush_changes, PartitionInfo, get_flash_info
from .sensor import reboot
from Crypto.Random import get_random_bytes
from Crypto.Cipher import AES
from fastecdsa.encoding.der import DEREncoder
from fastecdsa.curve import P256
from fastecdsa.ecdsa import sign
from fastecdsa.point import Point
from fastecdsa.keys import gen_private_key, get_public_key
from util import assert_status, unhex
from blobs import reset_blob
from .util import assert_status, unhex
from .blobs import reset_blob
flash_layout_hardcoded=[
# id type access offset size
# lvl
PartitionInfo(1, 4, 7, 0x00001000, 0x00001000), # cert store
#PartitionInfo(2, 1, 2, 0x00002000, 0x0003e000), # xpfwext
PartitionInfo(2, 1, 7, 0x00002000, 0x0003e000), # xpfwext
PartitionInfo(2, 1, 2, 0x00002000, 0x0003e000), # xpfwext
PartitionInfo(5, 5, 3, 0x00040000, 0x00008000), # ???
PartitionInfo(6, 6, 3, 0x00048000, 0x00008000), # calibration data
PartitionInfo(4, 3, 5, 0x00050000, 0x00030000), # template database
@ -32,10 +30,7 @@ flash_layout_hardcoded=[
def with_hdr(id, buf):
return pack('<HH', id, len(buf)) + buf
client_private = gen_private_key(P256)
client_public = get_public_key(client_private, P256)
def encrypt_key():
def encrypt_key(client_private, client_public):
x = unhexlify('%064x' % client_public.x)[::-1]
y = unhexlify('%064x' % client_public.y)[::-1]
d = unhexlify('%064x' % client_private)[::-1]
@ -50,10 +45,7 @@ def encrypt_key():
sig = hmac.new(psk_validation_key, c, sha256).digest()
return b'\x02' + c + sig
tls.handle_priv(encrypt_key())
def make_cert():
def make_cert(client_public):
msg=(pack('<LL', 0x17, 0x20) +
unhexlify('%064x' % client_public.x)[::-1] +
(b'\0'*0x24) +
@ -63,7 +55,7 @@ def make_cert():
s=DEREncoder().encode_signature(s[0], s[1])
s=pack('<L', len(s)) + s
msg = msg + s
msg += b'\0'*(444 - len(msg)) # not sure this math is right
msg += b'\0'*(444 - len(msg)) # FIXME not sure this math is right
return msg
def serialize_flash_params(ic):
@ -74,7 +66,7 @@ def serialize_partition(p):
b = b + b'\0'*4 + sha256(b).digest()
return b
def partition_flash(layout):
def partition_flash(layout, client_public):
info = get_flash_info()
print('Detected Flash IC: %s, %d bytes' % (info.ic.name, info.ic.size))
@ -85,7 +77,7 @@ def partition_flash(layout):
cmd = unhex('4f 0000 0000')
cmd += with_hdr(0, serialize_flash_params(info.ic))
cmd += with_hdr(1, b''.join([serialize_partition(p) for p in layout]))
cmd += with_hdr(5, make_cert())
cmd += with_hdr(5, make_cert(client_public))
cmd += with_hdr(3, crt_hardcoded)
rsp = tls.cmd(cmd)
assert_status(rsp)
@ -97,42 +89,42 @@ def partition_flash(layout):
rsp = rsp[crt_len:]
# ^ TODO - figure out what the rest of rsp means
#usb.trace_enabled=True
#tls.trace_enabled=True
def init_flash():
assert_status(usb.cmd(reset_blob))
usb.open()
assert_status(usb.cmd(reset_blob))
client_private = gen_private_key(P256)
client_public = get_public_key(client_private, P256)
partition_flash(flash_layout_hardcoded)
partition_flash(flash_layout_hardcoded, client_public)
rsp=usb.cmd(unhex('01'))
assert_status(rsp)
# ^ get device info, contains firmware version which is needed to lookup pubkey for server cert validation
rsp=usb.cmd(unhex('01'))
assert_status(rsp)
# ^ get device info, contains firmware version which is needed to lookup pubkey for server cert validation
rsp=usb.cmd(unhex('50'))
# ^ TODO validate response
# It should be signed by the firmware private key.
# The corresponding pub key is hardcoded for each fw revision in the synaWudfBioUsb.dll.
# for my device it is: x=f727653b4e16ce0665a6894d7f3a30d7d0a0be310d1292a743671fdf69f6a8d3,
# y=a85538f8b6bec50d6eef8bd5f4d07a886243c58b2393948df761a84721a6ca94
assert_status(rsp)
rsp=rsp[2:]
l,=unpack('<L', rsp[:4])
tls.handle_ecdh(rsp[l-400:]) # not sure offsets are calculated this way
rsp=usb.cmd(unhex('50'))
# ^ TODO validate the response. It is very insecure to simply trust any device to do the auth.
# It should be signed by the firmware private key.
# The corresponding pub key is hardcoded for each fw revision in the synaWudfBioUsb.dll.
# for my device it is: x=f727653b4e16ce0665a6894d7f3a30d7d0a0be310d1292a743671fdf69f6a8d3,
# y=a85538f8b6bec50d6eef8bd5f4d07a886243c58b2393948df761a84721a6ca94
assert_status(rsp)
rsp=rsp[2:]
l,=unpack('<L', rsp[:4])
flush_changes()
flush_changes()
tls.handle_ecdh(rsp[l-400:]) # FIXME not sure offsets are calculated this way
tls.handle_priv(encrypt_key(client_private, client_public))
tls.open()
tls.open()
# Wipe newly created partitions clean
erase_flash(1)
erase_flash(2)
erase_flash(5)
erase_flash(6)
erase_flash(4)
# Wipe newly created partitions clean
erase_flash(1)
erase_flash(2)
erase_flash(5)
erase_flash(6)
erase_flash(4)
# Persist certs and keys on cert partition.
write_flash(1, 0, tls.makeTlsFlash())
# Persist certs and keys on cert partition.
write_flash(1, 0, tls.makeTlsFlash())
# Reboot
reboot()
# Reboot
reboot()

View file

@ -1,13 +1,13 @@
from tls97 import tls
from usb97 import usb
from db97 import db, subtype_to_string
from .tls import tls
from .usb import usb
from .db import db, subtype_to_string
from time import sleep
from struct import pack, unpack
from binascii import hexlify, unhexlify
from util import assert_status, unhex
from hw_tables import dev_info_lookup
from blobs import identify_prg, enroll_prg
from .util import assert_status, unhex
from .hw_tables import dev_info_lookup
from .blobs import identify_prg, enroll_prg, reset_blob
def glow_start_scan():
@ -279,6 +279,11 @@ def write_hw_reg32(addr, val):
def reboot():
assert_status(tls.cmd(unhex('050200')))
def factory_reset():
assert_status(usb.cmd(reset_blob))
assert_status(usb.cmd(b'\x10' + b'\0'*0x61))
reboot()
def identify_sensor():
rsp=tls.cmd(b'\x75')
assert_status(rsp)

View file

@ -3,7 +3,7 @@ import hmac
import sys
from hashlib import sha256, md5, sha1
from binascii import *
from usb97 import unhex, usb
from .usb import unhex, usb
from struct import pack, unpack
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
@ -12,7 +12,7 @@ from fastecdsa.point import Point
from fastecdsa.keys import gen_private_key, get_public_key
from fastecdsa.ecdsa import sign
from fastecdsa.encoding.der import DEREncoder
from util import assert_status
from .util import assert_status
import pickle
@ -91,6 +91,9 @@ class Tls():
def __init__(self, usb):
self.usb = usb
self.reset()
def reset(self):
self.trace_enabled = False
self.secure_rx = False
self.secure_tx = False

46
proto97/upload_fwext.py Normal file
View file

@ -0,0 +1,46 @@
from binascii import hexlify, unhexlify
from time import ctime
from .tls import tls
from .usb import usb
from .sensor import reboot, write_hw_reg32, read_hw_reg32, identify_sensor
from .flash import flush_changes, read_flash, erase_flash, write_flash_all, write_fw_signature, get_fw_info
from .util import assert_status
def upload_fwext():
# no idea what this is:
write_hw_reg32(0x8000205c, 7)
if read_hw_reg32(0x80002080) != 2:
raise Exception('Unexpected register value')
dev=identify_sensor()
print('Sensor: %s' % dev.name)
# ^ TODO -- what is the real reason to detect HW at this stage?
# just a guess: perhaps it is used to construct fwext filename
with open('6_07f_lenovo_mis.xpfwext', 'rb') as f:
fwext=f.read()
fwext=fwext[fwext.index(b'\x1a')+1:]
fwext, signature = fwext[:-0x100], fwext[-0x100:]
# get fwext header info
rsp=tls.cmd(unhexlify('4302')) # should fail 'cause no fwext uploaded yet
fwi=get_fw_info(2)
if fwi != None:
raise Exception('FW is already present (version %d.%d (%s))' % (fwi.major, fwi.minor, ctime(fwi.buildtime)))
#flush_changes()
write_flash_all(2, 0, fwext)
write_fw_signature(2, signature)
fwi=get_fw_info(2)
if fwi == None:
raise Exception('No firmware detected')
print('Loaded FWExt version %d.%d (%s), %d modules' % (fwi.major, fwi.minor, ctime(fwi.buildtime), len(fwi.modules)))
# Reboot
reboot()

View file

@ -1,7 +1,7 @@
import usb.core as ucore
from binascii import *
from util import assert_status, unhex
from .util import assert_status, unhex
from struct import unpack
from usb.util import claim_interface, release_interface
from queue import Queue

View file

@ -1,14 +1,10 @@
from util import assert_status
from time import sleep
from struct import pack, unpack
from binascii import hexlify, unhexlify
from tls97 import tls
from usb97 import usb, unhex
from sensor import enroll, identify
from db97 import db
from flash import read_flash
from sid import *
from proto97.tls import tls
from proto97.usb import usb
from proto97.db import db
from proto97.flash import read_flash
from proto97.sensor import *
from proto97.sid import *
def open97():
usb.open()

View file

@ -1,9 +0,0 @@
from usb97 import usb
from sensor import reboot
#usb.trace_enabled=True
#tls.trace_enabled=True
usb.open()
reboot()

View file

@ -1,280 +0,0 @@
#!/usr/bin/env python3
import re
from struct import unpack
from binascii import unhexlify, hexlify
def unhex(x):
return unhexlify(re.sub('\W', '', x))
enroll_prg=unhex('''
02980000002300000020000800002000800000010032007000000000802020050024200000502077362820010030200100082170000c210
000482102004c210000582000005c20000060200000682005006c20012970200121742001887820018084202000942001809c200902a020
0b19b4200000b8203b04bc201400c0200200c4200100c82002003300100000000080cc200000f503d0200000a1013200440000000080dc2
0e803e0206401e420d002e8200001f0200500f8200500fc200000b8203b0000080400140800000808000008080000140830000808000014
0831001c081a0032000c0000000080501101004c1126003400080310071d10071d10071d10071d10071c01065810080101000007c8078c0
6100000204f80007f000003070107010c07032c08fc80095a800afc08fb800b5a095b800afb08fa800b5b095c800afa08f9800b5c095d80
0af908f8800b5d095e800af808f7800b5e095f800af708f6800b5f0960800af608f5800b600961800af508f4800b610962800af408f3800
b620963800af308f2800b630964800af208f1800b640965800af108f0800b650966800af008ef800b660967800aef08ee800b670968800a
ee08ed800b68096c800aed08ec800b6c096d800aec08eb800b6d096e800aeb08ea800b6e096f800aea08e9800b6f0970800ae908e8800b7
00971800ae808e7800b710972800ae708e6800b720973800ae608e5800b730974800ae508e4800b740975800ae408e3800b750976800ae3
08e2800b760977800ae208e1800b770978800ae108e0800b780979800ae008df800b79097a800adf08de800b7a097b800ade08dd800b7b0
97c800add08dc800b7c097d800adc08db800b7d097e800adb08da800b7e097f800ada08d9800b7f0980800ad908d8800b800981800ad808
d7800b810982800ad708d6800b820983800ad608d5800b830984800ad508d4800b840985800ad408d3800b850986800ad308d2800b86098
7800ad208d1800b870988800ad108d0800b880989800ad008cf800b89098a800acf08ce800b8a098b800ace08cd800b8b098c800acd08cc
800b8c098d800acc08cb800b8d098e800acb08ca800b8e098f800aca08c9800b8f0990800ac908c8800b900991800ac808c7800b9109928
00ac708c6800b920993800ac608c5800b930994800ac508c4800b940995800ac408c3800b950996800ac308c2800b960997800ac208c180
0b970998800ac108c0800b980999800ac008bf800b99099a800abf08be800b9a099b800abe08bd800b9b099c800abd08bc800b9c099d800
abc08bb800b9d099e800abb08ba800b9e099f800aba08b9800b9f09a0800ab908b8800ba00801800ab808b7800a010802800ab708b6800a
020803800ab608b5800a030804802003070404020000000000002f000400900000002900040000000000350004001000000017000000260
02800fbb20f00f2220f00300000006001020040010a00018000000a0200000b19000050c360ea010910002e001c00020018002300000090
0090004d01000090017c013c323232640a02013000cc0103000000ff0000001d000003ff00000025000003ff00000022000003101112131
415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b
4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f8081828
38485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f2b23203c2d182e1e30182e1c321d341d341e321c301e1e241e201f
201d1c321a301e1c211e21341f1e202024201f1e20201f212221221d221e23341e1d1e1d20341f1d193b341c1d1e35201e201c20221f341
c1e1e1c221f201d21201e1c1f34242221201f20221f201e241e241d2020221e2420231d221e211e1f1e1e341c321e3220301d2d302f2d2c
2b23223a211c7e7f807f8080808080808080808080808080808080808180818081808180808080808180818080808180818081808180818
08180818081808180808081808180808081807f808081808080818081808180808081808180818081808180818080808180818081808180
81808180818081808180818081808180818081808080808080808080807f807f807f807f7f7e7e
''')
short=unhex('''
029000010023000000320074000000008000200000502077322820020030200000082110000c211000482105004c21050020200000242
00000582000005c20000060204300682014006c2001247020012c842020008c20900190202c01942001809c200902a0200b19b4200000
b8203a00bc201400c0200200c4200200c82008003300100000000080cc200000a101d0200000a10132004c0000000080dc20e803e0206
401e420d002e8200001ec201400f0200500fc200000b8203a00140800000008040008080000080802001408300008080300140831001c
081a004c112400501100002a00080020010100100100002c002800802080200000000000013f4000000000080f080f00000000279c100
0279c10000000000000000000340040000300000007160000240a59085a0701c9500aaa07010ada08db0701c9460b2107010800800a00
88c9590a5a07010aa908aa0701c91f0ac900000c04010000000029000400000000003500040000000000150008000000000020280000
''')
calibrate=unhex('''
02980061032300000020000800002000800000010032007000000000802020050024200000502077362820010030200100082170000c2
10000482102004c210000582000005c20000060200000682005006c20012970200121742001887820018084202000942001809c200902
a0200b19b4200000b8203b04bc201400c0200200c4200100c82002003300100000000080cc200000f503d0200000a1013200440000000
080dc20e803e0206401e420d002e8200001f0200500f8200500fc200000b8203b00000804001408000008080000080800001408300008
080000140831001c081a0032000c0000000080501101004c1126003400080310071d10071d10071d10071d10071c01065810080101000
007c8078c06100000204f80007f000003070107010c07032c08fc80095a800afc08fb800b5a095b800afb08fa800b5b095c800afa08f9
800b5c095d800af908f8800b5d095e800af808f7800b5e095f800af708f6800b5f0960800af608f5800b600961800af508f4800b61096
2800af408f3800b620963800af308f2800b630964800af208f1800b640965800af108f0800b650966800af008ef800b660967800aef08
ee800b670968800aee08ed800b68096c800aed08ec800b6c096d800aec08eb800b6d096e800aeb08ea800b6e096f800aea08e9800b6f0
970800ae908e8800b700971800ae808e7800b710972800ae708e6800b720973800ae608e5800b730974800ae508e4800b740975800ae4
08e3800b750976800ae308e2800b760977800ae208e1800b770978800ae108e0800b780979800ae008df800b79097a800adf08de800b7
a097b800ade08dd800b7b097c800add08dc800b7c097d800adc08db800b7d097e800adb08da800b7e097f800ada08d9800b7f0980800a
d908d8800b800981800ad808d7800b810982800ad708d6800b820983800ad608d5800b830984800ad508d4800b840985800ad408d3800
b850986800ad308d2800b860987800ad208d1800b870988800ad108d0800b880989800ad008cf800b89098a800acf08ce800b8a098b80
0ace08cd800b8b098c800acd08cc800b8c098d800acc08cb800b8d098e800acb08ca800b8e098f800aca08c9800b8f0990800ac908c88
00b900991800ac808c7800b910992800ac708c6800b920993800ac608c5800b930994800ac508c4800b940995800ac408c3800b950996
800ac308c2800b960997800ac208c1800b970998800ac108c0800b980999800ac008bf800b99099a800abf08be800b9a099b800abe08b
d800b9b099c800abd08bc800b9c099d800abc08bb800b9d099e800abb08ba800b9e099f800aba08b9800b9f09a0800ab908b8800ba008
01800ab808b7800a010802800ab708b6800a020803800ab608b5800a030804802003070404020000000000002f0004009000000029000
400000000003500040010000000170000003000cc0103000000ff0000001d000003ff00000025000003ff000000220000031011121314
15161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4
b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f8081
82838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f2b23203c2d182e1e30182e1c321d341d341e321c301e1e241
e201f201d1c321a301e1c211e21341f1e202024201f1e20201f212221221d221e23341e1d1e1d20341f1d193b341c1d1e35201e201c20
221f341c1e1e1c221f201d21201e1c1f34242221201f20221f201e241e241d2020221e2420231d221e211e1f1e1e341c321e3220301d2
d302f2d2c2b23223a211c7e7f807f80808080808080808080808080808080808081808180818081808080808081808180808081808180
8180818081808180818081808180808081808180808081807f80808180808081808180818080808180818081808180818081808080818
081808180818081808180818081808180818081808180818081808080808080808080807f807f807f807f7f7e7e
''')
codes={}
codes[0x0] = "No Operation"
codes[0x1] = "Swipe"
codes[0x2] = "Timeslot Configuration"
codes[0x3] = "Register"
codes[0x4] = "Register Set 32"
codes[0x5] = "Register Operation 32"
codes[0x6] = "Security"
codes[0x7] = "WOE"
codes[0x8] = "Motion 1"
codes[0xa] = "CPUCLK"
codes[0xb] = "Motion 2"
codes[0xc] = "Calibration Block"
codes[0xd] = "Sweep"
codes[0xe] = "Zone Configuration"
codes[0xf] = "Zones Per Sweep"
codes[0x10] = "Lines Per Sweep Iteration"
codes[0x11] = "Lines Per Sweep"
codes[0x12] = "Total Zones"
codes[0x13] = "CAL WOE Ctrl"
codes[0x14] = "Cal WOE Mask"
codes[0x15] = "BW Reduciton"
codes[0x16] = "AGC"
codes[0x17] = "Reply Configuration"
codes[0x18] = "Motion 3"
codes[0x19] = "WOVAR"
codes[0x1a] = "Block MOde"
codes[0x1b] = "Bit Reduction"
codes[0x1c] = "Motion 4"
codes[0x1d] = "Calibration WOENF"
codes[0x1e] = "Calibration"
codes[0x1f] = "Zone Configuration A"
codes[0x20] = "Set Register 32"
codes[0x21] = "Register Operation 32A"
codes[0x22] = "Fingerprint Buffering"
codes[0x23] = "Reply Config + Timeslot Table"
codes[0x24] = "Baseline"
codes[0x25] = "SO Alternate"
codes[0x26] = "Finger Detect"
codes[0x27] = "Finger Detect Sample Register"
codes[0x28] = "Finger Detect Scan Registers"
codes[0x29] = "Timeslot Table Offset"
codes[0x2a] = "ACM Config"
codes[0x2b] = "ACM Control"
codes[0x2c] = "CEM Config"
codes[0x2d] = "CEM Control"
codes[0x2e] = "Image Reconstruction"
codes[0x2f] = "2D"
codes[0x30] = "Line Update"
codes[0x31] = "FDetect Timeslot Table"
codes[0x32] = "Register List 16"
codes[0x33] = "Register list 32"
codes[0x34] = "Timeslot Table 2D"
codes[0x35] = "Timeslot Table Offset for Finger Detect"
codes[0x36] = "Security Aligned"
codes[0x37] = "WOF2"
codes[0x38] = "WOE WOF"
codes[0x39] = "Navigation"
codes[0x3a] = "WOE WOF2 Version2"
codes[0x3b] = "Cal WOE WOF2"
codes[0x3c] = "Event Signal"
codes[0x3d] = "IFS Frame Stats"
codes[0x3e] = "SNR Method 4"
codes[0x3f] = "WOE WOF2 Version 3"
codes[0x40] = "Calibrate WOE WOF2 Version 3"
codes[0x41] = "Finger Detect Ratchet"
codes[0x42] = "Data Encoder"
codes[0x43] = "Line Update Transform"
codes[0x44] = "Line Update InterLeave"
codes[0x45] = "SO Table Values for Macros"
codes[0x46] = "Timeslot Macro Definitions"
codes[0x47] = "Enable ASP Feature"
codes[0x48] = "Baseline Frame"
codes[0x49] = "Rx Select"
codes[0xffff] = "Unknown"
def decode_tt_inst(b):
if b[0] == 0:
return (0, 1, 'NOOP')
elif b[0] == 1:
return (1, 1, 'End of Table')
elif b[0] == 2:
return (2, 1, 'Return')
elif b[0] == 3:
return (3, 1, 'Clear SO')
elif b[0] == 4:
return (4, 1, 'End of Data')
elif b[0] == 6:
return (6, 2, 'Enable Rx 0x%02x' % b[1])
elif b[0] == 7:
return (7, 2, 'Idle Rx 0x%03x' % (0x100 if b[1] == 0 else b[1]))
elif b[0] & 0xfe == 8:
return (8, 2, 'Enable SO 0x%03x' % ((b[0] & 1) << 8 | b[1]) )
elif b[0] & 0xfe == 0xa:
return (9, 2, 'Disable SO 0x%03x' % ((b[0] & 1) << 8 | b[1]) )
elif b[0] & 0xfc == 0xc:
return (0xa, 1, 'Interrupt %x' % (b[0] & 3))
elif b[0] & 0xf8 == 0x10:
return (0xb, 3, 'Call rx_inc=%d, addr=%x, repeat=%d' % (b[0] & 7, b[1] << 2, 0x100 if b[2] == 0 else b[2]))
elif b[0] & 0xe0 == 0x20:
return (0xc, 1, 'Features %02x' % (b[0] & 0x1f)) # TODO check how features are converted to op args
elif b[0] & 0xc0 == 0x40:
reg=b[0] & 0x3f
reg=reg*4 + 0x80002000
val=b[1] | (b[2] << 8)
return (0xd, 3, 'Register Write *0x%08x = 0x%04x' % (reg, val))
elif b[0] & 0xc0 == 0x80:
return (0xe, 1, 'Sample %x, %x' % ((b[0] & 0x38) >> 3, b[0] & 7 ))
elif b[0] & 0xc0 == 0xc0:
return (0xf, 2, 'Sample Repeat %x, %x, repeat=%x' % ( (b[0] & 0x38) >> 3, b[0] & 7, 0x100 if b[1] == 0 else b[1] ))
elif b[0] == 5:
return (5, 2, 'Marco %02x' % b[1])
else:
raise Exception('Unhandled instruction %02x' % b[0])
def decode_tt(b):
pc=0
while len(b) > 0:
(op, sz, txt) = decode_tt_inst(b)
if sz > len(b):
raise Exception('Truncated instruction')
print('%04x: %-6s %s' % (pc, hexlify(b[:sz]).decode(), txt))
b = b[sz:]
pc += sz
#b=enroll_prg[5:]
#b=short[5:]
def dump_prg(b):
while len(b) > 0:
(typ, sz), b = unpack('<HH', b[:4]), b[4:]
p, b = b[:sz], b[sz:]
if typ == 0x20:
addr, val = unpack('<LL', p)
print('Set Register 32:')
print(' *0x%08x = 0x%08x' % (addr, val))
elif typ == 0x32:
print('Set Registers 16:')
(base,), p = unpack('<L', p[:4]), p[4:]
while len(p) > 0:
(off, val), p = unpack('<HH', p[:4]), p[4:]
print(' *0x%08x = 0x%04x' % (off + base, val))
elif typ == 0x33:
print('Set Registers 32:')
(base,), p = unpack('<L', p[:4]), p[4:]
while len(p) > 0:
(off, val), p = unpack('<HL', p[:6]), p[6:]
print(' *0x%08x = 0x%08x' % (off + base, val))
elif typ == 0x34:
print('Timeslot Table 2D:')
decode_tt(p)
#print('%04x (%20s): %s' % (typ, codes[typ], hexlify(p)))
else:
print('%04x (%20s): (0x%x bytes) %s' % (typ, codes[typ], len(p), hexlify(p).decode()))
# rdata.bin is a .rdata segment of synaWudfBioUsb.dll
with open('rdata.bin', 'rb') as f:
rdata=f.read()
rdata_vma=0x18012d000
#0x18012D7B8
def block(addr, size):
addr -= rdata_vma
return rdata[addr:addr+size]
def read16(addr):
return unpack('<H', block(addr, 2))[0]
def read64(addr):
return unpack('<Q', block(addr, 8))[0]
def dump_table(base):
while True:
sz=read16(base)
addr=read64(base+2)
if sz == 0 or addr == 0:
break
print('*** Prog at %016x, %d bytes' % (addr, sz))
dump_prg(block(addr, sz))
base+=2+8
def dump_all_tables(ptr):
while True:
addr = read64(ptr)
if addr == 0:
break
dump_table(read64(addr+0x34))
ptr += 8
#dump_all_tables(0x1801BF0A0)
#dump_prg(calibrate[5:])
dump_prg(enroll_prg[5:])

View file

@ -1,52 +0,0 @@
from binascii import hexlify, unhexlify
from time import ctime
from tls97 import tls
from usb97 import usb
from sensor import reboot, write_hw_reg32, read_hw_reg32, identify_sensor
from flash import flush_changes, read_flash, erase_flash, write_flash_all, write_fw_signature, get_fw_info
from util import assert_status
with open('6_07f_lenovo_mis.xpfwext', 'rb') as f:
fwext=f.read()
fwext=fwext[fwext.index(b'\x1a')+1:]
fwext, signature = fwext[:-0x100], fwext[-0x100:]
#usb.trace_enabled=True
#tls.trace_enabled=True
usb.open()
tls.parseTlsFlash(read_flash(1, 0, 0x1000))
tls.open()
# no idea what this is:
write_hw_reg32(0x8000205c, 7)
if read_hw_reg32(0x80002080) != 2:
raise Exception('Unexpected register value')
dev=identify_sensor()
print('Sensor: %s' % dev.name)
# ^ TODO -- what is the real reason to detect HW at this stage?
# get fwext header info
rsp=tls.cmd(unhexlify('4302')) # should fail 'cause no fwext uploaded yet
fwi=get_fw_info(2)
if fwi != None:
raise Exception('FW is already present (version %d.%d (%s))' % (fwi.major, fwi.minor, ctime(fwi.buildtime)))
#flush_changes()
write_flash_all(2, 0, fwext)
write_fw_signature(2, signature)
fwi=get_fw_info(2)
if fwi == None:
raise Exception('No firmware detected')
print('Loaded FWExt version %d.%d (%s), %d modules' % (fwi.major, fwi.minor, ctime(fwi.buildtime), len(fwi.modules)))
# Reboot
reboot()