passing all tests

This commit is contained in:
Conor Patrick 2018-07-13 00:48:46 -04:00
parent 202f76d313
commit 42fe31af17
3 changed files with 431 additions and 128 deletions

View File

@ -1455,7 +1455,7 @@ int8_t ctap_load_key(uint8_t index, uint8_t * key)
return ERR_NO_KEY_SPACE; return ERR_NO_KEY_SPACE;
} }
if (STATE.key_lens[index] == 0) if (STATE.key_lens[index] == 0xffff)
{ {
return ERR_KEY_SPACE_EMPTY; return ERR_KEY_SPACE_EMPTY;
} }
@ -1481,6 +1481,12 @@ void ctap_reset()
authenticator_write_state(&STATE, 0); authenticator_write_state(&STATE, 0);
authenticator_write_state(&STATE, 1); authenticator_write_state(&STATE, 1);
if (ctap_generate_rng(PIN_TOKEN, PIN_TOKEN_SIZE) != 1)
{
printf2(TAG_ERR,"Error, rng failed\n");
exit(1);
}
ctap_reset_state(); ctap_reset_state();
memset(PIN_CODE_HASH,0,sizeof(PIN_CODE_HASH)); memset(PIN_CODE_HASH,0,sizeof(PIN_CODE_HASH));
crypto_ecc256_make_key_pair(KEY_AGREEMENT_PUB, KEY_AGREEMENT_PRIV); crypto_ecc256_make_key_pair(KEY_AGREEMENT_PUB, KEY_AGREEMENT_PRIV);

View File

