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 "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);

View File

@ -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

View File

@ -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_ */

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);
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)
{

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);
void u2f_reset_response();
void u2f_set_writeback_buffer(CTAP_RESPONSE * resp);
int16_t u2f_version();

View File

@ -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;

View File

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

View File

@ -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:

View File

@ -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())
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')
if self.exchange == self.exchange_hid:
chunk = 2048
else:
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...')
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)

View File

@ -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++){