#ifdef HAVE_U2F /* ******************************************************************************* * Portable FIDO U2F implementation * (c) 2016 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. ********************************************************************************/ #include #include #include "u2f_service.h" #include "u2f_transport.h" #include "u2f_processing.h" #include "u2f_timer.h" // not too fast blinking #define DEFAULT_TIMER_INTERVAL_MS 500 void u2f_reset(u2f_service_t *service, bool keepUserPresence) { service->transportState = U2F_IDLE; service->runningCommand = false; // service->promptUserPresence = false; if (service->keepUserPresence) { keepUserPresence = true; service->keepUserPresence = false; } #ifdef HAVE_NO_USER_PRESENCE_CHECK service->keepUserPresence = true; service->userPresence = true; #endif // HAVE_NO_USER_PRESENCE_CHECK } void u2f_initialize_service(u2f_service_t *service) { service->handleFunction = (u2fHandle_t)u2f_process_message; service->timeoutFunction = (u2fTimer_t)u2f_timeout; service->timerInterval = DEFAULT_TIMER_INTERVAL_MS; u2f_reset(service, false); service->promptUserPresence = false; service->userPresence = false; #ifdef HAVE_NO_USER_PRESENCE_CHECK service->keepUserPresence = true; service->userPresence = true; #endif // HAVE_NO_USER_PRESENCE_CHECK } void u2f_send_direct_response_short(u2f_service_t *service, uint8_t *buffer, uint16_t len) { (void)service; uint16_t maxSize = 0; switch (service->packetMedia) { case U2F_MEDIA_USB: maxSize = USB_SEGMENT_SIZE; break; #ifdef HAVE_BLE case U2F_MEDIA_BLE: maxSize = service->bleMtu; break; #endif default: PRINTF("Request to send on unsupported media %d\n", service->packetMedia); break; } if (len > maxSize) { return; } u2f_io_send(buffer, len, service->packetMedia); u2f_io_close_session(); } void u2f_send_fragmented_response(u2f_service_t *service, uint8_t cmd, uint8_t *buffer, uint16_t len, bool resetAfterSend) { if (resetAfterSend) { service->transportState = U2F_SENDING_RESPONSE; } service->sending = true; service->sendPacketIndex = 0; service->sendBuffer = buffer; service->sendOffset = 0; service->sendLength = len; service->sendCmd = cmd; service->resetAfterSend = resetAfterSend; u2f_continue_sending_fragmented_response(service); } void u2f_continue_sending_fragmented_response(u2f_service_t *service) { do { uint16_t channelHeader = (service->transportMedia == U2F_MEDIA_USB ? 4 : 0); uint8_t headerSize = (service->sendPacketIndex == 0 ? (channelHeader + 3) : (channelHeader + 1)); uint16_t maxBlockSize = (service->transportMedia == U2F_MEDIA_USB ? USB_SEGMENT_SIZE : service->bleMtu); uint16_t blockSize = ((service->sendLength - service->sendOffset) > (maxBlockSize - headerSize) ? (maxBlockSize - headerSize) : service->sendLength - service->sendOffset); uint16_t dataSize = blockSize + headerSize; uint16_t offset = 0; // Fragment if (service->transportMedia == U2F_MEDIA_USB) { os_memset(service->outputBuffer, 0, USB_SEGMENT_SIZE); os_memmove(service->outputBuffer + offset, service->channel, 4); offset += 4; } if (service->sendPacketIndex == 0) { service->outputBuffer[offset++] = service->sendCmd; service->outputBuffer[offset++] = (service->sendLength >> 8); service->outputBuffer[offset++] = (service->sendLength & 0xff); } else { service->outputBuffer[offset++] = (service->sendPacketIndex - 1); } if (service->sendBuffer != NULL) { os_memmove(service->outputBuffer + headerSize, service->sendBuffer + service->sendOffset, blockSize); } u2f_io_send(service->outputBuffer, dataSize, service->packetMedia); service->sendOffset += blockSize; service->sendPacketIndex++; } while (service->sendOffset != service->sendLength); if (service->sendOffset == service->sendLength) { u2f_io_close_session(); service->sending = false; if (service->resetAfterSend) { u2f_reset(service, false); } } } #endif