A few things.

- automatically locate supported usb device
- prototype will call reboot() on exit
- enroll() & identify() are now async
- dbus service rewriten to use standard dbus.service
- use own simplified DBus interface for backend comms
This commit is contained in:
Viktor Dragomiretskyy 2020-07-07 02:22:43 +12:00
parent e158e9e969
commit 7f14da61a8
8 changed files with 517 additions and 359 deletions

View file

@ -1,69 +1,77 @@
from threading import Thread
from gi.repository import GLib
from pydbus import SystemBus
from pydbus.generic import signal
import pkg_resources
from time import sleep
from prototype import *
from proto9x.db import subtype_to_string
from proto9x.sensor import cancel_capture
import dbus
import dbus.mainloop.glib
import dbus.service
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
from gi.repository import GObject, GLib
import logging
from proto9x import init
from proto9x.tls import tls
from proto9x.usb import usb
from proto9x.sid import sid_from_string
from proto9x.db import subtype_to_string, db
from proto9x.sensor import sensor, reboot
import pwd
import atexit
import signal
print("Starting up")
GLib.threads_init()
loop = GLib.MainLoop()
INTERFACE_NAME='io.github.uunicorn.Fprint.Device'
logging.basicConfig(level=logging.DEBUG)
bus = dbus.SystemBus()
bus_name = dbus.service.BusName('io.github.uunicorn.Fprint', bus)
loop = GObject.MainLoop()
usb.quit = lambda e: loop.quit()
bus = SystemBus()
def uid2identity(uid):
sidstr='S-1-5-21-111111111-1111111111-1111111111-%d' % uid
return sid_from_string(sidstr)
class AlreadyInUse(Exception):
__name__ = 'net.reactivated.Fprint.Error.AlreadyInUse'
class Device():
name = 'Validity sensor'
class Device(dbus.service.Object):
def __init__(self):
setattr(self, 'num-enroll-stages', 7)
setattr(self, 'scan-type', 'press')
self.capturing = False
dbus.service.Object.__init__(self, bus_name, '/io/github/uunicorn/Fprint/Device')
def Claim(self, usr):
print('In Claim %s' % usr)
def Release(self):
print('In Release')
self.caimed = False
def ListEnrolledFingers(self, usr, dbus_context):
@dbus.service.method(dbus_interface=INTERFACE_NAME,
in_signature="s",
out_signature="as")
def ListEnrolledFingers(self, usr):
try:
print('In ListEnrolledFingers %s' % usr)
if len(usr) > 0:
pw=pwd.getpwnam(usr)
uid=pw.pw_uid
else:
sender=dbus_context.sender
uid=bus.dbus.GetConnectionUnixUser(dbus_context.sender)
logging.debug('In ListEnrolledFingers %s' % usr)
pw=pwd.getpwnam(usr)
uid=pw.pw_uid
usr=db.lookup_user(uid2identity(uid))
if usr == None:
print('User not found on this device')
return []
rc = [subtype_to_string(f['subtype']) for f in usr.fingers]
print(repr(rc))
return rc
except Exception as e:
loop.quit()
raise e
@dbus.service.method(dbus_interface=INTERFACE_NAME,
in_signature='s',
out_signature='')
def DeleteEnrolledFingers(self, user):
logging.debug('In DeleteEnrolledFingers %s' % user)
pw=pwd.getpwnam(user)
usr=db.lookup_user(uid2identity(pw.pw_uid))
if usr == None:
return
db.del_record(usr.dbid)
def do_scan(self):
if self.capturing:
return
@ -71,34 +79,37 @@ class Device():
try:
self.capturing = True
z=identify()
self.VerifyStatus('verify-match', True)
except Exception as e:
self.VerifyStatus('verify-no-match', True)
#loop.quit();
raise e
finally:
self.capturing = False
def VerifyStart(self, finger_name):
print('In VerifyStart %s' % finger_name)
Thread(target=lambda: self.do_scan()).start()
#self.do_scan()
@dbus.service.method(dbus_interface=INTERFACE_NAME,
in_signature='ss',
out_signature='')
def VerifyStart(self, user, finger):
logging.debug('In VerifyStart %s' % finger)
def VerifyStop(self):
print('In VerifyStop')
if self.capturing:
cancel_capture()
def complete_cb(rsp, e):
if e is not None:
self.VerifyStatus('verify-no-match', True)
else:
self.VerifyStatus('verify-match', True)
usrid, subtype, hsh = rsp
# TODO pass down the user DB id
# check that a correct finger was identified
def DeleteEnrolledFingers(self, user):
print('In DeleteEnrolledFingers %s' % user)
pw=pwd.getpwnam(user)
usr=db.lookup_user(uid2identity(pw.pw_uid))
def update_cb(e):
self.VerifyStatus('verify-retry-scan', False)
if usr == None:
print('User not found on this device')
return
sensor.identify(update_cb, complete_cb)
db.del_record(usr.dbid)
@dbus.service.method(dbus_interface=INTERFACE_NAME,
in_signature='',
out_signature='')
def Cancel(self):
sensor.cancel()
def do_enroll(self, finger_name, uid):
# it is pointless to try and remember username passed in claim as Gnome does not seem to be passing anything useful anyway
@ -112,56 +123,46 @@ class Device():
#loop.quit();
raise e
def EnrollStart(self, finger_name, dbus_context):
sender=dbus_context.sender
uid=bus.dbus.GetConnectionUnixUser(dbus_context.sender)
print('In EnrollStart %s for %d' % (finger_name, uid))
Thread(target=lambda: self.do_enroll(finger_name, uid)).start()
@dbus.service.method(dbus_interface=INTERFACE_NAME,
in_signature='ss',
out_signature='')
def EnrollStart(self, user, finger_name):
logging.debug('In EnrollStart %s for %s' % (finger_name, user))
pw=pwd.getpwnam(user)
uid=pw.pw_uid
def update_cb(rsp, e):
if e is not None:
self.EnrollStatus('enroll-retry-scan', False)
else:
self.EnrollStatus('enroll-stage-passed', False)
def EnrollStop(self):
print('In EnrollStop')
def complete_cb(rsp, e):
if e is not None:
self.EnrollStatus('enroll-failed', True)
else:
self.EnrollStatus('enroll-completed', True)
VerifyFingerSelected = signal()
VerifyStatus = signal()
EnrollStatus = signal()
sensor.enroll(uid2identity(uid), 0xf5, update_cb, complete_cb) # TODO parse the finger name
class Manager():
def GetDevices(self):
print('In GetDevices')
return ['/net/reactivated/Fprint/Device/0']
@dbus.service.signal(dbus_interface=INTERFACE_NAME, signature='sb')
def VerifyStatus(self, result, done):
logging.debug('VerifyStatus')
def GetDefaultDevice(self):
print('In GetDefaultDevice')
return '/net/reactivated/Fprint/Device/0'
@dbus.service.signal(dbus_interface=INTERFACE_NAME, signature='sb')
def EnrollStatus(self, result, done):
logging.debug('EnrollStatus')
def readif(fn):
with open('/usr/share/dbus-1/interfaces/' + fn, 'rb') as f:
# for some reason Gio can't seem to handle XML entities declared inline
return f.read().decode('utf-8') \
.replace('&ERROR_CLAIM_DEVICE;', 'net.reactivated.Fprint.Error.ClaimDevice') \
.replace('&ERROR_ALREADY_IN_USE;', 'net.reactivated.Fprint.Error.AlreadyInUse') \
.replace('&ERROR_INTERNAL;', 'net.reactivated.Fprint.Error.Internal') \
.replace('&ERROR_PERMISSION_DENIED;', 'net.reactivated.Fprint.Error.PermissionDenied') \
.replace('&ERROR_NO_ENROLLED_PRINTS;', 'net.reactivated.Fprint.Error.NoEnrolledPrints') \
.replace('&ERROR_NO_ACTION_IN_PROGRESS;', 'net.reactivated.Fprint.Error.NoActionInProgress') \
.replace('&ERROR_INVALID_FINGERNAME;', 'net.reactivated.Fprint.Error.InvalidFingername') \
.replace('&ERROR_NO_SUCH_DEVICE;', 'net.reactivated.Fprint.Error.NoSuchDevice')
Device.dbus=[readif('net.reactivated.Fprint.Device.xml')]
Manager.dbus=[readif('net.reactivated.Fprint.Manager.xml')]
bus.publish('net.reactivated.Fprint',
('/net/reactivated/Fprint/Manager', Manager()),
('/net/reactivated/Fprint/Device/0', Device())
)
open97()
init.open()
usb.trace_enabled = True
tls.trace_enabled = True
loop.run()
svc = Device()
try:
loop.run()
finally:
reboot()
raise
print("Normal exit")

