From 6fff0c353fdd5b117370a1fb3c7b93a8b8b6a783 Mon Sep 17 00:00:00 2001 From: Conor Patrick Date: Mon, 10 Sep 2018 18:22:14 -0400 Subject: [PATCH] update web api to latest --- web/index.html | 9 ++++ web/js/wallet.js | 98 ++++++++++++++++++++++++++++++++++++-- web/make_localhost_cert.sh | 4 ++ 3 files changed, 107 insertions(+), 4 deletions(-) create mode 100644 web/make_localhost_cert.sh diff --git a/web/index.html b/web/index.html index b6283bf..596a310 100644 --- a/web/index.html +++ b/web/index.html @@ -4,6 +4,15 @@

U2F Bridge Demo

+

+ +

To update firmware, upload JSON file here.

+ + +
+
+ + diff --git a/web/js/wallet.js b/web/js/wallet.js index e752af5..432cf10 100644 --- a/web/js/wallet.js +++ b/web/js/wallet.js @@ -236,6 +236,7 @@ var CMD = { reset: 0x13, version: 0x14, rng: 0x15, + pubkey: 0x16, boot_write: 0x40, boot_done: 0x41, boot_check: 0x42, @@ -870,6 +871,24 @@ var get_rng_ = function(func){ }); }; +// Derive public key from the private key stored on device. Returns X,Y point. 64 bytes. +var get_pubkey_ = function(func){ + + var pinAuth = undefined; + + if (this.pinToken) { + pinAuth = computePinAuth(this.pinToken, CMD.pubkey, 0, 0); + } + + var req = formatRequest(CMD.pubkey,0,0, pinAuth); + + var self = this; + + send_msg(req, function(resp){ + if (func)func(resp); + }); +}; + var is_bootloader_ = function(func){ var req = formatBootRequest(CMD.boot_check); @@ -983,6 +1002,8 @@ function WalletDevice() { this.get_rng = get_rng_; + this.get_pubkey = get_pubkey_; + this.is_bootloader = is_bootloader_; this.bootloader_write = bootloader_write_; @@ -1001,12 +1022,71 @@ function WalletDevice() { this.register = wrap_promise.call(this, this.register ); this.reset = wrap_promise.call(this,this.reset ); this.get_rng = wrap_promise.call(this,this.get_rng); + this.get_pubkey = wrap_promise.call(this,this.get_pubkey); this.is_bootloader = wrap_promise.call(this,this.is_bootloader); this.bootloader_write = wrap_promise.call(this,this.bootloader_write); this.bootloader_finish = wrap_promise.call(this,this.bootloader_finish); } +async function handleFirmware(files) +{ + var dev = new WalletDevice(); + + var p = await dev.is_bootloader(); + + document.getElementById('errors').textContent = ''; + if (p.status != 'CTAP1_SUCCESS') + { + document.getElementById('errors').textContent = 'Make sure device is in bootloader mode. Unplug, hold button, plug in, wait for flashing yellow light.'; + return; + } + + var reader = new FileReader(); + reader.onload = async function(ev){ + var resp = JSON.parse(ev.target.result); + resp.firmware = websafe2string(resp.firmware); + + console.log(resp); + var addr = 0x4000; + var num_pages = 64; + var sig = websafe2array(resp.signature); + var badsig = websafe2array(resp.signature); + badsig[40] = badsig[40] ^ 1; + + var blocks = MemoryMap.fromHex(resp.firmware); + var addresses = blocks.keys(); + + var addr = addresses.next(); + var chunk_size = 244; + while(!addr.done) { + var data = blocks.get(addr.value); + var i; + for (i = 0; i < data.length; i += chunk_size) { + var chunk = data.slice(i,i+chunk_size); + p = await dev.bootloader_write(addr.value + i, chunk); + TEST(p.status == 'CTAP1_SUCCESS', 'Device wrote data'); + var progress = (((i/data.length) * 100 * 100) | 0)/100; + document.getElementById('progress').textContent = ''+progress+' %'; + } + + addr = addresses.next(); + } + p = await dev.bootloader_finish(sig); + if(p.status != 'CTAP1_SUCCESS') + { + document.getElementById('errors').textContent = 'Firmware image signature denied'; + } + else + { + document.getElementById('errors').textContent = 'Update successful'; + } + + }; + + reader.readAsText(files[0]); +} + function TEST(bool, test){ if (bool) { if (test ) console.log("PASS: " + test); @@ -1166,15 +1246,25 @@ async function run_tests() { TEST(p.status == "CTAP1_SUCCESS", 'Right pin works'); } + p = await dev.get_pubkey(); + TEST(p.status == 'CTAP1_SUCCESS', '(1) Wallet derives public key from stored private key'); + p = await dev.register(wif); TEST(p.status == 'CTAP2_ERR_KEY_STORE_FULL', 'Wallet does not accept another key'); p = await dev.sign({challenge: chal}); TEST(p.status == 'CTAP1_SUCCESS', 'Wallet returns signature'); + var sig = p.sig; - var ver = key.verify(chal, p.sig); + var ver = key.verify(chal, sig); TEST(ver, 'Signature is valid'); + p = await dev.get_pubkey(); + TEST(p.status == 'CTAP1_SUCCESS', '(2) Wallet derives public key from stored private key'); + + var key2 = ec.keyFromPublic('04'+array2hex(p.data), 'hex'); + ver = key2.verify(chal, sig); + TEST(ver, 'Signature verifies with the derived public key'); var count = p.count; p = await dev.sign({challenge: chal}); @@ -1374,9 +1464,9 @@ async function run_tests() { //while(1) { await device_start_over(); - await test_pin(); + //await test_pin(); await test_crypto(); - await test_rng(); + //await test_rng(); } //await benchmark(); //await test_persistence(); @@ -1389,5 +1479,5 @@ var test; EC = elliptic.ec -run_tests() +//run_tests() diff --git a/web/make_localhost_cert.sh b/web/make_localhost_cert.sh new file mode 100644 index 0000000..3da47f3 --- /dev/null +++ b/web/make_localhost_cert.sh @@ -0,0 +1,4 @@ +# Run this to make cert you can import in chrome, then run localhost with HTTPS + + openssl req -x509 -out localhost.crt -keyout localhost.key -newkey rsa:2048 -nodes -sha256 -subj '/CN=localhost' -extensions EXT -config <( \ + printf "[dn]\nCN=localhost\n[req]\ndistinguished_name = dn\n[EXT]\nsubjectAltName=DNS:localhost\nkeyUsage=digitalSignature\nextendedKeyUsage=serverAuth")