program using just hid protocol, quicker

This commit is contained in:
Conor Patrick 2018-12-04 00:15:58 -05:00
parent 5a96e82f4d
commit b0cc9cf582
10 changed files with 137 additions and 38 deletions

View File

@ -30,6 +30,7 @@
#include "time.h" #include "time.h"
#include "util.h" #include "util.h"
#include "log.h" #include "log.h"
#include "extensions.h"
#include APP_CONFIG #include APP_CONFIG
typedef enum typedef enum
@ -682,6 +683,23 @@ uint8_t ctaphid_handle_packet(uint8_t * pkt_raw)
printf1(TAG_HID,"CTAPHID_CANCEL\n"); printf1(TAG_HID,"CTAPHID_CANCEL\n");
is_busy = 0; is_busy = 0;
break; 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: default:
printf2(TAG_ERR,"error, unimplemented HID cmd: %02x\r\n", buffer_cmd()); printf2(TAG_ERR,"error, unimplemented HID cmd: %02x\r\n", buffer_cmd());
ctaphid_send_error(cid, CTAP1_ERR_INVALID_COMMAND); ctaphid_send_error(cid, CTAP1_ERR_INVALID_COMMAND);

View File

@ -38,6 +38,9 @@
#define CTAPHID_ERROR (TYPE_INIT | 0x3f) #define CTAPHID_ERROR (TYPE_INIT | 0x3f)
#define CTAPHID_KEEPALIVE (TYPE_INIT | 0x3b) #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_CMD 0x01
#define ERR_INVALID_PAR 0x02 #define ERR_INVALID_PAR 0x02
#define ERR_INVALID_SEQ 0x04 #define ERR_INVALID_SEQ 0x04

View File

@ -25,6 +25,6 @@
int16_t extend_u2f(struct u2f_request_apdu* req, uint32_t len); 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_ */ #endif /* EXTENSIONS_H_ */

View File

