Incrementally pair with the device. Try downloading the firmware from postinst.
This commit is contained in:
parent
b909438785
commit
600202b4d6
15 changed files with 237 additions and 332 deletions
12
README.md
12
README.md
|
|
@ -1,12 +1,16 @@
|
|||
# python-validity
|
||||
Validity fingerprint sensor library.
|
||||
Originally designed to capture some of my findings for 138a:0097, but if you manage to get it working for some other Validity sensor - pull requests are welcome.
|
||||
Validity fingerprint sensor driver.
|
||||
|
||||
## Setting up
|
||||
|
||||
On a Debian-based system, to re-install from sources (useful for testing):
|
||||
On Ubuntu system:
|
||||
```
|
||||
./setup.py install --force --install-layout deb --prefix=/usr --root=/
|
||||
$ sudo apt remove fprintd
|
||||
$ sudo add-apt-repository ppa:uunicorn/open-fprintd
|
||||
$ sudo aptget update
|
||||
$ sudo apt install open-fprintd fprintd-clients python-validity
|
||||
...wait a bit...
|
||||
$ fprintd-enroll
|
||||
```
|
||||
|
||||
## Initialization
|
||||
|
|
|
|||
122
bin/validity-sensors-firmware
Executable file
122
bin/validity-sensors-firmware
Executable file
|
|
@ -0,0 +1,122 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# 2020 - Marco Trevisan
|
||||
#
|
||||
# Initializer for ThinkPad's validity sensors 138a:0090 and 138a:0097 and 06cb:009a
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import urllib.request
|
||||
import shutil
|
||||
|
||||
from enum import Enum, auto
|
||||
from time import sleep
|
||||
from usb import core as usb_core
|
||||
|
||||
python_validity_data='/usr/share/python-validity/'
|
||||
|
||||
# FIXME: supported usb ids are duplicated in usb.py
|
||||
class VFS(Enum):
|
||||
DEV_90 = (0x138a, 0x0090)
|
||||
DEV_97 = (0x138a, 0x0097)
|
||||
DEV_9a = (0x06cb, 0x009a)
|
||||
|
||||
DEFAULT_URIS = {
|
||||
VFS.DEV_90: {
|
||||
'driver': 'https://download.lenovo.com/pccbbs/mobiles/n1cgn08w.exe',
|
||||
'referral': 'https://support.lenovo.com/us/en/downloads/DS120491',
|
||||
},
|
||||
VFS.DEV_97: {
|
||||
'driver': 'https://download.lenovo.com/pccbbs/mobiles/nz3gf07w.exe',
|
||||
'referral': 'https://download.lenovo.com/pccbbs/mobiles/nz3gf07w.exe'
|
||||
},
|
||||
VFS.DEV_9a: {
|
||||
'driver': 'https://download.lenovo.com/pccbbs/mobiles/nz3gf07w.exe',
|
||||
'referral': 'https://download.lenovo.com/pccbbs/mobiles/nz3gf07w.exe'
|
||||
}
|
||||
}
|
||||
|
||||
# FIXME: filenames are duplicated in upload_fwext.py
|
||||
DEFAULT_FW_NAMES = {
|
||||
VFS.DEV_90: '6_07f_Lenovo.xpfwext',
|
||||
VFS.DEV_97: '6_07f_lenovo_mis_qm.xpfwext',
|
||||
VFS.DEV_9a: '6_07f_lenovo_mis_qm.xpfwext'
|
||||
}
|
||||
|
||||
|
||||
def download_and_extract_fw(dev_type, fwdir, fwuri=None):
|
||||
fwuri = fwuri if fwuri else DEFAULT_URIS[dev_type]['driver']
|
||||
fwarchive = os.path.join(fwdir, 'fwinstaller.exe')
|
||||
fwname = DEFAULT_FW_NAMES[dev_type]
|
||||
|
||||
print('Downloading {} to extract {}'.format(fwuri, fwname))
|
||||
|
||||
req = urllib.request.Request(fwuri)
|
||||
req.add_header('Referer', DEFAULT_URIS[dev_type].get('referral', ''))
|
||||
req.add_header('User-Agent', 'Mozilla/5.0 (X11; U; Linux)')
|
||||
|
||||
with urllib.request.urlopen(req) as response:
|
||||
with open(fwarchive, 'wb') as out_file:
|
||||
out_file.write(response.read())
|
||||
|
||||
subprocess.check_call(['innoextract',
|
||||
'--output-dir', fwdir,
|
||||
'--include', fwname,
|
||||
'--collisions', 'overwrite',
|
||||
fwarchive
|
||||
])
|
||||
|
||||
fwpath = subprocess.check_output([
|
||||
'find', fwdir, '-name', fwname]).decode('utf-8').strip()
|
||||
print('Found firmware at {}'.format(fwpath))
|
||||
|
||||
if not fwpath:
|
||||
raise Exception('No {} found in the archive'.format(fwname))
|
||||
|
||||
return fwpath
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--driver-uri')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if os.geteuid() != 0:
|
||||
raise Exception('This script needs to be executed as root')
|
||||
|
||||
dev_type = None
|
||||
for d in VFS:
|
||||
dev = usb_core.find(idVendor=d.value[0], idProduct=d.value[1])
|
||||
if dev:
|
||||
dev_type = d
|
||||
|
||||
if not dev_type:
|
||||
raise Exception('No supported validity device found')
|
||||
|
||||
try:
|
||||
subprocess.check_call(['innoextract', '--version'],
|
||||
stdout=subprocess.DEVNULL)
|
||||
except Exception as e:
|
||||
print('Impossible to run innoextract: {}'.format(e))
|
||||
sys.exit(1)
|
||||
|
||||
with tempfile.TemporaryDirectory() as fwdir:
|
||||
fwpath = download_and_extract_fw(dev_type, fwdir, fwuri=args.driver_uri)
|
||||
shutil.copy(fwpath, python_validity_data)
|
||||
|
|
@ -1,239 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# 2020 - Marco Trevisan
|
||||
#
|
||||
# Initializer for ThinkPad's validity sensors 138a:0090 and 138a:0097 and 06cb:009a
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import urllib.request
|
||||
import shutil
|
||||
|
||||
|
||||
from enum import Enum, auto
|
||||
from time import sleep
|
||||
from usb import core as usb_core
|
||||
|
||||
from validitysensor.flash import read_tls_flash
|
||||
from validitysensor.init_db import init_db
|
||||
from validitysensor.init_flash import init_flash
|
||||
from validitysensor.sensor import sensor as vfs_sensor
|
||||
from validitysensor.sensor import factory_reset
|
||||
from validitysensor.tls import tls as vfs_tls
|
||||
from validitysensor.upload_fwext import upload_fwext
|
||||
from validitysensor.usb import usb as vfs_usb
|
||||
|
||||
class VFS(Enum):
|
||||
DEV_90 = (0x138a, 0x0090)
|
||||
DEV_97 = (0x138a, 0x0097)
|
||||
DEV_9a = (0x06cb, 0x009a)
|
||||
|
||||
DEFAULT_URIS = {
|
||||
VFS.DEV_90: {
|
||||
'driver': 'https://download.lenovo.com/pccbbs/mobiles/n1cgn08w.exe',
|
||||
'referral': 'https://support.lenovo.com/us/en/downloads/DS120491',
|
||||
},
|
||||
VFS.DEV_97: {
|
||||
'driver': 'https://download.lenovo.com/pccbbs/mobiles/nz3gf07w.exe',
|
||||
'referral': 'https://download.lenovo.com/pccbbs/mobiles/nz3gf07w.exe'
|
||||
},
|
||||
VFS.DEV_9a: {
|
||||
'driver': 'https://download.lenovo.com/pccbbs/mobiles/nz3gf07w.exe',
|
||||
'referral': 'https://download.lenovo.com/pccbbs/mobiles/nz3gf07w.exe'
|
||||
}
|
||||
}
|
||||
|
||||
DEFAULT_FW_NAMES = {
|
||||
VFS.DEV_90: '6_07f_Lenovo.xpfwext',
|
||||
VFS.DEV_97: '6_07f_lenovo_mis_qm.xpfwext',
|
||||
VFS.DEV_9a: '6_07f_lenovo_mis_qm.xpfwext'
|
||||
}
|
||||
|
||||
|
||||
class VFSInitializer():
|
||||
def __init__(self, args, usb_dev, dev_type):
|
||||
self.args = args
|
||||
self.usb_dev = usb_dev
|
||||
self.dev_type = dev_type
|
||||
self.dev_str = repr(usb_dev)
|
||||
|
||||
print('Found device {}'.format(self.dev_str))
|
||||
|
||||
if self.args.host_product or self.args.host_serial:
|
||||
vfs_tls.set_hwkey(product_name=self.args.host_product,
|
||||
serial_number=self.args.host_serial)
|
||||
|
||||
def open_device(self, init=False):
|
||||
print('Opening device',hex(self.dev_type.value[1]))
|
||||
vfs_usb.open(vendor=self.dev_type.value[0], product=self.dev_type.value[1])
|
||||
|
||||
if init:
|
||||
vfs_usb.send_init()
|
||||
|
||||
# try to init TLS session from the flash
|
||||
vfs_tls.parseTlsFlash(read_tls_flash())
|
||||
vfs_tls.open()
|
||||
|
||||
def restart(self):
|
||||
vfs_tls.reset()
|
||||
self.open_device(init=True)
|
||||
|
||||
def download_and_extract_fw(self, fwdir, fwuri=None):
|
||||
fwuri = fwuri if fwuri else DEFAULT_URIS[self.dev_type]['driver']
|
||||
fwarchive = os.path.join(fwdir, 'fwinstaller.exe')
|
||||
fwname = DEFAULT_FW_NAMES[self.dev_type]
|
||||
|
||||
print('Downloading {} to extract {}'.format(fwuri, fwname))
|
||||
|
||||
req = urllib.request.Request(fwuri)
|
||||
req.add_header('Referer', DEFAULT_URIS[self.dev_type].get('referral', ''))
|
||||
req.add_header('User-Agent', 'Mozilla/5.0 (X11; U; Linux)')
|
||||
|
||||
with urllib.request.urlopen(req) as response:
|
||||
with open(fwarchive, 'wb') as out_file:
|
||||
out_file.write(response.read())
|
||||
|
||||
subprocess.check_call(['innoextract',
|
||||
'--output-dir', fwdir,
|
||||
'--include', fwname,
|
||||
'--collisions', 'overwrite',
|
||||
fwarchive
|
||||
])
|
||||
|
||||
fwpath = subprocess.check_output([
|
||||
'find', fwdir, '-name', fwname]).decode('utf-8').strip()
|
||||
print('Found firmware at {}'.format(fwpath))
|
||||
|
||||
if not fwpath:
|
||||
raise Exception('No {} found in the archive'.format(fwname))
|
||||
|
||||
return fwpath
|
||||
|
||||
def sleep(self, sec=3):
|
||||
print('Sleeping...')
|
||||
sleep(sec)
|
||||
|
||||
def try_factory_reset(self):
|
||||
self.open_device()
|
||||
try:
|
||||
print('Factory reset...')
|
||||
factory_reset()
|
||||
except Exception as e:
|
||||
print('Factory reset failed with {}, this should not happen, but ' \
|
||||
'we can ignore it, if pairing works...'.format(e))
|
||||
|
||||
def pair(self):
|
||||
print('Pairing the sensor with device')
|
||||
|
||||
max_retries = 5
|
||||
err = None
|
||||
for i in range(0, max_retries):
|
||||
try:
|
||||
self.open_device()
|
||||
|
||||
print('Initializing flash...')
|
||||
init_flash()
|
||||
break
|
||||
except Exception as e:
|
||||
if err is None:
|
||||
err = e
|
||||
self.sleep()
|
||||
print('Try {} failed with error: {}'.format(i+1, e))
|
||||
finally:
|
||||
max_retries -= 1
|
||||
|
||||
if max_retries == 0:
|
||||
print('Device didn\'t show up after reset, retry...')
|
||||
raise(err)
|
||||
|
||||
self.sleep()
|
||||
self.restart()
|
||||
|
||||
print('Uploading firmware...')
|
||||
upload_fwext()
|
||||
|
||||
self.sleep()
|
||||
self.restart()
|
||||
|
||||
print('Calibrating...')
|
||||
vfs_sensor.open()
|
||||
vfs_sensor.calibrate()
|
||||
|
||||
print('Init database...')
|
||||
init_db()
|
||||
|
||||
vfs_tls.reset()
|
||||
|
||||
print('That\'s it, pairing with {} finished'.format(self.dev_str))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--driver-uri')
|
||||
parser.add_argument('--firmware-path', type=argparse.FileType('r'))
|
||||
parser.add_argument('--calibration-data', type=argparse.FileType('r'))
|
||||
parser.add_argument('--host-product')
|
||||
parser.add_argument('--host-serial')
|
||||
parser.add_argument('--simulate-virtualbox', action='store_true')
|
||||
parser.add_argument('--download-fw-only', action='store_true')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.simulate_virtualbox and (args.host_product or args.host_serial):
|
||||
parser.error("--simulate-virtualbox is incompatible with host params.")
|
||||
|
||||
if os.geteuid() != 0:
|
||||
raise Exception('This script needs to be executed as root')
|
||||
|
||||
usb_dev = None
|
||||
for d in VFS:
|
||||
dev = usb_core.find(idVendor=d.value[0], idProduct=d.value[1])
|
||||
if dev:
|
||||
dev_type = d
|
||||
usb_dev = dev
|
||||
|
||||
if not usb_dev:
|
||||
raise Exception('No supported validity device found')
|
||||
|
||||
try:
|
||||
subprocess.check_call(['innoextract', '--version'],
|
||||
stdout=subprocess.DEVNULL)
|
||||
except Exception as e:
|
||||
print('Impossible to run innoextract: {}'.format(e))
|
||||
sys.exit(1)
|
||||
|
||||
vfs_initializer = VFSInitializer(args, usb_dev, dev_type)
|
||||
|
||||
with tempfile.TemporaryDirectory() as fwdir:
|
||||
if args.firmware_path:
|
||||
fwpath = args.firmware_path.name
|
||||
else:
|
||||
fwpath = vfs_initializer.download_and_extract_fw(fwdir,
|
||||
fwuri=args.driver_uri)
|
||||
|
||||
shutil.copy(fwpath, '/usr/share/python-validity/')
|
||||
|
||||
if not args.download_fw_only:
|
||||
input('The device will be now reset to factory and associated to the ' \
|
||||
'current laptop.\nPress Enter to continue (or Ctrl+C to cancel)...')
|
||||
|
||||
vfs_initializer.try_factory_reset()
|
||||
vfs_initializer.sleep()
|
||||
vfs_initializer.pair()
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import argparse
|
||||
import re
|
||||
import sys
|
||||
import dbus
|
||||
import dbus.mainloop.glib
|
||||
import dbus.service
|
||||
|
|
@ -15,7 +16,7 @@ from validitysensor.tls import tls
|
|||
from validitysensor.usb import usb
|
||||
from validitysensor.sid import sid_from_string
|
||||
from validitysensor.db import subtype_to_string, db
|
||||
from validitysensor.sensor import sensor, reboot
|
||||
from validitysensor.sensor import sensor, reboot, RebootException
|
||||
import pwd
|
||||
|
||||
GLib.threads_init()
|
||||
|
|
@ -166,14 +167,18 @@ if __name__ == '__main__':
|
|||
usb.trace_enabled = True
|
||||
tls.trace_enabled = True
|
||||
|
||||
if args.devpath is None:
|
||||
init.open()
|
||||
else:
|
||||
z = re.match('^usb-(\d+)-(\d+)$', args.devpath)
|
||||
if not z:
|
||||
parser.error('Option --devpath should look like this: usb-<busnum>-<address>')
|
||||
try:
|
||||
if args.devpath is None:
|
||||
init.open()
|
||||
else:
|
||||
z = re.match('^usb-(\d+)-(\d+)$', args.devpath)
|
||||
if not z:
|
||||
parser.error('Option --devpath should look like this: usb-<busnum>-<address>')
|
||||
|
||||
init.open_devpath(*map(int, z.groups()))
|
||||
init.open_devpath(*map(int, z.groups()))
|
||||
except RebootException:
|
||||
print('Initialization ended up in rebooting the sensor. Normal exit.')
|
||||
sys.exit(0)
|
||||
|
||||
bus = dbus.SystemBus()
|
||||
|
||||
|
|
|
|||
1
debian/python3-validity.postinst
vendored
1
debian/python3-validity.postinst
vendored
|
|
@ -4,6 +4,7 @@ set -e
|
|||
#DEBHELPER#
|
||||
|
||||
if [ "$1" = "configure" ]; then
|
||||
validity-sensors-firmware || true
|
||||
systemctl daemon-reload || true
|
||||
udevadm control --reload-rules || true
|
||||
udevadm trigger || true
|
||||
|
|
|
|||
1
debian/python3-validity@.service
vendored
1
debian/python3-validity@.service
vendored
|
|
@ -5,5 +5,6 @@ Description=python-validity driver dbus service for %I
|
|||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/lib/python-validity/dbus-service --devpath %i
|
||||
StandardOutput=syslog
|
||||
Restart=no
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
|
||||
from validitysensor.usb import usb
|
||||
from validitysensor.sensor import factory_reset
|
||||
from validitysensor.sensor import factory_reset, RebootException
|
||||
|
||||
usb.open()
|
||||
factory_reset()
|
||||
try:
|
||||
usb.open()
|
||||
factory_reset()
|
||||
except RebootException:
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -1,44 +0,0 @@
|
|||
|
||||
from time import sleep
|
||||
|
||||
from validitysensor.usb import usb
|
||||
from validitysensor.tls import tls
|
||||
from validitysensor.flash import read_tls_flash
|
||||
from validitysensor.init_flash import init_flash
|
||||
from validitysensor.upload_fwext import upload_fwext
|
||||
from validitysensor.init_db import init_db
|
||||
from validitysensor.sensor import sensor, reboot
|
||||
|
||||
#usb.trace_enabled=True
|
||||
#tls.trace_enabled=True
|
||||
|
||||
def restart():
|
||||
print('Sleeping...')
|
||||
sleep(3)
|
||||
tls.reset()
|
||||
usb.open()
|
||||
usb.send_init()
|
||||
tls.parseTlsFlash(read_tls_flash())
|
||||
tls.open()
|
||||
|
||||
try:
|
||||
usb.open()
|
||||
print('Initializing flash...')
|
||||
init_flash()
|
||||
|
||||
restart()
|
||||
print('Uploading firmware...')
|
||||
upload_fwext()
|
||||
|
||||
restart()
|
||||
print('Calibrating...')
|
||||
sensor.open(False)
|
||||
sensor.calibrate()
|
||||
|
||||
print('Init database...')
|
||||
init_db()
|
||||
|
||||
print('That\'s it, pairing\'s finished')
|
||||
finally:
|
||||
sleep(1)
|
||||
reboot()
|
||||
3
setup.py
3
setup.py
|
|
@ -8,7 +8,7 @@ setup(name='python-validity',
|
|||
packages=['validitysensor'],
|
||||
scripts=[
|
||||
'bin/validity-led-dance',
|
||||
'bin/validity-sensors-initializer',
|
||||
'bin/validity-sensors-firmware',
|
||||
],
|
||||
install_requires=[
|
||||
'cryptography >= 2.1.4',
|
||||
|
|
@ -21,7 +21,6 @@ setup(name='python-validity',
|
|||
'scripts/dbus-cmd.py',
|
||||
'scripts/lsdbus.py',
|
||||
'scripts/factory-reset.py',
|
||||
'scripts/pair.py',
|
||||
'scripts/prototype.py'
|
||||
]),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -121,10 +121,10 @@ def write_flash_all(partition, ptr, buf):
|
|||
write_flash(partition, ptr, chunk)
|
||||
ptr += len(chunk)
|
||||
|
||||
def read_flash_all(partition, start, end):
|
||||
def read_flash_all(partition, start, size):
|
||||
bs = 0x1000
|
||||
blocks = [read_flash(partition, addr, bs) for addr in range(start, end, bs)]
|
||||
return b''.join(blocks)
|
||||
blocks = [read_flash(partition, addr, bs) for addr in range(start, start+size, bs)]
|
||||
return b''.join(blocks)[:size]
|
||||
|
||||
def write_fw_signature(partition, signature):
|
||||
rsp=tls.cmd(pack('<BBxH', 0x42, partition, len(signature)) + signature)
|
||||
|
|
|
|||
|
|
@ -6,6 +6,9 @@ from validitysensor.tls import tls
|
|||
from validitysensor.sensor import sensor
|
||||
from validitysensor.sensor import reboot
|
||||
from validitysensor.flash import read_tls_flash
|
||||
from validitysensor.init_flash import init_flash
|
||||
from validitysensor.upload_fwext import upload_fwext
|
||||
from validitysensor.init_db import init_db
|
||||
|
||||
def close():
|
||||
if usb.dev is not None:
|
||||
|
|
@ -14,19 +17,26 @@ def close():
|
|||
# The reboot command may fail if we're shutting down because the device is already gone.
|
||||
try:
|
||||
reboot()
|
||||
except RebootException:
|
||||
pass
|
||||
finally:
|
||||
usb.close()
|
||||
|
||||
def open_common():
|
||||
init_flash()
|
||||
usb.send_init()
|
||||
# We must register atexit only after we opened usb device,
|
||||
# so that our handler is called before pyusb's one and we can still talk to the device
|
||||
atexit.register(close)
|
||||
|
||||
tls.parseTlsFlash(read_tls_flash())
|
||||
tls.open()
|
||||
|
||||
upload_fwext()
|
||||
sensor.open()
|
||||
init_db()
|
||||
|
||||
# We must register atexit only after we opened usb device,
|
||||
# so that our atexit handler is called before pyusb's one and we can still talk to the device
|
||||
#
|
||||
# Don't attempt to autoreboot unless we finished initializing the sensor successfully.
|
||||
# Otherwise if there is a bug in initialization, we may end up in a loop constantly rebooting the device.
|
||||
atexit.register(close)
|
||||
|
||||
def open():
|
||||
usb.open()
|
||||
|
|
|
|||
|
|
@ -11,8 +11,9 @@ def machine_id_rec_value(b):
|
|||
b = b + b'\0' * (0x94 - len(b))
|
||||
return pack('<HH', 0x102, len(b)) + b # 0x102 = Machine ID/GUID?
|
||||
|
||||
# TODO: make this GUID unique!
|
||||
def init_db(machine_guid='e7260876-58db-4d27-8c40-8d13110d6a71'):
|
||||
stg=db.get_user_storage(name='StgWindsor')
|
||||
stg = db.get_user_storage(name='StgWindsor')
|
||||
if stg == None:
|
||||
print('Creating a new user storage object')
|
||||
db.new_user_storate()
|
||||
|
|
@ -21,7 +22,7 @@ def init_db(machine_guid='e7260876-58db-4d27-8c40-8d13110d6a71'):
|
|||
|
||||
if rc == []:
|
||||
print('Creating a host machine GUID record')
|
||||
stg=db.get_user_storage(name='StgWindsor')
|
||||
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()
|
||||
|
||||
|
|
|
|||
|
|
@ -85,14 +85,9 @@ def serialize_partition(p):
|
|||
b = b + b'\0'*4 + sha256(b).digest()
|
||||
return b
|
||||
|
||||
def partition_flash(layout, client_public):
|
||||
info = get_flash_info()
|
||||
|
||||
def partition_flash(info, layout, client_public):
|
||||
print('Detected Flash IC: %s, %d bytes' % (info.ic.name, info.ic.size))
|
||||
|
||||
if len(info.partitions) > 0:
|
||||
raise Exception('Flash is already partitioned')
|
||||
|
||||
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]) + get_partition_signature())
|
||||
|
|
@ -108,6 +103,14 @@ def partition_flash(layout, client_public):
|
|||
# ^ TODO - figure out what the rest of rsp means
|
||||
|
||||
def init_flash():
|
||||
info = get_flash_info()
|
||||
|
||||
if len(info.partitions) > 0:
|
||||
print('Flash has %d partitions.' % len(info.partitions))
|
||||
return
|
||||
else:
|
||||
print('Flash was not initialized yet. Formatting...')
|
||||
|
||||
assert_status(usb.cmd(reset_blob))
|
||||
|
||||
skey = ec.generate_private_key(ec.SECP256R1(), crypto_backend)
|
||||
|
|
@ -115,7 +118,7 @@ def init_flash():
|
|||
client_private = snums.private_value
|
||||
client_public = snums.public_numbers
|
||||
|
||||
partition_flash(flash_layout_hardcoded, client_public)
|
||||
partition_flash(info, flash_layout_hardcoded, client_public)
|
||||
|
||||
rsp=usb.cmd(unhex('01'))
|
||||
assert_status(rsp)
|
||||
|
|
@ -150,5 +153,6 @@ def init_flash():
|
|||
# Persist certs and keys on cert partition.
|
||||
write_flash(1, 0, tls.makeTlsFlash())
|
||||
|
||||
# Reboot
|
||||
# Reboot.
|
||||
# The device will disconnect and our service will be started by udev as soon as it is connected again.
|
||||
reboot()
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import os.path
|
|||
from .tls import tls
|
||||
from .usb import usb
|
||||
from .db import db, subtype_to_string
|
||||
from .flash import write_enable, flush_changes, read_flash, erase_flash, write_flash_all
|
||||
from .flash import write_enable, flush_changes, read_flash, erase_flash, write_flash_all, read_flash_all
|
||||
from time import sleep
|
||||
from struct import pack, unpack
|
||||
from .table_types import SensorTypeInfo, SensorCaptureProg
|
||||
|
|
@ -55,9 +55,12 @@ def write_hw_reg32(addr, val):
|
|||
rsp=tls.cmd(pack('<BLLB', 8, addr, val, 4))
|
||||
assert_status(rsp)
|
||||
|
||||
class RebootException(Exception):
|
||||
pass
|
||||
|
||||
def reboot():
|
||||
assert_status(tls.cmd(unhex('050200')))
|
||||
raise RebootException()
|
||||
|
||||
def factory_reset():
|
||||
assert_status(usb.cmd(reset_blob))
|
||||
|
|
@ -192,7 +195,7 @@ class CancelledException(Exception):
|
|||
class Sensor():
|
||||
calib_data=b''
|
||||
|
||||
def open(self, load_calib_data=True):
|
||||
def open(self):
|
||||
self.interrupt_cb = None
|
||||
self.device_info = identify_sensor()
|
||||
|
||||
|
|
@ -227,13 +230,7 @@ class Sensor():
|
|||
if 7 in factory_bits:
|
||||
self.factory_calib_data = factory_bits[7][4:]
|
||||
|
||||
if load_calib_data and os.path.isfile(calib_data_path):
|
||||
with open(calib_data_path, 'rb') as f:
|
||||
self.calib_data = f.read()
|
||||
print('Calibration data loaded from a file.')
|
||||
else:
|
||||
self.calib_data = b''
|
||||
print('Warning: no calibration data was loaded. Consider calibrating the sensor.')
|
||||
self.calibrate()
|
||||
|
||||
def save(self):
|
||||
with open(calib_data_path, 'wb') as f:
|
||||
|
|
@ -569,7 +566,43 @@ class Sensor():
|
|||
|
||||
write_flash_all(6, 0, clean_slate)
|
||||
|
||||
def check_clean_slate(self):
|
||||
start = read_flash(6, 0, 0x44)
|
||||
magic, l = unpack('<HH', start[:4])
|
||||
start = start[4:]
|
||||
|
||||
if magic != 0x5002:
|
||||
return False
|
||||
|
||||
hs, zeroes = start[0:0x20], start[0x20:0x40]
|
||||
|
||||
if zeroes != b'\0'*0x20:
|
||||
print('Unexpected contents in calibration flash partition')
|
||||
return False
|
||||
|
||||
img = read_flash_all(6, 0x44, l)
|
||||
if hs != sha256(img).digest():
|
||||
print('Calibration flash hash mismatch')
|
||||
print(hexlify(hs), hexlify(img))
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def calibrate(self):
|
||||
if os.path.isfile(calib_data_path):
|
||||
with open(calib_data_path, 'rb') as f:
|
||||
self.calib_data = f.read()
|
||||
print('Calibration data loaded from a file.')
|
||||
|
||||
if self.check_clean_slate():
|
||||
return
|
||||
else:
|
||||
print('No calibration data on the flash. Calibrating...')
|
||||
else:
|
||||
self.calib_data = b''
|
||||
print('No calibration data was loaded. Calibrating...')
|
||||
|
||||
for i in range(0, self.calibration_iterations):
|
||||
print('Calibration iteration %d...' % i)
|
||||
rsp = tls.cmd(self.build_cmd_02(CaptureMode.CALIBRATE))
|
||||
|
|
@ -586,7 +619,7 @@ class Sensor():
|
|||
clean_slate = pack('<H', len(clean_slate)) + clean_slate
|
||||
clean_slate = clean_slate + pack('<H', 0) # TODO: still don't know what this zero is for
|
||||
clean_slate = pack('<H', len(clean_slate)) + sha256(clean_slate).digest() + b'\0'*0x20 + clean_slate
|
||||
clean_slate = unhexlify('0250') + clean_slate
|
||||
clean_slate = pack('<H', 0x5002) + clean_slate
|
||||
|
||||
self.persist_clean_slate(clean_slate)
|
||||
self.save()
|
||||
|
|
|
|||
|
|
@ -23,6 +23,13 @@ def default_fwext_name():
|
|||
return '6_07f_lenovo_mis_qm.xpfwext'
|
||||
|
||||
def upload_fwext(fw_path=None):
|
||||
fwi=get_fw_info(2)
|
||||
if fwi != None:
|
||||
print('Detected firmware version %d.%d (%s))' % (fwi.major, fwi.minor, ctime(fwi.buildtime)))
|
||||
return
|
||||
else:
|
||||
print('No firmware detected. Uploading...')
|
||||
|
||||
# no idea what this is:
|
||||
write_hw_reg32(0x8000205c, 7)
|
||||
if read_hw_reg32(0x80002080) not in [2, 3]:
|
||||
|
|
@ -33,6 +40,7 @@ def upload_fwext(fw_path=None):
|
|||
# ^ TODO -- what is the real reason to detect HW at this stage?
|
||||
# just a guess: perhaps it is used to construct fwext filename
|
||||
|
||||
|
||||
default_name = firmware_home + '/' + default_fwext_name()
|
||||
if not fw_path:
|
||||
fw_path = default_name
|
||||
|
|
@ -46,10 +54,6 @@ def upload_fwext(fw_path=None):
|
|||
fwext=fwext[fwext.index(b'\x1a')+1:]
|
||||
fwext, signature = fwext[:-0x100], fwext[-0x100:]
|
||||
|
||||
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)
|
||||
|
|
@ -62,3 +66,4 @@ def upload_fwext(fw_path=None):
|
|||
|
||||
# Reboot
|
||||
reboot()
|
||||
raise Exception('Reboot')
|
||||
|
|
|
|||
Loading…
Reference in a new issue