python script for progamming via solo bootloader
This commit is contained in:
parent
9b4b18e1a4
commit
5a96e82f4d
@ -27,6 +27,8 @@
|
|||||||
|
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
|
||||||
|
#define htonl(x) (((x & 0xff) << 24) | ((x & 0xff00) << 8) \
|
||||||
|
| ((x & 0xff0000) >> 8) | ((x & 0xff000000) >> 24) )
|
||||||
|
|
||||||
int is_extension_request(uint8_t * kh, int len)
|
int is_extension_request(uint8_t * kh, int len)
|
||||||
{
|
{
|
||||||
@ -53,11 +55,11 @@ int16_t bridge_u2f_to_extensions(uint8_t * _chal, uint8_t * _appid, uint8_t klen
|
|||||||
uint8_t sig[72];
|
uint8_t sig[72];
|
||||||
if (extension_needs_atomic_count(klen, keyh))
|
if (extension_needs_atomic_count(klen, keyh))
|
||||||
{
|
{
|
||||||
count = ctap_atomic_count(0);
|
count = htonl(ctap_atomic_count(0));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
count = 10;
|
count = htonl(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
u2f_response_writeback(&up,1);
|
u2f_response_writeback(&up,1);
|
||||||
@ -102,7 +104,8 @@ int16_t extend_u2f(struct u2f_request_apdu* req, uint32_t len)
|
|||||||
{
|
{
|
||||||
rcode = U2F_SW_WRONG_DATA;
|
rcode = U2F_SW_WRONG_DATA;
|
||||||
}
|
}
|
||||||
printf1(TAG_WALLET,"Ignoring U2F request\n");
|
printf1(TAG_EXT,"Ignoring U2F request\n");
|
||||||
|
dump_hex1(TAG_EXT, (uint8_t *) &auth->kh, auth->khl);
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -110,7 +113,8 @@ int16_t extend_u2f(struct u2f_request_apdu* req, uint32_t len)
|
|||||||
if ( ! is_extension_request((uint8_t *) &auth->kh, auth->khl)) // Pin requests
|
if ( ! is_extension_request((uint8_t *) &auth->kh, auth->khl)) // Pin requests
|
||||||
{
|
{
|
||||||
rcode = U2F_SW_WRONG_PAYLOAD;
|
rcode = U2F_SW_WRONG_PAYLOAD;
|
||||||
printf1(TAG_WALLET,"Ignoring U2F request\n");
|
printf1(TAG_EXT, "Ignoring U2F request\n");
|
||||||
|
dump_hex1(TAG_EXT, (uint8_t *) &auth->kh, auth->khl);
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
rcode = bridge_u2f_to_extensions(auth->chal, auth->app, auth->khl, (uint8_t*)&auth->kh);
|
rcode = bridge_u2f_to_extensions(auth->chal, auth->app, auth->khl, (uint8_t*)&auth->kh);
|
||||||
@ -118,7 +122,7 @@ int16_t extend_u2f(struct u2f_request_apdu* req, uint32_t len)
|
|||||||
}
|
}
|
||||||
else if (req->ins == U2F_VERSION)
|
else if (req->ins == U2F_VERSION)
|
||||||
{
|
{
|
||||||
printf1(TAG_U2F, "U2F_VERSION\n");
|
printf1(TAG_EXT, "U2F_VERSION\n");
|
||||||
if (len)
|
if (len)
|
||||||
{
|
{
|
||||||
rcode = U2F_SW_WRONG_LENGTH;
|
rcode = U2F_SW_WRONG_LENGTH;
|
||||||
|
@ -61,6 +61,7 @@ struct logtag tagtable[] = {
|
|||||||
{TAG_WALLET,"[1;34mWALLET[0m"},
|
{TAG_WALLET,"[1;34mWALLET[0m"},
|
||||||
{TAG_STOR,"[1;35mSTOR[0m"},
|
{TAG_STOR,"[1;35mSTOR[0m"},
|
||||||
{TAG_BOOT,"[1;36mBOOT[0m"},
|
{TAG_BOOT,"[1;36mBOOT[0m"},
|
||||||
|
{TAG_BOOT,"[1;37mEXT[0m"},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -56,6 +56,7 @@ typedef enum
|
|||||||
TAG_STOR = (1 << 15),
|
TAG_STOR = (1 << 15),
|
||||||
TAG_DUMP2 = (1 << 16),
|
TAG_DUMP2 = (1 << 16),
|
||||||
TAG_BOOT = (1 << 17),
|
TAG_BOOT = (1 << 17),
|
||||||
|
TAG_EXT = (1 << 17),
|
||||||
|
|
||||||
TAG_FILENO = (1<<31)
|
TAG_FILENO = (1<<31)
|
||||||
} LOG_TAG;
|
} LOG_TAG;
|
||||||
|
@ -22,7 +22,6 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#if DEBUG_LEVEL
|
|
||||||
void dump_hex(uint8_t * buf, int size)
|
void dump_hex(uint8_t * buf, int size)
|
||||||
{
|
{
|
||||||
while(size--)
|
while(size--)
|
||||||
@ -31,9 +30,3 @@ void dump_hex(uint8_t * buf, int size)
|
|||||||
}
|
}
|
||||||
printf("\n");
|
printf("\n");
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
void dump_hex(uint8_t * buf, int size)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
@ -130,6 +130,7 @@ int bootloader_bridge(uint8_t klen, uint8_t * keyh)
|
|||||||
return 0;
|
return 0;
|
||||||
break;
|
break;
|
||||||
case BootVersion:
|
case BootVersion:
|
||||||
|
has_erased = 0;
|
||||||
printf1(TAG_BOOT, "BootVersion.\r\n");
|
printf1(TAG_BOOT, "BootVersion.\r\n");
|
||||||
u2f_response_writeback(&version,1);
|
u2f_response_writeback(&version,1);
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -72,6 +72,7 @@ int main(int argc, char * argv[])
|
|||||||
// TAG_TIME|
|
// TAG_TIME|
|
||||||
// TAG_DUMP|
|
// TAG_DUMP|
|
||||||
TAG_BOOT|
|
TAG_BOOT|
|
||||||
|
TAG_EXT|
|
||||||
TAG_GREEN|
|
TAG_GREEN|
|
||||||
TAG_RED|
|
TAG_RED|
|
||||||
TAG_ERR
|
TAG_ERR
|
||||||
|
@ -11,6 +11,11 @@
|
|||||||
|
|
||||||
#define BOOT_TO_DFU 0
|
#define BOOT_TO_DFU 0
|
||||||
|
|
||||||
|
// Uncomment SOLO_HACKKER to Only use level 1 read-out-protection,
|
||||||
|
// allows booting to ST bootloader or Solo bootloader without any button press,
|
||||||
|
// Disables signature check in Solo bootloader.
|
||||||
|
#define SOLO_HACKER
|
||||||
|
|
||||||
//#define USING_DEV_BOARD
|
//#define USING_DEV_BOARD
|
||||||
|
|
||||||
//#define ENABLE_U2F_EXTENSIONS
|
//#define ENABLE_U2F_EXTENSIONS
|
||||||
|
122
tools/programmer.py
Normal file
122
tools/programmer.py
Normal 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])
|
@ -22,6 +22,8 @@ def get_firmware_object(sk_name, hex_file):
|
|||||||
fw = base64.b64encode(fw.encode())
|
fw = base64.b64encode(fw.encode())
|
||||||
fw = to_websafe(fw.decode())
|
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
|
START = 0x08008000
|
||||||
END = START + 1024 * 186 - 8
|
END = START + 1024 * 186 - 8
|
||||||
|
|
||||||
@ -58,5 +60,3 @@ if __name__ == '__main__':
|
|||||||
wfile = open(sys.argv[3],'wb+')
|
wfile = open(sys.argv[3],'wb+')
|
||||||
wfile.write(json.dumps(msg).encode())
|
wfile.write(json.dumps(msg).encode())
|
||||||
wfile.close()
|
wfile.close()
|
||||||
|
|
||||||
|
|
||||||
|
@ -262,6 +262,7 @@ var CMD = {
|
|||||||
boot_done: 0x41,
|
boot_done: 0x41,
|
||||||
boot_check: 0x42,
|
boot_check: 0x42,
|
||||||
boot_erase: 0x43,
|
boot_erase: 0x43,
|
||||||
|
boot_version: 0x44,
|
||||||
};
|
};
|
||||||
|
|
||||||
var PIN = {
|
var PIN = {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user