View file

@ -3,7 +3,7 @@ from time import sleep
from proto9x.usb import usb
from proto9x.tls import tls
from proto9x.flash import read_flash
from proto9x.flash import read_tls_flash
from proto9x.init_flash import init_flash
from proto9x.upload_fwext import upload_fwext
from proto9x.init_db import init_db
@ -18,7 +18,7 @@ def restart():
tls.reset()
usb.open()
usb.send_init()
tls.parseTlsFlash(read_flash(1, 0, 0x1000))
tls.parseTlsFlash(read_tls_flash())
tls.open()
usb.open()

View file

@ -130,3 +130,5 @@ def write_fw_signature(partition, signature):
rsp=tls.cmd(pack('<BBxH', 0x42, partition, len(signature)) + signature)
assert_status(rsp)
def read_tls_flash():
return read_flash(1, 0, 0x1000)

16
proto9x/init.py Normal file
View file

@ -0,0 +1,16 @@
from proto9x.usb import usb
from proto9x.tls import tls
from proto9x.sensor import sensor
from proto9x.flash import read_tls_flash
from usb import core as usb_core
def open():
usb.open()
usb.send_init()
tls.parseTlsFlash(read_tls_flash())
tls.open()
sensor.open()

View file

@ -21,31 +21,15 @@ debug=False
line_update_type1_devices = [ 0xB5, 0x885, 0xB3, 0x143B, 0x1055, 0xE1, 0x8B1, 0xEA, 0xE4, 0xED, 0x1825, 0x1FF5, 0x199 ]
# TODO use more sophisticated glow patters in different cases
def glow_start_scan():
cmd=unhexlify('3920bf0200ffff0000019900200000000099990000000000000000000000000020000000000000000000000000ffff000000990020000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000')
assert_status(tls.app(cmd))
def glow_end_enroll():
def glow_end_scan():
cmd=unhexlify('39f4010000f401000001ff002000000000ffff0000000000000000000000000020000000000000000000000000f401000000ff0020000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000')
assert_status(tls.app(cmd))
def cancel_capture():
usb.queue.put(b'')
#sleep(0.2)
#rsp=tls.app(b'\x04')
#assert_status(rsp)
usb.read_82()
def wait_for_finger():
while True:
b=usb.wait_int()
if len(b) == 0:
raise Exception('Cancelled')
if b[0] == 2:
break
def get_prg_status():
return tls.app(unhexlify('5100000000'))
@ -58,7 +42,7 @@ def wait_till_finished():
sleep(0.2)
def stop_prg():
def get_prg_status2():
return tls.app(unhexlify('5100200000'))
@ -223,10 +207,14 @@ class CaptureMode(Enum):
IDENTIFY=2
ENROLL=3
class CancelledException(Exception):
pass
class Sensor():
calib_data=b''
def open(self, load_calib_data=True):
self.interrupt_cb = None
self.device_info = identify_sensor()
print('Opening sensor: %s' % self.device_info.name)
@ -624,244 +612,323 @@ class Sensor():
self.persist_clean_slate(clean_slate)
self.save()
sensor = Sensor()
def cancel(self):
cb=usb.interrupt_cb
if cb is not None:
cb(None)
def capture(mode):
usb.purge_int_queue()
def capture(self, mode, complete_cb):
if usb.interrupt_cb is not None:
raise Exception('Wrong state for capture')
assert_status(tls.app(sensor.build_cmd_02(mode)))
def next(cb):
if cb is None:
usb.interrupt_cb = None
return
b=usb.wait_int()
if b[0] != 0:
raise Exception('Unexpected interrupt type %s' % hexlify(b).decode())
def run(b):
try:
if b is None:
raise CancelledException()
try:
wait_for_finger()
#wait_till_finished()
while True:
b=usb.wait_int()
cb(b)
except Exception as e:
usb.interrupt_cb = None
tls.app(unhexlify('04')) # capture stop if still running, cleanup
complete_cb(None, e)
usb.interrupt_cb = run
def wait_start(b):
if b[0] != 0:
raise Exception('wait_start: Unexpected interrupt type %s' % hexlify(b).decode())
next(wait_finger)
def wait_finger(b):
if b[0] == 2:
next(wait_capture_complete)
# TODO: report status?
def wait_capture_complete(b):
if b[0] != 3:
raise Exception('Unexpected interrupt type %s' % hexlify(b).decode())
raise Exception('wait_finger: Unexpected interrupt type %s' % hexlify(b).decode())
if b[2] & 4:
break
finally:
res=stop_prg()
assert_status(res)
res = res[2:]
l, res = res[:4], res[4:]
l, = unpack('<L', l)
if l != len(res):
raise Exception('Response size does not match %d != %d', l, len(res))
x, y, w1, w2, error = unpack('<HHHHL', res)
return error
def enrollment_update_start(key=0):
rsp=tls.app(pack('<BLL', 0x68, key, 0))
assert_status(rsp)
new_key, = unpack('<L', rsp[2:])
usb.wait_int()
return new_key
def enrollment_update_end():
assert_status(tls.app(pack('<BL', 0x69, 0)))
def enrollment_update(prev):
write_enable()
rsp=tls.app(b'\x6b' + prev)
assert_status(rsp)
flush_changes()
return rsp[2:]
def append_new_image(key=0, prev=b''):
enrollment_update(prev)
usb.wait_int()
res = enrollment_update(prev)
l, res = res[:2], res[2:]
l, = unpack('<H', l)
if l != len(res):
raise Exception('Response size does not match %d != %d', l, len(res))
magic_len = 0x38 # hardcoded in the DLL
template = header = tid = None
while len(res) > 0:
tag, l = unpack('<HH', res[:4])
if tag == 0:
template = res[:magic_len+l]
elif tag == 1:
header = res[magic_len:magic_len+l]
elif tag == 3:
tid = res[magic_len:magic_len+l]
else:
print('Ignoring unknown tag %x' % tag)
capture_complete()
return
res=res[magic_len+l:]
# TODO: report status?
return (header, template, tid)
def capture_complete():
next(None)
def make_finger_data(subtype, template, tid):
template = pack('<HH', 1, len(template)) + template
tid = pack('<HH', 2, len(tid)) + tid
res = get_prg_status2()
tinfo = template + tid
assert_status(res)
res = res[2:]
l, res = res[:4], res[4:]
l, = unpack('<L', l)
tinfo = pack('<HHHH', subtype, 3, len(tinfo), 0x20) + tinfo
tinfo += b'\0' * 0x20
if l != len(res):
raise Exception('Response size does not match %d != %d', l, len(res))
return tinfo
x, y, w1, w2, error = unpack('<HHHHL', res)
def enroll(identity, subtype):
key=0
template=b''
if error != 0:
raise Exception('Scanning problem: %04x' % error)
print('Waiting for a finger...')
complete_cb((x, y, w1, w2), None)
while True:
glow_start_scan()
next(wait_start)
try:
err = capture(CaptureMode.ENROLL)
if err != 0:
print('Error %08x, try again' % err)
continue
except Exception as e:
print('Capture failed (%s), try again' % repr(e))
sleep(1)
continue
assert_status(tls.app(self.build_cmd_02(mode)))
def enrollment_update_start(self, key, complete_cb):
if usb.interrupt_cb is not None:
raise Exception('Wrong state, sensor\'s busy')
def wait(b):
if b is None:
raise Exception('Cancelling while enrollment is being updated is not a good idea.')
usb.interrupt_cb = None
complete_cb(new_key, b)
usb.interrupt_cb = wait
rsp=tls.app(pack('<BLL', 0x68, key, 0))
assert_status(rsp)
new_key, = unpack('<L', rsp[2:])
def enrollment_update_end(self):
assert_status(tls.app(pack('<BL', 0x69, 0)))
# Generates interrupt
def enrollment_update(self, prev):
write_enable()
rsp=tls.app(b'\x6b' + prev)
assert_status(rsp)
flush_changes()
return rsp[2:]
def append_new_image(self, prev, complete_cb):
if usb.interrupt_cb is not None:
raise Exception('Wrong state, sensor\'s busy')
def finished(b):
res = self.enrollment_update(prev)
l, res = res[:2], res[2:]
l, = unpack('<H', l)
if l != len(res):
raise Exception('Response size does not match %d != %d', l, len(res))
magic_len = 0x38 # hardcoded in the DLL
template = header = tid = None
while len(res) > 0:
tag, l = unpack('<HH', res[:4])
if tag == 0:
template = res[:magic_len+l]
elif tag == 1:
header = res[magic_len:magic_len+l]
elif tag == 3:
tid = res[magic_len:magic_len+l]
else:
print('Ignoring unknown tag %x' % tag)
res=res[magic_len+l:]
return (header, template, tid)
def wait(b):
if b is None:
raise Exception('Cancelling while enrollment is being updated is not a good idea.')
usb.interrupt_cb = None
try:
complete_cb(finished(b), None)
except Exception as e:
complete_cb(None, e)
usb.interrupt_cb = wait
# Start the work. Interrupt will be generated when it is finished.
self.enrollment_update(prev)
key = enrollment_update_start(key)
header, template, tid = append_new_image(key, template)
enrollment_update_end()
print(hexlify(header))
def make_finger_data(self, subtype, template, tid):
template = pack('<HH', 1, len(template)) + template
tid = pack('<HH', 2, len(tid)) + tid
if tid:
break
tinfo = template + tid
# TODO check for duplicates
tinfo = pack('<HHHH', subtype, 3, len(tinfo), 0x20) + tinfo
tinfo += b'\0' * 0x20
tinfo = make_finger_data(subtype, template, tid)
return tinfo
usr=db.lookup_user(identity)
if usr == None:
usr = db.new_user(identity)
else:
usr = usr.dbid
recid = db.new_finger(usr, tinfo)
def enroll(self, identity, subtype, update_cb, complete_cb):
def do_create_finger(final_template, tid):
try:
tinfo = self.make_finger_data(subtype, final_template, tid)
glow_end_enroll()
usr=db.lookup_user(identity)
if usr == None:
usr = db.new_user(identity)
else:
usr = usr.dbid
recid = db.new_finger(usr, tinfo)
print('All done')
glow_end_scan()
return recid
complete_cb(recid, None)
except Exception as e:
complete_cb(None, e)
def parse_dict(x):
rc={}
while len(x) > 0:
(t, l), x = unpack('<HH', x[:4]), x[4:]
rc[t], x = x[:l], x[l:]
def start_iteration(key=0, template=b''):
def wrap_cb(f):
def r(res, ex):
try:
f(res, ex)
except CancelledException as e:
glow_end_scan()
complete_cb(None, e)
except Exception as e:
update_cb(None, e)
# sleep, so we don't end up in a busy loop spaming the sensor with requests in case of unrecoverable error
sleep(1)
self.enrollment_update_end()
start_iteration(key, template)
return rc
return r
def identify():
glow_start_scan()
try:
err = capture(CaptureMode.IDENTIFY)
if err != 0:
raise Exception('Capture failed: %08x' % err)
def capture_cb(res, e):
if e is not None: raise e
def enrollment_update_start_cb(new_key, b):
def append_new_image_cb(rsp, e):
if e is not None: raise e
self.enrollment_update_end()
header, new_template, tid = rsp
update_cb(header, None)
if tid:
do_create_finger(new_template, tid)
else:
start_iteration(new_key, new_template)
## end of append_new_image_cb
self.append_new_image(template, wrap_cb(append_new_image_cb))
## end of enrollment_update_start_cb
self.enrollment_update_start(key, wrap_cb(enrollment_update_start_cb))
## end of capture_cb
glow_start_scan()
self.capture(CaptureMode.ENROLL, wrap_cb(capture_cb))
start_iteration()
def parse_dict(self, x):
rc={}
while len(x) > 0:
(t, l), x = unpack('<HH', x[:4]), x[4:]
rc[t], x = x[:l], x[l:]
return rc
def match_finger(self, complete_cb):
if usb.interrupt_cb is not None:
raise Exception('Wrong state for capture')
def wait(b):
if b is None:
raise Exception('Cancelling while finger match is running is not a good idea.')
try:
if b[0] != 3:
raise Exception('Finger not recognized: %s' % hexlify(b).decode())
# get results
rsp = tls.app(unhexlify('6000000000'))
assert_status(rsp)
rsp = rsp[2:]
(l,), rsp = unpack('<H', rsp[:2]), rsp[2:]
if l != len(rsp):
raise Exception('Response size does not match')
rsp=self.parse_dict(rsp)
usrid, subtype, hsh = rsp[1], rsp[3], rsp[4]
usrid, = unpack('<L', usrid)
subtype, = unpack('<H', subtype)
complete_cb((usrid, subtype, hsh), None)
except Exception as e:
complete_cb(None, e)
finally:
usb.interrupt_cb = None
# cleanup, ignore any errors
tls.app(unhexlify('6200000000'))
usb.interrupt_cb = wait
# which finger?
stg_id=0 # match against any storage
usr_id=0 # match against any user
cmd=pack('<BBBHHHHH', 0x5e, 2, 0xff, stg_id, usr_id, 1, 0,0)
rsp=tls.app(cmd)
assert_status(rsp)
b = usb.wait_int()
if b[0] != 3:
raise Exception('Identification failed: %s' % hexlify(b).decode())
rsp = tls.app(unhexlify('6000000000'))
assert_status(rsp)
rsp = rsp[2:]
finally:
# finish
assert_status(tls.app(unhexlify('6200000000')))
(l,), rsp = unpack('<H', rsp[:2]), rsp[2:]
if l != len(rsp):
raise Exception('Response size does not match')
rsp=parse_dict(rsp)
#for k in rsp:
# print('%04x: %s (%d)' % (k, hexlify(rsp[k]).decode(), len(rsp[k])))
#on 0097
#0001: 09000000 (4)
#0003: f500 (2)
#0004: 8dee792532d3432d41c872fd4d6d590fbc855ad449cf2753cd919eb9c94675c6 (32)
#0005: 0000000000000000000000000000000000000000000000000000000000000000 (32)
#0008: 0a00 (2) <-- finger record db id
#0002: 010b0000 (4)
#0006: 00000000000000000000000000000000000000000000000000000000000000000000000000000000 (40)
#on 009a (no finger record db id)
#0000 8a00
# 0100 0400 05000000
# 0300 0200 f500
# 0400 2000 b147dd1eda8da322fb7a2a51d0eab6fe94bef46c05204fbefb1fd16360903791
# 0500 2000 0000000000000000000000000000000000000000000000000000000000000000
# 0200 0400 650a0000
# 0600 2800 00000000000000000000000000000000000000000000000000000000000000000000000000000000
usrid, subtype, hsh = rsp[1], rsp[3], rsp[4]
usrid, = unpack('<L', usrid)
subtype, = unpack('<H', subtype)
usr = db.get_user(usrid)
fingerids = [f['dbid'] for f in usr.fingers if f['subtype'] == subtype]
if len(fingerids) != 1:
raise Exception('Unexpected matching finger count')
finger_record = db.get_record_children(fingerids[0])
# Device won't let you add more than one data blob
if len(finger_record.children) > 1:
raise Exception('Expected only one child record for finger')
print('Recognised finger %02x (%s) from user %s' % (subtype, subtype_to_string(subtype), repr(usr.identity)))
print('Template hash: %s' % hexlify(hsh).decode())
if len(finger_record.children) > 0:
if finger_record.children[0]['type'] != 8:
raise Exception('Expected data blob as a finger child')
blob_id = finger_record.children[0]['dbid']
blob = db.get_record_value(blob_id).value
def identify(self, update_cb, complete_cb):
def start():
try:
glow_start_scan()
self.capture(CaptureMode.IDENTIFY, capture_cb)
except Exception as e:
# failed to start the capture, pointless to retry
glow_end_scan()
complete_cb(None, e)
tag, sz = unpack('<HH', blob[:4])
val = blob[4:4+sz]
def capture_cb(_, e):
try:
if e is not None: raise e
self.match_finger(complete_cb)
except CancelledException as e:
glow_end_scan()
complete_cb(None, e)
except Exception as e:
# Capture failed, retry
update_cb(e)
sleep(1)
start()
print('Data blob associated with the finger: %04x: %s' % (tag, hexlify(val).decode()))
start()
def get_finger_blobs(self, usrid, subtype):
usr = db.get_user(usrid)
fingerids = [f['dbid'] for f in usr.fingers if f['subtype'] == subtype]
if len(fingerids) != 1:
raise Exception('Unexpected matching finger count')
return rsp
finger_record = db.get_record_children(fingerids[0])
ids=[r['dbid'] for r in finger_record.children if r['type'] == 8]
return [db.get_record_value(id).value for id in ids]
sensor = Sensor()

