diff --git a/fido2/ctap.c b/fido2/ctap.c index 2d3bb48..c3b4a4c 100644 --- a/fido2/ctap.c +++ b/fido2/ctap.c @@ -26,6 +26,7 @@ #include "cbor.h" #include "ctap.h" +#include "ctaphid.h" #include "ctap_parse.h" #include "ctap_errors.h" #include "cose_key.h" @@ -43,6 +44,7 @@ uint8_t PIN_TOKEN[PIN_TOKEN_SIZE]; uint8_t KEY_AGREEMENT_PUB[64]; static uint8_t KEY_AGREEMENT_PRIV[32]; static uint8_t PIN_CODE_HASH[32]; +static int8_t PIN_BOOT_ATTEMPTS_LEFT = PIN_BOOT_ATTEMPTS; AuthenticatorState STATE; @@ -264,11 +266,11 @@ static int ctap_generate_cose_key(CborEncoder * cose_key, uint8_t * hmac_input, return 0; } -void make_auth_tag(struct rpId * rp, CTAP_userEntity * user, uint32_t count, uint8_t * tag) +void make_auth_tag(uint8_t * nonce, CTAP_userEntity * user, uint32_t count, uint8_t * tag) { uint8_t hashbuf[32]; crypto_sha256_hmac_init(NULL, 0, hashbuf); - crypto_sha256_update(rp->id, rp->size); + crypto_sha256_update(nonce, CREDENTIAL_NONCE_SIZE); crypto_sha256_update(user->id, user->id_size); crypto_sha256_update(user->name, strnlen((const char*)user->name, USER_NAME_LIMIT)); crypto_sha256_update((uint8_t*)&count, 4); @@ -283,21 +285,19 @@ static uint32_t auth_data_update_count(CTAP_authDataHeader * authData) if (count == 0) // count 0 will indicate invalid token { count = ctap_atomic_count( 0 ); + } uint8_t * byte = (uint8_t*) &authData->signCount; - *byte++ = count & 0xff; - count = count >> 8; - *byte++ = count & 0xff; - count = count >> 8; - *byte++ = count & 0xff; - count = count >> 8; - *byte++ = count & 0xff; + *byte++ = (count >> 0) & 0xff; + *byte++ = (count >> 8) & 0xff; + *byte++ = (count >> 16) & 0xff; + *byte++ = (count >> 24) & 0xff; return count; } -static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * auth_data_buf, int len, CTAP_userEntity * user, uint8_t credtype, int32_t algtype) +static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * auth_data_buf, int len, CTAP_userEntity * user, uint8_t credtype, int32_t algtype, int32_t * sz) { CborEncoder cose_key; int auth_data_sz, ret; @@ -318,10 +318,24 @@ static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * au count = auth_data_update_count(&authData->head); - authData->head.flags = (ctap_user_presence_test() << 0); + device_set_status(CTAPHID_STATUS_UPNEEDED); + int but = ctap_user_presence_test(); + + if (!but) + { + return CTAP2_ERR_OPERATION_DENIED; + } + else if (but < 0) // Cancel + { + return CTAP2_ERR_KEEPALIVE_CANCEL; + } + device_set_status(CTAPHID_STATUS_PROCESSING); + + authData->head.flags = (but << 0); authData->head.flags |= (ctap_user_verification(0) << 2); + if (credtype != 0) { // add attestedCredentialData @@ -338,19 +352,19 @@ static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * au #else memset((uint8_t*)&authData->attest.credential, 0, sizeof(struct Credential)); - // Make a tag we can later check to make sure this is a token we made - make_auth_tag(rp, user, count, authData->attest.credential.tag); + ctap_generate_rng(authData->attest.credential.nonce, CREDENTIAL_NONCE_SIZE); memmove(&authData->attest.credential.enc.user, user, sizeof(CTAP_userEntity)); //TODO encrypt this authData->attest.credential.enc.count = count; + // Make a tag we can later check to make sure this is a token we made + make_auth_tag(authData->attest.credential.nonce, user, count, authData->attest.credential.tag); + crypto_aes256_init(CRYPTO_TRANSPORT_KEY, NULL); crypto_aes256_encrypt((uint8_t*)&authData->attest.credential.enc, CREDENTIAL_ENC_SIZE); ctap_generate_cose_key(&cose_key, (uint8_t*)&authData->attest.credential, sizeof(struct Credential), credtype, algtype); - printf1(TAG_MC,"COSE_KEY: "); dump_hex1(TAG_MC, cose_key_buf, cbor_encoder_get_buffer_size(&cose_key, cose_key_buf)); - auth_data_sz = sizeof(CTAP_authData) + cbor_encoder_get_buffer_size(&cose_key, cose_key_buf); #endif @@ -367,7 +381,8 @@ static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * au check_ret(ret); } - return auth_data_sz; + if (sz) *sz = auth_data_sz; + return 0; } @@ -419,7 +434,6 @@ int ctap_calculate_signature(uint8_t * data, int datalen, uint8_t * clientDataHa crypto_sha256_update(clientDataHash, CLIENT_DATA_HASH_SIZE); crypto_sha256_final(hashbuf); - printf1(TAG_GREEN, "sha256: "); dump_hex1(TAG_DUMP,hashbuf,32); crypto_ecc256_sign(hashbuf, 32, sigbuf); return ctap_encode_der_sig(sigbuf,sigder); @@ -471,13 +485,8 @@ uint8_t ctap_add_attest_statement(CborEncoder * map, uint8_t * sigder, int len) int ctap_authenticate_credential(struct rpId * rp, CTAP_credentialDescriptor * desc) { uint8_t tag[16]; - if (desc->type != PUB_KEY_CRED_PUB_KEY) - { - printf1(TAG_GA,"unsupported credential type: %d\n", desc->type); - return 0; - } - make_auth_tag(rp, &desc->credential.enc.user, desc->credential.enc.count, tag); + make_auth_tag(desc->credential.nonce, &desc->credential.enc.user, desc->credential.enc.count, tag); return (memcmp(desc->credential.tag, tag, CREDENTIAL_TAG_SIZE) == 0); } @@ -519,6 +528,12 @@ uint8_t ctap_make_credential(CborEncoder * encoder, uint8_t * request, int lengt } } + if (MC.up) + { + return CTAP2_ERR_INVALID_OPTION; + } + + crypto_aes256_init(CRYPTO_TRANSPORT_KEY, NULL); for (i = 0; i < MC.excludeListSize; i++) { ret = parse_credential_descriptor(&MC.excludeList, excl_cred); @@ -528,8 +543,11 @@ uint8_t ctap_make_credential(CborEncoder * encoder, uint8_t * request, int lengt } check_retr(ret); + crypto_aes256_reset_iv(NULL); + crypto_aes256_decrypt((uint8_t*)& excl_cred->credential.enc, CREDENTIAL_ENC_SIZE); if (ctap_authenticate_credential(&MC.rp, excl_cred)) { + printf1(TAG_MC, "Cred %d failed!\r\n",i); return CTAP2_ERR_CREDENTIAL_EXCLUDED; } @@ -540,9 +558,11 @@ uint8_t ctap_make_credential(CborEncoder * encoder, uint8_t * request, int lengt CborEncoder map; ret = cbor_encoder_create_map(encoder, &map, 3); check_ret(ret); + int32_t auth_data_sz; - int auth_data_sz = ctap_make_auth_data(&MC.rp, &map, auth_data_buf, sizeof(auth_data_buf), - &MC.user, MC.publicKeyCredentialType, MC.COSEAlgorithmIdentifier); + ret = ctap_make_auth_data(&MC.rp, &map, auth_data_buf, sizeof(auth_data_buf), + &MC.user, MC.publicKeyCredentialType, MC.COSEAlgorithmIdentifier, &auth_data_sz); + check_retr(ret); crypto_ecc256_load_attestation_key(); int sigder_sz = ctap_calculate_signature(auth_data_buf, auth_data_sz, MC.clientDataHash, auth_data_buf, sigbuf, sigder); @@ -724,11 +744,6 @@ uint8_t ctap_end_get_assertion(CborEncoder * map, CTAP_credentialDescriptor * cr crypto_ecc256_load_key((uint8_t*)&cred->credential, sizeof(struct Credential), NULL, 0); - /*printf1(TAG_GREEN,"auth_data_buf: "); dump_hex1(TAG_DUMP, auth_data_buf, sizeof(CTAP_authDataHeader));*/ - /*printf1(TAG_GREEN,"clientdatahash: "); dump_hex1(TAG_DUMP, clientDataHash, 32);*/ - /*printf1(TAG_GREEN,"credential: # %d\n", cred->credential.enc.count);*/ - /*dump_hex1(TAG_DUMP, clientDataHash, 32);*/ - int sigder_sz = ctap_calculate_signature(auth_data_buf, sizeof(CTAP_authDataHeader), clientDataHash, auth_data_buf, sigbuf, sigder); { @@ -801,12 +816,16 @@ uint8_t ctap_get_assertion(CborEncoder * encoder, uint8_t * request, int length) } } - + if (!GA.rp.size || !GA.clientDataHashPresent) + { + return CTAP2_ERR_MISSING_PARAMETER; + } CborEncoder map; ret = cbor_encoder_create_map(encoder, &map, 5); check_ret(ret); - ctap_make_auth_data(&GA.rp, &map, auth_data_buf, sizeof(auth_data_buf), NULL, 0,0); + ret = ctap_make_auth_data(&GA.rp, &map, auth_data_buf, sizeof(auth_data_buf), NULL, 0,0,NULL); + check_retr(ret); printf1(TAG_GA, "ALLOW_LIST has %d creds\n", GA.credLen); /*for (int j = 0; j < GA.credLen; j++)*/ @@ -834,13 +853,13 @@ uint8_t ctap_get_assertion(CborEncoder * encoder, uint8_t * request, int length) else { printf2(TAG_ERR,"Error, no authentic credential\n"); - return CTAP2_ERR_CREDENTIAL_NOT_VALID; + return CTAP2_ERR_NO_CREDENTIALS; } - printf1(TAG_RED,"resulting order of creds:\n"); + printf1(TAG_GA,"resulting order of creds:\n"); for (int j = 0; j < GA.credLen; j++) { - printf1(TAG_RED,"CRED ID (# %d)\n", GA.creds[j].credential.enc.count); + printf1(TAG_GA,"CRED ID (# %d)\n", GA.creds[j].credential.enc.count); } { @@ -861,6 +880,18 @@ uint8_t ctap_get_assertion(CborEncoder * encoder, uint8_t * request, int length) return 0; } +// Return how many trailing zeros in a buffer +static int trailing_zeros(uint8_t * buf, int indx) +{ + int c = 0; + while(0==buf[indx] && indx) + { + indx--; + c++; + } + return c; +} + uint8_t ctap_update_pin_if_verified(uint8_t * pinEnc, int len, uint8_t * platform_pubkey, uint8_t * pinAuth, uint8_t * pinHashEnc) { uint8_t shared_secret[32]; @@ -876,7 +907,11 @@ uint8_t ctap_update_pin_if_verified(uint8_t * pinEnc, int len, uint8_t * platfor { if (ctap_device_locked()) { - return CTAP2_ERR_OPERATION_DENIED; + return CTAP2_ERR_PIN_BLOCKED; + } + if (ctap_device_boot_locked()) + { + return CTAP2_ERR_PIN_AUTH_BLOCKED; } } @@ -911,25 +946,31 @@ uint8_t ctap_update_pin_if_verified(uint8_t * pinEnc, int len, uint8_t * platfor crypto_aes256_decrypt(pinEnc, len); - printf1(TAG_CP,"new pin: %s\n", pinEnc); - ret = strnlen((const char *)pinEnc, NEW_PIN_ENC_MAX_SIZE); - if (ret == NEW_PIN_ENC_MAX_SIZE) + + ret = trailing_zeros(pinEnc, NEW_PIN_ENC_MIN_SIZE - 1); + ret = NEW_PIN_ENC_MIN_SIZE - ret; + + if (ret < NEW_PIN_MIN_SIZE || ret >= NEW_PIN_MAX_SIZE) { - printf2(TAG_ERR,"No NULL terminator in new pin string\n"); - return CTAP1_ERR_OTHER; - } - else if (ret < 4) - { - printf2(TAG_ERR,"new PIN is too short\n"); + printf2(TAG_ERR,"new PIN is too short or too long [%d bytes]\n", ret); return CTAP2_ERR_PIN_POLICY_VIOLATION; } + else + { + printf1(TAG_CP,"new pin: %s [%d bytes]\n", pinEnc, ret); + dump_hex1(TAG_CP, pinEnc, ret); + } if (ctap_is_pin_set()) { if (ctap_device_locked()) { - return CTAP2_ERR_OPERATION_DENIED; + return CTAP2_ERR_PIN_BLOCKED; + } + if (ctap_device_boot_locked()) + { + return CTAP2_ERR_PIN_AUTH_BLOCKED; } crypto_aes256_reset_iv(NULL); crypto_aes256_decrypt(pinHashEnc, 16); @@ -937,6 +978,10 @@ uint8_t ctap_update_pin_if_verified(uint8_t * pinEnc, int len, uint8_t * platfor { crypto_ecc256_make_key_pair(KEY_AGREEMENT_PUB, KEY_AGREEMENT_PRIV); ctap_decrement_pin_attempts(); + if (ctap_device_boot_locked()) + { + return CTAP2_ERR_PIN_AUTH_BLOCKED; + } return CTAP2_ERR_PIN_INVALID; } else @@ -976,6 +1021,10 @@ uint8_t ctap_add_pin_if_verified(uint8_t * pinTokenEnc, uint8_t * platform_pubke // Generate new keyAgreement pair crypto_ecc256_make_key_pair(KEY_AGREEMENT_PUB, KEY_AGREEMENT_PRIV); ctap_decrement_pin_attempts(); + if (ctap_device_boot_locked()) + { + return CTAP2_ERR_PIN_AUTH_BLOCKED; + } return CTAP2_ERR_PIN_INVALID; } @@ -995,6 +1044,20 @@ uint8_t ctap_client_pin(CborEncoder * encoder, uint8_t * request, int length) uint8_t pinTokenEnc[PIN_TOKEN_SIZE]; int ret = ctap_parse_client_pin(&CP,request,length); + switch(CP.subCommand) + { + case CP_cmdSetPin: + case CP_cmdChangePin: + case CP_cmdGetPinToken: + if (ctap_device_locked()) + { + return CTAP2_ERR_PIN_BLOCKED; + } + if (ctap_device_boot_locked()) + { + return CTAP2_ERR_PIN_AUTH_BLOCKED; + } + } if (ret != 0) { @@ -1104,7 +1167,7 @@ uint8_t ctap_client_pin(CborEncoder * encoder, uint8_t * request, int length) check_ret(ret); } - if (num_map) + if (num_map || CP.getRetries) { ret = cbor_encoder_close_container(encoder, &map); check_ret(ret); @@ -1141,10 +1204,14 @@ uint8_t ctap_request(uint8_t * pkt_raw, int length, CTAP_RESPONSE * resp) { case CTAP_MAKE_CREDENTIAL: case CTAP_GET_ASSERTION: - case CTAP_CLIENT_PIN: if (ctap_device_locked()) { - status = CTAP2_ERR_OPERATION_DENIED; + status = CTAP2_ERR_PIN_BLOCKED; + goto done; + } + if (ctap_device_boot_locked()) + { + status = CTAP2_ERR_PIN_AUTH_BLOCKED; goto done; } break; @@ -1153,6 +1220,7 @@ uint8_t ctap_request(uint8_t * pkt_raw, int length, CTAP_RESPONSE * resp) switch(cmd) { case CTAP_MAKE_CREDENTIAL: + device_set_status(CTAPHID_STATUS_PROCESSING); printf1(TAG_CTAP,"CTAP_MAKE_CREDENTIAL\n"); t1 = millis(); status = ctap_make_credential(&encoder, pkt_raw, length); @@ -1164,6 +1232,7 @@ uint8_t ctap_request(uint8_t * pkt_raw, int length, CTAP_RESPONSE * resp) resp->length = cbor_encoder_get_buffer_size(&encoder, buf); break; case CTAP_GET_ASSERTION: + device_set_status(CTAPHID_STATUS_PROCESSING); printf1(TAG_CTAP,"CTAP_GET_ASSERTION\n"); t1 = millis(); status = ctap_get_assertion(&encoder, pkt_raw, length); @@ -1190,6 +1259,7 @@ uint8_t ctap_request(uint8_t * pkt_raw, int length, CTAP_RESPONSE * resp) case CTAP_CLIENT_PIN: printf1(TAG_CTAP,"CTAP_CLIENT_PIN\n"); status = ctap_client_pin(&encoder, pkt_raw, length); + resp->length = cbor_encoder_get_buffer_size(&encoder, buf); dump_hex1(TAG_DUMP, buf, cbor_encoder_get_buffer_size(&encoder, buf)); break; @@ -1228,6 +1298,7 @@ uint8_t ctap_request(uint8_t * pkt_raw, int length, CTAP_RESPONSE * resp) } done: + device_set_status(CTAPHID_STATUS_IDLE); getAssertionState.lastcmd = cmd; if (status != CTAP1_ERR_SUCCESS) @@ -1235,7 +1306,7 @@ done: resp->length = 0; } - printf1(TAG_CTAP,"cbor output structure: %d bytes\n", resp->length); + printf1(TAG_CTAP,"cbor output structure: %d bytes. Return 0x%02x\n", resp->length, status); return status; } @@ -1267,6 +1338,8 @@ void ctap_init() authenticator_read_state(&STATE); + device_set_status(CTAPHID_STATUS_IDLE); + if (STATE.is_initialized == INITIALIZED_MARKER) { printf1(TAG_STOR,"Auth state is initialized\n"); @@ -1295,7 +1368,7 @@ void ctap_init() { printf1(TAG_STOR,"pin code: \"%s\"\n", STATE.pin_code); crypto_sha256_init(); - crypto_sha256_update(STATE.pin_code, strnlen(STATE.pin_code, NEW_PIN_ENC_MAX_SIZE)); + crypto_sha256_update(STATE.pin_code, STATE.pin_code_length); crypto_sha256_final(PIN_CODE_HASH); printf1(TAG_STOR, "attempts_left: %d\n", STATE.remaining_tries); } @@ -1335,14 +1408,15 @@ uint8_t ctap_pin_matches(uint8_t * pin, int len) void ctap_update_pin(uint8_t * pin, int len) { - // TODO this should go in flash - if (len > NEW_PIN_ENC_MAX_SIZE-1 || len < 4) + if (len > NEW_PIN_ENC_MIN_SIZE || len < 4) { printf2(TAG_ERR, "Update pin fail length\n"); exit(1); } - memset(STATE.pin_code, 0, NEW_PIN_ENC_MAX_SIZE); + memset(STATE.pin_code, 0, NEW_PIN_ENC_MIN_SIZE); memmove(STATE.pin_code, pin, len); + STATE.pin_code_length = len; + STATE.pin_code[NEW_PIN_ENC_MIN_SIZE - 1] = 0; crypto_sha256_init(); crypto_sha256_update(STATE.pin_code, len); @@ -1350,18 +1424,25 @@ void ctap_update_pin(uint8_t * pin, int len) STATE.is_pin_set = 1; + authenticator_write_state(&STATE, 1); + authenticator_write_state(&STATE, 0); + printf1(TAG_CTAP, "New pin set: %s\n", STATE.pin_code); } uint8_t ctap_decrement_pin_attempts() { - if (STATE.remaining_tries > 0) + if (PIN_BOOT_ATTEMPTS_LEFT > 0) + { + PIN_BOOT_ATTEMPTS_LEFT--; + } + if (! ctap_device_locked()) { STATE.remaining_tries--; ctap_flush_state(0); printf1(TAG_CP, "ATTEMPTS left: %d\n", STATE.remaining_tries); - if (STATE.remaining_tries == 0) + if (ctap_device_locked()) { memset(PIN_TOKEN,0,sizeof(PIN_TOKEN)); memset(PIN_CODE_HASH,0,sizeof(PIN_CODE_HASH)); @@ -1378,7 +1459,12 @@ uint8_t ctap_decrement_pin_attempts() int8_t ctap_device_locked() { - return STATE.remaining_tries == 0; + return STATE.remaining_tries <= 0; +} + +int8_t ctap_device_boot_locked() +{ + return PIN_BOOT_ATTEMPTS_LEFT <= 0; } int8_t ctap_leftover_pin_attempts() @@ -1389,6 +1475,7 @@ int8_t ctap_leftover_pin_attempts() void ctap_reset_pin_attempts() { STATE.remaining_tries = PIN_LOCKOUT_ATTEMPTS; + PIN_BOOT_ATTEMPTS_LEFT = PIN_BOOT_ATTEMPTS; ctap_flush_state(0); } diff --git a/fido2/ctap.h b/fido2/ctap.h index e594d27..8f5a16c 100644 --- a/fido2/ctap.h +++ b/fido2/ctap.h @@ -34,7 +34,8 @@ #define CTAP_VENDOR_FIRST 0x40 #define CTAP_VENDOR_LAST 0xBF -#define CTAP_AAGUID ((uint8_t*)"\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff") +// AAGUID For Solo +#define CTAP_AAGUID ((uint8_t*)"\x88\x76\x63\x1b\xd4\xa0\x42\x7f\x57\x73\x0e\xc7\x1c\x9e\x02\x79") #define MC_clientDataHash 0x01 #define MC_rp 0x02 @@ -126,10 +127,14 @@ #define ALLOW_LIST_MAX_SIZE 20 #define NEW_PIN_ENC_MAX_SIZE 256 // includes NULL terminator +#define NEW_PIN_ENC_MIN_SIZE 64 +#define NEW_PIN_MAX_SIZE 64 +#define NEW_PIN_MIN_SIZE 4 -#define CTAP_RESPONSE_BUFFER_SIZE 1024 +#define CTAP_RESPONSE_BUFFER_SIZE 4096 -#define PIN_LOCKOUT_ATTEMPTS 8 +#define PIN_LOCKOUT_ATTEMPTS 8 // Number of attempts total +#define PIN_BOOT_ATTEMPTS 3 // number of attempts per boot typedef struct { @@ -198,6 +203,7 @@ typedef struct uint8_t rk; uint8_t uv; + uint8_t up; uint8_t pinAuth[16]; uint8_t pinAuthPresent; @@ -215,6 +221,7 @@ typedef struct { uint32_t paramsParsed; uint8_t clientDataHash[CLIENT_DATA_HASH_SIZE]; + uint8_t clientDataHashPresent; struct rpId rp; @@ -222,12 +229,14 @@ typedef struct uint8_t rk; uint8_t uv; + uint8_t up; uint8_t pinAuth[16]; uint8_t pinAuthPresent; int pinProtocol; CTAP_credentialDescriptor creds[ALLOW_LIST_MAX_SIZE]; + uint8_t allowListPresent; } CTAP_getAssertion; typedef struct @@ -281,6 +290,7 @@ uint8_t ctap_is_pin_set(); uint8_t ctap_pin_matches(uint8_t * pin, int len); void ctap_reset(); int8_t ctap_device_locked(); +int8_t ctap_device_boot_locked(); // Key storage API diff --git a/fido2/ctap_parse.c b/fido2/ctap_parse.c index b959d19..757c26a 100644 --- a/fido2/ctap_parse.c +++ b/fido2/ctap_parse.c @@ -92,7 +92,7 @@ const char * cbor_value_get_type_string(const CborValue *value) uint8_t parse_user(CTAP_makeCredential * MC, CborValue * val) { size_t sz, map_length; - uint8_t key[8]; + uint8_t key[24]; int ret; int i; CborValue map; @@ -126,6 +126,7 @@ uint8_t parse_user(CTAP_makeCredential * MC, CborValue * val) printf2(TAG_ERR,"Error, rp map key is too large\n"); return CTAP2_ERR_LIMIT_EXCEEDED; } + check_ret(ret); key[sizeof(key) - 1] = 0; @@ -153,6 +154,11 @@ uint8_t parse_user(CTAP_makeCredential * MC, CborValue * val) } else if (strcmp((const char *)key, "name") == 0) { + if (cbor_value_get_type(&map) != CborTextStringType) + { + printf2(TAG_ERR,"Error, expecting text string type for user.name value\n"); + return CTAP2_ERR_INVALID_CBOR_TYPE; + } sz = USER_NAME_LIMIT; ret = cbor_value_copy_text_string(&map, (char *)MC->user.name, &sz, NULL); if (ret != CborErrorOutOfMemory) @@ -161,6 +167,22 @@ uint8_t parse_user(CTAP_makeCredential * MC, CborValue * val) } MC->user.name[USER_NAME_LIMIT - 1] = 0; } + else if (strcmp((const char *)key, "displayName") == 0) + { + if (cbor_value_get_type(&map) != CborTextStringType) + { + printf2(TAG_ERR,"Error, expecting text string type for user.displayName value\n"); + return CTAP2_ERR_INVALID_CBOR_TYPE; + } + } + else if (strcmp((const char *)key, "icon") == 0) + { + if (cbor_value_get_type(&map) != CborTextStringType) + { + printf2(TAG_ERR,"Error, expecting text string type for user.icon value\n"); + return CTAP2_ERR_INVALID_CBOR_TYPE; + } + } else { printf1(TAG_PARSE,"ignoring key %s for user map\n", key); @@ -263,6 +285,19 @@ uint8_t parse_pub_key_cred_params(CTAP_makeCredential * MC, CborValue * val) ret = cbor_value_get_array_length(val, &arr_length); check_ret(ret); + for (i = 0; i < arr_length; i++) + { + if ((ret = parse_pub_key_cred_param(&arr, &cred_type, &alg_type)) != 0) + { + return ret; + } + ret = cbor_value_advance(&arr); + check_ret(ret); + } + + ret = cbor_value_enter_container(val,&arr); + check_ret(ret); + for (i = 0; i < arr_length; i++) { if ((ret = parse_pub_key_cred_param(&arr, &cred_type, &alg_type)) == 0) @@ -275,11 +310,6 @@ uint8_t parse_pub_key_cred_params(CTAP_makeCredential * MC, CborValue * val) return 0; } } - else - { - // Continue? fail? - return ret; - } ret = cbor_value_advance(&arr); check_ret(ret); } @@ -309,10 +339,40 @@ uint8_t parse_fixed_byte_string(CborValue * map, uint8_t * dst, int len) return 0; } +uint8_t parse_verify_exclude_list(CborValue * val) +{ + int i; + int ret; + CborValue arr; + size_t size; + CTAP_credentialDescriptor cred; + if (cbor_value_get_type(val) != CborArrayType) + { + printf2(TAG_ERR,"error, exclude list is not a map\n"); + return CTAP2_ERR_INVALID_CBOR_TYPE; + } + ret = cbor_value_get_array_length(val, &size); + check_ret(ret); + ret = cbor_value_enter_container(val,&arr); + check_ret(ret); + for (i = 0; i < size; i++) + { + ret = parse_credential_descriptor(&arr, &cred); + check_ret(ret); + ret = cbor_value_advance(&arr); + check_ret(ret); + + } + return 0; +} uint8_t parse_rp_id(struct rpId * rp, CborValue * val) { size_t sz = DOMAIN_NAME_MAX_SIZE; + if (cbor_value_get_type(val) != CborTextStringType) + { + return CTAP2_ERR_INVALID_CBOR_TYPE; + } int ret = cbor_value_copy_text_string(val, (char*)rp->id, &sz, NULL); if (ret == CborErrorOutOfMemory) { @@ -413,7 +473,7 @@ uint8_t parse_rp(struct rpId * rp, CborValue * val) return 0; } -uint8_t parse_options(CborValue * val, uint8_t * rk, uint8_t * uv) +uint8_t parse_options(CborValue * val, uint8_t * rk, uint8_t * uv, uint8_t * up) { size_t sz, map_length; char key[8]; @@ -463,21 +523,27 @@ uint8_t parse_options(CborValue * val, uint8_t * rk, uint8_t * uv) return CTAP2_ERR_INVALID_CBOR_TYPE; } - if (strcmp(key, "rk") == 0) + if (strncmp(key, "rk",2) == 0) { ret = cbor_value_get_boolean(&map, &b); check_ret(ret); *rk = b; } - else if (strcmp(key, "uv") == 0) + else if (strncmp(key, "uv",2) == 0) { ret = cbor_value_get_boolean(&map, &b); check_ret(ret); *uv = b; } + else if (strncmp(key, "up",2) == 0) + { + ret = cbor_value_get_boolean(&map, &b); + check_ret(ret); + *up = b; + } else { - printf1(TAG_PARSE,"ignoring key %s for option map\n", key); + printf2(TAG_PARSE,"ignoring option specified %s\n", key); } @@ -576,27 +642,30 @@ uint8_t ctap_parse_make_credential(CTAP_makeCredential * MC, CborEncoder * encod break; case MC_excludeList: printf1(TAG_MC,"CTAP_excludeList\n"); - if( cbor_value_get_type(&map) == CborArrayType ) - { - ret = cbor_value_enter_container(&map, &MC->excludeList); - check_ret(ret); + ret = parse_verify_exclude_list(&map); + check_ret(ret); + + ret = cbor_value_enter_container(&map, &MC->excludeList); + check_ret(ret); + + ret = cbor_value_get_array_length(&map, &MC->excludeListSize); + check_ret(ret); + - ret = cbor_value_get_array_length(&map, &MC->excludeListSize); - check_ret(ret); - } - else - { - return CTAP2_ERR_INVALID_CBOR_TYPE; - } printf1(TAG_MC,"CTAP_excludeList done\n"); break; case MC_extensions: printf1(TAG_MC,"CTAP_extensions\n"); + type = cbor_value_get_type(&map); + if (type != CborMapType) + { + return CTAP2_ERR_INVALID_CBOR_TYPE; + } break; case MC_options: printf1(TAG_MC,"CTAP_options\n"); - ret = parse_options(&map, &MC->rk, &MC->uv); + ret = parse_options(&map, &MC->rk, &MC->uv, &MC->up); check_retr(ret); break; case MC_pinAuth: @@ -661,8 +730,8 @@ uint8_t parse_credential_descriptor(CborValue * arr, CTAP_credentialDescriptor * cbor_value_copy_byte_string(&val, (uint8_t*)&cred->credential, &buflen, NULL); if (buflen != CREDENTIAL_ID_SIZE) { - printf2(TAG_ERR,"Error, credential is incorrect length\n"); - return CTAP2_ERR_CBOR_UNEXPECTED_TYPE; // maybe just skip it instead of fail? + printf2(TAG_ERR,"Ignoring credential is incorrect length\n"); + //return CTAP2_ERR_CBOR_UNEXPECTED_TYPE; // maybe just skip it instead of fail? } ret = cbor_value_map_find_value(arr, "type", &val); @@ -677,13 +746,14 @@ uint8_t parse_credential_descriptor(CborValue * arr, CTAP_credentialDescriptor * buflen = sizeof(type); cbor_value_copy_text_string(&val, type, &buflen, NULL); - if (strcmp(type, "public-key") == 0) + if (strncmp(type, "public-key",11) == 0) { cred->type = PUB_KEY_CRED_PUB_KEY; } else { cred->type = PUB_KEY_CRED_UNKNOWN; + printf1(TAG_RED, "Unknown type: %s\r\n", type); } return 0; @@ -783,6 +853,7 @@ uint8_t ctap_parse_get_assertion(CTAP_getAssertion * GA, uint8_t * request, int ret = parse_fixed_byte_string(&map, GA->clientDataHash, CLIENT_DATA_HASH_SIZE); check_retr(ret); + GA->clientDataHashPresent = 1; printf1(TAG_GA," "); dump_hex1(TAG_GA, GA->clientDataHash, 32); break; @@ -796,10 +867,9 @@ uint8_t ctap_parse_get_assertion(CTAP_getAssertion * GA, uint8_t * request, int case GA_allowList: printf1(TAG_GA,"GA_allowList\n"); ret = parse_allow_list(GA, &map); - if (ret == 0) - { + check_ret(ret); + GA->allowListPresent = 1; - } break; case GA_extensions: printf1(TAG_GA,"GA_extensions\n"); @@ -807,7 +877,7 @@ uint8_t ctap_parse_get_assertion(CTAP_getAssertion * GA, uint8_t * request, int case GA_options: printf1(TAG_GA,"CTAP_options\n"); - ret = parse_options(&map, &GA->rk, &GA->uv); + ret = parse_options(&map, &GA->rk, &GA->uv, &GA->up); check_retr(ret); break; case GA_pinAuth: @@ -1033,10 +1103,11 @@ uint8_t ctap_parse_client_pin(CTAP_clientPin * CP, uint8_t * request, int length { ret = cbor_value_calculate_string_length(&map, &sz); check_ret(ret); - if (sz > NEW_PIN_ENC_MAX_SIZE) + if (sz > NEW_PIN_ENC_MAX_SIZE || sz < NEW_PIN_ENC_MIN_SIZE) { - return CTAP1_ERR_OTHER; + return CTAP2_ERR_PIN_POLICY_VIOLATION; } + CP->newPinEncSize = sz; sz = NEW_PIN_ENC_MAX_SIZE; ret = cbor_value_copy_byte_string(&map, CP->newPinEnc, &sz, NULL); @@ -1078,5 +1149,3 @@ uint8_t ctap_parse_client_pin(CTAP_clientPin * CP, uint8_t * request, int length return 0; } - - diff --git a/fido2/ctap_parse.h b/fido2/ctap_parse.h index c5f011f..12d814d 100644 --- a/fido2/ctap_parse.h +++ b/fido2/ctap_parse.h @@ -42,7 +42,7 @@ uint8_t parse_pub_key_cred_params(CTAP_makeCredential * MC, CborValue * val); uint8_t parse_fixed_byte_string(CborValue * map, uint8_t * dst, int len); uint8_t parse_rp_id(struct rpId * rp, CborValue * val); uint8_t parse_rp(struct rpId * rp, CborValue * val); -uint8_t parse_options(CborValue * val, uint8_t * rk, uint8_t * uv); +uint8_t parse_options(CborValue * val, uint8_t * rk, uint8_t * uv, uint8_t * up); uint8_t parse_allow_list(CTAP_getAssertion * GA, CborValue * it); uint8_t parse_cose_key(CborValue * it, uint8_t * x, uint8_t * y, int * kty, int * crv); diff --git a/fido2/ctaphid.c b/fido2/ctaphid.c index 8d4af1b..46d5523 100644 --- a/fido2/ctaphid.c +++ b/fido2/ctaphid.c @@ -43,6 +43,8 @@ typedef enum EMPTY = 0, BUFFERING, BUFFERED, + HID_ERROR, + HID_IGNORE, } CTAP_BUFFER_STATE; @@ -341,7 +343,7 @@ static void send_init_response(uint32_t oldcid, uint32_t newcid, uint8_t * nonce memmove(init_resp.nonce, nonce, 8); init_resp.cid = newcid; - init_resp.protocol_version = 0;//? + init_resp.protocol_version = CTAPHID_PROTOCOL_VERSION; init_resp.version_major = 0;//? init_resp.version_minor = 0;//? init_resp.build_version = 0;//? @@ -367,8 +369,21 @@ void ctaphid_check_timeouts() } +void ctaphid_update_status(int8_t status) +{ + CTAPHID_WRITE_BUFFER wb; + printf1(TAG_HID, "Send device update %d!\n",status); + ctaphid_write_buffer_init(&wb); -void ctaphid_handle_packet(uint8_t * pkt_raw) + wb.cid = buffer_cid(); + wb.cmd = CTAPHID_KEEPALIVE; + wb.bcnt = 1; + + ctaphid_write(&wb, &status, 1); + ctaphid_write(&wb, NULL, 0); +} + +static int ctaphid_buffer_packet(uint8_t * pkt_raw, uint8_t * cmd, uint32_t * cid, int * len) { CTAPHID_PACKET * pkt = (CTAPHID_PACKET *)(pkt_raw); @@ -378,29 +393,25 @@ void ctaphid_handle_packet(uint8_t * pkt_raw) if (!is_cont_pkt(pkt)) printf1(TAG_HID, " length: %d\n", ctaphid_packet_len(pkt)); int ret; - uint8_t status; uint32_t oldcid; uint32_t newcid; - static CTAPHID_WRITE_BUFFER wb; - uint32_t active_cid; - uint32_t t1,t2; - CTAP_RESPONSE ctap_resp; + *cid = pkt->cid; if (is_init_pkt(pkt)) { if (ctaphid_packet_len(pkt) != 8) { printf2(TAG_ERR, "Error,invalid length field for init packet\n"); - ctaphid_send_error(pkt->cid, CTAP1_ERR_INVALID_LENGTH); - return; + *cmd = CTAP1_ERR_INVALID_LENGTH; + return HID_ERROR; } if (pkt->cid == 0) { printf2(TAG_ERR,"Error, invalid cid 0\n"); - ctaphid_send_error(pkt->cid, CTAP1_ERR_INVALID_CHANNEL); - return; + *cmd = CTAP1_ERR_INVALID_CHANNEL; + return HID_ERROR; } ctaphid_init(); @@ -426,21 +437,21 @@ void ctaphid_handle_packet(uint8_t * pkt_raw) if (ret == -1) { printf2(TAG_ERR, "Error, not enough memory for new CID. return BUSY.\n"); - ctaphid_send_error(pkt->cid, CTAP1_ERR_CHANNEL_BUSY); - return; + *cmd = CTAP1_ERR_CHANNEL_BUSY; + return HID_ERROR; } send_init_response(oldcid, newcid, pkt->pkt.init.payload); cid_del(newcid); - return; + return HID_IGNORE; } else { // Check if matches existing CID if (pkt->cid == CTAPHID_BROADCAST_CID) { - ctaphid_send_error(pkt->cid, CTAP1_ERR_INVALID_CHANNEL); - return; + *cmd = CTAP1_ERR_INVALID_CHANNEL; + return HID_ERROR; } if (cid_exists(pkt->cid)) { @@ -450,14 +461,14 @@ void ctaphid_handle_packet(uint8_t * pkt_raw) { printf2(TAG_ERR,"INVALID_SEQ\n"); printf2(TAG_ERR,"Have %d/%d bytes\n", ctap_buffer_offset, ctap_buffer_bcnt); - ctaphid_send_error(pkt->cid, CTAP1_ERR_INVALID_SEQ); - return; + *cmd = CTAP1_ERR_INVALID_SEQ; + return HID_ERROR; } else if (pkt->cid != buffer_cid()) { printf2(TAG_ERR,"BUSY with %08x\n", buffer_cid()); - ctaphid_send_error(pkt->cid, CTAP1_ERR_CHANNEL_BUSY); - return; + *cmd = CTAP1_ERR_CHANNEL_BUSY; + return HID_ERROR; } } if (! is_cont_pkt(pkt)) @@ -465,8 +476,8 @@ void ctaphid_handle_packet(uint8_t * pkt_raw) if (ctaphid_packet_len(pkt) > CTAPHID_BUFFER_SIZE) { - ctaphid_send_error(pkt->cid, CTAP1_ERR_INVALID_LENGTH); - return; + *cmd = CTAP1_ERR_INVALID_LENGTH; + return HID_ERROR; } } else @@ -474,14 +485,14 @@ void ctaphid_handle_packet(uint8_t * pkt_raw) if (buffer_status() == EMPTY || pkt->cid != buffer_cid()) { printf2(TAG_ERR,"ignoring random cont packet\n"); - return; + return HID_IGNORE; } } if (buffer_packet(pkt) == SEQUENCE_ERROR) { printf2(TAG_ERR,"Buffering sequence error\n"); - ctaphid_send_error(pkt->cid, CTAP1_ERR_INVALID_SEQ); - return; + *cmd = CTAP1_ERR_INVALID_SEQ; + return HID_ERROR; } ret = cid_refresh(pkt->cid); if (ret != 0) @@ -489,133 +500,174 @@ void ctaphid_handle_packet(uint8_t * pkt_raw) printf2(TAG_ERR,"Error, refresh cid failed\n"); exit(1); } - active_cid = pkt->cid; } else if (is_cont_pkt(pkt)) { printf2(TAG_ERR,"ignoring unwarranted cont packet\n"); + // Ignore - return; + return HID_IGNORE; } else { printf2(TAG_ERR,"BUSY\n"); - ctaphid_send_error(pkt->cid, CTAP1_ERR_CHANNEL_BUSY); - return; + *cmd = CTAP1_ERR_CHANNEL_BUSY; + return HID_ERROR; } } - - - switch(buffer_status()) - { - case BUFFERING: - printf1(TAG_HID,"BUFFERING\n"); - active_cid_timestamp = millis(); - break; - - case EMPTY: - printf1(TAG_HID,"empty buffer!\n"); - case BUFFERED: - switch(buffer_cmd()) - { - - case CTAPHID_INIT: - printf2(TAG_ERR,"CTAPHID_INIT, error this should already be handled\n"); - exit(1); - break; -#ifndef DISABLE_CTAPHID_PING - case CTAPHID_PING: - printf1(TAG_HID,"CTAPHID_PING\n"); - - ctaphid_write_buffer_init(&wb); - wb.cid = active_cid; - wb.cmd = CTAPHID_PING; - wb.bcnt = buffer_len(); - t1 = millis(); - ctaphid_write(&wb, ctap_buffer, buffer_len()); - ctaphid_write(&wb, NULL,0); - t2 = millis(); - printf1(TAG_TIME,"PING writeback: %d ms\n",(uint32_t)(t2-t1)); - break; -#endif -#ifndef DISABLE_CTAPHID_WINK - case CTAPHID_WINK: - printf1(TAG_HID,"CTAPHID_WINK\n"); - - ctaphid_write_buffer_init(&wb); - - wb.cid = active_cid; - wb.cmd = CTAPHID_WINK; - - ctaphid_write(&wb,NULL,0); - - break; -#endif -#ifndef DISABLE_CTAPHID_CBOR - case CTAPHID_CBOR: - printf1(TAG_HID,"CTAPHID_CBOR\n"); - if (buffer_len() == 0) - { - printf2(TAG_ERR,"Error,invalid 0 length field for cbor packet\n"); - ctaphid_send_error(pkt->cid, CTAP1_ERR_INVALID_LENGTH); - return; - } - - ctap_response_init(&ctap_resp); - status = ctap_request(ctap_buffer, buffer_len(), &ctap_resp); - - ctaphid_write_buffer_init(&wb); - wb.cid = active_cid; - wb.cmd = CTAPHID_CBOR; - wb.bcnt = (ctap_resp.length+1); - - - t1 = millis(); - ctaphid_write(&wb, &status, 1); - ctaphid_write(&wb, ctap_resp.data, ctap_resp.length); - ctaphid_write(&wb, NULL, 0); - t2 = millis(); - printf1(TAG_TIME,"CBOR writeback: %d ms\n",(uint32_t)(t2-t1)); - break; -#endif - case CTAPHID_MSG: - printf1(TAG_HID,"CTAPHID_MSG\n"); - if (buffer_len() == 0) - { - printf2(TAG_ERR,"Error,invalid 0 length field for MSG/U2F packet\n"); - ctaphid_send_error(pkt->cid, CTAP1_ERR_INVALID_LENGTH); - return; - } - - ctap_response_init(&ctap_resp); - u2f_request((struct u2f_request_apdu*)ctap_buffer, &ctap_resp); - - ctaphid_write_buffer_init(&wb); - wb.cid = active_cid; - wb.cmd = CTAPHID_MSG; - wb.bcnt = (ctap_resp.length); - - ctaphid_write(&wb, ctap_resp.data, ctap_resp.length); - ctaphid_write(&wb, NULL, 0); - break; - - default: - printf2(TAG_ERR,"error, unimplemented HID cmd: %02x\r\n", buffer_cmd()); - ctaphid_send_error(pkt->cid, CTAP1_ERR_INVALID_COMMAND); - break; - } - cid_del(buffer_cid()); - buffer_reset(); - break; - - default: - printf2(TAG_ERR,"invalid buffer state; abort\n"); - exit(1); - break; - } - - printf1(TAG_HID,"\n"); - + *len = buffer_len(); + *cmd = buffer_cmd(); + return buffer_status(); } +uint8_t ctaphid_handle_packet(uint8_t * pkt_raw) +{ + uint8_t cmd; + uint32_t cid; + int len; + int status; + + static uint8_t is_busy = 0; + static CTAPHID_WRITE_BUFFER wb; + CTAP_RESPONSE ctap_resp; + + uint32_t t1,t2; + + int bufstatus = ctaphid_buffer_packet(pkt_raw, &cmd, &cid, &len); + + if (bufstatus == HID_IGNORE) + { + return 0; + } + + if (bufstatus == HID_ERROR) + { + cid_del(cid); + buffer_reset(); + ctaphid_send_error(cid, cmd); + return 0; + } + + if (bufstatus == BUFFERING) + { + active_cid_timestamp = millis(); + return 0; + } + + + switch(cmd) + { + + case CTAPHID_INIT: + printf2(TAG_ERR,"CTAPHID_INIT, error this should already be handled\n"); + exit(1); + break; +#ifndef DISABLE_CTAPHID_PING + case CTAPHID_PING: + printf1(TAG_HID,"CTAPHID_PING\n"); + + ctaphid_write_buffer_init(&wb); + wb.cid = cid; + wb.cmd = CTAPHID_PING; + wb.bcnt = len; + t1 = millis(); + ctaphid_write(&wb, ctap_buffer, len); + ctaphid_write(&wb, NULL,0); + t2 = millis(); + printf1(TAG_TIME,"PING writeback: %d ms\n",(uint32_t)(t2-t1)); + break; +#endif +#ifndef DISABLE_CTAPHID_WINK + case CTAPHID_WINK: + printf1(TAG_HID,"CTAPHID_WINK\n"); + + ctaphid_write_buffer_init(&wb); + + wb.cid = cid; + wb.cmd = CTAPHID_WINK; + + ctaphid_write(&wb,NULL,0); + + break; +#endif +#ifndef DISABLE_CTAPHID_CBOR + case CTAPHID_CBOR: + printf1(TAG_HID,"CTAPHID_CBOR\n"); + + if (len == 0) + { + printf2(TAG_ERR,"Error,invalid 0 length field for cbor packet\n"); + ctaphid_send_error(cid, CTAP1_ERR_INVALID_LENGTH); + return 0; + } + if (is_busy) + { + printf1(TAG_HID,"Channel busy for CBOR\n"); + ctaphid_send_error(cid, CTAP1_ERR_CHANNEL_BUSY); + return 0; + } + is_busy = 1; + ctap_response_init(&ctap_resp); + status = ctap_request(ctap_buffer, len, &ctap_resp); + + ctaphid_write_buffer_init(&wb); + wb.cid = cid; + wb.cmd = CTAPHID_CBOR; + wb.bcnt = (ctap_resp.length+1); + + + t1 = millis(); + ctaphid_write(&wb, &status, 1); + ctaphid_write(&wb, ctap_resp.data, ctap_resp.length); + ctaphid_write(&wb, NULL, 0); + t2 = millis(); + printf1(TAG_TIME,"CBOR writeback: %d ms\n",(uint32_t)(t2-t1)); + is_busy = 0; + break; +#endif + case CTAPHID_MSG: + + printf1(TAG_HID,"CTAPHID_MSG\n"); + if (len == 0) + { + printf2(TAG_ERR,"Error,invalid 0 length field for MSG/U2F packet\n"); + ctaphid_send_error(cid, CTAP1_ERR_INVALID_LENGTH); + return 0; + } + if (is_busy) + { + printf1(TAG_HID,"Channel busy for MSG\n"); + ctaphid_send_error(cid, CTAP1_ERR_CHANNEL_BUSY); + return 0; + } + is_busy = 1; + ctap_response_init(&ctap_resp); + u2f_request((struct u2f_request_apdu*)ctap_buffer, &ctap_resp); + + ctaphid_write_buffer_init(&wb); + wb.cid = cid; + wb.cmd = CTAPHID_MSG; + wb.bcnt = (ctap_resp.length); + + ctaphid_write(&wb, ctap_resp.data, ctap_resp.length); + ctaphid_write(&wb, NULL, 0); + is_busy = 0; + break; + case CTAPHID_CANCEL: + printf1(TAG_HID,"CTAPHID_CANCEL\n"); + is_busy = 0; + break; + default: + printf2(TAG_ERR,"error, unimplemented HID cmd: %02x\r\n", buffer_cmd()); + ctaphid_send_error(cid, CTAP1_ERR_INVALID_COMMAND); + break; + } + cid_del(cid); + buffer_reset(); + + printf1(TAG_HID,"\n"); + if (!is_busy) return cmd; + else return 0; + +} diff --git a/fido2/ctaphid.h b/fido2/ctaphid.h index a141864..f0e5bf5 100644 --- a/fido2/ctaphid.h +++ b/fido2/ctaphid.h @@ -36,6 +36,7 @@ #define CTAPHID_CBOR (TYPE_INIT | 0x10) #define CTAPHID_CANCEL (TYPE_INIT | 0x11) #define CTAPHID_ERROR (TYPE_INIT | 0x3f) +#define CTAPHID_KEEPALIVE (TYPE_INIT | 0x3b) #define ERR_INVALID_CMD 0x01 #define ERR_INVALID_PAR 0x02 @@ -43,6 +44,11 @@ #define ERR_MSG_TIMEOUT 0x05 #define ERR_CHANNEL_BUSY 0x06 +#define CTAPHID_PROTOCOL_VERSION 2 + +#define CTAPHID_STATUS_IDLE 0 +#define CTAPHID_STATUS_PROCESSING 1 +#define CTAPHID_STATUS_UPNEEDED 2 #define CTAPHID_INIT_PAYLOAD_SIZE (HID_MESSAGE_SIZE-7) #define CTAPHID_CONT_PAYLOAD_SIZE (HID_MESSAGE_SIZE-5) @@ -91,10 +97,12 @@ typedef struct void ctaphid_init(); -void ctaphid_handle_packet(uint8_t * pkt_raw); +uint8_t ctaphid_handle_packet(uint8_t * pkt_raw); void ctaphid_check_timeouts(); +void ctaphid_update_status(int8_t status); + #define ctaphid_packet_len(pkt) ((uint16_t)((pkt)->pkt.init.bcnth << 8) | ((pkt)->pkt.init.bcntl)) diff --git a/fido2/device.h b/fido2/device.h index a49391c..adbc842 100644 --- a/fido2/device.h +++ b/fido2/device.h @@ -56,8 +56,12 @@ void authenticator_write_state(AuthenticatorState *, int backup); // Called each main loop. Doesn't need to do anything. void device_manage(); +// sets status that's uses for sending status updates ~100ms. +// A timer should be set up to call `ctaphid_update_status` +void device_set_status(int status); + // Test for user presence -// Return 1 for user is present, 0 user not present +// Return 1 for user is present, 0 user not present, -1 if cancel is requested. extern int ctap_user_presence_test(); // Generate @num bytes of random numbers to @dest diff --git a/fido2/main.c b/fido2/main.c index a2cca61..797c856 100644 --- a/fido2/main.c +++ b/fido2/main.c @@ -45,20 +45,20 @@ int main(int argc, char * argv[]) set_logging_mask( /*0*/ - TAG_GEN| - /*TAG_MC |*/ - /*TAG_GA |*/ - TAG_WALLET | + // TAG_GEN| + // TAG_MC | + // TAG_GA | + // TAG_WALLET | TAG_STOR | - /*TAG_CP |*/ + // TAG_CP | TAG_CTAP| -// TAG_HID| + // TAG_HID| /*TAG_U2F|*/ - /*TAG_PARSE |*/ -// TAG_TIME| - /*TAG_DUMP|*/ - /*TAG_GREEN|*/ - /*TAG_RED|*/ + // TAG_PARSE | + //TAG_TIME| + // TAG_DUMP| + TAG_GREEN| + TAG_RED| TAG_ERR ); @@ -89,12 +89,11 @@ int main(int argc, char * argv[]) if (usbhid_recv(hidmsg) > 0) { - printf1(TAG_DUMP,"%d>> ",count++); dump_hex1(TAG_DUMP, hidmsg,sizeof(hidmsg)); t2 = millis(); ctaphid_handle_packet(hidmsg); accum += millis() - t2; - printf1(TAG_TIME,"accum: %d\n", (uint32_t)accum); - printf1(TAG_TIME,"dt: %d\n", t2 - dt); + // printf1(TAG_TIME,"accum: %d\n", (uint32_t)accum); + // printf1(TAG_TIME,"dt: %d\n", t2 - dt); dt = t2; memset(hidmsg, 0, sizeof(hidmsg)); } diff --git a/fido2/storage.h b/fido2/storage.h index c9b5bbe..aafde2d 100644 --- a/fido2/storage.h +++ b/fido2/storage.h @@ -39,8 +39,9 @@ typedef struct // Pin information uint8_t is_initialized; uint8_t is_pin_set; - uint8_t pin_code[NEW_PIN_ENC_MAX_SIZE]; - uint8_t remaining_tries; + uint8_t pin_code[NEW_PIN_ENC_MIN_SIZE]; + int pin_code_length; + int8_t remaining_tries; uint16_t key_lens[MAX_KEYS]; uint8_t key_space[KEY_SPACE_BYTES]; diff --git a/targets/stm32l442/Makefile b/targets/stm32l442/Makefile index f4f59d3..55802c1 100644 --- a/targets/stm32l442/Makefile +++ b/targets/stm32l442/Makefile @@ -40,6 +40,7 @@ HW=-mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=hard -mthumb CHIP=STM32L442xx DEFINES = -D$(CHIP) -DAES256=1 -DUSE_FULL_LL_DRIVER +# DEFINES += -DTEST_SOLO_STM32 -DTEST -DTEST_FIFO=1 CFLAGS=$(INC) -c $(DEFINES) -Os -Wall -fdata-sections -ffunction-sections $(HW) LDFLAGS_LIB=$(HW) $(SEARCH) -specs=nano.specs -specs=nosys.specs -Wl,--gc-sections -u _printf_float -lnosys @@ -68,7 +69,8 @@ clean: rm -f *.o src/*.o src/*.elf *.elf *.hex $(OBJ) flash: $(TARGET).hex - STM32_Programmer_CLI -c port=SWD -halt -d $(TARGET).hex -rst + STM32_Programmer_CLI -c port=SWD -halt -e all + STM32_Programmer_CLI -c port=SWD -halt -d $(TARGET).hex -rst sleep 0.5 python dfuse-tool/dfuse-tool.py --leave diff --git a/targets/stm32l442/src/device.c b/targets/stm32l442/src/device.c index ed56c79..f4ecb7f 100644 --- a/targets/stm32l442/src/device.c +++ b/targets/stm32l442/src/device.c @@ -15,6 +15,7 @@ #include "util.h" #include "fifo.h" #include "log.h" +#include "ctaphid.h" #define PAGE_SIZE 2048 @@ -34,18 +35,28 @@ #define AUTH_WORD_ADDR (flash_addr(APPLICATION_END_PAGE)-4) -uint32_t __65_seconds = 0; +uint32_t __90_ms = CTAPHID_STATUS_IDLE; +uint32_t __device_status = 0; +uint32_t __last_update = 0; extern PCD_HandleTypeDef hpcd; #define IS_BUTTON_PRESSED() (0 == (LL_GPIO_ReadInputPort(SOLO_BUTTON_PORT) & SOLO_BUTTON_PIN)) -// Timer6 overflow handler +// Timer6 overflow handler. happens every ~90ms. void TIM6_DAC_IRQHandler() { // timer is only 16 bits, so roll it over here TIM6->SR = 0; - __65_seconds += 1; + __90_ms += 1; + if ((millis() - __last_update) > 5) + { + if (__device_status != CTAPHID_STATUS_IDLE) + { + ctaphid_update_status(__device_status); + } + } } + // Global USB interrupt handler void USB_IRQHandler(void) { @@ -55,10 +66,21 @@ void USB_IRQHandler(void) uint32_t millis() { - return (((uint32_t)TIM6->CNT) | (__65_seconds<<16)); + return (((uint32_t)TIM6->CNT) + (__90_ms * 90)); } +void device_set_status(int status) +{ + __disable_irq(); + __last_update = millis(); + __enable_irq(); + if (status != CTAPHID_STATUS_IDLE && __device_status != status) + { + ctaphid_update_status(status); + } + __device_status = status; +} void delay(uint32_t ms) @@ -75,7 +97,6 @@ void device_init() LL_GPIO_SetPinPull(SOLO_BUTTON_PORT,SOLO_BUTTON_PIN,LL_GPIO_PULL_UP); printf1(TAG_GEN,"hello solo\r\n"); - } void usbhid_init() @@ -86,7 +107,6 @@ int usbhid_recv(uint8_t * msg) { if (fifo_hidmsg_size()) { - fifo_hidmsg_take(msg); printf1(TAG_DUMP,">> "); dump_hex1(TAG_DUMP,msg, HID_PACKET_SIZE); @@ -188,9 +208,11 @@ uint32_t ctap_atomic_count(int sel) int offset = 0; uint32_t * ptr = (uint32_t *)flash_addr(COUNTER1_PAGE); uint32_t erases = *(uint32_t *)flash_addr(COUNTER2_PAGE); + static uint32_t sc = 0; if (erases == 0xffffffff) { erases = 1; + flash_erase_page(COUNTER2_PAGE); flash_write(flash_addr(COUNTER2_PAGE), (uint8_t*)&erases, 4); } @@ -220,44 +242,54 @@ uint32_t ctap_atomic_count(int sel) if (!lastc) // Happens on initialization as well. { - printf("warning, power interrupted during previous count. Restoring.\r\n"); + printf2(TAG_ERR,"warning, power interrupted during previous count. Restoring. lastc==%lu, erases=%lu, offset=%d\r\n", lastc,erases,offset); // there are 32 counts per page - lastc = erases * 32; + lastc = erases * 256 + 1; flash_erase_page(COUNTER1_PAGE); flash_write(flash_addr(COUNTER1_PAGE), (uint8_t*)&lastc, 4); erases++; flash_erase_page(COUNTER2_PAGE); flash_write(flash_addr(COUNTER2_PAGE), (uint8_t*)&erases, 4); + return lastc; } lastc++; - if (lastc/32 > erases) + if (lastc/256 > erases) { - printf("warning, power interrupted, erases mark, restoring\r\n"); - erases = lastc/32 + 1; + printf2(TAG_ERR,"warning, power interrupted, erases mark, restoring. lastc==%lu, erases=%lu\r\n", lastc,erases); + erases = lastc/256; flash_erase_page(COUNTER2_PAGE); flash_write(flash_addr(COUNTER2_PAGE), (uint8_t*)&erases, 4); } if (offset == PAGE_SIZE/4) { - if (lastc/32 > erases) + if (lastc/256 > erases) { - printf("warning, power interrupted, erases mark, restoring\r\n"); + printf2(TAG_ERR,"warning, power interrupted, erases mark, restoring lastc==%lu, erases=%lu\r\n", lastc,erases); } - erases = lastc/32 + 1; + erases = lastc/256 + 1; flash_erase_page(COUNTER2_PAGE); flash_write(flash_addr(COUNTER2_PAGE), (uint8_t*)&erases, 4); flash_erase_page(COUNTER1_PAGE); + offset = 0; } - else + + + flash_write(flash_addr(COUNTER1_PAGE) + offset * 4, (uint8_t*)&lastc, 4); + + if (lastc == sc) { - flash_write(flash_addr(COUNTER1_PAGE) + offset * 4, (uint8_t*)&lastc, 4); + printf1(TAG_RED,"no count detected: lastc==%lu, erases=%lu, offset=%d\r\n", lastc,erases,offset); + while(1) + ; } + sc = lastc; + return lastc; } @@ -284,10 +316,38 @@ void device_manage() #endif } +static int handle_packets() +{ + static uint8_t hidmsg[HID_PACKET_SIZE]; + memset(hidmsg,0, sizeof(hidmsg)); + if (usbhid_recv(hidmsg) > 0) + { + if ( ctaphid_handle_packet(hidmsg) == CTAPHID_CANCEL) + { + printf1(TAG_GREEN, "CANCEL!\r\n"); + return -1; + } + else + { + return 0; + } + } + return 0; +} + int ctap_user_presence_test() { + int oldstatus = __device_status; + int ret; #if SKIP_BUTTON_CHECK - return 1; + int i=500; + while(i--) + { + delay(1); + ret = handle_packets(); + if (ret) return ret; + } + goto done; #endif uint32_t t1 = millis(); @@ -297,15 +357,17 @@ int ctap_user_presence_test() delay(3000); led_rgb(0x001040); delay(50); - return 1; + goto done; #endif while (IS_BUTTON_PRESSED()) { if (t1 + 5000 < millis()) { printf1(TAG_GEN,"Button not pressed\n"); - return 0; + goto fail; } + ret = handle_packets(); + if (ret) return ret; } t1 = millis(); @@ -314,11 +376,13 @@ do { if (t1 + 5000 < millis()) { - return 0; + goto fail; } if (! IS_BUTTON_PRESSED()) continue; delay(1); + ret = handle_packets(); + if (ret) return ret; } while (! IS_BUTTON_PRESSED()); @@ -326,7 +390,11 @@ led_rgb(0x001040); delay(50); +done: return 1; + +fail: +return 0; } int ctap_generate_rng(uint8_t * dst, size_t num) diff --git a/targets/stm32l442/src/fifo.c b/targets/stm32l442/src/fifo.c index c8f621e..cd13348 100644 --- a/targets/stm32l442/src/fifo.c +++ b/targets/stm32l442/src/fifo.c @@ -6,7 +6,7 @@ FIFO_CREATE(debug,1024,1) -FIFO_CREATE(hidmsg,100,100) +FIFO_CREATE(hidmsg,100,64) #if TEST_FIFO FIFO_CREATE(test,10,100) @@ -24,23 +24,25 @@ void fifo_test() for (int i = 0; i < 10; i++) { + printf("rhead: %d, whead: %d\r\n", fifo_test_rhead(), fifo_test_whead()); ret = fifo_test_add(data[i]); printf("%d\r\n",i); if (ret != 0) { printf("fifo_test_add fail\r\n"); - goto end; + goto fail; } } for (int i = 0; i < 10; i++) { + printf("rhead: %d, whead: %d\r\n", fifo_test_rhead(), fifo_test_whead()); ret = fifo_test_take(verif[i]); printf("%d\r\n",i ); if (ret != 0) { printf("fifo_test_take fail\r\n"); - goto end; + goto fail; } if (memcmp(verif[i], data[i], 100) != 0) @@ -48,17 +50,18 @@ void fifo_test() printf("fifo_test_take result fail\r\n"); dump_hex(data[i],100); dump_hex(verif[i],100); - goto end; + goto fail; } } for (int i = 0; i < 10; i++) { + printf("rhead: %d, whead: %d\r\n", fifo_test_rhead(), fifo_test_whead()); ret = fifo_test_add(data[i]); if (ret != 0) { printf("fifo_test_add 2 fail\r\n"); - goto end; + goto fail; } } @@ -66,22 +69,25 @@ void fifo_test() if (ret == 0) { printf("fifo_test_add should have failed\r\n"); - goto end; + goto fail; } + + for (int i = 0; i < 10; i++) { + printf("rhead: %d, whead: %d\r\n", fifo_test_rhead(), fifo_test_whead()); ret = fifo_test_take(verif[i]); if (ret != 0) { printf("fifo_test_take fail\r\n"); - goto end; + goto fail; } if (memcmp(verif[i], data[i], 100) != 0) { printf("fifo_test_take result fail\r\n"); - goto end; + goto fail; } } @@ -89,12 +95,12 @@ void fifo_test() if (ret == 0) { printf("fifo_test_take should have failed\r\n"); - goto end; + goto fail; } printf("test pass!\r\n"); - - end: + return ; + fail: while(1) ; } diff --git a/targets/stm32l442/src/fifo.h b/targets/stm32l442/src/fifo.h index 31efa95..115e04f 100644 --- a/targets/stm32l442/src/fifo.h +++ b/targets/stm32l442/src/fifo.h @@ -3,7 +3,9 @@ #include "app.h" +#ifndef TEST_FIFO #define TEST_FIFO 0 +#endif #define FIFO_CREATE(NAME,LENGTH,BYTES)\ int __##NAME##_WRITE_PTR = 0;\ @@ -13,7 +15,7 @@ static uint8_t __##NAME##_WRITE_BUF[BYTES * LENGTH];\ \ int fifo_##NAME##_add(uint8_t * c)\ {\ - if (__##NAME##_WRITE_PTR != __##NAME##_READ_PTR || !__##NAME##_SIZE)\ + if (__##NAME##_SIZE < LENGTH)\ {\ memmove(__##NAME##_WRITE_BUF + __##NAME##_WRITE_PTR * BYTES, c, BYTES);\ __##NAME##_WRITE_PTR ++;\ @@ -28,7 +30,7 @@ int fifo_##NAME##_add(uint8_t * c)\ int fifo_##NAME##_take(uint8_t * c)\ {\ memmove(c, __##NAME##_WRITE_BUF + __##NAME##_READ_PTR * BYTES, BYTES);\ - if (__##NAME##_READ_PTR != __##NAME##_WRITE_PTR || __##NAME##_SIZE)\ + if ( __##NAME##_SIZE > 0)\ {\ __##NAME##_READ_PTR ++;\ if (__##NAME##_READ_PTR >= LENGTH)\ @@ -43,17 +45,27 @@ uint32_t fifo_##NAME##_size()\ {\ return (__##NAME##_SIZE);\ }\ +uint32_t fifo_##NAME##_rhead()\ +{\ + return (__##NAME##_READ_PTR);\ +}\ +uint32_t fifo_##NAME##_whead()\ +{\ + return (__##NAME##_WRITE_PTR);\ +}\ -#define FIFO_CREATE_H(NAME,LENGTH,BYTES)\ +#define FIFO_CREATE_H(NAME)\ int fifo_##NAME##_add(uint8_t * c);\ int fifo_##NAME##_take(uint8_t * c);\ uint32_t fifo_##NAME##_size();\ +uint32_t fifo_##NAME##_rhead();\ +uint32_t fifo_##NAME##_whead();\ -FIFO_CREATE_H(hidmsg,10,64) +FIFO_CREATE_H(hidmsg) -FIFO_CREATE_H(debug,1024,1) +FIFO_CREATE_H(debug) -FIFO_CREATE_H(test,100,100) +FIFO_CREATE_H(test) void fifo_test(); diff --git a/targets/stm32l442/src/flash.c b/targets/stm32l442/src/flash.c index b86ef87..d87a002 100644 --- a/targets/stm32l442/src/flash.c +++ b/targets/stm32l442/src/flash.c @@ -18,6 +18,7 @@ static void flash_unlock() void flash_erase_page(uint8_t page) { __disable_irq(); + flash_unlock(); // Wait if flash is busy while (FLASH->SR & (1<<16)) ; @@ -71,9 +72,10 @@ void flash_write(uint32_t addr, uint8_t * data, size_t sz) { int i; uint8_t buf[8]; + flash_unlock(); // dword align - addr &= ~(0x7); + addr &= ~(0x07); for(i = 0; i < sz; i+=8) { diff --git a/targets/stm32l442/src/init.c b/targets/stm32l442/src/init.c index 62d5a19..dc7e498 100644 --- a/targets/stm32l442/src/init.c +++ b/targets/stm32l442/src/init.c @@ -337,10 +337,10 @@ static void MX_TIM6_Init(void) LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM6); // 48 MHz sys clock --> 6 MHz timer clock - // 6 MHz / 6000 == 1000 Hz + // 48 MHz / 48000 == 1000 Hz TIM_InitStruct.Prescaler = 48000; TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP; - TIM_InitStruct.Autoreload = 0xffff; + TIM_InitStruct.Autoreload = 90; LL_TIM_Init(TIM6, &TIM_InitStruct); LL_TIM_DisableARRPreload(TIM6); @@ -356,6 +356,35 @@ static void MX_TIM6_Init(void) LL_TIM_EnableCounter(TIM6); } +/* TIM7 init function */ +// static void MX_TIM7_Init(void) +// { +// +// LL_TIM_InitTypeDef TIM_InitStruct; +// +// /* Peripheral clock enable */ +// LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM7); +// +// // 48 MHz sys clock --> 6 MHz timer clock +// // 6 MHz / 6000 == 1000 Hz +// TIM_InitStruct.Prescaler = 48000; +// TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP; +// TIM_InitStruct.Autoreload = 0xffff; +// LL_TIM_Init(TIM6, &TIM_InitStruct); +// +// LL_TIM_DisableARRPreload(TIM7); +// +// LL_TIM_SetTriggerOutput(TIM7, LL_TIM_TRGO_RESET); +// +// LL_TIM_DisableMasterSlaveMode(TIM7); +// +// // enable interrupt +// TIM7->DIER |= 1; +// +// // Start immediately +// LL_TIM_EnableCounter(TIM7); +// } + /* RNG init function */ static void MX_RNG_Init(void) { diff --git a/targets/stm32l442/src/main.c b/targets/stm32l442/src/main.c index 1aef4a3..87cbe4d 100644 --- a/targets/stm32l442/src/main.c +++ b/targets/stm32l442/src/main.c @@ -27,9 +27,126 @@ #include "device.h" #include "util.h" #include "fifo.h" +#include "log.h" #ifdef TEST_SOLO_STM32 #define Error_Handler() _Error_Handler(__FILE__,__LINE__) +#define PAGE_SIZE 2048 +#define PAGES 128 +// Pages 119-127 are data +#define COUNTER2_PAGE (PAGES - 4) +#define COUNTER1_PAGE (PAGES - 3) +#define STATE2_PAGE (PAGES - 2) +#define STATE1_PAGE (PAGES - 1) + +void test_atomic_counter() +{ + // flash_erase_page(COUNTER1_PAGE); + // flash_erase_page(COUNTER2_PAGE); + int i; + uint32_t c0 = ctap_atomic_count(0); + for (i = 0; i < 128; i++) + { + uint32_t c1 = ctap_atomic_count(0); + if (c1 <= (c0 )) + { + printf("error, count failed %lu <= %lu\r\n",c1,c0); + while(1) + ; + } + printf("%lu\r\n", c1); + c0 = c1; + } + + printf("test faults\r\n"); + + printf("%lu\r\n", ctap_atomic_count(0)); + printf("%lu\r\n", ctap_atomic_count(0)); + printf("%lu\r\n", ctap_atomic_count(0)); + printf("%lu\r\n", ctap_atomic_count(0)); + printf("%lu\r\n", ctap_atomic_count(0)); + + flash_erase_page(COUNTER1_PAGE); + + printf("%lu\r\n", ctap_atomic_count(0)); + printf("%lu\r\n", ctap_atomic_count(0)); + printf("%lu\r\n", ctap_atomic_count(0)); + printf("%lu\r\n", ctap_atomic_count(0)); + printf("%lu\r\n", ctap_atomic_count(0)); + + flash_erase_page(COUNTER1_PAGE); + printf("%lu\r\n", ctap_atomic_count(0)); + printf("%lu\r\n", ctap_atomic_count(0)); + printf("%lu\r\n", ctap_atomic_count(0)); + printf("%lu\r\n", ctap_atomic_count(0)); + printf("%lu\r\n", ctap_atomic_count(0)); + + flash_erase_page(COUNTER1_PAGE); + printf("%lu\r\n", ctap_atomic_count(0)); + printf("%lu\r\n", ctap_atomic_count(0)); + printf("%lu\r\n", ctap_atomic_count(0)); + printf("%lu\r\n", ctap_atomic_count(0)); + printf("%lu\r\n", ctap_atomic_count(0)); + + flash_erase_page(COUNTER1_PAGE); + + printf("%lu\r\n", ctap_atomic_count(0)); + printf("%lu\r\n", ctap_atomic_count(0)); + printf("%lu\r\n", ctap_atomic_count(0)); + printf("%lu\r\n", ctap_atomic_count(0)); + printf("%lu\r\n", ctap_atomic_count(0)); + + flash_erase_page(COUNTER2_PAGE); + + printf("%lu\r\n", ctap_atomic_count(0)); + printf("%lu\r\n", ctap_atomic_count(0)); + printf("%lu\r\n", ctap_atomic_count(0)); + printf("%lu\r\n", ctap_atomic_count(0)); + printf("%lu\r\n", ctap_atomic_count(0)); + + flash_erase_page(COUNTER2_PAGE); + + printf("%lu\r\n", ctap_atomic_count(0)); + printf("%lu\r\n", ctap_atomic_count(0)); + printf("%lu\r\n", ctap_atomic_count(0)); + printf("%lu\r\n", ctap_atomic_count(0)); + printf("%lu\r\n", ctap_atomic_count(0)); + + flash_erase_page(COUNTER2_PAGE); + + printf("%lu\r\n", ctap_atomic_count(0)); + printf("%lu\r\n", ctap_atomic_count(0)); + printf("%lu\r\n", ctap_atomic_count(0)); + printf("%lu\r\n", ctap_atomic_count(0)); + printf("%lu\r\n", ctap_atomic_count(0)); + + flash_erase_page(COUNTER2_PAGE); + + printf("%lu\r\n", ctap_atomic_count(0)); + printf("%lu\r\n", ctap_atomic_count(0)); + printf("%lu\r\n", ctap_atomic_count(0)); + printf("%lu\r\n", ctap_atomic_count(0)); + printf("%lu\r\n", ctap_atomic_count(0)); + + flash_erase_page(COUNTER1_PAGE); + + printf("%lu\r\n", ctap_atomic_count(0)); + printf("%lu\r\n", ctap_atomic_count(0)); + printf("%lu\r\n", ctap_atomic_count(0)); + printf("%lu\r\n", ctap_atomic_count(0)); + printf("%lu\r\n", ctap_atomic_count(0)); + + flash_erase_page(COUNTER1_PAGE); + + printf("%lu\r\n", ctap_atomic_count(0)); + printf("%lu\r\n", ctap_atomic_count(0)); + printf("%lu\r\n", ctap_atomic_count(0)); + printf("%lu\r\n", ctap_atomic_count(0)); + printf("%lu\r\n", ctap_atomic_count(0)); + + flash_erase_page(COUNTER1_PAGE); + +} int main(void) { @@ -41,7 +158,24 @@ int main(void) uint8_t hidbuf[HID_PACKET_SIZE]; hw_init(); - + set_logging_mask( + /*0*/ + // TAG_GEN| + TAG_MC | + TAG_GA | + // TAG_WALLET | + TAG_STOR | + TAG_CP | + TAG_CTAP| +// TAG_HID| + /*TAG_U2F|*/ + TAG_PARSE | + //TAG_TIME| + // TAG_DUMP| + TAG_GREEN| + TAG_RED| + TAG_ERR + ); printf("hello solo\r\n"); // Test flash @@ -50,6 +184,24 @@ int main(void) memmove(buf,(uint8_t*)flash_addr(60),sizeof(str)); printf("flash: \"%s\"\r\n", buf); + // test_atomic_counter(); + + + // Note that 4 byte aligned addresses won't get written correctly. + flash_erase_page(60); + uint32_t count = 0; + flash_write(flash_addr(60) + 0,(uint8_t*)&count,4); + count += 1; + flash_write(flash_addr(60) + 4,(uint8_t*)&count,4); + count += 1; + flash_write(flash_addr(60) + 8,(uint8_t*)&count,4); + count += 1; + flash_write(flash_addr(60) + 12,(uint8_t*)&count,4); + count += 1; + flash_write(flash_addr(60) + 16,(uint8_t*)&count,4); + dump_hex((uint8_t *)flash_addr(60), 20); + + // test timer uint32_t t1 = millis(); delay(100); @@ -63,7 +215,7 @@ int main(void) /*// Test PWM + weighting of RGB*/ /*led_test_colors();*/ - + fifo_test(); uint32_t t0 = millis(); @@ -91,15 +243,9 @@ int main(void) fifo_hidmsg_take(hidbuf); dump_hex(hidbuf, HID_PACKET_SIZE); } + while(1) + ; } } - -void _Error_Handler(char *file, int line) -{ - printf("Error: %s: %d\r\n", file, line); - while(1) - { - } -} #endif diff --git a/targets/stm32l442/stm32l432xx.ld b/targets/stm32l442/stm32l432xx.ld index 762abfe..2c44fe9 100644 --- a/targets/stm32l442/stm32l432xx.ld +++ b/targets/stm32l442/stm32l432xx.ld @@ -41,7 +41,7 @@ _Min_Stack_Size = 0x400; /* required amount of stack */ /* Specify the memory areas */ MEMORY { -FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 240K /* Leave out 16 Kb for data */ +FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 238K /* Leave out 18 Kb for data */ RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 48K SRAM2 (rw) : ORIGIN = 0x10000000, LENGTH = 16K } diff --git a/tools/ctap_test.py b/tools/ctap_test.py index c80b36d..0dc807e 100644 --- a/tools/ctap_test.py +++ b/tools/ctap_test.py @@ -387,13 +387,13 @@ class Tester(): def test_u2f(self,): pass - def test_fido2_simple(self): + def test_fido2_simple(self, pin_token=None): creds = [] exclude_list = [] rp = {'id': 'examplo.org', 'name': 'ExaRP'} user = {'id': b'usee_od', 'name': 'AB User'} challenge = 'Y2hhbGxlbmdl' - PIN = None + PIN = pin_token fake_id1 = array.array('B',[randint(0,255) for i in range(0,150)]).tostring() fake_id2 = array.array('B',[randint(0,255) for i in range(0,73)]).tostring() @@ -488,6 +488,7 @@ class Tester(): attest.verify(data.hash) cred = attest.auth_data.credential_data creds.append(cred) + print(cred) print('PASS') if PIN is not None: @@ -511,15 +512,19 @@ class Tester(): real_excl = [{'id': cred.credential_id, 'type': 'public-key'}] try: attest, data = self.client.make_credential(rp, user, challenge, pin = PIN, exclude_list = exclude_list + real_excl) + raise RuntimeError('Exclude list did not return expected error') except CtapError as e: assert(e.code == CtapError.ERR.CREDENTIAL_EXCLUDED) + except ClientError as e: + assert(e.cause.code == CtapError.ERR.CREDENTIAL_EXCLUDED) print('PASS') - print('get assertion') - allow_list = [{'id':creds[0].credential_id, 'type': 'public-key'}] - assertions, client_data = self.client.get_assertion(rp['id'], challenge, allow_list, pin = PIN) - assertions[0].verify(client_data.hash, creds[0].public_key) - print('PASS') + for i, x in enumerate(creds): + print('get assertion %d' % i) + allow_list = [{'id':x.credential_id, 'type': 'public-key'}] + assertions, client_data = self.client.get_assertion(rp['id'], challenge, allow_list, pin = PIN) + assertions[0].verify(client_data.hash, x.public_key) + print('PASS') if PIN is not None: print('get assertion with wrong pin code') @@ -531,11 +536,16 @@ class Tester(): assert(e.cause.code == CtapError.ERR.PIN_INVALID) print('PASS') + print('get multiple assertions') allow_list = [{'id': x.credential_id, 'type': 'public-key'} for x in creds] assertions, client_data = self.client.get_assertion(rp['id'], challenge, allow_list, pin = PIN) + for ass,cred in zip(assertions, creds): + i += 1 + ass.verify(client_data.hash, cred.public_key) + print('%d verified' % i) print('PASS') print('Reset device') @@ -573,6 +583,20 @@ class Tester(): assert(e.code == CtapError.ERR.PIN_INVALID) print('PASS') + print('MC using wrong pin') + try: + self.test_fido2_simple('abcd3'); + except CtapError as e: + assert(e.code == CtapError.ERR.PIN_INVALID) + except ClientError as e: + assert(e.cause.code == CtapError.ERR.PIN_INVALID) + print('PASS') + + print('Reboot device and hit enter') + input() + self.find_device() + self.test_fido2_simple(PIN); + print('Re-run make_credential and get_assertion tests with pin code') test(self, PIN) @@ -583,7 +607,6 @@ class Tester(): print('Warning, reset failed: ', e) print('PASS') - def test_find_brute_force(): i = 0 while 1: @@ -602,6 +625,6 @@ if __name__ == '__main__': # t.test_hid() # t.test_long_ping() t.test_fido2() - #test_find_brute_force() + # test_find_brute_force() #t.test_fido2_simple() #t.test_fido2_brute_force() diff --git a/tools/gencert/cbytes.py b/tools/gencert/cbytes.py index 6ad5fa5..b37c5bd 100644 --- a/tools/gencert/cbytes.py +++ b/tools/gencert/cbytes.py @@ -1,5 +1,6 @@ #!/usr/bin/env python from __future__ import print_function +import base64 """ cbytes.py @@ -39,4 +40,6 @@ print() print('code uint8_t __attest[] = \n%s;' % c_str) print('const uint16_t __attest_size = sizeof(__attest)-1;') - +b = base64.b64encode(buf) +print('b64: ') +print(b) diff --git a/tools/gencert/gen_intermediate.sh b/tools/gencert/gen_intermediate.sh new file mode 100644 index 0000000..290905f --- /dev/null +++ b/tools/gencert/gen_intermediate.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +keyname=interkey.pem +certname=intercert.pem +smallcertname=intercert.der +curve=prime256v1 + +[[ "$#" != 2 ]] && echo "usage: $0 " && exit 1 + +# generate EC private key +openssl ecparam -genkey -name "$curve" -out "$keyname" -rand seed.txt + +# generate a "signing request" +openssl req -new -key "$keyname" -out "$keyname".csr -subj "/C=US/ST=Maryland/O=Solo Keys/OU=Authenticator Attestation/CN=solokeys.com/emailAddress=hello@solokeys.com" + +# sign the request +openssl x509 -req -days 18250 -in "$keyname".csr -extfile v3.ext -CA "$2" -CAkey "$1" -set_serial 01 -out "$certname" -sha256 + +# convert to smaller size format DER +openssl x509 -in $certname -outform der -out $smallcertname + +openssl x509 -in $certname -text -noout diff --git a/tools/gencert/genca.sh b/tools/gencert/genca.sh index 3ee8b62..f282fdf 100644 --- a/tools/gencert/genca.sh +++ b/tools/gencert/genca.sh @@ -6,12 +6,13 @@ smallcertname=cert.der curve=prime256v1 # generate EC private key -openssl ecparam -genkey -name "$curve" -out "$keyname" +openssl ecparam -genkey -name "$curve" -out "$keyname" -rand seed.txt # generate a "signing request" -openssl req -new -key "$keyname" -out "$keyname".csr +openssl req -new -key "$keyname" -out "$keyname".csr -subj "/C=US/ST=Maryland/O=Solo Keys/OU=Root CA/CN=solokeys.com/emailAddress=hello@solokeys.com" # self sign the request -openssl x509 -req -days 18250 -in "$keyname".csr -signkey "$keyname" -out "$certname" +openssl x509 -trustout -req -days 18250 -in "$keyname".csr -signkey "$keyname" -out "$certname" -sha256 # convert to smaller size format DER openssl x509 -in $certname -outform der -out $smallcertname +openssl x509 -in $certname -text -noout diff --git a/tools/gencert/print_x_y.py b/tools/gencert/print_x_y.py new file mode 100644 index 0000000..9c61d96 --- /dev/null +++ b/tools/gencert/print_x_y.py @@ -0,0 +1,16 @@ +import sys +from ecdsa import SigningKey, NIST256p + +sk = SigningKey.from_pem(open(sys.argv[1]).read()) + + +print('Private key in various formats:') +print() +print([c for c in sk.to_string()]) +print() +print(''.join(['%02x'%c for c in sk.to_string()])) +print() +print('"\\x' + '\\x'.join(['%02x'%c for c in sk.to_string()]) + '"') +print() + + diff --git a/tools/gencert/verify_certs.sh b/tools/gencert/verify_certs.sh new file mode 100644 index 0000000..520678e --- /dev/null +++ b/tools/gencert/verify_certs.sh @@ -0,0 +1,22 @@ + +# verify that the root CA/keypair and intermediate CA/keypairs are set up correctly. + +[[ "$#" != 4 ]] && echo "usage: $0 " && exit 1 + +ikey=$1 +icert=$2 + +rkey=$3 +rcert=$4 + +echo 'challenge $RANDOM' > chal.txt + +# check that they are actual key pairs +openssl dgst -sha256 -sign "$ikey" -out sig.txt chal.txt +openssl dgst -sha256 -verify <(openssl x509 -in "$icert" -pubkey -noout) -signature sig.txt chal.txt + +openssl dgst -sha256 -sign "$rkey" -out sig.txt chal.txt +openssl dgst -sha256 -verify <(openssl x509 -in "$rcert" -pubkey -noout) -signature sig.txt chal.txt + +# Check they are a chain +openssl verify -verbose -CAfile "$rcert" "$icert"