/*! \fn getYesNoAnswerInput(uint8_t blocking) * \brief Use the input interface to get user input * \param blocking Boolean to know if we should wait for input or timeout * \note In case of a non blocking call, caller must call activityDetectedRoutine() & miniWheelClearDetections() * \return see mini_input_yes_no_ret_t */ RET_TYPE getYesNoAnswerInput(uint8_t blocking) { #if defined(ALWAYS_ACCEPT_REQUESTS) return MINI_INPUT_RET_YES; #endif uint8_t incomingData[RAWHID_TX_SIZE]; RET_TYPE detect_result; if (blocking == TRUE) { // Switch on lights activityDetectedRoutine(); // Clear possible remaining detection miniWheelClearDetections(); } // Wait for a touch press do { // User interaction timeout or smartcard removed if ((hasTimerExpired(TIMER_USERINT, TRUE) == TIMER_EXPIRED) || (isSmartCardAbsent() == RETURN_OK)) { return MINI_INPUT_RET_TIMEOUT; } // Read usb comms as the plugin could ask to cancel the request if ((getMooltipassParameterInEeprom(USER_REQ_CANCEL_PARAM) != FALSE) && (usbRawHidRecv(incomingData) == RETURN_COM_TRANSF_OK)) { if (incomingData[HID_TYPE_FIELD] == CMD_CANCEL_REQUEST) { // Request canceled return MINI_INPUT_RET_TIMEOUT; } else { // Another packet (that shouldn't be sent!), ask to retry later... usbSendMessage(CMD_PLEASE_RETRY, 0, incomingData); } } // Check if something has been pressed detect_result = miniGetWheelAction(FALSE, TRUE); if (detect_result == WHEEL_ACTION_SHORT_CLICK) { return MINI_INPUT_RET_YES; } } while(blocking != FALSE); // Return MINI_INPUT_RET_NONE if nothing was pressed and no timeout occurred return MINI_INPUT_RET_NONE; }
/*! \fn guiAskForConfirmation(const char* string) * \brief Ask for user confirmation for different things * \param nb_args Number of text lines (must be either 1 2 or 3/4 (depending on the MP version)) * \param text_object Pointer to the text object if more than 1 line, pointer to the string if not * \return User confirmation or not */ RET_TYPE guiAskForConfirmation(uint8_t nb_args, confirmationText_t* text_object) { uint8_t flash_flag_set = FALSE; uint8_t flash_flag = FALSE; uint8_t flash_sm = 0; // LED animation #ifdef LEDS_ENABLED_MINI miniLedsSetAnimation(ANIM_PULSE_UP_RAMP_DOWN); #endif // Check if we want to flash the screen if ((nb_args & 0xF0) != 0) { flash_flag_set = TRUE; nb_args = nb_args & 0x0F; // Check that the user didn't disable it if (getMooltipassParameterInEeprom(FLASH_SCREEN_PARAM) != FALSE) { flash_flag = TRUE; } } // Variables for scrolling uint8_t string_y_indexes[3]; uint8_t string_extra_chars[3]; uint8_t string_offset_cntrs[3] = {0,0,0}; // Display variables uint8_t approve_selected = TRUE; // Draw asking bitmap miniOledClearFrameBuffer(); miniOledSetMaxTextY(SSD1305_OLED_WIDTH-15); miniOledBitmapDrawFlash(SSD1305_OLED_WIDTH-15, 0, BITMAP_APPROVE, 0); // Display lines. // Note: line are truncated at the miniOled driver level when miniOledTextWritingYIncrement is set to FALSE (default) if (nb_args == 1) { miniOledPutCenteredString(THREE_LINE_TEXT_SECOND_POS, (char*)text_object); } else if (nb_args == 2) { string_y_indexes[0] = TWO_LINE_TEXT_FIRST_POS; string_y_indexes[1] = TWO_LINE_TEXT_SECOND_POS; } else { string_y_indexes[0] = THREE_LINE_TEXT_FIRST_POS; string_y_indexes[1] = THREE_LINE_TEXT_SECOND_POS; string_y_indexes[2] = THREE_LINE_TEXT_THIRD_POS; } // For loop to display lines when there is more than one arg if (nb_args > 1) { for (uint8_t i = 0; i < nb_args; i++) { string_extra_chars[i] = strlen(text_object->lines[i]) - miniOledPutCenteredString(string_y_indexes[i], text_object->lines[i]); } } miniOledFlushEntireBufferToDisplay(); miniOledResetMaxTextY(); // Wait for user input RET_TYPE input_answer = MINI_INPUT_RET_NONE; RET_TYPE detect_result; // Switch on lights activityDetectedRoutine(); // Clear possible remaining detection miniWheelClearDetections(); // Arm timer for scrolling (caps timer that isn't relevant here) activateTimer(TIMER_CAPS, SCROLLING_DEL); // Arm timer for flashing activateTimer(TIMER_FLASHING, 500); // Loop while no timeout occurs or no button is pressed while (input_answer == MINI_INPUT_RET_NONE) { // User interaction timeout or smartcard removed if ((hasTimerExpired(TIMER_USERINT, TRUE) == TIMER_EXPIRED) || (isSmartCardAbsent() == RETURN_OK)) { input_answer = MINI_INPUT_RET_TIMEOUT; } // Read usb comms as the plugin could ask to cancel the request if (usbCancelRequestReceived() == RETURN_OK) { input_answer = MINI_INPUT_RET_TIMEOUT; } // Screen flashing logic if ((hasTimerExpired(TIMER_FLASHING, TRUE) == TIMER_EXPIRED) && (flash_flag == TRUE) && (flash_sm < 4)) { // Look at the flash_sm LSb to know what is the display state if ((flash_sm++ & 0x01) != 0x00) { miniOledNormalDisplay(); } else { miniOledInvertedDisplay(); } // Re-arm timer activateTimer(TIMER_FLASHING, 500); } // Check if something has been pressed detect_result = miniGetWheelAction(FALSE, TRUE); if (detect_result == WHEEL_ACTION_SHORT_CLICK) { input_answer = MINI_INPUT_RET_YES; } else if (detect_result == WHEEL_ACTION_LONG_CLICK) { input_answer = MINI_INPUT_RET_BACK; } // Knock to approve #if defined(HARDWARE_MINI_CLICK_V2) if ((scanAndGetDoubleZTap(FALSE) == ACC_RET_KNOCK) && (flash_flag_set != FALSE)) { input_answer = MINI_INPUT_RET_YES; } #else (void)flash_flag_set; #endif // Text scrolling if ((hasTimerExpired(TIMER_CAPS, FALSE) == TIMER_EXPIRED) && (nb_args > 1)) { miniOledClearFrameBuffer(); activateTimer(TIMER_CAPS, SCROLLING_DEL); miniOledSetMaxTextY(SSD1305_OLED_WIDTH-15); if(approve_selected == FALSE) { miniOledBitmapDrawFlash(SSD1305_OLED_WIDTH-15, 0, BITMAP_DENY, 0); } else { miniOledBitmapDrawFlash(SSD1305_OLED_WIDTH-15, 0, BITMAP_APPROVE, 0); } for (uint8_t i = 0; i < nb_args; i++) { if (string_extra_chars[i] > 0) { miniOledPutCenteredString(string_y_indexes[i], (text_object->lines[i]) + string_offset_cntrs[i]); if (string_offset_cntrs[i]++ == string_extra_chars[i]) { string_offset_cntrs[i] = 0; } } else { miniOledPutCenteredString(string_y_indexes[i], text_object->lines[i]); } } miniOledFlushEntireBufferToDisplay(); miniOledResetMaxTextY(); } // Approve / deny display change if (getWheelCurrentIncrement() != 0) { if(approve_selected == FALSE) { miniOledBitmapDrawFlash(SSD1305_OLED_WIDTH-15, 0, BITMAP_APPROVE, 0); } else { miniOledBitmapDrawFlash(SSD1305_OLED_WIDTH-15, 0, BITMAP_DENY, 0); } approve_selected = !approve_selected; miniOledFlushEntireBufferToDisplay(); } } // In case display was inverted, set it normally miniOledNormalDisplay(); if ((input_answer == MINI_INPUT_RET_YES) && (approve_selected != FALSE)) { // LED animation #ifdef LEDS_ENABLED_MINI miniLedsSetAnimation(ANIM_NONE); #endif return RETURN_OK; } else if (input_answer == MINI_INPUT_RET_BACK) { // LED animation #ifdef LEDS_ENABLED_MINI miniLedsSetAnimation(ANIM_NONE); #endif return RETURN_BACK; } else { // LED animation #ifdef LEDS_ENABLED_MINI miniLedsSetAnimation(ANIM_NONE); #endif return RETURN_NOK; } }
/*! \fn guiScreenLoop(uint8_t input_interface_result) * \brief Function called to handle screen changes * \param input_interface_result Touch detection result */ void guiScreenLoop(uint8_t input_interface_result) { // If no press, you can return! if ((input_interface_result == WHEEL_ACTION_NONE) || (currentScreen == SCREEN_DEFAULT_INSERTED_INVALID) || (currentScreen == SCREEN_DEFAULT_INSERTED_UNKNOWN)) { return; } if (currentScreen == SCREEN_DEFAULT_NINSERTED) { // No smart card inserted, ask the user to insert one guiDisplayInsertSmartCardScreenAndWait(); } else if (currentScreen == SCREEN_MEMORY_MGMT) { // Currently in memory management mode, tell the user to finish it via the plugin/app guiDisplayInformationOnScreenAndWait(ID_STRING_CLOSEMEMMGMT); guiGetBackToCurrentScreen(); miniWheelClearDetections(); } else if (currentScreen == SCREEN_DEFAULT_INSERTED_LCK) { // Locked screen and a detection happened, check that the user hasn't removed his card, launch unlocking process if ((cardDetectedRoutine() == RETURN_MOOLTIPASS_USER) && (validCardDetectedFunction(0, TRUE) == RETURN_VCARD_OK)) { // User approved his pin unlockFeatureCheck(); currentScreen = SCREEN_DEFAULT_INSERTED_NLCK; } // Go to the new screen guiGetBackToCurrentScreen(); } else { if (input_interface_result == WHEEL_ACTION_UP) { // We can do that because of defines and bitmap order (see logic_fw_flash_storage and gui.h) if (currentScreen == SCREEN_LOCK) { currentScreen = SCREEN_SETTINGS; } else if (currentScreen == SCREEN_SETTINGS_CHANGE_PIN) { currentScreen = SCREEN_SETTINGS_ERASE; } else { currentScreen--; } // We can do that because of defines and bitmap order (see logic_fw_flash_storage and gui.h) for (uint8_t i = 0; i < NB_BMPS_PER_TRANSITION; i++) { miniOledBitmapDrawFlash(0, 0, (currentScreen-SCREEN_LOCK)*NB_BMPS_PER_TRANSITION+BITMAP_MAIN_LOCK+NB_BMPS_PER_TRANSITION-1-i, OLED_SCROLL_FLIP); timerBasedDelayMs(12); } } else if (input_interface_result == WHEEL_ACTION_DOWN) { // We can do that because of defines and bitmap order (see logic_fw_flash_storage and gui.h) for (uint8_t i = 0; i < NB_BMPS_PER_TRANSITION-1; i++) { miniOledBitmapDrawFlash(0, 0, (currentScreen-SCREEN_LOCK)*NB_BMPS_PER_TRANSITION+BITMAP_MAIN_LOCK+1+i, OLED_SCROLL_FLIP); timerBasedDelayMs(12); } if (currentScreen == SCREEN_SETTINGS) { currentScreen = SCREEN_LOCK; } else if (currentScreen == SCREEN_SETTINGS_ERASE) { currentScreen = SCREEN_SETTINGS_CHANGE_PIN; } else { currentScreen++; } miniOledBitmapDrawFlash(0, 0, (currentScreen-SCREEN_LOCK)*NB_BMPS_PER_TRANSITION+BITMAP_MAIN_LOCK, OLED_SCROLL_FLIP); } else if (input_interface_result == WHEEL_ACTION_LONG_CLICK) { // Long press in main menu : lock, long press in settings menu: go back to login screen if ((currentScreen >= SCREEN_SETTINGS_CHANGE_PIN) && (currentScreen <= SCREEN_SETTINGS_ERASE)) { currentScreen = SCREEN_LOGIN; miniOledBitmapDrawFlash(0, 0, (currentScreen-SCREEN_LOCK)*NB_BMPS_PER_TRANSITION+BITMAP_MAIN_LOCK, OLED_SCROLL_UP); } } else if (input_interface_result == WHEEL_ACTION_SHORT_CLICK) { switch(currentScreen) { case SCREEN_LOCK: { // User wants to lock his mooltipass currentScreen = SCREEN_DEFAULT_INSERTED_LCK; handleSmartcardRemoved(); guiGetBackToCurrentScreen(); break; } case SCREEN_LOGIN: { // User wants to go to the login menu if (getStartingParentAddress() != NODE_ADDR_NULL) { loginSelectLogic(); } else { guiDisplayInformationOnScreenAndWait(ID_STRING_NO_CREDS); } guiGetBackToCurrentScreen(); break; } case SCREEN_FAVORITES: { // User wants to go to the favorite menu #if defined(ENABLE_CREDENTIAL_MANAGEMENT) && defined(REPLACE_FAVORITES_WITH_CREDENTIAL_MANAGEMENT) managementActionPickingLogic(); #else favoritePickingLogic(); #endif guiGetBackToCurrentScreen(); break; } case SCREEN_SETTINGS: { currentScreen = SCREEN_SETTINGS_CHANGE_PIN; miniOledBitmapDrawFlash(0, 0, (currentScreen-SCREEN_LOCK)*NB_BMPS_PER_TRANSITION+BITMAP_MAIN_LOCK, OLED_SCROLL_UP); break; } case SCREEN_SETTINGS_HOME: { currentScreen = SCREEN_LOGIN; miniOledBitmapDrawFlash(0, 0, (currentScreen-SCREEN_LOCK)*NB_BMPS_PER_TRANSITION+BITMAP_MAIN_LOCK, OLED_SCROLL_UP); break; } case SCREEN_SETTINGS_CHANGE_PIN: { // User wants to change his PIN code // Reauth user if (removeCardAndReAuthUser() == RETURN_OK) { // User approved his pin, ask his new one volatile uint16_t pin_code; if (guiAskForNewPin(&pin_code, ID_STRING_NEW_PINQ) == RETURN_NEW_PIN_OK) { // User successfully entered a new pin writeSecurityCode(&pin_code); // Inform of success guiDisplayInformationOnScreenAndWait(ID_STRING_PIN_CHANGED); } else { // Inform of fail guiDisplayInformationOnScreenAndWait(ID_STRING_PIN_NCGHANGED); } pin_code = 0x0000; } else { currentScreen = SCREEN_DEFAULT_INSERTED_LCK; } guiGetBackToCurrentScreen(); break; } case SCREEN_SETTINGS_BACKUP: { // User wants to clone his smartcard volatile uint16_t pin_code; RET_TYPE temp_rettype; // Reauth user if (removeCardAndReAuthUser() == RETURN_OK) { // Ask for new pin temp_rettype = guiAskForNewPin(&pin_code, ID_STRING_PIN_NEW_CARD); if (temp_rettype == RETURN_NEW_PIN_OK) { // Start the cloning process if (cloneSmartCardProcess(&pin_code) == RETURN_OK) { // Well it worked.... } else { currentScreen = SCREEN_DEFAULT_INSERTED_LCK; guiDisplayInformationOnScreen(ID_STRING_TGT_CARD_NBL); } pin_code = 0x0000; } else if (temp_rettype == RETURN_NEW_PIN_DIFF) { currentScreen = SCREEN_DEFAULT_INSERTED_LCK; guiDisplayInformationOnScreen(ID_STRING_PIN_DIFF); } else { guiGetBackToCurrentScreen(); return; } } else { currentScreen = SCREEN_DEFAULT_INSERTED_LCK; guiDisplayInformationOnScreen(ID_STRING_FAILED); } userViewDelay(); guiGetBackToCurrentScreen(); break; } case SCREEN_SETTINGS_ERASE: { // User wants to delete his profile in flash / eeprom.... if ((guiAskForConfirmation(1, (confirmationText_t*)readStoredStringToBuffer(ID_STRING_AREYOUSURE)) == RETURN_OK) && (removeCardAndReAuthUser() == RETURN_OK) && (guiAskForConfirmation(1, (confirmationText_t*)readStoredStringToBuffer(ID_STRING_AREYOURLSURE)) == RETURN_OK)) { uint8_t currentuserid = getCurrentUserID(); guiDisplayProcessingScreen(); deleteCurrentUserFromFlash(); if (guiAskForConfirmation(1, (confirmationText_t*)readStoredStringToBuffer(ID_STRING_ERASE_TCARD)) == RETURN_OK) { guiDisplayProcessingScreen(); eraseSmartCard(); // Erase other smartcards while (guiAskForConfirmation(1, (confirmationText_t*)readStoredStringToBuffer(ID_STRING_OTHECARDFUSER)) == RETURN_OK) { // Ask the user to insert other smartcards guiDisplayInformationOnScreen(ID_STRING_INSERT_OTHER); // Wait for the user to remove and enter another smartcard while (isCardPlugged() != RETURN_JRELEASED); // Wait for the user to insert a new smart card while (isCardPlugged() != RETURN_JDETECT); guiDisplayProcessingScreen(); // Check the card type & ask user to enter his pin, check that the new user id loaded by validCardDetectedFunction is still the same if ((cardDetectedRoutine() == RETURN_MOOLTIPASS_USER) && (validCardDetectedFunction(0, FALSE) == RETURN_VCARD_OK) && (currentuserid == getCurrentUserID())) { eraseSmartCard(); } } } // Delete LUT entries guiDisplayProcessingScreen(); deleteUserIdFromSMCUIDLUT(currentuserid); // Go to invalid screen currentScreen = SCREEN_DEFAULT_INSERTED_INVALID; } else { currentScreen = SCREEN_DEFAULT_INSERTED_LCK; } userViewDelay(); handleSmartcardRemoved(); guiGetBackToCurrentScreen(); break; } default: break; } } } }
/*! \fn guiAskForConfirmation(const char* string) * \brief Ask for user confirmation for different things * \param nb_args Number of text lines (must be either 1 2 or 3/4 (depending on the MP version)) * \param text_object Pointer to the text object if more than 1 line, pointer to the string if not * \return User confirmation or not */ RET_TYPE guiAskForConfirmation(uint8_t nb_args, confirmationText_t* text_object) { uint8_t flash_flag = FALSE; // Check if we want to flash the screen if ((nb_args & 0xF0) != 0) { nb_args = nb_args & 0x0F; // Check that the user didn't disable it if (getMooltipassParameterInEeprom(FLASH_SCREEN_PARAM) != FALSE) { flash_flag = TRUE; } } #if defined(HARDWARE_OLIVIER_V1) // Temp string for truncating char string_tbd[31]; string_tbd[30] = 0; // Draw asking bitmap oledClear(); oledBitmapDrawFlash(0, 0, BITMAP_YES_NO_INT_L, 0); oledBitmapDrawFlash(222, 0, BITMAP_YES_NO_INT_R, 0); // If more than one line if (nb_args == 1) { // Yeah, that's a bit dirty oledPutstrXY(0, 24, OLED_CENTRE, (char*)text_object); } else { while (nb_args--) { // Truncate and then display string memcpy(string_tbd, text_object->lines[nb_args], 30); oledPutstrXY(0, 2 + (nb_args << 4), OLED_CENTRE, string_tbd); } } // Display result oledDisplayOtherBuffer(); #elif defined(MINI_VERSION) // Variables for scrolling uint8_t string_y_indexes[3]; uint8_t string_extra_chars[3]; uint8_t string_offset_cntrs[3] = {0,0,0}; // Display variables uint8_t approve_selected = TRUE; // Draw asking bitmap oledClear(); miniOledSetMaxTextY(SSD1305_OLED_WIDTH-15); oledBitmapDrawFlash(SSD1305_OLED_WIDTH-15, 0, BITMAP_APPROVE, 0); // Display lines. // Note: line are truncated at the oled driver level when miniOledTextWritingYIncrement is set to FALSE if (nb_args == 1) { miniOledPutCenteredString(THREE_LINE_TEXT_SECOND_POS, (char*)text_object); } else if (nb_args == 2) { string_y_indexes[0] = TWO_LINE_TEXT_FIRST_POS; string_y_indexes[1] = TWO_LINE_TEXT_SECOND_POS; } else { string_y_indexes[0] = THREE_LINE_TEXT_FIRST_POS; string_y_indexes[1] = THREE_LINE_TEXT_SECOND_POS; string_y_indexes[2] = THREE_LINE_TEXT_THIRD_POS; } // For loop to display lines when there is more than one arg if (nb_args > 1) { for (uint8_t i = 0; i < nb_args; i++) { string_extra_chars[i] = strlen(text_object->lines[i]) - miniOledPutCenteredString(string_y_indexes[i], text_object->lines[i]); } } miniOledFlushEntireBufferToDisplay(); miniOledResetMaxTextY(); #endif // In case the display inverted, set it correctly if (flash_flag == TRUE) { activityDetectedRoutine(); oledInvertedDisplay(); timerBased500MsDelay(); oledNormalDisplay(); timerBased500MsDelay(); oledInvertedDisplay(); timerBased500MsDelay(); oledNormalDisplay(); } // Wait for user input #if defined(HARDWARE_OLIVIER_V1) if(getTouchedPositionAnswer(LED_MASK_WHEEL) == TOUCHPOS_RIGHT) { return RETURN_OK; } else { return RETURN_NOK; } #elif defined(MINI_VERSION) RET_TYPE input_answer = MINI_INPUT_RET_NONE; // Switch on lights activityDetectedRoutine(); // Clear possible remaining detection miniWheelClearDetections(); // Arm timer for scrolling (caps timer that isn't relevant here) activateTimer(TIMER_CAPS, SCROLLING_DEL); // Loop while no timeout occurs or no button is pressed while (input_answer == MINI_INPUT_RET_NONE) { input_answer = getYesNoAnswerInput(FALSE); // Text scrolling if ((hasTimerExpired(TIMER_CAPS, TRUE) == TIMER_EXPIRED) && (nb_args > 1)) { miniOledDrawRectangle(0, 0, SSD1305_OLED_WIDTH-15, SSD1305_OLED_HEIGHT, FALSE); activateTimer(TIMER_CAPS, SCROLLING_DEL); miniOledSetMaxTextY(SSD1305_OLED_WIDTH-15); for (uint8_t i = 0; i < nb_args; i++) { if (string_extra_chars[i] > 0) { miniOledPutCenteredString(string_y_indexes[i], (text_object->lines[i]) + string_offset_cntrs[i]); if (string_offset_cntrs[i]++ == string_extra_chars[i]) { string_offset_cntrs[i] = 0; } } else { miniOledPutCenteredString(string_y_indexes[i], text_object->lines[i]); } } miniOledFlushEntireBufferToDisplay(); miniOledResetMaxTextY(); } // Approve / deny display change if (getWheelCurrentIncrement() != 0) { if(approve_selected == FALSE) { oledBitmapDrawFlash(SSD1305_OLED_WIDTH-15, 0, BITMAP_APPROVE, 0); } else { oledBitmapDrawFlash(SSD1305_OLED_WIDTH-15, 0, BITMAP_DENY, 0); } approve_selected = !approve_selected; miniOledFlushEntireBufferToDisplay(); } } if ((input_answer == MINI_INPUT_RET_YES) && (approve_selected != FALSE)) { return RETURN_OK; } else { return RETURN_NOK; } #endif }