diff --git a/Makefile b/Makefile index 7f93bde4..48c84d9f 100644 --- a/Makefile +++ b/Makefile @@ -135,6 +135,9 @@ DEFINES += HAVE_DEBUG_THROWS #DEFINES += HAVE_CBOR_DEBUG + +ENABLE_NFC = 1 + ############## # Compiler # ############## diff --git a/include/ctap2.h b/include/ctap2.h index b79db1f8..cd8b5507 100644 --- a/include/ctap2.h +++ b/include/ctap2.h @@ -103,6 +103,15 @@ #define CMD_IS_OVER_U2F_CMD (G_io_app.apdu_state != APDU_IDLE) #define CMD_IS_OVER_CTAP2_CBOR_CMD (G_io_app.apdu_state == APDU_IDLE) +#define CMD_IS_OVER_U2F_USB (G_io_u2f.media == U2F_MEDIA_USB) + +#ifdef HAVE_NFC +#define CMD_IS_OVER_U2F_NFC (G_io_app.apdu_media == IO_APDU_MEDIA_NFC) +void nfc_idle_work2(void); +#else +#define CMD_IS_OVER_U2F_NFC false +#endif + extern const uint8_t AAGUID[16]; typedef struct ctap2_register_data_s { @@ -177,7 +186,10 @@ void ctap2_send_keepalive_processing(void); // Correspond to FIDO2.1 spec performBuiltInUv() operation void performBuiltInUv(void); -void ctap2_make_credential_handle(u2f_service_t *service, uint8_t *buffer, uint16_t length); +void ctap2_make_credential_handle(u2f_service_t *service, + uint8_t *buffer, + uint16_t length, + bool *immediateReply); void ctap2_get_assertion_handle(u2f_service_t *service, uint8_t *buffer, uint16_t length, diff --git a/include/nfc_io.h b/include/nfc_io.h new file mode 100644 index 00000000..6000ef38 --- /dev/null +++ b/include/nfc_io.h @@ -0,0 +1,44 @@ +/* +******************************************************************************* +* Ledger App Security Key +* (c) 2024 Ledger +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ + +#ifdef HAVE_NFC +void nfc_io_set_le(uint32_t le); +void nfc_io_set_response_ready(uint16_t sw, uint16_t len, const char *status); +bool nfc_io_is_response_pending(void); +int nfc_io_send_prepared_response(void); + +#else +static inline void nfc_io_set_le(uint32_t le __attribute__((unused))) { + return; +} + +static inline void nfc_io_set_response_ready(uint16_t sw, uint16_t len, const char *status) { + UNUSED(sw); + UNUSED(len); + UNUSED(status); + return; +} + +static inline bool nfc_io_is_response_pending(void) { + return false; +} + +static inline int nfc_io_send_prepared_response(void) { + return -1; +} +#endif diff --git a/include/u2f_process.h b/include/u2f_process.h index 3da017ef..4a5f24c0 100644 --- a/include/u2f_process.h +++ b/include/u2f_process.h @@ -28,4 +28,8 @@ typedef struct u2f_data_t { int u2f_handle_apdu(uint8_t *rx, int length); -#endif +#ifdef HAVE_NFC +void nfc_idle_work(void); +#endif // HAVE_NFC + +#endif // __U2F_PROCESS_H__ diff --git a/src/app_main.c b/src/app_main.c index fb28ed96..65dc7c3d 100644 --- a/src/app_main.c +++ b/src/app_main.c @@ -35,6 +35,10 @@ void app_ticker_event_callback(void) { if (ctap2UxState != CTAP2_UX_STATE_NONE) { u2f_transport_ctap2_send_keepalive(&G_io_u2f, KEEPALIVE_REASON_TUP_NEEDED); } +#ifdef HAVE_NFC + nfc_idle_work(); + nfc_idle_work2(); +#endif } /** diff --git a/src/ctap2_get_assertion.c b/src/ctap2_get_assertion.c index 9ba999d8..925d9de0 100644 --- a/src/ctap2_get_assertion.c +++ b/src/ctap2_get_assertion.c @@ -57,7 +57,7 @@ static int parse_getAssert_authnr_rpid(cbipDecoder_t *decoder, cbipItem_t *mapIt } #ifdef HAVE_FIDO2_RPID_FILTER - if (CMD_IS_OVER_U2F_CMD) { + if (CMD_IS_OVER_U2F_CMD && !CMD_IS_OVER_U2F_NFC) { if (ctap2_check_rpid_filter(ctap2AssertData->rpId, ctap2AssertData->rpIdLen)) { PRINTF("rpId denied by filter\n"); return ERROR_PROP_RPID_MEDIA_DENIED; @@ -380,7 +380,17 @@ void ctap2_get_assertion_handle(u2f_service_t *service, goto exit; } - if (!ctap2AssertData->userPresenceRequired && !ctap2AssertData->pinRequired) { + if (CMD_IS_OVER_U2F_NFC) { + // No up nor uv requested, skip UX and reply immediately + // TODO: is this what we want? + // TODO: Handle cases where availableCredentials is != 1 + // -> which credentials should be chosen? + // -> when credentials comes from allowListPresent, I think the spec allow to choose for + // the user + // -> when credentials comes from rk, the spec ask to use authenticatorGetNextAssertion + // features + *immediateReply = true; + } else if (!ctap2AssertData->userPresenceRequired && !ctap2AssertData->pinRequired) { // No up nor uv required, skip UX and reply immediately *immediateReply = true; } else { diff --git a/src/ctap2_make_credential.c b/src/ctap2_make_credential.c index 38ef166d..241c0a97 100644 --- a/src/ctap2_make_credential.c +++ b/src/ctap2_make_credential.c @@ -93,7 +93,7 @@ static int parse_makeCred_authnr_rp(cbipDecoder_t *decoder, cbipItem_t *mapItem) } #ifdef HAVE_FIDO2_RPID_FILTER - if (CMD_IS_OVER_U2F_CMD) { + if (CMD_IS_OVER_U2F_CMD && !CMD_IS_OVER_U2F_NFC) { if (ctap2_check_rpid_filter(ctap2RegisterData->rpId, ctap2RegisterData->rpIdLen)) { PRINTF("rpId denied by filter\n"); return ERROR_PROP_RPID_MEDIA_DENIED; @@ -407,7 +407,10 @@ static int process_makeCred_authnr_pin(cbipDecoder_t *decoder, cbipItem_t *mapIt return 0; } -void ctap2_make_credential_handle(u2f_service_t *service, uint8_t *buffer, uint16_t length) { +void ctap2_make_credential_handle(u2f_service_t *service, + uint8_t *buffer, + uint16_t length, + bool *immediateReply) { ctap2_register_data_t *ctap2RegisterData = globals_get_ctap2_register_data(); cbipDecoder_t decoder; cbipItem_t mapItem; @@ -415,6 +418,7 @@ void ctap2_make_credential_handle(u2f_service_t *service, uint8_t *buffer, uint1 PRINTF("ctap2_make_credential_handle\n"); + *immediateReply = false; memset(ctap2RegisterData, 0, sizeof(ctap2_register_data_t)); ctap2RegisterData->buffer = buffer; @@ -480,7 +484,13 @@ void ctap2_make_credential_handle(u2f_service_t *service, uint8_t *buffer, uint1 goto exit; } - ctap2_make_credential_ux(); + if (CMD_IS_OVER_U2F_NFC) { + // No up nor uv requested, skip UX and reply immediately + // TODO: is this what we want? + *immediateReply = true; + } else { + ctap2_make_credential_ux(); + } exit: if (status != 0) { @@ -490,23 +500,83 @@ void ctap2_make_credential_handle(u2f_service_t *service, uint8_t *buffer, uint1 return; } +static int generate_pubkey(const uint8_t *nonce, int coseAlgorithm, cx_ecfp_public_key_t *pubkey) { + cx_ecfp_private_key_t privateKey; + cx_curve_t bolosCurve = cose_alg_to_cx(coseAlgorithm); + + if (crypto_generate_private_key(nonce, &privateKey, bolosCurve) != 0) { + return -1; + } + if (cx_ecfp_generate_pair_no_throw(bolosCurve, pubkey, &privateKey, 1) != CX_OK) { + return -1; + } + + return 0; +} + +#ifdef HAVE_NFC +static bool nfc_nonce_and_pubkey_ready; +static uint8_t nfc_nonce[CREDENTIAL_NONCE_SIZE]; +static cx_ecfp_public_key_t nfc_pubkey_ES256; +static cx_ecfp_public_key_t nfc_pubkey_ES256K; +static cx_ecfp_public_key_t nfc_pubkey_EDDSA; + +void nfc_idle_work2(void) { + // Generate a new nonce/pubkey pair only if not already available and in idle + if (nfc_nonce_and_pubkey_ready) { + return; + } + + cx_rng_no_throw(nfc_nonce, CREDENTIAL_NONCE_SIZE); + + if (generate_pubkey(nfc_nonce, COSE_ALG_ES256, &nfc_pubkey_ES256) != 0) { + return; + } + + if (generate_pubkey(nfc_nonce, COSE_ALG_ES256K, &nfc_pubkey_ES256K) != 0) { + return; + } + + if (generate_pubkey(nfc_nonce, COSE_ALG_EDDSA, &nfc_pubkey_EDDSA) != 0) { + return; + } + + nfc_nonce_and_pubkey_ready = true; +} +#endif + static int encode_makeCred_public_key(const uint8_t *nonce, int coseAlgorithm, uint8_t *buffer, uint32_t bufferLength) { cbipEncoder_t encoder; - cx_ecfp_private_key_t privateKey; cx_ecfp_public_key_t publicKey; - cx_curve_t bolosCurve; int status; - bolosCurve = cose_alg_to_cx(coseAlgorithm); +#ifdef HAVE_NFC + // Spare response time by pre-generating part of the answer + if (nfc_nonce_and_pubkey_ready) { + switch (coseAlgorithm) { + case COSE_ALG_ES256: + memcpy(&publicKey, &nfc_pubkey_ES256, sizeof(publicKey)); + break; + case COSE_ALG_ES256K: + memcpy(&publicKey, &nfc_pubkey_ES256K, sizeof(publicKey)); + break; + case COSE_ALG_EDDSA: + memcpy(&publicKey, &nfc_pubkey_EDDSA, sizeof(publicKey)); + break; + default: + return -1; + } - if (crypto_generate_private_key(nonce, &privateKey, bolosCurve) != 0) { - return -1; - } - if (cx_ecfp_generate_pair_no_throw(bolosCurve, &publicKey, &privateKey, 1) != CX_OK) { - return -1; + nfc_nonce_and_pubkey_ready = false; + } else +#endif + { + if (generate_pubkey(nonce, coseAlgorithm, &publicKey) != 0) { + return -1; + } } cbip_encoder_init(&encoder, buffer, bufferLength); @@ -670,7 +740,15 @@ void ctap2_make_credential_confirm() { ctap2_send_keepalive_processing(); // Generate nonce - cx_rng_no_throw(nonce, CREDENTIAL_NONCE_SIZE); +#ifdef HAVE_NFC + // Spare response time by pre-generating part of the answer + if (nfc_nonce_and_pubkey_ready) { + memcpy(nonce, nfc_nonce, CREDENTIAL_NONCE_SIZE); + } else +#endif + { + cx_rng_no_throw(nonce, CREDENTIAL_NONCE_SIZE); + } // Build auth data status = diff --git a/src/ctap2_processing.c b/src/ctap2_processing.c index 9dbb3568..c680299b 100644 --- a/src/ctap2_processing.c +++ b/src/ctap2_processing.c @@ -29,10 +29,20 @@ #include "fido_known_apps.h" #include "ui_shared.h" #include "sw_code.h" +#include "nfc_io.h" + +static uint8_t cmdType; #define RPID_FILTER "webctap." #define RPID_FILTER_SIZE (sizeof(RPID_FILTER) - 1) +#define CBOR_MAKE_CREDENTIAL 0x01 +#define CBOR_GET_ASSERTION 0x02 +#define CBOR_GET_NEXT_ASSERTION 0x08 +#define CBOR_GET_INFO 0x04 +#define CBOR_CLIENT_PIN 0x06 +#define CBOR_RESET 0x07 + bool ctap2_check_rpid_filter(const char *rpId, uint32_t rpIdLen) { if ((rpIdLen < RPID_FILTER_SIZE) || (memcmp(rpId, RPID_FILTER, RPID_FILTER_SIZE) != 0)) { return true; @@ -50,7 +60,16 @@ void send_cbor_error(u2f_service_t *service, uint8_t error) { } void send_cbor_response(u2f_service_t *service, uint32_t length) { - if (CMD_IS_OVER_U2F_CMD) { + if (CMD_IS_OVER_U2F_NFC) { + const char *status = NULL; + if (cmdType == CBOR_MAKE_CREDENTIAL) { + status = "Registration details\nsent"; + } else if (cmdType == CBOR_GET_ASSERTION) { + status = "Login request signed"; + } + nfc_io_set_response_ready(SW_NO_ERROR, length, status); + nfc_io_send_prepared_response(); + } else if (CMD_IS_OVER_U2F_CMD) { io_send_response_pointer(responseBuffer, length, SW_NO_ERROR); } else { u2f_message_reply(service, CTAP2_CMD_CBOR, responseBuffer, length); @@ -69,13 +88,6 @@ void performBuiltInUv(void) { // No-op as the user is verified through the session PIN. } -#define CBOR_MAKE_CREDENTIAL 0x01 -#define CBOR_GET_ASSERTION 0x02 -#define CBOR_GET_NEXT_ASSERTION 0x08 -#define CBOR_GET_INFO 0x04 -#define CBOR_CLIENT_PIN 0x06 -#define CBOR_RESET 0x07 - void ctap2_handle_cmd_cbor(u2f_service_t *service, uint8_t *buffer, uint16_t length) { int status; // PRINTF("cmd_cbor %d %.*H\n", length, length, buffer); @@ -96,11 +108,16 @@ void ctap2_handle_cmd_cbor(u2f_service_t *service, uint8_t *buffer, uint16_t len send_cbor_error(service, ERROR_INVALID_CBOR); return; } + cmdType = buffer[0]; switch (buffer[0]) { - case CBOR_MAKE_CREDENTIAL: - ctap2_make_credential_handle(service, buffer + 1, length - 1); - break; + case CBOR_MAKE_CREDENTIAL: { + bool immediateReply; + ctap2_make_credential_handle(service, buffer + 1, length - 1, &immediateReply); + if (immediateReply) { + ctap2_make_credential_confirm(); + } + } break; case CBOR_GET_ASSERTION: { bool immediateReply; ctap2_get_assertion_handle(service, buffer + 1, length - 1, &immediateReply); diff --git a/src/ctap2_reset.c b/src/ctap2_reset.c index 283722ed..dc20100e 100644 --- a/src/ctap2_reset.c +++ b/src/ctap2_reset.c @@ -28,6 +28,12 @@ void ctap2_reset_handle(u2f_service_t *service, uint8_t *buffer, uint16_t length UNUSED(buffer); UNUSED(length); + if (CMD_IS_OVER_U2F_NFC) { + // Denied authenticatorReset over NFC as it can't be approved by the user. + // Note, this is a behavior allowed by the FIDO spec. + send_cbor_error(&G_io_u2f, ERROR_OPERATION_DENIED); + } + PRINTF("ctap2_reset_handle\n"); ctap2_reset_ux(); } diff --git a/src/nfc_io.c b/src/nfc_io.c new file mode 100644 index 00000000..e45df41d --- /dev/null +++ b/src/nfc_io.c @@ -0,0 +1,91 @@ +/* +******************************************************************************* +* Ledger App Security Key +* (c) 2024 Ledger +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ + +#ifdef HAVE_NFC +#include +#include + +#include "os_math.h" +#include "io.h" +#include "sw_code.h" +#include "globals.h" +#include "nfc_io.h" +#include "ui_shared.h" + +static bool nfc_data_ready; +static uint16_t nfc_sw; +static uint16_t nfc_buffer_len; +static uint16_t nfc_buffer_offset; +static uint32_t nfc_le; +static const char *nfc_status; + +void nfc_io_set_le(uint32_t le) { + nfc_le = le; +} + +void nfc_io_set_response_ready(uint16_t sw, uint16_t len, const char *status) { + nfc_sw = sw; + nfc_buffer_len = len; + nfc_status = status; + nfc_buffer_offset = 0; + nfc_data_ready = true; +} + +bool nfc_io_is_response_pending(void) { + return nfc_data_ready; +} + +int nfc_io_send_prepared_response(void) { + if (!nfc_data_ready) { + return io_send_sw(SW_WRONG_DATA); + } + + if (nfc_sw != SW_NO_ERROR) { + nfc_data_ready = false; + return io_send_sw(nfc_sw); + } + + if (nfc_buffer_offset >= nfc_buffer_len) { + nfc_data_ready = false; + return io_send_sw(SW_WRONG_DATA); + } + + uint16_t size = MIN(nfc_le, nfc_buffer_len - nfc_buffer_offset); + uint16_t start = nfc_buffer_offset; + + nfc_buffer_offset += size; + + uint16_t sw; + if ((nfc_buffer_len - nfc_buffer_offset) >= 256) { + sw = SW_MORE_DATA; + } else if (nfc_buffer_len == nfc_buffer_offset) { + nfc_data_ready = false; + sw = SW_NO_ERROR; + } else { + sw = SW_MORE_DATA + (nfc_buffer_len - nfc_buffer_offset); + } + + int ret = io_send_response_pointer(responseBuffer + start, size, sw); + if (sw == SW_NO_ERROR && nfc_status != NULL) { + app_nbgl_status(nfc_status, true, ui_idle, TUNE_SUCCESS); + } + + return ret; +} + +#endif diff --git a/src/u2f_processing.c b/src/u2f_processing.c index c05da21d..b5add9e5 100644 --- a/src/u2f_processing.c +++ b/src/u2f_processing.c @@ -38,21 +38,34 @@ #include "globals.h" #include "fido_known_apps.h" #include "ctap2.h" +#include "nfc_io.h" #include "sw_code.h" #define U2F_VERSION "U2F_V2" #define U2F_VERSION_SIZE (sizeof(U2F_VERSION) - 1) +#define FIDO2_VERSION "FIDO_2_0" +#define FIDO2_VERSION_SIZE (sizeof(FIDO2_VERSION) - 1) + +#define FIDO_AID_SIZE 8 +static const uint8_t FIDO_AID[FIDO_AID_SIZE] = {0xA0, 0x00, 0x00, 0x06, 0x47, 0x2F, 0x00, 0x01}; + #define OFFSET_CLA 0 #define OFFSET_INS 1 #define OFFSET_P1 2 #define OFFSET_P2 3 -#define FIDO_CLA 0x00 -#define FIDO_INS_ENROLL 0x01 -#define FIDO_INS_SIGN 0x02 -#define FIDO_INS_GET_VERSION 0x03 -#define FIDO_INS_CTAP2_PROXY 0x10 +#define FIDO_CLA 0x00 +#define FIDO_INS_ENROLL 0x01 +#define FIDO_INS_SIGN 0x02 +#define FIDO_INS_GET_VERSION 0x03 +#define FIDO_INS_CTAP2_PROXY 0x10 +#define FIDO_INS_APPLET_SELECT 0xA4 + +#define FIDO2_NFC_CLA 0x80 +#define FIDO2_NFC_CHAINING_CLA 0x90 +#define FIDO2_NFC_INS_CTAP2_PROXY 0x10 +#define FIDO2_NFC_INS_APPLET_DESELECT 0x12 #define P1_U2F_CHECK_IS_REGISTERED 0x07 #define P1_U2F_REQUEST_USER_PRESENCE 0x03 @@ -74,7 +87,11 @@ static const uint8_t DUMMY_USER_PRESENCE[] = {SIGN_USER_PRESENCE_MASK}; #define SHORT_ENC_DATA_OFFSET 5 #define EXT_ENC_DATA_OFFSET 7 -int u2f_get_cmd_msg_data(uint8_t *rx, uint16_t rx_length, uint8_t **data, uint16_t *le) { +#define SHORT_ENC_DEFAULT_LE \ + 253 // Should be 256, stax-rc4 MCU only support 255, so use 253 + 2 for now here +#define EXT_ENC_DEFAULT_LE 65536 + +static int u2f_get_cmd_msg_data(uint8_t *rx, uint16_t rx_length, uint8_t **data, uint32_t *le) { uint32_t data_length; /* Parse buffer to retrieve the data length. Both Short and Extended encodings are supported */ @@ -87,7 +104,8 @@ int u2f_get_cmd_msg_data(uint8_t *rx, uint16_t rx_length, uint8_t **data, uint16 } if (rx_length == APDU_MIN_HEADER) { - // Either short or extended encoding with Lc and Le omitted + // Short encoding with Lc and Le omitted + *le = SHORT_ENC_DEFAULT_LE; return 0; } @@ -103,6 +121,9 @@ int u2f_get_cmd_msg_data(uint8_t *rx, uint16_t rx_length, uint8_t **data, uint16 *le = rx[APDU_MIN_HEADER]; } + if (*le == 0) { + *le = SHORT_ENC_DEFAULT_LE; + } return 0; } @@ -117,6 +138,11 @@ int u2f_get_cmd_msg_data(uint8_t *rx, uint16_t rx_length, uint8_t **data, uint16 } else { return -1; } + + if (*le == 0) { + *le = SHORT_ENC_DEFAULT_LE; + } + return data_length; } if (rx_length == APDU_MIN_HEADER + 3) { @@ -136,6 +162,10 @@ int u2f_get_cmd_msg_data(uint8_t *rx, uint16_t rx_length, uint8_t **data, uint16 } else { return -1; } + + if (*le == 0) { + *le = SHORT_ENC_DEFAULT_LE; + } return data_length; } else { // Can't be short encoding as Lc = 0x00 would lead to invalid length @@ -144,6 +174,12 @@ int u2f_get_cmd_msg_data(uint8_t *rx, uint16_t rx_length, uint8_t **data, uint16 // - Lc omitted and Le = 0x00 0xyy 0xzz // so no way to check the value // but anyway the data length is 0 + *le = (rx[APDU_MIN_HEADER + 1] << 8) + rx[APDU_MIN_HEADER + 2]; + + if (*le == 0) { + *le = EXT_ENC_DEFAULT_LE; + } + return 0; } } @@ -162,6 +198,10 @@ int u2f_get_cmd_msg_data(uint8_t *rx, uint16_t rx_length, uint8_t **data, uint16 } else { return -1; } + + if (*le == 0) { + *le = SHORT_ENC_DEFAULT_LE; + } return data_length; } else { // Can't be short encoding as Lc = 0 would lead to invalid length @@ -175,10 +215,13 @@ int u2f_get_cmd_msg_data(uint8_t *rx, uint16_t rx_length, uint8_t **data, uint16 } else if (APDU_MIN_HEADER + EXT_ENC_LC_SIZE + data_length + EXT_ENC_LE_SIZE == rx_length) { /* Le is present*/ *le = (rx[EXT_ENC_DATA_OFFSET + data_length] << 8) + - rx[EXT_ENC_DATA_OFFSET + data_length]; + rx[EXT_ENC_DATA_OFFSET + data_length + 1]; } else { return -1; } + if (*le == 0) { + *le = EXT_ENC_DEFAULT_LE; + } return data_length; } } @@ -297,6 +340,26 @@ static int u2f_generate_pubkey(const uint8_t *nonce, return 0; } +#ifdef HAVE_NFC +static bool nfc_nonce_and_pubkey_ready; +static uint8_t nfc_nonce[CREDENTIAL_NONCE_SIZE]; +static uint8_t nfc_pubkey[U2F_ENROLL_USER_KEY_SIZE]; + +void nfc_idle_work(void) { + // Generate a new nonce/pubkey pair only if not already available and in idle + if (nfc_nonce_and_pubkey_ready || nfc_io_is_response_pending()) { + return; + } + + cx_rng_no_throw(nfc_nonce, CREDENTIAL_NONCE_SIZE); + if (u2f_generate_pubkey(nfc_nonce, nfc_pubkey) != 0) { + return; + } + + nfc_nonce_and_pubkey_ready = true; +} +#endif + static uint16_t u2f_prepare_enroll_response(uint8_t *buffer, uint16_t *length) { int offset = 0; int result; @@ -310,6 +373,14 @@ static uint16_t u2f_prepare_enroll_response(uint8_t *buffer, uint16_t *length) { // Fill reserved byte reg_resp_base->reserved_byte = U2F_ENROLL_RESERVED; +#ifdef HAVE_NFC + // Spare response time by pre-generating part of the answer + if (nfc_nonce_and_pubkey_ready) { + memcpy(globals_get_u2f_data()->nonce, nfc_nonce, CREDENTIAL_NONCE_SIZE); + memcpy(reg_resp_base->user_key, nfc_pubkey, U2F_ENROLL_USER_KEY_SIZE); + nfc_nonce_and_pubkey_ready = false; + } else +#endif { // Generate nonce cx_rng_no_throw(globals_get_u2f_data()->nonce, CREDENTIAL_NONCE_SIZE); @@ -616,7 +687,14 @@ static int u2f_handle_apdu_enroll(const uint8_t *rx, uint32_t data_length, const reg_req->application_param, sizeof(reg_req->application_param)); - if (G_io_u2f.media == U2F_MEDIA_USB) { + if (CMD_IS_OVER_U2F_NFC) { + uint16_t length = 0; + uint16_t sw = u2f_prepare_enroll_response(responseBuffer, &length); + + nfc_io_set_response_ready(sw, length, "Registration details\nsent"); + + return nfc_io_send_prepared_response(); + } else if (CMD_IS_OVER_U2F_USB) { u2f_message_set_autoreply_wait_user_presence(&G_io_u2f, true); } u2f_prompt_user_presence(true, globals_get_u2f_data()->application_param); @@ -681,11 +759,30 @@ static int u2f_handle_apdu_sign(const uint8_t *rx, uint32_t data_length, uint8_t auth_req_base->application_param, sizeof(auth_req_base->application_param)); - if (G_io_u2f.media == U2F_MEDIA_USB) { + // clang-format off + // following macros + `else if` was messing with clang until the `return` +#ifdef HAVE_NFC + if (CMD_IS_OVER_U2F_NFC) { + // Android doesn't support answering SW_MORE_DATA here... + // so compute the real answer as fast as possible + uint16_t length = 0; + uint16_t sw = u2f_prepare_sign_response(responseBuffer, &length); + + // Message fit in a single response, answer directly without nfc_io features + io_send_response_pointer(responseBuffer, length, sw); + + app_nbgl_status("Login request signed", true, ui_idle, TUNE_SUCCESS); + return 0; + } else +#endif // HAVE_NFC + if (CMD_IS_OVER_U2F_USB) { u2f_message_set_autoreply_wait_user_presence(&G_io_u2f, true); } + u2f_prompt_user_presence(false, globals_get_u2f_data()->application_param); return 0; + + // clang-format on } static int u2f_handle_apdu_get_version(const uint8_t *rx, @@ -712,9 +809,23 @@ static int u2f_handle_apdu_ctap2_proxy(uint8_t *rx, int data_length, uint8_t *da return 0; } +static int u2f_handle_apdu_applet_select(uint8_t *rx, int data_length, const uint8_t *data) { + if ((rx[OFFSET_P1] != 0x04) || (rx[OFFSET_P2] != 0)) { + return io_send_sw(SW_INCORRECT_P1P2); + } + + if ((data_length != FIDO_AID_SIZE) || (memcmp(data, FIDO_AID, FIDO_AID_SIZE) != 0)) { + return io_send_sw(SW_WRONG_DATA); + } + + return io_send_response_pointer((const uint8_t *) U2F_VERSION, U2F_VERSION_SIZE, SW_NO_ERROR); +} + int u2f_handle_apdu(uint8_t *rx, int rx_length) { + // PRINTF("=> RAW=%.*H\n", rx_length, rx); + uint8_t *data = NULL; - uint16_t le = 0; + uint32_t le = 0; // PRINTF("Media handleApdu %d\n", G_io_app.apdu_state); // Make sure cmd is detected as over U2F_CMD and not as CMD_IS_OVER_CTAP2_CBOR_CMD @@ -727,6 +838,10 @@ int u2f_handle_apdu(uint8_t *rx, int rx_length) { return io_send_sw(SW_WRONG_LENGTH); } + if (CMD_IS_OVER_U2F_NFC) { + nfc_io_set_le(le); + } + PRINTF("INS %d, P1 %d P2 %d L %d\n", rx[OFFSET_INS], rx[OFFSET_P1], rx[OFFSET_P2], data_length); if (rx[OFFSET_CLA] == FIDO_CLA) { @@ -747,6 +862,50 @@ int u2f_handle_apdu(uint8_t *rx, int rx_length) { PRINTF("ctap2_proxy\n"); return u2f_handle_apdu_ctap2_proxy(rx, data_length, data); + case FIDO_INS_APPLET_SELECT: + PRINTF("applet_select\n"); + // return io_send_sw(SW_INS_NOT_SUPPORTED); + return u2f_handle_apdu_applet_select(rx, data_length, data); + + case 0xc0: + if (!CMD_IS_OVER_U2F_NFC) { + return io_send_sw(SW_INS_NOT_SUPPORTED); + } + return nfc_io_send_prepared_response(); + + default: + PRINTF("unsupported\n"); + return io_send_sw(SW_INS_NOT_SUPPORTED); + } + } else if (CMD_IS_OVER_U2F_NFC && (rx[OFFSET_CLA] == FIDO2_NFC_CLA)) { + switch (rx[OFFSET_INS]) { + case FIDO2_NFC_INS_CTAP2_PROXY: + PRINTF("ctap2_proxy\n"); + return u2f_handle_apdu_ctap2_proxy(rx, data_length, data); + + case 0x11: + PRINTF("NFCCTAP_GETRESPONSE\n"); + return nfc_io_send_prepared_response(); + + case FIDO2_NFC_INS_APPLET_DESELECT: + PRINTF("unsupported\n"); + return io_send_sw(SW_INS_NOT_SUPPORTED); + + case 0xc0: + return nfc_io_send_prepared_response(); + + default: + PRINTF("unsupported\n"); + return io_send_sw(SW_INS_NOT_SUPPORTED); + } + } else if (CMD_IS_OVER_U2F_NFC && (rx[OFFSET_CLA] == FIDO2_NFC_CHAINING_CLA)) { + // TODO but as of now it's not used neither on: + // - iOS: using extended encoding + // - Android: using U2F only + switch (rx[OFFSET_INS]) { + case 0x60: + return io_send_sw(0x9000); + default: PRINTF("unsupported\n"); return io_send_sw(SW_INS_NOT_SUPPORTED); diff --git a/src/ui_shared.c b/src/ui_shared.c index e7d037fc..c0ce9ff6 100644 --- a/src/ui_shared.c +++ b/src/ui_shared.c @@ -269,9 +269,19 @@ static void ui_menu_settings_page(void) { } void ui_idle(void) { + const char *txt = "Use this app for two-factor\nauthentication and\npassword-less log ins."; +#ifdef HAVE_NFC + bool nfc_enabled; + + nfc_enabled = os_setting_get(OS_SETTING_FEATURES, NULL, 0) & OS_SETTING_FEATURES_NFC_ENABLED; + if (!nfc_enabled) { + txt = "\n\n/!\\ NFC is disabled /!\\"; + } +#endif + nbgl_useCaseHome(APPNAME, &C_icon_security_key_64px, - "Use this app for two-factor\nauthentication and\npassword-less log ins.", + txt, #ifdef HAVE_RK_SUPPORT_SETTING true, #else diff --git a/tests/nfc_test_u2f.py b/tests/nfc_test_u2f.py new file mode 100755 index 00000000..366571e8 --- /dev/null +++ b/tests/nfc_test_u2f.py @@ -0,0 +1,238 @@ +#!/usr/bin/env python3 + +import nfc +from time import sleep +from datetime import datetime +from fido2 import cbor +from fido2.ctap2.base import Ctap2, Info, args, AttestationResponse, AssertionResponse +from fido2.ctap1 import Ctap1, RegistrationData, SignatureData +from fido2.webauthn import AttestedCredentialData +from fido2.cose import ES256 + +import struct +import secrets +import random +import string + + +FIDO_CLA = 0x00 +FIDO_AID = bytearray.fromhex("A0000006472F0001") + + +def generate_random_bytes(length): + return secrets.token_bytes(length) + + +def generate_random_string(length): + return "".join(random.choice(string.ascii_lowercase) for _ in range(length)) + + +def generate_make_credentials_params(): + rp_base = generate_random_string(20) + rp_id = "webctap.{}.com".format(rp_base) + user_id = generate_random_bytes(64) + user_name = "vgfjbdeskjgbrsbvgsb" + + client_data_hash = generate_random_bytes(32) + rp = {"id": rp_id} + user = {"id": user_id} + if user_name: + user["name"] = user_name + key_params = [{"type": "public-key", "alg": ES256.ALGORITHM}] + return client_data_hash, rp, user, key_params + + +class DongleNFC(): + def __init__(self, debug=False): + self.waitImpl = self + self.opened = True + self.debug = debug + self.clf = nfc.ContactlessFrontend('usb') + self.tag = self.clf.connect(rdwr={'on-connect': lambda tag: False}) + + def exchange(self, apdu): + if self.debug: + print(f"[NFC] => {apdu.hex()}") + response = self.tag.transceive(apdu, 5.0) + if self.debug: + print(f"[NFC] <= {response.hex()}") + return response + + def parse_u2f_response(self, response): + return response[-2:].hex(), response[:-2] + + def send_u2f_apdu(self, apdu): + t1 = datetime.now() + response = self.exchange(apdu) + t2 = datetime.now() + if self.debug: + print((t2 - t1).microseconds // 1000, "ms", len(response), "bytes") + return self.parse_u2f_response(response) + + def craft_apdu(self, cla, ins, p1=0, p2=0, data=None, le=0, short_encoding=True): + apdu = struct.pack(">BBBB", cla, ins, p1, p2) + if short_encoding: + if data: + lc = len(data) + assert lc < 256 + apdu += struct.pack(">B", lc) + apdu += data + apdu += struct.pack(">B", le) + else: + apdu += struct.pack(">B", 0) + if data: + lc = len(data) + apdu += struct.pack(">H", lc) + apdu += data + apdu += struct.pack(">H", le) + return apdu + + def send_u2f_cmd(self, cla, ins, p1=0, p2=0, data=None, le=0, short_encoding=True): + apdu = self.craft_apdu(cla, ins, p1, p2, data, le, short_encoding) + + sw, rx = self.send_u2f_apdu(apdu) + response = rx + if short_encoding: + while sw.startswith("61"): + apdu = self.craft_apdu(cla, 0xC0) + sw, rx = self.send_u2f_apdu(apdu) + response += rx + + return sw, response + + def parse_fido2_response(self, sw, response): + assert sw == "9000" + assert response[0] == 0 + + response = response[1:] + decoded = cbor.decode(response) + return decoded + + def send_fido2_cbor(self, cmd, data=None, short_encoding=True): + request = struct.pack(">B", cmd) + if data is not None: + request += cbor.encode(data) + + if short_encoding: + while request: + more = False + if len(request) > 255: + more = True + cla = 0x90 + else: + cla = 0x80 + + lc = min(255, len(request)) + data = request[:lc] + + request = request[lc:] + sw, rx = self.send_u2f_cmd(cla=cla, ins=0x10, data=data, short_encoding=True) + + if more: + assert sw == "9000" + assert not rx + else: + break + else: + sw, rx = self.send_u2f_cmd(cla=0x0, ins=0x10, data=request, short_encoding=False) + + return self.parse_fido2_response(sw, rx) + + def close(self): + self.clf.close() + + +def test_u2f(short_encoding): + challenge_param = generate_random_bytes(32) + app_param = generate_random_bytes(32) + + dongle = DongleNFC(True) + + # APPLET_SELECT + sw, resp = dongle.send_u2f_cmd(cla=FIDO_CLA, ins=0xA4, p1=0x04, data=FIDO_AID, short_encoding=short_encoding) + assert sw == "9000" + assert resp.decode() == "U2F_V2" + + # U2F_VERSION + sw, resp = dongle.send_u2f_cmd(cla=FIDO_CLA, ins=Ctap1.INS.VERSION, short_encoding=short_encoding) + assert sw == "9000" + assert resp.decode() == "U2F_V2" + + # U2F_REGISTER + data = challenge_param + app_param + sw, resp = dongle.send_u2f_cmd(cla=FIDO_CLA, ins=Ctap1.INS.REGISTER, data=data, short_encoding=short_encoding) + assert sw == "9000" + registration_data = RegistrationData(resp) + registration_data.verify(app_param, challenge_param) + + # U2F_AUTHENTICATE + challenge_param = generate_random_bytes(32) + data = challenge_param + app_param + data += struct.pack(">B", len(registration_data.key_handle)) + data += registration_data.key_handle + sw, resp = dongle.send_u2f_cmd(cla=FIDO_CLA, ins=Ctap1.INS.AUTHENTICATE, p1=3, data=data, short_encoding=short_encoding) + assert sw == "9000" + auth_data = SignatureData(resp) + auth_data.verify(app_param, challenge_param, registration_data.public_key) + + dongle.close() + + +def test_fido2(short_encoding): + + dongle = DongleNFC(True) + + # APPLET_SELECT + sw, resp = dongle.send_u2f_cmd(cla=FIDO_CLA, ins=0xA4, p1=0x04, data=FIDO_AID, short_encoding=short_encoding) + assert sw == "9000" + assert resp.decode() == "U2F_V2" + + # U2F_VERSION + sw, resp = dongle.send_u2f_cmd(cla=FIDO_CLA, ins=Ctap1.INS.VERSION, short_encoding=short_encoding) + assert sw == "9000" + assert resp.decode() == "U2F_V2" + + # GET INFO + decoded = dongle.send_fido2_cbor(Ctap2.CMD.GET_INFO, short_encoding=short_encoding) + Info.from_dict(decoded) + + client_data_hash, rp, user, key_params = generate_make_credentials_params() + + # MAKE_CREDENTIAL + data = args(client_data_hash, + rp, + user, + key_params, + None, + None, + None, + None, + None, + None) + decoded = dongle.send_fido2_cbor(Ctap2.CMD.MAKE_CREDENTIAL, data, short_encoding=short_encoding) + attestation = AttestationResponse.from_dict(decoded) + + # GET_ASSERTION + credential_data = AttestedCredentialData(attestation.auth_data.credential_data) + client_data_hash = generate_random_bytes(32) + allow_list = [ + {"id": credential_data.credential_id, "type": "public-key"}, + {"id": credential_data.credential_id, "type": "public-key"} # to increase the cmd size so that is above 255bytes + ] + data = args(rp["id"], + client_data_hash, + allow_list, + None, + None, + None, + None) + decoded = dongle.send_fido2_cbor(Ctap2.CMD.GET_ASSERTION, data, short_encoding=short_encoding) + assertion = AssertionResponse.from_dict(decoded) + + assertion.verify(client_data_hash, credential_data.public_key) + + +#test_u2f(True) +#test_u2f(False) +#test_fido2(True) +test_fido2(False) diff --git a/tests/speculos/conftest.py b/tests/speculos/conftest.py index 06175ba0..e758544a 100644 --- a/tests/speculos/conftest.py +++ b/tests/speculos/conftest.py @@ -97,3 +97,20 @@ def client(firmware, backend, navigator, transport: str, ctap2_u2f_proxy): client = TestClient(firmware, backend, navigator, transport, ctap2_u2f_proxy) client.start() return client + + +@pytest.fixture(autouse=True) +def skip_by_endpoint(request, client): + print(client.use_U2F_endpoint) + if request.node.get_closest_marker('skip_endpoint'): + endpoint = request.node.get_closest_marker('skip_endpoint').args[0].lower() + print(endpoint) + if (client.use_U2F_endpoint and endpoint == "u2f") \ + or (not client.use_U2F_endpoint and endpoint == "hid"): + pytest.skip('skipped on this endpoint: {}'.format(endpoint)) + + +def pytest_configure(config): + config.addinivalue_line( + "markers", "skip_endpoint(endpoint): skip test depending on endpoint (either HID or U2F)" + ) diff --git a/tests/speculos/snapshots/flex/test_authenticate_ok/00000.png b/tests/speculos/snapshots/flex/test_authenticate_ok/00000.png new file mode 100644 index 00000000..06646d8b Binary files /dev/null and b/tests/speculos/snapshots/flex/test_authenticate_ok/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_authenticate_user_refused/00000.png b/tests/speculos/snapshots/flex/test_authenticate_user_refused/00000.png new file mode 100644 index 00000000..06646d8b Binary files /dev/null and b/tests/speculos/snapshots/flex/test_authenticate_user_refused/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_fido2_screens_short_id/get/00000.png b/tests/speculos/snapshots/flex/test_fido2_screens_short_id/get/00000.png new file mode 100644 index 00000000..afc85da9 Binary files /dev/null and b/tests/speculos/snapshots/flex/test_fido2_screens_short_id/get/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_fido2_screens_short_id/make/00000.png b/tests/speculos/snapshots/flex/test_fido2_screens_short_id/make/00000.png new file mode 100644 index 00000000..40bf5847 Binary files /dev/null and b/tests/speculos/snapshots/flex/test_fido2_screens_short_id/make/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_fido2_screens_user_display_name/get/00000.png b/tests/speculos/snapshots/flex/test_fido2_screens_user_display_name/get/00000.png new file mode 100644 index 00000000..ebf725c8 Binary files /dev/null and b/tests/speculos/snapshots/flex/test_fido2_screens_user_display_name/get/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_fido2_screens_user_display_name/make/00000.png b/tests/speculos/snapshots/flex/test_fido2_screens_user_display_name/make/00000.png new file mode 100644 index 00000000..e29e7963 Binary files /dev/null and b/tests/speculos/snapshots/flex/test_fido2_screens_user_display_name/make/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_fido2_screens_user_icon/get/00000.png b/tests/speculos/snapshots/flex/test_fido2_screens_user_icon/get/00000.png new file mode 100644 index 00000000..afc85da9 Binary files /dev/null and b/tests/speculos/snapshots/flex/test_fido2_screens_user_icon/get/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_fido2_screens_user_icon/make/00000.png b/tests/speculos/snapshots/flex/test_fido2_screens_user_icon/make/00000.png new file mode 100644 index 00000000..40bf5847 Binary files /dev/null and b/tests/speculos/snapshots/flex/test_fido2_screens_user_icon/make/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_fido2_screens_user_name/get/00000.png b/tests/speculos/snapshots/flex/test_fido2_screens_user_name/get/00000.png new file mode 100644 index 00000000..589e66cd Binary files /dev/null and b/tests/speculos/snapshots/flex/test_fido2_screens_user_name/get/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_fido2_screens_user_name/make/00000.png b/tests/speculos/snapshots/flex/test_fido2_screens_user_name/make/00000.png new file mode 100644 index 00000000..95cb7157 Binary files /dev/null and b/tests/speculos/snapshots/flex/test_fido2_screens_user_name/make/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_fido2_screens_user_name_and_display_name/get/00000.png b/tests/speculos/snapshots/flex/test_fido2_screens_user_name_and_display_name/get/00000.png new file mode 100644 index 00000000..ebf725c8 Binary files /dev/null and b/tests/speculos/snapshots/flex/test_fido2_screens_user_name_and_display_name/get/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_fido2_screens_user_name_and_display_name/make/00000.png b/tests/speculos/snapshots/flex/test_fido2_screens_user_name_and_display_name/make/00000.png new file mode 100644 index 00000000..e29e7963 Binary files /dev/null and b/tests/speculos/snapshots/flex/test_fido2_screens_user_name_and_display_name/make/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_fido_screens_settings/00000.png b/tests/speculos/snapshots/flex/test_fido_screens_settings/00000.png new file mode 100644 index 00000000..20faaf6d Binary files /dev/null and b/tests/speculos/snapshots/flex/test_fido_screens_settings/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_fido_screens_settings/00001.png b/tests/speculos/snapshots/flex/test_fido_screens_settings/00001.png new file mode 100644 index 00000000..b73069a3 Binary files /dev/null and b/tests/speculos/snapshots/flex/test_fido_screens_settings/00001.png differ diff --git a/tests/speculos/snapshots/flex/test_fido_screens_settings/00002.png b/tests/speculos/snapshots/flex/test_fido_screens_settings/00002.png new file mode 100644 index 00000000..7c76800c Binary files /dev/null and b/tests/speculos/snapshots/flex/test_fido_screens_settings/00002.png differ diff --git a/tests/speculos/snapshots/flex/test_fido_screens_settings/00003.png b/tests/speculos/snapshots/flex/test_fido_screens_settings/00003.png new file mode 100644 index 00000000..3ffc6493 Binary files /dev/null and b/tests/speculos/snapshots/flex/test_fido_screens_settings/00003.png differ diff --git a/tests/speculos/snapshots/flex/test_fido_screens_settings/00004.png b/tests/speculos/snapshots/flex/test_fido_screens_settings/00004.png new file mode 100644 index 00000000..ba9e3ec5 Binary files /dev/null and b/tests/speculos/snapshots/flex/test_fido_screens_settings/00004.png differ diff --git a/tests/speculos/snapshots/flex/test_fido_screens_settings/00005.png b/tests/speculos/snapshots/flex/test_fido_screens_settings/00005.png new file mode 100644 index 00000000..0ec0b5ca Binary files /dev/null and b/tests/speculos/snapshots/flex/test_fido_screens_settings/00005.png differ diff --git a/tests/speculos/snapshots/flex/test_fido_screens_settings/00006.png b/tests/speculos/snapshots/flex/test_fido_screens_settings/00006.png new file mode 100644 index 00000000..7c76800c Binary files /dev/null and b/tests/speculos/snapshots/flex/test_fido_screens_settings/00006.png differ diff --git a/tests/speculos/snapshots/flex/test_fido_screens_settings/00007.png b/tests/speculos/snapshots/flex/test_fido_screens_settings/00007.png new file mode 100644 index 00000000..20faaf6d Binary files /dev/null and b/tests/speculos/snapshots/flex/test_fido_screens_settings/00007.png differ diff --git a/tests/speculos/snapshots/flex/test_get_assertion/00000.png b/tests/speculos/snapshots/flex/test_get_assertion/00000.png new file mode 100644 index 00000000..00f8e3fd Binary files /dev/null and b/tests/speculos/snapshots/flex/test_get_assertion/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_get_assertion_allow_list/00000.png b/tests/speculos/snapshots/flex/test_get_assertion_allow_list/00000.png new file mode 100644 index 00000000..2af7ffe3 Binary files /dev/null and b/tests/speculos/snapshots/flex/test_get_assertion_allow_list/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_get_assertion_allow_list/00001.png b/tests/speculos/snapshots/flex/test_get_assertion_allow_list/00001.png new file mode 100644 index 00000000..25d3802b Binary files /dev/null and b/tests/speculos/snapshots/flex/test_get_assertion_allow_list/00001.png differ diff --git a/tests/speculos/snapshots/flex/test_get_assertion_allow_list/00002.png b/tests/speculos/snapshots/flex/test_get_assertion_allow_list/00002.png new file mode 100644 index 00000000..61f017bb Binary files /dev/null and b/tests/speculos/snapshots/flex/test_get_assertion_allow_list/00002.png differ diff --git a/tests/speculos/snapshots/flex/test_get_assertion_duplicate_allow_list_entries/00000.png b/tests/speculos/snapshots/flex/test_get_assertion_duplicate_allow_list_entries/00000.png new file mode 100644 index 00000000..00f8e3fd Binary files /dev/null and b/tests/speculos/snapshots/flex/test_get_assertion_duplicate_allow_list_entries/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_get_assertion_no_credentials/00000.png b/tests/speculos/snapshots/flex/test_get_assertion_no_credentials/00000.png new file mode 100644 index 00000000..b53584e7 Binary files /dev/null and b/tests/speculos/snapshots/flex/test_get_assertion_no_credentials/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_get_assertion_user_refused/00000.png b/tests/speculos/snapshots/flex/test_get_assertion_user_refused/00000.png new file mode 100644 index 00000000..00f8e3fd Binary files /dev/null and b/tests/speculos/snapshots/flex/test_get_assertion_user_refused/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_get_assertion_uv/00000.png b/tests/speculos/snapshots/flex/test_get_assertion_uv/00000.png new file mode 100644 index 00000000..00f8e3fd Binary files /dev/null and b/tests/speculos/snapshots/flex/test_get_assertion_uv/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_get_assertion_wrong_id/00000.png b/tests/speculos/snapshots/flex/test_get_assertion_wrong_id/00000.png new file mode 100644 index 00000000..b53584e7 Binary files /dev/null and b/tests/speculos/snapshots/flex/test_get_assertion_wrong_id/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_get_assertion_wrong_rp/00000.png b/tests/speculos/snapshots/flex/test_get_assertion_wrong_rp/00000.png new file mode 100644 index 00000000..ee4979c6 Binary files /dev/null and b/tests/speculos/snapshots/flex/test_get_assertion_wrong_rp/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_make_credential/00000.png b/tests/speculos/snapshots/flex/test_make_credential/00000.png new file mode 100644 index 00000000..0e8a98cb Binary files /dev/null and b/tests/speculos/snapshots/flex/test_make_credential/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_make_credential_certificate/00000.png b/tests/speculos/snapshots/flex/test_make_credential_certificate/00000.png new file mode 100644 index 00000000..0e8a98cb Binary files /dev/null and b/tests/speculos/snapshots/flex/test_make_credential_certificate/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_make_credential_exclude_list/00000.png b/tests/speculos/snapshots/flex/test_make_credential_exclude_list/00000.png new file mode 100644 index 00000000..0e8a98cb Binary files /dev/null and b/tests/speculos/snapshots/flex/test_make_credential_exclude_list/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_make_credential_up/00000.png b/tests/speculos/snapshots/flex/test_make_credential_up/00000.png new file mode 100644 index 00000000..0e8a98cb Binary files /dev/null and b/tests/speculos/snapshots/flex/test_make_credential_up/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_make_credential_user_refused/00000.png b/tests/speculos/snapshots/flex/test_make_credential_user_refused/00000.png new file mode 100644 index 00000000..0e8a98cb Binary files /dev/null and b/tests/speculos/snapshots/flex/test_make_credential_user_refused/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_make_credential_uv/00000.png b/tests/speculos/snapshots/flex/test_make_credential_uv/00000.png new file mode 100644 index 00000000..0e8a98cb Binary files /dev/null and b/tests/speculos/snapshots/flex/test_make_credential_uv/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_option_rk_get_assertion/0/get_allow_list/00000.png b/tests/speculos/snapshots/flex/test_option_rk_get_assertion/0/get_allow_list/00000.png new file mode 100644 index 00000000..9deab0d7 Binary files /dev/null and b/tests/speculos/snapshots/flex/test_option_rk_get_assertion/0/get_allow_list/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_option_rk_get_assertion/0/get_rk/00000.png b/tests/speculos/snapshots/flex/test_option_rk_get_assertion/0/get_rk/00000.png new file mode 100644 index 00000000..9deab0d7 Binary files /dev/null and b/tests/speculos/snapshots/flex/test_option_rk_get_assertion/0/get_rk/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_option_rk_get_assertion/0/make/00000.png b/tests/speculos/snapshots/flex/test_option_rk_get_assertion/0/make/00000.png new file mode 100644 index 00000000..a62f59c2 Binary files /dev/null and b/tests/speculos/snapshots/flex/test_option_rk_get_assertion/0/make/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_option_rk_get_assertion/1/get_allow_list/00000.png b/tests/speculos/snapshots/flex/test_option_rk_get_assertion/1/get_allow_list/00000.png new file mode 100644 index 00000000..cdbf81be Binary files /dev/null and b/tests/speculos/snapshots/flex/test_option_rk_get_assertion/1/get_allow_list/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_option_rk_get_assertion/1/get_rk/00000.png b/tests/speculos/snapshots/flex/test_option_rk_get_assertion/1/get_rk/00000.png new file mode 100644 index 00000000..cdbf81be Binary files /dev/null and b/tests/speculos/snapshots/flex/test_option_rk_get_assertion/1/get_rk/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_option_rk_get_assertion/1/make/00000.png b/tests/speculos/snapshots/flex/test_option_rk_get_assertion/1/make/00000.png new file mode 100644 index 00000000..df3f1884 Binary files /dev/null and b/tests/speculos/snapshots/flex/test_option_rk_get_assertion/1/make/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_option_rk_get_assertion/2/get_allow_list/00000.png b/tests/speculos/snapshots/flex/test_option_rk_get_assertion/2/get_allow_list/00000.png new file mode 100644 index 00000000..e833825c Binary files /dev/null and b/tests/speculos/snapshots/flex/test_option_rk_get_assertion/2/get_allow_list/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_option_rk_get_assertion/2/get_rk/00000.png b/tests/speculos/snapshots/flex/test_option_rk_get_assertion/2/get_rk/00000.png new file mode 100644 index 00000000..e833825c Binary files /dev/null and b/tests/speculos/snapshots/flex/test_option_rk_get_assertion/2/get_rk/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_option_rk_get_assertion/2/make/00000.png b/tests/speculos/snapshots/flex/test_option_rk_get_assertion/2/make/00000.png new file mode 100644 index 00000000..a50108ee Binary files /dev/null and b/tests/speculos/snapshots/flex/test_option_rk_get_assertion/2/make/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_option_rk_make_cred_exclude_refused/00000.png b/tests/speculos/snapshots/flex/test_option_rk_make_cred_exclude_refused/00000.png new file mode 100644 index 00000000..0e8a98cb Binary files /dev/null and b/tests/speculos/snapshots/flex/test_option_rk_make_cred_exclude_refused/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_option_rk_overwrite_get_assertion/1/get_assertion/00000.png b/tests/speculos/snapshots/flex/test_option_rk_overwrite_get_assertion/1/get_assertion/00000.png new file mode 100644 index 00000000..5a325957 Binary files /dev/null and b/tests/speculos/snapshots/flex/test_option_rk_overwrite_get_assertion/1/get_assertion/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_option_rk_overwrite_get_assertion/1/make/00000.png b/tests/speculos/snapshots/flex/test_option_rk_overwrite_get_assertion/1/make/00000.png new file mode 100644 index 00000000..67741dce Binary files /dev/null and b/tests/speculos/snapshots/flex/test_option_rk_overwrite_get_assertion/1/make/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_option_rk_overwrite_get_assertion/2/get_assertion/00000.png b/tests/speculos/snapshots/flex/test_option_rk_overwrite_get_assertion/2/get_assertion/00000.png new file mode 100644 index 00000000..692b2e1c Binary files /dev/null and b/tests/speculos/snapshots/flex/test_option_rk_overwrite_get_assertion/2/get_assertion/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_option_rk_overwrite_get_assertion/2/make/00000.png b/tests/speculos/snapshots/flex/test_option_rk_overwrite_get_assertion/2/make/00000.png new file mode 100644 index 00000000..48c2c54f Binary files /dev/null and b/tests/speculos/snapshots/flex/test_option_rk_overwrite_get_assertion/2/make/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_register_ok/00000.png b/tests/speculos/snapshots/flex/test_register_ok/00000.png new file mode 100644 index 00000000..e15761be Binary files /dev/null and b/tests/speculos/snapshots/flex/test_register_ok/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_register_user_refused/00000.png b/tests/speculos/snapshots/flex/test_register_user_refused/00000.png new file mode 100644 index 00000000..e15761be Binary files /dev/null and b/tests/speculos/snapshots/flex/test_register_user_refused/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_reset/False/00000.png b/tests/speculos/snapshots/flex/test_reset/False/00000.png new file mode 100644 index 00000000..5894db80 Binary files /dev/null and b/tests/speculos/snapshots/flex/test_reset/False/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_reset/True/00000.png b/tests/speculos/snapshots/flex/test_reset/True/00000.png new file mode 100644 index 00000000..5894db80 Binary files /dev/null and b/tests/speculos/snapshots/flex/test_reset/True/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/0_Binance/00000.png b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/0_Binance/00000.png new file mode 100644 index 00000000..4d717e32 Binary files /dev/null and b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/0_Binance/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/10_Fedora/00000.png b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/10_Fedora/00000.png new file mode 100644 index 00000000..c0527292 Binary files /dev/null and b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/10_Fedora/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/11_Gandi/00000.png b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/11_Gandi/00000.png new file mode 100644 index 00000000..ea982585 Binary files /dev/null and b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/11_Gandi/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/12_GitHub/00000.png b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/12_GitHub/00000.png new file mode 100644 index 00000000..9e15a7df Binary files /dev/null and b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/12_GitHub/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/13_GitLab/00000.png b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/13_GitLab/00000.png new file mode 100644 index 00000000..c02676e0 Binary files /dev/null and b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/13_GitLab/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/14_Google/00000.png b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/14_Google/00000.png new file mode 100644 index 00000000..d6896a1a Binary files /dev/null and b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/14_Google/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/15_Keeper/00000.png b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/15_Keeper/00000.png new file mode 100644 index 00000000..0d7c38cd Binary files /dev/null and b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/15_Keeper/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/16_LastPass/00000.png b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/16_LastPass/00000.png new file mode 100644 index 00000000..4b3135d2 Binary files /dev/null and b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/16_LastPass/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/17_Slush Pool/00000.png b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/17_Slush Pool/00000.png new file mode 100644 index 00000000..a1700204 Binary files /dev/null and b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/17_Slush Pool/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/18_Stripe/00000.png b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/18_Stripe/00000.png new file mode 100644 index 00000000..248137d2 Binary files /dev/null and b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/18_Stripe/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/19_u2f.bin.coffee/00000.png b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/19_u2f.bin.coffee/00000.png new file mode 100644 index 00000000..735dfa84 Binary files /dev/null and b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/19_u2f.bin.coffee/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/1_Bitbucket/00000.png b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/1_Bitbucket/00000.png new file mode 100644 index 00000000..a4b9bbbe Binary files /dev/null and b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/1_Bitbucket/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/20_webauthn.bin.coffee/00000.png b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/20_webauthn.bin.coffee/00000.png new file mode 100644 index 00000000..992b7593 Binary files /dev/null and b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/20_webauthn.bin.coffee/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/21_WebAuthn.io/00000.png b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/21_WebAuthn.io/00000.png new file mode 100644 index 00000000..951fae72 Binary files /dev/null and b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/21_WebAuthn.io/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/22_WebAuthn.me/00000.png b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/22_WebAuthn.me/00000.png new file mode 100644 index 00000000..24c1f16d Binary files /dev/null and b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/22_WebAuthn.me/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/23_demo.yubico.com/00000.png b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/23_demo.yubico.com/00000.png new file mode 100644 index 00000000..ad7c6ec0 Binary files /dev/null and b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/23_demo.yubico.com/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/2_Bitfinex/00000.png b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/2_Bitfinex/00000.png new file mode 100644 index 00000000..f97c0f5b Binary files /dev/null and b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/2_Bitfinex/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/3_Bitwarden/00000.png b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/3_Bitwarden/00000.png new file mode 100644 index 00000000..39c0776c Binary files /dev/null and b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/3_Bitwarden/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/4_Coinbase/00000.png b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/4_Coinbase/00000.png new file mode 100644 index 00000000..3ee9f828 Binary files /dev/null and b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/4_Coinbase/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/5_Dashlane/00000.png b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/5_Dashlane/00000.png new file mode 100644 index 00000000..c6d0c955 Binary files /dev/null and b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/5_Dashlane/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/6_Dropbox/00000.png b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/6_Dropbox/00000.png new file mode 100644 index 00000000..bc4d4621 Binary files /dev/null and b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/6_Dropbox/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/7_Dropbox/00000.png b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/7_Dropbox/00000.png new file mode 100644 index 00000000..94af81f1 Binary files /dev/null and b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/7_Dropbox/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/8_Duo/00000.png b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/8_Duo/00000.png new file mode 100644 index 00000000..786076ea Binary files /dev/null and b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/8_Duo/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/9_FastMail/00000.png b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/9_FastMail/00000.png new file mode 100644 index 00000000..203bb628 Binary files /dev/null and b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/log/9_FastMail/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/0_Binance/00000.png b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/0_Binance/00000.png new file mode 100644 index 00000000..8a6aaa9f Binary files /dev/null and b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/0_Binance/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/10_Fedora/00000.png b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/10_Fedora/00000.png new file mode 100644 index 00000000..6a0d9e64 Binary files /dev/null and b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/10_Fedora/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/11_Gandi/00000.png b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/11_Gandi/00000.png new file mode 100644 index 00000000..fa9f53ef Binary files /dev/null and b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/11_Gandi/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/12_GitHub/00000.png b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/12_GitHub/00000.png new file mode 100644 index 00000000..983379a2 Binary files /dev/null and b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/12_GitHub/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/13_GitLab/00000.png b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/13_GitLab/00000.png new file mode 100644 index 00000000..700d70f3 Binary files /dev/null and b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/13_GitLab/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/14_Google/00000.png b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/14_Google/00000.png new file mode 100644 index 00000000..0dce4042 Binary files /dev/null and b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/14_Google/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/15_Keeper/00000.png b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/15_Keeper/00000.png new file mode 100644 index 00000000..1ae8c234 Binary files /dev/null and b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/15_Keeper/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/16_LastPass/00000.png b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/16_LastPass/00000.png new file mode 100644 index 00000000..fc727feb Binary files /dev/null and b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/16_LastPass/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/17_Slush Pool/00000.png b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/17_Slush Pool/00000.png new file mode 100644 index 00000000..3d2e456e Binary files /dev/null and b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/17_Slush Pool/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/18_Stripe/00000.png b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/18_Stripe/00000.png new file mode 100644 index 00000000..c48cea1d Binary files /dev/null and b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/18_Stripe/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/19_u2f.bin.coffee/00000.png b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/19_u2f.bin.coffee/00000.png new file mode 100644 index 00000000..a935bbb2 Binary files /dev/null and b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/19_u2f.bin.coffee/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/1_Bitbucket/00000.png b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/1_Bitbucket/00000.png new file mode 100644 index 00000000..a3e68959 Binary files /dev/null and b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/1_Bitbucket/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/20_webauthn.bin.coffee/00000.png b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/20_webauthn.bin.coffee/00000.png new file mode 100644 index 00000000..2fde0163 Binary files /dev/null and b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/20_webauthn.bin.coffee/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/21_WebAuthn.io/00000.png b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/21_WebAuthn.io/00000.png new file mode 100644 index 00000000..f48057db Binary files /dev/null and b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/21_WebAuthn.io/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/22_WebAuthn.me/00000.png b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/22_WebAuthn.me/00000.png new file mode 100644 index 00000000..97c1ad53 Binary files /dev/null and b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/22_WebAuthn.me/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/23_demo.yubico.com/00000.png b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/23_demo.yubico.com/00000.png new file mode 100644 index 00000000..7eece2bb Binary files /dev/null and b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/23_demo.yubico.com/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/2_Bitfinex/00000.png b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/2_Bitfinex/00000.png new file mode 100644 index 00000000..0d1641ac Binary files /dev/null and b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/2_Bitfinex/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/3_Bitwarden/00000.png b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/3_Bitwarden/00000.png new file mode 100644 index 00000000..b72f96e7 Binary files /dev/null and b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/3_Bitwarden/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/4_Coinbase/00000.png b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/4_Coinbase/00000.png new file mode 100644 index 00000000..64eb3457 Binary files /dev/null and b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/4_Coinbase/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/5_Dashlane/00000.png b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/5_Dashlane/00000.png new file mode 100644 index 00000000..9ede5708 Binary files /dev/null and b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/5_Dashlane/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/6_Dropbox/00000.png b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/6_Dropbox/00000.png new file mode 100644 index 00000000..fd002216 Binary files /dev/null and b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/6_Dropbox/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/7_Dropbox/00000.png b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/7_Dropbox/00000.png new file mode 100644 index 00000000..c088fcbb Binary files /dev/null and b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/7_Dropbox/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/8_Duo/00000.png b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/8_Duo/00000.png new file mode 100644 index 00000000..c48f1c62 Binary files /dev/null and b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/8_Duo/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/9_FastMail/00000.png b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/9_FastMail/00000.png new file mode 100644 index 00000000..a149e82f Binary files /dev/null and b/tests/speculos/snapshots/flex/test_u2f_screens_fido_known_list/reg/9_FastMail/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_u2f_screens_idle/00000.png b/tests/speculos/snapshots/flex/test_u2f_screens_idle/00000.png new file mode 100644 index 00000000..20faaf6d Binary files /dev/null and b/tests/speculos/snapshots/flex/test_u2f_screens_idle/00000.png differ diff --git a/tests/speculos/snapshots/flex/test_u2f_screens_idle/00001.png b/tests/speculos/snapshots/flex/test_u2f_screens_idle/00001.png new file mode 100644 index 00000000..b73069a3 Binary files /dev/null and b/tests/speculos/snapshots/flex/test_u2f_screens_idle/00001.png differ diff --git a/tests/speculos/snapshots/flex/test_u2f_screens_idle/00002.png b/tests/speculos/snapshots/flex/test_u2f_screens_idle/00002.png new file mode 100644 index 00000000..7c76800c Binary files /dev/null and b/tests/speculos/snapshots/flex/test_u2f_screens_idle/00002.png differ diff --git a/tests/speculos/snapshots/flex/test_u2f_screens_idle/00003.png b/tests/speculos/snapshots/flex/test_u2f_screens_idle/00003.png new file mode 100644 index 00000000..20faaf6d Binary files /dev/null and b/tests/speculos/snapshots/flex/test_u2f_screens_idle/00003.png differ diff --git a/tests/speculos/snapshots/stax/test_fido_screens_settings/00000.png b/tests/speculos/snapshots/stax/test_fido_screens_settings/00000.png index 3b0c2b11..efc4f221 100644 Binary files a/tests/speculos/snapshots/stax/test_fido_screens_settings/00000.png and b/tests/speculos/snapshots/stax/test_fido_screens_settings/00000.png differ diff --git a/tests/speculos/snapshots/stax/test_fido_screens_settings/00007.png b/tests/speculos/snapshots/stax/test_fido_screens_settings/00007.png index 3b0c2b11..efc4f221 100644 Binary files a/tests/speculos/snapshots/stax/test_fido_screens_settings/00007.png and b/tests/speculos/snapshots/stax/test_fido_screens_settings/00007.png differ diff --git a/tests/speculos/snapshots/stax/test_u2f_screens_idle/00000.png b/tests/speculos/snapshots/stax/test_u2f_screens_idle/00000.png index 3b0c2b11..efc4f221 100644 Binary files a/tests/speculos/snapshots/stax/test_u2f_screens_idle/00000.png and b/tests/speculos/snapshots/stax/test_u2f_screens_idle/00000.png differ diff --git a/tests/speculos/snapshots/stax/test_u2f_screens_idle/00003.png b/tests/speculos/snapshots/stax/test_u2f_screens_idle/00003.png index 3b0c2b11..efc4f221 100644 Binary files a/tests/speculos/snapshots/stax/test_u2f_screens_idle/00003.png and b/tests/speculos/snapshots/stax/test_u2f_screens_idle/00003.png differ diff --git a/tests/speculos/u2f/test_cmd.py b/tests/speculos/u2f/test_cmd.py index 84f7735b..9dbf158a 100644 --- a/tests/speculos/u2f/test_cmd.py +++ b/tests/speculos/u2f/test_cmd.py @@ -21,8 +21,9 @@ def test_cmd_wrong_cla(client): def test_cmd_wrong_ins(client): for ins in range(0xff + 1): - # Only supported INS are [0x01, 0x02, 0x03, 0x10] - if ins in [0x01, 0x02, 0x03, 0x10]: + # Only supported INS are [0x01, 0x02, 0x03, 0x10, 0xa4] + # source: src/u2f_processing.c + if ins in [0x01, 0x02, 0x03, 0x10, 0xa4]: continue with pytest.raises(ApduError) as e: @@ -71,7 +72,7 @@ def test_cmd_length(client): assert e.value.code == APDU.SW_WRONG_LENGTH -def test_cmd_no_data_encoding(client): +def test_cmd_no_data_extended_encoding(client): cla = 0x00 ins = Ctap1.INS.VERSION p1 = 0x00 @@ -81,11 +82,11 @@ def test_cmd_no_data_encoding(client): # Extended encoding, explicit Lc and Le apdu = struct.pack(">BBBBBHH", cla, ins, p1, p2, 0, nc, ne) - client.ctap1.send_raw_apdu(apdu) + result = client.ctap1.send_raw_apdu(apdu) # Extended encoding, explicit Lc and no Le apdu = struct.pack(">BBBBBH", cla, ins, p1, p2, 0, nc) - client.ctap1.send_raw_apdu(apdu) + assert result == client.ctap1.send_raw_apdu(apdu) # Test errors @@ -95,23 +96,49 @@ def test_cmd_no_data_encoding(client): client.ctap1.send_raw_apdu(apdu) assert e.value.code == APDU.SW_WRONG_LENGTH + +@pytest.mark.skip_endpoint("u2f") +def test_cmd_no_data_extended_encoding_hid_only(client): + cla = 0x00 + ins = Ctap1.INS.VERSION + p1 = 0x00 + p2 = 0x00 + ne = 0xaabb # Can be quite anything + + # Extended encoding, no Lc and explicit Le + apdu = struct.pack(">BBBBBH", cla, ins, p1, p2, 0, ne) + result = client.ctap1.send_raw_apdu(apdu) + + # Extended encoding, no Lc and no Le + apdu = struct.pack(">BBBB", cla, ins, p1, p2) + assert result == client.ctap1.send_raw_apdu(apdu) + + +@pytest.mark.skip_endpoint("hid") +def test_cmd_no_data_short_encoding_u2f_only(client): + cla = 0x00 + ins = Ctap1.INS.VERSION + p1 = 0x00 + p2 = 0x00 + # Short encoding (not supported), Lc and Le apdu = struct.pack(">BBBBBB", cla, ins, p1, p2, 0, 0xaa) with pytest.raises(ApduError) as e: client.ctap1.send_raw_apdu(apdu) assert e.value.code == APDU.SW_WRONG_LENGTH - # Next tests only work over raw§HID until all sdk u2f_impl.c are updated - if client.use_U2F_endpoint: - pytest.skip("Does not work with this transport until SDK patch") - # Extended encoding, no Lc and explicit Le - apdu = struct.pack(">BBBBBH", cla, ins, p1, p2, 0, ne) - client.ctap1.send_raw_apdu(apdu) +@pytest.mark.skip_endpoint("u2f") +def test_cmd_no_data_short_encoding_hid_only(client): + cla = 0x00 + ins = Ctap1.INS.VERSION + p1 = 0x00 + p2 = 0x00 + nc = 0 - # Extended encoding, no Lc and no Le - apdu = struct.pack(">BBBB", cla, ins, p1, p2) - client.ctap1.send_raw_apdu(apdu) + # Short encoding Lc and Le + apdu = struct.pack(">BBBBBB", cla, ins, p1, p2, 0, 0xaa) + result = client.ctap1.send_raw_apdu(apdu) # Short encoding, Lc and no Le # This should not be supported as spec requires that messages over HID @@ -120,4 +147,4 @@ def test_cmd_no_data_encoding(client): # However, it is not respected on v1.7.0 even after an issue was raised: # https://github.com/fido-alliance/conformance-test-tools-resources/issues/614 apdu = struct.pack(">BBBBB", cla, ins, p1, p2, nc) - client.ctap1.send_raw_apdu(apdu) + assert result == client.ctap1.send_raw_apdu(apdu)