View file

@ -14,11 +14,12 @@ def default_fwext_name():
if usb.usb_dev().idVendor == 0x138a:
if usb.usb_dev().idProduct == 0x0090:
return '6_07f_Lenovo.xpfwext'
elif usb.usb_dev().idProduct == 0x0097:
return '6_07f_lenovo_mis.xpfwext'
elif usb.usb_dev().idVendor == 0x06cb:
if usb.usb_dev().idProduct == 0x009a:
return '6_07f_lenovo_mis_qm.xpfwext'
# It looks like the firmware file must match DLL, not the hardware.
# Both DLL and xpfwext are universal.
# The device dependant code seems to be loaded dynamically (via encrypted blobs).
# So, it is important that xpfwext file is matching the blobs contents.
return '6_07f_lenovo_mis_qm.xpfwext'
def upload_fwext(fw_path=None):
# no idea what this is:

View file

@ -4,27 +4,36 @@ from binascii import *
from .util import assert_status, unhex
from struct import unpack
from usb.util import claim_interface, release_interface
from queue import Queue
from threading import Thread
from usb.core import USBError
from .blobs import init_hardcoded, init_hardcoded_clean_slate
supported_devices=[
(0x138a, 0x0097),
(0x06cb, 0x009a),
]
def custom_match(d):
return (d.idVendor, d.idProduct) in supported_devices
class Usb():
def __init__(self):
self.interrupt_cb = None
self.trace_enabled = False
self.queue = Queue(maxsize=10)
self.quit = None
self.dev = None
def purge_int_queue(self):
try:
while True:
self.queue.get_nowait()
except:
pass
def open(self, vendor=None, product=None):
if vendor is not None and product is not None:
dev = ucore.find(idVendor=vendor, idProduct=product)
else:
dev = ucore.find(custom_match=custom_match)
def open(self, vendor=0x06cb, product=0x009a):
self.dev = ucore.find(idVendor=vendor, idProduct=product)
self.open_dev(dev)
def open_dev(self, dev):
self.dev = dev
self.dev.default_timeout = 15000
self.thread = Thread(target=lambda: self.int_thread())
self.thread.daemon = True
@ -80,7 +89,14 @@ class Usb():
resp = self.dev.read(131, 1024, timeout=0)
resp = bytes(resp)
self.trace('<int< %s' % hexlify(resp).decode())
self.queue.put(resp)
cb=self.interrupt_cb
if cb is None:
print('Ignoring spurious interrupt: %s' % hexlify(resp).decode())
else:
try:
cb(resp)
except Exception as e:
print('Exception on the interrupt thread: %s' % e)
except USBError as e:
self.trace('<int< Exception on interrupt thread: %s' % repr(e))
if self.quit != None:
@ -89,10 +105,6 @@ class Usb():
self.trace('<int< Interrupt thread is dead')
def wait_int(self):
resp = self.queue.get()
return resp
def trace(self, s):
if self.trace_enabled:
print(s)

