Add typing information
This commit is contained in:
parent
ee6fc3cdd8
commit
844c5100ef
14 changed files with 162 additions and 146 deletions
|
|
@ -21,10 +21,9 @@ if __name__ == "__main__":
|
||||||
glow_end_scan()
|
glow_end_scan()
|
||||||
sleep(0.05)
|
sleep(0.05)
|
||||||
|
|
||||||
led_script = unhexlify(
|
led_script = unhexlify('39ff100000ff03000001ff002000000000ffff0000ffff0000ff03000001ff00'
|
||||||
'39ff100000ff03000001ff002000000000ffff0000ffff0000ff03000001ff00'
|
'200000000000000000ffff0000ff03000001ff002000000000ffff0000000000'
|
||||||
'200000000000000000ffff0000ff03000001ff002000000000ffff0000000000'
|
'0000000000000000000000000000000000000000000000000000000000000000'
|
||||||
'0000000000000000000000000000000000000000000000000000000000000000'
|
'0000000000000000000000000000000000000000000000000000000000')
|
||||||
'0000000000000000000000000000000000000000000000000000000000')
|
|
||||||
|
|
||||||
assert_status(tls.app(led_script))
|
assert_status(tls.app(led_script))
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,17 @@
|
||||||
from binascii import hexlify
|
from binascii import hexlify
|
||||||
|
from struct import pack, unpack
|
||||||
|
import typing
|
||||||
|
|
||||||
from .blobs import db_write_enable
|
from .blobs import db_write_enable
|
||||||
from .flash import call_cleanups
|
from .flash import call_cleanups
|
||||||
from .sid import *
|
from .sid import SidIdentity, sid_from_bytes
|
||||||
from .tls import tls
|
from .tls import tls
|
||||||
from .util import assert_status
|
from .util import assert_status
|
||||||
from .winbio_constants import finger_names
|
from .winbio_constants import finger_names
|
||||||
|
|
||||||
|
|
||||||
class UserStorage:
|
class UserStorage:
|
||||||
def __init__(self, dbid, name):
|
def __init__(self, dbid: int, name: str):
|
||||||
self.dbid = dbid
|
self.dbid = dbid
|
||||||
self.name = name
|
self.name = name
|
||||||
self.users = []
|
self.users = []
|
||||||
|
|
@ -20,10 +22,10 @@ class UserStorage:
|
||||||
|
|
||||||
|
|
||||||
class User:
|
class User:
|
||||||
def __init__(self, dbid, identity):
|
def __init__(self, dbid: int, identity: str):
|
||||||
self.dbid = dbid
|
self.dbid = dbid
|
||||||
self.identity = identity
|
self.identity = identity
|
||||||
self.fingers = []
|
self.fingers: typing.List[typing.Mapping[str, int]] = []
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '<User: dbid=%04x identity=%s fingers=%s>' % (self.dbid, repr(
|
return '<User: dbid=%04x identity=%s fingers=%s>' % (self.dbid, repr(
|
||||||
|
|
@ -35,7 +37,7 @@ def subtype_to_string(s: int):
|
||||||
return finger_name or 'Unknown'
|
return finger_name or 'Unknown'
|
||||||
|
|
||||||
|
|
||||||
def parse_user_storage(rsp):
|
def parse_user_storage(rsp: bytes):
|
||||||
rc, = unpack('<H', rsp[:2])
|
rc, = unpack('<H', rsp[:2])
|
||||||
|
|
||||||
if rc == 0x04b3:
|
if rc == 0x04b3:
|
||||||
|
|
@ -62,7 +64,7 @@ def parse_user_storage(rsp):
|
||||||
return storage
|
return storage
|
||||||
|
|
||||||
|
|
||||||
def parse_identity(b):
|
def parse_identity(b: bytes):
|
||||||
t, b = b[:4], b[4:]
|
t, b = b[:4], b[4:]
|
||||||
t, = unpack('<L', t)
|
t, = unpack('<L', t)
|
||||||
|
|
||||||
|
|
@ -127,7 +129,7 @@ class DbRecord:
|
||||||
|
|
||||||
class Db:
|
class Db:
|
||||||
class Info:
|
class Info:
|
||||||
def __init__(self, total, used, free, records, roots):
|
def __init__(self, total: int, used: int, free: int, records: int, roots):
|
||||||
self.total = total # partition size
|
self.total = total # partition size
|
||||||
self.used = used # used (not deleted)
|
self.used = used # used (not deleted)
|
||||||
self.free = free # unallocated space
|
self.free = free # unallocated space
|
||||||
|
|
@ -154,7 +156,7 @@ class Db:
|
||||||
rc = self.get_record_children(stg.dbid).children
|
rc = self.get_record_children(stg.dbid).children
|
||||||
return [i['dbid'] for i in rc if i['type'] == 8] # 8 == "data" type
|
return [i['dbid'] for i in rc if i['type'] == 8] # 8 == "data" type
|
||||||
|
|
||||||
def get_user(self, dbid):
|
def get_user(self, dbid: int):
|
||||||
return parse_user(tls.cmd(pack('<BHHH', 0x4a, dbid, 0, 0)))
|
return parse_user(tls.cmd(pack('<BHHH', 0x4a, dbid, 0, 0)))
|
||||||
|
|
||||||
def lookup_user(self, identity: str) -> typing.Optional[User]:
|
def lookup_user(self, identity: str) -> typing.Optional[User]:
|
||||||
|
|
@ -169,7 +171,7 @@ class Db:
|
||||||
else:
|
else:
|
||||||
return parse_user(rsp)
|
return parse_user(rsp)
|
||||||
|
|
||||||
def get_record_value(self, dbid):
|
def get_record_value(self, dbid: int):
|
||||||
rsp = tls.cmd(pack('<BH', 0x49, dbid))
|
rsp = tls.cmd(pack('<BH', 0x49, dbid))
|
||||||
assert_status(rsp)
|
assert_status(rsp)
|
||||||
|
|
||||||
|
|
@ -179,7 +181,7 @@ class Db:
|
||||||
|
|
||||||
return rec
|
return rec
|
||||||
|
|
||||||
def get_record_children(self, dbid):
|
def get_record_children(self, dbid: int):
|
||||||
rsp = tls.cmd(pack('<BH', 0x46, dbid))
|
rsp = tls.cmd(pack('<BH', 0x46, dbid))
|
||||||
assert_status(rsp)
|
assert_status(rsp)
|
||||||
|
|
||||||
|
|
@ -193,7 +195,7 @@ class Db:
|
||||||
|
|
||||||
return rec
|
return rec
|
||||||
|
|
||||||
def del_record(self, dbid):
|
def del_record(self, dbid: int):
|
||||||
assert_status(tls.cmd(pack('<BH', 0x48, dbid)))
|
assert_status(tls.cmd(pack('<BH', 0x48, dbid)))
|
||||||
|
|
||||||
def db_info(self):
|
def db_info(self):
|
||||||
|
|
@ -208,7 +210,7 @@ class Db:
|
||||||
|
|
||||||
return Db.Info(total, used, free, records, roots)
|
return Db.Info(total, used, free, records, roots)
|
||||||
|
|
||||||
def new_record(self, parent, typ, storage, data):
|
def new_record(self, parent: int, typ: int, storage: int, data: bytes):
|
||||||
self.db_info() # TODO check free space, compact the partition when out of storage
|
self.db_info() # TODO check free space, compact the partition when out of storage
|
||||||
assert_status(tls.cmd(db_write_enable))
|
assert_status(tls.cmd(db_write_enable))
|
||||||
try:
|
try:
|
||||||
|
|
@ -219,21 +221,21 @@ class Db:
|
||||||
call_cleanups()
|
call_cleanups()
|
||||||
return recid
|
return recid
|
||||||
|
|
||||||
def new_user(self, identity):
|
def new_user(self, identity: str):
|
||||||
data = identity_to_bytes(identity)
|
data = identity_to_bytes(identity)
|
||||||
|
|
||||||
stg = self.get_user_storage(name='StgWindsor')
|
stg = self.get_user_storage(name='StgWindsor')
|
||||||
rec = self.new_record(stg.dbid, 5, stg.dbid, data)
|
rec = self.new_record(stg.dbid, 5, stg.dbid, data)
|
||||||
return rec
|
return rec
|
||||||
|
|
||||||
def new_finger(self, userid, template):
|
def new_finger(self, userid: int, template: bytes):
|
||||||
stg = self.get_user_storage(name='StgWindsor')
|
stg = self.get_user_storage(name='StgWindsor')
|
||||||
# We ask to create an object of type 0xb,
|
# We ask to create an object of type 0xb,
|
||||||
# but because of the magical `db_write_enable` in the new_record() it ends up being 0x6
|
# but because of the magical `db_write_enable` in the new_record() it ends up being 0x6
|
||||||
rec = self.new_record(userid, 0xb, stg.dbid, template)
|
rec = self.new_record(userid, 0xb, stg.dbid, template)
|
||||||
return rec
|
return rec
|
||||||
|
|
||||||
def new_data(self, parent, data):
|
def new_data(self, parent: int, data: bytes):
|
||||||
stg = self.get_user_storage(name='StgWindsor')
|
stg = self.get_user_storage(name='StgWindsor')
|
||||||
data = pack('<HH', 1, len(data)) + data
|
data = pack('<HH', 1, len(data)) + data
|
||||||
rec = self.new_record(parent, 0x8, stg.dbid, data)
|
rec = self.new_record(parent, 0x8, stg.dbid, data)
|
||||||
|
|
|
||||||
|
|
@ -1,28 +1,18 @@
|
||||||
|
import typing
|
||||||
from struct import pack, unpack
|
from struct import pack, unpack
|
||||||
|
|
||||||
from .blobs import db_write_enable
|
from .blobs import db_write_enable
|
||||||
from .hw_tables import flash_ic_table_lookup
|
from .hw_tables import flash_ic_table_lookup, FlashIcInfo
|
||||||
from .tls import tls
|
from .tls import tls
|
||||||
from .util import assert_status, unhex
|
from .util import assert_status, unhex
|
||||||
|
|
||||||
|
|
||||||
class FlashInfo:
|
|
||||||
def __init__(self, ic, blocks, unknown0, blocksize, unknown1, partitions):
|
|
||||||
self.ic, self.blocks, self.unknown0, self.blocksize, self.unknown1, self.partitions = \
|
|
||||||
ic, blocks, unknown0, blocksize, unknown1, partitions
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return 'FlashInfo(%s, 0x%x, 0x%x, 0x%x, 0x%x, %s)' % (repr(
|
|
||||||
self.ic), self.blocks, self.unknown0, self.blocksize, self.unknown1,
|
|
||||||
repr(self.partitions))
|
|
||||||
|
|
||||||
|
|
||||||
# type 01 is for the firmware - written blocks are decrypted on the fly using fw key
|
# type 01 is for the firmware - written blocks are decrypted on the fly using fw key
|
||||||
# access lvl:
|
# access lvl:
|
||||||
# 2 -- write only
|
# 2 -- write only
|
||||||
# 7 -- can read/write even without TLS
|
# 7 -- can read/write even without TLS
|
||||||
class PartitionInfo:
|
class PartitionInfo:
|
||||||
def __init__(self, id, type, access_lvl, offset, size):
|
def __init__(self, id: int, type: int, access_lvl: int, offset: int, size: int):
|
||||||
self.id, self.type, self.access_lvl, self.offset, self.size = id, type, access_lvl, offset, size
|
self.id, self.type, self.access_lvl, self.offset, self.size = id, type, access_lvl, offset, size
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
|
@ -30,6 +20,22 @@ class PartitionInfo:
|
||||||
self.id, self.type, self.access_lvl, self.offset, self.size)
|
self.id, self.type, self.access_lvl, self.offset, self.size)
|
||||||
|
|
||||||
|
|
||||||
|
class FlashInfo:
|
||||||
|
def __init__(self, ic: FlashIcInfo, blocks: int, unknown0: int, blocksize: int, unknown1: int,
|
||||||
|
partitions: typing.Sequence[PartitionInfo]):
|
||||||
|
self.ic = ic
|
||||||
|
self.blocks = blocks
|
||||||
|
self.unknown0 = unknown0
|
||||||
|
self.blocksize = blocksize
|
||||||
|
self.unknown1 = unknown1
|
||||||
|
self.partitions = partitions
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return 'FlashInfo(%s, 0x%x, 0x%x, 0x%x, 0x%x, %s)' % (repr(
|
||||||
|
self.ic), self.blocks, self.unknown0, self.blocksize, self.unknown1,
|
||||||
|
repr(self.partitions))
|
||||||
|
|
||||||
|
|
||||||
def get_flash_info():
|
def get_flash_info():
|
||||||
rsp = tls.cmd(unhex('3e'))
|
rsp = tls.cmd(unhex('3e'))
|
||||||
assert_status(rsp)
|
assert_status(rsp)
|
||||||
|
|
@ -65,7 +71,7 @@ def get_flash_info():
|
||||||
# 0200 6637 0100 0c00 f0220200
|
# 0200 6637 0100 0c00 f0220200
|
||||||
# 0100 2556 0000 0100 60040000
|
# 0100 2556 0000 0100 60040000
|
||||||
class ModuleInfo:
|
class ModuleInfo:
|
||||||
def __init__(self, type, subtype, major, minor, size):
|
def __init__(self, type: int, subtype: int, major: int, minor: int, size: int):
|
||||||
self.type, self.subtype, self.major, self.minor, self.size = type, subtype, major, minor, size
|
self.type, self.subtype, self.major, self.minor, self.size = type, subtype, major, minor, size
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
|
@ -74,7 +80,7 @@ class ModuleInfo:
|
||||||
|
|
||||||
|
|
||||||
class FirmwareInfo:
|
class FirmwareInfo:
|
||||||
def __init__(self, major, minor, buildtime, modules):
|
def __init__(self, major: int, minor: int, buildtime: int, modules: typing.Sequence[ModuleInfo]):
|
||||||
self.major, self.minor, self.buildtime, self.modules = major, minor, buildtime, modules
|
self.major, self.minor, self.buildtime, self.modules = major, minor, buildtime, modules
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
|
@ -82,7 +88,7 @@ class FirmwareInfo:
|
||||||
repr(self.modules))
|
repr(self.modules))
|
||||||
|
|
||||||
|
|
||||||
def get_fw_info(partition):
|
def get_fw_info(partition: int):
|
||||||
rsp = tls.cmd(pack('<BB', 0x43, partition))
|
rsp = tls.cmd(pack('<BB', 0x43, partition))
|
||||||
|
|
||||||
# don't want to throw exception here - it is normal not to have FW when we're about to upload it
|
# don't want to throw exception here - it is normal not to have FW when we're about to upload it
|
||||||
|
|
@ -113,7 +119,7 @@ def call_cleanups():
|
||||||
assert_status(rsp)
|
assert_status(rsp)
|
||||||
|
|
||||||
|
|
||||||
def erase_flash(partition):
|
def erase_flash(partition: int):
|
||||||
assert_status(tls.cmd(db_write_enable))
|
assert_status(tls.cmd(db_write_enable))
|
||||||
try:
|
try:
|
||||||
assert_status(tls.cmd(pack('<BB', 0x3f, partition)))
|
assert_status(tls.cmd(pack('<BB', 0x3f, partition)))
|
||||||
|
|
@ -121,7 +127,7 @@ def erase_flash(partition):
|
||||||
call_cleanups()
|
call_cleanups()
|
||||||
|
|
||||||
|
|
||||||
def read_flash(partition, addr, size):
|
def read_flash(partition: int, addr: int, size: int) -> bytes:
|
||||||
cmd = pack('<BBBHLL', 0x40, partition, 1, 0, addr, size)
|
cmd = pack('<BBBHLL', 0x40, partition, 1, 0, addr, size)
|
||||||
rsp = tls.cmd(cmd)
|
rsp = tls.cmd(cmd)
|
||||||
assert_status(rsp)
|
assert_status(rsp)
|
||||||
|
|
@ -130,7 +136,7 @@ def read_flash(partition, addr, size):
|
||||||
return rsp[8:8 + sz]
|
return rsp[8:8 + sz]
|
||||||
|
|
||||||
|
|
||||||
def write_flash(partition, addr, buf):
|
def write_flash(partition: int, addr: int, buf: bytes):
|
||||||
tls.cmd(db_write_enable)
|
tls.cmd(db_write_enable)
|
||||||
cmd = pack('<BBBHLL', 0x41, partition, 1, 0, addr, len(buf)) + buf
|
cmd = pack('<BBBHLL', 0x41, partition, 1, 0, addr, len(buf)) + buf
|
||||||
try:
|
try:
|
||||||
|
|
@ -140,7 +146,7 @@ def write_flash(partition, addr, buf):
|
||||||
call_cleanups()
|
call_cleanups()
|
||||||
|
|
||||||
|
|
||||||
def write_flash_all(partition, ptr, buf):
|
def write_flash_all(partition: int, ptr: int, buf: bytes):
|
||||||
bs = 0x1000
|
bs = 0x1000
|
||||||
while len(buf) > 0:
|
while len(buf) > 0:
|
||||||
chunk, buf = buf[:bs], buf[bs:]
|
chunk, buf = buf[:bs], buf[bs:]
|
||||||
|
|
@ -148,13 +154,13 @@ def write_flash_all(partition, ptr, buf):
|
||||||
ptr += len(chunk)
|
ptr += len(chunk)
|
||||||
|
|
||||||
|
|
||||||
def read_flash_all(partition, start, size):
|
def read_flash_all(partition: int, start: int, size: int):
|
||||||
bs = 0x1000
|
bs = 0x1000
|
||||||
blocks = [read_flash(partition, addr, bs) for addr in range(start, start + size, bs)]
|
blocks = [read_flash(partition, addr, bs) for addr in range(start, start + size, bs)]
|
||||||
return b''.join(blocks)[:size]
|
return b''.join(blocks)[:size]
|
||||||
|
|
||||||
|
|
||||||
def write_fw_signature(partition, signature):
|
def write_fw_signature(partition: int, signature: bytes):
|
||||||
rsp = tls.cmd(pack('<BBxH', 0x42, partition, len(signature)) + signature)
|
rsp = tls.cmd(pack('<BBxH', 0x42, partition, len(signature)) + signature)
|
||||||
assert_status(rsp)
|
assert_status(rsp)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
class DeviceInfo:
|
class DeviceInfo:
|
||||||
def __init__(self, major, type, version, version_mask, name):
|
def __init__(self, major: int, type: int, version: int, version_mask: int, name: str):
|
||||||
self.major, self.type, self.version, self.version_mask, self.name = major, type, version, version_mask, name
|
self.major, self.type, self.version, self.version_mask, self.name = major, type, version, version_mask, name
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
|
@ -428,7 +428,7 @@ dev_info_table = [
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def dev_info_lookup(major, ver):
|
def dev_info_lookup(major: int, ver: int):
|
||||||
fuzzy_match = None
|
fuzzy_match = None
|
||||||
|
|
||||||
for i in dev_info_table:
|
for i in dev_info_table:
|
||||||
|
|
@ -446,8 +446,8 @@ def dev_info_lookup(major, ver):
|
||||||
|
|
||||||
|
|
||||||
class FlashIcInfo:
|
class FlashIcInfo:
|
||||||
def __init__(self, name, size, f18, jid0, jid1, f1b, f1c, f1e, secror_size, sector_erase_cmd,
|
def __init__(self, name: str, size: int, f18: int, jid0: int, jid1: int, f1b: int, f1c: int,
|
||||||
f25, f26):
|
f1e: int, secror_size: int, sector_erase_cmd: int, f25: int, f26: int):
|
||||||
self.name, self.size, self.f18, self.jid0, self.jid1, self.f1b, self.f1c, self.f1e, self.secror_size, self.sector_erase_cmd, self.f25, self.f26 = name, size, f18, jid0, jid1, f1b, f1c, f1e, secror_size, sector_erase_cmd, f25, f26
|
self.name, self.size, self.f18, self.jid0, self.jid1, self.f1b, self.f1c, self.f1e, self.secror_size, self.sector_erase_cmd, self.f25, self.f26 = name, size, f18, jid0, jid1, f1b, f1c, f1e, secror_size, sector_erase_cmd, f25, f26
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
|
@ -480,7 +480,7 @@ flash_ic_table = [
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def flash_ic_table_lookup(jedec_id0, jedec_id1, size):
|
def flash_ic_table_lookup(jedec_id0: int, jedec_id1: int, size: int):
|
||||||
for i in flash_ic_table:
|
for i in flash_ic_table:
|
||||||
if i.jid0 == jedec_id0 and i.jid1 == jedec_id1 and i.size == size:
|
if i.jid0 == jedec_id0 and i.jid1 == jedec_id1 and i.size == size:
|
||||||
return i
|
return i
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,6 @@ def open():
|
||||||
open_common()
|
open_common()
|
||||||
|
|
||||||
|
|
||||||
def open_devpath(busnum, address):
|
def open_devpath(busnum: int, address: int):
|
||||||
usb.open_devpath(busnum, address)
|
usb.open_devpath(busnum, address)
|
||||||
open_common()
|
open_common()
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ from struct import pack, unpack
|
||||||
from .db import db
|
from .db import db
|
||||||
|
|
||||||
|
|
||||||
def machine_id_rec_value(b):
|
def machine_id_rec_value(b: str):
|
||||||
b = b.encode('utf-16le')
|
b = b.encode('utf-16le')
|
||||||
b = b + b'\0' * (0x94 - len(b))
|
b = b + b'\0' * (0x94 - len(b))
|
||||||
return pack('<HH', 0x102, len(b)) + b # 0x102 = Machine ID/GUID?
|
return pack('<HH', 0x102, len(b)) + b # 0x102 = Machine ID/GUID?
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import hmac
|
import hmac
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
import typing
|
||||||
from binascii import unhexlify
|
from binascii import unhexlify
|
||||||
from hashlib import sha256
|
from hashlib import sha256
|
||||||
from struct import pack, unpack
|
from struct import pack, unpack
|
||||||
|
|
@ -11,7 +12,8 @@ from cryptography.hazmat.primitives.asymmetric import ec
|
||||||
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
||||||
|
|
||||||
from .blobs import reset_blob
|
from .blobs import reset_blob
|
||||||
from .flash import write_flash, erase_flash, call_cleanups, PartitionInfo, get_flash_info
|
from .flash import write_flash, erase_flash, call_cleanups, PartitionInfo, get_flash_info, FlashInfo
|
||||||
|
from .hw_tables import FlashIcInfo
|
||||||
from .sensor import reboot, RomInfo
|
from .sensor import reboot, RomInfo
|
||||||
from .tls import tls, hs_key, crt_hardcoded
|
from .tls import tls, hs_key, crt_hardcoded
|
||||||
from .usb import usb
|
from .usb import usb
|
||||||
|
|
@ -45,7 +47,7 @@ def get_partition_signature():
|
||||||
return partition_signature
|
return partition_signature
|
||||||
|
|
||||||
|
|
||||||
def with_hdr(id, buf):
|
def with_hdr(id: int, buf: bytes):
|
||||||
return pack('<HH', id, len(buf)) + buf
|
return pack('<HH', id, len(buf)) + buf
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -78,17 +80,17 @@ def make_cert(client_public):
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
|
|
||||||
def serialize_flash_params(ic):
|
def serialize_flash_params(ic: FlashIcInfo):
|
||||||
return pack('<LLxxBx', ic.size, ic.secror_size, ic.sector_erase_cmd)
|
return pack('<LLxxBx', ic.size, ic.secror_size, ic.sector_erase_cmd)
|
||||||
|
|
||||||
|
|
||||||
def serialize_partition(p):
|
def serialize_partition(p: PartitionInfo):
|
||||||
b = pack('<BBHLL', p.id, p.type, p.access_lvl, p.offset, p.size)
|
b = pack('<BBHLL', p.id, p.type, p.access_lvl, p.offset, p.size)
|
||||||
b = b + b'\0' * 4 + sha256(b).digest()
|
b = b + b'\0' * 4 + sha256(b).digest()
|
||||||
return b
|
return b
|
||||||
|
|
||||||
|
|
||||||
def partition_flash(info, layout, client_public):
|
def partition_flash(info: FlashInfo, layout: typing.List[PartitionInfo], client_public):
|
||||||
logging.info('Detected Flash IC: %s, %d bytes' % (info.ic.name, info.ic.size))
|
logging.info('Detected Flash IC: %s, %d bytes' % (info.ic.name, info.ic.size))
|
||||||
|
|
||||||
cmd = unhex('4f 0000 0000')
|
cmd = unhex('4f 0000 0000')
|
||||||
|
|
|
||||||
|
|
@ -60,14 +60,14 @@ def get_prg_status2():
|
||||||
return tls.app(unhexlify('5100200000'))
|
return tls.app(unhexlify('5100200000'))
|
||||||
|
|
||||||
|
|
||||||
def read_hw_reg32(addr):
|
def read_hw_reg32(addr: int):
|
||||||
rsp = tls.cmd(pack('<BLB', 7, addr, 4))
|
rsp = tls.cmd(pack('<BLB', 7, addr, 4))
|
||||||
assert_status(rsp)
|
assert_status(rsp)
|
||||||
rsp, = unpack('<L', rsp[2:])
|
rsp, = unpack('<L', rsp[2:])
|
||||||
return rsp
|
return rsp
|
||||||
|
|
||||||
|
|
||||||
def write_hw_reg32(addr, val):
|
def write_hw_reg32(addr: int, val: int):
|
||||||
rsp = tls.cmd(pack('<BLLB', 8, addr, val, 4))
|
rsp = tls.cmd(pack('<BLLB', 8, addr, val, 4))
|
||||||
assert_status(rsp)
|
assert_status(rsp)
|
||||||
|
|
||||||
|
|
@ -95,7 +95,7 @@ class RomInfo:
|
||||||
rsp = rsp[2:]
|
rsp = rsp[2:]
|
||||||
return cls(*unpack('<LLBBxBxxxB', rsp[0:0x10]))
|
return cls(*unpack('<LLBBxBxxxB', rsp[0:0x10]))
|
||||||
|
|
||||||
def __init__(self, timestamp, build, major, minor, product, u1):
|
def __init__(self, timestamp: int, build: int, major: int, minor: int, product: int, u1: int):
|
||||||
self.timestamp, self.build, self.major, self.minor, self.product, self.u1 = timestamp, build, major, minor, product, u1
|
self.timestamp, self.build, self.major, self.minor, self.product, self.u1 = timestamp, build, major, minor, product, u1
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
|
@ -124,7 +124,7 @@ def identify_sensor():
|
||||||
# d0000000 9400 0e00 0700 0080 07000000 2b23203c2d182e1e30182e1c321d341d341e321c301e1e241e201f201d1c321a301e1c211e21341f1e202024201f
|
# d0000000 9400 0e00 0700 0080 07000000 2b23203c2d182e1e30182e1c321d341d341e321c301e1e241e201f201d1c321a301e1c211e21341f1e202024201f
|
||||||
# 6c010000 1400 0e00 0f00 0080 05550007 7701002805720000080100020811e107
|
# 6c010000 1400 0e00 0f00 0080 05550007 7701002805720000080100020811e107
|
||||||
# 88010000 0c00 0e00 1200 0080 07000000 7002 7800 7002 7800
|
# 88010000 0c00 0e00 1200 0080 07000000 7002 7800 7002 7800
|
||||||
def get_factory_bits(tag):
|
def get_factory_bits(tag: int):
|
||||||
rsp = tls.cmd(pack('<B H HL', 0x6f, tag, 0, 0))
|
rsp = tls.cmd(pack('<B H HL', 0x6f, tag, 0, 0))
|
||||||
assert_status(rsp)
|
assert_status(rsp)
|
||||||
rsp = rsp[2:]
|
rsp = rsp[2:]
|
||||||
|
|
@ -177,15 +177,16 @@ def bitpack(b):
|
||||||
|
|
||||||
|
|
||||||
class Line:
|
class Line:
|
||||||
mask = None
|
def __init__(self):
|
||||||
flags = None
|
self.mask: typing.Optional[int] = None
|
||||||
data = None
|
self.flags: typing.Optional[int] = None
|
||||||
v0 = 0
|
self.data: typing.Optional[bytes] = None
|
||||||
v1 = 0
|
self.v0 = 0
|
||||||
v2 = 0
|
self.v1 = 0
|
||||||
|
self.v2 = 0
|
||||||
|
|
||||||
|
|
||||||
def clip(x):
|
def clip(x: int):
|
||||||
if x < -128:
|
if x < -128:
|
||||||
x = -128
|
x = -128
|
||||||
|
|
||||||
|
|
@ -195,19 +196,19 @@ def clip(x):
|
||||||
return x & 0xff
|
return x & 0xff
|
||||||
|
|
||||||
|
|
||||||
def scale(x):
|
def scale(x: int):
|
||||||
x -= 0x80
|
x -= 0x80
|
||||||
x = int(x * 10 / 0x22) # TODO: scaling factor depends on a device
|
x = int(x * 10 / 0x22) # TODO: scaling factor depends on a device
|
||||||
return clip(x)
|
return clip(x)
|
||||||
|
|
||||||
|
|
||||||
def add(l, r):
|
def add(l: int, r: int):
|
||||||
# Make signed
|
# Make signed
|
||||||
l, r = unpack('bb', pack('BB', l, r))
|
l, r = unpack('bb', pack('BB', l, r))
|
||||||
return clip(l + r)
|
return clip(l + r)
|
||||||
|
|
||||||
|
|
||||||
def chunks(b, l):
|
def chunks(b: bytes, l: int):
|
||||||
return [b[i:i + l] for i in range(0, len(b), l)]
|
return [b[i:i + l] for i in range(0, len(b), l)]
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -266,7 +267,7 @@ class Sensor:
|
||||||
|
|
||||||
# This is the exact logic from the DLL.
|
# This is the exact logic from the DLL.
|
||||||
# If it looks broken that was probably intended.
|
# If it looks broken that was probably intended.
|
||||||
def patch_timeslot_table(self, b, inc_address, mult):
|
def patch_timeslot_table(self, b: bytes, inc_address: bool, mult: int):
|
||||||
b = bytearray(b)
|
b = bytearray(b)
|
||||||
i = 0
|
i = 0
|
||||||
while i + 3 < len(b):
|
while i + 3 < len(b):
|
||||||
|
|
@ -290,7 +291,7 @@ class Sensor:
|
||||||
|
|
||||||
return bytes(b)
|
return bytes(b)
|
||||||
|
|
||||||
def patch_timeslot_again(self, b):
|
def patch_timeslot_again(self, b: bytes):
|
||||||
b = bytearray(b)
|
b = bytearray(b)
|
||||||
|
|
||||||
pc = 0
|
pc = 0
|
||||||
|
|
@ -336,7 +337,7 @@ class Sensor:
|
||||||
|
|
||||||
return bytes(b)
|
return bytes(b)
|
||||||
|
|
||||||
def average(self, raw_calib_data):
|
def average(self, raw_calib_data: bytes):
|
||||||
frame_size = self.lines_per_frame * self.bytes_per_line
|
frame_size = self.lines_per_frame * self.bytes_per_line
|
||||||
interleave_lines = self.lines_per_frame // self.type_info.lines_per_calibration_data # 2, TODO: algo is quite different when it is 1
|
interleave_lines = self.lines_per_frame // self.type_info.lines_per_calibration_data # 2, TODO: algo is quite different when it is 1
|
||||||
input_frames = self.calibration_frames
|
input_frames = self.calibration_frames
|
||||||
|
|
@ -371,7 +372,7 @@ class Sensor:
|
||||||
|
|
||||||
return frame
|
return frame
|
||||||
|
|
||||||
def process_calibration_results(self, cooked_data):
|
def process_calibration_results(self, cooked_data: bytes):
|
||||||
frame = chunks(cooked_data, self.bytes_per_line)
|
frame = chunks(cooked_data, self.bytes_per_line)
|
||||||
|
|
||||||
# apply scaling factors
|
# apply scaling factors
|
||||||
|
|
@ -407,7 +408,7 @@ class Sensor:
|
||||||
|
|
||||||
return key_line
|
return key_line
|
||||||
|
|
||||||
def line_update_type_1(self, mode, chunks):
|
def line_update_type_1(self, mode: CaptureMode, chunks: typing.List[typing.List[typing.Union[int, bytes]]]):
|
||||||
for c in chunks:
|
for c in chunks:
|
||||||
# Timeslot Table 2D
|
# Timeslot Table 2D
|
||||||
if c[0] == 0x34:
|
if c[0] == 0x34:
|
||||||
|
|
@ -451,7 +452,7 @@ class Sensor:
|
||||||
# ---------------- Interleave ---------------
|
# ---------------- Interleave ---------------
|
||||||
chunks += [[0x44, pack('<L', 1)]]
|
chunks += [[0x44, pack('<L', 1)]]
|
||||||
|
|
||||||
lines = []
|
lines: typing.List[Line] = []
|
||||||
cnt = 2 # TODO figure out why 2
|
cnt = 2 # TODO figure out why 2
|
||||||
|
|
||||||
l = Line()
|
l = Line()
|
||||||
|
|
@ -510,7 +511,7 @@ class Sensor:
|
||||||
|
|
||||||
return chunks
|
return chunks
|
||||||
|
|
||||||
def line_update_type_2(self, mode, chunks):
|
def line_update_type_2(self, mode: CaptureMode, chunks: typing.List[typing.List[typing.Union[int, bytes]]]):
|
||||||
for c in chunks:
|
for c in chunks:
|
||||||
# patch the 2D params.
|
# patch the 2D params.
|
||||||
# The following is only needed on some rom versions below 6.5 as reported by cmd_01
|
# The following is only needed on some rom versions below 6.5 as reported by cmd_01
|
||||||
|
|
@ -597,7 +598,7 @@ class Sensor:
|
||||||
|
|
||||||
return chunks
|
return chunks
|
||||||
|
|
||||||
def build_cmd_02(self, mode):
|
def build_cmd_02(self, mode: CaptureMode):
|
||||||
chunks = list(prg.split_chunks(self.hardcoded_prog))
|
chunks = list(prg.split_chunks(self.hardcoded_prog))
|
||||||
|
|
||||||
if self.rom_info.product != 0x30:
|
if self.rom_info.product != 0x30:
|
||||||
|
|
@ -615,7 +616,7 @@ class Sensor:
|
||||||
|
|
||||||
return pack('<BHH', 2, self.bytes_per_line, req_lines) + prg.merge_chunks(chunks)
|
return pack('<BHH', 2, self.bytes_per_line, req_lines) + prg.merge_chunks(chunks)
|
||||||
|
|
||||||
def persist_clean_slate(self, clean_slate):
|
def persist_clean_slate(self, clean_slate: bytes):
|
||||||
start = read_flash(6, 0, 0x44)
|
start = read_flash(6, 0, 0x44)
|
||||||
|
|
||||||
if start != b'\xff' * 0x44:
|
if start != b'\xff' * 0x44:
|
||||||
|
|
@ -688,7 +689,7 @@ class Sensor:
|
||||||
def cancel(self):
|
def cancel(self):
|
||||||
usb.cancel = True
|
usb.cancel = True
|
||||||
|
|
||||||
def capture(self, mode):
|
def capture(self, mode: CaptureMode) -> typing.Tuple[int, int, int, int]:
|
||||||
try:
|
try:
|
||||||
assert_status(tls.app(self.build_cmd_02(mode)))
|
assert_status(tls.app(self.build_cmd_02(mode)))
|
||||||
|
|
||||||
|
|
@ -733,7 +734,7 @@ class Sensor:
|
||||||
finally:
|
finally:
|
||||||
tls.app(unhexlify('04')) # capture stop if still running, cleanup
|
tls.app(unhexlify('04')) # capture stop if still running, cleanup
|
||||||
|
|
||||||
def enrollment_update_start(self, key):
|
def enrollment_update_start(self, key: int) -> int:
|
||||||
rsp = tls.app(pack('<BLL', 0x68, key, 0))
|
rsp = tls.app(pack('<BLL', 0x68, key, 0))
|
||||||
assert_status(rsp)
|
assert_status(rsp)
|
||||||
new_key, = unpack('<L', rsp[2:])
|
new_key, = unpack('<L', rsp[2:])
|
||||||
|
|
@ -749,7 +750,7 @@ class Sensor:
|
||||||
assert_status(tls.app(pack('<BL', 0x69, 0)))
|
assert_status(tls.app(pack('<BL', 0x69, 0)))
|
||||||
|
|
||||||
# Generates interrupt
|
# Generates interrupt
|
||||||
def enrollment_update(self, prev):
|
def enrollment_update(self, prev: bytes):
|
||||||
write_enable()
|
write_enable()
|
||||||
try:
|
try:
|
||||||
rsp = tls.app(b'\x6b' + prev)
|
rsp = tls.app(b'\x6b' + prev)
|
||||||
|
|
@ -759,7 +760,7 @@ class Sensor:
|
||||||
|
|
||||||
return rsp[2:]
|
return rsp[2:]
|
||||||
|
|
||||||
def append_new_image(self, prev):
|
def append_new_image(self, prev: bytes):
|
||||||
self.enrollment_update(prev)
|
self.enrollment_update(prev)
|
||||||
|
|
||||||
usb.wait_int()
|
usb.wait_int()
|
||||||
|
|
@ -790,7 +791,7 @@ class Sensor:
|
||||||
|
|
||||||
return header, template, tid
|
return header, template, tid
|
||||||
|
|
||||||
def make_finger_data(self, subtype, template, tid):
|
def make_finger_data(self, subtype: int, template: bytes, tid: bytes):
|
||||||
template = pack('<HH', 1, len(template)) + template
|
template = pack('<HH', 1, len(template)) + template
|
||||||
tid = pack('<HH', 2, len(tid)) + tid
|
tid = pack('<HH', 2, len(tid)) + tid
|
||||||
|
|
||||||
|
|
@ -804,7 +805,7 @@ class Sensor:
|
||||||
# TODO: Better typing information needed.
|
# TODO: Better typing information needed.
|
||||||
def enroll(self, identity: SidIdentity, subtype: int,
|
def enroll(self, identity: SidIdentity, subtype: int,
|
||||||
update_cb: typing.Callable[[typing.Any, typing.Optional[Exception]], None]):
|
update_cb: typing.Callable[[typing.Any, typing.Optional[Exception]], None]):
|
||||||
def do_create_finger(final_template, tid):
|
def do_create_finger(final_template: bytes, tid: bytes):
|
||||||
tinfo = self.make_finger_data(subtype, final_template, tid)
|
tinfo = self.make_finger_data(subtype, final_template, tid)
|
||||||
|
|
||||||
usr = db.lookup_user(identity)
|
usr = db.lookup_user(identity)
|
||||||
|
|
@ -849,7 +850,7 @@ class Sensor:
|
||||||
self.enrollment_update_end() # done twice for some reason
|
self.enrollment_update_end() # done twice for some reason
|
||||||
return do_create_finger(template, tid)
|
return do_create_finger(template, tid)
|
||||||
|
|
||||||
def parse_dict(self, x):
|
def parse_dict(self, x: bytes):
|
||||||
rc = {}
|
rc = {}
|
||||||
|
|
||||||
while len(x) > 0:
|
while len(x) > 0:
|
||||||
|
|
@ -890,7 +891,7 @@ class Sensor:
|
||||||
# cleanup, ignore any errors
|
# cleanup, ignore any errors
|
||||||
tls.app(unhexlify('6200000000'))
|
tls.app(unhexlify('6200000000'))
|
||||||
|
|
||||||
def identify(self, update_cb):
|
def identify(self, update_cb: typing.Callable[[Exception], None]):
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
glow_start_scan()
|
glow_start_scan()
|
||||||
|
|
@ -908,7 +909,7 @@ class Sensor:
|
||||||
|
|
||||||
return self.match_finger()
|
return self.match_finger()
|
||||||
|
|
||||||
def get_finger_blobs(self, usrid, subtype):
|
def get_finger_blobs(self, usrid: int, subtype: int):
|
||||||
usr = db.get_user(usrid)
|
usr = db.get_user(usrid)
|
||||||
fingerids = [f['dbid'] for f in usr.fingers if f['subtype'] == subtype]
|
fingerids = [f['dbid'] for f in usr.fingers if f['subtype'] == subtype]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,20 @@
|
||||||
|
import typing
|
||||||
from binascii import hexlify, unhexlify
|
from binascii import hexlify, unhexlify
|
||||||
|
|
||||||
|
|
||||||
class SensorTypeInfo:
|
class SensorTypeInfo:
|
||||||
table = []
|
table: typing.List["SensorTypeInfo"] = []
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_by_type(cls, sensor_type):
|
def get_by_type(cls, sensor_type: int) -> typing.Optional["SensorTypeInfo"]:
|
||||||
|
# noinspection PyUnresolvedReferences
|
||||||
from . import generated_tables
|
from . import generated_tables
|
||||||
for i in cls.table:
|
for i in cls.table:
|
||||||
if i.sensor_type == sensor_type:
|
if i.sensor_type == sensor_type:
|
||||||
return i
|
return i
|
||||||
|
|
||||||
def __init__(self, sensor_type, bytes_per_line, repeat_multiplier, lines_per_calibration_data,
|
def __init__(self, sensor_type: int, bytes_per_line: int, repeat_multiplier: int, lines_per_calibration_data: int,
|
||||||
line_width, calibration_blob):
|
line_width: int, calibration_blob: str):
|
||||||
self.sensor_type = sensor_type
|
self.sensor_type = sensor_type
|
||||||
self.repeat_multiplier = repeat_multiplier
|
self.repeat_multiplier = repeat_multiplier
|
||||||
self.lines_per_calibration_data = lines_per_calibration_data
|
self.lines_per_calibration_data = lines_per_calibration_data
|
||||||
|
|
@ -50,10 +52,11 @@ def metric(i, rominfo):
|
||||||
|
|
||||||
|
|
||||||
class SensorCaptureProg:
|
class SensorCaptureProg:
|
||||||
table = []
|
table: typing.List["SensorCaptureProg"] = []
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get(cls, rominfo, sensor_type, a0, a1):
|
def get(cls, rominfo, sensor_type: int, a0: int, a1: int):
|
||||||
|
# noinspection PyUnresolvedReferences
|
||||||
from . import generated_tables
|
from . import generated_tables
|
||||||
|
|
||||||
maximum = 0
|
maximum = 0
|
||||||
|
|
@ -80,7 +83,8 @@ class SensorCaptureProg:
|
||||||
if found is not None:
|
if found is not None:
|
||||||
return b''.join(found.blobs)
|
return b''.join(found.blobs)
|
||||||
|
|
||||||
def __init__(self, major, minor, build, u1, dev_type, a0, a1, blobs):
|
def __init__(self, major: int, minor: int, build: int, u1: int, dev_type: int, a0: int, a1: int,
|
||||||
|
blobs: typing.Sequence[str]):
|
||||||
self.major = major
|
self.major = major
|
||||||
self.minor = minor
|
self.minor = minor
|
||||||
self.build = build
|
self.build = build
|
||||||
|
|
@ -88,8 +92,7 @@ class SensorCaptureProg:
|
||||||
self.dev_type = dev_type
|
self.dev_type = dev_type
|
||||||
self.a0 = a0
|
self.a0 = a0
|
||||||
self.a1 = a1
|
self.a1 = a1
|
||||||
blobs = [unhexlify(b) for b in blobs]
|
self.blobs = [unhexlify(b) for b in blobs]
|
||||||
self.blobs = blobs
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
blobs = [hexlify(b).decode() for b in self.blobs]
|
blobs = [hexlify(b).decode() for b in self.blobs]
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
from binascii import hexlify
|
from binascii import hexlify
|
||||||
from struct import unpack, pack
|
from struct import unpack, pack
|
||||||
|
import typing
|
||||||
|
|
||||||
codes = {}
|
codes = {}
|
||||||
codes[0x0] = "No Operation"
|
codes[0x0] = "No Operation"
|
||||||
|
|
@ -98,7 +99,7 @@ insn_to_string = [
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def decode_insn(b):
|
def decode_insn(b: bytes):
|
||||||
if b[0] == 0:
|
if b[0] == 0:
|
||||||
return 0, 1
|
return 0, 1
|
||||||
elif b[0] == 1:
|
elif b[0] == 1:
|
||||||
|
|
@ -135,7 +136,7 @@ def decode_insn(b):
|
||||||
raise Exception('Unhandled instruction %02x' % b)
|
raise Exception('Unhandled instruction %02x' % b)
|
||||||
|
|
||||||
|
|
||||||
def disassm_timeslot_table(b, off):
|
def disassm_timeslot_table(b: bytes, off: int):
|
||||||
pc = off
|
pc = off
|
||||||
while len(b) > 0:
|
while len(b) > 0:
|
||||||
op, sz, *operands = decode_insn(b)
|
op, sz, *operands = decode_insn(b)
|
||||||
|
|
@ -147,7 +148,7 @@ def disassm_timeslot_table(b, off):
|
||||||
pc += sz
|
pc += sz
|
||||||
|
|
||||||
|
|
||||||
def find_nth_insn(b, opcode, n):
|
def find_nth_insn(b: bytes, opcode: int, n: int):
|
||||||
pc = 0
|
pc = 0
|
||||||
while len(b) > 0:
|
while len(b) > 0:
|
||||||
op, sz, *_ = decode_insn(b)
|
op, sz, *_ = decode_insn(b)
|
||||||
|
|
@ -164,7 +165,7 @@ def find_nth_insn(b, opcode, n):
|
||||||
pc += sz
|
pc += sz
|
||||||
|
|
||||||
|
|
||||||
def find_nth_regwrite(b, reg_addr, n):
|
def find_nth_regwrite(b: bytes, reg_addr: int, n: int):
|
||||||
pc = 0
|
pc = 0
|
||||||
while len(b) > 0:
|
while len(b) > 0:
|
||||||
op, sz, *operands = decode_insn(b)
|
op, sz, *operands = decode_insn(b)
|
||||||
|
|
@ -183,20 +184,20 @@ def find_nth_regwrite(b, reg_addr, n):
|
||||||
pc += sz
|
pc += sz
|
||||||
|
|
||||||
|
|
||||||
def split_chunks(b):
|
def split_chunks(b: bytes) -> typing.Generator[typing.List[typing.Union[int, bytes]], None, None]:
|
||||||
while len(b) > 0:
|
while len(b) > 0:
|
||||||
(typ, sz), b = unpack('<HH', b[:4]), b[4:]
|
(typ, sz), b = unpack('<HH', b[:4]), b[4:]
|
||||||
p, b = b[:sz], b[sz:]
|
p, b = b[:sz], b[sz:]
|
||||||
yield [typ, p]
|
yield [typ, p]
|
||||||
|
|
||||||
|
|
||||||
def merge_chunks(cs):
|
def merge_chunks(cs: typing.List[typing.List[typing.Union[int, bytes]]]):
|
||||||
return b''.join([pack('<HH', key, len(val)) + val for key, val in cs])
|
return b''.join([pack('<HH', key, len(val)) + val for key, val in cs])
|
||||||
|
|
||||||
|
|
||||||
def dump_all(b: bytes):
|
def dump_all(b: bytes):
|
||||||
ts = None # type: typing.Optional[bytes]
|
ts: typing.Optional[bytes] = None
|
||||||
ts_off = None # type: typing.Optional[int]
|
ts_off: typing.Optional[int] = None
|
||||||
while len(b) > 0:
|
while len(b) > 0:
|
||||||
(typ, sz), b = unpack('<HH', b[:4]), b[4:]
|
(typ, sz), b = unpack('<HH', b[:4]), b[4:]
|
||||||
p, b = b[:sz], b[sz:]
|
p, b = b[:sz], b[sz:]
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import hmac
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import pickle
|
import pickle
|
||||||
|
import typing
|
||||||
from binascii import hexlify, unhexlify
|
from binascii import hexlify, unhexlify
|
||||||
from hashlib import sha256
|
from hashlib import sha256
|
||||||
from struct import pack, unpack
|
from struct import pack, unpack
|
||||||
|
|
@ -12,7 +13,7 @@ from cryptography.hazmat.primitives.asymmetric import ec
|
||||||
from cryptography.hazmat.primitives.asymmetric.utils import Prehashed
|
from cryptography.hazmat.primitives.asymmetric.utils import Prehashed
|
||||||
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
||||||
|
|
||||||
from .usb import usb
|
from .usb import usb, Usb
|
||||||
from .util import unhex
|
from .util import unhex
|
||||||
|
|
||||||
password_hardcoded = unhexlify('717cd72d0962bc4a2846138dbb2c24192512a76407065f383846139d4bec2033')
|
password_hardcoded = unhexlify('717cd72d0962bc4a2846138dbb2c24192512a76407065f383846139d4bec2033')
|
||||||
|
|
@ -34,7 +35,7 @@ fff000000000000000000000000000000000000000000000000000000000000000000000000
|
||||||
crypto_backend = default_backend()
|
crypto_backend = default_backend()
|
||||||
|
|
||||||
|
|
||||||
def prf(secret, seed, length):
|
def prf(secret: bytes, seed: bytes, length: int):
|
||||||
n = (length + 0x20 - 1) // 0x20
|
n = (length + 0x20 - 1) // 0x20
|
||||||
|
|
||||||
res = b''
|
res = b''
|
||||||
|
|
@ -55,15 +56,15 @@ def hs_key():
|
||||||
return int(hs_key[::-1].hex(), 16)
|
return int(hs_key[::-1].hex(), 16)
|
||||||
|
|
||||||
|
|
||||||
def with_2bytes_size(chunk):
|
def with_2bytes_size(chunk: bytes):
|
||||||
return pack('>H', len(chunk)) + chunk
|
return pack('>H', len(chunk)) + chunk
|
||||||
|
|
||||||
|
|
||||||
def with_3bytes_size(chunk):
|
def with_3bytes_size(chunk: bytes):
|
||||||
return pack('>BH', len(chunk) >> 16, len(chunk)) + chunk
|
return pack('>BH', len(chunk) >> 16, len(chunk)) + chunk
|
||||||
|
|
||||||
|
|
||||||
def with_1byte_size(chunk):
|
def with_1byte_size(chunk: bytes):
|
||||||
return pack('>B', len(chunk)) + chunk
|
return pack('>B', len(chunk)) + chunk
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -76,18 +77,18 @@ def to_bytes(n):
|
||||||
return b
|
return b
|
||||||
|
|
||||||
|
|
||||||
def pad(b):
|
def pad(b: bytes):
|
||||||
l = 16 - (len(b) % 16)
|
l = 16 - (len(b) % 16)
|
||||||
return b + bytes([l - 1]) * l
|
return b + bytes([l - 1]) * l
|
||||||
|
|
||||||
|
|
||||||
def unpad(b):
|
def unpad(b: bytes):
|
||||||
return b[:-1 - b[-1]]
|
return b[:-1 - b[-1]]
|
||||||
|
|
||||||
|
|
||||||
# TODO assert the right state transitions
|
# TODO assert the right state transitions
|
||||||
class Tls:
|
class Tls:
|
||||||
def __init__(self, usb):
|
def __init__(self, usb: Usb):
|
||||||
self.usb = usb
|
self.usb = usb
|
||||||
self.reset()
|
self.reset()
|
||||||
try:
|
try:
|
||||||
|
|
@ -107,7 +108,7 @@ class Tls:
|
||||||
self.secure_tx = False
|
self.secure_tx = False
|
||||||
|
|
||||||
# Info about the host computer
|
# Info about the host computer
|
||||||
def set_hwkey(self, product_name, serial_number):
|
def set_hwkey(self, product_name: str, serial_number: str):
|
||||||
hw_key = bytes(product_name, 'ascii') + b'\0' + \
|
hw_key = bytes(product_name, 'ascii') + b'\0' + \
|
||||||
bytes(serial_number, 'ascii') + b'\0'
|
bytes(serial_number, 'ascii') + b'\0'
|
||||||
|
|
||||||
|
|
@ -116,7 +117,7 @@ class Tls:
|
||||||
self.psk_validation_key = prf(self.psk_encryption_key, b'GWK_SIGN' + gwk_sign_hardcoded,
|
self.psk_validation_key = prf(self.psk_encryption_key, b'GWK_SIGN' + gwk_sign_hardcoded,
|
||||||
0x20)
|
0x20)
|
||||||
|
|
||||||
def cmd(self, cmd):
|
def cmd(self, cmd: typing.Union[bytes, typing.Callable[[], bytes]]):
|
||||||
if self.secure_rx and self.secure_tx:
|
if self.secure_rx and self.secure_tx:
|
||||||
rsp = self.app(cmd)
|
rsp = self.app(cmd)
|
||||||
else:
|
else:
|
||||||
|
|
@ -142,15 +143,15 @@ class Tls:
|
||||||
|
|
||||||
self.parse_tls_response(rsp)
|
self.parse_tls_response(rsp)
|
||||||
|
|
||||||
def trace(self, s):
|
def trace(self, s: str):
|
||||||
if self.trace_enabled:
|
if self.trace_enabled:
|
||||||
logging.debug(s)
|
logging.debug(s)
|
||||||
|
|
||||||
def app(self, b):
|
def app(self, b: typing.Union[bytes, typing.Callable[[], bytes]]):
|
||||||
b = b() if callable(b) else b
|
b = b() if callable(b) else b
|
||||||
return self.parse_tls_response(self.usb.cmd(self.make_app_data(b)))
|
return self.parse_tls_response(self.usb.cmd(self.make_app_data(b)))
|
||||||
|
|
||||||
def update_neg(self, b):
|
def update_neg(self, b: bytes):
|
||||||
self.handshake_hash.update(b)
|
self.handshake_hash.update(b)
|
||||||
|
|
||||||
def make_keys(self):
|
def make_keys(self):
|
||||||
|
|
@ -187,7 +188,7 @@ class Tls:
|
||||||
self.secure_rx = d['secure_rx']
|
self.secure_rx = d['secure_rx']
|
||||||
self.secure_tx = d['secure_tx']
|
self.secure_tx = d['secure_tx']
|
||||||
|
|
||||||
def decrypt(self, c):
|
def decrypt(self, c: bytes):
|
||||||
iv, c = c[:0x10], c[0x10:]
|
iv, c = c[:0x10], c[0x10:]
|
||||||
cipher = Cipher(algorithms.AES(self.decryption_key), modes.CBC(iv), backend=crypto_backend)
|
cipher = Cipher(algorithms.AES(self.decryption_key), modes.CBC(iv), backend=crypto_backend)
|
||||||
decryptor = cipher.decryptor()
|
decryptor = cipher.decryptor()
|
||||||
|
|
@ -195,7 +196,7 @@ class Tls:
|
||||||
m = unpad(m)
|
m = unpad(m)
|
||||||
return m
|
return m
|
||||||
|
|
||||||
def encrypt(self, b):
|
def encrypt(self, b: bytes):
|
||||||
#iv = unhexlify('454849acdd075174d6b9e713a957c2e7')
|
#iv = unhexlify('454849acdd075174d6b9e713a957c2e7')
|
||||||
iv = os.urandom(0x10)
|
iv = os.urandom(0x10)
|
||||||
cipher = Cipher(algorithms.AES(self.encryption_key), modes.CBC(iv), backend=crypto_backend)
|
cipher = Cipher(algorithms.AES(self.encryption_key), modes.CBC(iv), backend=crypto_backend)
|
||||||
|
|
@ -204,7 +205,7 @@ class Tls:
|
||||||
c = encryptor.update(b) + encryptor.finalize()
|
c = encryptor.update(b) + encryptor.finalize()
|
||||||
return iv + c
|
return iv + c
|
||||||
|
|
||||||
def validate(self, t, b):
|
def validate(self, t: int, b: bytes):
|
||||||
b, hs = b[:-0x20], b[-0x20:]
|
b, hs = b[:-0x20], b[-0x20:]
|
||||||
|
|
||||||
hdr = pack('>BBBH', t, 3, 3, len(b))
|
hdr = pack('>BBBH', t, 3, 3, len(b))
|
||||||
|
|
@ -216,7 +217,7 @@ class Tls:
|
||||||
self.trace('<tls< %02x: %s' % (t, hexlify(b).decode()))
|
self.trace('<tls< %02x: %s' % (t, hexlify(b).decode()))
|
||||||
return b
|
return b
|
||||||
|
|
||||||
def sign(self, t, b):
|
def sign(self, t: int, b: bytes):
|
||||||
self.trace('>tls> %02x: %s' % (t, hexlify(b).decode()))
|
self.trace('>tls> %02x: %s' % (t, hexlify(b).decode()))
|
||||||
|
|
||||||
hdr = pack('>BBBH', t, 3, 3, len(b))
|
hdr = pack('>BBBH', t, 3, 3, len(b))
|
||||||
|
|
@ -240,7 +241,7 @@ class Tls:
|
||||||
cert = pack('>BH', 0, len(self.tls_cert)) + cert # same
|
cert = pack('>BH', 0, len(self.tls_cert)) + cert # same
|
||||||
return self.with_neg_hdr(0x0b, cert)
|
return self.with_neg_hdr(0x0b, cert)
|
||||||
|
|
||||||
def with_neg_hdr(self, t, b):
|
def with_neg_hdr(self, t: int, b: bytes):
|
||||||
b = pack('>B', t) + with_3bytes_size(b)
|
b = pack('>B', t) + with_3bytes_size(b)
|
||||||
self.update_neg(b)
|
self.update_neg(b)
|
||||||
return b
|
return b
|
||||||
|
|
@ -254,7 +255,7 @@ class Tls:
|
||||||
b = self.priv_key.sign(buf, ec.ECDSA(Prehashed(hashes.SHA256())))
|
b = self.priv_key.sign(buf, ec.ECDSA(Prehashed(hashes.SHA256())))
|
||||||
return self.with_neg_hdr(0x0f, b)
|
return self.with_neg_hdr(0x0f, b)
|
||||||
|
|
||||||
def handle_server_hello(self, p):
|
def handle_server_hello(self, p: bytes):
|
||||||
if p[:2] != unhexlify('0303'):
|
if p[:2] != unhexlify('0303'):
|
||||||
raise Exception('unexpected TLS version %s' % hexlify(p[:2]).decode())
|
raise Exception('unexpected TLS version %s' % hexlify(p[:2]).decode())
|
||||||
|
|
||||||
|
|
@ -278,7 +279,7 @@ class Tls:
|
||||||
if p != b'':
|
if p != b'':
|
||||||
raise Exception('Not expecting any more data')
|
raise Exception('Not expecting any more data')
|
||||||
|
|
||||||
def handle_cert_req(self, p):
|
def handle_cert_req(self, p: bytes):
|
||||||
(sign_and_hash_algo, ), p = unpack('>H', p[:2]), p[2:]
|
(sign_and_hash_algo, ), p = unpack('>H', p[:2]), p[2:]
|
||||||
if sign_and_hash_algo != 0x140:
|
if sign_and_hash_algo != 0x140:
|
||||||
raise Exception(
|
raise Exception(
|
||||||
|
|
@ -292,24 +293,24 @@ class Tls:
|
||||||
if p != b'':
|
if p != b'':
|
||||||
raise Exception('Not expecting any more data')
|
raise Exception('Not expecting any more data')
|
||||||
|
|
||||||
def handle_server_hello_done(self, p):
|
def handle_server_hello_done(self, p: bytes):
|
||||||
if p != b'':
|
if p != b'':
|
||||||
raise Exception('Not expecting any body for "server hello done" pkt: %s' %
|
raise Exception('Not expecting any body for "server hello done" pkt: %s' %
|
||||||
hexlify(p).decode())
|
hexlify(p).decode())
|
||||||
|
|
||||||
def handle_finish(self, b):
|
def handle_finish(self, b: bytes):
|
||||||
hs_hash = self.handshake_hash.copy().digest()
|
hs_hash = self.handshake_hash.copy().digest()
|
||||||
verify_data = prf(self.master_secret, b'server finished' + hs_hash, 0xc)
|
verify_data = prf(self.master_secret, b'server finished' + hs_hash, 0xc)
|
||||||
if verify_data != b:
|
if verify_data != b:
|
||||||
raise Exception('Final handshake check failed')
|
raise Exception('Final handshake check failed')
|
||||||
|
|
||||||
def handle_app_data(self, b):
|
def handle_app_data(self, b: bytes):
|
||||||
if not self.secure_rx:
|
if not self.secure_rx:
|
||||||
raise Exception('App payload before secure connection established')
|
raise Exception('App payload before secure connection established')
|
||||||
|
|
||||||
return self.validate(0x17, self.decrypt(b))
|
return self.validate(0x17, self.decrypt(b))
|
||||||
|
|
||||||
def handle_handshake(self, handshake):
|
def handle_handshake(self, handshake: bytes) -> None:
|
||||||
if self.secure_rx:
|
if self.secure_rx:
|
||||||
handshake = self.validate(0x16, self.decrypt(handshake))
|
handshake = self.validate(0x16, self.decrypt(handshake))
|
||||||
|
|
||||||
|
|
@ -335,7 +336,7 @@ class Tls:
|
||||||
|
|
||||||
self.update_neg(hdr + p)
|
self.update_neg(hdr + p)
|
||||||
|
|
||||||
def parse_tls_response(self, rsp):
|
def parse_tls_response(self, rsp: bytes):
|
||||||
app_data = b''
|
app_data = b''
|
||||||
|
|
||||||
while len(rsp) > 0:
|
while len(rsp) > 0:
|
||||||
|
|
@ -366,7 +367,7 @@ class Tls:
|
||||||
|
|
||||||
return app_data
|
return app_data
|
||||||
|
|
||||||
def make_app_data(self, b):
|
def make_app_data(self, b: bytes):
|
||||||
if not self.secure_tx:
|
if not self.secure_tx:
|
||||||
raise Exception('App payload before secure connection established')
|
raise Exception('App payload before secure connection established')
|
||||||
|
|
||||||
|
|
@ -374,7 +375,7 @@ class Tls:
|
||||||
|
|
||||||
return unhexlify('170303') + with_2bytes_size(b)
|
return unhexlify('170303') + with_2bytes_size(b)
|
||||||
|
|
||||||
def make_handshake(self, b):
|
def make_handshake(self, b: bytes):
|
||||||
if self.secure_tx:
|
if self.secure_tx:
|
||||||
b = self.encrypt(self.sign(0x16, b))
|
b = self.encrypt(self.sign(0x16, b))
|
||||||
|
|
||||||
|
|
@ -404,10 +405,10 @@ class Tls:
|
||||||
|
|
||||||
return self.with_neg_hdr(0x01, h)
|
return self.with_neg_hdr(0x01, h)
|
||||||
|
|
||||||
def make_ext(self, id, b):
|
def make_ext(self, id: int, b: bytes):
|
||||||
return pack('>H', id) + with_2bytes_size(b)
|
return pack('>H', id) + with_2bytes_size(b)
|
||||||
|
|
||||||
def parse_tls_flash(self, reply):
|
def parse_tls_flash(self, reply: bytes):
|
||||||
while len(reply) > 0:
|
while len(reply) > 0:
|
||||||
hdr, reply = reply[:4], reply[4:]
|
hdr, reply = reply[:4], reply[4:]
|
||||||
hs, reply = reply[:0x20], reply[0x20:]
|
hs, reply = reply[:0x20], reply[0x20:]
|
||||||
|
|
@ -440,7 +441,7 @@ class Tls:
|
||||||
else:
|
else:
|
||||||
self.trace('unhandled block id %04x (%d bytes): %s' % (id, sz, hexlify(body)))
|
self.trace('unhandled block id %04x (%d bytes): %s' % (id, sz, hexlify(body)))
|
||||||
|
|
||||||
def make_tls_flash_block(self, id, body):
|
def make_tls_flash_block(self, id: int, body: bytes):
|
||||||
m = sha256()
|
m = sha256()
|
||||||
m.update(body)
|
m.update(body)
|
||||||
hdr = pack('<HH', id, len(body))
|
hdr = pack('<HH', id, len(body))
|
||||||
|
|
@ -457,16 +458,16 @@ class Tls:
|
||||||
b += b'\xff' * (0x1000 - len(b))
|
b += b'\xff' * (0x1000 - len(b))
|
||||||
return b
|
return b
|
||||||
|
|
||||||
def handle_empty(self, body):
|
def handle_empty(self, body: bytes):
|
||||||
if body != b'\0' * len(body):
|
if body != b'\0' * len(body):
|
||||||
raise Exception('Expected empty block')
|
raise Exception('Expected empty block')
|
||||||
|
|
||||||
def handle_cert(self, body):
|
def handle_cert(self, body: bytes):
|
||||||
# TODO validate cert, check if pub keys match
|
# TODO validate cert, check if pub keys match
|
||||||
self.tls_cert = body
|
self.tls_cert = body
|
||||||
self.trace('TLS cert blob: %s' % hexlify(self.tls_cert))
|
self.trace('TLS cert blob: %s' % hexlify(self.tls_cert))
|
||||||
|
|
||||||
def handle_ecdh(self, body):
|
def handle_ecdh(self, body: bytes):
|
||||||
self.ecdh_blob = body
|
self.ecdh_blob = body
|
||||||
key, signature = body[:0x90], body[0x90:]
|
key, signature = body[:0x90], body[0x90:]
|
||||||
x = key[0x8:0x8 + 0x20]
|
x = key[0x8:0x8 + 0x20]
|
||||||
|
|
@ -499,7 +500,7 @@ class Tls:
|
||||||
# throws InvalidSignature
|
# throws InvalidSignature
|
||||||
fwpub.verify(signature, key, ec.ECDSA(hashes.SHA256()))
|
fwpub.verify(signature, key, ec.ECDSA(hashes.SHA256()))
|
||||||
|
|
||||||
def handle_priv(self, body):
|
def handle_priv(self, body: bytes):
|
||||||
self.priv_blob = body
|
self.priv_blob = body
|
||||||
prefix, body = body[0], body[1:]
|
prefix, body = body[0], body[1:]
|
||||||
if prefix != 2:
|
if prefix != 2:
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import logging
|
import logging
|
||||||
from os.path import basename
|
from os.path import basename
|
||||||
from time import ctime
|
from time import ctime
|
||||||
|
import typing
|
||||||
|
|
||||||
from .flash import write_flash_all, write_fw_signature, get_fw_info
|
from .flash import write_flash_all, write_fw_signature, get_fw_info
|
||||||
from .sensor import reboot, write_hw_reg32, read_hw_reg32, identify_sensor
|
from .sensor import reboot, write_hw_reg32, read_hw_reg32, identify_sensor
|
||||||
|
|
@ -21,7 +22,7 @@ def default_fwext_name():
|
||||||
return '6_07f_lenovo_mis_qm.xpfwext'
|
return '6_07f_lenovo_mis_qm.xpfwext'
|
||||||
|
|
||||||
|
|
||||||
def upload_fwext(fw_path=None):
|
def upload_fwext(fw_path: typing.Optional[str]=None):
|
||||||
fwi = get_fw_info(2)
|
fwi = get_fw_info(2)
|
||||||
if fwi is not None:
|
if fwi is not None:
|
||||||
logging.info('Detected firmware version %d.%d (%s))' %
|
logging.info('Detected firmware version %d.%d (%s))' %
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import errno
|
||||||
import logging
|
import logging
|
||||||
from binascii import hexlify, unhexlify
|
from binascii import hexlify, unhexlify
|
||||||
from struct import unpack
|
from struct import unpack
|
||||||
|
import typing
|
||||||
|
|
||||||
import usb.core as ucore
|
import usb.core as ucore
|
||||||
from usb.core import USBError
|
from usb.core import USBError
|
||||||
|
|
@ -22,9 +23,8 @@ class CancelledException(Exception):
|
||||||
|
|
||||||
class Usb:
|
class Usb:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.interrupt_cb = None
|
|
||||||
self.trace_enabled = False
|
self.trace_enabled = False
|
||||||
self.dev = None
|
self.dev: typing.Optional[ucore.Device] = None
|
||||||
self.cancel = False
|
self.cancel = False
|
||||||
|
|
||||||
def open(self, vendor=None, product=None):
|
def open(self, vendor=None, product=None):
|
||||||
|
|
@ -39,7 +39,7 @@ class Usb:
|
||||||
|
|
||||||
self.open_dev(dev)
|
self.open_dev(dev)
|
||||||
|
|
||||||
def open_devpath(self, busnum, address):
|
def open_devpath(self, busnum: int, address: int):
|
||||||
def match(d):
|
def match(d):
|
||||||
return d.bus == busnum and d.address == address
|
return d.bus == busnum and d.address == address
|
||||||
|
|
||||||
|
|
@ -47,7 +47,7 @@ class Usb:
|
||||||
|
|
||||||
self.open_dev(dev)
|
self.open_dev(dev)
|
||||||
|
|
||||||
def open_dev(self, dev):
|
def open_dev(self, dev: ucore.Device):
|
||||||
if dev is None:
|
if dev is None:
|
||||||
raise Exception('No matching devices found')
|
raise Exception('No matching devices found')
|
||||||
|
|
||||||
|
|
@ -84,7 +84,7 @@ class Usb:
|
||||||
logging.info('Clean slate')
|
logging.info('Clean slate')
|
||||||
self.cmd(init_hardcoded_clean_slate)
|
self.cmd(init_hardcoded_clean_slate)
|
||||||
|
|
||||||
def cmd(self, out):
|
def cmd(self, out: typing.Union[bytes, typing.Callable[[], bytes]]):
|
||||||
if callable(out):
|
if callable(out):
|
||||||
out = out()
|
out = out()
|
||||||
if not out:
|
if not out:
|
||||||
|
|
@ -126,7 +126,7 @@ class Usb:
|
||||||
else:
|
else:
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
def trace(self, s):
|
def trace(self, s: str):
|
||||||
if self.trace_enabled:
|
if self.trace_enabled:
|
||||||
logging.debug(s)
|
logging.debug(s)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ from binascii import unhexlify
|
||||||
from struct import unpack
|
from struct import unpack
|
||||||
|
|
||||||
|
|
||||||
def assert_status(b):
|
def assert_status(b: bytes):
|
||||||
s, = unpack('<H', b[:2])
|
s, = unpack('<H', b[:2])
|
||||||
if s != 0:
|
if s != 0:
|
||||||
if s == 0x44f:
|
if s == 0x44f:
|
||||||
|
|
@ -12,5 +12,5 @@ def assert_status(b):
|
||||||
raise Exception('Failed: %04x' % s)
|
raise Exception('Failed: %04x' % s)
|
||||||
|
|
||||||
|
|
||||||
def unhex(x):
|
def unhex(x: str):
|
||||||
return unhexlify(re.sub(r'\W', '', x))
|
return unhexlify(re.sub(r'\W', '', x))
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue