program using just hid protocol, quicker
This commit is contained in:
parent
5a96e82f4d
commit
b0cc9cf582
@ -30,6 +30,7 @@
|
||||
#include "time.h"
|
||||
#include "util.h"
|
||||
#include "log.h"
|
||||
#include "extensions.h"
|
||||
#include APP_CONFIG
|
||||
|
||||
typedef enum
|
||||
@ -682,6 +683,23 @@ uint8_t ctaphid_handle_packet(uint8_t * pkt_raw)
|
||||
printf1(TAG_HID,"CTAPHID_CANCEL\n");
|
||||
is_busy = 0;
|
||||
break;
|
||||
#if defined(IS_BOOTLOADER)
|
||||
case CTAPHID_BOOT:
|
||||
printf1(TAG_HID,"CTAPHID_BOOT\n");
|
||||
ctap_response_init(&ctap_resp);
|
||||
u2f_set_writeback_buffer(&ctap_resp);
|
||||
is_busy = bootloader_bridge(len, ctap_buffer);
|
||||
|
||||
ctaphid_write_buffer_init(&wb);
|
||||
wb.cid = cid;
|
||||
wb.cmd = CTAPHID_BOOT;
|
||||
wb.bcnt = (ctap_resp.length + 1);
|
||||
ctaphid_write(&wb, &is_busy, 1);
|
||||
ctaphid_write(&wb, ctap_resp.data, ctap_resp.length);
|
||||
ctaphid_write(&wb, NULL, 0);
|
||||
is_busy = 0;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
printf2(TAG_ERR,"error, unimplemented HID cmd: %02x\r\n", buffer_cmd());
|
||||
ctaphid_send_error(cid, CTAP1_ERR_INVALID_COMMAND);
|
||||
|
@ -38,6 +38,9 @@
|
||||
#define CTAPHID_ERROR (TYPE_INIT | 0x3f)
|
||||
#define CTAPHID_KEEPALIVE (TYPE_INIT | 0x3b)
|
||||
|
||||
// Custom commands between 0x40-0x7f
|
||||
#define CTAPHID_BOOT (TYPE_INIT | 0x50)
|
||||
|
||||
#define ERR_INVALID_CMD 0x01
|
||||
#define ERR_INVALID_PAR 0x02
|
||||
#define ERR_INVALID_SEQ 0x04
|
||||
|
@ -25,6 +25,6 @@
|
||||
|
||||
int16_t extend_u2f(struct u2f_request_apdu* req, uint32_t len);
|
||||
|
||||
int bootloader_bridge(uint8_t klen, uint8_t * keyh);
|
||||
int bootloader_bridge(int klen, uint8_t * keyh);
|
||||
|
||||
#endif /* EXTENSIONS_H_ */
|
||||
|
@ -34,6 +34,7 @@ static int16_t u2f_authenticate(struct u2f_authenticate_request * req, uint8_t c
|
||||
int8_t u2f_response_writeback(const uint8_t * buf, uint16_t len);
|
||||
void u2f_reset_response();
|
||||
|
||||
|
||||
static CTAP_RESPONSE * _u2f_resp = NULL;
|
||||
|
||||
void u2f_request(struct u2f_request_apdu* req, CTAP_RESPONSE * resp)
|
||||
@ -43,7 +44,7 @@ void u2f_request(struct u2f_request_apdu* req, CTAP_RESPONSE * resp)
|
||||
uint32_t len = ((req->LC3) | ((uint32_t)req->LC2 << 8) | ((uint32_t)req->LC1 << 16));
|
||||
uint8_t byte;
|
||||
|
||||
_u2f_resp = resp;
|
||||
u2f_set_writeback_buffer(resp);
|
||||
|
||||
if (req->cla != 0)
|
||||
{
|
||||
@ -137,6 +138,10 @@ void u2f_reset_response()
|
||||
ctap_response_init(_u2f_resp);
|
||||
}
|
||||
|
||||
void u2f_set_writeback_buffer(CTAP_RESPONSE * resp)
|
||||
{
|
||||
_u2f_resp = resp;
|
||||
}
|
||||
|
||||
static void dump_signature_der(uint8_t * sig)
|
||||
{
|
||||
|
@ -116,6 +116,7 @@ void u2f_request(struct u2f_request_apdu* req, CTAP_RESPONSE * resp);
|
||||
|
||||
int8_t u2f_response_writeback(const uint8_t * buf, uint16_t len);
|
||||
void u2f_reset_response();
|
||||
void u2f_set_writeback_buffer(CTAP_RESPONSE * resp);
|
||||
|
||||
int16_t u2f_version();
|
||||
|
||||
|
@ -28,8 +28,9 @@ typedef struct {
|
||||
uint8_t op;
|
||||
uint8_t addr[3];
|
||||
uint8_t tag[4];
|
||||
uint8_t len;
|
||||
uint8_t payload[255 - 9];
|
||||
uint8_t lenh;
|
||||
uint8_t lenl;
|
||||
uint8_t payload[255 - 10];
|
||||
} __attribute__((packed)) BootloaderReq;
|
||||
|
||||
|
||||
@ -49,30 +50,29 @@ static void authorize_application()
|
||||
ptr = (uint32_t *)AUTH_WORD_ADDR;
|
||||
flash_write((uint32_t)ptr, (uint8_t *)&zero, 4);
|
||||
}
|
||||
|
||||
int is_authorized_to_boot()
|
||||
{
|
||||
uint32_t * auth = (uint32_t *)AUTH_WORD_ADDR;
|
||||
return *auth == 0;
|
||||
}
|
||||
|
||||
int bootloader_bridge(uint8_t klen, uint8_t * keyh)
|
||||
int bootloader_bridge(int klen, uint8_t * keyh)
|
||||
{
|
||||
static int has_erased = 0;
|
||||
BootloaderReq * req = (BootloaderReq * )keyh;
|
||||
uint8_t payload[256];
|
||||
uint8_t hash[32];
|
||||
uint8_t version = 1;
|
||||
uint16_t len = (req->lenh << 8) | (req->lenl);
|
||||
|
||||
uint8_t * pubkey = (uint8_t*)"\x85\xaa\xce\xda\xd4\xb4\xd8\x0d\xf7\x0e\xe8\x91\x6d\x69\x8e\x00\x7a\x27\x40\x76\x93\x7a\x1d\x63\xb1\xcf\xe8\x22\xdd\x9f\xbc\x43\x3e\x34\x0a\x05\x9d\x8a\x9d\x72\xdc\xc2\x4b\x56\x9c\x64\x3d\xc1\x0d\x14\x64\x69\x52\x31\xd7\x54\xa3\xb6\x69\xa7\x6f\x6b\x81\x8d";
|
||||
const struct uECC_Curve_t * curve = NULL;
|
||||
|
||||
if (req->len > 255-9)
|
||||
if (len > klen-10)
|
||||
{
|
||||
printf1(TAG_BOOT,"Invalid length %d / %d\r\n", len, klen-9);
|
||||
return CTAP1_ERR_INVALID_LENGTH;
|
||||
}
|
||||
|
||||
memset(payload, 0xff, sizeof(payload));
|
||||
memmove(payload, req->payload, req->len);
|
||||
uint8_t * pubkey = (uint8_t*)"\x85\xaa\xce\xda\xd4\xb4\xd8\x0d\xf7\x0e\xe8\x91\x6d\x69\x8e\x00\x7a\x27\x40\x76\x93\x7a\x1d\x63\xb1\xcf\xe8\x22\xdd\x9f\xbc\x43\x3e\x34\x0a\x05\x9d\x8a\x9d\x72\xdc\xc2\x4b\x56\x9c\x64\x3d\xc1\x0d\x14\x64\x69\x52\x31\xd7\x54\xa3\xb6\x69\xa7\x6f\x6b\x81\x8d";
|
||||
const struct uECC_Curve_t * curve = NULL;
|
||||
|
||||
uint32_t addr = ((*((uint32_t*)req->addr)) & 0xffffff) | 0x8000000;
|
||||
|
||||
@ -82,7 +82,7 @@ int bootloader_bridge(uint8_t klen, uint8_t * keyh)
|
||||
case BootWrite:
|
||||
printf1(TAG_BOOT, "BootWrite: %08lx\r\n",(uint32_t)ptr);
|
||||
if ((uint32_t)ptr < APPLICATION_START_ADDR || (uint32_t)ptr >= APPLICATION_END_ADDR
|
||||
|| ((uint32_t)ptr+req->len) > APPLICATION_END_ADDR)
|
||||
|| ((uint32_t)ptr+len) > APPLICATION_END_ADDR)
|
||||
{
|
||||
printf1(TAG_BOOT,"Bound exceeded [%08lx, %08lx]\r\n",APPLICATION_START_ADDR,APPLICATION_END_ADDR);
|
||||
return CTAP2_ERR_NOT_ALLOWED;
|
||||
@ -99,11 +99,17 @@ int bootloader_bridge(uint8_t klen, uint8_t * keyh)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
flash_write((uint32_t)ptr,payload, req->len);
|
||||
flash_write((uint32_t)ptr,req->payload, len);
|
||||
break;
|
||||
case BootDone:
|
||||
printf1(TAG_BOOT, "BootDone: ");
|
||||
dump_hex1(TAG_BOOT, payload, 32);
|
||||
#ifndef SOLO_HACKER
|
||||
if (len != 64)
|
||||
{
|
||||
printf1(TAG_BOOT,"Invalid length for signature\r\n");
|
||||
return CTAP1_ERR_INVALID_LENGTH;
|
||||
}
|
||||
dump_hex1(TAG_BOOT, req->payload, 32);
|
||||
ptr = (uint32_t *)APPLICATION_START_ADDR;
|
||||
crypto_sha256_init();
|
||||
crypto_sha256_update((uint8_t*)ptr, APPLICATION_END_ADDR-APPLICATION_START_ADDR);
|
||||
@ -113,11 +119,12 @@ int bootloader_bridge(uint8_t klen, uint8_t * keyh)
|
||||
if (! uECC_verify(pubkey,
|
||||
hash,
|
||||
32,
|
||||
payload,
|
||||
req->payload,
|
||||
curve))
|
||||
{
|
||||
return CTAP2_ERR_OPERATION_DENIED;
|
||||
}
|
||||
#endif
|
||||
authorize_application();
|
||||
REBOOT_FLAG = 1;
|
||||
break;
|
||||
|
@ -70,7 +70,8 @@ int main(int argc, char * argv[])
|
||||
/*TAG_U2F|*/
|
||||
// TAG_PARSE |
|
||||
// TAG_TIME|
|
||||
// TAG_DUMP|
|
||||
// TAG_DUMP|
|
||||
// TAG_DUMP2|
|
||||
TAG_BOOT|
|
||||
TAG_EXT|
|
||||
TAG_GREEN|
|
||||
|
@ -74,7 +74,6 @@ class Tester():
|
||||
#cmd,resp = self.recv_raw()
|
||||
|
||||
def send_data(self, cmd, data):
|
||||
#print('<<', hexlify(data))
|
||||
if type(data) != type(b''):
|
||||
data = struct.pack('%dB' % len(data), *[ord(x) for x in data])
|
||||
with Timeout(1.0) as event:
|
||||
|
@ -1,7 +1,7 @@
|
||||
# Programs solo using the Solo bootloader
|
||||
# Requires python-fido2, intelhex
|
||||
|
||||
import sys,os,time,struct
|
||||
import sys,os,time,struct,argparse
|
||||
import array,struct,socket,json,base64
|
||||
import tempfile
|
||||
from binascii import hexlify
|
||||
@ -10,6 +10,7 @@ from fido2.hid import CtapHidDevice, CTAPHID
|
||||
from fido2.client import Fido2Client, ClientError
|
||||
from fido2.ctap import CtapError
|
||||
from fido2.ctap1 import CTAP1
|
||||
from fido2.utils import Timeout
|
||||
|
||||
from intelhex import IntelHex
|
||||
|
||||
@ -22,12 +23,26 @@ class SoloBootloader:
|
||||
erase = 0x43
|
||||
version = 0x44
|
||||
|
||||
HIDCommand = 0x50
|
||||
|
||||
TAG = b'\x8C\x27\x90\xf6'
|
||||
|
||||
class Programmer():
|
||||
|
||||
def __init__(self,):
|
||||
self.origin = 'https://example.org'
|
||||
self.exchange = self.exchange_hid
|
||||
self.reboot = True
|
||||
|
||||
def use_u2f(self,):
|
||||
self.exchange = self.exchange_u2f
|
||||
|
||||
def use_hid(self,):
|
||||
self.exchange = self.exchange_hid
|
||||
|
||||
def set_reboot(self,val):
|
||||
""" option to reboot after programming """
|
||||
self.reboot = val
|
||||
|
||||
def find_device(self,):
|
||||
dev = next(CtapHidDevice.list_devices(), None)
|
||||
@ -36,16 +51,36 @@ class Programmer():
|
||||
self.dev = dev
|
||||
self.ctap1 = CTAP1(dev)
|
||||
|
||||
if self.exchange == self.exchange_hid:
|
||||
self.send_data_hid(CTAPHID.INIT, '\x11\x11\x11\x11\x11\x11\x11\x11')
|
||||
|
||||
@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))
|
||||
length = struct.pack('>H', len(data))
|
||||
|
||||
return cmd + addr[:3] + SoloBootloader.TAG + length + data
|
||||
|
||||
def exchange(self,cmd,addr=0,data=b'A'*16):
|
||||
def send_data_hid(self, cmd, data):
|
||||
if type(data) != type(b''):
|
||||
data = struct.pack('%dB' % len(data), *[ord(x) for x in data])
|
||||
with Timeout(1.0) as event:
|
||||
return self.dev.call(cmd, data,event)
|
||||
|
||||
def exchange_hid(self,cmd,addr=0,data=b'A'*16):
|
||||
req = Programmer.format_request(cmd,addr,data)
|
||||
|
||||
data = self.send_data_hid(SoloBootloader.HIDCommand, req)
|
||||
|
||||
ret = data[0]
|
||||
if ret != CtapError.ERR.SUCCESS:
|
||||
raise RuntimeError('Device returned non-success code %02x' % ret)
|
||||
|
||||
return data[1:]
|
||||
|
||||
def exchange_u2f(self,cmd,addr=0,data=b'A'*16):
|
||||
appid = b'A'*32
|
||||
chal = b'B'*32
|
||||
|
||||
@ -77,21 +112,32 @@ class Programmer():
|
||||
|
||||
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())
|
||||
if name.lower().endswith('.json'):
|
||||
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')
|
||||
else:
|
||||
if not name.lower().endswith('.hex'):
|
||||
print('Warning, assuming "%s" is an Intel Hex file.' % name)
|
||||
sig = None
|
||||
ih = IntelHex()
|
||||
ih.fromfile(tmp.name, format='hex')
|
||||
|
||||
ih = IntelHex()
|
||||
tmp = tempfile.NamedTemporaryFile(delete=False)
|
||||
tmp.write(fw)
|
||||
tmp.seek(0)
|
||||
tmp.close()
|
||||
ih.fromfile(tmp.name, format='hex')
|
||||
if self.exchange == self.exchange_hid:
|
||||
chunk = 2048
|
||||
else:
|
||||
chunk = 240
|
||||
|
||||
chunk = 240
|
||||
seg = ih.segments()[0]
|
||||
size = seg[1] - seg[0]
|
||||
total = 0
|
||||
t1 = time.time()*1000
|
||||
print('erasing...')
|
||||
for i in range(seg[0], seg[1], chunk):
|
||||
s = i
|
||||
e = min(i+chunk,seg[1])
|
||||
@ -100,23 +146,41 @@ class Programmer():
|
||||
total += chunk
|
||||
progress = total/float(size)*100
|
||||
sys.stdout.write('downloading %.2f%%...\r' % progress)
|
||||
sys.stdout.write('downloading 100% \r\n')
|
||||
sys.stdout.write('downloaded 100% \r\n')
|
||||
t2 = time.time()*1000
|
||||
print('time: %.2f s' % ((t2-t1)/1000.0))
|
||||
|
||||
print('Verifying...')
|
||||
self.verify_flash(sig)
|
||||
if sig is not None:
|
||||
self.verify_flash(sig)
|
||||
else:
|
||||
self.verify_flash(b'A'*64)
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv) != 2:
|
||||
print('usage: %s <firmware.json>' % sys.argv[0])
|
||||
sys.exit(1)
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("<firmware>", help = 'firmware file. Either a JSON or hex file. JSON file contains signature while hex does not.')
|
||||
parser.add_argument("--use-hid", action="store_true", help = 'Programs using custom HID command (default). Quicker than using U2F authenticate which is what a browser has to use.')
|
||||
parser.add_argument("--use-u2f", action="store_true", help = 'Programs using U2F authenticate. This is what a web application will use.')
|
||||
parser.add_argument("--no-reset", action="store_true", help = 'Don\'t reset after writing firmware. Stay in bootloader mode.')
|
||||
parser.add_argument("--reset-only", action="store_true", help = 'Don\'t write anything, try to boot without a signature.')
|
||||
args = parser.parse_args()
|
||||
print()
|
||||
|
||||
p = Programmer()
|
||||
p.find_device()
|
||||
|
||||
if args.use_u2f:
|
||||
p.use_u2f()
|
||||
|
||||
if args.no_reset:
|
||||
p.set_reboot(False)
|
||||
|
||||
print('version is ', p.version())
|
||||
|
||||
p.program_file(sys.argv[1])
|
||||
if not args.reset_only:
|
||||
p.program_file(args.__dict__['<firmware>'])
|
||||
else:
|
||||
p.exchange(SoloBootloader.done,0,b'A'*64)
|
||||
|
@ -425,9 +425,10 @@ function formatBootRequest(cmd, addr, data) {
|
||||
array[6] = 0x90;
|
||||
array[7] = 0xf6;
|
||||
|
||||
array[8] = data.length & 0xff;
|
||||
array[8] = 0;
|
||||
array[9] = data.length & 0xff;
|
||||
|
||||
var offset = 9;
|
||||
var offset = 10;
|
||||
|
||||
var i;
|
||||
for (i = 0; i < data.length; i++){
|
||||
|
Loading…
x
Reference in New Issue
Block a user