View file

@ -5,33 +5,92 @@ from proto9x.db import db
from proto9x.flash import read_flash
from proto9x.sensor import *
from proto9x.sid import *
from proto9x.init import open as open9x
from threading import Condition
from time import sleep
import code
def open_common():
usb.send_init()
#usb.trace_enabled = True
#tls.trace_enabled = True
# try to init TLS session from the flash
tls.parseTlsFlash(read_flash(1, 0, 0x1000))
def identify():
cv=Condition()
result=[]
tls.open()
tls.save()
sensor.open()
#usb.trace_enabled = True
#tls.trace_enabled = True
def update_cb(e):
print('Capture error: %s, try again' % repr(e))
def open97():
usb.open(vendor=0x138a, product=0x0097)
open_common()
def complete_cb(rsp, e):
cv.acquire()
try:
result.append((rsp, e))
cv.notify()
finally:
cv.release()
def open9a():
usb.open(vendor=0x06cb, product=0x009a)
open_common()
sensor.identify(update_cb, complete_cb)
def load97():
#usb.trace_enabled = True
#tls.trace_enabled = True
usb.open()
tls.load()
sensor.open()
cv.acquire()
try:
cv.wait()
rsp, e = result[0]
if e is not None: raise e
except:
sensor.cancel()
raise
finally:
cv.release()
usrid, subtype, hsh = rsp
print('Got finger %x for user recordid %d. Hash: %s' % (subtype, usrid, hexlify(hsh).decode()))
def enroll(sid, finger):
cv=Condition()
result=[]
def update_cb(x, e):
if e is not None:
print('Enroll error: %s, try again' % repr(e))
else:
print('Enroll progress: %s' % hexlify(x).decode())
def complete_cb(rsp, e):
cv.acquire()
try:
result.append((rsp, e))
cv.notify()
finally:
cv.release()
sensor.enroll(sid, finger, update_cb, complete_cb)
cv.acquire()
try:
cv.wait()
recid, e = result[0]
if e is not None: raise e
except:
sensor.cancel()
raise
finally:
cv.release()
print('Created a finger record with dbid %d' % recid)
# can't use atexit as it conflicts with atexit installed by libusb
class Blah:
def __init__(self):
self.tls=tls
def __del__(self):
if usb.dev is not None:
print('Rebooting device...')
try:
reboot()
except:
pass
blah=Blah()