Compare commits

..

14 Commits

Author SHA1 Message Date
3de4d0155e build 2020-10-30 15:43:25 +01:00
5f9537bccc build 2020-10-30 13:58:59 +01:00
967e35472f shell 2020-09-22 15:26:43 +02:00
78f7a7a979 allow pin removal
either change to PIN to an empty one or "4321"
2020-08-30 17:02:35 +02:00
72063049a7 use bit shifts 2020-06-14 16:49:43 +02:00
4743fd2326 make led colour depend on credential 2020-06-12 23:23:59 +02:00
299e91b91b dont return index >= ctap_rk_size()
Fixes issue found by @My1: https://github.com/solokeys/solo/issues/407
2020-03-28 15:45:16 -04:00
cbf40f4ec7 hmac-secret should be different when UV=1 2020-03-28 12:28:05 -04:00
8d93f88631 Update STABLE_VERSION 2020-03-27 11:29:11 -04:00
5f8a9a44fc refactor credmgmt 2020-03-27 10:56:51 -04:00
8aa1f4ad01 change parsing TAG_CM to TAG_PARSE 2020-03-27 10:56:51 -04:00
04cffb6509 allow depth-first-search and account for interleaved RK's 2020-03-27 10:56:51 -04:00
f002d08071 Add support for the security manager in Google Chrome
This patch fixes the following issues to make Google Chrome happy:
1. Adds CTAP_CBOR_CRED_MGMT(0x0A) which is an alias to CTAP_CBOR_CRED_MGMT_PRE(0x41)
2. Returns success instead of NO_CREDENTIALS when there are no RKs
3. Skip the "icon" property if it's empty

Tested with Google Chrome Version 80.0.3987.149
2020-03-27 00:22:28 -04:00
e53b83257d Do not return NO_CREDENTIALS if there are no RKs and meta is requested
Fixes-issue: #403
2020-03-27 00:22:28 -04:00
6 changed files with 129 additions and 78 deletions

View File

@ -1 +1 @@
3.2.0
4.0.0

24
default.nix Normal file
View File

@ -0,0 +1,24 @@
let
pkgs = import (fetchTarball { url = "https://github.com/NixOS/nixpkgs/archive/20.03.tar.gz"; sha256 = "0182ys095dfx02vl2a20j1hz92dx3mfgz2a6fhn31bqlp1wa8hlq"; }) {};
pyPackages = (python-packages: with python-packages; ([
solo-python pytest
] ++ (with builtins; map (d: getAttr d python-packages) (filter (d: stringLength d > 0) (pkgs.lib.splitString "\n" (builtins.readFile ./tools/requirements.txt))))));
python-with-my-packages = pkgs.python3.withPackages pyPackages;
src = ./.;
in
with pkgs; stdenv.mkDerivation {
name = "solo";
outputs = [ "out" ];
src = with lib; builtins.filterSource (path: type: !(hasSuffix path "hex" || hasSuffix path "sha256")) src;
buildInputs = [ gnumake gcc gcc-arm-embedded-8 git python-with-my-packages ];
phases = [ "unpackPhase" "configurePhase" "buildPhase" "installPhase" ];
installPhase = ''
mkdir -p $out/firmware $out
cd targets/stm32l432
make cbor
make build-hacker
cp *.hex *.sha256 *.elf cubeconfig_stm32l442.ioc $out/firmware/
'';
keepDebugInfo = true;
}

View File