@ -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); int8_t u2f_response_writeback(const uint8_t * buf, uint16_t len);
void u2f_reset_response(); void u2f_reset_response();
static CTAP_RESPONSE * _u2f_resp = NULL; static CTAP_RESPONSE * _u2f_resp = NULL;
void u2f_request(struct u2f_request_apdu* req, CTAP_RESPONSE * resp) 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)); uint32_t len = ((req->LC3) | ((uint32_t)req->LC2 << 8) | ((uint32_t)req->LC1 << 16));
uint8_t byte; uint8_t byte;
_u2f_resp = resp; u2f_set_writeback_buffer(resp);
if (req->cla != 0) if (req->cla != 0)
{ {
@ -137,6 +138,10 @@ void u2f_reset_response()
ctap_response_init(_u2f_resp); ctap_response_init(_u2f_resp);
} }
void u2f_set_writeback_buffer(CTAP_RESPONSE * resp)
{
_u2f_resp = resp;
}
static void dump_signature_der(uint8_t * sig) static void dump_signature_der(uint8_t * sig)
{ {

View File

@ -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); int8_t u2f_response_writeback(const uint8_t * buf, uint16_t len);
void u2f_reset_response(); void u2f_reset_response();
void u2f_set_writeback_buffer(CTAP_RESPONSE * resp);
int16_t u2f_version(); int16_t u2f_version();

View File

@ -28,8 +28,9 @@ typedef struct {
uint8_t op; uint8_t op;
uint8_t addr[3]; uint8_t addr[3];
uint8_t tag[4]; uint8_t tag[4];
uint8_t len; uint8_t lenh;
uint8_t payload[255 - 9]; uint8_t lenl;
uint8_t payload[255 - 10];
} __attribute__((packed)) BootloaderReq; } __attribute__((packed)) BootloaderReq;
@ -49,30 +50,29 @@ static void authorize_application()
ptr = (uint32_t *)AUTH_WORD_ADDR; ptr = (uint32_t *)AUTH_WORD_ADDR;
flash_write((uint32_t)ptr, (uint8_t *)&zero, 4); flash_write((uint32_t)ptr, (uint8_t *)&zero, 4);
} }
int is_authorized_to_boot() int is_authorized_to_boot()
{ {
uint32_t * auth = (uint32_t *)AUTH_WORD_ADDR; uint32_t * auth = (uint32_t *)AUTH_WORD_ADDR;
return *auth == 0; 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; static int has_erased = 0;
BootloaderReq * req = (BootloaderReq * )keyh; BootloaderReq * req = (BootloaderReq * )keyh;
uint8_t payload[256];
uint8_t hash[32]; uint8_t hash[32];
uint8_t version = 1; 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"; if (len > klen-10)
const struct uECC_Curve_t * curve = NULL;
if (req->len > 255-9)
{ {
printf1(TAG_BOOT,"Invalid length %d / %d\r\n", len, klen-9);
return CTAP1_ERR_INVALID_LENGTH; return CTAP1_ERR_INVALID_LENGTH;
} }
memset(payload, 0xff, sizeof(payload)); 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";
memmove(payload, req->payload, req->len); const struct uECC_Curve_t * curve = NULL;
uint32_t addr = ((*((uint32_t*)req->addr)) & 0xffffff) | 0x8000000; uint32_t addr = ((*((uint32_t*)req->addr)) & 0xffffff) | 0x8000000;
@ -82,7 +82,7 @@ int bootloader_bridge(uint8_t klen, uint8_t * keyh)
case BootWrite: case BootWrite:
printf1(TAG_BOOT, "BootWrite: %08lx\r\n",(uint32_t)ptr); printf1(TAG_BOOT, "BootWrite: %08lx\r\n",(uint32_t)ptr);
if ((uint32_t)ptr < APPLICATION_START_ADDR || (uint32_t)ptr >= APPLICATION_END_ADDR 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); printf1(TAG_BOOT,"Bound exceeded [%08lx, %08lx]\r\n",APPLICATION_START_ADDR,APPLICATION_END_ADDR);
return CTAP2_ERR_NOT_ALLOWED; return CTAP2_ERR_NOT_ALLOWED;
@ -99,11 +99,17 @@ int bootloader_bridge(uint8_t klen, uint8_t * keyh)
exit(1); exit(1);
} }
flash_write((uint32_t)ptr,payload, req->len); flash_write((uint32_t)ptr,req->payload, len);
break; break;
case BootDone: case BootDone:
printf1(TAG_BOOT, "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; ptr = (uint32_t *)APPLICATION_START_ADDR;
crypto_sha256_init(); crypto_sha256_init();
crypto_sha256_update((uint8_t*)ptr, APPLICATION_END_ADDR-APPLICATION_START_ADDR); 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, if (! uECC_verify(pubkey,
hash, hash,
32, 32,
payload, req->payload,
curve)) curve))
{ {
return CTAP2_ERR_OPERATION_DENIED; return CTAP2_ERR_OPERATION_DENIED;
} }
#endif
authorize_application(); authorize_application();
REBOOT_FLAG = 1; REBOOT_FLAG = 1;
break; break;

View File

@ -70,7 +70,8 @@ int main(int argc, char * argv[])
/*TAG_U2F|*/ /*TAG_U2F|*/
// TAG_PARSE | // TAG_PARSE |
// TAG_TIME| // TAG_TIME|
// TAG_DUMP| // TAG_DUMP|
// TAG_DUMP2|
TAG_BOOT| TAG_BOOT|
TAG_EXT| TAG_EXT|
TAG_GREEN| TAG_GREEN|

View File

@ -74,7 +74,6 @@ class Tester():
#cmd,resp = self.recv_raw() #cmd,resp = self.recv_raw()
def send_data(self, cmd, data): def send_data(self, cmd, data):
#print('<<', hexlify(data))
if type(data) != type(b''): if type(data) != type(b''):
data = struct.pack('%dB' % len(data), *[ord(x) for x in data]) data = struct.pack('%dB' % len(data), *[ord(x) for x in data])
with Timeout(1.0) as event: with Timeout(1.0) as event:

View File

