diff --git a/fido2/cose_key.h b/fido2/cose_key.h index 4750c31..6056f02 100644 --- a/fido2/cose_key.h +++ b/fido2/cose_key.h @@ -16,7 +16,7 @@ #define COSE_KEY_KTY_EC2 2 #define COSE_KEY_CRV_P256 1 - -#define COSE_ALG_ES256 -7 +#define COSE_ALG_ES256 -7 +#define COSE_ALG_ECDH_ES_HKDF_256 -25 #endif diff --git a/fido2/ctap.c b/fido2/ctap.c index 4e7b465..6d18464 100644 --- a/fido2/ctap.c +++ b/fido2/ctap.c @@ -702,6 +702,14 @@ uint8_t ctap_make_credential(CborEncoder * encoder, uint8_t * request, int lengt printf2(TAG_ERR,"error, parse_make_credential failed\n"); return ret; } + if (MC.pinAuthEmpty) + { + if (!device_is_nfc() && !ctap_user_presence_test()) + { + return CTAP2_ERR_OPERATION_DENIED; + } + return ctap_is_pin_set() == 1 ? CTAP2_ERR_PIN_INVALID : CTAP2_ERR_PIN_NOT_SET; + } if ((MC.paramsParsed & MC_requiredMask) != MC_requiredMask) { printf2(TAG_ERR,"error, required parameter(s) for makeCredential are missing\n"); @@ -1133,6 +1141,14 @@ uint8_t ctap_get_assertion(CborEncoder * encoder, uint8_t * request, int length) return ret; } + if (GA.pinAuthEmpty) + { + if (!device_is_nfc() && !ctap_user_presence_test()) + { + return CTAP2_ERR_OPERATION_DENIED; + } + return ctap_is_pin_set() == 1 ? CTAP2_ERR_PIN_INVALID : CTAP2_ERR_PIN_NOT_SET; + } if (GA.pinAuthPresent) { ret = verify_pin_auth(GA.pinAuth, GA.clientDataHash); @@ -1460,7 +1476,7 @@ uint8_t ctap_client_pin(CborEncoder * encoder, uint8_t * request, int length) ret = cbor_encode_int(&map, RESP_keyAgreement); check_ret(ret); - ret = ctap_add_cose_key(&map, KEY_AGREEMENT_PUB, KEY_AGREEMENT_PUB+32, PUB_KEY_CRED_PUB_KEY, COSE_ALG_ES256); + ret = ctap_add_cose_key(&map, KEY_AGREEMENT_PUB, KEY_AGREEMENT_PUB+32, PUB_KEY_CRED_PUB_KEY, COSE_ALG_ECDH_ES_HKDF_256); check_retr(ret); break; diff --git a/fido2/ctap.h b/fido2/ctap.h index 33f9edb..e36017e 100644 --- a/fido2/ctap.h +++ b/fido2/ctap.h @@ -243,6 +243,11 @@ typedef struct uint8_t pinAuth[16]; uint8_t pinAuthPresent; + // pinAuthEmpty is true iff an empty bytestring was provided as pinAuth. + // This is exclusive with |pinAuthPresent|. It exists because an empty + // pinAuth is a special signal to block for touch. See + // https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html#using-pinToken-in-authenticatorMakeCredential + uint8_t pinAuthEmpty; int pinProtocol; CTAP_extensions extensions; @@ -266,6 +271,11 @@ typedef struct uint8_t pinAuth[16]; uint8_t pinAuthPresent; + // pinAuthEmpty is true iff an empty bytestring was provided as pinAuth. + // This is exclusive with |pinAuthPresent|. It exists because an empty + // pinAuth is a special signal to block for touch. See + // https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html#using-pinToken-in-authenticatorGetAssertion + uint8_t pinAuthEmpty; int pinProtocol; CTAP_credentialDescriptor creds[ALLOW_LIST_MAX_SIZE]; diff --git a/fido2/ctap_parse.c b/fido2/ctap_parse.c index fd92e85..4276da1 100644 --- a/fido2/ctap_parse.c +++ b/fido2/ctap_parse.c @@ -823,14 +823,22 @@ uint8_t ctap_parse_make_credential(CTAP_makeCredential * MC, CborEncoder * encod ret = parse_options(&map, &MC->credInfo.rk, &MC->uv, &MC->up); check_retr(ret); break; - case MC_pinAuth: + case MC_pinAuth: { printf1(TAG_MC,"CTAP_pinAuth\n"); + size_t pinSize; + if (cbor_value_get_type(&map) == CborByteStringType && + cbor_value_get_string_length(&map, &pinSize) == CborNoError && + pinSize == 0) + { + MC->pinAuthEmpty = 1; + break; + } + ret = parse_fixed_byte_string(&map, MC->pinAuth, 16); if (CTAP1_ERR_INVALID_LENGTH != ret) // damn microsoft { check_retr(ret); - } else { @@ -838,6 +846,7 @@ uint8_t ctap_parse_make_credential(CTAP_makeCredential * MC, CborEncoder * encod } MC->pinAuthPresent = 1; break; + } case MC_pinProtocol: printf1(TAG_MC,"CTAP_pinProtocol\n"); if (cbor_value_get_type(&map) == CborIntegerType) @@ -1055,9 +1064,18 @@ uint8_t ctap_parse_get_assertion(CTAP_getAssertion * GA, uint8_t * request, int ret = parse_options(&map, &GA->rk, &GA->uv, &GA->up); check_retr(ret); break; - case GA_pinAuth: + case GA_pinAuth: { printf1(TAG_GA,"CTAP_pinAuth\n"); + size_t pinSize; + if (cbor_value_get_type(&map) == CborByteStringType && + cbor_value_get_string_length(&map, &pinSize) == CborNoError && + pinSize == 0) + { + GA->pinAuthEmpty = 1; + break; + } + ret = parse_fixed_byte_string(&map, GA->pinAuth, 16); if (CTAP1_ERR_INVALID_LENGTH != ret) // damn microsoft { @@ -1073,6 +1091,7 @@ uint8_t ctap_parse_get_assertion(CTAP_getAssertion * GA, uint8_t * request, int GA->pinAuthPresent = 1; break; + } case GA_pinProtocol: printf1(TAG_GA,"CTAP_pinProtocol\n"); if (cbor_value_get_type(&map) == CborIntegerType)