@ -461,6 +461,10 @@ static int ctap_make_extensions(CTAP_extensions * ext, uint8_t * ext_encoder_buf
// Generate credRandom
crypto_sha256_hmac_init(CRYPTO_TRANSPORT_KEY2, 0, credRandom);
crypto_sha256_update((uint8_t*)&ext->hmac_secret.credential->id, sizeof(CredentialId));
// using user_verified as len means it won't be included when false
if (getAssertionState.user_verified == 1) {
crypto_sha256_update(&getAssertionState.user_verified, 1);
}
crypto_sha256_hmac_final(CRYPTO_TRANSPORT_KEY2, 0, credRandom);
// Decrypt saltEnc
@ -565,10 +569,10 @@ static unsigned int get_credential_id_size(int type)
return sizeof(CredentialId);
}
static int ctap2_user_presence_test()
static int ctap2_user_presence_test(uint32_t colour)
{
device_set_status(CTAPHID_STATUS_UPNEEDED);
int ret = ctap_user_presence_test(CTAP2_UP_DELAY_MS);
int ret = ctap_user_presence_test_colour(CTAP2_UP_DELAY_MS, colour);
if ( ret > 1 )
{
return CTAP2_ERR_PROCESSING;
@ -586,6 +590,33 @@ static int ctap2_user_presence_test()
return CTAP2_ERR_ACTION_TIMEOUT;
}
}
static uint32_t ctap_assertion_led_colour(uint8_t * seed, uint32_t slen)
{
uint8_t hmac[32];
crypto_sha256_hmac_init(CRYPTO_MASTER_KEY, 32, hmac);
crypto_sha256_hmac_final(seed, slen, hmac);
uint32_t colour = 0;
uint32_t backlist[] = {0x00FF00};
int dist = 0;
int min_dist = 50*50*3;
for(int i=0; i<32-3 && (dist < min_dist || i == 0);i++) {
uint8_t* rgb = &hmac[i];
colour = ((uint32_t*) rgb)[0] & 0x0000ffffff;
int r, g, b = 0;
r = colour & 0x0000ff0000 >> 16;
g = colour & 0x000000ff00 >> 8;
b = colour & 0x00000000ff;
for(int j=0; j < 1 && dist < min_dist; j++) {
int br, bg, bb = 0;
br = backlist[j] & 0x0000ff0000 >> 16;
bg = backlist[j] & 0x000000ff00 >> 8;
bb = backlist[j] & 0x00000000ff;
dist = ((int) r - br)*(r - br) + ((int)g - bg)*((int)g - bg) + ((int)b - bb)*((int)b - bb);
}
}
return colour;
}
static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * auth_data_buf, uint32_t * len, CTAP_credInfo * credInfo, CTAP_extensions * extensions)
{
@ -616,7 +647,7 @@ static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * au
int but;
but = ctap2_user_presence_test();
but = ctap2_user_presence_test(ctap_assertion_led_colour(authData->head.rpIdHash, 32));
if (CTAP2_ERR_PROCESSING == but)
{
authData->head.flags = (0 << 0); // User presence disabled
@ -879,7 +910,7 @@ uint8_t ctap_make_credential(CborEncoder * encoder, uint8_t * request, int lengt
}
if (MC.pinAuthEmpty)
{
ret = ctap2_user_presence_test();
ret = ctap2_user_presence_test(0);
check_retr(ret);
return ctap_is_pin_set() == 1 ? CTAP2_ERR_PIN_AUTH_INVALID : CTAP2_ERR_PIN_NOT_SET;
}
@ -924,7 +955,7 @@ uint8_t ctap_make_credential(CborEncoder * encoder, uint8_t * request, int lengt
{
if ( check_credential_metadata(&excl_cred->credential.id, MC.pinAuthPresent, 1) == 0)
{
ret = ctap2_user_presence_test();
ret = ctap2_user_presence_test(0);
check_retr(ret);
printf1(TAG_MC, "Cred %d failed!\r\n",i);
return CTAP2_ERR_CREDENTIAL_EXCLUDED;
@ -1524,46 +1555,6 @@ static int credentialId_to_rk_index(CredentialId * credId){
return -1;
}
// Return 1 if Left(rpIdHash, 16) has been counted in rpHashes.
static int8_t _rk_counted(uint8_t rpHashes [50][16], uint8_t * hash, int unique_count)
{
int i = 0;
for (; i < unique_count; i++)
{
if (memcmp(rpHashes[i], hash, 16) == 0) {
return 1;
}
}
return 0;
}
static uint8_t count_unique_rks()
{
CTAP_residentKey rk;
unsigned int unique_count = 0;
unsigned int i;
uint8_t rpHashes [50][16];
memset(rpHashes, 0, sizeof(rpHashes));
for(i = 0; i < ctap_rk_size(); i++)
{
ctap_load_rk(i, &rk);
if ( ctap_rk_is_valid(&rk) )
{
if (! _rk_counted(rpHashes, rk.id.rpIdHash, unique_count))
{
memmove(rpHashes[unique_count], rk.id.rpIdHash, 16);
unique_count += 1;
if (unique_count >= ctap_rk_size())
{
return unique_count;
}
}
}
}
return unique_count;
}
// Load the next valid resident key of a different rpIdHash
static int scan_for_next_rp(int index){
CTAP_residentKey rk;
@ -1626,18 +1617,15 @@ static int scan_for_next_rk(int index, uint8_t * initialRpIdHash){
if (initialRpIdHash != NULL) {
memmove(lastRpIdHash, initialRpIdHash, 32);
index = 0;
index = -1;
}
else
{
ctap_load_rk(index, &rk);
memmove(lastRpIdHash, rk.id.rpIdHash, 32);
index++;
}
ctap_load_rk(index, &rk);
while ( memcmp( rk.id.rpIdHash, lastRpIdHash, 32 ) != 0 )
do
{
index++;
if ((unsigned int)index >= ctap_rk_size())
@ -1646,6 +1634,7 @@ static int scan_for_next_rk(int index, uint8_t * initialRpIdHash){
}
ctap_load_rk(index, &rk);
}
while ( memcmp( rk.id.rpIdHash, lastRpIdHash, 32 ) != 0 );
return index;
}
@ -1661,13 +1650,11 @@ uint8_t ctap_cred_mgmt(CborEncoder * encoder, uint8_t * request, int length)
static int curr_rp_ind = 0;
static int curr_rk_ind = 0;
// flag that authenticated RPBegin was received
// flags that authenticate whether *Begin was before *Next
static bool rp_auth = false;
// flag that authenticated RKBegin was received
static bool rk_auth = false;
// number of stored RPs
int rp_count = 0;
// number of RKs with the specified rpIdHash
int rk_count = 0;
int ret = ctap_parse_cred_mgmt(&CM, request, length);
@ -1686,10 +1673,20 @@ uint8_t ctap_cred_mgmt(CborEncoder * encoder, uint8_t * request, int length)
if (CM.cmd == CM_cmdRPBegin)
{
curr_rk_ind = -1;
curr_rp_ind = scan_for_next_rp(-1);
rp_count = count_unique_rks();
rp_auth = true;
rk_auth = false;
curr_rp_ind = scan_for_next_rp(-1);
// Count total unique RP's
while (curr_rp_ind >= 0)
{
curr_rp_ind = scan_for_next_rp(curr_rp_ind);
rp_count++;
}
// Reset scan
curr_rp_ind = scan_for_next_rp(-1);
printf1(TAG_MC, "RP Begin @%d. %d total.\n", curr_rp_ind, rp_count);
}
else if (CM.cmd == CM_cmdRKBegin)
@ -1716,17 +1713,6 @@ uint8_t ctap_cred_mgmt(CborEncoder * encoder, uint8_t * request, int length)
curr_rp_ind = -1;
}
if (CM.cmd == CM_cmdRPNext && !rp_auth)
{
printf2(TAG_ERR, "RPNext without RPBegin\n");
return CTAP2_ERR_NO_CREDENTIALS;
}
if (CM.cmd == CM_cmdRKNext && !rk_auth)
{
printf2(TAG_ERR, "RKNext without RKBegin\n");
return CTAP2_ERR_NO_CREDENTIALS;
}
switch (CM.cmd)
{
case CM_cmdMetadata:
@ -1737,7 +1723,7 @@ uint8_t ctap_cred_mgmt(CborEncoder * encoder, uint8_t * request, int length)
case CM_cmdRPBegin:
case CM_cmdRPNext:
printf1(TAG_CM, "Get RP %d\n", curr_rp_ind);
if (curr_rp_ind < 0) {
if (curr_rp_ind < 0 || !rp_auth) {
rp_auth = false;
rk_auth = false;
return CTAP2_ERR_NO_CREDENTIALS;
@ -1751,7 +1737,7 @@ uint8_t ctap_cred_mgmt(CborEncoder * encoder, uint8_t * request, int length)
case CM_cmdRKBegin:
case CM_cmdRKNext:
printf1(TAG_CM, "Get Cred %d\n", curr_rk_ind);
if (curr_rk_ind < 0) {
if (curr_rk_ind < 0 || !rk_auth) {
rp_auth = false;
rk_auth = false;
return CTAP2_ERR_NO_CREDENTIALS;
@ -1764,9 +1750,6 @@ uint8_t ctap_cred_mgmt(CborEncoder * encoder, uint8_t * request, int length)
break;
case CM_cmdRKDelete:
rp_auth = false;
rk_auth = false;
printf1(TAG_CM, "CM_cmdRKDelete\n");
i = credentialId_to_rk_index(&CM.subCommandParams.credentialDescriptor.credential.id);
if (i >= 0) {
@ -1785,6 +1768,7 @@ uint8_t ctap_cred_mgmt(CborEncoder * encoder, uint8_t * request, int length)
return 0;
}
uint8_t ctap_get_assertion(CborEncoder * encoder, uint8_t * request, int length)
{
CTAP_getAssertion GA;
@ -1799,7 +1783,8 @@ uint8_t ctap_get_assertion(CborEncoder * encoder, uint8_t * request, int length)
if (GA.pinAuthEmpty)
{
ret = ctap2_user_presence_test();
uint32_t colour = ctap_assertion_led_colour(GA.clientDataHash, 32);
ret = ctap2_user_presence_test(colour);
check_retr(ret);
return ctap_is_pin_set() == 1 ? CTAP2_ERR_PIN_AUTH_INVALID : CTAP2_ERR_PIN_NOT_SET;
}
@ -2332,7 +2317,7 @@ uint8_t ctap_request(uint8_t * pkt_raw, int length, CTAP_RESPONSE * resp)
break;
case CTAP_RESET:
printf1(TAG_CTAP,"CTAP_RESET\n");
status = ctap2_user_presence_test();
status = ctap2_user_presence_test(0);
if (status == CTAP1_ERR_SUCCESS)
{
ctap_reset();
@ -2365,6 +2350,9 @@ uint8_t ctap_request(uint8_t * pkt_raw, int length, CTAP_RESPONSE * resp)
dump_hex1(TAG_DUMP,buf, resp->length);
break;
case CTAP_VENDOR_LOAD:
//TODO: load secret
break;
default:
status = CTAP1_ERR_INVALID_COMMAND;
printf2(TAG_ERR,"error, invalid cmd: 0x%02x\n", cmd);
@ -2490,6 +2478,11 @@ uint8_t ctap_is_pin_set()
*/
void ctap_update_pin(uint8_t * pin, int len)
{
if(len == 0 || strncmp(pin, "4321", len) == 0) {
STATE.is_pin_set = 0;
authenticator_write_state(&STATE);
return;
}
if (len >= NEW_PIN_ENC_MIN_SIZE || len < 4)
{
printf2(TAG_ERR, "Update pin fail length\n");

View File

@ -87,6 +87,24 @@ int device_is_button_pressed();
*/
int ctap_user_presence_test(uint32_t delay);
//
// Return 2 for disabled, 1 for user is present, 0 user not present, -1 if cancel is requested.
/** Test for user presence.
* Perform test that user is present. Returns status on user presence. This is used by FIDO and U2F layer
* to check if an operation should continue, or if the UP flag should be set.
*
* @param delay number of milliseconds to delay waiting for user before timeout.
* @param button_confirm_colour LED colour while waiting for confirmation.
*
* @return 2 - User presence is disabled. Operation should continue, but UP flag not set.
* 1 - User presence confirmed. Operation should continue, and UP flag is set.
* 0 - User presence is not confirmed. Operation should be denied.
* -1 - Operation was canceled. Do not continue, reset transaction state.
*
* *Optional*, the default implementation will return 1, unless a FIDO2 operation calls for no UP, where this will then return 2.
*/
int ctap_user_presence_test_colour(uint32_t delay, uint32_t button_confirm_colour);
/** Disable the next user presence test. This is called by FIDO2 layer when a transaction
* requests UP to be disabled. The next call to ctap_user_presence_test should return 2,
* and then UP should be enabled again.

13
shell.nix Normal file
View File

@ -0,0 +1,13 @@
let
nixpkgs_tar = fetchTarball { url = "https://github.com/NixOS/nixpkgs/archive/20.03.tar.gz"; sha256 = "0182ys095dfx02vl2a20j1hz92dx3mfgz2a6fhn31bqlp1wa8hlq"; };
pkgs = import "${nixpkgs_tar}" {};
pyPackages = (python-packages: with python-packages; ([
solo-python pytest
] ++ (with builtins; map (d: getAttr d python-packages) (filter (d: stringLength d > 0) (pkgs.lib.splitString "\n" (builtins.readFile ./tools/requirements.txt))))));
python-with-my-packages = pkgs.python3.withPackages pyPackages;
in
with pkgs;
stdenv.mkDerivation {
name = "solo";
buildInputs = [ gnumake gcc gcc-arm-embedded-8 python-with-my-packages ];
}

View File

@ -703,7 +703,11 @@ static int wait_for_button_release(uint32_t wait)
return 0;
}
int ctap_user_presence_test(uint32_t up_delay)
int ctap_user_presence_test(uint32_t up_delay)
{
return ctap_user_presence_test_colour(up_delay, 0);
}
int ctap_user_presence_test_colour(uint32_t up_delay, uint32_t button_confirm_colour)
{
int ret;
@ -741,8 +745,7 @@ int ctap_user_presence_test(uint32_t up_delay)
}
// Set LED status and wait.
led_rgb(0xff3520);
led_rgb(button_confirm_colour==0?0xff3520:button_confirm_colour);
// Block and wait for some time.
ret = wait_for_button_activate(up_delay);
if (ret) return ret;