@ -41,6 +41,7 @@ typedef enum
WalletPin = 0x12, WalletPin = 0x12,
WalletReset= 0x13, WalletReset= 0x13,
WalletVersion= 0x14, WalletVersion= 0x14,
WalletRng = 0x15,
} WalletOperation; } WalletOperation;
@ -198,7 +199,14 @@ int16_t bridge_u2f_to_wallet(uint8_t * _chal, uint8_t * _appid, uint8_t klen, ui
printf1(TAG_WALLET, "u2f2wallet [%d]: ",reqlen); dump_hex1(TAG_WALLET, msg_buf,reqlen); printf1(TAG_WALLET, "u2f2wallet [%d]: ",reqlen); dump_hex1(TAG_WALLET, msg_buf,reqlen);
count = ctap_atomic_count(0); if (req->operation != WalletRng)
{
count = ctap_atomic_count(0);
}
else
{
count = 0;
}
u2f_response_writeback(&up,1); u2f_response_writeback(&up,1);
u2f_response_writeback((uint8_t *)&count,4); u2f_response_writeback((uint8_t *)&count,4);
u2f_response_writeback((uint8_t *)&ret,1); u2f_response_writeback((uint8_t *)&ret,1);
@ -274,7 +282,7 @@ int16_t bridge_u2f_to_wallet(uint8_t * _chal, uint8_t * _appid, uint8_t klen, ui
keysize = ctap_key_len(0); keysize = ctap_key_len(0);
crypto_load_external_key(key, keysize); crypto_load_external_key(key, keysize);
crypto_ecdsa_sign(args[0], lens[0], sig, MBEDTLS_ECP_DP_SECP256R1); crypto_ecdsa_sign(args[0], lens[0], sig, MBEDTLS_ECP_DP_SECP256K1);
u2f_response_writeback(sig,64); u2f_response_writeback(sig,64);
@ -314,6 +322,8 @@ int16_t bridge_u2f_to_wallet(uint8_t * _chal, uint8_t * _appid, uint8_t klen, ui
memmove(chksum, args[0] + lens[0] - 4, 4); memmove(chksum, args[0] + lens[0] - 4, 4);
lens[0] -= 4; lens[0] -= 4;
printf("chksum: "); dump_hex1(TAG_WALLET, chksum, 4);
// perform integrity check // perform integrity check
printf1(TAG_WALLET,"shasum on [%d]: ",lens[0]); dump_hex1(TAG_WALLET, args[0], lens[0]); printf1(TAG_WALLET,"shasum on [%d]: ",lens[0]); dump_hex1(TAG_WALLET, args[0], lens[0]);
crypto_sha256_init(); crypto_sha256_init();
@ -323,6 +333,8 @@ int16_t bridge_u2f_to_wallet(uint8_t * _chal, uint8_t * _appid, uint8_t klen, ui
crypto_sha256_update(shasum, 32); crypto_sha256_update(shasum, 32);
crypto_sha256_final(shasum); crypto_sha256_final(shasum);
printf1(TAG_WALLET,"shasum: "); dump_hex1(TAG_WALLET, shasum, 32);
if (memcmp(shasum, chksum, 4) != 0) if (memcmp(shasum, chksum, 4) != 0)
{ {
ret = CTAP2_ERR_CREDENTIAL_NOT_VALID; ret = CTAP2_ERR_CREDENTIAL_NOT_VALID;
@ -390,8 +402,39 @@ int16_t bridge_u2f_to_wallet(uint8_t * _chal, uint8_t * _appid, uint8_t klen, ui
break; break;
case WalletVersion: case WalletVersion:
u2f_response_writeback(WALLET_VERSION, sizeof(WALLET_VERSION)); u2f_response_writeback(WALLET_VERSION, sizeof(WALLET_VERSION)-1);
break; break;
case WalletRng:
printf1(TAG_WALLET,"WalletRng\n");
if ( ctap_device_locked() )
{
printf1(TAG_ERR,"device locked\n");
ret = CTAP2_ERR_NOT_ALLOWED;
goto cleanup;
}
if ( ctap_is_pin_set() )
{
if ( ! check_pinhash(req->pinAuth, msg_buf, reqlen))
{
printf1(TAG_WALLET,"pinAuth is NOT valid\n");
dump_hex(msg_buf,reqlen);
ret = CTAP2_ERR_PIN_AUTH_INVALID;
goto cleanup;
}
}
ret = ctap_generate_rng(sig, 72);
if (ret != 1)
{
printf1(TAG_WALLET,"Rng failed\n");
ret = CTAP2_ERR_PROCESSING;
goto cleanup;
}
ret = 0;
u2f_response_writeback((uint8_t *)sig,72);
break;
default: default:
printf2(TAG_ERR,"Invalid wallet command: %x\n",req->operation); printf2(TAG_ERR,"Invalid wallet command: %x\n",req->operation);
ret = CTAP1_ERR_INVALID_COMMAND; ret = CTAP1_ERR_INVALID_COMMAND;

View File

@ -3,6 +3,41 @@ DEVELOPMENT = 1;
var to_b58 = function(B){var A="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";var d=[],s="",i,j,c,n;for(i in B){j=0,c=B[i];s+=c||s.length^i?"":1;while(j in d||c){n=d[j];n=n?n*256+c:c;c=n/58|0;d[j]=n%58;j++}}while(j--)s+=A[d[j]];return s}; var to_b58 = function(B){var A="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";var d=[],s="",i,j,c,n;for(i in B){j=0,c=B[i];s+=c||s.length^i?"":1;while(j in d||c){n=d[j];n=n?n*256+c:c;c=n/58|0;d[j]=n%58;j++}}while(j--)s+=A[d[j]];return s};
var from_b58 = function(S){var A="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";var d=[],b=[],i,j,c,n;for(i in S){j=0,c=A.indexOf(S[i]);if(c<0)throw new Error('Invald b58 character');c||b.length^i?i:b.push(0);while(j in d||c){n=d[j];n=n?n*58+c:c;c=n>>8;d[j]=n%256;j++}}while(j--)b.push(d[j]);return new Uint8Array(b)}; var from_b58 = function(S){var A="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";var d=[],b=[],i,j,c,n;for(i in S){j=0,c=A.indexOf(S[i]);if(c<0)throw new Error('Invald b58 character');c||b.length^i?i:b.push(0);while(j in d||c){n=d[j];n=n?n*58+c:c;c=n>>8;d[j]=n%256;j++}}while(j--)b.push(d[j]);return new Uint8Array(b)};
// Calculate the Shannon entropy of a string in bits per symbol.
// https://gist.github.com/jabney/5018b4adc9b2bf488696
(function(shannon) {
'use strict';
// Create a dictionary of character frequencies and iterate over it.
function process(s, evaluator) {
var h = Object.create(null), k;
s.split('').forEach(function(c) {
h[c] && h[c]++ || (h[c] = 1); });
if (evaluator) for (k in h) evaluator(k, h[k]);
return h;
};
// Measure the entropy of a string in bits per symbol.
shannon.entropy = function(s) {
var sum = 0,len = s.length;
process(s, function(k, f) {
var p = f/len;
sum -= p * Math.log(p) / Math.log(2);
});
return sum;
};
// Measure the entropy of a string in total bits.
shannon.bits = function(s) {
return shannon.entropy(s) * s.length;
};
// Log the entropy of a string to the console.
shannon.log = function(s) {
console.log('Entropy of "' + s + '" in bits per symbol:', shannon.entropy(s));
};
})(window.shannon = window.shannon || Object.create(null));
function hex(byteArray, join) { function hex(byteArray, join) {
if (join === undefined) join = ' '; if (join === undefined) join = ' ';
return Array.from(byteArray, function(byte) { return Array.from(byteArray, function(byte) {
@ -197,6 +232,7 @@ var CMD = {
pin: 0x12, pin: 0x12,
reset: 0x13, reset: 0x13,
version: 0x14, version: 0x14,
rng: 0x15,
}; };
var PIN = { var PIN = {
@ -233,6 +269,7 @@ function parse_device_response(arr)
data = null; data = null;
if (arr[5] == 0) { if (arr[5] == 0) {
data = arr.slice(6,arr.length); data = arr.slice(6,arr.length);
} }
return {count: count, status: error2string(arr[5]), data: data}; return {count: count, status: error2string(arr[5]), data: data};
} }
@ -417,7 +454,11 @@ function signRequestFormat(sigAlg,pinToken,challenge,keyid) {
var args = [challenge]; var args = [challenge];
if (keyid) args.push(keyid) if (keyid) args.push(keyid)
var pinAuth = computePinAuth(pinToken,cmd,p1,p2,args); var pinAuth;
if (pinToken) pinAuth = computePinAuth(pinToken,cmd,p1,p2,args);
else pinAuth = new Uint8Array(16);
var req = formatRequest(cmd,p1,p2,pinAuth,args); var req = formatRequest(cmd,p1,p2,pinAuth,args);
return req; return req;
@ -432,7 +473,11 @@ function registerRequestFormat(wifkey, pinToken) {
var keyarr = from_b58(wifkey); var keyarr = from_b58(wifkey);
var args = [keyarr]; var args = [keyarr];
var pinAuth = computePinAuth(pinToken,cmd,p1,p2,args); var pinAuth;
if (pinToken) pinAuth = computePinAuth(pinToken,cmd,p1,p2,args);
else pinAuth = new Uint8Array(16);
var req = formatRequest(cmd,p1,p2,pinAuth,args); var req = formatRequest(cmd,p1,p2,pinAuth,args);
return req; return req;
@ -466,23 +511,26 @@ var get_shared_secret_ = function(func) {
var self = this; var self = this;
send_msg(req, function(resp){ send_msg(req, function(resp){
var i; if (resp.status == 'CTAP1_SUCCESS') {
var i;
var devicePubkeyHex = '04'+hex(resp.data,''); var devicePubkeyHex = '04'+hex(resp.data,'');
var devicePubkey = self.ecp256.keyFromPublic(devicePubkeyHex,'hex'); var devicePubkey = self.ecp256.keyFromPublic(devicePubkeyHex,'hex');
// Generate a new key pair for shared secret // Generate a new key pair for shared secret
var platform_keypair = self.ecp256.genKeyPair(); var platform_keypair = self.ecp256.genKeyPair();
self.platform_keypair = platform_keypair; self.platform_keypair = platform_keypair;
// shared secret // shared secret
var shared = platform_keypair.derive(devicePubkey.getPublic()).toArray(); var shared = platform_keypair.derive(devicePubkey.getPublic()).toArray();
var hash = sha256.create(); var hash = sha256.create();
hash.update(shared); hash.update(shared);
shared = hash.array(); shared = hash.array();
if (func) func(shared); resp.data = shared;
}
if (func) func(resp);
}); });
}; };
@ -514,9 +562,16 @@ var authenticate_ = function(pin, func){
send_msg(req, function(resp){ send_msg(req, function(resp){
var aesCbc = new aesjs.ModeOfOperation.cbc(self.shared_secret, iv); var aesCbc = new aesjs.ModeOfOperation.cbc(self.shared_secret, iv);
var pinTokenEnc = resp.data; var pinTokenEnc = resp.data;
var pinToken = aesCbc.decrypt(pinTokenEnc); if (resp.status == 'CTAP1_SUCCESS') {
self.pinToken = pinToken; var pinToken = aesCbc.decrypt(pinTokenEnc);
if (func) func({pinToken: pinToken, status: resp.status}); self.pinToken = pinToken;
if (func) func({pinToken: pinToken, status: resp.status});
} else {
self.init(function(){
if (func) func(resp);
});
}
}); });
}; };
@ -551,7 +606,7 @@ function pin2bytes(pin){
return pinBytesPadded; return pinBytesPadded;
} }
var set_pin_ = function(pin, func, failAuth){ var set_pin_ = function(pin, failAuth, func){
var subcmd = PIN.setPin; var subcmd = PIN.setPin;
var pinBytesPadded = pin2bytes(pin); var pinBytesPadded = pin2bytes(pin);
@ -565,6 +620,11 @@ var set_pin_ = function(pin, func, failAuth){
var pinAuth = computePinAuthRaw(this.shared_secret, pinEnc); var pinAuth = computePinAuthRaw(this.shared_secret, pinEnc);
if (func == undefined && typeof failAuth == 'function'){
func = failAuth;
failAuth = false;
}
if (failAuth){ if (failAuth){
pinAuth.fill(0xAA); pinAuth.fill(0xAA);
pinEnc.fill(0xAA); pinEnc.fill(0xAA);
@ -582,17 +642,18 @@ var set_pin_ = function(pin, func, failAuth){
var is_pin_set_ = function(func) var is_pin_set_ = function(func)
{ {
this.set_pin('12345', function(resp){ this.set_pin('12345', true, function(resp){
if (resp.status == "CTAP2_ERR_NOT_ALLOWED") { if (resp.status == "CTAP2_ERR_NOT_ALLOWED") {
func(true); func({data:true, status: 'CTAP1_SUCCESS'});
} }
else if (resp.status == "CTAP2_ERR_PIN_AUTH_INVALID"){ else if (resp.status == "CTAP2_ERR_PIN_AUTH_INVALID"){
func(false); func({data:false, status: 'CTAP1_SUCCESS'});
} }
else { else {
throw new Error("Device returned expected status: " + stat); func({data: undefined, status: resp.status});
//throw new Error("Device returned expected status: " + stat);
} }
}, true); });
} }
var change_pin_ = function(curpin, newpin, func, failAuth){ var change_pin_ = function(curpin, newpin, func, failAuth){
@ -629,7 +690,7 @@ var change_pin_ = function(curpin, newpin, func, failAuth){
var req = pinRequestFormat(subcmd, pinAuth, ourPubkeyBytes, newPinEnc, curPinHashEnc); var req = pinRequestFormat(subcmd, pinAuth, ourPubkeyBytes, newPinEnc, curPinHashEnc);
send_msg(req, function(resp){ send_msg(req, function(resp){
if (func) func(resp.status); if (func) func(resp);
}); });
} }
@ -639,7 +700,8 @@ var get_retries_ = function(func){
var req = pinRequestFormat(subcmd); var req = pinRequestFormat(subcmd);
send_msg(req, function(resp){ send_msg(req, function(resp){
if (func) func(resp.data[0]); resp.data = resp.data[0];
if (func) func(resp);
}); });
} }
@ -650,9 +712,21 @@ var sign_ = function(obj, func){
var alg = obj.alg || 3; var alg = obj.alg || 3;
var req = signRequestFormat(alg,this.pinToken,obj.challenge,obj.keyid); var pinToken = this.pinToken || undefined;
var req = signRequestFormat(alg,pinToken,obj.challenge,obj.keyid);
send_msg(req, function(resp){ send_msg(req, function(resp){
if (resp.status == 'CTAP1_SUCCESS') {
var r = resp.data.slice(0,32);
var s = resp.data.slice(32,64);
r = array2hex(r);
s = array2hex(s);
resp.sig = {};
resp.sig.r = r;
resp.sig.s = s;
}
if (func) func(resp); if (func) func(resp);
}); });
@ -697,6 +771,25 @@ var reset_ = function(func){
}); });
}; };
// Read 72 random bytes from hardware RNG on device
var get_rng_ = function(func){
var pinAuth = undefined;
if (this.pinToken) {
pinAuth = computePinAuth(this.pinToken, CMD.rng, 0, 0);
}
var req = formatRequest(CMD.rng,0,0, pinAuth);
var self = this;
send_msg(req, function(resp){
if (func)func(resp);
});
};
function wrap_promise(func) function wrap_promise(func)
{ {
var self = this; var self = this;
@ -704,20 +797,26 @@ function wrap_promise(func)
var args = arguments; var args = arguments;
return new Promise(function(resolve,reject){ return new Promise(function(resolve,reject){
var i; var i;
var oldfunc = null;
for (i = 0; i < args.length; i++) for (i = 0; i < args.length; i++)
{ {
if (typeof args[i] == 'function') if (typeof args[i] == 'function')
{ {
var oldfunc = args[i]; oldfunc = args[i];
args[i] = function(){ args[i] = function(){
oldfunc.apply(self,arguments); oldfunc.apply(self,arguments);
resolve.apply(self,arguments); resolve.apply(self,arguments);
//oldfunc.call(arguments);
//resolve.call(arguments);
}; };
break; break;
} }
} }
if (oldfunc === null)
{
args = Array.prototype.slice.call(args);
args.push(function(){
resolve.apply(self,arguments);
});
}
func.apply(self,args); func.apply(self,args);
}); });
} }
@ -733,9 +832,11 @@ function WalletDevice() {
this.init = function(func){ this.init = function(func){
self.get_version(function(ver){ self.get_version(function(ver){
self.version = ver; self.version = ver;
self.get_shared_secret(function(shared){ self.get_shared_secret(function(resp){
self.shared_secret = shared; if (resp.status == "CTAP1_SUCCESS") self.shared_secret = resp.data;
if (func) func(); else {
}
if (func) func(resp);
}); });
}); });
}; };
@ -768,18 +869,31 @@ function WalletDevice() {
this.reset = reset_; this.reset = reset_;
//this.init = wrap_promise(this.init); this.get_rng = get_rng_;
//this.get_version = wrap_promise(this.get_version);
//this.get_shared_secret = wrap_promise(this.get_shared_secret );
//this.authenticate = wrap_promise(this.authenticate );
//this.sign = wrap_promise(this.sign );
//this.set_pin = wrap_promise(this.set_pin );
//this.is_pin_set = wrap_promise(this.is_pin_set );
//this.change_pin = wrap_promise(this.change_pin );
//this.get_retries = wrap_promise(this.get_retries );
//this.register = wrap_promise(this.register );
//this.reset = wrap_promise(this.reset );
this.init = wrap_promise.call(this, this.init);
this.get_version = wrap_promise.call(this, this.get_version);
this.get_shared_secret = wrap_promise.call(this, this.get_shared_secret );
this.authenticate = wrap_promise.call(this,this.authenticate );
this.sign = wrap_promise.call(this, this.sign );
this.set_pin = wrap_promise.call(this,this.set_pin );
this.is_pin_set = wrap_promise.call(this, this.is_pin_set );
this.change_pin = wrap_promise.call(this, this.change_pin );
this.get_retries = wrap_promise.call(this, this.get_retries );
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);
}
function TEST(bool, test){
if (bool) {
if (test ) console.log("PASS: " + test);
}
else {
console.log("FAIL: " + test);
throw new Error("FAIL: " + test);
}
} }
async function run_tests() { async function run_tests() {
@ -788,92 +902,232 @@ async function run_tests() {
var pin = "Conor's pin 👽 "; var pin = "Conor's pin 👽 ";
var pin2 = "sogyhdxoh3qwli😀"; var pin2 = "sogyhdxoh3qwli😀";
function string2challenge(chal) {
function device_start_over(next)
{
dev.init(function(resp){
console.log('connected. version: ', dev.version);
dev.is_pin_set(function(bool){
if (bool) {
dev.authenticate(pin, function(resp){
//reset_device(next);
});
}
else {
//reset_device(next);
}
});
});
}
function reset_device(func)
{
dev.reset(function(resp){
console.log("reset: ",resp);
if (func) func();
});
}
function test_pin(next)
{
dev.set_pin(pin, function(resp){
if (resp.status == "CTAP1_SUCCESS"){
console.log('Set pin to ' + pin);
dev.set_pin(pin, function(resp){
});
}
else {
console.log("Fail set_pin");
}
});
}
device_start_over();
function t2 ()
{
var ec = new EC('p256');
var key = ec.genKeyPair();
var priv = key.getPrivate('hex');
// convert to wif
priv = key2wif(priv);
var chal = 'ogfhriodghdro;igh';
var hash = sha256.create(); var hash = sha256.create();
hash.update(chal); hash.update(chal);
chal = hash.array(); chal = hash.array();
return chal;
dev.register(priv, function(resp){
console.log('register response', resp);
dev.sign({challenge: chal}, function(resp){
var r = resp.data.slice(0,32);
var s = resp.data.slice(32,64);
r = array2hex(r);
s = array2hex(s);
var sig = {r: r, s: s};
console.log('sign response', resp);
var ver = key.verify(chal, sig);
console.log("verify: ",ver);
});
});
} }
async function device_start_over()
{
var p = await dev.init();
if (p.status == 'CTAP2_ERR_NOT_ALLOWED') { // its already locked
p = await dev.reset();
TEST(p.status == "CTAP1_SUCCESS", 'Device reset');
p = await dev.init();
TEST(p.status == "CTAP1_SUCCESS", 'Device initialize');
} else {
TEST(p.status == "CTAP1_SUCCESS", 'Device initialize');
//console.log(dev);
TEST(dev.version == "WALLET_V1.0", 'Device reports right version');
p = await dev.is_pin_set();
TEST(p.status == "CTAP1_SUCCESS", 'Check if pin is set');
if (!p.data) {
} else {
p = await dev.authenticate(pin);
if (p.status == "CTAP2_ERR_PIN_INVALID" ) {
p = await dev.authenticate(pin2); // try second pin
}
else {
}
TEST(p.status == "CTAP1_SUCCESS", 'Authenticated');
}
p = await dev.reset();
TEST(p.status == "CTAP1_SUCCESS", 'Device reset');
}
}
async function test_pin()
{
var p = await dev.is_pin_set();
TEST(p.status == "CTAP1_SUCCESS" && !p.data, 'Pin is not set');
p = await dev.set_pin(pin);
TEST(p.status == "CTAP1_SUCCESS", 'A pin was set');
p = await dev.is_pin_set();
TEST(p.status == "CTAP1_SUCCESS" && p.data, 'Pin set is detected');
p = await dev.set_pin(pin);
TEST(p.status == "CTAP2_ERR_NOT_ALLOWED", 'Trying to set a pin again will fail');
p = await dev.change_pin(pin, pin2);
TEST(p.status == "CTAP1_SUCCESS", 'Going through change pin process is successful');
p = await dev.authenticate(pin);
TEST(p.status == "CTAP2_ERR_PIN_INVALID", 'Authenticating to previous/wrong pin is denied');
p = await dev.get_retries();
TEST(p.status == "CTAP1_SUCCESS" && p.data > 2, 'Have at least 2 tries left ('+p.data+')');
var tries = p.data;
p = await dev.authenticate(pin);
TEST(p.status == "CTAP2_ERR_PIN_INVALID");
p = await dev.get_retries();
TEST(p.status == "CTAP1_SUCCESS" && (p.data > 1) && (p.data < tries),
'Have less attempts left after another failed attempt (' + p.data+')');
p = await dev.authenticate(pin2);
TEST(p.status == "CTAP1_SUCCESS", 'Authenticating with correct pin success');
p = await dev.get_retries();
TEST(p.status == "CTAP1_SUCCESS" && p.data > tries, 'Retries reset ('+ p.data+')');
p = await dev.change_pin(pin2, pin);
TEST(p.status == "CTAP1_SUCCESS", 'Change pin back');
// Reset device for next set of tests
p = await dev.authenticate(pin);
TEST(p.status == "CTAP1_SUCCESS");
p = await dev.reset();
TEST(p.status == "CTAP1_SUCCESS");
}
async function test_crypto(){
var ec = new EC('secp256k1');
var key = ec.genKeyPair();
var priv = key.getPrivate('hex');
var wif = key2wif(priv); // convert to wif
// Corrupt 1 byte
var b = (wif[32] == 'A') ? 'B' : 'A';
var badwif = wif.substring(0, 32) + b + wif.substring(32+1);
var p = await dev.set_pin(pin);
TEST(p.status == "CTAP1_SUCCESS");
var chal = string2challenge('abc');
p = await dev.sign({challenge: chal});
TEST(p.status == 'CTAP2_ERR_PIN_AUTH_INVALID', 'No signature without authenticating first');
p = await dev.register(wif);
TEST(p.status == 'CTAP2_ERR_PIN_AUTH_INVALID', 'No key register without authenticating first');
p = await dev.get_rng();
TEST(p.status == "CTAP2_ERR_PIN_AUTH_INVALID", 'No rng without authenticating first');
p = await dev.authenticate(pin);
TEST(p.status == "CTAP1_SUCCESS");
p = await dev.sign({challenge: chal});
TEST(p.status == 'CTAP2_ERR_NO_CREDENTIALS', 'No signature without key');
p = await dev.register(badwif);
TEST(p.status == 'CTAP2_ERR_CREDENTIAL_NOT_VALID', 'Wallet does not accept corrupted key');
p = await dev.register(wif);
TEST(p.status == 'CTAP1_SUCCESS', 'Wallet accepts good WIF 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 ver = key.verify(chal, p.sig);
TEST(ver, 'Signature is valid');
var count = p.count;
p = await dev.sign({challenge: chal});
ver = key.verify(chal, p.sig);
TEST(p.status == 'CTAP1_SUCCESS' && p.count > count && ver, 'Count increments for each signature');
// Test lockout
console.log("Exceeding all pin attempts...");
p = await dev.get_retries();
TEST(p.status == "CTAP1_SUCCESS");
var tries = p.data;
while (tries > 0) {
p = await dev.authenticate('1234'); // wrong pin
TEST(p.status == "CTAP2_ERR_PIN_INVALID");
p = await dev.get_retries();
TEST(p.status == "CTAP1_SUCCESS");
tries = p.data;
}
p = await dev.get_retries();
TEST(p.status == "CTAP1_SUCCESS");
tries = p.data;
TEST(tries == 0, 'Device has 0 tries left (lockout)');
p = await dev.register(wif);
TEST(p.status == 'CTAP2_ERR_PIN_AUTH_INVALID', 'Register is denied');
p = await dev.sign({challenge: chal});
TEST(p.status == 'CTAP2_ERR_PIN_AUTH_INVALID', 'Sign is denied');
p = await dev.set_pin(pin);
TEST(p.status == "CTAP2_ERR_NOT_ALLOWED", 'set_pin is locked out');
p = await dev.set_pin(pin,pin2);
TEST(p.status == "CTAP2_ERR_NOT_ALLOWED", 'change_pin is locked out');
p = await dev.get_rng();
TEST(p.status == "CTAP2_ERR_NOT_ALLOWED", 'get_rng is locked out');
p = await dev.init();
TEST(p.status == "CTAP2_ERR_NOT_ALLOWED", 'init (getKeyAgreement) is locked out');
p = await dev.reset();
TEST(p.status == "CTAP1_SUCCESS");
p = await dev.get_retries();
TEST(p.status == "CTAP1_SUCCESS");
tries = p.data;
p = await dev.is_pin_set();
TEST(p.status == "CTAP1_SUCCESS");
var is_pin_set = p.data;
p = await dev.sign({challenge: chal});
TEST(p.status == 'CTAP2_ERR_NO_CREDENTIALS');
TEST(tries > 2 && is_pin_set == false, 'Device is no longer locked after reset and pin and key are gone');
}
async function test_rng(){
var pool = '';
var p = await dev.get_rng();
TEST(p.status == "CTAP1_SUCCESS", 'Rng responds');
pool += array2hex(p.data);
console.log("Gathering many RNG bytes..");
while (pool.length < 1024 * 10) {
var p = await dev.get_rng();
TEST(p.status == "CTAP1_SUCCESS");
pool += array2hex(p.data);
}
var entropy = shannon.entropy(pool) * 2;
TEST(entropy > 7.99, 'Rng has good entropy: ' + entropy);
}
await device_start_over();
await test_pin();
await test_crypto();
await test_rng();
} }