@ -1,7 +1,7 @@
# Programs solo using the Solo bootloader # Programs solo using the Solo bootloader
# Requires python-fido2, intelhex # Requires python-fido2, intelhex
import sys,os,time,struct import sys,os,time,struct,argparse
import array,struct,socket,json,base64 import array,struct,socket,json,base64
import tempfile import tempfile
from binascii import hexlify from binascii import hexlify
@ -10,6 +10,7 @@ from fido2.hid import CtapHidDevice, CTAPHID
from fido2.client import Fido2Client, ClientError from fido2.client import Fido2Client, ClientError
from fido2.ctap import CtapError from fido2.ctap import CtapError
from fido2.ctap1 import CTAP1 from fido2.ctap1 import CTAP1
from fido2.utils import Timeout
from intelhex import IntelHex from intelhex import IntelHex
@ -22,12 +23,26 @@ class SoloBootloader:
erase = 0x43 erase = 0x43
version = 0x44 version = 0x44
HIDCommand = 0x50
TAG = b'\x8C\x27\x90\xf6' TAG = b'\x8C\x27\x90\xf6'
class Programmer(): class Programmer():
def __init__(self,): def __init__(self,):
self.origin = 'https://example.org' 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,): def find_device(self,):
dev = next(CtapHidDevice.list_devices(), None) dev = next(CtapHidDevice.list_devices(), None)
@ -36,16 +51,36 @@ class Programmer():
self.dev = dev self.dev = dev
self.ctap1 = CTAP1(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 @staticmethod
def format_request(cmd,addr = 0,data = b'A'*16): def format_request(cmd,addr = 0,data = b'A'*16):
arr = b'\x00'*9 arr = b'\x00'*9
addr = struct.pack('<L', addr) addr = struct.pack('<L', addr)
cmd = struct.pack('B', cmd) 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 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 appid = b'A'*32
chal = b'B'*32 chal = b'B'*32
@ -77,21 +112,32 @@ class Programmer():
def program_file(self,name): def program_file(self,name):
data = json.loads(open(name,'r').read()) data = json.loads(open(name,'r').read())
fw = base64.b64decode(from_websafe(data['firmware']).encode()) if name.lower().endswith('.json'):
sig = base64.b64decode(from_websafe(data['signature']).encode()) 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() if self.exchange == self.exchange_hid:
tmp = tempfile.NamedTemporaryFile(delete=False) chunk = 2048
tmp.write(fw) else:
tmp.seek(0) chunk = 240
tmp.close()
ih.fromfile(tmp.name, format='hex')
chunk = 240
seg = ih.segments()[0] seg = ih.segments()[0]
size = seg[1] - seg[0] size = seg[1] - seg[0]
total = 0 total = 0
t1 = time.time()*1000 t1 = time.time()*1000
print('erasing...')
for i in range(seg[0], seg[1], chunk): for i in range(seg[0], seg[1], chunk):
s = i s = i
e = min(i+chunk,seg[1]) e = min(i+chunk,seg[1])
@ -100,23 +146,41 @@ class Programmer():
total += chunk total += chunk
progress = total/float(size)*100 progress = total/float(size)*100
sys.stdout.write('downloading %.2f%%...\r' % progress) 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 t2 = time.time()*1000
print('time: %.2f s' % ((t2-t1)/1000.0)) print('time: %.2f s' % ((t2-t1)/1000.0))
print('Verifying...') 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 __name__ == '__main__':
if len(sys.argv) != 2:
print('usage: %s <firmware.json>' % sys.argv[0]) parser = argparse.ArgumentParser()
sys.exit(1) 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 = Programmer()
p.find_device() p.find_device()
if args.use_u2f:
p.use_u2f()
if args.no_reset:
p.set_reboot(False)
print('version is ', p.version()) 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)

View File

@ -425,9 +425,10 @@ function formatBootRequest(cmd, addr, data) {
array[6] = 0x90; array[6] = 0x90;
array[7] = 0xf6; array[7] = 0xf6;
array[8] = data.length & 0xff; array[8] = 0;
array[9] = data.length & 0xff;
var offset = 9; var offset = 10;
var i; var i;
for (i = 0; i < data.length; i++){ for (i = 0; i < data.length; i++){