python script for progamming via solo bootloader

This commit is contained in:
Conor Patrick
2018-12-03 23:01:51 -05:00
parent 9b4b18e1a4
commit 5a96e82f4d
10 changed files with 144 additions and 15 deletions

122
tools/programmer.py Normal file
View File

@@ -0,0 +1,122 @@
# Programs solo using the Solo bootloader
# Requires python-fido2, intelhex
import sys,os,time,struct
import array,struct,socket,json,base64
import tempfile
from binascii import hexlify
from fido2.hid import CtapHidDevice, CTAPHID
from fido2.client import Fido2Client, ClientError
from fido2.ctap import CtapError
from fido2.ctap1 import CTAP1
from intelhex import IntelHex
from sign_firmware import *
class SoloBootloader:
write = 0x40
done = 0x41
check = 0x42
erase = 0x43
version = 0x44
TAG = b'\x8C\x27\x90\xf6'
class Programmer():
def __init__(self,):
self.origin = 'https://example.org'
def find_device(self,):
dev = next(CtapHidDevice.list_devices(), None)
if not dev:
raise RuntimeError('No FIDO device found')
self.dev = dev
self.ctap1 = CTAP1(dev)
@staticmethod
def format_request(cmd,addr = 0,data = b'A'*16):
arr = b'\x00'*9
addr = struct.pack('<L', addr)
cmd = struct.pack('B', cmd)
length = struct.pack('B', len(data))
return cmd + addr[:3] + SoloBootloader.TAG + length + data
def exchange(self,cmd,addr=0,data=b'A'*16):
appid = b'A'*32
chal = b'B'*32
req = Programmer.format_request(cmd,addr,data)
res = self.ctap1.authenticate(chal,appid, req)
ret = res.signature[0]
if ret != CtapError.ERR.SUCCESS:
raise RuntimeError('Device returned non-success code %02x' % ret)
return res.signature[1:]
def version(self,):
data = self.exchange(SoloBootloader.version)
return data[0]
def write_flash(self,addr,data):
self.exchange(SoloBootloader.write,addr,data)
def verify_flash(self,sig):
"""
Tells device to check signature against application. If it passes,
the application will boot.
Exception raises if signature fails.
"""
self.exchange(SoloBootloader.done,0,sig)
def program_file(self,name):
data = json.loads(open(name,'r').read())
fw = base64.b64decode(from_websafe(data['firmware']).encode())
sig = base64.b64decode(from_websafe(data['signature']).encode())
ih = IntelHex()
tmp = tempfile.NamedTemporaryFile(delete=False)
tmp.write(fw)
tmp.seek(0)
tmp.close()
ih.fromfile(tmp.name, format='hex')
chunk = 240
seg = ih.segments()[0]
size = seg[1] - seg[0]
total = 0
t1 = time.time()*1000
for i in range(seg[0], seg[1], chunk):
s = i
e = min(i+chunk,seg[1])
data = ih.tobinarray(start=i,size = e-s)
self.write_flash(i,data)
total += chunk
progress = total/float(size)*100
sys.stdout.write('downloading %.2f%%...\r' % progress)
sys.stdout.write('downloading 100% \r\n')
t2 = time.time()*1000
print('time: %.2f s' % ((t2-t1)/1000.0))
print('Verifying...')
self.verify_flash(sig)
if __name__ == '__main__':
if len(sys.argv) != 2:
print('usage: %s <firmware.json>' % sys.argv[0])
sys.exit(1)
p = Programmer()
p.find_device()
print('version is ', p.version())
p.program_file(sys.argv[1])

View File

@@ -22,6 +22,8 @@ def get_firmware_object(sk_name, hex_file):
fw = base64.b64encode(fw.encode())
fw = to_websafe(fw.decode())
# start of firmware and the size of the flash region allocated for it.
# TODO put this somewhere else.
START = 0x08008000
END = START + 1024 * 186 - 8
@@ -58,5 +60,3 @@ if __name__ == '__main__':
wfile = open(sys.argv[3],'wb+')
wfile.write(json.dumps(msg).encode())
wfile.close()