diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e9da91d..2cf829f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -5,6 +5,9 @@ on: push: tags: - "v*" +env: + TARGET: esp32s3 + IDF_TARGET: esp32s3 jobs: Build-Firmware: runs-on: ubuntu-latest @@ -21,7 +24,7 @@ jobs: uses: actions/checkout@v2 with: repository: espressif/esp-idf - ref: release/v4.4 + ref: v4.4.1 path: esp-idf submodules: true - name: Install SDK @@ -48,8 +51,8 @@ jobs: source esp-idf/export.sh ln -sfn $GITHUB_WORKSPACE/tildamk6 micropython/ports/esp32/boards/tildamk6 cd micropython/ports/esp32 - make submodules BOARD=tildamk6 USER_C_MODULES=$GITHUB_WORKSPACE/drivers/micropython.cmake IOT_SOLUTION_PATH=$GITHUB_WORKSPACE/esp-iot-solution TARGET=esp32s3 - make BOARD=tildamk6 USER_C_MODULES=$GITHUB_WORKSPACE/drivers/micropython.cmake $@ IOT_SOLUTION_PATH=$GITHUB_WORKSPACE/esp-iot-solution TARGET=esp32s3 + make submodules BOARD=tildamk6 USER_C_MODULES=$GITHUB_WORKSPACE/drivers/micropython.cmake USER_COMPONENTS=$GITHUB_WORKSPACE/components/tinyusb IOT_SOLUTION_PATH=$GITHUB_WORKSPACE/esp-iot-solution TARGET=esp32s3 + make BOARD=tildamk6 USER_C_MODULES=$GITHUB_WORKSPACE/drivers/micropython.cmake USER_COMPONENTS=$GITHUB_WORKSPACE/components/tinyusb $@ IOT_SOLUTION_PATH=$GITHUB_WORKSPACE/esp-iot-solution TARGET=esp32s3 cd ../../.. echo "{\"build\":\"$(git describe --tags --always)\", \"name\":\"$(git describe --tags --always)\"}" > micropython/ports/esp32/build-tildamk6/tidal.txt - name: Archive firmware diff --git a/components/tinyusb/CMakeLists.txt b/components/tinyusb/CMakeLists.txt new file mode 100644 index 0000000..23a5532 --- /dev/null +++ b/components/tinyusb/CMakeLists.txt @@ -0,0 +1,107 @@ +set(IDF_TARGET esp32s3) +idf_build_get_property(target IDF_TARGET) + +set(srcs) +set(includes_public) +set(includes_private) +set(compile_options) + + +if(CONFIG_TINYUSB) + set(tusb_mcu "OPT_MCU_ESP32S3") + set(tusb_family "esp32sx") + + list(APPEND compile_options + "-DCFG_TUSB_MCU=${tusb_mcu}" + "-DCFG_TUSB_DEBUG=${CONFIG_TINYUSB_DEBUG_LEVEL}" + ) + + idf_component_get_property(freertos_component_dir freertos COMPONENT_DIR) + + list(APPEND includes_private + "${IDF_PATH}/components/tinyusb/tinyusb/hw/bsp/" + "${IDF_PATH}/components/tinyusb/tinyusb/src/" + "${IDF_PATH}/components/tinyusb/tinyusb/src/device" + "${IDF_PATH}/components/tinyusb/tinyusb/src/class/bth" + #"additions/include_private" + ) + + list(APPEND includes_public + "${IDF_PATH}/components/tinyusb/tinyusb/src/" + "additions/include" + "additions/include_private" + "additions/tusb/src/lib/networking" + "additions/tusb/src/class/dfu" + # The FreeRTOS API include convention in tinyusb is different from esp-idf + "${freertos_component_dir}/include/freertos" + ) + + list(APPEND srcs + "${IDF_PATH}/components/tinyusb/tinyusb/src/portable/espressif/${tusb_family}/dcd_${tusb_family}.c" + "${IDF_PATH}/components/tinyusb/tinyusb/src/class/cdc/cdc_device.c" + "${IDF_PATH}/components/tinyusb/tinyusb/src/class/hid/hid_device.c" + "${IDF_PATH}/components/tinyusb/tinyusb/src/class/midi/midi_device.c" + "${IDF_PATH}/components/tinyusb/tinyusb/src/class/msc/msc_device.c" + "${IDF_PATH}/components/tinyusb/tinyusb/src/class/vendor/vendor_device.c" + "${IDF_PATH}/components/tinyusb/tinyusb/src/common/tusb_fifo.c" + "${IDF_PATH}/components/tinyusb/tinyusb/src/device/usbd_control.c" + "${IDF_PATH}/components/tinyusb/tinyusb/src/tusb.c" + "additions/src/usbd.c" + "additions/src/descriptors_control.c" + "additions/src/tinyusb.c" + "additions/src/tusb_tasks.c" + "additions/src/usb_descriptors.c" + ) + + # when no builtin class driver is enabled, an uint8_t data compared with `BUILTIN_DRIVER_COUNT` will always be false + set_source_files_properties("tinyusb/src/device/usbd.c" PROPERTIES COMPILE_FLAGS "-Wno-type-limits") + + if(CONFIG_TINYUSB_CDC_ENABLED) + list(APPEND srcs + "additions/src/cdc.c" + "additions/src/tusb_cdc_acm.c" + "additions/src/tusb_console.c" + "additions/src/vfs_tinyusb.c" + ) + endif() # CONFIG_TINYUSB_CDC_ENABLED + + if(CONFIG_TINYUSB_HID_ENABLED) + list(APPEND srcs + "additions/src/tusb_hid.c") + endif() + + if(CONFIG_TINYUSB_MSC_ENABLED) + list(APPEND srcs + "additions/src/tusb_msc.c") + endif() + + if(CONFIG_TINYUSB_NET_ENABLED) + list(APPEND srcs + "additions/src/tusb_net.c" + "additions/tusb/src/class/net/net_device.c" + "additions/tusb/src/lib/networking/rndis_reports.c") + endif() + + if(CONFIG_TINYUSB_BTH_ENABLED) + list(APPEND srcs + "additions/src/tusb_bth.c" + "${IDF_PATH}/components/tinyusb/tinyusb/src/class/bth/bth_device.c") + endif() + + if(CONFIG_TINYUSB_DFU_ENABLED) + list(APPEND srcs + "additions/src/tusb_dfu.c" + "additions/tusb/src/class/dfu/dfu_device.c") + endif() + +endif() # CONFIG_TINYUSB + +idf_component_register(SRCS ${srcs} + INCLUDE_DIRS ${includes_public} + PRIV_INCLUDE_DIRS ${includes_private} + PRIV_REQUIRES "vfs" "fatfs" "bt" "app_update" + ) + +if(CONFIG_TINYUSB) + target_compile_options(${COMPONENT_LIB} PRIVATE ${compile_options}) +endif() diff --git a/components/tinyusb/Kconfig b/components/tinyusb/Kconfig new file mode 100644 index 0000000..f695104 --- /dev/null +++ b/components/tinyusb/Kconfig @@ -0,0 +1,267 @@ +menu "TinyUSB Stack" + visible if USB_OTG_SUPPORTED + + config TINYUSB + bool "Use TinyUSB Stack" + depends on USB_OTG_SUPPORTED + default n + help + Enable TinyUSB stack support. + Note that, esp-idf only uses the device stack provided by TinyUSB. + + if TINYUSB + config TINYUSB_DEBUG_LEVEL + int "TinyUSB log level (0-3)" + default 0 + range 0 3 + help + Specify verbosity of TinyUSB log output. + + menu "TinyUSB task configuration" + config TINYUSB_NO_DEFAULT_TASK + bool "Do not create a TinyUSB task" + default n + help + This option allows to not create the FreeRTOS task during the driver initialization. + User will have to handle TinyUSB events manually. + + config TINYUSB_TASK_PRIORITY + int "TinyUSB task priority" + default 5 + depends on !TINYUSB_NO_DEFAULT_TASK + help + Set the priority of the default TinyUSB main task. + + config TINYUSB_TASK_STACK_SIZE + int "TinyUSB task stack size (bytes)" + default 4096 + depends on !TINYUSB_NO_DEFAULT_TASK + help + Set the stack size of the default TinyUSB main task. + endmenu + + menu "Descriptor configuration" + config TINYUSB_DESC_USE_ESPRESSIF_VID + bool "VID: Use Espressif's vendor ID" + default y + help + Enable this option, USB device will use Espressif's vendor ID as its VID. + This is helpful at product develop stage. + + config TINYUSB_DESC_CUSTOM_VID + hex "VID: Custom vendor ID" + default 0x1234 + depends on !TINYUSB_DESC_USE_ESPRESSIF_VID + help + Custom Vendor ID. + + config TINYUSB_DESC_USE_DEFAULT_PID + bool "PID: Use a default PID assigned to TinyUSB" + default y + help + Default TinyUSB PID assigning uses values 0x4000...0x4007. + + config TINYUSB_DESC_CUSTOM_PID + hex "PID: Custom product ID" + default 0x5678 + depends on !TINYUSB_DESC_USE_DEFAULT_PID + help + Custom Product ID. + + config TINYUSB_DESC_BCD_DEVICE + hex "bcdDevice" + default 0x0100 + help + Version of the firmware of the USB device. + + config TINYUSB_DESC_MANUFACTURER_STRING + string "Manufacturer name" + default "Espressif Systems" + help + Name of the manufacturer of the USB device. + + config TINYUSB_DESC_PRODUCT_STRING + string "Product name" + default "Espressif Device" + help + Name of the USB device. + + config TINYUSB_DESC_SERIAL_STRING + string "Serial string" + default "123456" + help + Serial number of the USB device. + + config TINYUSB_DESC_CDC_STRING + depends on TINYUSB_CDC_ENABLED + string "CDC Device String" + default "Espressif CDC Device" + help + Name of the CDC device. + + config TINYUSB_DESC_MSC_STRING + depends on TINYUSB_MSC_ENABLED + string "MSC Device String" + default "Espressif MSC Device" + help + Name of the MSC device. + + config TINYUSB_DESC_HID_STRING + depends on TINYUSB_HID_ENABLED + string "HID Device String" + default "Espressif HID Device" + help + Name of the HID device + config TINYUSB_DESC_NET_STRING + depends on TINYUSB_NET_ENABLED + string "NET Device String" + default "Espressif NET Device" + help + Name of the NET device. + config TINYUSB_DESC_BTH_STRING + depends on TINYUSB_BTH_ENABLED + string "BTH String" + default "Espressif BTH Device" + help + Name of the BTH device. + endmenu # "Descriptor configuration" + + menu "Massive Storage Class (MSC)" + config TINYUSB_MSC_ENABLED + bool "Enable TinyUSB MSC feature" + default n + help + Enable TinyUSB MSC feature. + + config TINYUSB_MSC_BUFSIZE + depends on TINYUSB_MSC_ENABLED + int "MSC FIFO size" + default 512 + help + MSC FIFO size, in bytes. + endmenu # "Massive Storage Class" + + menu "Communication Device Class (CDC)" + config TINYUSB_CDC_ENABLED + bool "Enable TinyUSB CDC feature" + default n + help + Enable TinyUSB CDC feature. + config TINYUSB_CDC_PORT_NUM + depends on TINYUSB_CDC_ENABLED + int "Number of Serial (CDC) Port" + default 1 + range 1 2 + help + Number of Serial (CDC) Port. + config TINYUSB_CDC_RX_BUFSIZE + depends on TINYUSB_CDC_ENABLED + int "CDC FIFO size of RX channel" + default 64 + help + CDC FIFO size of RX channel. + + config TINYUSB_CDC_TX_BUFSIZE + depends on TINYUSB_CDC_ENABLED + int "CDC FIFO size of TX channel" + default 64 + help + CDC FIFO size of TX channel. + endmenu # "Communication Device Class" + + menu "Human Interface Device Class (HID)" + config TINYUSB_HID_ENABLED + bool "Enable TinyUSB HID feature" + default n + help + Enable TinyUSB HID feature. + + config TINYUSB_U2FHID_ENABLED + bool "Enable TinyUSB U2FHID tunneling" + default n + help + Enable TinyUSB U2FHID feature. + + config TINYUSB_HIDKEYBOARD_ENABLED + bool "Enable TinyUSB HID Keyboard" + default n + help + Enable TinyUSB HID Keyboard. + + config TINYUSB_HID_BUFSIZE + int "HID FIFO size" + default 64 + depends on TINYUSB_HID_ENABLED + help + HID FIFO size + endmenu # "Human Interface Device Class" + + menu "USB Network Class (RNDIS, ECM)" + + config TINYUSB_NET_ENABLED + bool "Enable TinyUSB NET driver" + default n + help + Enable USB NET TinyUSB driver. + + config TINYUSB_RNDIS_VENDOR + string "USB Vendor" + default "Espressif Incorporated" + depends on TINYUSB_NET_ENABLED + help + Vendor description. + + choice TINYUSB_NET + prompt "Choose a network communication standard" + default TINYUSB_NET_RNDIS + depends on TINYUSB_NET_ENABLED + help + RNDIS and CDC-ECM + - Windows only works with RNDIS + - MacOS only works with CDC-ECM + - Linux will work on both + + config TINYUSB_NET_RNDIS + bool "RNDIS" + help + RNDIS. + config TINYUSB_NET_ECM + bool "CDC-ECM" + help + CDC-ECM. + endchoice + endmenu # "usb network" + + menu "Bluetooth Host Class (BTH)" + config TINYUSB_BTH_ENABLED + bool "Enable TinyUSB BTH feature" + default n + help + Enable TinyUSB BTH feature. + + config TINYUSB_BTH_ISO_ALT_COUNT + depends on TINYUSB_BTH_ENABLED + int "BTH ISO ALT COUNT" + default 0 + help + BTH ISO ALT COUNT. + endmenu # "Bluetooth Host Device Class" + + menu "Firmware Upgrade Class (DFU)" + config TINYUSB_DFU_ENABLED + bool "Enable TinyUSB DFU feature" + default n + help + Enable TinyUSB DFU feature. + + config TINYUSB_DFU_BUFSIZE + depends on TINYUSB_DFU_ENABLED + int "DFU XFER BUFFSIZE" + default 512 + help + DFU XFER BUFFSIZE. + endmenu # "Firmware Upgrade Class" + + endif # TINYUSB + +endmenu # "TinyUSB Stack" diff --git a/components/tinyusb/additions/include/tinyusb.h b/components/tinyusb/additions/include/tinyusb.h new file mode 100644 index 0000000..00da34c --- /dev/null +++ b/components/tinyusb/additions/include/tinyusb.h @@ -0,0 +1,103 @@ +// Copyright 2020 Espressif Systems (Shanghai) PTE LTD +// +// 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. + +#pragma once + +#include +#include "tusb.h" +#include "tusb_option.h" +#include "tusb_config.h" +#include "tinyusb_types.h" + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* tinyusb uses buffers with type of uint8_t[] but in our driver we are reading them as a 32-bit word */ +#if (CFG_TUD_ENDPOINT0_SIZE < 4) +# define CFG_TUD_ENDPOINT0_SIZE 4 +# warning "CFG_TUD_ENDPOINT0_SIZE was too low and was set to 4" +#endif + +#if TUSB_OPT_DEVICE_ENABLED + +# if CFG_TUD_HID +# if (CFG_TUD_HID_BUFSIZE < 4) +# define CFG_TUD_HID_BUFSIZE 4 +# warning "CFG_TUD_HID_BUFSIZE was too low and was set to 4" +# endif +# endif + +# if CFG_TUD_CDC +# if (CFG_TUD_CDC_EP_BUFSIZE < 4) +# define CFG_TUD_CDC_EP_BUFSIZE 4 +# warning "CFG_TUD_CDC_EP_BUFSIZE was too low and was set to 4" +# endif +# endif + +# if CFG_TUD_MSC +# if (CFG_TUD_MSC_BUFSIZE < 4) +# define CFG_TUD_MSC_BUFSIZE 4 +# warning "CFG_TUD_MSC_BUFSIZE was too low and was set to 4" +# endif +# endif + +# if CFG_TUD_MIDI +# if (CFG_TUD_MIDI_EPSIZE < 4) +# define CFG_TUD_MIDI_EPSIZE 4 +# warning "CFG_TUD_MIDI_EPSIZE was too low and was set to 4" +# endif +# endif + +# if CFG_TUD_CUSTOM_CLASS +# warning "Please check that the buffer is more then 4 bytes" +# endif +#endif + +/** + * @brief Configuration structure of the tinyUSB core + */ +typedef struct { + tusb_desc_device_t *descriptor; /*!< Pointer to a device descriptor */ + const char **string_descriptor; /*!< Pointer to an array of string descriptors */ + const uint8_t *config_descriptor; /*!< Pointer to config descriptors */ + bool external_phy; /*!< Should USB use an external PHY */ +} tinyusb_config_t; + +/** + * @brief This is an all-in-one helper function, including: + * 1. USB device driver initialization + * 2. Descriptors preparation + * 3. TinyUSB stack initialization + * 4. Creates and start a task to handle usb events + * + * @note Don't change Custom descriptor, but if it has to be done, + * Suggest to define as follows in order to match the Interface Association Descriptor (IAD): + * bDeviceClass = TUSB_CLASS_MISC, + * bDeviceSubClass = MISC_SUBCLASS_COMMON, + * + * @param config tinyusb stack specific configuration + * @retval ESP_ERR_INVALID_ARG Install driver and tinyusb stack failed because of invalid argument + * @retval ESP_FAIL Install driver and tinyusb stack failed because of internal error + * @retval ESP_OK Install driver and tinyusb stack successfully + */ +esp_err_t tinyusb_driver_install(const tinyusb_config_t *config); + +// TODO esp_err_t tinyusb_driver_uninstall(void); (IDF-1474) + +#ifdef __cplusplus +} +#endif diff --git a/components/tinyusb/additions/include/tinyusb_types.h b/components/tinyusb/additions/include/tinyusb_types.h new file mode 100644 index 0000000..0f226f3 --- /dev/null +++ b/components/tinyusb/additions/include/tinyusb_types.h @@ -0,0 +1,32 @@ +// Copyright 2020 Espressif Systems (Shanghai) Co. Ltd. +// +// 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. + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#define USB_ESPRESSIF_VID 0x303A +#define USB_STRING_DESCRIPTOR_ARRAY_SIZE 7 + +typedef enum{ + TINYUSB_USBDEV_0, +} tinyusb_usbdev_t; + +typedef const char *tusb_desc_strarray_device_t[USB_STRING_DESCRIPTOR_ARRAY_SIZE]; + +#ifdef __cplusplus +} +#endif diff --git a/components/tinyusb/additions/include/tusb_bth.h b/components/tinyusb/additions/include/tusb_bth.h new file mode 100644 index 0000000..55f7675 --- /dev/null +++ b/components/tinyusb/additions/include/tusb_bth.h @@ -0,0 +1,39 @@ +/** + * @copyright Copyright 2021 Espressif Systems (Shanghai) Co. Ltd. + * + * 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. + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include "esp_err.h" +#include "tinyusb.h" + +/* HCI message type definitions (for H4 messages) */ +#define HCIT_TYPE_COMMAND 1 +#define HCIT_TYPE_ACL_DATA 2 +#define HCIT_TYPE_SCO_DATA 3 +#define HCIT_TYPE_EVENT 4 + +/** + * @brief Initialize BTH Device. + */ +void tusb_bth_init(void); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/components/tinyusb/additions/include/tusb_cdc_acm.h b/components/tinyusb/additions/include/tusb_cdc_acm.h new file mode 100644 index 0000000..c8942ee --- /dev/null +++ b/components/tinyusb/additions/include/tusb_cdc_acm.h @@ -0,0 +1,204 @@ +// Copyright 2020 Espressif Systems (Shanghai) Co. Ltd. +// +// 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. + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include "freertos/FreeRTOS.h" +#include "freertos/ringbuf.h" +#include "freertos/semphr.h" +#include "freertos/timers.h" +#include "tusb.h" +#include "tinyusb.h" + +/** + * @brief CDC ports available to setup + */ +typedef enum{ + TINYUSB_CDC_ACM_0 = 0x0, + TINYUSB_CDC_ACM_1, + TINYUSB_CDC_TOTAL +}tinyusb_cdcacm_itf_t; + +/* Callbacks and events + ********************************************************************* */ + +/** + * @brief Data provided to the input of the `callback_rx_wanted_char` callback + */ +typedef struct { + char wanted_char; /*!< Wanted character */ +} cdcacm_event_rx_wanted_char_data_t; + +/** + * @brief Data provided to the input of the `callback_line_state_changed` callback + */ +typedef struct { + bool dtr; /*!< Data Terminal Ready (DTR) line state */ + bool rts; /*!< Request To Send (RTS) line state */ +} cdcacm_event_line_state_changed_data_t; + +/** + * @brief Data provided to the input of the `line_coding_changed` callback + */ +typedef struct { + cdc_line_coding_t const *p_line_coding; /*!< New line coding value */ +} cdcacm_event_line_coding_changed_data_t; + +/** + * @brief Types of CDC ACM events + */ +typedef enum { + CDC_EVENT_RX, + CDC_EVENT_RX_WANTED_CHAR, + CDC_EVENT_LINE_STATE_CHANGED, + CDC_EVENT_LINE_CODING_CHANGED +} cdcacm_event_type_t; + +/** + * @brief Describes an event passing to the input of a callbacks + */ +typedef struct { + cdcacm_event_type_t type; /*!< Event type */ + union { + cdcacm_event_rx_wanted_char_data_t rx_wanted_char_data; /*!< Data input of the `callback_rx_wanted_char` callback */ + cdcacm_event_line_state_changed_data_t line_state_changed_data; /*!< Data input of the `callback_line_state_changed` callback */ + cdcacm_event_line_coding_changed_data_t line_coding_changed_data; /*!< Data input of the `line_coding_changed` callback */ + }; +} cdcacm_event_t; + +/** + * @brief CDC-ACM callback type + */ +typedef void(*tusb_cdcacm_callback_t)(int itf, cdcacm_event_t *event); + +/*********************************************************************** Callbacks and events*/ +/* Other structs + ********************************************************************* */ + +/** + * @brief Configuration structure for CDC-ACM + */ +typedef struct { + tinyusb_usbdev_t usb_dev; /*!< Usb device to set up */ + tinyusb_cdcacm_itf_t cdc_port; /*!< CDC port */ + size_t rx_unread_buf_sz; /*!< Amount of data that can be passed to the AMC at once */ + tusb_cdcacm_callback_t callback_rx; /*!< Pointer to the function with the `tusb_cdcacm_callback_t` type that will be handled as a callback */ + tusb_cdcacm_callback_t callback_rx_wanted_char; /*!< Pointer to the function with the `tusb_cdcacm_callback_t` type that will be handled as a callback */ + tusb_cdcacm_callback_t callback_line_state_changed; /*!< Pointer to the function with the `tusb_cdcacm_callback_t` type that will be handled as a callback */ + tusb_cdcacm_callback_t callback_line_coding_changed; /*!< Pointer to the function with the `tusb_cdcacm_callback_t` type that will be handled as a callback */ +} tinyusb_config_cdcacm_t; + +/*********************************************************************** Other structs*/ +/* Public functions + ********************************************************************* */ +/** + * @brief Initialize CDC ACM. Initialization will be finished with + * the `tud_cdc_line_state_cb` callback + * + * @param cfg - init configuration structure + * @return esp_err_t + */ +esp_err_t tusb_cdc_acm_init(const tinyusb_config_cdcacm_t *cfg); + + +/** + * @brief Register a callback invoking on CDC event. If the callback had been + * already registered, it will be overwritten + * + * @param itf - number of a CDC object + * @param event_type - type of registered event for a callback + * @param callback - callback function + * @return esp_err_t - ESP_OK or ESP_ERR_INVALID_ARG + */ +esp_err_t tinyusb_cdcacm_register_callback(tinyusb_cdcacm_itf_t itf, + cdcacm_event_type_t event_type, + tusb_cdcacm_callback_t callback); + + +/** + * @brief Unregister a callback invoking on CDC event. + * + * @param itf - number of a CDC object + * @param event_type - type of registered event for a callback + * @return esp_err_t - ESP_OK or ESP_ERR_INVALID_ARG + */ +esp_err_t tinyusb_cdcacm_unregister_callback(tinyusb_cdcacm_itf_t itf, cdcacm_event_type_t event_type); + + +/** + * @brief Sent one character to a write buffer + * + * @param itf - number of a CDC object + * @param ch - character to send + * @return size_t - amount of queued bytes + */ +size_t tinyusb_cdcacm_write_queue_char(tinyusb_cdcacm_itf_t itf, char ch); + + +/** + * @brief Write data to write buffer from a byte array + * + * @param itf - number of a CDC object + * @param in_buf - a source array + * @param in_size - size to write from arr_src + * @return size_t - amount of queued bytes + */ +size_t tinyusb_cdcacm_write_queue(tinyusb_cdcacm_itf_t itf, uint8_t *in_buf, size_t in_size); + +/** + * @brief Send all data from a write buffer. Use `tinyusb_cdcacm_write_queue` to add data to the buffer. + * + * WARNING! TinyUSB can block output Endpoint for several RX callbacks, after will do additional flush + * after the each trasfer. That can leads to the situation when you requested a flush, but it will fail until + * ont of the next callbacks ends. + * SO USING OF THE FLUSH WITH TIMEOUTS IN CALLBACKS IS NOT RECOMENDED - YOU CAN GET A LOCK FOR THE TIMEOUT + * + * @param itf - number of a CDC object + * @param timeout_ticks - waiting until flush will be considered as failed + * @return esp_err_t - ESP_OK if (timeout_ticks > 0) and and flush was successful, + * ESP_ERR_TIMEOUT if timeout occurred3 or flush was successful with (timeout_ticks == 0) + * ESP_FAIL if flush was unsuccessful + */ +esp_err_t tinyusb_cdcacm_write_flush(tinyusb_cdcacm_itf_t itf, uint32_t timeout_ticks); + +/** + * @brief Read a content to the array, and defines it's size to the sz_store + * + * @param itf - number of a CDC object + * @param out_buf - to this array will be stored the object from a CDC buffer + * @param out_buf_sz - size of buffer for results + * @param rx_data_size - to this address will be stored the object's size + * @return esp_err_t ESP_OK, ESP_FAIL or ESP_ERR_INVALID_STATE + */ +esp_err_t tinyusb_cdcacm_read(tinyusb_cdcacm_itf_t itf, uint8_t *out_buf, size_t out_buf_sz, size_t *rx_data_size); + + +/** + * @brief Check if the ACM initialized + * + * @param itf - number of a CDC object + * @return true or false + */ +bool tusb_cdc_acm_initialized(tinyusb_cdcacm_itf_t itf); + +/*********************************************************************** Public functions*/ + +#ifdef __cplusplus +} +#endif diff --git a/components/tinyusb/additions/include/tusb_config.h b/components/tinyusb/additions/include/tusb_config.h new file mode 100644 index 0000000..6caaa5e --- /dev/null +++ b/components/tinyusb/additions/include/tusb_config.h @@ -0,0 +1,134 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org), + * Additions Copyright (c) 2020, Espressif Systems (Shanghai) PTE LTD + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#pragma once + +#include +#include "tusb_option.h" +#include "sdkconfig.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef CONFIG_TINYUSB_CDC_ENABLED +# define CONFIG_TINYUSB_CDC_ENABLED 0 +# define CONFIG_TINYUSB_CDC_PORT_NUM 0 +#endif + +#ifndef CONFIG_TINYUSB_MSC_ENABLED +# define CONFIG_TINYUSB_MSC_ENABLED 0 +#endif + +#ifndef CONFIG_TINYUSB_HID_ENABLED +# define CONFIG_TINYUSB_HID_ENABLED 0 +#endif + +#ifndef CONFIG_TINYUSB_U2FHID_ENABLED +# define CONFIG_TINYUSB_U2FHID_ENABLED 0 +#endif + +#ifndef CONFIG_TINYUSB_HIDKEYBOARD_ENABLED +# define CONFIG_TINYUSB_HIDKEYBOARD_ENABLED 0 +#endif + +#ifndef CONFIG_TINYUSB_MIDI_ENABLED +# define CONFIG_TINYUSB_MIDI_ENABLED 0 +#endif + +#ifndef CONFIG_TINYUSB_CUSTOM_CLASS_ENABLED +# define CONFIG_TINYUSB_CUSTOM_CLASS_ENABLED 0 +#endif + +#ifndef CONFIG_TINYUSB_NET_ENABLED +# define CONFIG_TINYUSB_NET_ENABLED 0 +#endif + +#ifndef CONFIG_TINYUSB_BTH_ENABLED +# define CONFIG_TINYUSB_BTH_ENABLED 0 +# define CONFIG_TINYUSB_BTH_ISO_ALT_COUNT 0 +#endif + +#ifndef CONFIG_TINYUSB_DFU_ENABLED +# define CONFIG_TINYUSB_DFU_ENABLED 0 +# define CONFIG_TINYUSB_DFU_BUFSIZE 512 +#endif + +#define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE | OPT_MODE_FULL_SPEED +#define CFG_TUSB_OS OPT_OS_FREERTOS + +/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment. + * Tinyusb use follows macros to declare transferring memory so that they can be put + * into those specific section. + * e.g + * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") )) + * - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4))) + */ +#ifndef CFG_TUSB_MEM_SECTION +# define CFG_TUSB_MEM_SECTION +#endif + +#ifndef CFG_TUSB_MEM_ALIGN +# define CFG_TUSB_MEM_ALIGN TU_ATTR_ALIGNED(4) +#endif + +#ifndef CFG_TUD_ENDPOINT0_SIZE +#define CFG_TUD_ENDPOINT0_SIZE 64 +#endif + +// CDC FIFO size of TX and RX +#define CFG_TUD_CDC_RX_BUFSIZE CONFIG_TINYUSB_CDC_RX_BUFSIZE +#define CFG_TUD_CDC_TX_BUFSIZE CONFIG_TINYUSB_CDC_TX_BUFSIZE + +// MSC Buffer size of Device Mass storage +#define CFG_TUD_MSC_BUFSIZE CONFIG_TINYUSB_MSC_BUFSIZE + +// HID buffer size Should be sufficient to hold ID (if any) + Data +#define CFG_TUD_HID_BUFSIZE CONFIG_TINYUSB_HID_BUFSIZE + +// Number of BTH ISO alternatives +#define CFG_TUD_BTH_ISO_ALT_COUNT CONFIG_TINYUSB_BTH_ISO_ALT_COUNT + +#define CFG_TUD_DFU_XFER_BUFSIZE CONFIG_TINYUSB_DFU_BUFSIZE + +// Enabled device class driver +#define CFG_TUD_CDC CONFIG_TINYUSB_CDC_PORT_NUM +#define CFG_TUD_MSC CONFIG_TINYUSB_MSC_ENABLED +#define CFG_TUD_HID_KBD CONFIG_TINYUSB_HIDKEYBOARD_ENABLED +#define CFG_TUD_U2FHID CONFIG_TINYUSB_U2FHID_ENABLED +#define CFG_TUD_HID CONFIG_TINYUSB_HID_ENABLED +#define CFG_TUD_MIDI CONFIG_TINYUSB_MIDI_ENABLED +#define CFG_TUD_CUSTOM_CLASS CONFIG_TINYUSB_CUSTOM_CLASS_ENABLED +#define CFG_TUD_NET CONFIG_TINYUSB_NET_ENABLED +#define CFG_TUD_BTH CONFIG_TINYUSB_BTH_ENABLED +#define CFG_TUD_DFU CONFIG_TINYUSB_DFU_ENABLED + +/* TODO: will be removed if upstream feat: Add net xmit status cb for application can block to get it #1001*/ +__attribute__((weak)) void tud_network_idle_status_change_cb(bool idle); + +#ifdef __cplusplus +} +#endif diff --git a/components/tinyusb/additions/include/tusb_console.h b/components/tinyusb/additions/include/tusb_console.h new file mode 100644 index 0000000..530221d --- /dev/null +++ b/components/tinyusb/additions/include/tusb_console.h @@ -0,0 +1,41 @@ +// Copyright 2020 Espressif Systems (Shanghai) Co. Ltd. +// +// 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. + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include "esp_err.h" + +/** + * @brief Redirect output to the USB serial + * @param cdc_intf - interface number of TinyUSB's CDC + * + * @return esp_err_t - ESP_OK, ESP_FAIL or an error code + */ +esp_err_t esp_tusb_init_console(int cdc_intf); + +/** + * @brief Switch log to the default output + * @param cdc_intf - interface number of TinyUSB's CDC + * + * @return esp_err_t + */ +esp_err_t esp_tusb_deinit_console(int cdc_intf); + +#ifdef __cplusplus +} +#endif diff --git a/components/tinyusb/additions/include/tusb_hid.h b/components/tinyusb/additions/include/tusb_hid.h new file mode 100644 index 0000000..5a3b6ca --- /dev/null +++ b/components/tinyusb/additions/include/tusb_hid.h @@ -0,0 +1,239 @@ +// Copyright 2020-2021 Espressif Systems (Shanghai) Co. Ltd. +// +// 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. + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include "tusb.h" +#include "tinyusb.h" + + +/** + * @brief Report delta movement of mouse. + * + * @param x Current delta x movement of the mouse + * @param y Current delta y movement on the mouse + * @param vertical Current delta wheel movement on the mouse + * @param horizontal using AC Pan + */ +void tinyusb_hid_mouse_move_report(int8_t x, int8_t y, int8_t vertical, int8_t horizontal); + +/** + * @brief Report button click in the mouse, using bitmap here. + * eg. MOUSE_BUTTON_LEFT | MOUSE_BUTTON_RIGHT, if click left and right button at same time. + * + * @param buttons hid mouse button bit mask + */ +void tinyusb_hid_mouse_button_report(uint8_t buttons_map); + +/** + * @brief Report key press in the keyboard, using array here, contains six keys at most. + * + * @param keycode hid keyboard code array + */ +void tinyusb_hid_keyboard_report(uint8_t keycode[]); + +//--------------------------------------------------------------------+ +// HID MOUSE BUTTON BIT MASK +//--------------------------------------------------------------------+ +// MOUSE_BUTTON_LEFT = TU_BIT(0), ///< Left button +// MOUSE_BUTTON_RIGHT = TU_BIT(1), ///< Right button +// MOUSE_BUTTON_MIDDLE = TU_BIT(2), ///< Middle button +// MOUSE_BUTTON_BACKWARD = TU_BIT(3), ///< Backward button, +// MOUSE_BUTTON_FORWARD = TU_BIT(4), ///< Forward button, + + +//--------------------------------------------------------------------+ +// HID KEYCODE +//--------------------------------------------------------------------+ +// #define HID_KEY_NONE 0x00 +// #define HID_KEY_A 0x04 +// #define HID_KEY_B 0x05 +// #define HID_KEY_C 0x06 +// #define HID_KEY_D 0x07 +// #define HID_KEY_E 0x08 +// #define HID_KEY_F 0x09 +// #define HID_KEY_G 0x0A +// #define HID_KEY_H 0x0B +// #define HID_KEY_I 0x0C +// #define HID_KEY_J 0x0D +// #define HID_KEY_K 0x0E +// #define HID_KEY_L 0x0F +// #define HID_KEY_M 0x10 +// #define HID_KEY_N 0x11 +// #define HID_KEY_O 0x12 +// #define HID_KEY_P 0x13 +// #define HID_KEY_Q 0x14 +// #define HID_KEY_R 0x15 +// #define HID_KEY_S 0x16 +// #define HID_KEY_T 0x17 +// #define HID_KEY_U 0x18 +// #define HID_KEY_V 0x19 +// #define HID_KEY_W 0x1A +// #define HID_KEY_X 0x1B +// #define HID_KEY_Y 0x1C +// #define HID_KEY_Z 0x1D +// #define HID_KEY_1 0x1E +// #define HID_KEY_2 0x1F +// #define HID_KEY_3 0x20 +// #define HID_KEY_4 0x21 +// #define HID_KEY_5 0x22 +// #define HID_KEY_6 0x23 +// #define HID_KEY_7 0x24 +// #define HID_KEY_8 0x25 +// #define HID_KEY_9 0x26 +// #define HID_KEY_0 0x27 +// #define HID_KEY_ENTER 0x28 +// #define HID_KEY_ESCAPE 0x29 +// #define HID_KEY_BACKSPACE 0x2A +// #define HID_KEY_TAB 0x2B +// #define HID_KEY_SPACE 0x2C +// #define HID_KEY_MINUS 0x2D +// #define HID_KEY_EQUAL 0x2E +// #define HID_KEY_BRACKET_LEFT 0x2F +// #define HID_KEY_BRACKET_RIGHT 0x30 +// #define HID_KEY_BACKSLASH 0x31 +// #define HID_KEY_EUROPE_1 0x32 +// #define HID_KEY_SEMICOLON 0x33 +// #define HID_KEY_APOSTROPHE 0x34 +// #define HID_KEY_GRAVE 0x35 +// #define HID_KEY_COMMA 0x36 +// #define HID_KEY_PERIOD 0x37 +// #define HID_KEY_SLASH 0x38 +// #define HID_KEY_CAPS_LOCK 0x39 +// #define HID_KEY_F1 0x3A +// #define HID_KEY_F2 0x3B +// #define HID_KEY_F3 0x3C +// #define HID_KEY_F4 0x3D +// #define HID_KEY_F5 0x3E +// #define HID_KEY_F6 0x3F +// #define HID_KEY_F7 0x40 +// #define HID_KEY_F8 0x41 +// #define HID_KEY_F9 0x42 +// #define HID_KEY_F10 0x43 +// #define HID_KEY_F11 0x44 +// #define HID_KEY_F12 0x45 +// #define HID_KEY_PRINT_SCREEN 0x46 +// #define HID_KEY_SCROLL_LOCK 0x47 +// #define HID_KEY_PAUSE 0x48 +// #define HID_KEY_INSERT 0x49 +// #define HID_KEY_HOME 0x4A +// #define HID_KEY_PAGE_UP 0x4B +// #define HID_KEY_DELETE 0x4C +// #define HID_KEY_END 0x4D +// #define HID_KEY_PAGE_DOWN 0x4E +// #define HID_KEY_ARROW_RIGHT 0x4F +// #define HID_KEY_ARROW_LEFT 0x50 +// #define HID_KEY_ARROW_DOWN 0x51 +// #define HID_KEY_ARROW_UP 0x52 +// #define HID_KEY_NUM_LOCK 0x53 +// #define HID_KEY_KEYPAD_DIVIDE 0x54 +// #define HID_KEY_KEYPAD_MULTIPLY 0x55 +// #define HID_KEY_KEYPAD_SUBTRACT 0x56 +// #define HID_KEY_KEYPAD_ADD 0x57 +// #define HID_KEY_KEYPAD_ENTER 0x58 +// #define HID_KEY_KEYPAD_1 0x59 +// #define HID_KEY_KEYPAD_2 0x5A +// #define HID_KEY_KEYPAD_3 0x5B +// #define HID_KEY_KEYPAD_4 0x5C +// #define HID_KEY_KEYPAD_5 0x5D +// #define HID_KEY_KEYPAD_6 0x5E +// #define HID_KEY_KEYPAD_7 0x5F +// #define HID_KEY_KEYPAD_8 0x60 +// #define HID_KEY_KEYPAD_9 0x61 +// #define HID_KEY_KEYPAD_0 0x62 +// #define HID_KEY_KEYPAD_DECIMAL 0x63 +// #define HID_KEY_EUROPE_2 0x64 +// #define HID_KEY_APPLICATION 0x65 +// #define HID_KEY_POWER 0x66 +// #define HID_KEY_KEYPAD_EQUAL 0x67 +// #define HID_KEY_F13 0x68 +// #define HID_KEY_F14 0x69 +// #define HID_KEY_F15 0x6A +// #define HID_KEY_F16 0x6B +// #define HID_KEY_F17 0x6C +// #define HID_KEY_F18 0x6D +// #define HID_KEY_F19 0x6E +// #define HID_KEY_F20 0x6F +// #define HID_KEY_F21 0x70 +// #define HID_KEY_F22 0x71 +// #define HID_KEY_F23 0x72 +// #define HID_KEY_F24 0x73 +// #define HID_KEY_EXECUTE 0x74 +// #define HID_KEY_HELP 0x75 +// #define HID_KEY_MENU 0x76 +// #define HID_KEY_SELECT 0x77 +// #define HID_KEY_STOP 0x78 +// #define HID_KEY_AGAIN 0x79 +// #define HID_KEY_UNDO 0x7A +// #define HID_KEY_CUT 0x7B +// #define HID_KEY_COPY 0x7C +// #define HID_KEY_PASTE 0x7D +// #define HID_KEY_FIND 0x7E +// #define HID_KEY_MUTE 0x7F +// #define HID_KEY_VOLUME_UP 0x80 +// #define HID_KEY_VOLUME_DOWN 0x81 +// #define HID_KEY_LOCKING_CAPS_LOCK 0x82 +// #define HID_KEY_LOCKING_NUM_LOCK 0x83 +// #define HID_KEY_LOCKING_SCROLL_LOCK 0x84 +// #define HID_KEY_KEYPAD_COMMA 0x85 +// #define HID_KEY_KEYPAD_EQUAL_SIGN 0x86 +// #define HID_KEY_KANJI1 0x87 +// #define HID_KEY_KANJI2 0x88 +// #define HID_KEY_KANJI3 0x89 +// #define HID_KEY_KANJI4 0x8A +// #define HID_KEY_KANJI5 0x8B +// #define HID_KEY_KANJI6 0x8C +// #define HID_KEY_KANJI7 0x8D +// #define HID_KEY_KANJI8 0x8E +// #define HID_KEY_KANJI9 0x8F +// #define HID_KEY_LANG1 0x90 +// #define HID_KEY_LANG2 0x91 +// #define HID_KEY_LANG3 0x92 +// #define HID_KEY_LANG4 0x93 +// #define HID_KEY_LANG5 0x94 +// #define HID_KEY_LANG6 0x95 +// #define HID_KEY_LANG7 0x96 +// #define HID_KEY_LANG8 0x97 +// #define HID_KEY_LANG9 0x98 +// #define HID_KEY_ALTERNATE_ERASE 0x99 +// #define HID_KEY_SYSREQ_ATTENTION 0x9A +// #define HID_KEY_CANCEL 0x9B +// #define HID_KEY_CLEAR 0x9C +// #define HID_KEY_PRIOR 0x9D +// #define HID_KEY_RETURN 0x9E +// #define HID_KEY_SEPARATOR 0x9F +// #define HID_KEY_OUT 0xA0 +// #define HID_KEY_OPER 0xA1 +// #define HID_KEY_CLEAR_AGAIN 0xA2 +// #define HID_KEY_CRSEL_PROPS 0xA3 +// #define HID_KEY_EXSEL 0xA4 +// //RESERVED 0xA5-DF +// #define HID_KEY_CONTROL_LEFT 0xE0 +// #define HID_KEY_SHIFT_LEFT 0xE1 +// #define HID_KEY_ALT_LEFT 0xE2 +// #define HID_KEY_GUI_LEFT 0xE3 +// #define HID_KEY_CONTROL_RIGHT 0xE4 +// #define HID_KEY_SHIFT_RIGHT 0xE5 +// #define HID_KEY_ALT_RIGHT 0xE6 +// #define HID_KEY_GUI_RIGHT 0xE7 + + +#ifdef __cplusplus +} +#endif diff --git a/components/tinyusb/additions/include/tusb_msc.h b/components/tinyusb/additions/include/tusb_msc.h new file mode 100644 index 0000000..b6158e6 --- /dev/null +++ b/components/tinyusb/additions/include/tusb_msc.h @@ -0,0 +1,42 @@ +// Copyright 2020-2021 Espressif Systems (Shanghai) Co. Ltd. +// +// 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. + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include "tusb.h" +#include "tinyusb.h" + +/** + * @brief Configuration structure for MSC + */ +typedef struct { + uint8_t pdrv; /* Physical drive nmuber (0..) */ +} tinyusb_config_msc_t; + +/** + * @brief Initialize MSC Device. + * + * @param cfg - init configuration structure + * @return esp_err_t + */ +esp_err_t tusb_msc_init(const tinyusb_config_msc_t *cfg); + +#ifdef __cplusplus +} +#endif diff --git a/components/tinyusb/additions/include/tusb_net.h b/components/tinyusb/additions/include/tusb_net.h new file mode 100644 index 0000000..38055b2 --- /dev/null +++ b/components/tinyusb/additions/include/tusb_net.h @@ -0,0 +1,49 @@ +/** + * @copyright Copyright 2021 Espressif Systems (Shanghai) Co. Ltd. + * + * 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. + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include "esp_err.h" +#include "tinyusb.h" + +/** + * @brief Forward packets from Wi-Fi to USB. + * + * @param buffer - Data pointer + * + * @param len - Data length + * + * @return esp_err_t + */ +esp_err_t pkt_wifi2usb(void *buffer, uint16_t len, void *eb); + +/** + * @brief Initialize NET Device. + */ +void tusb_net_init(void); + +void ecm_close(void); + +void ecm_open(void); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/components/tinyusb/additions/include/tusb_tasks.h b/components/tinyusb/additions/include/tusb_tasks.h new file mode 100644 index 0000000..7f3209d --- /dev/null +++ b/components/tinyusb/additions/include/tusb_tasks.h @@ -0,0 +1,46 @@ +// Copyright 2020 Espressif Systems (Shanghai) PTE LTD +// +// 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. + +#pragma once + +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief This helper function creates and starts a task which wraps `tud_task()`. + * + * The wrapper function basically wraps tud_task and some log. + * Default parameters: stack size and priority as configured, argument = NULL, not pinned to any core. + * If you have more requirements for this task, you can create your own task which calls tud_task as the last step. + * + * @retval ESP_OK run tinyusb main task successfully + * @retval ESP_FAIL run tinyusb main task failed of internal error + * @retval ESP_ERR_INVALID_STATE tinyusb main task has been created before + */ +esp_err_t tusb_run_task(void); + +/** + * @brief This helper function stops and destroys the task created by `tusb_run_task()` + * + * @retval ESP_OK stop and destory tinyusb main task successfully + * @retval ESP_ERR_INVALID_STATE tinyusb main task hasn't been created yet + */ +esp_err_t tusb_stop_task(void); + +#ifdef __cplusplus +} +#endif diff --git a/drivers/tidal_usb/u2f_hid.h b/components/tinyusb/additions/include/u2f_hid.h similarity index 100% rename from drivers/tidal_usb/u2f_hid.h rename to components/tinyusb/additions/include/u2f_hid.h diff --git a/components/tinyusb/additions/include/vfs_tinyusb.h b/components/tinyusb/additions/include/vfs_tinyusb.h new file mode 100644 index 0000000..233b7f1 --- /dev/null +++ b/components/tinyusb/additions/include/vfs_tinyusb.h @@ -0,0 +1,42 @@ +// Copyright 2020 Espressif Systems (Shanghai) Co. Ltd. +// +// 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. + +#pragma once + +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Register TinyUSB CDC at VFS with path + * @param cdc_intf - interface number of TinyUSB's CDC + * @param path - path where the CDC will be registered, `/dev/tusb_cdc` will be used if left NULL. + * + * @return esp_err_t ESP_OK or ESP_FAIL + */ +esp_err_t esp_vfs_tusb_cdc_register(int cdc_intf, char const *path); + +/** + * @brief Unregister TinyUSB CDC from VFS + * @param path - path where the CDC will be unregistered if NULL will be used `/dev/tusb_cdc` + * + * @return esp_err_t ESP_OK or ESP_FAIL + */ +esp_err_t esp_vfs_tusb_cdc_unregister(char const *path); + +#ifdef __cplusplus +} +#endif diff --git a/components/tinyusb/additions/include_private/cdc.h b/components/tinyusb/additions/include_private/cdc.h new file mode 100644 index 0000000..b3aa870 --- /dev/null +++ b/components/tinyusb/additions/include_private/cdc.h @@ -0,0 +1,99 @@ +// Copyright 2020 Espressif Systems (Shanghai) Co. Ltd. +// +// 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. + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include "freertos/FreeRTOS.h" +#include "freertos/ringbuf.h" +#include "freertos/semphr.h" +#include "freertos/timers.h" +#include "tusb.h" +#include "tinyusb_types.h" + +/* CDC classification + ********************************************************************* */ +typedef enum { + TINYUSB_CDC_DATA = 0x00, +} cdc_data_sublcass_type_t; // CDC120 specification + +/* Note:other classification is represented in the file components\tinyusb\tinyusb\src\class\cdc\cdc.h */ + +/*********************************************************************** CDC classification*/ +/* Structs + ********************************************************************* */ +typedef struct { + tinyusb_usbdev_t usb_dev; /*!< USB device to set up */ + tusb_class_code_t cdc_class; /*!< CDC device class : Communications or Data device */ + union { + cdc_comm_sublcass_type_t comm_subclass; /*!< Communications device subclasses: AMC, ECM, etc. */ + cdc_data_sublcass_type_t data_subclass; /*!< Data device has only one subclass.*/ + } cdc_subclass; /*!< CDC device subclass according to Class Definitions for Communications Devices the CDC v.1.20 */ +} tinyusb_config_cdc_t; /*!< Main configuration structure of a CDC device */ + +typedef struct { + tinyusb_usbdev_t usb_dev; /*!< USB device used for the instance */ + tusb_class_code_t type; + union { + cdc_comm_sublcass_type_t comm_subclass; /*!< Communications device subclasses: AMC, ECM, etc. */ + cdc_data_sublcass_type_t data_subclass; /*!< Data device has only one subclass.*/ + } cdc_subclass; /*!< CDC device subclass according to Class Definitions for Communications Devices the CDC v.1.20 */ + void *subclass_obj; /*!< Dynamically allocated subclass specific object */ +} esp_tusb_cdc_t; +/*********************************************************************** Structs*/ +/* Functions + ********************************************************************* */ +/** + * @brief Initializing CDC basic object + * @param itf - number of a CDC object + * @param cfg - CDC configuration structure + * + * @return esp_err_t ESP_OK or ESP_FAIL + */ +esp_err_t tinyusb_cdc_init(int itf, const tinyusb_config_cdc_t *cfg); + + +/** + * @brief De-initializing CDC. Clean its objects + * @param itf - number of a CDC object + * @return esp_err_t ESP_OK, ESP_ERR_INVALID_ARG, ESP_ERR_INVALID_STATE + * + */ +esp_err_t tinyusb_cdc_deinit(int itf); + + +/** + * @brief Checks if the CDC initialized and ready to interaction + * + * @return true or false + */ +bool tinyusb_cdc_initialized(int itf); + + +/** + * @brief Return interface of a CDC device + * + * @param itf_num + * @return esp_tusb_cdc_t* pointer to the interface or (NULL) on error + */ +esp_tusb_cdc_t *tinyusb_cdc_get_intf(int itf_num); +/*********************************************************************** Functions*/ + +#ifdef __cplusplus +} +#endif diff --git a/components/tinyusb/additions/include_private/descriptors_control.h b/components/tinyusb/additions/include_private/descriptors_control.h new file mode 100644 index 0000000..370eb55 --- /dev/null +++ b/components/tinyusb/additions/include_private/descriptors_control.h @@ -0,0 +1,165 @@ +// Copyright 2020 Espressif Systems (Shanghai) PTE LTD +// +// 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. + +#pragma once + +#include +#include "usb_descriptors.h" + + +/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug. + * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC. + * + * Auto ProductID layout's Bitmap: + * [MSB] NET | VENDOR | MIDI | HID | MSC | CDC [LSB] + */ + +#ifdef __cplusplus +extern "C" { +#endif + +int32_t hid_mode; + + +//------------- EndPoint Descriptor -------------// +enum { + EPNUM_DEFAULT = 0, +# if CFG_TUD_BTH + EPNUM_BT_EVT, + EPNUM_BT_BULK_OUT, +# endif + +# if CFG_TUD_NET + EPNUM_NET_NOTIF, + EPNUM_NET_DATA, +# endif + +# if CFG_TUD_CDC + EPNUM_CDC_NOTIF, + EPNUM_CDC_DATA, +# endif + +# if CFG_TUD_MSC + EPNUM_MSC_DATA, +# endif + +# if CFG_TUD_HID_KBD + EPNUM_HID_DATA, +# endif +}; + +#if ((CFG_TUD_BTH * 2) + (CFG_TUD_NET * 2) + (CFG_TUD_CDC * 2) + CFG_TUD_MSC + CFG_TUD_HID) > 4 +#error "USB endpoint number not be more than 5" +#endif + +//------------- HID Report Descriptor -------------// +#if CFG_TUD_HID +enum { + REPORT_ID_KEYBOARD = 1, + REPORT_ID_MOUSE +}; +#endif + +//------------- Configuration Descriptor -------------// +enum { +# if CFG_TUD_BTH + ITF_NUM_BTH = 0, + ITF_NUM_BTH_DATA, +# endif + +# if CFG_TUD_NET + ITF_NUM_NET, + ITF_NUM_NET_DATA, +# endif + +# if CFG_TUD_CDC + ITF_NUM_CDC, + ITF_NUM_CDC_DATA, +# endif + +# if CFG_TUD_MSC + ITF_NUM_MSC, +# endif + +# if CFG_TUD_HID + ITF_NUM_HID, +# endif + +# if CFG_TUD_DFU + ITF_NUM_DFU, +# endif + ITF_NUM_TOTAL +}; + +//------------- STRID -------------// +enum { + STRID_LANGID = 0, + STRID_MANUFACTURER, + STRID_PRODUCT, + STRID_SERIAL, +#if CFG_TUD_CDC + STRID_CDC_INTERFACE, +#endif +#if CFG_TUD_NET + STRID_NET_INTERFACE, + STRID_MAC, +#endif +#if CFG_TUD_MSC + STRID_MSC_INTERFACE, +#endif +#if CFG_TUD_HID_KBD + STRID_HID_INTERFACE, +#endif +#if CFG_TUD_U2FHID + STRID_HID_INTERFACE_2, +#endif +#if CFG_TUD_BTH + STRID_BTH_INTERFACE, +#endif +#if CFG_TUD_DFU + STRID_DFU_INTERFACE, +#endif +}; + +#define DFU_ALT_COUNT 2 +#define TUD_DFU_DESC_LEN(_alt_count) (9 + (_alt_count) * 9) + +enum { + TUSB_DESC_TOTAL_LEN = TUD_CONFIG_DESC_LEN + + TUD_CDC_DESC_LEN * CFG_TUD_CDC + + TUD_RNDIS_DESC_LEN * CFG_TUD_NET + + TUD_MSC_DESC_LEN * CFG_TUD_MSC + + TUD_HID_DESC_LEN * (CFG_TUD_HID_KBD | CFG_TUD_U2FHID) + + TUD_BTH_DESC_LEN * CFG_TUD_BTH, + + ALT_CONFIG_TOTAL_LEN = TUD_CONFIG_DESC_LEN + + TUD_CDC_ECM_DESC_LEN * CFG_TUD_NET + + TUD_CDC_DESC_LEN * CFG_TUD_CDC + + TUD_MSC_DESC_LEN * CFG_TUD_MSC + + TUD_HID_DESC_LEN * CFG_TUD_HID_KBD + + TUD_HID_DESC_LEN * CFG_TUD_U2FHID + + TUD_BTH_DESC_LEN * CFG_TUD_BTH + + TUD_DFU_DESC_LEN(DFU_ALT_COUNT) * CFG_TUD_DFU +}; + +bool tusb_desc_set; +void tusb_set_descriptor(tusb_desc_device_t *desc, const char **str_desc); +void tusb_set_config_descriptor(const uint8_t *config_desc); +tusb_desc_device_t *tusb_get_active_desc(void); +char **tusb_get_active_str_desc(void); +void tusb_clear_descriptor(void); + +#ifdef __cplusplus +} +#endif diff --git a/components/tinyusb/additions/include_private/usb_descriptors.h b/components/tinyusb/additions/include_private/usb_descriptors.h new file mode 100644 index 0000000..76c2b54 --- /dev/null +++ b/components/tinyusb/additions/include_private/usb_descriptors.h @@ -0,0 +1,34 @@ +// Copyright 2020 Espressif Systems (Shanghai) PTE LTD +// +// 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. + +#pragma once + +#include "tusb.h" +#include "tinyusb_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define _PID_MAP(itf, n) ((CFG_TUD_##itf) << (n)) + +extern tusb_desc_device_t descriptor_tinyusb; +extern tusb_desc_strarray_device_t descriptor_str_tinyusb; + +extern tusb_desc_device_t descriptor_kconfig; +extern tusb_desc_strarray_device_t descriptor_str_kconfig; + +#ifdef __cplusplus +} +#endif diff --git a/components/tinyusb/additions/src/cdc.c b/components/tinyusb/additions/src/cdc.c new file mode 100644 index 0000000..957ac7b --- /dev/null +++ b/components/tinyusb/additions/src/cdc.c @@ -0,0 +1,162 @@ +// Copyright 2020 Espressif Systems (Shanghai) Co. Ltd. +// +// 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 "esp_check.h" +#include "esp_err.h" +#include "esp_log.h" +#include "tusb.h" +#include "cdc.h" +#include "tusb_cdc_acm.h" +#include "sdkconfig.h" + +static const char *TAG = "tusb_cdc"; + +#define CDC_INTF_NUM CFG_TUD_CDC // number of cdc blocks + +static esp_tusb_cdc_t *cdc_obj[CDC_INTF_NUM] = {}; + +/* Common CDC functions + ********************************************************************* */ +bool tinyusb_cdc_initialized(int itf) +{ + return (cdc_obj[itf] != NULL); +} + +static esp_err_t cdc_interface_check(int itf) +{ + if (tinyusb_cdc_initialized(itf)) { + return ESP_OK; + } else { + ESP_LOGE(TAG, "Interface %d is not initialized. Use `tinyusb_cdc_init` for initialization", itf); + return ESP_ERR_INVALID_STATE; + } +} + +/** + * @brief + * + * @param itf + * @param expected_inited + * @param expected_type use -1 if you don't care + * @return esp_err_t + */ +static esp_err_t cdc_obj_check(int itf, bool expected_inited, tusb_class_code_t expected_type) +{ + bool inited = (cdc_obj[itf] != NULL); + if (expected_inited != inited) { + ESP_LOGE(TAG, "Wrong state of the interface. Expected state: %s", + expected_inited ? "initialized" : "not initialized"); + return ESP_ERR_INVALID_STATE; + } + if (inited && (expected_type != -1) && !(cdc_obj[itf]->type == expected_type)) { + ESP_LOGE(TAG, "Wrong type of the interface. Should be : 0x%x (tusb_class_code_t)", expected_type); + return ESP_ERR_INVALID_ARG; + } + return ESP_OK; +} + +esp_tusb_cdc_t *tinyusb_cdc_get_intf(int itf_num) +{ + if (cdc_interface_check(itf_num) != ESP_OK) { + return NULL; + } + return cdc_obj[itf_num]; +} + +/*********************************************************************** Common CDC functions*/ +/* CDC class funcs + ********************************************************************* */ +static esp_err_t tusb_cdc_comm_init(int itf) +{ + ESP_RETURN_ON_ERROR(cdc_obj_check(itf, false, -1), TAG, "cdc_obj_check failed"); + cdc_obj[itf] = calloc(1, sizeof(esp_tusb_cdc_t)); + if (cdc_obj[itf] != NULL) { + cdc_obj[itf]->type = TUSB_CLASS_CDC; + ESP_LOGD(TAG, "CDC Comm class initialized"); + return ESP_OK; + } else { + ESP_LOGE(TAG, "CDC Comm initialization error"); + return ESP_FAIL; + } +} + +static esp_err_t tusb_cdc_deinit_comm(int itf) +{ + ESP_RETURN_ON_ERROR(cdc_obj_check(itf, true, TUSB_CLASS_CDC), TAG, "cdc_obj_check failed"); + free(cdc_obj[itf]); + cdc_obj[itf] = NULL; + return ESP_OK; +} + +static esp_err_t tusb_cdc_data_init(int itf) +{ + ESP_RETURN_ON_ERROR(cdc_obj_check(itf, false, TUSB_CLASS_CDC_DATA), TAG, "cdc_obj_check failed"); + cdc_obj[itf] = calloc(1, sizeof(esp_tusb_cdc_t)); + if (cdc_obj[itf] != NULL) { + cdc_obj[itf]->type = TUSB_CLASS_CDC_DATA; + ESP_LOGD(TAG, "CDC Data class initialized"); + return ESP_OK; + } else { + ESP_LOGE(TAG, "CDC Data initialization error"); + return ESP_FAIL; + } +} + +static esp_err_t tusb_cdc_deinit_data(int itf) +{ + ESP_RETURN_ON_ERROR(cdc_obj_check(itf, true, TUSB_CLASS_CDC_DATA), TAG, "cdc_obj_check failed"); + free(cdc_obj[itf]); + cdc_obj[itf] = NULL; + return ESP_OK; +} +/*********************************************************************** CDC class funcs*/ +/* CDC initialization + ********************************************************************* */ +esp_err_t tinyusb_cdc_init(int itf, const tinyusb_config_cdc_t *cfg) +{ + ESP_LOGD(TAG, "CDC initialization..."); + if (itf >= TINYUSB_CDC_TOTAL) { + ESP_LOGE(TAG, "There is not CDC no.%d", itf); + return ESP_ERR_INVALID_ARG; + } + if (cfg->cdc_class == TUSB_CLASS_CDC) { + ESP_RETURN_ON_ERROR(tusb_cdc_comm_init(itf), TAG, "tusb_cdc_comm_init failed"); + cdc_obj[itf]->cdc_subclass.comm_subclass = cfg->cdc_subclass.comm_subclass; + } else { + ESP_RETURN_ON_ERROR(tusb_cdc_data_init(itf), TAG, "tusb_cdc_data_init failed"); + cdc_obj[itf]->cdc_subclass.data_subclass = cfg->cdc_subclass.data_subclass; + } + cdc_obj[itf]->usb_dev = cfg->usb_dev; + return ESP_OK; +} + + +esp_err_t tinyusb_cdc_deinit(int itf) +{ + if (itf >= TINYUSB_CDC_TOTAL) { + ESP_LOGE(TAG, "There is not CDC no.%d", itf); + return ESP_ERR_INVALID_ARG; + } + if (cdc_obj[itf]->type == TUSB_CLASS_CDC) { + ESP_RETURN_ON_ERROR(tusb_cdc_deinit_comm(itf), TAG, "tusb_cdc_deinit_comm failed"); + } else if (cdc_obj[itf]->type == TUSB_CLASS_CDC_DATA) { + ESP_RETURN_ON_ERROR(tusb_cdc_deinit_data(itf), TAG, "tusb_cdc_deinit_data failed"); + } else { + return ESP_ERR_INVALID_ARG; + } + ESP_LOGD(TAG, "De-initialized"); + return ESP_OK; +} +/*********************************************************************** CDC initialization*/ diff --git a/components/tinyusb/additions/src/descriptors_control.c b/components/tinyusb/additions/src/descriptors_control.c new file mode 100644 index 0000000..5b7b04c --- /dev/null +++ b/components/tinyusb/additions/src/descriptors_control.c @@ -0,0 +1,347 @@ +// Copyright 2020 Espressif Systems (Shanghai) PTE LTD +// +// 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 "esp_log.h" +#include "descriptors_control.h" +#include "dfu_device.h" +#include "u2f_hid.h" +#include "nvs.h" + + +#define TILDA_REPORT_DESC_U2F(...) \ + HID_USAGE_PAGE_N ( FIDO_USAGE_PAGE, 2 ) ,\ + HID_USAGE ( FIDO_USAGE_U2FHID ) ,\ + HID_COLLECTION ( HID_COLLECTION_APPLICATION ) ,\ + /* Report ID if any */\ + __VA_ARGS__ \ + HID_USAGE ( FIDO_USAGE_DATA_IN ) ,\ + HID_LOGICAL_MIN ( 0 ) ,\ + HID_LOGICAL_MAX_N ( 0xff, 2 ) ,\ + HID_REPORT_COUNT ( 64 ) ,\ + HID_REPORT_SIZE ( 8 ) ,\ + HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\ + HID_USAGE ( FIDO_USAGE_DATA_OUT ) ,\ + HID_LOGICAL_MIN ( 0 ) ,\ + HID_LOGICAL_MAX_N ( 0xff, 2 ) ,\ + HID_REPORT_COUNT ( 64 ) ,\ + HID_REPORT_SIZE ( 8 ) ,\ + HID_OUTPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\ + HID_COLLECTION_END \ + + +static const char *TAG = "tusb_desc"; +static tusb_desc_device_t s_descriptor; +static char *s_str_descriptor[USB_STRING_DESCRIPTOR_ARRAY_SIZE]; +static uint8_t *s_config_descriptor = NULL; +#define MAX_DESC_BUF_SIZE 32 + +#if CFG_TUD_U2FHID //HID Report Descriptor +uint8_t const desc_hid_report_2[] = { + TILDA_REPORT_DESC_U2F() +}; +#endif +#if CFG_TUD_HID //HID Report Descriptor +uint8_t const desc_hid_report[] = { + TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(REPORT_ID_KEYBOARD)), + TUD_HID_REPORT_DESC_MOUSE(HID_REPORT_ID(REPORT_ID_MOUSE)) +}; +#endif + +#define FUNC_ATTRS (DFU_ATTR_CAN_UPLOAD | DFU_ATTR_CAN_DOWNLOAD | DFU_ATTR_MANIFESTATION_TOLERANT) + +int32_t hid_mode = 0; + +uint8_t const desc_configuration[] = { +#if CONFIG_TINYUSB_NET_ECM + // Config number, interface count, string index, total length, attribute, power in mA + TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, ALT_CONFIG_TOTAL_LEN, 0, 100), +#else + // Config number, interface count, string index, total length, attribute, power in mA + TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, TUSB_DESC_TOTAL_LEN, 0, 500), +#endif +#if CFG_TUD_BTH + // BT Primary controller descriptor + // Interface number, string index, attributes, event endpoint, event endpoint size, interval, data in, data out, data endpoint size, iso endpoint sizes + TUD_BTH_DESCRIPTOR(ITF_NUM_BTH, 0 /* STRID_BTH_INTERFACE */, (0x80 | EPNUM_BT_EVT), 16, 1, (0x80 | EPNUM_BT_BULK_OUT), EPNUM_BT_BULK_OUT, 64, 0, 9, 17, 25, 33, 49), +#endif +#if CFG_TUD_NET +#if CONFIG_TINYUSB_NET_ECM + // Interface number, description string index, MAC address string index, EP notification address and size, EP data address (out, in), and size, max segment size. + TUD_CDC_ECM_DESCRIPTOR(ITF_NUM_NET, STRID_NET_INTERFACE, STRID_MAC, (0x80 | EPNUM_NET_NOTIF), 64, EPNUM_NET_DATA, (0x80 | EPNUM_NET_DATA), CFG_TUD_NET_ENDPOINT_SIZE, CFG_TUD_NET_MTU), +#elif CONFIG_TINYUSB_NET_RNDIS + // Interface number, string index, EP notification address and size, EP data address (out, in) and size. + TUD_RNDIS_DESCRIPTOR(ITF_NUM_NET, STRID_NET_INTERFACE, (0x80 | EPNUM_NET_NOTIF), 8, EPNUM_NET_DATA, (0x80 | EPNUM_NET_DATA), CFG_TUD_NET_ENDPOINT_SIZE), +#endif +#endif +#if CFG_TUD_CDC + // Interface number, string index, EP notification address and size, EP data address (out, in) and size. + TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, STRID_CDC_INTERFACE, (0x80 | EPNUM_CDC_NOTIF), 8, EPNUM_CDC_DATA, (0x80 | EPNUM_CDC_DATA), 64), +#endif +#if CFG_TUD_MSC + // Interface number, string index, EP Out & EP In address, EP size + TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, STRID_MSC_INTERFACE, EPNUM_MSC_DATA, (0x80 | EPNUM_MSC_DATA), 64), // highspeed 512 +#endif +#if CFG_TUD_HID_KBD + // Interface number, string index, protocol, report descriptor len, EP In address, size & polling interval + TUD_HID_DESCRIPTOR(ITF_NUM_HID, STRID_HID_INTERFACE, HID_PROTOCOL_KEYBOARD, sizeof(desc_hid_report), (0x80 | EPNUM_HID_DATA), 8, 10), +#endif + +#if CFG_TUD_DFU + // Interface number, Alternate count, starting string index, attributes, detach timeout, transfer size + TUD_DFU_DESCRIPTOR(ITF_NUM_DFU, DFU_ALT_COUNT, STRID_DFU_INTERFACE, FUNC_ATTRS, 1000, CFG_TUD_DFU_XFER_BUFSIZE), +#endif + +}; + + +uint8_t const desc_configuration_2[] = { +#if CONFIG_TINYUSB_NET_ECM + // Config number, interface count, string index, total length, attribute, power in mA + TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, ALT_CONFIG_TOTAL_LEN, 0, 100), +#else + // Config number, interface count, string index, total length, attribute, power in mA + TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, TUSB_DESC_TOTAL_LEN, 0, 500), +#endif +#if CFG_TUD_BTH + // BT Primary controller descriptor + // Interface number, string index, attributes, event endpoint, event endpoint size, interval, data in, data out, data endpoint size, iso endpoint sizes + TUD_BTH_DESCRIPTOR(ITF_NUM_BTH, 0 /* STRID_BTH_INTERFACE */, (0x80 | EPNUM_BT_EVT), 16, 1, (0x80 | EPNUM_BT_BULK_OUT), EPNUM_BT_BULK_OUT, 64, 0, 9, 17, 25, 33, 49), +#endif +#if CFG_TUD_NET +#if CONFIG_TINYUSB_NET_ECM + // Interface number, description string index, MAC address string index, EP notification address and size, EP data address (out, in), and size, max segment size. + TUD_CDC_ECM_DESCRIPTOR(ITF_NUM_NET, STRID_NET_INTERFACE, STRID_MAC, (0x80 | EPNUM_NET_NOTIF), 64, EPNUM_NET_DATA, (0x80 | EPNUM_NET_DATA), CFG_TUD_NET_ENDPOINT_SIZE, CFG_TUD_NET_MTU), +#elif CONFIG_TINYUSB_NET_RNDIS + // Interface number, string index, EP notification address and size, EP data address (out, in) and size. + TUD_RNDIS_DESCRIPTOR(ITF_NUM_NET, STRID_NET_INTERFACE, (0x80 | EPNUM_NET_NOTIF), 8, EPNUM_NET_DATA, (0x80 | EPNUM_NET_DATA), CFG_TUD_NET_ENDPOINT_SIZE), +#endif +#endif +#if CFG_TUD_CDC + // Interface number, string index, EP notification address and size, EP data address (out, in) and size. + TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, STRID_CDC_INTERFACE, (0x80 | EPNUM_CDC_NOTIF), 8, EPNUM_CDC_DATA, (0x80 | EPNUM_CDC_DATA), 64), +#endif +#if CFG_TUD_MSC + // Interface number, string index, EP Out & EP In address, EP size + TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, STRID_MSC_INTERFACE, EPNUM_MSC_DATA, (0x80 | EPNUM_MSC_DATA), 64), // highspeed 512 +#endif +#if CFG_TUD_U2FHID + TUD_HID_DESCRIPTOR(ITF_NUM_HID, STRID_HID_INTERFACE, HID_PROTOCOL_NONE, sizeof(desc_hid_report_2), (0x80 | EPNUM_HID_DATA), 8, 10), +#endif + +#if CFG_TUD_DFU + // Interface number, Alternate count, starting string index, attributes, detach timeout, transfer size + TUD_DFU_DESCRIPTOR(ITF_NUM_DFU, DFU_ALT_COUNT, STRID_DFU_INTERFACE, FUNC_ATTRS, 1000, CFG_TUD_DFU_XFER_BUFSIZE), +#endif + +}; + + +// ============================================================================= +// CALLBACKS +// ============================================================================= + +/** + * @brief Invoked when received GET DEVICE DESCRIPTOR. + * Application returns pointer to descriptor + * + * @return uint8_t const* + */ +uint8_t const *tud_descriptor_device_cb(void) +{ + return (uint8_t const *)&s_descriptor; +} + +/** + * @brief Invoked when received GET CONFIGURATION DESCRIPTOR. + * Descriptor contents must exist long enough for transfer to complete + * + * @param index + * @return uint8_t const* Application return pointer to descriptor + */ +uint8_t const *tud_descriptor_configuration_cb(uint8_t index) +{ + (void)index; // for multiple configurations + return s_config_descriptor; +} + +static uint16_t _desc_str[MAX_DESC_BUF_SIZE]; + +// Invoked when received GET STRING DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete +uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) +{ + (void) langid; + + uint8_t chr_count; + + if ( index == 0) { + memcpy(&_desc_str[1], s_str_descriptor[0], 2); + chr_count = 1; + } +#if CONFIG_TINYUSB_NET_ECM + else if (STRID_MAC == index) + { + // Convert MAC address into UTF-16 + + for (unsigned i=0; i> 4) & 0xf]; + _desc_str[1+chr_count++] = "0123456789ABCDEF"[(tud_network_mac_address[i] >> 0) & 0xf]; + } + } +#endif + else + { + // Convert ASCII string into UTF-16 + + if ( index >= sizeof(s_str_descriptor) / sizeof(s_str_descriptor[0]) ) { + return NULL; + } + + const char *str = s_str_descriptor[index]; + + // Cap at max char + chr_count = strlen(str); + if ( chr_count > MAX_DESC_BUF_SIZE - 1 ) { + chr_count = MAX_DESC_BUF_SIZE - 1; + } + + for (uint8_t i = 0; i < chr_count; i++) { + _desc_str[1 + i] = str[i]; + } + } + + // first byte is length (including header), second byte is string type + _desc_str[0] = (TUSB_DESC_STRING << 8 ) | (2 * chr_count + 2); + + return _desc_str; +} + +/** + * @brief Invoked when received GET HID REPORT DESCRIPTOR + * Application returns pointer to descriptor. Descriptor contents must exist + * long enough for transfer to complete + * + * @return uint8_t const* + */ +#if CFG_TUD_HID +uint8_t const *tud_hid_descriptor_report_cb(uint8_t itf) +{ + ESP_LOGW(TAG, "Getting descriptor for interface %d", itf); + #if CFG_TUD_HID_KBD + if (hid_mode == 0) { + return desc_hid_report; + } + #endif + #if CFG_TUD_U2FHID + if (hid_mode == 1) { + return desc_hid_report_2; + } + #endif + ESP_LOGE(TAG, "Unknown descriptor %d", itf); + return NULL; +} +#endif + +// ============================================================================= +// Driver functions +// ============================================================================= + +void tusb_set_descriptor(tusb_desc_device_t *dev_desc, const char **str_desc) +{ + ESP_LOGI(TAG, "\n" + "┌─────────────────────────────────┐\n" + "│ USB Device Descriptor Summary │\n" + "├───────────────────┬─────────────┤\n" + "│bDeviceClass │ %-4u │\n" + "├───────────────────┼─────────────┤\n" + "│bDeviceSubClass │ %-4u │\n" + "├───────────────────┼─────────────┤\n" + "│bDeviceProtocol │ %-4u │\n" + "├───────────────────┼─────────────┤\n" + "│bMaxPacketSize0 │ %-4u │\n" + "├───────────────────┼─────────────┤\n" + "│idVendor │ %-#10x │\n" + "├───────────────────┼─────────────┤\n" + "│idProduct │ %-#10x │\n" + "├───────────────────┼─────────────┤\n" + "│bcdDevice │ %-#10x │\n" + "├───────────────────┼─────────────┤\n" + "│iManufacturer │ %-#10x │\n" + "├───────────────────┼─────────────┤\n" + "│iProduct │ %-#10x │\n" + "├───────────────────┼─────────────┤\n" + "│iSerialNumber │ %-#10x │\n" + "├───────────────────┼─────────────┤\n" + "│bNumConfigurations │ %-#10x │\n" + "└───────────────────┴─────────────┘", + dev_desc->bDeviceClass, dev_desc->bDeviceSubClass, + dev_desc->bDeviceProtocol, dev_desc->bMaxPacketSize0, + dev_desc->idVendor, dev_desc->idProduct, dev_desc->bcdDevice, + dev_desc->iManufacturer, dev_desc->iProduct, dev_desc->iSerialNumber, + dev_desc->bNumConfigurations); + s_descriptor = *dev_desc; + + if (str_desc != NULL) { + memcpy(s_str_descriptor, str_desc, + sizeof(s_str_descriptor[0])*USB_STRING_DESCRIPTOR_ARRAY_SIZE); + } + tusb_desc_set = true; +} + +void tusb_set_config_descriptor(const uint8_t *config_desc) +{ + size_t length = 0; + const uint8_t *config_descriptor = NULL; + + nvs_handle_t h = 0; + esp_err_t err = nvs_open("tidal", NVS_READONLY, &h); + hid_mode = 1; + err = nvs_get_i32(h, "enable_u2f", &hid_mode); + nvs_close(h); + if (err != ESP_OK) { + hid_mode = 1; + } + + if (hid_mode == 0) { + config_descriptor = desc_configuration; + ESP_LOGI(TAG, "using keyboard descriptors"); + } else { + config_descriptor = desc_configuration_2; + ESP_LOGI(TAG, "using u2fhid descriptors"); + } + length = (config_descriptor[3]<<8) + config_descriptor[2]; + ESP_LOGI(TAG, "config desc size=%d", length); + s_config_descriptor = realloc(s_config_descriptor, length); + memcpy(s_config_descriptor, config_descriptor, length); +} + +tusb_desc_device_t *tusb_get_active_desc(void) +{ + return &s_descriptor; +} + +char **tusb_get_active_str_desc(void) +{ + return s_str_descriptor; +} + +void tusb_clear_descriptor(void) +{ + memset(&s_descriptor, 0, sizeof(s_descriptor)); + memset(&s_str_descriptor, 0, sizeof(s_str_descriptor)); + free(s_config_descriptor); + s_config_descriptor = NULL; + tusb_desc_set = false; +} diff --git a/components/tinyusb/additions/src/tinyusb.c b/components/tinyusb/additions/src/tinyusb.c new file mode 100644 index 0000000..2363ae3 --- /dev/null +++ b/components/tinyusb/additions/src/tinyusb.c @@ -0,0 +1,85 @@ +// Copyright 2020 Espressif Systems (Shanghai) PTE LTD +// +// 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 "sdkconfig.h" +#include "driver/gpio.h" +#include "driver/periph_ctrl.h" +#include "esp_log.h" +#include "esp_check.h" +#include "esp_rom_gpio.h" +#include "hal/gpio_ll.h" +#include "hal/usb_hal.h" +#include "soc/gpio_periph.h" +#include "soc/usb_periph.h" +#include "tinyusb.h" +#include "descriptors_control.h" +#include "tusb.h" +#include "tusb_tasks.h" + +const static char *TAG = "TinyUSB"; + +static void configure_pins(usb_hal_context_t *usb) +{ + /* usb_periph_iopins currently configures USB_OTG as USB Device. + * Introduce additional parameters in usb_hal_context_t when adding support + * for USB Host. + */ + for (const usb_iopin_dsc_t *iopin = usb_periph_iopins; iopin->pin != -1; ++iopin) { + if ((usb->use_external_phy) || (iopin->ext_phy_only == 0)) { + esp_rom_gpio_pad_select_gpio(iopin->pin); + if (iopin->is_output) { + esp_rom_gpio_connect_out_signal(iopin->pin, iopin->func, false, false); + } else { + esp_rom_gpio_connect_in_signal(iopin->pin, iopin->func, false); + if ((iopin->pin != GPIO_FUNC_IN_LOW) && (iopin->pin != GPIO_FUNC_IN_HIGH)) { + gpio_ll_input_enable(&GPIO, iopin->pin); + } + } + esp_rom_gpio_pad_unhold(iopin->pin); + } + } + if (!usb->use_external_phy) { + gpio_set_drive_capability(USBPHY_DM_NUM, GPIO_DRIVE_CAP_3); + gpio_set_drive_capability(USBPHY_DP_NUM, GPIO_DRIVE_CAP_3); + } +} + +esp_err_t tinyusb_driver_install(const tinyusb_config_t *config) +{ + tusb_desc_device_t *dev_descriptor; + const char **string_descriptor; + ESP_RETURN_ON_FALSE(config, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + // Enable APB CLK to USB peripheral + periph_module_enable(PERIPH_USB_MODULE); + periph_module_reset(PERIPH_USB_MODULE); + // Initialize HAL layer + usb_hal_context_t hal = { + .use_external_phy = config->external_phy + }; + usb_hal_init(&hal); + configure_pins(&hal); + + dev_descriptor = config->descriptor ? config->descriptor : &descriptor_kconfig; + string_descriptor = config->string_descriptor ? config->string_descriptor : descriptor_str_kconfig; + + tusb_set_config_descriptor(config->config_descriptor); //if NULL, using default descriptor config by menuconfig + tusb_set_descriptor(dev_descriptor, string_descriptor); + + ESP_RETURN_ON_FALSE(tusb_init(), ESP_FAIL, TAG, "Init TinyUSB stack failed"); +#if !CONFIG_TINYUSB_NO_DEFAULT_TASK + ESP_RETURN_ON_ERROR(tusb_run_task(), TAG, "Run TinyUSB task failed"); +#endif + ESP_LOGI(TAG, "TinyUSB Driver installed"); + return ESP_OK; +} diff --git a/components/tinyusb/additions/src/tusb_bth.c b/components/tinyusb/additions/src/tusb_bth.c new file mode 100644 index 0000000..066a144 --- /dev/null +++ b/components/tinyusb/additions/src/tusb_bth.c @@ -0,0 +1,212 @@ +/** + * @copyright Copyright 2021 Espressif Systems (Shanghai) Co. Ltd. + * + * 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 "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include "esp_bt.h" + +#include "tinyusb.h" +#include "tusb_bth.h" + +#define BUFFER_SIZE_MAX 256 +#define LE_READ_BUFF_SIZE 0x2002 +#define HCI_H4_CMD_PREAMBLE_SIZE (4) +#define UINT16_TO_STREAM(p, u16) {*(p)++ = (uint8_t)(u16); *(p)++ = (uint8_t)((u16) >> 8);} +#define UINT8_TO_STREAM(p, u8) {*(p)++ = (uint8_t)(u8);} + +static const char *TAG = "tusb_bth"; +static uint16_t acl_buf_size_max = 0; +uint8_t * p_acl_buf = NULL; + +void ble_controller_init(void) { + esp_err_t ret; + esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); + + if ((ret = esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT)) != ESP_OK) { + ESP_LOGI(TAG, "Bluetooth controller release classic bt memory failed: %s", esp_err_to_name(ret)); + return; + } + + if ((ret = esp_bt_controller_init(&bt_cfg)) != ESP_OK) { + ESP_LOGI(TAG, "Bluetooth controller initialize failed: %s", esp_err_to_name(ret)); + return; + } + + if ((ret = esp_bt_controller_enable(ESP_BT_MODE_BLE)) != ESP_OK) { + ESP_LOGI(TAG, "Bluetooth controller enable failed: %s", esp_err_to_name(ret)); + return; + } +} + +/* + * @brief: BT controller callback function, used to notify the upper layer that + * controller is ready to receive command + */ +static void controller_rcv_pkt_ready(void) +{ + //TODO +} + +/* + * @brief: BT controller callback function, to transfer data packet to upper + * controller is ready to receive command + */ +static int host_rcv_pkt(uint8_t *data, uint16_t len) +{ + uint16_t act_len = len - 1; + + if(data[0] == HCIT_TYPE_EVENT) { // event data from controller + uint8_t *hci_buf = (uint8_t *)malloc(len); + memcpy(hci_buf, data +1 , act_len); + ESP_LOGI(TAG, "evt_data from controller, evt_data_length: %d :", act_len); + tud_bt_event_send(hci_buf, act_len); + free(hci_buf); + } else if(data[0] == HCIT_TYPE_ACL_DATA) { // acl data from controller + uint8_t *hci_acl_buf_contr = (uint8_t *)malloc(len); + memcpy(hci_acl_buf_contr, data +1 , act_len); + ESP_LOGI(TAG, "acl_data from controller, acl_data_length: %d :", act_len); + tud_bt_acl_data_send(hci_acl_buf_contr, act_len); + free(hci_acl_buf_contr); + } + return 0; +} + +static esp_vhci_host_callback_t vhci_host_cb = { + controller_rcv_pkt_ready, + host_rcv_pkt +}; + +static int host_rcv_pkt_test (uint8_t *data, uint16_t len) { + + ESP_LOGI(TAG, "host_rcv_pkt_test evt_data_length: %d :", len); + if(data[1] == 0x0e && data[2] == 0x07 + && data[4] == 0x02 && data[5] == 0x20) { + // LE Read Buffer size command complete event + uint16_t* size_p = NULL; + uint16_t cmd_value; + size_p = (uint8_t*)(&cmd_value); + *size_p = data[7]; + *(size_p+1) = data[8]; + acl_buf_size_max = *size_p; + ESP_LOGI(TAG, "acl_buf_size_max: %d", acl_buf_size_max); + } + if (!acl_buf_size_max) { + acl_buf_size_max = BUFFER_SIZE_MAX; + } + + p_acl_buf = (uint8_t *)malloc(acl_buf_size_max + 1); + esp_vhci_host_register_callback(&vhci_host_cb); + return 0; +} + +uint16_t make_cmd_le_read_buff_size(uint8_t *buf) +{ + UINT8_TO_STREAM (buf, HCIT_TYPE_COMMAND); + UINT16_TO_STREAM (buf, LE_READ_BUFF_SIZE); + UINT8_TO_STREAM (buf, 0); + return HCI_H4_CMD_PREAMBLE_SIZE; +} + +static esp_vhci_host_callback_t vhci_host_cb_test = { + controller_rcv_pkt_ready, + host_rcv_pkt_test +}; + +void tusb_bth_init(void) +{ + ble_controller_init(); + // register vhci_host_cb_test, test le read buffer size + esp_vhci_host_register_callback(&vhci_host_cb_test); + uint8_t buf[6]; + uint16_t sz = make_cmd_le_read_buff_size(buf); + esp_vhci_host_send_packet(buf, sz); +} + +//--------------------------------------------------------------------+ +// tinyusb callbacks +//--------------------------------------------------------------------+ + +// Invoked when HCI command was received over USB from Bluetooth host. +// Detailed format is described in Bluetooth core specification Vol 2, +// Part E, 5.4.1. +// Length of the command is from 3 bytes (2 bytes for OpCode, +// 1 byte for parameter total length) to 258. +void tud_bt_hci_cmd_cb(void *hci_cmd, size_t cmd_len) +{ + uint8_t *hci_cmd_buf = (uint8_t *)malloc(cmd_len + 1); + + hci_cmd_buf[0] = HCIT_TYPE_COMMAND; + memcpy(hci_cmd_buf+1, hci_cmd, cmd_len); + esp_vhci_host_send_packet(hci_cmd_buf, cmd_len +1); + + free(hci_cmd_buf); +} + +// Invoked when ACL data was received over USB from Bluetooth host. +// Detailed format is described in Bluetooth core specification Vol 2, +// Part E, 5.4.2. +// Length is from 4 bytes, (12 bits for Handle, 4 bits for flags +// and 16 bits for data total length) to endpoint size. +static bool prepare_write = false; +static uint16_t write_offset = 0; +static uint16_t acl_data_length = 0; +void tud_bt_acl_data_received_cb(void *acl_data, uint16_t data_len) +{ + + // if acl_data is long data + if(!prepare_write) { + // first get acl_data_length + acl_data_length = *(((uint16_t * )acl_data) + 1); + if(acl_data_length > data_len) { + prepare_write = true; + p_acl_buf[0] = HCIT_TYPE_ACL_DATA; + memcpy(p_acl_buf + 1, acl_data, data_len); + write_offset = data_len + 1; + } else { + p_acl_buf[0] = HCIT_TYPE_ACL_DATA; + memcpy(p_acl_buf + 1, acl_data, data_len); + ESP_LOGI(TAG, "short acl_data from host, will send to controller, length: %d", (data_len + 1)); + esp_vhci_host_send_packet(p_acl_buf, data_len + 1); + } + } else { + memcpy(p_acl_buf + write_offset, acl_data, data_len); + write_offset += data_len; + if(acl_data_length > write_offset) { + ESP_LOGI(TAG, "Remaining bytes: %d", (acl_data_length - write_offset)); + return; + } + ESP_LOGI(TAG, "long acl_data from host, will send to controller, length: %d", (data_len + 1)); + prepare_write = false; + esp_vhci_host_send_packet(p_acl_buf, data_len + write_offset); + } + //free(hci_data_buf); +} + +// Called when event sent with tud_bt_event_send() was delivered to BT stack. +// Controller can release/reuse buffer with Event packet at this point. +void tud_bt_event_sent_cb(uint16_t sent_bytes) +{ + //TODO +} + +// Called when ACL data that was sent with tud_bt_acl_data_send() +// was delivered to BT stack. +// Controller can release/reuse buffer with ACL packet at this point. +void tud_bt_acl_data_sent_cb(uint16_t sent_bytes) +{ + //TODO +} \ No newline at end of file diff --git a/components/tinyusb/additions/src/tusb_cdc_acm.c b/components/tinyusb/additions/src/tusb_cdc_acm.c new file mode 100644 index 0000000..f2e536c --- /dev/null +++ b/components/tinyusb/additions/src/tusb_cdc_acm.c @@ -0,0 +1,426 @@ +// Copyright 2020 Espressif Systems (Shanghai) Co. Ltd. +// +// 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 "esp_check.h" +#include "esp_err.h" +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "tusb.h" +#include "tusb_cdc_acm.h" +#include "cdc.h" +#include "sdkconfig.h" + +#define RX_UNREADBUF_SZ_DEFAULT 64 // buffer storing all unread RX data + + +typedef struct { + bool initialized; + size_t rx_unread_buf_sz; + RingbufHandle_t rx_unread_buf; + xSemaphoreHandle ringbuf_read_mux; + uint8_t *rx_tfbuf; + tusb_cdcacm_callback_t callback_rx; + tusb_cdcacm_callback_t callback_rx_wanted_char; + tusb_cdcacm_callback_t callback_line_state_changed; + tusb_cdcacm_callback_t callback_line_coding_changed; +} esp_tusb_cdcacm_t; /*!< CDC_AMC object */ + +static const char *TAG = "tusb_cdc_acm"; + +static inline esp_tusb_cdcacm_t *get_acm(tinyusb_cdcacm_itf_t itf) +{ + esp_tusb_cdc_t *cdc_inst = tinyusb_cdc_get_intf(itf); + if (cdc_inst == NULL) { + return (esp_tusb_cdcacm_t *)NULL; + } + return (esp_tusb_cdcacm_t *)(cdc_inst->subclass_obj); +} + + +/* TinyUSB callbacks + ********************************************************************* */ + +/* Invoked by cdc interface when line state changed e.g connected/disconnected */ +void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts) +{ + esp_tusb_cdcacm_t *acm = get_acm(itf); + if (dtr && rts) { // connected + if (acm != NULL) { + ESP_LOGV(TAG, "Host connected to CDC no.%d.", itf); + } else { + ESP_LOGW(TAG, "Host is connected to CDC no.%d, but it is not initialized. Initialize it using `tinyusb_cdc_init`.", itf); + return; + } + } else { // disconnected + if (acm != NULL) { + ESP_LOGV(TAG, "Serial device is ready to connect to CDC no.%d", itf); + } else { + return; + } + } + if (acm) { + tusb_cdcacm_callback_t cb = acm->callback_line_state_changed; + if (cb) { + cdcacm_event_t event = { + .type = CDC_EVENT_LINE_STATE_CHANGED, + .line_state_changed_data = { + .dtr = dtr, + .rts = rts + } + }; + cb(itf, &event); + } + } +} + + +/* Invoked when CDC interface received data from host */ +void tud_cdc_rx_cb(uint8_t itf) +{ + esp_tusb_cdcacm_t *acm = get_acm(itf); + if (acm) { + if (!acm->rx_unread_buf) { + ESP_LOGE(TAG, "There is no RX buffer created"); + abort(); + } + } else { + tud_cdc_n_read_flush(itf); // we have no place to store data, so just drop it + return; + } + while (tud_cdc_n_available(itf)) { + int read_res = tud_cdc_n_read( itf, + acm->rx_tfbuf, + CONFIG_TINYUSB_CDC_RX_BUFSIZE ); + int res = xRingbufferSend(acm->rx_unread_buf, + acm->rx_tfbuf, + read_res, 0); + if (res != pdTRUE) { + ESP_LOGW(TAG, "The unread buffer is too small, the data has been lost"); + } else { + ESP_LOGV(TAG, "Sent %d bytes to the buffer", read_res); + } + } + if (acm) { + tusb_cdcacm_callback_t cb = acm->callback_rx; + if (cb) { + cdcacm_event_t event = { + .type = CDC_EVENT_RX + }; + cb(itf, &event); + } + } +} + +// Invoked when line coding is change via SET_LINE_CODING +void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const *p_line_coding) +{ + esp_tusb_cdcacm_t *acm = get_acm(itf); + if (acm) { + tusb_cdcacm_callback_t cb = acm->callback_line_coding_changed; + if (cb) { + cdcacm_event_t event = { + .type = CDC_EVENT_LINE_CODING_CHANGED, + .line_coding_changed_data = { + .p_line_coding = p_line_coding, + } + }; + cb(itf, &event); + } + } else { + return; + } +} + +// Invoked when received `wanted_char` +void tud_cdc_rx_wanted_cb(uint8_t itf, char wanted_char) +{ + esp_tusb_cdcacm_t *acm = get_acm(itf); + if (acm) { + tusb_cdcacm_callback_t cb = acm->callback_rx_wanted_char; + if (cb) { + cdcacm_event_t event = { + .type = CDC_EVENT_RX_WANTED_CHAR, + .rx_wanted_char_data = { + .wanted_char = wanted_char, + } + }; + cb(itf, &event); + } + } else { + return; + } +} + + + +esp_err_t tinyusb_cdcacm_register_callback(tinyusb_cdcacm_itf_t itf, + cdcacm_event_type_t event_type, + tusb_cdcacm_callback_t callback) +{ + esp_tusb_cdcacm_t *acm = get_acm(itf); + if (acm) { + switch (event_type) { + case CDC_EVENT_RX: + acm->callback_rx = callback; + return ESP_OK; + case CDC_EVENT_RX_WANTED_CHAR: + acm->callback_rx_wanted_char = callback; + return ESP_OK; + case CDC_EVENT_LINE_STATE_CHANGED: + acm->callback_line_state_changed = callback; + return ESP_OK; + case CDC_EVENT_LINE_CODING_CHANGED: + acm->callback_line_coding_changed = callback; + return ESP_OK; + default: + ESP_LOGE(TAG, "Wrong event type"); + return ESP_ERR_INVALID_ARG; + } + } else { + ESP_LOGE(TAG, "CDC-ACM is not initialized"); + return ESP_ERR_INVALID_STATE; + } +} + + +esp_err_t tinyusb_cdcacm_unregister_callback(tinyusb_cdcacm_itf_t itf, + cdcacm_event_type_t event_type) +{ + esp_tusb_cdcacm_t *acm = get_acm(itf); + if (!acm) { + ESP_LOGE(TAG, "Interface is not initialized. Use `tinyusb_cdc_init` for initialization"); + return ESP_ERR_INVALID_STATE; + } + switch (event_type) { + case CDC_EVENT_RX: + acm->callback_rx = NULL; + return ESP_OK; + case CDC_EVENT_RX_WANTED_CHAR: + acm->callback_rx_wanted_char = NULL; + return ESP_OK; + case CDC_EVENT_LINE_STATE_CHANGED: + acm->callback_line_state_changed = NULL; + return ESP_OK; + case CDC_EVENT_LINE_CODING_CHANGED: + acm->callback_line_coding_changed = NULL; + return ESP_OK; + default: + ESP_LOGE(TAG, "Wrong event type"); + return ESP_ERR_INVALID_ARG; + } +} + +/*********************************************************************** TinyUSB callbacks*/ +/* CDC-ACM + ********************************************************************* */ + +static esp_err_t read_from_rx_unread_to_buffer(esp_tusb_cdcacm_t *acm, uint8_t *out_buf, size_t req_bytes, size_t *read_bytes) +{ + uint8_t *buf = xRingbufferReceiveUpTo(acm->rx_unread_buf, read_bytes, 0, req_bytes); + if (buf) { + memcpy(out_buf, buf, *read_bytes); + vRingbufferReturnItem(acm->rx_unread_buf, (void *)(buf)); + return ESP_OK; + } else { + return ESP_ERR_NO_MEM; + } +} + +static esp_err_t ringbuf_mux_take(esp_tusb_cdcacm_t *acm) +{ + if (xSemaphoreTake(acm->ringbuf_read_mux, 0) != pdTRUE) { + ESP_LOGW(TAG, "Read error: ACM is busy"); + return ESP_ERR_INVALID_STATE; + } + return ESP_OK; +} + +static esp_err_t ringbuf_mux_give(esp_tusb_cdcacm_t *acm) +{ + BaseType_t ret = xSemaphoreGive(acm->ringbuf_read_mux); + assert(ret == pdTRUE); + return ESP_OK; +} + +esp_err_t tinyusb_cdcacm_read(tinyusb_cdcacm_itf_t itf, uint8_t *out_buf, size_t out_buf_sz, size_t *rx_data_size) +{ + esp_tusb_cdcacm_t *acm = get_acm(itf); + ESP_RETURN_ON_FALSE(acm, ESP_ERR_INVALID_STATE, TAG, "Interface is not initialized. Use `tinyusb_cdc_init` for initialization"); + size_t read_sz; + + /* Take a mutex to proceed two uninterrupted read operations */ + ESP_RETURN_ON_ERROR(ringbuf_mux_take(acm), TAG, "ringbuf_mux_take failed"); + + esp_err_t res = read_from_rx_unread_to_buffer(acm, out_buf, out_buf_sz, &read_sz); + if (res != ESP_OK) { + ESP_RETURN_ON_ERROR(ringbuf_mux_give(acm), TAG, "ringbuf_mux_give failed"); + return res; + } + + *rx_data_size = read_sz; + /* Buffer's data can be wrapped, at that situations we should make another retrievement */ + if (read_from_rx_unread_to_buffer(acm, out_buf + read_sz, out_buf_sz - read_sz, &read_sz) == ESP_OK) { + *rx_data_size += read_sz; + } + + ESP_RETURN_ON_ERROR(ringbuf_mux_give(acm), TAG, "ringbuf_mux_give failed"); + return ESP_OK; +} + + +size_t tinyusb_cdcacm_write_queue_char(tinyusb_cdcacm_itf_t itf, char ch) +{ + if (!get_acm(itf)) { // non-initialized + return 0; + } + return tud_cdc_n_write_char(itf, ch); +} + + +size_t tinyusb_cdcacm_write_queue(tinyusb_cdcacm_itf_t itf, uint8_t *in_buf, size_t in_size) +{ + if (!get_acm(itf)) { // non-initialized + return 0; + } + return tud_cdc_n_write(itf, in_buf, in_size); +} + +static uint32_t tud_cdc_n_write_occupied(tinyusb_cdcacm_itf_t itf) +{ + return CFG_TUD_CDC_TX_BUFSIZE - tud_cdc_n_write_available(itf); +} + +esp_err_t tinyusb_cdcacm_write_flush(tinyusb_cdcacm_itf_t itf, uint32_t timeout_ticks) +{ + if (!get_acm(itf)) { // non-initialized + return ESP_FAIL; + } + + if (!timeout_ticks) { // if no timeout - nonblocking mode + int res = tud_cdc_n_write_flush(itf); + if (!res) { + ESP_LOGW(TAG, "flush failed (res: %d)", res); + return ESP_FAIL; + } else { + if (tud_cdc_n_write_occupied(itf)) { + ESP_LOGW(TAG, "remained data to flush!"); + return ESP_FAIL; + } + } + return ESP_ERR_TIMEOUT; + } else { // trying during the timeout + uint32_t ticks_start = xTaskGetTickCount(); + uint32_t ticks_now = ticks_start; + while (1) { // loop until success or until the time runs out + ticks_now = xTaskGetTickCount(); + if (!tud_cdc_n_write_occupied(itf)) { // if nothing to write - nothing to flush + break; + } + if (tud_cdc_n_write_flush(itf)) { // Success + break; + } + if ( (ticks_now - ticks_start) > timeout_ticks ) { // Time is up + ESP_LOGW(TAG, "Flush failed"); + return ESP_ERR_TIMEOUT; + } + vTaskDelay(1); + } + return ESP_OK; + } +} + +static esp_err_t alloc_obj(tinyusb_cdcacm_itf_t itf) +{ + esp_tusb_cdc_t *cdc_inst = tinyusb_cdc_get_intf(itf); + cdc_inst->subclass_obj = calloc(1, sizeof(esp_tusb_cdcacm_t)); + if (!cdc_inst->subclass_obj) { + return ESP_FAIL; + } else { + return ESP_OK; + } +} + +static void free_obj(tinyusb_cdcacm_itf_t itf) +{ + esp_tusb_cdc_t *cdc_inst = tinyusb_cdc_get_intf(itf); + free(cdc_inst->subclass_obj); + cdc_inst->subclass_obj = NULL; +} + +esp_err_t tusb_cdc_acm_init(const tinyusb_config_cdcacm_t *cfg) +{ + int itf = (int)cfg->cdc_port; + /* Creating a CDC object */ + const tinyusb_config_cdc_t cdc_cfg = { + .usb_dev = cfg->usb_dev, + .cdc_class = TUSB_CLASS_CDC, + .cdc_subclass.comm_subclass = CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL + }; + ESP_RETURN_ON_ERROR(tinyusb_cdc_init(itf, &cdc_cfg), TAG, "tinyusb_cdc_init failed"); + ESP_RETURN_ON_ERROR(alloc_obj(itf), TAG, "alloc_obj failed"); + + esp_tusb_cdcacm_t *acm = get_acm(itf); + /* Callbacks setting up*/ + if (cfg->callback_rx) { + tinyusb_cdcacm_register_callback(itf, CDC_EVENT_RX, cfg->callback_rx); + } + if (cfg->callback_rx_wanted_char) { + tinyusb_cdcacm_register_callback(itf, CDC_EVENT_RX_WANTED_CHAR, cfg->callback_rx_wanted_char); + } + if (cfg->callback_line_state_changed) { + tinyusb_cdcacm_register_callback(itf, CDC_EVENT_LINE_STATE_CHANGED, cfg->callback_line_state_changed); + } + if (cfg->callback_line_coding_changed) { + tinyusb_cdcacm_register_callback( itf, CDC_EVENT_LINE_CODING_CHANGED, cfg->callback_line_coding_changed); + } + + /* Buffers */ + + acm->ringbuf_read_mux = xSemaphoreCreateMutex(); + if (acm->ringbuf_read_mux == NULL) { + ESP_LOGE(TAG, "Creation of a ringbuf mutex failed"); + free_obj(itf); + return ESP_ERR_NO_MEM; + } + + acm->rx_tfbuf = malloc(CONFIG_TINYUSB_CDC_RX_BUFSIZE); + if (!acm->rx_tfbuf) { + ESP_LOGE(TAG, "Creation buffer error"); + free_obj(itf); + return ESP_ERR_NO_MEM; + } + acm->rx_unread_buf_sz = cfg->rx_unread_buf_sz == 0 ? RX_UNREADBUF_SZ_DEFAULT : cfg->rx_unread_buf_sz; + acm->rx_unread_buf = xRingbufferCreate(acm->rx_unread_buf_sz, RINGBUF_TYPE_BYTEBUF); + if (acm->rx_unread_buf == NULL) { + ESP_LOGE(TAG, "Creation buffer error"); + free_obj(itf); + return ESP_ERR_NO_MEM; + } else { + ESP_LOGD(TAG, "Comm Initialized buff:%d bytes", cfg->rx_unread_buf_sz); + return ESP_OK; + } +} + +bool tusb_cdc_acm_initialized(tinyusb_cdcacm_itf_t itf) +{ + esp_tusb_cdcacm_t *acm = get_acm(itf); + if (acm) { + return true; + } else { + return false; + } +} +/*********************************************************************** CDC-ACM*/ diff --git a/components/tinyusb/additions/src/tusb_console.c b/components/tinyusb/additions/src/tusb_console.c new file mode 100644 index 0000000..1942a05 --- /dev/null +++ b/components/tinyusb/additions/src/tusb_console.c @@ -0,0 +1,142 @@ +// Copyright 2020 Espressif Systems (Shanghai) Co. Ltd. +// +// 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 "esp_log.h" +#include "cdc.h" +#include "tusb_console.h" +#include "tinyusb.h" +#include "vfs_tinyusb.h" + +#define STRINGIFY(s) STRINGIFY2(s) +#define STRINGIFY2(s) #s + +static const char *TAG = "tusb_console"; + +typedef struct { + FILE *in; + FILE *out; + FILE *err; +} console_handle_t; + +static console_handle_t con; + + +/** + * @brief Reopen standard streams using a new path + * + * @param f_in - pointer to a pointer holding a file for in or NULL to don't change stdin + * @param f_out - pointer to a pointer holding a file for out or NULL to don't change stdout + * @param f_err - pointer to a pointer holding a file for err or NULL to don't change stderr + * @param path - mount point + * @return esp_err_t ESP_FAIL or ESP_OK + */ +static esp_err_t redirect_std_streams_to(FILE **f_in, FILE **f_out, FILE **f_err, const char *path) +{ + if (f_in) { + *f_in = freopen(path, "r", stdin); + if (*f_in == NULL) { + ESP_LOGE(TAG, "Failed to reopen in!"); + return ESP_FAIL; + } + } + if (f_out) { + *f_out = freopen(path, "w", stdout); + if (*f_out == NULL) { + ESP_LOGE(TAG, "Failed to reopen out!"); + return ESP_FAIL; + } + } + if (f_err) { + *f_err = freopen(path, "w", stderr); + if (*f_err == NULL) { + ESP_LOGE(TAG, "Failed to reopen err!"); + return ESP_FAIL; + } + } + + return ESP_OK; +} + +/** + * @brief Restore output to default + * + * @param f_in - pointer to a pointer of an in file updated with `redirect_std_streams_to` or NULL to don't change stdin + * @param f_out - pointer to a pointer of an out file updated with `redirect_std_streams_to` or NULL to don't change stdout + * @param f_err - pointer to a pointer of an err file updated with `redirect_std_streams_to` or NULL to don't change stderr + * @return esp_err_t ESP_FAIL or ESP_OK + */ +static esp_err_t restore_std_streams(FILE **f_in, FILE **f_out, FILE **f_err) +{ + const char *default_uart_dev = "/dev/uart/" STRINGIFY(CONFIG_ESP_CONSOLE_UART_NUM); + if (f_in) { + stdin = freopen(default_uart_dev, "r", *f_in); + if (stdin == NULL) { + ESP_LOGE(TAG, "Failed to reopen stdin!"); + return ESP_FAIL; + } + } + if (f_out) { + stdout = freopen(default_uart_dev, "w", *f_out); + if (stdout == NULL) { + ESP_LOGE(TAG, "Failed to reopen stdout!"); + return ESP_FAIL; + } + } + if (f_err) { + stderr = freopen(default_uart_dev, "w", *f_err); + if (stderr == NULL) { + ESP_LOGE(TAG, "Failed to reopen stderr!"); + return ESP_FAIL; + } + } + return ESP_OK; +} + +esp_err_t esp_tusb_init_console(int cdc_intf) +{ + if (!tinyusb_cdc_initialized(cdc_intf)) { + ESP_LOGE(TAG, "Can't init the console because TinyUSB's CDC is not initialized!"); + return ESP_ERR_INVALID_STATE; + } + /* Registering TUSB at VFS */ + int res = esp_vfs_tusb_cdc_register(cdc_intf, NULL); + if (res != ESP_OK) { + return res; + } + + res = redirect_std_streams_to(&con.in, &con.out, &con.err, "/dev/tusb_cdc"); + if (res != ESP_OK) { + return res; + } + + return ESP_OK; +} + +esp_err_t esp_tusb_deinit_console(int cdc_intf) +{ + if (!tinyusb_cdc_initialized(cdc_intf)) { + ESP_LOGE(TAG, "Can't deinit the console because TinyUSB's CDC is not initialized!"); + return ESP_ERR_INVALID_STATE; + } + + int res = restore_std_streams(&con.in, &con.out, &con.err); + if (res != ESP_OK) { + return res; + } + esp_vfs_tusb_cdc_unregister(NULL); + return ESP_OK; +} diff --git a/components/tinyusb/additions/src/tusb_dfu.c b/components/tinyusb/additions/src/tusb_dfu.c new file mode 100644 index 0000000..2c5efb5 --- /dev/null +++ b/components/tinyusb/additions/src/tusb_dfu.c @@ -0,0 +1,295 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + + /* + * After device is enumerated in dfu mode run the following commands + * + * To transfer firmware from host to device (best to test with text file) + * + * $ dfu-util -d cafe -a 0 -D [filename] + * $ dfu-util -d cafe -a 1 -D [filename] + * + * To transfer firmware from device to host: + * + * $ dfu-util -d cafe -a 0 -U [filename] + * $ dfu-util -d cafe -a 1 -U [filename] + * + */ + +#include +#include +#include + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +#include "tusb.h" +#include "tinyusb.h" +#include "nvs_flash.h" +#include "esp_log.h" +#include "esp_err.h" +#include "dfu_device.h" + +#include "esp_system.h" +#include "esp_log.h" +#include "esp_ota_ops.h" +#include "esp_flash_partitions.h" +#include "esp_partition.h" + +static char* TAG = "esp_dfu"; + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF PROTYPES +//--------------------------------------------------------------------+ +const char* upload_image[2]= +{ + "Hello world from TinyUSB DFU! - Partition 0", + "Hello world from TinyUSB DFU! - Partition 1" +}; + +#define BUFFSIZE CONFIG_TINYUSB_DFU_BUFSIZE +/*an ota data write buffer ready to write to the flash*/ +static char ota_write_data[BUFFSIZE + 1] = { 0 }; + +static esp_ota_handle_t update_handle = 0; +static const esp_partition_t *update_partition = NULL; +static int binary_file_length = 0; +/*deal with all receive packet*/ +static bool image_header_was_checked = false; + +static esp_err_t ota_start(uint8_t* ota_write_data, uint32_t data_read) +{ + esp_err_t err; + /* update handle : set by esp_ota_begin(), must be freed via esp_ota_end() */ + + if (image_header_was_checked == false) { + esp_app_desc_t new_app_info; + + ESP_LOGI(TAG, "Starting OTA example"); + + const esp_partition_t *running = esp_ota_get_running_partition(); + + ESP_LOGI(TAG, "Running partition type %d subtype %d (offset 0x%08x)", + running->type, running->subtype, running->address); + + update_partition = esp_ota_get_next_update_partition(NULL); + assert(update_partition != NULL); + ESP_LOGI(TAG, "Writing to partition subtype %d at offset 0x%x", + update_partition->subtype, update_partition->address); + if (data_read > sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t) + sizeof(esp_app_desc_t)) { + // check current version with downloading + memcpy(&new_app_info, &ota_write_data[sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t)], sizeof(esp_app_desc_t)); + ESP_LOGI(TAG, "New firmware version: %s", new_app_info.version); + + esp_app_desc_t running_app_info; + if (esp_ota_get_partition_description(running, &running_app_info) == ESP_OK) { + ESP_LOGI(TAG, "Running firmware version: %s", running_app_info.version); + } + + const esp_partition_t* last_invalid_app = esp_ota_get_last_invalid_partition(); + esp_app_desc_t invalid_app_info; + if (esp_ota_get_partition_description(last_invalid_app, &invalid_app_info) == ESP_OK) { + ESP_LOGI(TAG, "Last invalid firmware version: %s", invalid_app_info.version); + } + + // check current version with last invalid partition + if (last_invalid_app != NULL) { + if (memcmp(invalid_app_info.version, new_app_info.version, sizeof(new_app_info.version)) == 0) { + ESP_LOGW(TAG, "New version is the same as invalid version."); + ESP_LOGW(TAG, "Previously, there was an attempt to launch the firmware with %s version, but it failed.", invalid_app_info.version); + ESP_LOGW(TAG, "The firmware has been rolled back to the previous version."); + } + } + + image_header_was_checked = true; + + err = esp_ota_begin(update_partition, OTA_WITH_SEQUENTIAL_WRITES, &update_handle); + if (err != ESP_OK) { + ESP_LOGE(TAG, "esp_ota_begin failed (%s)", esp_err_to_name(err)); + esp_ota_abort(update_handle); + return err; + } + ESP_LOGI(TAG, "esp_ota_begin succeeded"); + } else { + ESP_LOGE(TAG, "received package is not fit len"); + esp_ota_abort(update_handle); + return err; + } + } + err = esp_ota_write( update_handle, (const void *)ota_write_data, data_read); + if (err != ESP_OK) { + esp_ota_abort(update_handle); + return err; + } + binary_file_length += data_read; + ESP_LOGD(TAG, "Written image length %d", binary_file_length); + return err; +} + +static esp_err_t ota_complete(void) +{ + esp_err_t err = ESP_OK; + ESP_LOGI(TAG, "Total Write binary data length: %d", binary_file_length); + + err = esp_ota_end(update_handle); + if (err != ESP_OK) { + if (err == ESP_ERR_OTA_VALIDATE_FAILED) { + ESP_LOGE(TAG, "Image validation failed, image is corrupted"); + } else { + ESP_LOGE(TAG, "esp_ota_end failed (%s)!", esp_err_to_name(err)); + } + return err; + } + + err = esp_ota_set_boot_partition(update_partition); + if (err != ESP_OK) { + ESP_LOGE(TAG, "esp_ota_set_boot_partition failed (%s)!", esp_err_to_name(err)); + return err; + } + ESP_LOGI(TAG, "OTA done, please restart system!"); + return ESP_OK; +} + +//--------------------------------------------------------------------+ +// Device callbacks +//--------------------------------------------------------------------+ + +// Invoked when device is mounted +void tud_mount_cb(void) +{ + ESP_LOGI(TAG, "mounted\n"); +} + +// Invoked when device is unmounted +void tud_umount_cb(void) +{ + ESP_LOGI(TAG, "umounted\n"); +} + +// Invoked when usb bus is suspended +// remote_wakeup_en : if host allow us to perform remote wakeup +// Within 7ms, device must draw an average of current less than 2.5 mA from bus +void tud_suspend_cb(bool remote_wakeup_en) +{ + (void) remote_wakeup_en; +} + +// Invoked when usb bus is resumed +void tud_resume_cb(void) +{ + +} + +//--------------------------------------------------------------------+ +// DFU callbacks +// Note: alt is used as the partition number, in order to support multiple partitions like FLASH, EEPROM, etc. +//--------------------------------------------------------------------+ + +// Invoked right before tud_dfu_download_cb() (state=DFU_DNBUSY) or tud_dfu_manifest_cb() (state=DFU_MANIFEST) +// Application return timeout in milliseconds (bwPollTimeout) for the next download/manifest operation. +// During this period, USB host won't try to communicate with us. +uint32_t tud_dfu_get_timeout_cb(uint8_t alt, uint8_t state) +{ + if ( state == DFU_DNBUSY ) + { + // For this example + // - Atl0 Flash is fast : 1 ms + // - Alt1 EEPROM is slow: 100 ms + return (alt == 0) ? 1 : 100; + } + else if (state == DFU_MANIFEST) + { + // since we don't buffer entire image and do any flashing in manifest stage + return 0; + } + + return 0; +} + +// Invoked when received DFU_DNLOAD (wLength>0) following by DFU_GETSTATUS (state=DFU_DNBUSY) requests +// This callback could be returned before flashing op is complete (async). +// Once finished flashing, application must call tud_dfu_finish_flashing() +void tud_dfu_download_cb(uint8_t alt, uint16_t block_num, uint8_t const* data, uint16_t length) +{ + (void) alt; + (void) block_num; + + ESP_LOGI(TAG, "Received Alt %u BlockNum %u of length %u", alt, block_num, length); + + esp_err_t ret = ota_start(data, length); + + if (ret == ESP_OK) { + tud_dfu_finish_flashing(DFU_STATUS_OK); + } else { + tud_dfu_finish_flashing(DFU_STATUS_ERR_WRITE); + } +} + +// Invoked when download process is complete, received DFU_DNLOAD (wLength=0) following by DFU_GETSTATUS (state=Manifest) +// Application can do checksum, or actual flashing if buffered entire image previously. +// Once finished flashing, application must call tud_dfu_finish_flashing() +void tud_dfu_manifest_cb(uint8_t alt) +{ + (void) alt; + ESP_LOGI(TAG, "Download completed, enter manifestation\r\n"); + + esp_err_t ret = ota_complete(); + // flashing op for manifest is complete without error + // Application can perform checksum, should it fail, use appropriate status such as errVERIFY. + if (ret == ESP_OK) { + tud_dfu_finish_flashing(DFU_STATUS_OK); + } else { + tud_dfu_finish_flashing(DFU_STATUS_ERR_VERIFY); + } + +} + +// Invoked when received DFU_UPLOAD request +// Application must populate data with up to length bytes and +// Return the number of written bytes +uint16_t tud_dfu_upload_cb(uint8_t alt, uint16_t block_num, uint8_t* data, uint16_t length) +{ + (void) block_num; + (void) length; + ESP_LOGI(TAG, "upload data,block_num: %d,length: %d\n", block_num, length); + uint16_t const xfer_len = (uint16_t) strlen(upload_image[alt]); + memcpy(data, upload_image[alt], xfer_len); + + return xfer_len; +} + +// Invoked when the Host has terminated a download or upload transfer +void tud_dfu_abort_cb(uint8_t alt) +{ + (void) alt; + ESP_LOGI(TAG, "Host aborted transfer\r\n"); +} + +// Invoked when a DFU_DETACH request is received +void tud_dfu_detach_cb(void) +{ + ESP_LOGI(TAG, "Host detach, we should probably reboot\r\n"); + esp_restart(); +} \ No newline at end of file diff --git a/components/tinyusb/additions/src/tusb_hid.c b/components/tinyusb/additions/src/tusb_hid.c new file mode 100644 index 0000000..368c8a4 --- /dev/null +++ b/components/tinyusb/additions/src/tusb_hid.c @@ -0,0 +1,143 @@ +// Copyright 2020-2021 Espressif Systems (Shanghai) Co. Ltd. +// +// 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 "esp_err.h" +#include "sdkconfig.h" +#include "esp_log.h" +#include "tusb_hid.h" +#include "descriptors_control.h" + +static const char *TAG = "tusb_hid"; +static bool s_keyboard_pressed = false; + +void tinyusb_hid_mouse_move_report(int8_t x, int8_t y, int8_t vertical, int8_t horizontal) +{ + ESP_LOGD(TAG, "x=%d, y=%d, vertical=%d, horizontal=%d", x, y, vertical, horizontal); + + // Remote wakeup + if (tud_suspended()) { + // Wake up host if we are in suspend mode + // and REMOTE_WAKEUP feature is enabled by host + tud_remote_wakeup(); + } else { + // Send the 1st of report chain, the rest will be sent by tud_hid_report_complete_cb() + // skip if hid is not ready yet + if (!tud_hid_ready()) { + ESP_LOGW(TAG, "tinyusb not ready %s %d", __func__, __LINE__); + return; + } + + tud_hid_mouse_report(REPORT_ID_MOUSE, 0x00, x, y, vertical, horizontal); + + } +} + +void tinyusb_hid_mouse_button_report(uint8_t buttons_map) +{ + ESP_LOGD(TAG, "buttons_map = %u", buttons_map); + + // Remote wakeup + if (tud_suspended()) { + // Wake up host if we are in suspend mode + // and REMOTE_WAKEUP feature is enabled by host + tud_remote_wakeup(); + } else { + // Send the 1st of report chain, the rest will be sent by tud_hid_report_complete_cb() + // skip if hid is not ready yet + if (!tud_hid_ready()) { + ESP_LOGW(TAG, "tinyusb not ready %s %d", __func__, __LINE__); + return; + } + + tud_hid_mouse_report(REPORT_ID_MOUSE, buttons_map, 0, 0, 0, 0); + + } +} + +void tinyusb_hid_keyboard_report(uint8_t keycode[]) +{ + ESP_LOGD(TAG, "keycode = %u %u %u %u %u %u", keycode[0], keycode[1], keycode[2], keycode[3], keycode[4], keycode[5]); + + // Remote wakeup + if (tud_suspended()) { + // Wake up host if we are in suspend mode + // and REMOTE_WAKEUP feature is enabled by host + tud_remote_wakeup(); + } else { + // Send the 1st of report chain, the rest will be sent by tud_hid_report_complete_cb() + // skip if hid is not ready yet + if (!tud_hid_ready()) { + ESP_LOGW(TAG, "tinyusb not ready %s %d", __func__, __LINE__); + return; + } + + uint8_t _keycode[6] = { 0 }; + _keycode[0] = keycode[0]; + _keycode[1] = keycode[1]; + _keycode[2] = keycode[2]; + _keycode[3] = keycode[3]; + _keycode[4] = keycode[4]; + _keycode[5] = keycode[5]; + + tud_hid_keyboard_report(REPORT_ID_KEYBOARD, 0, _keycode); + s_keyboard_pressed = true; + } +} + +/************************************************** TinyUSB callbacks ***********************************************/ +// Invoked when sent REPORT successfully to host +// Application can use this to send the next report +// Note: For composite reports, report[0] is report ID +void tud_hid_report_complete_cb(uint8_t itf, uint8_t const *report, uint8_t len) +{ + (void) itf; + (void) len; + uint8_t report_id = report[0]; + + if (report_id == REPORT_ID_KEYBOARD && s_keyboard_pressed) { + tud_hid_keyboard_report(REPORT_ID_KEYBOARD, 0, NULL); + s_keyboard_pressed = false; + } +} + +// Invoked when received GET_REPORT control request +// Application must fill buffer report's content and return its length. +// Return zero will cause the stack to STALL request +uint16_t tud_hid_get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen) +{ + // TODO not Implemented + (void) itf; + (void) report_id; + (void) report_type; + (void) buffer; + (void) reqlen; + + return 0; +} + +// Invoked when received SET_REPORT control request or +// received data on OUT endpoint ( Report ID = 0, Type = 0 ) +/* +void tud_hid_set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) +{ + // TODO set LED based on CAPLOCK, NUMLOCK etc... + (void) itf; + (void) report_id; + (void) report_type; + (void) buffer; + (void) bufsize; +} +*/ \ No newline at end of file diff --git a/components/tinyusb/additions/src/tusb_msc.c b/components/tinyusb/additions/src/tusb_msc.c new file mode 100644 index 0000000..dfc40a0 --- /dev/null +++ b/components/tinyusb/additions/src/tusb_msc.c @@ -0,0 +1,291 @@ +// Copyright 2020-2021 Espressif Systems (Shanghai) Co. Ltd. +// +// 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 "esp_err.h" +#include "esp_log.h" +#include "ffconf.h" +#include "ff.h" +#include "diskio.h" +#include "tusb_msc.h" + +static uint8_t s_pdrv = 0; +static int s_disk_block_size = 0; + +#define LOGICAL_DISK_NUM 1 +static bool ejected[LOGICAL_DISK_NUM] = {true}; + +esp_err_t tusb_msc_init(const tinyusb_config_msc_t *cfg) +{ + if (cfg == NULL) { + return ESP_ERR_INVALID_ARG; + } + + if (cfg->pdrv > 0) { + return ESP_ERR_NOT_SUPPORTED; + } + + s_pdrv = cfg->pdrv; + return ESP_OK; +} + +//--------------------------------------------------------------------+ +// tinyusb callbacks +//--------------------------------------------------------------------+ + +// Invoked when device is mounted +void tud_mount_cb(void) +{ + // Reset the ejection tracking every time we're plugged into USB. This allows for us to battery + // power the device, eject, unplug and plug it back in to get the drive. + for (uint8_t i = 0; i < LOGICAL_DISK_NUM; i++) { + ejected[i] = false; + } + + ESP_LOGI(__func__, ""); +} + +// Invoked when device is unmounted +void tud_umount_cb(void) +{ + ESP_LOGW(__func__, ""); +} + +// Invoked when usb bus is suspended +// remote_wakeup_en : if host allows us to perform remote wakeup +// USB Specs: Within 7ms, device must draw an average current less than 2.5 mA from bus +void tud_suspend_cb(bool remote_wakeup_en) +{ + ESP_LOGW(__func__, ""); +} + +// Invoked when usb bus is resumed +void tud_resume_cb(void) +{ + ESP_LOGW(__func__, ""); +} + +// Callback invoked when WRITE10 command is completed (status received and accepted by host). +// used to flush any pending cache. +void tud_msc_write10_complete_cb(uint8_t lun) +{ + if (lun >= LOGICAL_DISK_NUM) { + ESP_LOGE(__func__, "invalid lun number %u", lun); + return; + } + + // This write is complete, start the autoreload clock. + ESP_LOGD(__func__, ""); +} + +static bool _logical_disk_ejected(void) +{ + bool all_ejected = true; + + for (uint8_t i = 0; i < LOGICAL_DISK_NUM; i++) { + all_ejected &= ejected[i]; + } + + return all_ejected; +} + +// Invoked when received SCSI_CMD_INQUIRY +// Application fill vendor id, product id and revision with string up to 8, 16, 4 characters respectively +void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4]) +{ + ESP_LOGD(__func__, ""); + + if (lun >= LOGICAL_DISK_NUM) { + ESP_LOGE(__func__, "invalid lun number %u", lun); + return; + } + + const char vid[] = "Espressif"; + const char pid[] = "Mass Storage"; + const char rev[] = "1.0"; + + memcpy(vendor_id, vid, strlen(vid)); + memcpy(product_id, pid, strlen(pid)); + memcpy(product_rev, rev, strlen(rev)); +} + +// Invoked when received Test Unit Ready command. +// return true allowing host to read/write this LUN e.g SD card inserted +bool tud_msc_test_unit_ready_cb(uint8_t lun) +{ + ESP_LOGD(__func__, ""); + + if (lun >= LOGICAL_DISK_NUM) { + ESP_LOGE(__func__, "invalid lun number %u", lun); + return false; + } + + if (_logical_disk_ejected()) { + // Set 0x3a for media not present. + tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x3A, 0x00); + return false; + } + + return true; +} + +// Invoked when received SCSI_CMD_READ_CAPACITY_10 and SCSI_CMD_READ_FORMAT_CAPACITY to determine the disk size +// Application update block count and block size +void tud_msc_capacity_cb(uint8_t lun, uint32_t *block_count, uint16_t *block_size) +{ + ESP_LOGD(__func__, ""); + + if (lun >= LOGICAL_DISK_NUM) { + ESP_LOGE(__func__, "invalid lun number %u", lun); + return; + } + + disk_ioctl(s_pdrv, GET_SECTOR_COUNT, block_count); + disk_ioctl(s_pdrv, GET_SECTOR_SIZE, block_size); + s_disk_block_size = *block_size; + ESP_LOGD(__func__, "GET_SECTOR_COUNT = %d,GET_SECTOR_SIZE = %d", *block_count, *block_size); +} + +bool tud_msc_is_writable_cb(uint8_t lun) +{ + ESP_LOGD(__func__, ""); + + if (lun >= LOGICAL_DISK_NUM) { + ESP_LOGE(__func__, "invalid lun number %u", lun); + return false; + } + + return true; +} + +// Invoked when received Start Stop Unit command +// - Start = 0 : stopped power mode, if load_eject = 1 : unload disk storage +// - Start = 1 : active mode, if load_eject = 1 : load disk storage +bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject) +{ + ESP_LOGI(__func__, ""); + (void) power_condition; + + if (lun >= LOGICAL_DISK_NUM) { + ESP_LOGE(__func__, "invalid lun number %u", lun); + return false; + } + + if (load_eject) { + if (!start) { + // Eject but first flush. + if (disk_ioctl(s_pdrv, CTRL_SYNC, NULL) != RES_OK) { + return false; + } else { + ejected[lun] = true; + } + } else { + // We can only load if it hasn't been ejected. + return !ejected[lun]; + } + } else { + if (!start) { + // Stop the unit but don't eject. + if (disk_ioctl(s_pdrv, CTRL_SYNC, NULL) != RES_OK) { + return false; + } + } + + // Always start the unit, even if ejected. Whether media is present is a separate check. + } + + return true; +} + +// Callback invoked when received READ10 command. +// Copy disk's data to buffer (up to bufsize) and return number of copied bytes. +int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void *buffer, uint32_t bufsize) +{ + ESP_LOGD(__func__, ""); + + if (lun >= LOGICAL_DISK_NUM) { + ESP_LOGE(__func__, "invalid lun number %u", lun); + return 0; + } + + const uint32_t block_count = bufsize / s_disk_block_size; + disk_read(s_pdrv, buffer, lba, block_count); + return block_count * s_disk_block_size; +} + +// Callback invoked when received WRITE10 command. +// Process data in buffer to disk's storage and return number of written bytes +int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t *buffer, uint32_t bufsize) +{ + ESP_LOGD(__func__, ""); + (void) offset; + + if (lun >= LOGICAL_DISK_NUM) { + ESP_LOGE(__func__, "invalid lun number %u", lun); + return 0; + } + + const uint32_t block_count = bufsize / s_disk_block_size; + disk_write(s_pdrv, buffer, lba, block_count); + return block_count * s_disk_block_size; +} + +// Callback invoked when received an SCSI command not in built-in list below +// - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, MODE_SENSE6, REQUEST_SENSE +// - READ10 and WRITE10 has their own callbacks +int32_t tud_msc_scsi_cb(uint8_t lun, uint8_t const scsi_cmd[16], void *buffer, uint16_t bufsize) +{ + // read10 & write10 has their own callback and MUST not be handled here + ESP_LOGD(__func__, ""); + + if (lun >= LOGICAL_DISK_NUM) { + ESP_LOGE(__func__, "invalid lun number %u", lun); + return 0; + } + + void const *response = NULL; + uint16_t resplen = 0; + + // most scsi handled is input + bool in_xfer = true; + + switch (scsi_cmd[0]) { + case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL: + // Host is about to read/write etc ... better not to disconnect disk + resplen = 0; + break; + + default: + // Set Sense = Invalid Command Operation + tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); + + // negative means error -> tinyusb could stall and/or response with failed status + resplen = -1; + break; + } + + // return resplen must not larger than bufsize + if (resplen > bufsize) { + resplen = bufsize; + } + + if (response && (resplen > 0)) { + if (in_xfer) { + memcpy(buffer, response, resplen); + } else { + // SCSI output + } + } + + return resplen; +} diff --git a/components/tinyusb/additions/src/tusb_net.c b/components/tinyusb/additions/src/tusb_net.c new file mode 100644 index 0000000..9b4a50b --- /dev/null +++ b/components/tinyusb/additions/src/tusb_net.c @@ -0,0 +1,93 @@ +/** + * @copyright Copyright 2021 Espressif Systems (Shanghai) Co. Ltd. + * + * 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 "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include "lwip/netif.h" +#include "esp_private/wifi.h" + +#include "tusb_net.h" + +extern bool s_wifi_is_connected; +static SemaphoreHandle_t Net_Semphore; + +bool tud_network_wait_xmit(uint32_t ms) +{ + if (xSemaphoreTake(Net_Semphore, ms/portTICK_PERIOD_MS) == pdTRUE) { + xSemaphoreGive(Net_Semphore); + return true; + } + return false; +} + +esp_err_t pkt_wifi2usb(void *buffer, uint16_t len, void *eb) +{ + if (!tud_ready()) { + esp_wifi_internal_free_rx_buffer(eb); + return ERR_USE; + } + + if (tud_network_wait_xmit(100)) { + /* if the network driver can accept another packet, we make it happen */ + if (tud_network_can_xmit()) { + tud_network_xmit(buffer, len); + } + } + + esp_wifi_internal_free_rx_buffer(eb); + return ESP_OK; +} + +void tusb_net_init(void) +{ + vSemaphoreCreateBinary(Net_Semphore); +} + +//--------------------------------------------------------------------+ +// tinyusb callbacks +//--------------------------------------------------------------------+ + +bool tud_network_recv_cb(const uint8_t *src, uint16_t size) +{ + if (s_wifi_is_connected) { + esp_wifi_internal_tx(ESP_IF_WIFI_STA, src, size); + } + tud_network_recv_renew(); + return true; +} + +uint16_t tud_network_xmit_cb(uint8_t *dst, void *ref, uint16_t arg) +{ + uint16_t len = arg; + + /* traverse the "pbuf chain"; see ./lwip/src/core/pbuf.c for more info */ + memcpy(dst, ref, len); + return len; +} + +void tud_network_init_cb(void) +{ + /* TODO */ +} + +void tud_network_idle_status_change_cb(bool enable) +{ + if (enable == true) { + xSemaphoreGive(Net_Semphore); + } else { + xSemaphoreTake(Net_Semphore, 0); + } +} \ No newline at end of file diff --git a/components/tinyusb/additions/src/tusb_tasks.c b/components/tinyusb/additions/src/tusb_tasks.c new file mode 100644 index 0000000..7312473 --- /dev/null +++ b/components/tinyusb/additions/src/tusb_tasks.c @@ -0,0 +1,54 @@ +// Copyright 2020 Espressif Systems (Shanghai) PTE LTD +// +// 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 "sdkconfig.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_log.h" +#include "esp_check.h" +#include "tinyusb.h" +#include "tusb_tasks.h" + +const static char *TAG = "tusb_tsk"; +static TaskHandle_t s_tusb_tskh; + +/** + * @brief This top level thread processes all usb events and invokes callbacks + */ +static void tusb_device_task(void *arg) +{ + ESP_LOGD(TAG, "tinyusb task started"); + while (1) { // RTOS forever loop + tud_task(); + } +} + +esp_err_t tusb_run_task(void) +{ + // This function is not garanteed to be thread safe, if invoked multiple times without calling `tusb_stop_task`, will cause memory leak + // doing a sanity check anyway + ESP_RETURN_ON_FALSE(!s_tusb_tskh, ESP_ERR_INVALID_STATE, TAG, "TinyUSB main task already started"); + // Create a task for tinyusb device stack: + xTaskCreate(tusb_device_task, "TinyUSB", CONFIG_TINYUSB_TASK_STACK_SIZE, NULL, CONFIG_TINYUSB_TASK_PRIORITY, &s_tusb_tskh); + ESP_RETURN_ON_FALSE(s_tusb_tskh, ESP_FAIL, TAG, "create TinyUSB main task failed"); + return ESP_OK; +} + +esp_err_t tusb_stop_task(void) +{ + ESP_RETURN_ON_FALSE(s_tusb_tskh, ESP_ERR_INVALID_STATE, TAG, "TinyUSB main task not started yet"); + vTaskDelete(s_tusb_tskh); + s_tusb_tskh = NULL; + return ESP_OK; +} diff --git a/components/tinyusb/additions/src/usb_descriptors.c b/components/tinyusb/additions/src/usb_descriptors.c new file mode 100644 index 0000000..af0de20 --- /dev/null +++ b/components/tinyusb/additions/src/usb_descriptors.c @@ -0,0 +1,153 @@ +// Copyright 2020 Espressif Systems (Shanghai) PTE LTD +// +// 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 "usb_descriptors.h" +#include "descriptors_control.h" +#include "sdkconfig.h" + +/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug. + * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC. + * + * Auto ProductID layout's Bitmap: + * [MSB] NET | VENDOR | MIDI | HID | MSC | CDC [LSB] + */ +#define USB_TUSB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \ + _PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4) | _PID_MAP(NET, 5) ) + +/**** TinyUSB default ****/ +tusb_desc_device_t descriptor_tinyusb = { + .bLength = sizeof(descriptor_tinyusb), + .bDescriptorType = TUSB_DESC_DEVICE, + .bcdUSB = 0x0200, + +#if CFG_TUD_CDC + // Use Interface Association Descriptor (IAD) for CDC + // As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1) + .bDeviceClass = TUSB_CLASS_MISC, + .bDeviceSubClass = MISC_SUBCLASS_COMMON, + .bDeviceProtocol = MISC_PROTOCOL_IAD, +#else + .bDeviceClass = 0x00, + .bDeviceSubClass = 0x00, + .bDeviceProtocol = 0x00, +#endif + + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + + .idVendor = 0xCafe, + .idProduct = USB_TUSB_PID, + .bcdDevice = 0x0100, + + .iManufacturer = 0x01, + .iProduct = 0x02, + .iSerialNumber = 0x03, + + .bNumConfigurations = 0x01 +}; + +tusb_desc_strarray_device_t descriptor_str_tinyusb = { + // array of pointer to string descriptors + (char[]){0x09, 0x04}, // 0: is supported language is English (0x0409) + "TinyUSB", // 1: Manufacturer + "TinyUSB Device", // 2: Product + "123456", // 3: Serials, should use chip ID + "TinyUSB CDC", // 4: CDC Interface + "TinyUSB MSC", // 5: MSC Interface + "TinyUSB HID" // 6: HID +}; +/* End of TinyUSB default */ + +/**** Kconfig driven Descriptor ****/ +tusb_desc_device_t descriptor_kconfig = { + .bLength = sizeof(descriptor_kconfig), + .bDescriptorType = TUSB_DESC_DEVICE, + .bcdUSB = 0x0200, + +#if CFG_TUD_CDC + // Use Interface Association Descriptor (IAD) for CDC + // As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1) + .bDeviceClass = TUSB_CLASS_MISC, + .bDeviceSubClass = MISC_SUBCLASS_COMMON, + .bDeviceProtocol = MISC_PROTOCOL_IAD, +#elif CFG_TUD_BTH + .bDeviceClass = TUSB_CLASS_WIRELESS_CONTROLLER, + .bDeviceSubClass = TUD_BT_APP_SUBCLASS, + .bDeviceProtocol = TUD_BT_PROTOCOL_PRIMARY_CONTROLLER, +#else + .bDeviceClass = 0x00, + .bDeviceSubClass = 0x00, + .bDeviceProtocol = 0x00, +#endif + + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + +#if CONFIG_TINYUSB_DESC_USE_ESPRESSIF_VID + .idVendor = USB_ESPRESSIF_VID, +#else + .idVendor = CONFIG_TINYUSB_DESC_CUSTOM_VID, +#endif + +#if CONFIG_TINYUSB_DESC_USE_DEFAULT_PID + .idProduct = USB_TUSB_PID, +#else + .idProduct = CONFIG_TINYUSB_DESC_CUSTOM_PID, +#endif + + .bcdDevice = CONFIG_TINYUSB_DESC_BCD_DEVICE, + + .iManufacturer = 0x01, + .iProduct = 0x02, + .iSerialNumber = 0x03, + + .bNumConfigurations = 0x01 +}; + +tusb_desc_strarray_device_t descriptor_str_kconfig = { + // array of pointer to string descriptors + (char[]){0x09, 0x04}, // 0: is supported language is English (0x0409) + CONFIG_TINYUSB_DESC_MANUFACTURER_STRING, // 1: Manufacturer + CONFIG_TINYUSB_DESC_PRODUCT_STRING, // 2: Product + CONFIG_TINYUSB_DESC_SERIAL_STRING, // 3: Serials, should use chip ID + +#if CFG_TUD_CDC + CONFIG_TINYUSB_DESC_CDC_STRING, // CDC Interface +#endif + +#if CFG_TUD_NET + CONFIG_TINYUSB_DESC_NET_STRING, // NET Interface + "", // MAC +#endif + +#if CFG_TUD_MSC + CONFIG_TINYUSB_DESC_MSC_STRING, // MSC Interface +#endif + +#if CFG_TUD_HID_KBD + CONFIG_TINYUSB_DESC_HID_STRING, // HIDs +#endif +#if CFG_TUD_U2FHID + "TiDAL U2F", // U2FHID +#endif + +#if CFG_TUD_BTH + CONFIG_TINYUSB_DESC_BTH_STRING, // BTH +#endif + +#if CFG_TUD_DFU + "FLASH", // 4: DFU Partition 1 + "EEPROM", // 5: DFU Partition 2 +#endif + +}; +/* End of Kconfig driven Descriptor */ diff --git a/components/tinyusb/additions/src/usbd.c b/components/tinyusb/additions/src/usbd.c new file mode 100644 index 0000000..8074ab3 --- /dev/null +++ b/components/tinyusb/additions/src/usbd.c @@ -0,0 +1,1349 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#include "tusb_option.h" +#if CFG_TUD_DFU + #include "dfu_device.h" +#endif + +#if TUSB_OPT_DEVICE_ENABLED + +#include "tusb.h" +#include "device/usbd.h" +#include "device/usbd_pvt.h" +#include "device/dcd.h" + + +#ifndef CFG_TUD_TASK_QUEUE_SZ +#define CFG_TUD_TASK_QUEUE_SZ 16 +#endif + +#ifndef CFG_TUD_EP_MAX +#define CFG_TUD_EP_MAX 9 +#endif + +//--------------------------------------------------------------------+ +// Device Data +//--------------------------------------------------------------------+ +typedef struct +{ + struct TU_ATTR_PACKED + { + volatile uint8_t connected : 1; + volatile uint8_t addressed : 1; + volatile uint8_t suspended : 1; + + uint8_t remote_wakeup_en : 1; // enable/disable by host + uint8_t remote_wakeup_support : 1; // configuration descriptor's attribute + uint8_t self_powered : 1; // configuration descriptor's attribute + }; + + volatile uint8_t cfg_num; // current active configuration (0x00 is not configured) + uint8_t speed; + + uint8_t itf2drv[16]; // map interface number to driver (0xff is invalid) + uint8_t ep2drv[CFG_TUD_EP_MAX][2]; // map endpoint to driver ( 0xff is invalid ) + + struct TU_ATTR_PACKED + { + volatile bool busy : 1; + volatile bool stalled : 1; + volatile bool claimed : 1; + + // TODO merge ep2drv here, 4-bit should be sufficient + }ep_status[CFG_TUD_EP_MAX][2]; + +}usbd_device_t; + +static usbd_device_t _usbd_dev; + +// Invalid driver ID in itf2drv[] ep2drv[][] mapping +enum { DRVID_INVALID = 0xFFu }; + +//--------------------------------------------------------------------+ +// Class Driver +//--------------------------------------------------------------------+ +#if CFG_TUSB_DEBUG >= 2 + #define DRIVER_NAME(_name) .name = _name, +#else + #define DRIVER_NAME(_name) +#endif + +// Built-in class drivers +static usbd_class_driver_t const _usbd_driver[] = +{ + #if CFG_TUD_CDC + { + DRIVER_NAME("CDC") + .init = cdcd_init, + .reset = cdcd_reset, + .open = cdcd_open, + .control_xfer_cb = cdcd_control_xfer_cb, + .xfer_cb = cdcd_xfer_cb, + .sof = NULL + }, + #endif + + #if CFG_TUD_MSC + { + DRIVER_NAME("MSC") + .init = mscd_init, + .reset = mscd_reset, + .open = mscd_open, + .control_xfer_cb = mscd_control_xfer_cb, + .xfer_cb = mscd_xfer_cb, + .sof = NULL + }, + #endif + + #if CFG_TUD_HID + { + DRIVER_NAME("HID") + .init = hidd_init, + .reset = hidd_reset, + .open = hidd_open, + .control_xfer_cb = hidd_control_xfer_cb, + .xfer_cb = hidd_xfer_cb, + .sof = NULL + }, + #endif + + #if CFG_TUD_AUDIO + { + DRIVER_NAME("AUDIO") + .init = audiod_init, + .reset = audiod_reset, + .open = audiod_open, + .control_xfer_cb = audiod_control_xfer_cb, + .xfer_cb = audiod_xfer_cb, + .sof = NULL + }, + #endif + + #if CFG_TUD_MIDI + { + DRIVER_NAME("MIDI") + .init = midid_init, + .open = midid_open, + .reset = midid_reset, + .control_xfer_cb = midid_control_xfer_cb, + .xfer_cb = midid_xfer_cb, + .sof = NULL + }, + #endif + + #if CFG_TUD_VENDOR + { + DRIVER_NAME("VENDOR") + .init = vendord_init, + .reset = vendord_reset, + .open = vendord_open, + .control_xfer_cb = tud_vendor_control_xfer_cb, + .xfer_cb = vendord_xfer_cb, + .sof = NULL + }, + #endif + + #if CFG_TUD_USBTMC + { + DRIVER_NAME("TMC") + .init = usbtmcd_init_cb, + .reset = usbtmcd_reset_cb, + .open = usbtmcd_open_cb, + .control_xfer_cb = usbtmcd_control_xfer_cb, + .xfer_cb = usbtmcd_xfer_cb, + .sof = NULL + }, + #endif + + #if CFG_TUD_DFU_RUNTIME + { + DRIVER_NAME("DFU-RUNTIME") + .init = dfu_rtd_init, + .reset = dfu_rtd_reset, + .open = dfu_rtd_open, + .control_xfer_cb = dfu_rtd_control_xfer_cb, + .xfer_cb = dfu_rtd_xfer_cb, + .sof = NULL + }, + #endif + + #if CFG_TUD_DFU + { + DRIVER_NAME("DFU") + .init = dfu_moded_init, + .reset = dfu_moded_reset, + .open = dfu_moded_open, + .control_xfer_cb = dfu_moded_control_xfer_cb, + .xfer_cb = NULL, + .sof = NULL + }, + #endif + + #if CFG_TUD_NET + { + DRIVER_NAME("NET") + .init = netd_init, + .reset = netd_reset, + .open = netd_open, + .control_xfer_cb = netd_control_xfer_cb, + .xfer_cb = netd_xfer_cb, + .sof = NULL, + }, + #endif + + #if CFG_TUD_BTH + { + DRIVER_NAME("BTH") + .init = btd_init, + .reset = btd_reset, + .open = btd_open, + .control_xfer_cb = btd_control_xfer_cb, + .xfer_cb = btd_xfer_cb, + .sof = NULL + }, + #endif +}; + +enum { BUILTIN_DRIVER_COUNT = TU_ARRAY_SIZE(_usbd_driver) }; + +// Additional class drivers implemented by application +static usbd_class_driver_t const * _app_driver = NULL; +static uint8_t _app_driver_count = 0; + +// virtually joins built-in and application drivers together. +// Application is positioned first to allow overwriting built-in ones. +static inline usbd_class_driver_t const * get_driver(uint8_t drvid) +{ + // Application drivers + if ( usbd_app_driver_get_cb ) + { + if ( drvid < _app_driver_count ) return &_app_driver[drvid]; + drvid -= _app_driver_count; + } + + // Built-in drivers + if (drvid < BUILTIN_DRIVER_COUNT) return &_usbd_driver[drvid]; + + return NULL; +} + +#define TOTAL_DRIVER_COUNT (_app_driver_count + BUILTIN_DRIVER_COUNT) + +//--------------------------------------------------------------------+ +// DCD Event +//--------------------------------------------------------------------+ + +// Event queue +// OPT_MODE_DEVICE is used by OS NONE for mutex (disable usb isr) +OSAL_QUEUE_DEF(OPT_MODE_DEVICE, _usbd_qdef, CFG_TUD_TASK_QUEUE_SZ, dcd_event_t); +static osal_queue_t _usbd_q; + +// Mutex for claiming endpoint, only needed when using with preempted RTOS +#if CFG_TUSB_OS != OPT_OS_NONE +static osal_mutex_def_t _ubsd_mutexdef; +static osal_mutex_t _usbd_mutex; +#endif + + +//--------------------------------------------------------------------+ +// Prototypes +//--------------------------------------------------------------------+ +static void mark_interface_endpoint(uint8_t ep2drv[][2], uint8_t const* p_desc, uint16_t desc_len, uint8_t driver_id); +static bool process_control_request(uint8_t rhport, tusb_control_request_t const * p_request); +static bool process_set_config(uint8_t rhport, uint8_t cfg_num); +static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const * p_request); + +// from usbd_control.c +void usbd_control_reset(void); +void usbd_control_set_request(tusb_control_request_t const *request); +void usbd_control_set_complete_callback( usbd_control_xfer_cb_t fp ); +bool usbd_control_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes); + + +//--------------------------------------------------------------------+ +// Debug +//--------------------------------------------------------------------+ +#if CFG_TUSB_DEBUG >= 2 +static char const* const _usbd_event_str[DCD_EVENT_COUNT] = +{ + "Invalid" , + "Bus Reset" , + "Unplugged" , + "SOF" , + "Suspend" , + "Resume" , + "Setup Received" , + "Xfer Complete" , + "Func Call" +}; + +static char const* const _tusb_std_request_str[] = +{ + "Get Status" , + "Clear Feature" , + "Reserved" , + "Set Feature" , + "Reserved" , + "Set Address" , + "Get Descriptor" , + "Set Descriptor" , + "Get Configuration" , + "Set Configuration" , + "Get Interface" , + "Set Interface" , + "Synch Frame" +}; + +// for usbd_control to print the name of control complete driver +void usbd_driver_print_control_complete_name(usbd_control_xfer_cb_t callback) +{ + for (uint8_t i = 0; i < TOTAL_DRIVER_COUNT; i++) + { + usbd_class_driver_t const * driver = get_driver(i); + if ( driver->control_xfer_cb == callback ) + { + TU_LOG2(" %s control complete\r\n", driver->name); + return; + } + } +} + +#endif + +//--------------------------------------------------------------------+ +// Application API +//--------------------------------------------------------------------+ +tusb_speed_t tud_speed_get(void) +{ + return (tusb_speed_t) _usbd_dev.speed; +} + +bool tud_connected(void) +{ + return _usbd_dev.connected; +} + +bool tud_mounted(void) +{ + return _usbd_dev.cfg_num ? true : false; +} + +bool tud_suspended(void) +{ + return _usbd_dev.suspended; +} + +bool tud_remote_wakeup(void) +{ + // only wake up host if this feature is supported and enabled and we are suspended + TU_VERIFY (_usbd_dev.suspended && _usbd_dev.remote_wakeup_support && _usbd_dev.remote_wakeup_en ); + dcd_remote_wakeup(TUD_OPT_RHPORT); + return true; +} + +bool tud_disconnect(void) +{ + TU_VERIFY(dcd_disconnect); + dcd_disconnect(TUD_OPT_RHPORT); + return true; +} + +bool tud_connect(void) +{ + TU_VERIFY(dcd_connect); + dcd_connect(TUD_OPT_RHPORT); + return true; +} + +//--------------------------------------------------------------------+ +// USBD Task +//--------------------------------------------------------------------+ +bool tud_init (void) +{ + TU_LOG2("USBD init\r\n"); + + tu_varclr(&_usbd_dev); + +#if CFG_TUSB_OS != OPT_OS_NONE + // Init device mutex + _usbd_mutex = osal_mutex_create(&_ubsd_mutexdef); + TU_ASSERT(_usbd_mutex); +#endif + + // Init device queue & task + _usbd_q = osal_queue_create(&_usbd_qdef); + TU_ASSERT(_usbd_q); + + // Get application driver if available + if ( usbd_app_driver_get_cb ) + { + _app_driver = usbd_app_driver_get_cb(&_app_driver_count); + } + + // Init class drivers + for (uint8_t i = 0; i < TOTAL_DRIVER_COUNT; i++) + { + usbd_class_driver_t const * driver = get_driver(i); + TU_LOG2("%s init\r\n", driver->name); + driver->init(); + } + + // Init device controller driver + dcd_init(TUD_OPT_RHPORT); + dcd_int_enable(TUD_OPT_RHPORT); + + return true; +} + +static void usbd_reset(uint8_t rhport) +{ + tu_varclr(&_usbd_dev); + + memset(_usbd_dev.itf2drv, DRVID_INVALID, sizeof(_usbd_dev.itf2drv)); // invalid mapping + memset(_usbd_dev.ep2drv , DRVID_INVALID, sizeof(_usbd_dev.ep2drv )); // invalid mapping + + usbd_control_reset(); + + for ( uint8_t i = 0; i < TOTAL_DRIVER_COUNT; i++ ) + { + get_driver(i)->reset(rhport); + } +} + +bool tud_task_event_ready(void) +{ + // Skip if stack is not initialized + if ( !tusb_inited() ) return false; + + return !osal_queue_empty(_usbd_q); +} + +/* USB Device Driver task + * This top level thread manages all device controller event and delegates events to class-specific drivers. + * This should be called periodically within the mainloop or rtos thread. + * + @code + int main(void) + { + application_init(); + tusb_init(); + + while(1) // the mainloop + { + application_code(); + tud_task(); // tinyusb device task + } + } + @endcode + */ +void tud_task (void) +{ + // Skip if stack is not initialized + if ( !tusb_inited() ) return; + + // Loop until there is no more events in the queue + while (1) + { + dcd_event_t event; + + if ( !osal_queue_receive(_usbd_q, &event) ) return; + +#if CFG_TUSB_DEBUG >= 2 + if (event.event_id == DCD_EVENT_SETUP_RECEIVED) TU_LOG2("\r\n"); // extra line for setup + TU_LOG2("USBD %s ", event.event_id < DCD_EVENT_COUNT ? _usbd_event_str[event.event_id] : "CORRUPTED"); +#endif + + switch ( event.event_id ) + { + case DCD_EVENT_BUS_RESET: + TU_LOG2("\r\n"); + usbd_reset(event.rhport); + _usbd_dev.speed = event.bus_reset.speed; + break; + + case DCD_EVENT_UNPLUGGED: + TU_LOG2("\r\n"); + usbd_reset(event.rhport); + + // invoke callback + if (tud_umount_cb) tud_umount_cb(); + break; + + case DCD_EVENT_SETUP_RECEIVED: + TU_LOG2_VAR(&event.setup_received); + TU_LOG2("\r\n"); + + // Mark as connected after receiving 1st setup packet. + // But it is easier to set it every time instead of wasting time to check then set + _usbd_dev.connected = 1; + + // mark both in & out control as free + _usbd_dev.ep_status[0][TUSB_DIR_OUT].busy = false; + _usbd_dev.ep_status[0][TUSB_DIR_OUT].claimed = 0; + _usbd_dev.ep_status[0][TUSB_DIR_IN ].busy = false; + _usbd_dev.ep_status[0][TUSB_DIR_IN ].claimed = 0; + + // Process control request + if ( !process_control_request(event.rhport, &event.setup_received) ) + { + TU_LOG2(" Stall EP0\r\n"); + // Failed -> stall both control endpoint IN and OUT + dcd_edpt_stall(event.rhport, 0); + dcd_edpt_stall(event.rhport, 0 | TUSB_DIR_IN_MASK); + } + break; + + case DCD_EVENT_XFER_COMPLETE: + { + // Invoke the class callback associated with the endpoint address + uint8_t const ep_addr = event.xfer_complete.ep_addr; + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const ep_dir = tu_edpt_dir(ep_addr); + + TU_LOG2("on EP %02X with %u bytes\r\n", ep_addr, (unsigned int) event.xfer_complete.len); + + _usbd_dev.ep_status[epnum][ep_dir].busy = false; + _usbd_dev.ep_status[epnum][ep_dir].claimed = 0; + + if ( 0 == epnum ) + { + usbd_control_xfer_cb(event.rhport, ep_addr, (xfer_result_t)event.xfer_complete.result, event.xfer_complete.len); + } + else + { + usbd_class_driver_t const * driver = get_driver( _usbd_dev.ep2drv[epnum][ep_dir] ); + TU_ASSERT(driver, ); + + TU_LOG2(" %s xfer callback\r\n", driver->name); + driver->xfer_cb(event.rhport, ep_addr, (xfer_result_t)event.xfer_complete.result, event.xfer_complete.len); + } + } + break; + + case DCD_EVENT_SUSPEND: + TU_LOG2("\r\n"); + if (tud_suspend_cb) tud_suspend_cb(_usbd_dev.remote_wakeup_en); + break; + + case DCD_EVENT_RESUME: + TU_LOG2("\r\n"); + if (tud_resume_cb) tud_resume_cb(); + break; + + case DCD_EVENT_SOF: + TU_LOG2("\r\n"); + for ( uint8_t i = 0; i < TOTAL_DRIVER_COUNT; i++ ) + { + usbd_class_driver_t const * driver = get_driver(i); + if ( driver->sof ) driver->sof(event.rhport); + } + break; + + case USBD_EVENT_FUNC_CALL: + TU_LOG2("\r\n"); + if ( event.func_call.func ) event.func_call.func(event.func_call.param); + break; + + default: + TU_BREAKPOINT(); + break; + } + } +} + +//--------------------------------------------------------------------+ +// Control Request Parser & Handling +//--------------------------------------------------------------------+ + +// Helper to invoke class driver control request handler +static bool invoke_class_control(uint8_t rhport, usbd_class_driver_t const * driver, tusb_control_request_t const * request) +{ + usbd_control_set_complete_callback(driver->control_xfer_cb); + TU_LOG2(" %s control request\r\n", driver->name); + return driver->control_xfer_cb(rhport, CONTROL_STAGE_SETUP, request); +} + +// This handles the actual request and its response. +// return false will cause its caller to stall control endpoint +static bool process_control_request(uint8_t rhport, tusb_control_request_t const * p_request) +{ + usbd_control_set_complete_callback(NULL); + + TU_ASSERT(p_request->bmRequestType_bit.type < TUSB_REQ_TYPE_INVALID); + + // Vendor request + if ( p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_VENDOR ) + { + TU_VERIFY(tud_vendor_control_xfer_cb); + + usbd_control_set_complete_callback(tud_vendor_control_xfer_cb); + return tud_vendor_control_xfer_cb(rhport, CONTROL_STAGE_SETUP, p_request); + } + +#if CFG_TUSB_DEBUG >= 2 + if (TUSB_REQ_TYPE_STANDARD == p_request->bmRequestType_bit.type && p_request->bRequest <= TUSB_REQ_SYNCH_FRAME) + { + TU_LOG2(" %s", _tusb_std_request_str[p_request->bRequest]); + if (TUSB_REQ_GET_DESCRIPTOR != p_request->bRequest) TU_LOG2("\r\n"); + } +#endif + + switch ( p_request->bmRequestType_bit.recipient ) + { + //------------- Device Requests e.g in enumeration -------------// + case TUSB_REQ_RCPT_DEVICE: + if ( TUSB_REQ_TYPE_CLASS == p_request->bmRequestType_bit.type ) + { + uint8_t const itf = tu_u16_low(p_request->wIndex); + TU_VERIFY(itf < TU_ARRAY_SIZE(_usbd_dev.itf2drv)); + + usbd_class_driver_t const * driver = get_driver(_usbd_dev.itf2drv[itf]); + TU_VERIFY(driver); + + // forward to class driver: "non-STD request to Interface" + return invoke_class_control(rhport, driver, p_request); + } + + if ( TUSB_REQ_TYPE_STANDARD != p_request->bmRequestType_bit.type ) + { + // Non standard request is not supported + TU_BREAKPOINT(); + return false; + } + + switch ( p_request->bRequest ) + { + case TUSB_REQ_SET_ADDRESS: + // Depending on mcu, status phase could be sent either before or after changing device address, + // or even require stack to not response with status at all + // Therefore DCD must take full responsibility to response and include zlp status packet if needed. + usbd_control_set_request(p_request); // set request since DCD has no access to tud_control_status() API + dcd_set_address(rhport, (uint8_t) p_request->wValue); + // skip tud_control_status() + _usbd_dev.addressed = 1; + break; + + case TUSB_REQ_GET_CONFIGURATION: + { + uint8_t cfg_num = _usbd_dev.cfg_num; + tud_control_xfer(rhport, p_request, &cfg_num, 1); + } + break; + + case TUSB_REQ_SET_CONFIGURATION: + { + uint8_t const cfg_num = (uint8_t) p_request->wValue; + + if ( !_usbd_dev.cfg_num && cfg_num ) TU_ASSERT( process_set_config(rhport, cfg_num) ); + _usbd_dev.cfg_num = cfg_num; + + tud_control_status(rhport, p_request); + } + break; + + case TUSB_REQ_GET_DESCRIPTOR: + TU_VERIFY( process_get_descriptor(rhport, p_request) ); + break; + + case TUSB_REQ_SET_FEATURE: + // Only support remote wakeup for device feature + TU_VERIFY(TUSB_REQ_FEATURE_REMOTE_WAKEUP == p_request->wValue); + + // Host may enable remote wake up before suspending especially HID device + _usbd_dev.remote_wakeup_en = true; + tud_control_status(rhport, p_request); + break; + + case TUSB_REQ_CLEAR_FEATURE: + // Only support remote wakeup for device feature + TU_VERIFY(TUSB_REQ_FEATURE_REMOTE_WAKEUP == p_request->wValue); + + // Host may disable remote wake up after resuming + _usbd_dev.remote_wakeup_en = false; + tud_control_status(rhport, p_request); + break; + + case TUSB_REQ_GET_STATUS: + { + // Device status bit mask + // - Bit 0: Self Powered + // - Bit 1: Remote Wakeup enabled + uint16_t status = (_usbd_dev.self_powered ? 1 : 0) | (_usbd_dev.remote_wakeup_en ? 2 : 0); + tud_control_xfer(rhport, p_request, &status, 2); + } + break; + + // Unknown/Unsupported request + default: TU_BREAKPOINT(); return false; + } + break; + + //------------- Class/Interface Specific Request -------------// + case TUSB_REQ_RCPT_INTERFACE: + { + uint8_t const itf = tu_u16_low(p_request->wIndex); + TU_VERIFY(itf < TU_ARRAY_SIZE(_usbd_dev.itf2drv)); + + usbd_class_driver_t const * driver = get_driver(_usbd_dev.itf2drv[itf]); + TU_VERIFY(driver); + + // all requests to Interface (STD or Class) is forwarded to class driver. + // notable requests are: GET HID REPORT DESCRIPTOR, SET_INTERFACE, GET_INTERFACE + if ( !invoke_class_control(rhport, driver, p_request) ) + { + // For GET_INTERFACE and SET_INTERFACE, it is mandatory to respond even if the class + // driver doesn't use alternate settings or implement this + TU_VERIFY(TUSB_REQ_TYPE_STANDARD == p_request->bmRequestType_bit.type); + + if (TUSB_REQ_GET_INTERFACE == p_request->bRequest) + { + uint8_t alternate = 0; + tud_control_xfer(rhport, p_request, &alternate, 1); + }else if (TUSB_REQ_SET_INTERFACE == p_request->bRequest) + { + tud_control_status(rhport, p_request); + } else + { + return false; + } + } + } + break; + + //------------- Endpoint Request -------------// + case TUSB_REQ_RCPT_ENDPOINT: + { + uint8_t const ep_addr = tu_u16_low(p_request->wIndex); + uint8_t const ep_num = tu_edpt_number(ep_addr); + uint8_t const ep_dir = tu_edpt_dir(ep_addr); + + TU_ASSERT(ep_num < TU_ARRAY_SIZE(_usbd_dev.ep2drv) ); + + usbd_class_driver_t const * driver = get_driver(_usbd_dev.ep2drv[ep_num][ep_dir]); + + if ( TUSB_REQ_TYPE_STANDARD != p_request->bmRequestType_bit.type ) + { + // Forward class request to its driver + TU_VERIFY(driver); + return invoke_class_control(rhport, driver, p_request); + } + else + { + // Handle STD request to endpoint + switch ( p_request->bRequest ) + { + case TUSB_REQ_GET_STATUS: + { + uint16_t status = usbd_edpt_stalled(rhport, ep_addr) ? 0x0001 : 0x0000; + tud_control_xfer(rhport, p_request, &status, 2); + } + break; + + case TUSB_REQ_CLEAR_FEATURE: + case TUSB_REQ_SET_FEATURE: + { + if ( TUSB_REQ_FEATURE_EDPT_HALT == p_request->wValue ) + { + if ( TUSB_REQ_CLEAR_FEATURE == p_request->bRequest ) + { + usbd_edpt_clear_stall(rhport, ep_addr); + }else + { + usbd_edpt_stall(rhport, ep_addr); + } + } + + if (driver) + { + // Some classes such as USBTMC needs to clear/re-init its buffer when receiving CLEAR_FEATURE request + // We will also forward std request targeted endpoint to class drivers as well + + // STD request must always be ACKed regardless of driver returned value + // Also clear complete callback if driver set since it can also stall the request. + (void) invoke_class_control(rhport, driver, p_request); + usbd_control_set_complete_callback(NULL); + + // skip ZLP status if driver already did that + if ( !_usbd_dev.ep_status[0][TUSB_DIR_IN].busy ) tud_control_status(rhport, p_request); + } + } + break; + + // Unknown/Unsupported request + default: TU_BREAKPOINT(); return false; + } + } + } + break; + + // Unknown recipient + default: TU_BREAKPOINT(); return false; + } + + return true; +} + +// Process Set Configure Request +// This function parse configuration descriptor & open drivers accordingly +static bool process_set_config(uint8_t rhport, uint8_t cfg_num) +{ + tusb_desc_configuration_t const * desc_cfg = (tusb_desc_configuration_t const *) tud_descriptor_configuration_cb(cfg_num-1); // index is cfg_num-1 + TU_ASSERT(desc_cfg != NULL && desc_cfg->bDescriptorType == TUSB_DESC_CONFIGURATION); + + // Parse configuration descriptor + _usbd_dev.remote_wakeup_support = (desc_cfg->bmAttributes & TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP) ? 1 : 0; + _usbd_dev.self_powered = (desc_cfg->bmAttributes & TUSB_DESC_CONFIG_ATT_SELF_POWERED) ? 1 : 0; + + // Parse interface descriptor + uint8_t const * p_desc = ((uint8_t const*) desc_cfg) + sizeof(tusb_desc_configuration_t); + uint8_t const * desc_end = ((uint8_t const*) desc_cfg) + desc_cfg->wTotalLength; + + while( p_desc < desc_end ) + { + tusb_desc_interface_assoc_t const * desc_itf_assoc = NULL; + + // Class will always starts with Interface Association (if any) and then Interface descriptor + if ( TUSB_DESC_INTERFACE_ASSOCIATION == tu_desc_type(p_desc) ) + { + desc_itf_assoc = (tusb_desc_interface_assoc_t const *) p_desc; + p_desc = tu_desc_next(p_desc); // next to Interface + } + + TU_ASSERT( TUSB_DESC_INTERFACE == tu_desc_type(p_desc) ); + + tusb_desc_interface_t const * desc_itf = (tusb_desc_interface_t const*) p_desc; + uint16_t const remaining_len = desc_end-p_desc; + + uint8_t drv_id; + for (drv_id = 0; drv_id < TOTAL_DRIVER_COUNT; drv_id++) + { + usbd_class_driver_t const *driver = get_driver(drv_id); + uint16_t const drv_len = driver->open(rhport, desc_itf, remaining_len); + + if ( drv_len > 0 ) + { + // Open successfully, check if length is correct + TU_ASSERT( sizeof(tusb_desc_interface_t) <= drv_len && drv_len <= remaining_len); + + // Interface number must not be used already + TU_ASSERT(DRVID_INVALID == _usbd_dev.itf2drv[desc_itf->bInterfaceNumber]); + + TU_LOG2(" %s opened\r\n", driver->name); + _usbd_dev.itf2drv[desc_itf->bInterfaceNumber] = drv_id; + + // If IAD exist, assign all interfaces to the same driver + if (desc_itf_assoc) + { + // IAD's first interface number and class should match with opened interface + TU_ASSERT(desc_itf_assoc->bFirstInterface == desc_itf->bInterfaceNumber && + desc_itf_assoc->bFunctionClass == desc_itf->bInterfaceClass); + + for(uint8_t i=1; ibInterfaceCount; i++) + { + _usbd_dev.itf2drv[desc_itf->bInterfaceNumber+i] = drv_id; + } + } + + mark_interface_endpoint(_usbd_dev.ep2drv, p_desc, drv_len, drv_id); // TODO refactor + + p_desc += drv_len; // next interface + + break; + } + } + + // Failed if cannot find supported driver + TU_ASSERT(drv_id < TOTAL_DRIVER_COUNT); + } + + // invoke callback + if (tud_mount_cb) tud_mount_cb(); + + return true; +} + +// Helper marking endpoint of interface belongs to class driver +static void mark_interface_endpoint(uint8_t ep2drv[][2], uint8_t const* p_desc, uint16_t desc_len, uint8_t driver_id) +{ + uint16_t len = 0; + + while( len < desc_len ) + { + if ( TUSB_DESC_ENDPOINT == tu_desc_type(p_desc) ) + { + uint8_t const ep_addr = ((tusb_desc_endpoint_t const*) p_desc)->bEndpointAddress; + + ep2drv[tu_edpt_number(ep_addr)][tu_edpt_dir(ep_addr)] = driver_id; + } + + len = (uint16_t)(len + tu_desc_len(p_desc)); + p_desc = tu_desc_next(p_desc); + } +} + +// return descriptor's buffer and update desc_len +static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const * p_request) +{ + tusb_desc_type_t const desc_type = (tusb_desc_type_t) tu_u16_high(p_request->wValue); + uint8_t const desc_index = tu_u16_low( p_request->wValue ); + + switch(desc_type) + { + case TUSB_DESC_DEVICE: + { + TU_LOG2(" Device\r\n"); + + uint16_t len = sizeof(tusb_desc_device_t); + + // Only send up to EP0 Packet Size if not addressed + // This only happens with the very first get device descriptor and EP0 size = 8 or 16. + if ((CFG_TUD_ENDPOINT0_SIZE < sizeof(tusb_desc_device_t)) && !_usbd_dev.addressed) + { + len = CFG_TUD_ENDPOINT0_SIZE; + + // Hack here: we modify the request length to prevent usbd_control response with zlp + ((tusb_control_request_t*) p_request)->wLength = CFG_TUD_ENDPOINT0_SIZE; + } + + return tud_control_xfer(rhport, p_request, (void*) tud_descriptor_device_cb(), len); + } + break; + + case TUSB_DESC_BOS: + { + TU_LOG2(" BOS\r\n"); + + // requested by host if USB > 2.0 ( i.e 2.1 or 3.x ) + if (!tud_descriptor_bos_cb) return false; + + tusb_desc_bos_t const* desc_bos = (tusb_desc_bos_t const*) tud_descriptor_bos_cb(); + + uint16_t total_len; + // Use offsetof to avoid pointer to the odd/misaligned address + memcpy(&total_len, (uint8_t*) desc_bos + offsetof(tusb_desc_bos_t, wTotalLength), 2); + + return tud_control_xfer(rhport, p_request, (void*) desc_bos, total_len); + } + break; + + case TUSB_DESC_CONFIGURATION: + { + TU_LOG2(" Configuration[%u]\r\n", desc_index); + + tusb_desc_configuration_t const* desc_config = (tusb_desc_configuration_t const*) tud_descriptor_configuration_cb(desc_index); + TU_ASSERT(desc_config); + + uint16_t total_len; + // Use offsetof to avoid pointer to the odd/misaligned address + memcpy(&total_len, (uint8_t*) desc_config + offsetof(tusb_desc_configuration_t, wTotalLength), 2); + + return tud_control_xfer(rhport, p_request, (void*) desc_config, total_len); + } + break; + + case TUSB_DESC_STRING: + { + TU_LOG2(" String[%u]\r\n", desc_index); + + // String Descriptor always uses the desc set from user + uint8_t const* desc_str = (uint8_t const*) tud_descriptor_string_cb(desc_index, p_request->wIndex); + TU_VERIFY(desc_str); + + // first byte of descriptor is its size + return tud_control_xfer(rhport, p_request, (void*) desc_str, desc_str[0]); + } + break; + + case TUSB_DESC_DEVICE_QUALIFIER: + TU_LOG2(" Device Qualifier\r\n"); + + // Host sends this request to ask why our device with USB BCD from 2.0 + // but is running at Full/Low Speed. If not highspeed capable stall this request, + // otherwise return the descriptor that could work in highspeed mode + if ( tud_descriptor_device_qualifier_cb ) + { + uint8_t const* desc_qualifier = tud_descriptor_device_qualifier_cb(); + TU_ASSERT(desc_qualifier); + + // first byte of descriptor is its size + return tud_control_xfer(rhport, p_request, (void*) desc_qualifier, desc_qualifier[0]); + }else + { + return false; + } + break; + + case TUSB_DESC_OTHER_SPEED_CONFIG: + TU_LOG2(" Other Speed Configuration\r\n"); + + // After Device Qualifier descriptor is received host will ask for this descriptor + return false; // not supported + break; + + default: return false; + } +} + +//--------------------------------------------------------------------+ +// DCD Event Handler +//--------------------------------------------------------------------+ +void dcd_event_handler(dcd_event_t const * event, bool in_isr) +{ + switch (event->event_id) + { + case DCD_EVENT_UNPLUGGED: + // UNPLUGGED event can be bouncing, only processing if we are currently connected + if ( _usbd_dev.connected ) + { + _usbd_dev.connected = 0; + _usbd_dev.addressed = 0; + _usbd_dev.cfg_num = 0; + _usbd_dev.suspended = 0; + osal_queue_send(_usbd_q, event, in_isr); + } + break; + + case DCD_EVENT_SOF: + return; // skip SOF event for now + break; + + case DCD_EVENT_SUSPEND: + // NOTE: When plugging/unplugging device, the D+/D- state are unstable and + // can accidentally meet the SUSPEND condition ( Bus Idle for 3ms ). + // In addition, some MCUs such as SAMD or boards that haven no VBUS detection cannot distinguish + // suspended vs disconnected. We will skip handling SUSPEND/RESUME event if not currently connected + if ( _usbd_dev.connected ) + { + _usbd_dev.suspended = 1; + osal_queue_send(_usbd_q, event, in_isr); + } + break; + + case DCD_EVENT_RESUME: + // skip event if not connected (especially required for SAMD) + if ( _usbd_dev.connected ) + { + _usbd_dev.suspended = 0; + osal_queue_send(_usbd_q, event, in_isr); + } + break; + + default: + osal_queue_send(_usbd_q, event, in_isr); + break; + } +} + +void dcd_event_bus_signal (uint8_t rhport, dcd_eventid_t eid, bool in_isr) +{ + dcd_event_t event = { .rhport = rhport, .event_id = eid }; + dcd_event_handler(&event, in_isr); +} + +void dcd_event_bus_reset (uint8_t rhport, tusb_speed_t speed, bool in_isr) +{ + dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_BUS_RESET }; + event.bus_reset.speed = speed; + dcd_event_handler(&event, in_isr); +} + +void dcd_event_setup_received(uint8_t rhport, uint8_t const * setup, bool in_isr) +{ + dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_SETUP_RECEIVED }; + memcpy(&event.setup_received, setup, 8); + + dcd_event_handler(&event, in_isr); +} + +void dcd_event_xfer_complete (uint8_t rhport, uint8_t ep_addr, uint32_t xferred_bytes, uint8_t result, bool in_isr) +{ + dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_XFER_COMPLETE }; + + event.xfer_complete.ep_addr = ep_addr; + event.xfer_complete.len = xferred_bytes; + event.xfer_complete.result = result; + + dcd_event_handler(&event, in_isr); +} + +//--------------------------------------------------------------------+ +// Helper +//--------------------------------------------------------------------+ + +// Parse consecutive endpoint descriptors (IN & OUT) +bool usbd_open_edpt_pair(uint8_t rhport, uint8_t const* p_desc, uint8_t ep_count, uint8_t xfer_type, uint8_t* ep_out, uint8_t* ep_in) +{ + for(int i=0; ibDescriptorType && xfer_type == desc_ep->bmAttributes.xfer); + TU_ASSERT(usbd_edpt_open(rhport, desc_ep)); + + if ( tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN ) + { + (*ep_in) = desc_ep->bEndpointAddress; + }else + { + (*ep_out) = desc_ep->bEndpointAddress; + } + + p_desc = tu_desc_next(p_desc); + } + + return true; +} + +// Helper to defer an isr function +void usbd_defer_func(osal_task_func_t func, void* param, bool in_isr) +{ + dcd_event_t event = + { + .rhport = 0, + .event_id = USBD_EVENT_FUNC_CALL, + }; + + event.func_call.func = func; + event.func_call.param = param; + + dcd_event_handler(&event, in_isr); +} + +//--------------------------------------------------------------------+ +// USBD Endpoint API +//--------------------------------------------------------------------+ + +bool usbd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * desc_ep) +{ + TU_LOG2(" Open EP %02X with Size = %u\r\n", desc_ep->bEndpointAddress, desc_ep->wMaxPacketSize.size); + + switch (desc_ep->bmAttributes.xfer) + { + case TUSB_XFER_ISOCHRONOUS: + { + uint16_t const max_epsize = (_usbd_dev.speed == TUSB_SPEED_HIGH ? 1024 : 1023); + TU_ASSERT(desc_ep->wMaxPacketSize.size <= max_epsize); + } + break; + + case TUSB_XFER_BULK: + if (_usbd_dev.speed == TUSB_SPEED_HIGH) + { + // Bulk highspeed must be EXACTLY 512 + TU_ASSERT(desc_ep->wMaxPacketSize.size == 512); + }else + { + // TODO Bulk fullspeed can only be 8, 16, 32, 64 + TU_ASSERT(desc_ep->wMaxPacketSize.size <= 64); + } + break; + + case TUSB_XFER_INTERRUPT: + { + uint16_t const max_epsize = (_usbd_dev.speed == TUSB_SPEED_HIGH ? 1024 : 64); + TU_ASSERT(desc_ep->wMaxPacketSize.size <= max_epsize); + } + break; + + default: return false; + } + + return dcd_edpt_open(rhport, desc_ep); +} + +bool usbd_edpt_claim(uint8_t rhport, uint8_t ep_addr) +{ + (void) rhport; + + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + +#if CFG_TUSB_OS != OPT_OS_NONE + // pre-check to help reducing mutex lock + TU_VERIFY((_usbd_dev.ep_status[epnum][dir].busy == 0) && (_usbd_dev.ep_status[epnum][dir].claimed == 0)); + + osal_mutex_lock(_usbd_mutex, OSAL_TIMEOUT_WAIT_FOREVER); +#endif + + // can only claim the endpoint if it is not busy and not claimed yet. + bool const ret = (_usbd_dev.ep_status[epnum][dir].busy == 0) && (_usbd_dev.ep_status[epnum][dir].claimed == 0); + if (ret) + { + _usbd_dev.ep_status[epnum][dir].claimed = 1; + } + +#if CFG_TUSB_OS != OPT_OS_NONE + osal_mutex_unlock(_usbd_mutex); +#endif + + return ret; +} + +bool usbd_edpt_release(uint8_t rhport, uint8_t ep_addr) +{ + (void) rhport; + + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + +#if CFG_TUSB_OS != OPT_OS_NONE + osal_mutex_lock(_usbd_mutex, OSAL_TIMEOUT_WAIT_FOREVER); +#endif + + // can only release the endpoint if it is claimed and not busy + bool const ret = (_usbd_dev.ep_status[epnum][dir].busy == 0) && (_usbd_dev.ep_status[epnum][dir].claimed == 1); + if (ret) + { + _usbd_dev.ep_status[epnum][dir].claimed = 0; + } + +#if CFG_TUSB_OS != OPT_OS_NONE + osal_mutex_unlock(_usbd_mutex); +#endif + + return ret; +} + +bool usbd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes) +{ + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + + TU_LOG2(" Queue EP %02X with %u bytes ... ", ep_addr, total_bytes); + + // Attempt to transfer on a busy endpoint, sound like an race condition ! + TU_ASSERT(_usbd_dev.ep_status[epnum][dir].busy == 0); + + // Set busy first since the actual transfer can be complete before dcd_edpt_xfer() could return + // and usbd task can preempt and clear the busy + _usbd_dev.ep_status[epnum][dir].busy = true; + + if ( dcd_edpt_xfer(rhport, ep_addr, buffer, total_bytes) ) + { + TU_LOG2("OK\r\n"); + return true; + }else + { + // DCD error, mark endpoint as ready to allow next transfer + _usbd_dev.ep_status[epnum][dir].busy = false; + _usbd_dev.ep_status[epnum][dir].claimed = 0; + TU_LOG2("failed\r\n"); + TU_BREAKPOINT(); + return false; + } +} + +// The number of bytes has to be given explicitly to allow more flexible control of how many +// bytes should be written and second to keep the return value free to give back a boolean +// success message. If total_bytes is too big, the FIFO will copy only what is available +// into the USB buffer! +bool usbd_edpt_iso_xfer(uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes) +{ + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + + TU_LOG2(" Queue ISO EP %02X with %u bytes ... ", ep_addr, total_bytes); + + // Attempt to transfer on a busy endpoint, sound like an race condition ! + TU_ASSERT(_usbd_dev.ep_status[epnum][dir].busy == 0); + + // Set busy first since the actual transfer can be complete before dcd_edpt_xfer() could return + // and usbd task can preempt and clear the busy + _usbd_dev.ep_status[epnum][dir].busy = true; + + if (dcd_edpt_xfer_fifo(rhport, ep_addr, ff, total_bytes)) + { + TU_LOG2("OK\r\n"); + return true; + }else + { + // DCD error, mark endpoint as ready to allow next transfer + _usbd_dev.ep_status[epnum][dir].busy = false; + _usbd_dev.ep_status[epnum][dir].claimed = 0; + TU_LOG2("failed\r\n"); + TU_BREAKPOINT(); + return false; + } +} + +bool usbd_edpt_busy(uint8_t rhport, uint8_t ep_addr) +{ + (void) rhport; + + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + + return _usbd_dev.ep_status[epnum][dir].busy; +} + +void usbd_edpt_stall(uint8_t rhport, uint8_t ep_addr) +{ + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + + dcd_edpt_stall(rhport, ep_addr); + _usbd_dev.ep_status[epnum][dir].stalled = true; + _usbd_dev.ep_status[epnum][dir].busy = true; +} + +void usbd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) +{ + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + + dcd_edpt_clear_stall(rhport, ep_addr); + _usbd_dev.ep_status[epnum][dir].stalled = false; + _usbd_dev.ep_status[epnum][dir].busy = false; +} + +bool usbd_edpt_stalled(uint8_t rhport, uint8_t ep_addr) +{ + (void) rhport; + + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + + return _usbd_dev.ep_status[epnum][dir].stalled; +} + +/** + * usbd_edpt_close will disable an endpoint. + * + * In progress transfers on this EP may be delivered after this call. + * + */ +void usbd_edpt_close(uint8_t rhport, uint8_t ep_addr) +{ + TU_ASSERT(dcd_edpt_close, /**/); + TU_LOG2(" CLOSING Endpoint: 0x%02X\r\n", ep_addr); + + dcd_edpt_close(rhport, ep_addr); + + return; +} + +#endif diff --git a/components/tinyusb/additions/src/vfs_tinyusb.c b/components/tinyusb/additions/src/vfs_tinyusb.c new file mode 100644 index 0000000..0349bb8 --- /dev/null +++ b/components/tinyusb/additions/src/vfs_tinyusb.c @@ -0,0 +1,297 @@ +// Copyright 2020 Espressif Systems (Shanghai) Co. Ltd. +// +// 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 +#include +#include +#include +#include +#include +#include +#include "esp_attr.h" +#include "esp_log.h" +#include "esp_vfs.h" +#include "esp_vfs_dev.h" +#include "tinyusb.h" +#include "tusb_cdc_acm.h" +#include "vfs_tinyusb.h" +#include "sdkconfig.h" + +const static char *TAG = "tusb_vfs"; +#define VFS_TUSB_MAX_PATH 16 +#define VFS_TUSB_PATH_DEFAULT "/dev/tusb_cdc" + +// Token signifying that no character is available +#define NONE -1 + +#define FD_CHECK(fd, ret_val) do { \ + if ((fd) != 0) { \ + errno = EBADF; \ + return (ret_val); \ + } \ + } while (0) + + + +#if CONFIG_NEWLIB_STDOUT_LINE_ENDING_CRLF +# define DEFAULT_TX_MODE ESP_LINE_ENDINGS_CRLF +#elif CONFIG_NEWLIB_STDOUT_LINE_ENDING_CR +# define DEFAULT_TX_MODE ESP_LINE_ENDINGS_CR +#else +# define DEFAULT_TX_MODE ESP_LINE_ENDINGS_LF +#endif + +#if CONFIG_NEWLIB_STDIN_LINE_ENDING_CRLF +# define DEFAULT_RX_MODE ESP_LINE_ENDINGS_CRLF +#elif CONFIG_NEWLIB_STDIN_LINE_ENDING_CR +# define DEFAULT_RX_MODE ESP_LINE_ENDINGS_CR +#else +# define DEFAULT_RX_MODE ESP_LINE_ENDINGS_LF +#endif + +typedef struct { + _lock_t write_lock; + _lock_t read_lock; + esp_line_endings_t tx_mode; // Newline conversion mode when transmitting + esp_line_endings_t rx_mode; // Newline conversion mode when receiving + uint32_t flags; + char vfs_path[VFS_TUSB_MAX_PATH]; + int cdc_intf; +} vfs_tinyusb_t; + +static vfs_tinyusb_t s_vfstusb; + + +static esp_err_t apply_path(char const *path) +{ + if (path != NULL) { + size_t path_len = strlen(path) + 1; + if (path_len > VFS_TUSB_MAX_PATH) { + ESP_LOGE(TAG, "The path is too long; maximum is %d characters", VFS_TUSB_MAX_PATH); + return ESP_ERR_INVALID_ARG; + } + strncpy(s_vfstusb.vfs_path, path, (VFS_TUSB_MAX_PATH - 1)); + } else { + strncpy(s_vfstusb.vfs_path, + VFS_TUSB_PATH_DEFAULT, + (VFS_TUSB_MAX_PATH - 1)); + } + ESP_LOGV(TAG, "Path is set to `%s`", s_vfstusb.vfs_path); + return ESP_OK; +} + +/** + * @brief Fill s_vfstusb + * + * @param cdc_intf - interface of tusb for registration + * @param path - a path where the CDC will be registered + * @return esp_err_t ESP_OK or ESP_ERR_INVALID_ARG + */ +static esp_err_t vfstusb_init(int cdc_intf, char const *path) +{ + s_vfstusb.cdc_intf = cdc_intf; + s_vfstusb.tx_mode = DEFAULT_TX_MODE; + s_vfstusb.rx_mode = DEFAULT_RX_MODE; + + return apply_path(path); +} + +/** + * @brief Clear s_vfstusb to default values + */ +static void vfstusb_deinit(void) +{ + memset(&s_vfstusb, 0, sizeof(s_vfstusb)); +} + + +static int tusb_open(const char *path, int flags, int mode) +{ + (void) mode; + (void) path; + s_vfstusb.flags = flags | O_NONBLOCK; // for now only non-blocking mode is implemented + return 0; +} + +static ssize_t tusb_write(int fd, const void *data, size_t size) +{ + FD_CHECK(fd, -1); + size_t written_sz = 0; + const char *data_c = (const char *)data; + _lock_acquire(&(s_vfstusb.write_lock)); + for (size_t i = 0; i < size; i++) { + int c = data_c[i]; + /* handling the EOL */ + if (c == '\n' && s_vfstusb.tx_mode != ESP_LINE_ENDINGS_LF) { + if (tinyusb_cdcacm_write_queue_char(s_vfstusb.cdc_intf, '\r')) { + written_sz++; + } else { + break; // can't write anymore + } + if (s_vfstusb.tx_mode == ESP_LINE_ENDINGS_CR) { + continue; + } + } + /* write a char */ + if (tinyusb_cdcacm_write_queue_char(s_vfstusb.cdc_intf, c)) { + written_sz++; + } else { + break; // can't write anymore + } + + } + tud_cdc_n_write_flush(s_vfstusb.cdc_intf); + _lock_release(&(s_vfstusb.write_lock)); + return written_sz; +} + +static int tusb_close(int fd) +{ + FD_CHECK(fd, -1); + return 0; +} + +static ssize_t tusb_read(int fd, void *data, size_t size) +{ + FD_CHECK(fd, -1); + char *data_c = (char *) data; + size_t received = 0; + _lock_acquire(&(s_vfstusb.read_lock)); + int cm1 = NONE; + int c = NONE; + + while (received < size) { + cm1 = c; // store the old char + int c = tud_cdc_n_read_char(0); // get a new one + if (s_vfstusb.rx_mode == ESP_LINE_ENDINGS_CR) { + if (c == '\r') { + c = '\n'; + } + } else if (s_vfstusb.rx_mode == ESP_LINE_ENDINGS_CR) { + if ((c == '\n') & (cm1 == '\r')) { + --received; // step back + c = '\n'; + } + } + if ( c == NONE) { // if data ends + break; + } + data_c[received] = (char) c; + ++received; + if (c == '\n') { + break; + } + } + _lock_release(&(s_vfstusb.read_lock)); + if (received > 0) { + return received; + } + errno = EWOULDBLOCK; + return -1; +} + + +static int tusb_fstat(int fd, struct stat *st) +{ + FD_CHECK(fd, -1); + memset(st, 0, sizeof(*st)); + st->st_mode = S_IFCHR; + return 0; +} + +static int tusb_fcntl(int fd, int cmd, int arg) +{ + FD_CHECK(fd, -1); + int result = 0; + switch (cmd) { + case F_GETFL: + result = s_vfstusb.flags; + break; + case F_SETFL: + s_vfstusb.flags = arg; + break; + default: + result = -1; + errno = ENOSYS; + break; + } + return result; +} + +esp_err_t esp_vfs_tusb_cdc_unregister(char const *path) +{ + ESP_LOGD(TAG, "Unregistering TinyUSB driver"); + int res; + + if (path == NULL) { // NULL means using the default path for unregistering: VFS_TUSB_PATH_DEFAULT + res = strcmp(s_vfstusb.vfs_path, VFS_TUSB_PATH_DEFAULT); + } else { + res = strcmp(s_vfstusb.vfs_path, path); + } + + if (res) { + res = ESP_ERR_INVALID_ARG; + ESP_LOGE(TAG, "There is no TinyUSB driver registerred to the path '%s' (err: 0x%x)", s_vfstusb.vfs_path, res); + return res; + } + + + + res = esp_vfs_unregister(s_vfstusb.vfs_path); + if (res != ESP_OK) { + ESP_LOGE(TAG, "Can't unregister TinyUSB driver from '%s' (err: 0x%x)", s_vfstusb.vfs_path, res); + } else { + ESP_LOGD(TAG, "Unregistered TinyUSB driver"); + vfstusb_deinit(); + } + return res; +} + + + + +esp_err_t esp_vfs_tusb_cdc_register(int cdc_intf, char const *path) +{ + ESP_LOGD(TAG, "Registering TinyUSB CDC driver"); + int res; + if (!tusb_cdc_acm_initialized(cdc_intf)) { + ESP_LOGE(TAG, "TinyUSB CDC#%d is not initialized", cdc_intf); + return ESP_ERR_INVALID_STATE; + } + + res = vfstusb_init(cdc_intf, path); + if (res != ESP_OK) { + return res; + } + + esp_vfs_t vfs = { + .flags = ESP_VFS_FLAG_DEFAULT, + .close = &tusb_close, + .fcntl = &tusb_fcntl, + .fstat = &tusb_fstat, + .open = &tusb_open, + .read = &tusb_read, + .write = &tusb_write, + }; + + res = esp_vfs_register(s_vfstusb.vfs_path, &vfs, NULL); + if (res != ESP_OK) { + ESP_LOGE(TAG, "Can't register TinyUSB driver (err: %x)", res); + } else { + ESP_LOGD(TAG, "TinyUSB CDC registered (%s)", s_vfstusb.vfs_path); + } + return res; +} diff --git a/components/tinyusb/additions/tusb/src/class/dfu/dfu.h b/components/tinyusb/additions/tusb/src/class/dfu/dfu.h new file mode 100644 index 0000000..114c827 --- /dev/null +++ b/components/tinyusb/additions/tusb/src/class/dfu/dfu.h @@ -0,0 +1,119 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021 XMOS LIMITED + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef _TUSB_DFU_H_ +#define _TUSB_DFU_H_ + +#include "common/tusb_common.h" + +#ifdef __cplusplus + extern "C" { +#endif + +//--------------------------------------------------------------------+ +// Common Definitions +//--------------------------------------------------------------------+ + +// DFU Protocol +typedef enum +{ + DFU_PROTOCOL_RT = 0x01, + DFU_PROTOCOL_DFU = 0x02, +} dfu_protocol_type_t; + +// DFU Descriptor Type +typedef enum +{ + DFU_DESC_FUNCTIONAL = 0x21, +} dfu_descriptor_type_t; + +// DFU Requests +typedef enum { + DFU_REQUEST_DETACH = 0, + DFU_REQUEST_DNLOAD = 1, + DFU_REQUEST_UPLOAD = 2, + DFU_REQUEST_GETSTATUS = 3, + DFU_REQUEST_CLRSTATUS = 4, + DFU_REQUEST_GETSTATE = 5, + DFU_REQUEST_ABORT = 6, +} dfu_requests_t; + +// DFU States +typedef enum { + APP_IDLE = 0, + APP_DETACH = 1, + DFU_IDLE = 2, + DFU_DNLOAD_SYNC = 3, + DFU_DNBUSY = 4, + DFU_DNLOAD_IDLE = 5, + DFU_MANIFEST_SYNC = 6, + DFU_MANIFEST = 7, + DFU_MANIFEST_WAIT_RESET = 8, + DFU_UPLOAD_IDLE = 9, + DFU_ERROR = 10, +} dfu_state_t; + +// DFU Status +typedef enum { + DFU_STATUS_OK = 0x00, + DFU_STATUS_ERR_TARGET = 0x01, + DFU_STATUS_ERR_FILE = 0x02, + DFU_STATUS_ERR_WRITE = 0x03, + DFU_STATUS_ERR_ERASE = 0x04, + DFU_STATUS_ERR_CHECK_ERASED = 0x05, + DFU_STATUS_ERR_PROG = 0x06, + DFU_STATUS_ERR_VERIFY = 0x07, + DFU_STATUS_ERR_ADDRESS = 0x08, + DFU_STATUS_ERR_NOTDONE = 0x09, + DFU_STATUS_ERR_FIRMWARE = 0x0A, + DFU_STATUS_ERR_VENDOR = 0x0B, + DFU_STATUS_ERR_USBR = 0x0C, + DFU_STATUS_ERR_POR = 0x0D, + DFU_STATUS_ERR_UNKNOWN = 0x0E, + DFU_STATUS_ERR_STALLEDPKT = 0x0F, +} dfu_status_t; + +#define DFU_ATTR_CAN_DOWNLOAD (1u << 0) +#define DFU_ATTR_CAN_UPLOAD (1u << 1) +#define DFU_ATTR_MANIFESTATION_TOLERANT (1u << 2) +#define DFU_ATTR_WILL_DETACH (1u << 3) + +// DFU Status Request Payload +typedef struct TU_ATTR_PACKED +{ + uint8_t bStatus; + uint8_t bwPollTimeout[3]; + uint8_t bState; + uint8_t iString; +} dfu_status_response_t; + +TU_VERIFY_STATIC( sizeof(dfu_status_response_t) == 6, "size is not correct"); + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_DFU_H_ */ diff --git a/components/tinyusb/additions/tusb/src/class/dfu/dfu_device.c b/components/tinyusb/additions/tusb/src/class/dfu/dfu_device.c new file mode 100644 index 0000000..14ca9ba --- /dev/null +++ b/components/tinyusb/additions/tusb/src/class/dfu/dfu_device.c @@ -0,0 +1,480 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021 XMOS LIMITED + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#include "tusb_option.h" + +#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_DFU) + +#include "device/usbd.h" +#include "device/usbd_pvt.h" + +#include "dfu_device.h" + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF +//--------------------------------------------------------------------+ + +//--------------------------------------------------------------------+ +// INTERNAL OBJECT & FUNCTION DECLARATION +//--------------------------------------------------------------------+ +typedef struct +{ + uint8_t attrs; + uint8_t alt; + + dfu_state_t state; + dfu_status_t status; + + bool flashing_in_progress; + uint16_t block; + uint16_t length; + + CFG_TUSB_MEM_ALIGN uint8_t transfer_buf[CFG_TUD_DFU_XFER_BUFSIZE]; +} dfu_state_ctx_t; + +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength; + uint8_t bDescriptorType; + + union { + struct TU_ATTR_PACKED { + uint8_t bitCanDnload : 1; + uint8_t bitCanUpload : 1; + uint8_t bitManifestationTolerant : 1; + uint8_t bitWillDetach : 1; + uint8_t reserved : 4; + } bmAttributes; + + uint8_t bAttributes; + }; + + uint16_t wDetachTimeOut; + uint16_t wTransferSize; + uint16_t bcdDFUVersion; +} tusb_desc_dfu_functional_t; + +// Only a single dfu state is allowed +CFG_TUSB_MEM_SECTION static dfu_state_ctx_t _dfu_ctx; + +static void reset_state(void) +{ + _dfu_ctx.state = DFU_IDLE; + _dfu_ctx.status = DFU_STATUS_OK; + _dfu_ctx.flashing_in_progress = false; +} + +static bool reply_getstatus(uint8_t rhport, tusb_control_request_t const * request, dfu_state_t state, dfu_status_t status, uint32_t timeout); +static bool process_download_get_status(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request); +static bool process_manifest_get_status(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request); + +//--------------------------------------------------------------------+ +// Debug +//--------------------------------------------------------------------+ +#if CFG_TUSB_DEBUG >= 2 + +static tu_lookup_entry_t const _dfu_request_lookup[] = +{ + { .key = DFU_REQUEST_DETACH , .data = "DETACH" }, + { .key = DFU_REQUEST_DNLOAD , .data = "DNLOAD" }, + { .key = DFU_REQUEST_UPLOAD , .data = "UPLOAD" }, + { .key = DFU_REQUEST_GETSTATUS , .data = "GETSTATUS" }, + { .key = DFU_REQUEST_CLRSTATUS , .data = "CLRSTATUS" }, + { .key = DFU_REQUEST_GETSTATE , .data = "GETSTATE" }, + { .key = DFU_REQUEST_ABORT , .data = "ABORT" }, +}; + +static tu_lookup_table_t const _dfu_request_table = +{ + .count = TU_ARRAY_SIZE(_dfu_request_lookup), + .items = _dfu_request_lookup +}; + +static tu_lookup_entry_t const _dfu_state_lookup[] = +{ + { .key = APP_IDLE , .data = "APP_IDLE" }, + { .key = APP_DETACH , .data = "APP_DETACH" }, + { .key = DFU_IDLE , .data = "IDLE" }, + { .key = DFU_DNLOAD_SYNC , .data = "DNLOAD_SYNC" }, + { .key = DFU_DNBUSY , .data = "DNBUSY" }, + { .key = DFU_DNLOAD_IDLE , .data = "DNLOAD_IDLE" }, + { .key = DFU_MANIFEST_SYNC , .data = "MANIFEST_SYNC" }, + { .key = DFU_MANIFEST , .data = "MANIFEST" }, + { .key = DFU_MANIFEST_WAIT_RESET , .data = "MANIFEST_WAIT_RESET" }, + { .key = DFU_UPLOAD_IDLE , .data = "UPLOAD_IDLE" }, + { .key = DFU_ERROR , .data = "ERROR" }, +}; + +static tu_lookup_table_t const _dfu_state_table = +{ + .count = TU_ARRAY_SIZE(_dfu_state_lookup), + .items = _dfu_state_lookup +}; + +static tu_lookup_entry_t const _dfu_status_lookup[] = +{ + { .key = DFU_STATUS_OK , .data = "OK" }, + { .key = DFU_STATUS_ERR_TARGET , .data = "errTARGET" }, + { .key = DFU_STATUS_ERR_FILE , .data = "errFILE" }, + { .key = DFU_STATUS_ERR_WRITE , .data = "errWRITE" }, + { .key = DFU_STATUS_ERR_ERASE , .data = "errERASE" }, + { .key = DFU_STATUS_ERR_CHECK_ERASED , .data = "errCHECK_ERASED" }, + { .key = DFU_STATUS_ERR_PROG , .data = "errPROG" }, + { .key = DFU_STATUS_ERR_VERIFY , .data = "errVERIFY" }, + { .key = DFU_STATUS_ERR_ADDRESS , .data = "errADDRESS" }, + { .key = DFU_STATUS_ERR_NOTDONE , .data = "errNOTDONE" }, + { .key = DFU_STATUS_ERR_FIRMWARE , .data = "errFIRMWARE" }, + { .key = DFU_STATUS_ERR_VENDOR , .data = "errVENDOR" }, + { .key = DFU_STATUS_ERR_USBR , .data = "errUSBR" }, + { .key = DFU_STATUS_ERR_POR , .data = "errPOR" }, + { .key = DFU_STATUS_ERR_UNKNOWN , .data = "errUNKNOWN" }, + { .key = DFU_STATUS_ERR_STALLEDPKT , .data = "errSTALLEDPKT" }, +}; + +static tu_lookup_table_t const _dfu_status_table = +{ + .count = TU_ARRAY_SIZE(_dfu_status_lookup), + .items = _dfu_status_lookup +}; + +#endif + +//--------------------------------------------------------------------+ +// USBD Driver API +//--------------------------------------------------------------------+ +void dfu_moded_reset(uint8_t rhport) +{ + (void) rhport; + + _dfu_ctx.attrs = 0; + _dfu_ctx.alt = 0; + + reset_state(); +} + +void dfu_moded_init(void) +{ + dfu_moded_reset(0); +} + +uint16_t dfu_moded_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len) +{ + (void) rhport; + + //------------- Interface (with Alt) descriptor -------------// + uint8_t const itf_num = itf_desc->bInterfaceNumber; + uint8_t alt_count = 0; + + uint16_t drv_len = 0; + while(itf_desc->bInterfaceSubClass == TUD_DFU_APP_SUBCLASS && itf_desc->bInterfaceProtocol == DFU_PROTOCOL_DFU) + { + TU_ASSERT(max_len > drv_len, 0); + + // Alternate must have the same interface number + TU_ASSERT(itf_desc->bInterfaceNumber == itf_num, 0); + + // Alt should increase by one every time + TU_ASSERT(itf_desc->bAlternateSetting == alt_count, 0); + alt_count++; + + drv_len += tu_desc_len(itf_desc); + itf_desc = (tusb_desc_interface_t const *) tu_desc_next(itf_desc); + } + + //------------- DFU Functional descriptor -------------// + tusb_desc_dfu_functional_t const *func_desc = (tusb_desc_dfu_functional_t const *) itf_desc; + TU_ASSERT(tu_desc_type(func_desc) == TUSB_DESC_FUNCTIONAL, 0); + drv_len += sizeof(tusb_desc_dfu_functional_t); + + _dfu_ctx.attrs = func_desc->bAttributes; + + // CFG_TUD_DFU_XFER_BUFSIZE has to be set to the buffer size used in TUD_DFU_DESCRIPTOR + uint16_t const transfer_size = tu_le16toh( tu_unaligned_read16((uint8_t const*) func_desc + offsetof(tusb_desc_dfu_functional_t, wTransferSize)) ); + TU_ASSERT(transfer_size <= CFG_TUD_DFU_XFER_BUFSIZE, drv_len); + + return drv_len; +} + +// Invoked when a control transfer occurred on an interface of this class +// Driver response accordingly to the request and the transfer stage (setup/data/ack) +// return false to stall control endpoint (e.g unsupported request) +bool dfu_moded_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request) +{ + TU_VERIFY(request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE); + + TU_LOG2(" DFU State : %s, Status: %s\r\n", tu_lookup_find(&_dfu_state_table, _dfu_ctx.state), tu_lookup_find(&_dfu_status_table, _dfu_ctx.status)); + + if ( request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD ) + { + // Standard request include GET/SET_INTERFACE + switch ( request->bRequest ) + { + case TUSB_REQ_SET_INTERFACE: + if ( stage == CONTROL_STAGE_SETUP ) + { + // Switch Alt interface and reset state machine + _dfu_ctx.alt = (uint8_t) request->wValue; + reset_state(); + return tud_control_status(rhport, request); + } + break; + + case TUSB_REQ_GET_INTERFACE: + if(stage == CONTROL_STAGE_SETUP) + { + return tud_control_xfer(rhport, request, &_dfu_ctx.alt, 1); + } + break; + + // unsupported request + default: return false; + } + } + else if ( request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS ) + { + TU_LOG2(" DFU Request: %s\r\n", tu_lookup_find(&_dfu_request_table, request->bRequest)); + + // Class request + switch ( request->bRequest ) + { + case DFU_REQUEST_DETACH: + if ( stage == CONTROL_STAGE_SETUP ) + { + tud_control_status(rhport, request); + } + else if ( stage == CONTROL_STAGE_ACK ) + { + if ( tud_dfu_detach_cb ) tud_dfu_detach_cb(); + } + break; + + case DFU_REQUEST_CLRSTATUS: + if ( stage == CONTROL_STAGE_SETUP ) + { + reset_state(); + tud_control_status(rhport, request); + } + break; + + case DFU_REQUEST_GETSTATE: + if ( stage == CONTROL_STAGE_SETUP ) + { + tud_control_xfer(rhport, request, &_dfu_ctx.state, 1); + } + break; + + case DFU_REQUEST_ABORT: + if ( stage == CONTROL_STAGE_SETUP ) + { + reset_state(); + tud_control_status(rhport, request); + } + else if ( stage == CONTROL_STAGE_ACK ) + { + if ( tud_dfu_abort_cb ) tud_dfu_abort_cb(_dfu_ctx.alt); + } + break; + + case DFU_REQUEST_UPLOAD: + if ( stage == CONTROL_STAGE_SETUP ) + { + TU_VERIFY(_dfu_ctx.attrs & DFU_ATTR_CAN_UPLOAD); + TU_VERIFY(tud_dfu_upload_cb); + TU_VERIFY(request->wLength <= CFG_TUD_DFU_XFER_BUFSIZE); + + uint16_t const xfer_len = tud_dfu_upload_cb(_dfu_ctx.alt, request->wValue, _dfu_ctx.transfer_buf, request->wLength); + + return tud_control_xfer(rhport, request, _dfu_ctx.transfer_buf, xfer_len); + } + break; + + case DFU_REQUEST_DNLOAD: + if ( stage == CONTROL_STAGE_SETUP ) + { + TU_VERIFY(_dfu_ctx.attrs & DFU_ATTR_CAN_DOWNLOAD); + TU_VERIFY(_dfu_ctx.state == DFU_IDLE || _dfu_ctx.state == DFU_DNLOAD_IDLE); + TU_VERIFY(request->wLength <= CFG_TUD_DFU_XFER_BUFSIZE); + + // set to true for both download and manifest + _dfu_ctx.flashing_in_progress = true; + + // save block and length for flashing + _dfu_ctx.block = request->wValue; + _dfu_ctx.length = request->wLength; + + if ( request->wLength ) + { + // Download with payload -> transition to DOWNLOAD SYNC + _dfu_ctx.state = DFU_DNLOAD_SYNC; + return tud_control_xfer(rhport, request, _dfu_ctx.transfer_buf, request->wLength); + } + else + { + // Download is complete -> transition to MANIFEST SYNC + _dfu_ctx.state = DFU_MANIFEST_SYNC; + return tud_control_status(rhport, request); + } + } + break; + + case DFU_REQUEST_GETSTATUS: + switch ( _dfu_ctx.state ) + { + case DFU_DNLOAD_SYNC: + return process_download_get_status(rhport, stage, request); + break; + + case DFU_MANIFEST_SYNC: + return process_manifest_get_status(rhport, stage, request); + break; + + default: + if ( stage == CONTROL_STAGE_SETUP ) return reply_getstatus(rhport, request, _dfu_ctx.state, _dfu_ctx.status, 0); + break; + } + break; + + default: return false; // stall unsupported request + } + }else + { + return false; // unsupported request + } + + return true; +} + +void tud_dfu_finish_flashing(uint8_t status) +{ + _dfu_ctx.flashing_in_progress = false; + + if ( status == DFU_STATUS_OK ) + { + if (_dfu_ctx.state == DFU_DNBUSY) + { + _dfu_ctx.state = DFU_DNLOAD_SYNC; + } + else if (_dfu_ctx.state == DFU_MANIFEST) + { + _dfu_ctx.state = (_dfu_ctx.attrs & DFU_ATTR_MANIFESTATION_TOLERANT) + ? DFU_MANIFEST_SYNC : DFU_MANIFEST_WAIT_RESET; + } + } + else + { + // failed while flashing, move to dfuError + _dfu_ctx.state = DFU_ERROR; + _dfu_ctx.status = (dfu_status_t)status; + } +} + +static bool process_download_get_status(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request) +{ + if ( stage == CONTROL_STAGE_SETUP ) + { + // only transition to next state on CONTROL_STAGE_ACK + dfu_state_t next_state; + uint32_t timeout; + + if ( _dfu_ctx.flashing_in_progress ) + { + next_state = DFU_DNBUSY; + timeout = tud_dfu_get_timeout_cb(_dfu_ctx.alt, (uint8_t) next_state); + } + else + { + next_state = DFU_DNLOAD_IDLE; + timeout = 0; + } + + return reply_getstatus(rhport, request, next_state, _dfu_ctx.status, timeout); + } + else if ( stage == CONTROL_STAGE_ACK ) + { + if ( _dfu_ctx.flashing_in_progress ) + { + _dfu_ctx.state = DFU_DNBUSY; + tud_dfu_download_cb(_dfu_ctx.alt, _dfu_ctx.block, _dfu_ctx.transfer_buf, _dfu_ctx.length); + }else + { + _dfu_ctx.state = DFU_DNLOAD_IDLE; + } + } + + return true; +} + +static bool process_manifest_get_status(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request) +{ + if ( stage == CONTROL_STAGE_SETUP ) + { + // only transition to next state on CONTROL_STAGE_ACK + dfu_state_t next_state; + uint32_t timeout; + + if ( _dfu_ctx.flashing_in_progress ) + { + next_state = DFU_MANIFEST; + timeout = tud_dfu_get_timeout_cb(_dfu_ctx.alt, next_state); + } + else + { + next_state = DFU_IDLE; + timeout = 0; + } + + return reply_getstatus(rhport, request, next_state, _dfu_ctx.status, timeout); + } + else if ( stage == CONTROL_STAGE_ACK ) + { + if ( _dfu_ctx.flashing_in_progress ) + { + _dfu_ctx.state = DFU_MANIFEST; + tud_dfu_manifest_cb(_dfu_ctx.alt); + } + else + { + _dfu_ctx.state = DFU_IDLE; + } + } + + return true; +} + +static bool reply_getstatus(uint8_t rhport, tusb_control_request_t const * request, dfu_state_t state, dfu_status_t status, uint32_t timeout) +{ + dfu_status_response_t resp; + resp.bStatus = (uint8_t) status; + resp.bwPollTimeout[0] = TU_U32_BYTE0(timeout); + resp.bwPollTimeout[1] = TU_U32_BYTE1(timeout); + resp.bwPollTimeout[2] = TU_U32_BYTE2(timeout); + resp.bState = (uint8_t) state; + resp.iString = 0; + + return tud_control_xfer(rhport, request, &resp, sizeof(dfu_status_response_t)); +} + +#endif diff --git a/components/tinyusb/additions/tusb/src/class/dfu/dfu_device.h b/components/tinyusb/additions/tusb/src/class/dfu/dfu_device.h new file mode 100644 index 0000000..084bfc3 --- /dev/null +++ b/components/tinyusb/additions/tusb/src/class/dfu/dfu_device.h @@ -0,0 +1,124 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021 XMOS LIMITED + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef _TUSB_DFU_DEVICE_H_ +#define _TUSB_DFU_DEVICE_H_ + +#include "dfu.h" + +#ifdef __cplusplus + extern "C" { +#endif + +//--------------------------------------------------------------------+ +// Class Driver Default Configure & Validation +//--------------------------------------------------------------------+ + +#if !defined(CFG_TUD_DFU_XFER_BUFSIZE) +#define CFG_TUD_DFU_XFER_BUFSIZE 512 +#endif + +#define TU_U32_BYTE3(_u32) ((uint8_t) ((((uint32_t) _u32) >> 24) & 0x000000ff)) // MSB +#define TU_U32_BYTE2(_u32) ((uint8_t) ((((uint32_t) _u32) >> 16) & 0x000000ff)) +#define TU_U32_BYTE1(_u32) ((uint8_t) ((((uint32_t) _u32) >> 8) & 0x000000ff)) +#define TU_U32_BYTE0(_u32) ((uint8_t) (((uint32_t) _u32) & 0x000000ff)) // LSB + +// Length of template descriptor: 9 bytes + number of alternatives * 9 +#define TUD_DFU_DESC_LEN(_alt_count) (9 + (_alt_count) * 9) + +// Interface number, Alternate count, starting string index, attributes, detach timeout, transfer size +// Note: Alternate count must be numberic or macro, string index is increased by one for each Alt interface +#define TUD_DFU_DESCRIPTOR(_itfnum, _alt_count, _stridx, _attr, _timeout, _xfer_size) \ + TU_XSTRCAT(_TUD_DFU_ALT_,_alt_count)(_itfnum, 0, _stridx), \ + /* Function */ \ + 9, DFU_DESC_FUNCTIONAL, _attr, U16_TO_U8S_LE(_timeout), U16_TO_U8S_LE(_xfer_size), U16_TO_U8S_LE(0x0101) + +#define _TUD_DFU_ALT(_itfnum, _alt, _stridx) \ + /* Interface */ \ + 9, TUSB_DESC_INTERFACE, _itfnum, _alt, 0, TUD_DFU_APP_CLASS, TUD_DFU_APP_SUBCLASS, DFU_PROTOCOL_DFU, _stridx + +#define _TUD_DFU_ALT_1(_itfnum, _alt_count, _stridx) \ + _TUD_DFU_ALT(_itfnum, _alt_count, _stridx) + +#define _TUD_DFU_ALT_2(_itfnum, _alt_count, _stridx) \ + _TUD_DFU_ALT(_itfnum, _alt_count, _stridx), \ + _TUD_DFU_ALT_1(_itfnum, _alt_count+1, _stridx+1) + +//--------------------------------------------------------------------+ +// Application API +//--------------------------------------------------------------------+ + +// Must be called when the application is done with flashing started by +// tud_dfu_download_cb() and tud_dfu_manifest_cb(). +// status is DFU_STATUS_OK if successful, any other error status will cause state to enter dfuError +void tud_dfu_finish_flashing(uint8_t status); + +//--------------------------------------------------------------------+ +// Application Callback API (weak is optional) +//--------------------------------------------------------------------+ + +// Note: alt is used as the partition number, in order to support multiple partitions like FLASH, EEPROM, etc. + +// Invoked right before tud_dfu_download_cb() (state=DFU_DNBUSY) or tud_dfu_manifest_cb() (state=DFU_MANIFEST) +// Application return timeout in milliseconds (bwPollTimeout) for the next download/manifest operation. +// During this period, USB host won't try to communicate with us. +uint32_t tud_dfu_get_timeout_cb(uint8_t alt, uint8_t state); + +// Invoked when received DFU_DNLOAD (wLength>0) following by DFU_GETSTATUS (state=DFU_DNBUSY) requests +// This callback could be returned before flashing op is complete (async). +// Once finished flashing, application must call tud_dfu_finish_flashing() +void tud_dfu_download_cb (uint8_t alt, uint16_t block_num, uint8_t const *data, uint16_t length); + +// Invoked when download process is complete, received DFU_DNLOAD (wLength=0) following by DFU_GETSTATUS (state=Manifest) +// Application can do checksum, or actual flashing if buffered entire image previously. +// Once finished flashing, application must call tud_dfu_finish_flashing() +void tud_dfu_manifest_cb(uint8_t alt); + +// Invoked when received DFU_UPLOAD request +// Application must populate data with up to length bytes and +// Return the number of written bytes +TU_ATTR_WEAK uint16_t tud_dfu_upload_cb(uint8_t alt, uint16_t block_num, uint8_t* data, uint16_t length); + +// Invoked when a DFU_DETACH request is received +TU_ATTR_WEAK void tud_dfu_detach_cb(void); + +// Invoked when the Host has terminated a download or upload transfer +TU_ATTR_WEAK void tud_dfu_abort_cb(uint8_t alt); + +//--------------------------------------------------------------------+ +// Internal Class Driver API +//--------------------------------------------------------------------+ +void dfu_moded_init(void); +void dfu_moded_reset(uint8_t rhport); +uint16_t dfu_moded_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len); +bool dfu_moded_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request); + + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_DFU_MODE_DEVICE_H_ */ diff --git a/components/tinyusb/additions/tusb/src/class/dfu/dfu_rt_device.c b/components/tinyusb/additions/tusb/src/class/dfu/dfu_rt_device.c new file mode 100644 index 0000000..afee2aa --- /dev/null +++ b/components/tinyusb/additions/tusb/src/class/dfu/dfu_rt_device.c @@ -0,0 +1,128 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Sylvain Munaut + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#include "tusb_option.h" + +#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_DFU_RUNTIME) + +#include "device/usbd.h" +#include "device/usbd_pvt.h" + +#include "dfu_rt_device.h" + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF +//--------------------------------------------------------------------+ + +//--------------------------------------------------------------------+ +// INTERNAL OBJECT & FUNCTION DECLARATION +//--------------------------------------------------------------------+ + +//--------------------------------------------------------------------+ +// USBD Driver API +//--------------------------------------------------------------------+ +void dfu_rtd_init(void) +{ +} + +void dfu_rtd_reset(uint8_t rhport) +{ + (void) rhport; +} + +uint16_t dfu_rtd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len) +{ + (void) rhport; + (void) max_len; + + // Ensure this is DFU Runtime + TU_VERIFY((itf_desc->bInterfaceSubClass == TUD_DFU_APP_SUBCLASS) && + (itf_desc->bInterfaceProtocol == DFU_PROTOCOL_RT), 0); + + uint8_t const * p_desc = tu_desc_next( itf_desc ); + uint16_t drv_len = sizeof(tusb_desc_interface_t); + + if ( TUSB_DESC_FUNCTIONAL == tu_desc_type(p_desc) ) + { + drv_len += tu_desc_len(p_desc); + p_desc = tu_desc_next(p_desc); + } + + return drv_len; +} + +// Invoked when a control transfer occurred on an interface of this class +// Driver response accordingly to the request and the transfer stage (setup/data/ack) +// return false to stall control endpoint (e.g unsupported request) +bool dfu_rtd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request) +{ + // nothing to do with DATA or ACK stage + if ( stage != CONTROL_STAGE_SETUP ) return true; + + TU_VERIFY(request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE); + + // dfu-util will try to claim the interface with SET_INTERFACE request before sending DFU request + if ( TUSB_REQ_TYPE_STANDARD == request->bmRequestType_bit.type && + TUSB_REQ_SET_INTERFACE == request->bRequest ) + { + tud_control_status(rhport, request); + return true; + } + + // Handle class request only from here + TU_VERIFY(request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS); + + switch (request->bRequest) + { + case DFU_REQUEST_DETACH: + { + TU_LOG2(" DFU RT Request: DETACH\r\n"); + tud_control_status(rhport, request); + tud_dfu_runtime_reboot_to_dfu_cb(); + } + break; + + case DFU_REQUEST_GETSTATUS: + { + TU_LOG2(" DFU RT Request: GETSTATUS\r\n"); + dfu_status_response_t resp; + // Status = OK, Poll timeout is ignored during RT, State = APP_IDLE, IString = 0 + memset(&resp, 0x00, sizeof(dfu_status_response_t)); + tud_control_xfer(rhport, request, &resp, sizeof(dfu_status_response_t)); + } + break; + + default: + { + TU_LOG2(" DFU RT Unexpected Request: %d\r\n", request->bRequest); + return false; // stall unsupported request + } + } + + return true; +} + +#endif diff --git a/components/tinyusb/additions/tusb/src/class/dfu/dfu_rt_device.h b/components/tinyusb/additions/tusb/src/class/dfu/dfu_rt_device.h new file mode 100644 index 0000000..babaa82 --- /dev/null +++ b/components/tinyusb/additions/tusb/src/class/dfu/dfu_rt_device.h @@ -0,0 +1,54 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Sylvain Munaut + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef _TUSB_DFU_RT_DEVICE_H_ +#define _TUSB_DFU_RT_DEVICE_H_ + +#include "dfu.h" + +#ifdef __cplusplus + extern "C" { +#endif + +//--------------------------------------------------------------------+ +// Application Callback API (weak is optional) +//--------------------------------------------------------------------+ +// Invoked when a DFU_DETACH request is received and bitWillDetach is set +void tud_dfu_runtime_reboot_to_dfu_cb(void); + +//--------------------------------------------------------------------+ +// Internal Class Driver API +//--------------------------------------------------------------------+ +void dfu_rtd_init(void); +void dfu_rtd_reset(uint8_t rhport); +uint16_t dfu_rtd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len); +bool dfu_rtd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request); + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_DFU_RT_DEVICE_H_ */ diff --git a/components/tinyusb/additions/tusb/src/class/net/net_device.c b/components/tinyusb/additions/tusb/src/class/net/net_device.c new file mode 100644 index 0000000..64066d8 --- /dev/null +++ b/components/tinyusb/additions/tusb/src/class/net/net_device.c @@ -0,0 +1,481 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2020 Peter Lawrence + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#include "tusb_option.h" + +#if ( TUSB_OPT_DEVICE_ENABLED && CFG_TUD_NET ) + +#include "class/net/net_device.h" +#include "device/usbd_pvt.h" +#include "rndis_protocol.h" + +void rndis_class_set_handler(uint8_t *data, int size); /* found in ./misc/networking/rndis_reports.c */ + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF +//--------------------------------------------------------------------+ +typedef struct +{ + uint8_t itf_num; // Index number of Management Interface, +1 for Data Interface + uint8_t itf_data_alt; // Alternate setting of Data Interface. 0 : inactive, 1 : active + + uint8_t ep_notif; + uint8_t ep_in; + uint8_t ep_out; + + bool ecm_mode; + + // Endpoint descriptor use to open/close when receving SetInterface + // TODO since configuration descriptor may not be long-lived memory, we should + // keep a copy of endpoint attribute instead + uint8_t const * ecm_desc_epdata; + +} netd_interface_t; + +#define CFG_TUD_NET_PACKET_PREFIX_LEN sizeof(rndis_data_packet_t) +#define CFG_TUD_NET_PACKET_SUFFIX_LEN 0 + +CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static uint8_t received[CFG_TUD_NET_PACKET_PREFIX_LEN + CFG_TUD_NET_MTU + CFG_TUD_NET_PACKET_PREFIX_LEN]; +CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static uint8_t transmitted[CFG_TUD_NET_PACKET_PREFIX_LEN + CFG_TUD_NET_MTU + CFG_TUD_NET_PACKET_PREFIX_LEN]; + +struct ecm_notify_struct +{ + tusb_control_request_t header; + uint32_t downlink, uplink; +}; + +static const struct ecm_notify_struct ecm_notify_nc = +{ + .header = { + .bmRequestType = 0xA1, + .bRequest = 0 /* NETWORK_CONNECTION aka NetworkConnection */, + .wValue = 1 /* Connected */, + .wLength = 0, + }, +}; + +static const struct ecm_notify_struct ecm_notify_csc = +{ + .header = { + .bmRequestType = 0xA1, + .bRequest = 0x2A /* CONNECTION_SPEED_CHANGE aka ConnectionSpeedChange */, + .wLength = 8, + }, + .downlink = 9728000, + .uplink = 9728000, +}; + +// TODO remove CFG_TUSB_MEM_SECTION, control internal buffer is already in this special section +CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static union +{ + uint8_t rndis_buf[120]; + struct ecm_notify_struct ecm_buf; +} notify; + +//--------------------------------------------------------------------+ +// INTERNAL OBJECT & FUNCTION DECLARATION +//--------------------------------------------------------------------+ +// TODO remove CFG_TUSB_MEM_SECTION +CFG_TUSB_MEM_SECTION static netd_interface_t _netd_itf; + +static bool can_xmit; + +void tud_network_recv_renew(void) +{ + usbd_edpt_xfer(TUD_OPT_RHPORT, _netd_itf.ep_out, received, sizeof(received)); +} + +static void do_in_xfer(uint8_t *buf, uint16_t len) +{ + can_xmit = false; + + if (tud_network_idle_status_change_cb) { + tud_network_idle_status_change_cb(can_xmit); + } + + usbd_edpt_xfer(TUD_OPT_RHPORT, _netd_itf.ep_in, buf, len); +} + +void netd_report(uint8_t *buf, uint16_t len) +{ + usbd_edpt_xfer(TUD_OPT_RHPORT, _netd_itf.ep_notif, buf, len); +} + +//--------------------------------------------------------------------+ +// USBD Driver API +//--------------------------------------------------------------------+ +void netd_init(void) +{ + tu_memclr(&_netd_itf, sizeof(_netd_itf)); +} + +void netd_reset(uint8_t rhport) +{ + (void) rhport; + + netd_init(); +} + +uint16_t netd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len) +{ + bool const is_rndis = (TUD_RNDIS_ITF_CLASS == itf_desc->bInterfaceClass && + TUD_RNDIS_ITF_SUBCLASS == itf_desc->bInterfaceSubClass && + TUD_RNDIS_ITF_PROTOCOL == itf_desc->bInterfaceProtocol); + + bool const is_ecm = (TUSB_CLASS_CDC == itf_desc->bInterfaceClass && + CDC_COMM_SUBCLASS_ETHERNET_CONTROL_MODEL == itf_desc->bInterfaceSubClass && + 0x00 == itf_desc->bInterfaceProtocol); + + TU_VERIFY(is_rndis || is_ecm, 0); + + // confirm interface hasn't already been allocated + TU_ASSERT(0 == _netd_itf.ep_notif, 0); + + // sanity check the descriptor + _netd_itf.ecm_mode = is_ecm; + + //------------- Management Interface -------------// + _netd_itf.itf_num = itf_desc->bInterfaceNumber; + + uint16_t drv_len = sizeof(tusb_desc_interface_t); + uint8_t const * p_desc = tu_desc_next( itf_desc ); + + // Communication Functional Descriptors + while ( TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) && drv_len <= max_len ) + { + drv_len += tu_desc_len(p_desc); + p_desc = tu_desc_next(p_desc); + } + + // notification endpoint (if any) + if ( TUSB_DESC_ENDPOINT == tu_desc_type(p_desc) ) + { + TU_ASSERT( usbd_edpt_open(rhport, (tusb_desc_endpoint_t const *) p_desc), 0 ); + + _netd_itf.ep_notif = ((tusb_desc_endpoint_t const *) p_desc)->bEndpointAddress; + + drv_len += tu_desc_len(p_desc); + p_desc = tu_desc_next(p_desc); + } + + //------------- Data Interface -------------// + // - RNDIS Data followed immediately by a pair of endpoints + // - CDC-ECM data interface has 2 alternate settings + // - 0 : zero endpoints for inactive (default) + // - 1 : IN & OUT endpoints for active networking + TU_ASSERT(TUSB_DESC_INTERFACE == tu_desc_type(p_desc), 0); + + do + { + tusb_desc_interface_t const * data_itf_desc = (tusb_desc_interface_t const *) p_desc; + TU_ASSERT(TUSB_CLASS_CDC_DATA == data_itf_desc->bInterfaceClass, 0); + + drv_len += tu_desc_len(p_desc); + p_desc = tu_desc_next(p_desc); + }while( _netd_itf.ecm_mode && (TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) && (drv_len <= max_len) ); + + // Pair of endpoints + TU_ASSERT(TUSB_DESC_ENDPOINT == tu_desc_type(p_desc), 0); + + if ( _netd_itf.ecm_mode ) + { + // ECM by default is in-active, save the endpoint attribute + // to open later when received setInterface + _netd_itf.ecm_desc_epdata = p_desc; + }else + { + // Open endpoint pair for RNDIS + TU_ASSERT( usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &_netd_itf.ep_out, &_netd_itf.ep_in), 0 ); + + tud_network_init_cb(); + + // we are ready to transmit a packet + can_xmit = true; + if (tud_network_idle_status_change_cb) { + tud_network_idle_status_change_cb(can_xmit); + } + + // prepare for incoming packets + tud_network_recv_renew(); + } + + drv_len += 2*sizeof(tusb_desc_endpoint_t); + + return drv_len; +} + +void ecm_close(void) +{ + printf("Ecm close\n"); + tusb_control_request_t notify_data = + { + .bmRequestType = 0xA1, + .bRequest = 0 /* NETWORK_CONNECTION aka NetworkConnection */, + .wValue = 0 /* Disconnected */, + .wLength = 0, + }; + notify_data.wIndex = _netd_itf.itf_num; + netd_report((uint8_t *)¬ify_data, sizeof(notify_data)); +} + +void ecm_open(void) +{ + printf("Ecm OPEN\n"); + tusb_control_request_t notify_data = + { + .bmRequestType = 0xA1, + .bRequest = 0 /* NETWORK_CONNECTION aka NetworkConnection */, + .wValue = 1 /* Connected */, + .wLength = 0, + }; + notify_data.wIndex = _netd_itf.itf_num; + netd_report((uint8_t *)¬ify_data, sizeof(notify_data)); +} + +static void ecm_report(bool nc) +{ + notify.ecm_buf = (nc) ? ecm_notify_nc : ecm_notify_csc; + notify.ecm_buf.header.wIndex = _netd_itf.itf_num; + netd_report((uint8_t *)¬ify.ecm_buf, (nc) ? sizeof(notify.ecm_buf.header) : sizeof(notify.ecm_buf)); +} + +// Invoked when a control transfer occurred on an interface of this class +// Driver response accordingly to the request and the transfer stage (setup/data/ack) +// return false to stall control endpoint (e.g unsupported request) +bool netd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request) +{ + if ( stage == CONTROL_STAGE_SETUP ) + { + switch ( request->bmRequestType_bit.type ) + { + case TUSB_REQ_TYPE_STANDARD: + switch ( request->bRequest ) + { + case TUSB_REQ_GET_INTERFACE: + { + uint8_t const req_itfnum = (uint8_t) request->wIndex; + TU_VERIFY(_netd_itf.itf_num+1 == req_itfnum); + + tud_control_xfer(rhport, request, &_netd_itf.itf_data_alt, 1); + } + break; + + case TUSB_REQ_SET_INTERFACE: + { + uint8_t const req_itfnum = (uint8_t) request->wIndex; + uint8_t const req_alt = (uint8_t) request->wValue; + + // Only valid for Data Interface with Alternate is either 0 or 1 + TU_VERIFY(_netd_itf.itf_num+1 == req_itfnum && req_alt < 2); + + // ACM-ECM only: qequest to enable/disable network activities + TU_VERIFY(_netd_itf.ecm_mode); + + _netd_itf.itf_data_alt = req_alt; + + if ( _netd_itf.itf_data_alt ) + { + // TODO since we don't actually close endpoint + // hack here to not re-open it + if ( _netd_itf.ep_in == 0 && _netd_itf.ep_out == 0 ) + { + TU_ASSERT(_netd_itf.ecm_desc_epdata); + TU_ASSERT( usbd_open_edpt_pair(rhport, _netd_itf.ecm_desc_epdata, 2, TUSB_XFER_BULK, &_netd_itf.ep_out, &_netd_itf.ep_in) ); + + // TODO should be merge with RNDIS's after endpoint opened + // Also should have opposite callback for application to disable network !! + tud_network_init_cb(); + can_xmit = true; // we are ready to transmit a packet + if (tud_network_idle_status_change_cb) { + tud_network_idle_status_change_cb(can_xmit); + } + tud_network_recv_renew(); // prepare for incoming packets + } + }else + { + // TODO close the endpoint pair + // For now pretend that we did, this should have no harm since host won't try to + // communicate with the endpoints again + // _netd_itf.ep_in = _netd_itf.ep_out = 0 + } + + tud_control_status(rhport, request); + } + break; + + // unsupported request + default: return false; + } + break; + + case TUSB_REQ_TYPE_CLASS: + TU_VERIFY (_netd_itf.itf_num == request->wIndex); + + if (_netd_itf.ecm_mode) + { + /* the only required CDC-ECM Management Element Request is SetEthernetPacketFilter */ + if (0x43 /* SET_ETHERNET_PACKET_FILTER */ == request->bRequest) + { + tud_control_xfer(rhport, request, NULL, 0); + ecm_report(true); + } + } + else + { + if (request->bmRequestType_bit.direction == TUSB_DIR_IN) + { + rndis_generic_msg_t *rndis_msg = (rndis_generic_msg_t *) ((void*) notify.rndis_buf); + uint32_t msglen = tu_le32toh(rndis_msg->MessageLength); + TU_ASSERT(msglen <= sizeof(notify.rndis_buf)); + tud_control_xfer(rhport, request, notify.rndis_buf, msglen); + } + else + { + tud_control_xfer(rhport, request, notify.rndis_buf, sizeof(notify.rndis_buf)); + } + } + break; + + // unsupported request + default: return false; + } + } + else if ( stage == CONTROL_STAGE_DATA ) + { + // Handle RNDIS class control OUT only + if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS && + request->bmRequestType_bit.direction == TUSB_DIR_OUT && + _netd_itf.itf_num == request->wIndex) + { + if ( !_netd_itf.ecm_mode ) + { + rndis_class_set_handler(notify.rndis_buf, request->wLength); + } + } + } + + return true; +} + +static void handle_incoming_packet(uint32_t len) +{ + uint8_t *pnt = received; + uint32_t size = 0; + + if (_netd_itf.ecm_mode) + { + size = len; + } + else + { + rndis_data_packet_t *r = (rndis_data_packet_t *) ((void*) pnt); + if (len >= sizeof(rndis_data_packet_t)) + if ( (r->MessageType == REMOTE_NDIS_PACKET_MSG) && (r->MessageLength <= len)) + if ( (r->DataOffset + offsetof(rndis_data_packet_t, DataOffset) + r->DataLength) <= len) + { + pnt = &received[r->DataOffset + offsetof(rndis_data_packet_t, DataOffset)]; + size = r->DataLength; + } + } + + if (!tud_network_recv_cb(pnt, size)) + { + /* if a buffer was never handled by user code, we must renew on the user's behalf */ + tud_network_recv_renew(); + } +} + +bool netd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) +{ + (void) rhport; + (void) result; + + /* new packet received */ + if ( ep_addr == _netd_itf.ep_out ) + { + handle_incoming_packet(xferred_bytes); + } + + /* data transmission finished */ + if ( ep_addr == _netd_itf.ep_in ) + { + /* TinyUSB requires the class driver to implement ZLP (since ZLP usage is class-specific) */ + + if ( xferred_bytes && (0 == (xferred_bytes % CFG_TUD_NET_ENDPOINT_SIZE)) ) + { + do_in_xfer(NULL, 0); /* a ZLP is needed */ + } + else + { + /* we're finally finished */ + can_xmit = true; + if (tud_network_idle_status_change_cb) { + tud_network_idle_status_change_cb(can_xmit); + } + } + } + + if ( _netd_itf.ecm_mode && (ep_addr == _netd_itf.ep_notif) ) + { + if (sizeof(notify.ecm_buf.header) == xferred_bytes) ecm_report(false); + } + + return true; +} + +bool tud_network_can_xmit(void) +{ + return can_xmit; +} + +void tud_network_xmit(void *ref, uint16_t arg) +{ + uint8_t *data; + uint16_t len; + + if (!can_xmit) + return; + + len = (_netd_itf.ecm_mode) ? 0 : CFG_TUD_NET_PACKET_PREFIX_LEN; + data = transmitted + len; + + len += tud_network_xmit_cb(data, ref, arg); + + if (!_netd_itf.ecm_mode) + { + rndis_data_packet_t *hdr = (rndis_data_packet_t *) ((void*) transmitted); + memset(hdr, 0, sizeof(rndis_data_packet_t)); + hdr->MessageType = REMOTE_NDIS_PACKET_MSG; + hdr->MessageLength = len; + hdr->DataOffset = sizeof(rndis_data_packet_t) - offsetof(rndis_data_packet_t, DataOffset); + hdr->DataLength = len - sizeof(rndis_data_packet_t); + } + + do_in_xfer(transmitted, len); +} + +#endif diff --git a/components/tinyusb/additions/tusb/src/lib/networking/ndis.h b/components/tinyusb/additions/tusb/src/lib/networking/ndis.h new file mode 100644 index 0000000..1c73757 --- /dev/null +++ b/components/tinyusb/additions/tusb/src/lib/networking/ndis.h @@ -0,0 +1,266 @@ +/* This file has been prepared for Doxygen automatic documentation generation.*/ +/*! \file ndis.h *************************************************************** + * + * \brief + * This file contains the possible external configuration of the USB. + * + * \addtogroup usbstick + * + * + ******************************************************************************/ + +/** + \ingroup usbstick + \defgroup RNDIS RNDIS Support + @{ + */ + +/* + * ndis.h + * + * Modified by Colin O'Flynn + * ntddndis.h modified by Benedikt Spranger + * + * Thanks to the cygwin development team, + * espacially to Casper S. Hornstrup + * + * THIS SOFTWARE IS NOT COPYRIGHTED + * + * This source code is offered for use in the public domain. You may + * use, modify or distribute it freely. + * + * This code is distributed in the hope that it will be useful but + * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY + * DISCLAIMED. This includes but is not limited to warranties of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + */ + +#ifndef _LINUX_NDIS_H +#define _LINUX_NDIS_H + + +#define NDIS_STATUS_MULTICAST_FULL 0xC0010009 +#define NDIS_STATUS_MULTICAST_EXISTS 0xC001000A +#define NDIS_STATUS_MULTICAST_NOT_FOUND 0xC001000B + +/* from drivers/net/sk98lin/h/skgepnmi.h */ +#define OID_PNP_CAPABILITIES 0xFD010100 +#define OID_PNP_SET_POWER 0xFD010101 +#define OID_PNP_QUERY_POWER 0xFD010102 +#define OID_PNP_ADD_WAKE_UP_PATTERN 0xFD010103 +#define OID_PNP_REMOVE_WAKE_UP_PATTERN 0xFD010104 +#define OID_PNP_ENABLE_WAKE_UP 0xFD010106 + +enum NDIS_DEVICE_POWER_STATE { + NdisDeviceStateUnspecified = 0, + NdisDeviceStateD0, + NdisDeviceStateD1, + NdisDeviceStateD2, + NdisDeviceStateD3, + NdisDeviceStateMaximum +}; + +struct NDIS_PM_WAKE_UP_CAPABILITIES { + enum NDIS_DEVICE_POWER_STATE MinMagicPacketWakeUp; + enum NDIS_DEVICE_POWER_STATE MinPatternWakeUp; + enum NDIS_DEVICE_POWER_STATE MinLinkChangeWakeUp; +}; + +/* NDIS_PNP_CAPABILITIES.Flags constants */ +#define NDIS_DEVICE_WAKE_UP_ENABLE 0x00000001 +#define NDIS_DEVICE_WAKE_ON_PATTERN_MATCH_ENABLE 0x00000002 +#define NDIS_DEVICE_WAKE_ON_MAGIC_PACKET_ENABLE 0x00000004 + +/* +struct NDIS_PNP_CAPABILITIES { + __le32 Flags; + struct NDIS_PM_WAKE_UP_CAPABILITIES WakeUpCapabilities; +}; + +struct NDIS_PM_PACKET_PATTERN { + __le32 Priority; + __le32 Reserved; + __le32 MaskSize; + __le32 PatternOffset; + __le32 PatternSize; + __le32 PatternFlags; +}; +*/ + +/* Required Object IDs (OIDs) */ +#define OID_GEN_SUPPORTED_LIST 0x00010101 +#define OID_GEN_HARDWARE_STATUS 0x00010102 +#define OID_GEN_MEDIA_SUPPORTED 0x00010103 +#define OID_GEN_MEDIA_IN_USE 0x00010104 +#define OID_GEN_MAXIMUM_LOOKAHEAD 0x00010105 +#define OID_GEN_MAXIMUM_FRAME_SIZE 0x00010106 +#define OID_GEN_LINK_SPEED 0x00010107 +#define OID_GEN_TRANSMIT_BUFFER_SPACE 0x00010108 +#define OID_GEN_RECEIVE_BUFFER_SPACE 0x00010109 +#define OID_GEN_TRANSMIT_BLOCK_SIZE 0x0001010A +#define OID_GEN_RECEIVE_BLOCK_SIZE 0x0001010B +#define OID_GEN_VENDOR_ID 0x0001010C +#define OID_GEN_VENDOR_DESCRIPTION 0x0001010D +#define OID_GEN_CURRENT_PACKET_FILTER 0x0001010E +#define OID_GEN_CURRENT_LOOKAHEAD 0x0001010F +#define OID_GEN_DRIVER_VERSION 0x00010110 +#define OID_GEN_MAXIMUM_TOTAL_SIZE 0x00010111 +#define OID_GEN_PROTOCOL_OPTIONS 0x00010112 +#define OID_GEN_MAC_OPTIONS 0x00010113 +#define OID_GEN_MEDIA_CONNECT_STATUS 0x00010114 +#define OID_GEN_MAXIMUM_SEND_PACKETS 0x00010115 +#define OID_GEN_VENDOR_DRIVER_VERSION 0x00010116 +#define OID_GEN_SUPPORTED_GUIDS 0x00010117 +#define OID_GEN_NETWORK_LAYER_ADDRESSES 0x00010118 +#define OID_GEN_TRANSPORT_HEADER_OFFSET 0x00010119 +#define OID_GEN_MACHINE_NAME 0x0001021A +#define OID_GEN_RNDIS_CONFIG_PARAMETER 0x0001021B +#define OID_GEN_VLAN_ID 0x0001021C + +/* Optional OIDs */ +#define OID_GEN_MEDIA_CAPABILITIES 0x00010201 +#define OID_GEN_PHYSICAL_MEDIUM 0x00010202 + +/* Required statistics OIDs */ +#define OID_GEN_XMIT_OK 0x00020101 +#define OID_GEN_RCV_OK 0x00020102 +#define OID_GEN_XMIT_ERROR 0x00020103 +#define OID_GEN_RCV_ERROR 0x00020104 +#define OID_GEN_RCV_NO_BUFFER 0x00020105 + +/* Optional statistics OIDs */ +#define OID_GEN_DIRECTED_BYTES_XMIT 0x00020201 +#define OID_GEN_DIRECTED_FRAMES_XMIT 0x00020202 +#define OID_GEN_MULTICAST_BYTES_XMIT 0x00020203 +#define OID_GEN_MULTICAST_FRAMES_XMIT 0x00020204 +#define OID_GEN_BROADCAST_BYTES_XMIT 0x00020205 +#define OID_GEN_BROADCAST_FRAMES_XMIT 0x00020206 +#define OID_GEN_DIRECTED_BYTES_RCV 0x00020207 +#define OID_GEN_DIRECTED_FRAMES_RCV 0x00020208 +#define OID_GEN_MULTICAST_BYTES_RCV 0x00020209 +#define OID_GEN_MULTICAST_FRAMES_RCV 0x0002020A +#define OID_GEN_BROADCAST_BYTES_RCV 0x0002020B +#define OID_GEN_BROADCAST_FRAMES_RCV 0x0002020C +#define OID_GEN_RCV_CRC_ERROR 0x0002020D +#define OID_GEN_TRANSMIT_QUEUE_LENGTH 0x0002020E +#define OID_GEN_GET_TIME_CAPS 0x0002020F +#define OID_GEN_GET_NETCARD_TIME 0x00020210 +#define OID_GEN_NETCARD_LOAD 0x00020211 +#define OID_GEN_DEVICE_PROFILE 0x00020212 +#define OID_GEN_INIT_TIME_MS 0x00020213 +#define OID_GEN_RESET_COUNTS 0x00020214 +#define OID_GEN_MEDIA_SENSE_COUNTS 0x00020215 +#define OID_GEN_FRIENDLY_NAME 0x00020216 +#define OID_GEN_MINIPORT_INFO 0x00020217 +#define OID_GEN_RESET_VERIFY_PARAMETERS 0x00020218 + +/* IEEE 802.3 (Ethernet) OIDs */ +#define NDIS_802_3_MAC_OPTION_PRIORITY 0x00000001 + +#define OID_802_3_PERMANENT_ADDRESS 0x01010101 +#define OID_802_3_CURRENT_ADDRESS 0x01010102 +#define OID_802_3_MULTICAST_LIST 0x01010103 +#define OID_802_3_MAXIMUM_LIST_SIZE 0x01010104 +#define OID_802_3_MAC_OPTIONS 0x01010105 +#define OID_802_3_RCV_ERROR_ALIGNMENT 0x01020101 +#define OID_802_3_XMIT_ONE_COLLISION 0x01020102 +#define OID_802_3_XMIT_MORE_COLLISIONS 0x01020103 +#define OID_802_3_XMIT_DEFERRED 0x01020201 +#define OID_802_3_XMIT_MAX_COLLISIONS 0x01020202 +#define OID_802_3_RCV_OVERRUN 0x01020203 +#define OID_802_3_XMIT_UNDERRUN 0x01020204 +#define OID_802_3_XMIT_HEARTBEAT_FAILURE 0x01020205 +#define OID_802_3_XMIT_TIMES_CRS_LOST 0x01020206 +#define OID_802_3_XMIT_LATE_COLLISIONS 0x01020207 + +/* Wireless LAN OIDs */ +/* Mandatory */ +#define OID_802_11_BSSID 0x0D010101 /* Q S */ +#define OID_802_11_SSID 0x0D010102 /* Q S */ +#define OID_802_11_NETWORK_TYPE_IN_USE 0x0D010204 /* Q S */ +#define OID_802_11_RSSI 0x0D010206 /* Q I */ +#define OID_802_11_BSSID_LIST 0x0D010217 /* Q */ +#define OID_802_11_BSSID_LIST_SCAN 0x0D01011A /* S */ +#define OID_802_11_INFRASTRUCTURE_MODE 0x0D010108 /* Q S */ +#define OID_802_11_SUPPORTED_RATES 0x0D01020E /* Q */ +#define OID_802_11_CONFIGURATION 0x0D010211 /* Q S */ +#define OID_802_11_ADD_WEP 0x0D010113 /* S */ +#define OID_802_11_WEP_STATUS 0x0D01011B /* Q S */ +#define OID_802_11_REMOVE_WEP 0x0D010114 /* S */ +#define OID_802_11_DISASSOCIATE 0x0D010115 /* S */ +#define OID_802_11_AUTHENTICATION_MODE 0x0D010118 /* Q S */ +#define OID_802_11_RELOAD_DEFAULTS 0x0D01011C /* S */ + + + +/* OID_GEN_MINIPORT_INFO constants */ +#define NDIS_MINIPORT_BUS_MASTER 0x00000001 +#define NDIS_MINIPORT_WDM_DRIVER 0x00000002 +#define NDIS_MINIPORT_SG_LIST 0x00000004 +#define NDIS_MINIPORT_SUPPORTS_MEDIA_QUERY 0x00000008 +#define NDIS_MINIPORT_INDICATES_PACKETS 0x00000010 +#define NDIS_MINIPORT_IGNORE_PACKET_QUEUE 0x00000020 +#define NDIS_MINIPORT_IGNORE_REQUEST_QUEUE 0x00000040 +#define NDIS_MINIPORT_IGNORE_TOKEN_RING_ERRORS 0x00000080 +#define NDIS_MINIPORT_INTERMEDIATE_DRIVER 0x00000100 +#define NDIS_MINIPORT_IS_NDIS_5 0x00000200 +#define NDIS_MINIPORT_IS_CO 0x00000400 +#define NDIS_MINIPORT_DESERIALIZE 0x00000800 +#define NDIS_MINIPORT_REQUIRES_MEDIA_POLLING 0x00001000 +#define NDIS_MINIPORT_SUPPORTS_MEDIA_SENSE 0x00002000 +#define NDIS_MINIPORT_NETBOOT_CARD 0x00004000 +#define NDIS_MINIPORT_PM_SUPPORTED 0x00008000 +#define NDIS_MINIPORT_SUPPORTS_MAC_ADDRESS_OVERWRITE 0x00010000 +#define NDIS_MINIPORT_USES_SAFE_BUFFER_APIS 0x00020000 +#define NDIS_MINIPORT_HIDDEN 0x00040000 +#define NDIS_MINIPORT_SWENUM 0x00080000 +#define NDIS_MINIPORT_SURPRISE_REMOVE_OK 0x00100000 +#define NDIS_MINIPORT_NO_HALT_ON_SUSPEND 0x00200000 +#define NDIS_MINIPORT_HARDWARE_DEVICE 0x00400000 +#define NDIS_MINIPORT_SUPPORTS_CANCEL_SEND_PACKETS 0x00800000 +#define NDIS_MINIPORT_64BITS_DMA 0x01000000 + +#define NDIS_MEDIUM_802_3 0x00000000 +#define NDIS_MEDIUM_802_5 0x00000001 +#define NDIS_MEDIUM_FDDI 0x00000002 +#define NDIS_MEDIUM_WAN 0x00000003 +#define NDIS_MEDIUM_LOCAL_TALK 0x00000004 +#define NDIS_MEDIUM_DIX 0x00000005 +#define NDIS_MEDIUM_ARCENT_RAW 0x00000006 +#define NDIS_MEDIUM_ARCENT_878_2 0x00000007 +#define NDIS_MEDIUM_ATM 0x00000008 +#define NDIS_MEDIUM_WIRELESS_LAN 0x00000009 +#define NDIS_MEDIUM_IRDA 0x0000000A +#define NDIS_MEDIUM_BPC 0x0000000B +#define NDIS_MEDIUM_CO_WAN 0x0000000C +#define NDIS_MEDIUM_1394 0x0000000D + +#define NDIS_PACKET_TYPE_DIRECTED 0x00000001 +#define NDIS_PACKET_TYPE_MULTICAST 0x00000002 +#define NDIS_PACKET_TYPE_ALL_MULTICAST 0x00000004 +#define NDIS_PACKET_TYPE_BROADCAST 0x00000008 +#define NDIS_PACKET_TYPE_SOURCE_ROUTING 0x00000010 +#define NDIS_PACKET_TYPE_PROMISCUOUS 0x00000020 +#define NDIS_PACKET_TYPE_SMT 0x00000040 +#define NDIS_PACKET_TYPE_ALL_LOCAL 0x00000080 +#define NDIS_PACKET_TYPE_GROUP 0x00000100 +#define NDIS_PACKET_TYPE_ALL_FUNCTIONAL 0x00000200 +#define NDIS_PACKET_TYPE_FUNCTIONAL 0x00000400 +#define NDIS_PACKET_TYPE_MAC_FRAME 0x00000800 + +#define NDIS_MEDIA_STATE_CONNECTED 0x00000000 +#define NDIS_MEDIA_STATE_DISCONNECTED 0x00000001 + +#define NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA 0x00000001 +#define NDIS_MAC_OPTION_RECEIVE_SERIALIZED 0x00000002 +#define NDIS_MAC_OPTION_TRANSFERS_NOT_PEND 0x00000004 +#define NDIS_MAC_OPTION_NO_LOOPBACK 0x00000008 +#define NDIS_MAC_OPTION_FULL_DUPLEX 0x00000010 +#define NDIS_MAC_OPTION_EOTX_INDICATION 0x00000020 +#define NDIS_MAC_OPTION_8021P_PRIORITY 0x00000040 +#define NDIS_MAC_OPTION_RESERVED 0x80000000 + +#endif /* _LINUX_NDIS_H */ + +/** @} */ diff --git a/components/tinyusb/additions/tusb/src/lib/networking/rndis_protocol.h b/components/tinyusb/additions/tusb/src/lib/networking/rndis_protocol.h new file mode 100644 index 0000000..39da296 --- /dev/null +++ b/components/tinyusb/additions/tusb/src/lib/networking/rndis_protocol.h @@ -0,0 +1,313 @@ +/** + * \file rndis_protocol.h + * RNDIS Defines + * + * \author + * Colin O'Flynn + * + * \addtogroup usbstick + */ + +/* Copyright (c) 2008 Colin O'Flynn + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + * Neither the name of the copyright holders nor the names of + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _RNDIS_H +#define _RNDIS_H + +/** + \addtogroup RNDIS + @{ + */ + +#include + +#define RNDIS_MAJOR_VERSION 1 +#define RNDIS_MINOR_VERSION 0 + +#define RNDIS_STATUS_SUCCESS 0X00000000 +#define RNDIS_STATUS_FAILURE 0XC0000001 +#define RNDIS_STATUS_INVALID_DATA 0XC0010015 +#define RNDIS_STATUS_NOT_SUPPORTED 0XC00000BB +#define RNDIS_STATUS_MEDIA_CONNECT 0X4001000B +#define RNDIS_STATUS_MEDIA_DISCONNECT 0X4001000C + + +/* Message set for Connectionless (802.3) Devices */ +#define REMOTE_NDIS_PACKET_MSG 0x00000001 +#define REMOTE_NDIS_INITIALIZE_MSG 0X00000002 +#define REMOTE_NDIS_HALT_MSG 0X00000003 +#define REMOTE_NDIS_QUERY_MSG 0X00000004 +#define REMOTE_NDIS_SET_MSG 0X00000005 +#define REMOTE_NDIS_RESET_MSG 0X00000006 +#define REMOTE_NDIS_INDICATE_STATUS_MSG 0X00000007 +#define REMOTE_NDIS_KEEPALIVE_MSG 0X00000008 +#define REMOTE_NDIS_INITIALIZE_CMPLT 0X80000002 +#define REMOTE_NDIS_QUERY_CMPLT 0X80000004 +#define REMOTE_NDIS_SET_CMPLT 0X80000005 +#define REMOTE_NDIS_RESET_CMPLT 0X80000006 +#define REMOTE_NDIS_KEEPALIVE_CMPLT 0X80000008 + +typedef uint32_t rndis_MessageType_t; +typedef uint32_t rndis_MessageLength_t; +typedef uint32_t rndis_RequestId_t; +typedef uint32_t rndis_MajorVersion_t; +typedef uint32_t rndis_MinorVersion_t; +typedef uint32_t rndis_MaxTransferSize_t; +typedef uint32_t rndis_Status_t; + + +/* Device Flags */ +#define RNDIS_DF_CONNECTIONLESS 0x00000001 +#define RNDIS_DF_CONNECTION_ORIENTED 0x00000002 +typedef uint32_t rndis_DeviceFlags_t; + +/* Mediums */ +#define RNDIS_MEDIUM_802_3 0x00000000 +typedef uint32_t rndis_Medium_t; + + +typedef uint32_t rndis_MaxPacketsPerTransfer_t; +typedef uint32_t rndis_PacketAlignmentFactor_t; +typedef uint32_t rndis_AfListOffset_t; +typedef uint32_t rndis_AfListSize_t; + +/*** Remote NDIS Generic Message type ***/ +typedef struct{ + rndis_MessageType_t MessageType; + rndis_MessageLength_t MessageLength; + } rndis_generic_msg_t; + + +/*** Remote NDIS Initialize Message ***/ +typedef struct{ + rndis_MessageType_t MessageType; + rndis_MessageLength_t MessageLength; + rndis_RequestId_t RequestId; + rndis_MajorVersion_t MajorVersion; + rndis_MinorVersion_t MinorVersion; + rndis_MaxTransferSize_t MaxTransferSize; + } rndis_initialize_msg_t; + +/* Response: */ +typedef struct{ + rndis_MessageType_t MessageType; + rndis_MessageLength_t MessageLength; + rndis_RequestId_t RequestId; + rndis_Status_t Status; + rndis_MajorVersion_t MajorVersion; + rndis_MinorVersion_t MinorVersion; + rndis_DeviceFlags_t DeviceFlags; + rndis_Medium_t Medium; + rndis_MaxPacketsPerTransfer_t MaxPacketsPerTransfer; + rndis_MaxTransferSize_t MaxTransferSize; + rndis_PacketAlignmentFactor_t PacketAlignmentFactor; + rndis_AfListOffset_t AfListOffset; + rndis_AfListSize_t AfListSize; + } rndis_initialize_cmplt_t; + + +/*** Remote NDIS Halt Message ***/ +typedef struct{ + rndis_MessageType_t MessageType; + rndis_MessageLength_t MessageLength; + rndis_RequestId_t RequestId; + } rndis_halt_msg_t; + +typedef uint32_t rndis_Oid_t; +typedef uint32_t rndis_InformationBufferLength_t; +typedef uint32_t rndis_InformationBufferOffset_t; +typedef uint32_t rndis_DeviceVcHandle_t; + +/*** Remote NDIS Query Message ***/ +typedef struct{ + rndis_MessageType_t MessageType; + rndis_MessageLength_t MessageLength; + rndis_RequestId_t RequestId; + rndis_Oid_t Oid; + rndis_InformationBufferLength_t InformationBufferLength; + rndis_InformationBufferOffset_t InformationBufferOffset; + rndis_DeviceVcHandle_t DeviceVcHandle; + } rndis_query_msg_t; + +/* Response: */ + +typedef struct{ + rndis_MessageType_t MessageType; + rndis_MessageLength_t MessageLength; + rndis_RequestId_t RequestId; + rndis_Status_t Status; + rndis_InformationBufferLength_t InformationBufferLength; + rndis_InformationBufferOffset_t InformationBufferOffset; + } rndis_query_cmplt_t; + +/*** Remote NDIS Set Message ***/ +typedef struct{ + rndis_MessageType_t MessageType; + rndis_MessageLength_t MessageLength; + rndis_RequestId_t RequestId; + rndis_Oid_t Oid; + rndis_InformationBufferLength_t InformationBufferLength; + rndis_InformationBufferOffset_t InformationBufferOffset; + rndis_DeviceVcHandle_t DeviceVcHandle; + } rndis_set_msg_t; + +/* Response */ +typedef struct{ + rndis_MessageType_t MessageType; + rndis_MessageLength_t MessageLength; + rndis_RequestId_t RequestId; + rndis_Status_t Status; + }rndis_set_cmplt_t; + +/* Information buffer layout for OID_GEN_RNDIS_CONFIG_PARAMETER */ +typedef uint32_t rndis_ParameterNameOffset_t; +typedef uint32_t rndis_ParameterNameLength_t; +typedef uint32_t rndis_ParameterType_t; +typedef uint32_t rndis_ParameterValueOffset_t; +typedef uint32_t rndis_ParameterValueLength_t; + +#define PARAMETER_TYPE_STRING 2 +#define PARAMETER_TYPE_NUMERICAL 0 + +typedef struct{ + rndis_ParameterNameOffset_t ParameterNameOffset; + rndis_ParameterNameLength_t ParameterNameLength; + rndis_ParameterType_t ParameterType; + rndis_ParameterValueOffset_t ParameterValueOffset; + rndis_ParameterValueLength_t ParameterValueLength; + }rndis_config_parameter_t; + +typedef uint32_t rndis_Reserved_t; + +/*** Remote NDIS Soft Reset Message ***/ +typedef struct{ + rndis_MessageType_t MessageType; + rndis_MessageLength_t MessageLength; + rndis_Reserved_t Reserved; + } rndis_reset_msg_t; + +typedef uint32_t rndis_AddressingReset_t; + +/* Response: */ +typedef struct{ + rndis_MessageType_t MessageType; + rndis_MessageLength_t MessageLength; + rndis_Status_t Status; + rndis_AddressingReset_t AddressingReset; + } rndis_reset_cmplt_t; + +/*** Remote NDIS Indicate Status Message ***/ +typedef struct{ + rndis_MessageType_t MessageType; + rndis_MessageLength_t MessageLength; + rndis_Status_t Status; + rndis_Status_t StatusBufferLength; + rndis_Status_t StatusBufferOffset; + } rndis_indicate_status_t; + +typedef uint32_t rndis_DiagStatus_t; +typedef uint32_t rndis_ErrorOffset_t; + +typedef struct { + rndis_DiagStatus_t DiagStatus; + rndis_ErrorOffset_t ErrorOffset; + }rndis_diagnostic_info_t; + +/*** Remote NDIS Keepalive Message */ +typedef struct{ + rndis_MessageType_t MessageType; + rndis_MessageLength_t MessageLength; + rndis_RequestId_t RequestId; + }rndis_keepalive_msg_t; + +/* Response: */ +typedef struct{ + rndis_MessageType_t MessageType; + rndis_MessageLength_t MessageLength; + rndis_RequestId_t RequestId; + rndis_Status_t Status; + }rndis_keepalive_cmplt_t; + +/*** Remote NDIS Data Packet ***/ + +typedef uint32_t rndis_DataOffset_t; +typedef uint32_t rndis_DataLength_t; +typedef uint32_t rndis_OOBDataOffset_t; +typedef uint32_t rndis_OOBDataLength_t; +typedef uint32_t rndis_NumOOBDataElements_t; +typedef uint32_t rndis_PerPacketInfoOffset_t; +typedef uint32_t rndis_PerPacketInfoLength_t; + +typedef struct{ + rndis_MessageType_t MessageType; + rndis_MessageLength_t MessageLength; + rndis_DataOffset_t DataOffset; + rndis_DataLength_t DataLength; + rndis_OOBDataOffset_t OOBDataOffset; + rndis_OOBDataLength_t OOBDataLength; + rndis_NumOOBDataElements_t NumOOBDataElements; + rndis_PerPacketInfoOffset_t PerPacketInfoOffset; + rndis_PerPacketInfoLength_t PerPacketInfoLength; + rndis_DeviceVcHandle_t DeviceVcHandle; + rndis_Reserved_t Reserved; + }rndis_data_packet_t; + +typedef uint32_t rndis_ClassInformationOffset_t; +typedef uint32_t rndis_Size_t; +typedef uint32_t rndis_Type_t; + +typedef struct{ + rndis_Size_t Size; + rndis_Type_t Type; + rndis_ClassInformationOffset_t ClassInformationType; + }rndis_OOB_packet_t; + +#include "ndis.h" + +typedef enum rnids_state_e { + rndis_uninitialized, + rndis_initialized, + rndis_data_initialized + } rndis_state_t; + +typedef struct { + uint32_t txok; + uint32_t rxok; + uint32_t txbad; + uint32_t rxbad; +} usb_eth_stat_t; + +//void rndis_indicate_status(int status); + +void rndis_disconnect(void); + +void rndis_connect(void); + +#endif /* _RNDIS_H */ + +/** @} */ diff --git a/components/tinyusb/additions/tusb/src/lib/networking/rndis_reports.c b/components/tinyusb/additions/tusb/src/lib/networking/rndis_reports.c new file mode 100644 index 0000000..53c43c4 --- /dev/null +++ b/components/tinyusb/additions/tusb/src/lib/networking/rndis_reports.c @@ -0,0 +1,329 @@ +/* + The original version of this code was lrndis/usbd_rndis_core.c from https://github.com/fetisov/lrndis + It has since been overhauled to suit this application +*/ + +/* + * The MIT License (MIT) + * + * Copyright (c) 2015 by Sergey Fetisov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include "class/net/net_device.h" +#include "rndis_protocol.h" +#include "netif/ethernet.h" +#include "esp_wifi.h" + +#define RNDIS_LINK_SPEED 12000000 /* Link baudrate (12Mbit/s for USB-FS) */ +#define RNDIS_VENDOR CONFIG_TINYUSB_RNDIS_VENDOR /* NIC vendor name */ + +static usb_eth_stat_t usb_eth_stat = { 0, 0, 0, 0 }; +static uint32_t oid_packet_filter = 0x0000000; +static rndis_state_t rndis_state; + +CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static uint8_t ndis_report[8] = { 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }; + +static const uint32_t OIDSupportedList[] = +{ + OID_GEN_SUPPORTED_LIST, + OID_GEN_HARDWARE_STATUS, + OID_GEN_MEDIA_SUPPORTED, + OID_GEN_MEDIA_IN_USE, + OID_GEN_MAXIMUM_FRAME_SIZE, + OID_GEN_LINK_SPEED, + OID_GEN_TRANSMIT_BLOCK_SIZE, + OID_GEN_RECEIVE_BLOCK_SIZE, + OID_GEN_VENDOR_ID, + OID_GEN_VENDOR_DESCRIPTION, + OID_GEN_VENDOR_DRIVER_VERSION, + OID_GEN_CURRENT_PACKET_FILTER, + OID_GEN_MAXIMUM_TOTAL_SIZE, + OID_GEN_PROTOCOL_OPTIONS, + OID_GEN_MAC_OPTIONS, + OID_GEN_MEDIA_CONNECT_STATUS, + OID_GEN_MAXIMUM_SEND_PACKETS, + OID_802_3_PERMANENT_ADDRESS, + OID_802_3_CURRENT_ADDRESS, + OID_802_3_MULTICAST_LIST, + OID_802_3_MAXIMUM_LIST_SIZE, + OID_802_3_MAC_OPTIONS +}; + +#define OID_LIST_LENGTH TU_ARRAY_SIZE(OIDSupportedList) +#define ENC_BUF_SIZE (OID_LIST_LENGTH * 4 + 32) + +static void *encapsulated_buffer; + +static void rndis_report(void) +{ + netd_report(ndis_report, sizeof(ndis_report)); +} + +static void rndis_query_cmplt32(int status, uint32_t data) +{ + rndis_query_cmplt_t *c; + c = (rndis_query_cmplt_t *)encapsulated_buffer; + c->MessageType = REMOTE_NDIS_QUERY_CMPLT; + c->MessageLength = sizeof(rndis_query_cmplt_t) + 4; + c->InformationBufferLength = 4; + c->InformationBufferOffset = 16; + c->Status = status; + memcpy(c + 1, &data, sizeof(data)); + rndis_report(); +} + +static void rndis_query_cmplt(int status, const void *data, int size) +{ + rndis_query_cmplt_t *c; + c = (rndis_query_cmplt_t *)encapsulated_buffer; + c->MessageType = REMOTE_NDIS_QUERY_CMPLT; + c->MessageLength = sizeof(rndis_query_cmplt_t) + size; + c->InformationBufferLength = size; + c->InformationBufferOffset = 16; + c->Status = status; + memcpy(c + 1, data, size); + rndis_report(); +} + +#define MAC_OPT NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA | \ + NDIS_MAC_OPTION_RECEIVE_SERIALIZED | \ + NDIS_MAC_OPTION_TRANSFERS_NOT_PEND | \ + NDIS_MAC_OPTION_NO_LOOPBACK + +static const char *rndis_vendor = RNDIS_VENDOR; + +static void rndis_query(void) +{ + uint8_t sta_mac[6] = {0}; + esp_wifi_get_mac(WIFI_IF_STA, sta_mac); + switch (((rndis_query_msg_t *)encapsulated_buffer)->Oid) + { + case OID_GEN_SUPPORTED_LIST: rndis_query_cmplt(RNDIS_STATUS_SUCCESS, OIDSupportedList, 4 * OID_LIST_LENGTH); return; + case OID_GEN_VENDOR_DRIVER_VERSION: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, 0x00001000); return; + case OID_802_3_CURRENT_ADDRESS: rndis_query_cmplt(RNDIS_STATUS_SUCCESS, sta_mac, 6); return; + case OID_802_3_PERMANENT_ADDRESS: rndis_query_cmplt(RNDIS_STATUS_SUCCESS, sta_mac, 6); return; + case OID_GEN_MEDIA_SUPPORTED: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, NDIS_MEDIUM_802_3); return; + case OID_GEN_MEDIA_IN_USE: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, NDIS_MEDIUM_802_3); return; + case OID_GEN_PHYSICAL_MEDIUM: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, NDIS_MEDIUM_802_3); return; + case OID_GEN_HARDWARE_STATUS: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, 0); return; + case OID_GEN_LINK_SPEED: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, RNDIS_LINK_SPEED / 100); return; + case OID_GEN_VENDOR_ID: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, 0x00FFFFFF); return; + case OID_GEN_VENDOR_DESCRIPTION: rndis_query_cmplt(RNDIS_STATUS_SUCCESS, rndis_vendor, strlen(rndis_vendor) + 1); return; + case OID_GEN_CURRENT_PACKET_FILTER: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, oid_packet_filter); return; + case OID_GEN_MAXIMUM_FRAME_SIZE: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, CFG_TUD_NET_MTU - SIZEOF_ETH_HDR); return; + case OID_GEN_MAXIMUM_TOTAL_SIZE: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, CFG_TUD_NET_MTU); return; + case OID_GEN_TRANSMIT_BLOCK_SIZE: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, CFG_TUD_NET_MTU); return; + case OID_GEN_RECEIVE_BLOCK_SIZE: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, CFG_TUD_NET_MTU); return; + case OID_GEN_MEDIA_CONNECT_STATUS: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, NDIS_MEDIA_STATE_CONNECTED); return; + case OID_GEN_RNDIS_CONFIG_PARAMETER: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, 0); return; + case OID_802_3_MAXIMUM_LIST_SIZE: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, 1); return; + case OID_802_3_MULTICAST_LIST: rndis_query_cmplt32(RNDIS_STATUS_NOT_SUPPORTED, 0); return; + case OID_802_3_MAC_OPTIONS: rndis_query_cmplt32(RNDIS_STATUS_NOT_SUPPORTED, 0); return; + case OID_GEN_MAC_OPTIONS: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, /*MAC_OPT*/ 0); return; + case OID_802_3_RCV_ERROR_ALIGNMENT: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, 0); return; + case OID_802_3_XMIT_ONE_COLLISION: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, 0); return; + case OID_802_3_XMIT_MORE_COLLISIONS: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, 0); return; + case OID_GEN_XMIT_OK: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, usb_eth_stat.txok); return; + case OID_GEN_RCV_OK: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, usb_eth_stat.rxok); return; + case OID_GEN_RCV_ERROR: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, usb_eth_stat.rxbad); return; + case OID_GEN_XMIT_ERROR: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, usb_eth_stat.txbad); return; + case OID_GEN_RCV_NO_BUFFER: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, 0); return; + default: rndis_query_cmplt(RNDIS_STATUS_FAILURE, NULL, 0); return; + } +} + +#define INFBUF ((uint8_t *)&(m->RequestId) + m->InformationBufferOffset) + +static void rndis_handle_config_parm(const char *data, int keyoffset, int valoffset, int keylen, int vallen) +{ + (void)data; + (void)keyoffset; + (void)valoffset; + (void)keylen; + (void)vallen; +} + +static void rndis_packetFilter(uint32_t newfilter) +{ + (void)newfilter; +} + +static void rndis_handle_set_msg(void) +{ + rndis_set_cmplt_t *c; + rndis_set_msg_t *m; + rndis_Oid_t oid; + + c = (rndis_set_cmplt_t *)encapsulated_buffer; + m = (rndis_set_msg_t *)encapsulated_buffer; + + oid = m->Oid; + c->MessageType = REMOTE_NDIS_SET_CMPLT; + c->MessageLength = sizeof(rndis_set_cmplt_t); + c->Status = RNDIS_STATUS_SUCCESS; + + switch (oid) + { + /* Parameters set up in 'Advanced' tab */ + case OID_GEN_RNDIS_CONFIG_PARAMETER: + { + rndis_config_parameter_t *p; + char *ptr = (char *)m; + ptr += sizeof(rndis_generic_msg_t); + ptr += m->InformationBufferOffset; + p = (rndis_config_parameter_t *) ((void*) ptr); + rndis_handle_config_parm(ptr, p->ParameterNameOffset, p->ParameterValueOffset, p->ParameterNameLength, p->ParameterValueLength); + } + break; + + /* Mandatory general OIDs */ + case OID_GEN_CURRENT_PACKET_FILTER: + memcpy(&oid_packet_filter, INFBUF, 4); + if (oid_packet_filter) + { + rndis_packetFilter(oid_packet_filter); + rndis_state = rndis_data_initialized; + } + else + { + rndis_state = rndis_initialized; + } + break; + + case OID_GEN_CURRENT_LOOKAHEAD: + break; + + case OID_GEN_PROTOCOL_OPTIONS: + break; + + /* Mandatory 802_3 OIDs */ + case OID_802_3_MULTICAST_LIST: + break; + + /* Power Managment: fails for now */ + case OID_PNP_ADD_WAKE_UP_PATTERN: + case OID_PNP_REMOVE_WAKE_UP_PATTERN: + case OID_PNP_ENABLE_WAKE_UP: + default: + c->Status = RNDIS_STATUS_FAILURE; + break; + } + + /* c->MessageID is same as before */ + rndis_report(); + return; +} + +void rndis_indicate_status(int status) +{ + rndis_indicate_status_t *c; + c = (rndis_indicate_status_t *)encapsulated_buffer; + c->MessageType = REMOTE_NDIS_INDICATE_STATUS_MSG; + c->MessageLength = sizeof(rndis_indicate_status_t); + c->Status = status; + c->StatusBufferLength = 0; + c->StatusBufferOffset = 0; + rndis_report(); +} + +void rndis_disconnect(void) +{ + int status = RNDIS_STATUS_MEDIA_DISCONNECT; + + rndis_indicate_status(status); +} + +void rndis_connect(void) +{ + int status = RNDIS_STATUS_MEDIA_CONNECT; + + rndis_indicate_status(status); +} + +void rndis_class_set_handler(uint8_t *data, int size) +{ + encapsulated_buffer = data; + (void)size; + + switch (((rndis_generic_msg_t *)encapsulated_buffer)->MessageType) + { + case REMOTE_NDIS_INITIALIZE_MSG: + { + rndis_initialize_cmplt_t *m; + m = ((rndis_initialize_cmplt_t *)encapsulated_buffer); + /* m->MessageID is same as before */ + m->MessageType = REMOTE_NDIS_INITIALIZE_CMPLT; + m->MessageLength = sizeof(rndis_initialize_cmplt_t); + m->MajorVersion = RNDIS_MAJOR_VERSION; + m->MinorVersion = RNDIS_MINOR_VERSION; + m->Status = RNDIS_STATUS_SUCCESS; + m->DeviceFlags = RNDIS_DF_CONNECTIONLESS; + m->Medium = RNDIS_MEDIUM_802_3; + m->MaxPacketsPerTransfer = 1; + m->MaxTransferSize = CFG_TUD_NET_MTU + sizeof(rndis_data_packet_t); + m->PacketAlignmentFactor = 0; + m->AfListOffset = 0; + m->AfListSize = 0; + rndis_state = rndis_initialized; + rndis_report(); + } + break; + + case REMOTE_NDIS_QUERY_MSG: + rndis_query(); + break; + + case REMOTE_NDIS_SET_MSG: + rndis_handle_set_msg(); + break; + + case REMOTE_NDIS_RESET_MSG: + { + rndis_reset_cmplt_t * m; + m = ((rndis_reset_cmplt_t *)encapsulated_buffer); + rndis_state = rndis_uninitialized; + m->MessageType = REMOTE_NDIS_RESET_CMPLT; + m->MessageLength = sizeof(rndis_reset_cmplt_t); + m->Status = RNDIS_STATUS_SUCCESS; + m->AddressingReset = 1; /* Make it look like we did something */ + /* m->AddressingReset = 0; - Windows halts if set to 1 for some reason */ + rndis_report(); + } + break; + + case REMOTE_NDIS_KEEPALIVE_MSG: + { + rndis_keepalive_cmplt_t * m; + m = (rndis_keepalive_cmplt_t *)encapsulated_buffer; + m->MessageType = REMOTE_NDIS_KEEPALIVE_CMPLT; + m->MessageLength = sizeof(rndis_keepalive_cmplt_t); + m->Status = RNDIS_STATUS_SUCCESS; + } + /* We have data to send back */ + rndis_report(); + break; + + default: + break; + } +} diff --git a/components/tinyusb/idf_component.yml b/components/tinyusb/idf_component.yml new file mode 100644 index 0000000..628cf7c --- /dev/null +++ b/components/tinyusb/idf_component.yml @@ -0,0 +1,10 @@ +version: "1.1.1" # Component version, required only for components pushed to the service +targets: # List of supported targets (optional, if missing all targets are considered to be supported) + - esp32s2 + - esp32s3 +description: ESP32-S2 TinyUSB Addtions # Description (optional) +url: https://github.com/iot-components/tinyusb # Original repository (optional) +dependencies: + # Required IDF version + idf: + version: "=4.4" diff --git a/drivers/ecc108a/cryptoauthlib/README.md b/drivers/ecc108a/cryptoauthlib/README.md new file mode 100644 index 0000000..7145a0e --- /dev/null +++ b/drivers/ecc108a/cryptoauthlib/README.md @@ -0,0 +1,3 @@ +Copied from https://github.com/MicrochipTech/cryptoauthlib (9a37b8d) +Any changes are guarded with #if(n)def ATCA_TIDAL + diff --git a/drivers/ecc108a/cryptoauthlib/atca_basic.c b/drivers/ecc108a/cryptoauthlib/atca_basic.c new file mode 100644 index 0000000..b533d6d --- /dev/null +++ b/drivers/ecc108a/cryptoauthlib/atca_basic.c @@ -0,0 +1,4142 @@ +/** + * \file + * \brief CryptoAuthLib Basic API methods. These methods provide a simpler way + * to access the core crypto methods. + * + * \copyright (c) 2015-2020 Microchip Technology Inc. and its subsidiaries. + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip software + * and any derivatives exclusively with Microchip products. It is your + * responsibility to comply with third party license terms applicable to your + * use of third party software (including open source software) that may + * accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER + * EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, + * SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE + * OF ANY KIND WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF + * MICROCHIP HAS BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE + * FORESEEABLE. TO THE FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL + * LIABILITY ON ALL CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED + * THE AMOUNT OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR + * THIS SOFTWARE. + */ + +#include "atca_basic.h" +#include "atca_version.h" + +#if defined(ATCA_USE_CONSTANT_HOST_NONCE) +#if defined(_MSC_VER) +#pragma message("Warning : Using a constant host nonce with atcab_read_enc, atcab_write_enc, etcc., can allow spoofing of a device by replaying previously recorded messages") +#else +#warning "Using a constant host nonce with atcab_read_enc, atcab_write_enc, etcc., can allow spoofing of a device by replaying previously recorded messages" +#endif +#endif + +const char atca_version[] = ATCA_LIBRARY_VERSION_DATE; +ATCADevice _gDevice = NULL; +#ifdef ATCA_NO_HEAP +SHARED_LIB_EXPORT struct atca_iface g_atcab_iface; +SHARED_LIB_EXPORT struct atca_device g_atcab_device; +#endif + +/** \brief basic API methods are all prefixed with atcab_ (CryptoAuthLib Basic) + * the fundamental premise of the basic API is it is based on a single interface + * instance and that instance is global, so all basic API commands assume that + * one global device is the one to operate on. + */ + +/** \brief returns a version string for the CryptoAuthLib release. + * The format of the version string returned is "yyyymmdd" + * \param[out] ver_str ptr to space to receive version string + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_version(char *ver_str) +{ + strcpy(ver_str, atca_version); + return ATCA_SUCCESS; +} + + +/** \brief Creates and initializes a ATCADevice context + * \param[out] device Pointer to the device context pointer + * \param[in] cfg Logical interface configuration. Some predefined + * configurations can be found in atca_cfgs.h + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_init_ext(ATCADevice* device, ATCAIfaceCfg *cfg) +{ + ATCA_STATUS status = ATCA_GEN_FAIL; + + if (device) + { + // If a device has already been initialized, release it + if (*device) + { + atcab_release_ext(device); + } + +#ifdef ATCA_NO_HEAP + g_atcab_device.mIface = g_atcab_iface; + status = initATCADevice(cfg, &g_atcab_device); + if (status != ATCA_SUCCESS) + { + return status; + } + *device = &g_atcab_device; +#else + *device = newATCADevice(cfg); + if (*device == NULL) + { + return ATCA_GEN_FAIL; + } +#endif + +#ifdef ATCA_ATECC608_SUPPORT + if (cfg->devtype == ATECC608) + { + if ((status = calib_read_bytes_zone(*device, ATCA_ZONE_CONFIG, 0, ATCA_CHIPMODE_OFFSET, &(*device)->clock_divider, 1)) != ATCA_SUCCESS) + { + return status; + } + (*device)->clock_divider &= ATCA_CHIPMODE_CLOCK_DIV_MASK; + } +#endif + +#ifdef ATCA_ECC204_SUPPORT + /* To compatible with kitprotocol firmware on otherside */ + /* On kitprotocol firmware, during discovery time itself ECC204 would have woke up */ + if ((ECC204 == cfg->devtype) && (atca_iface_is_kit(atGetIFace(*device)))) + { + (*device)->device_state = ATCA_DEVICE_STATE_ACTIVE; + } +#endif + + } + + return ATCA_SUCCESS; +} + +/** \brief Creates a global ATCADevice object used by Basic API. + * \param[in] cfg Logical interface configuration. Some predefined + * configurations can be found in atca_cfgs.h + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_init(ATCAIfaceCfg* cfg) +{ + return atcab_init_ext(&_gDevice, cfg); +} + +/** \brief Initialize the global ATCADevice object to point to one of your + * choosing for use with all the atcab_ basic API. + * + * \deprecated This function is not recommended for use generally. Use of _ext + * is recommended instead. You can use atcab_init_ext to obtain an initialized + * instance and associated it with the global structure - but this shouldn't be + * a required process except in extremely unusual circumstances. + * + * \param[in] ca_device ATCADevice instance to use as the global Basic API + * crypto device instance + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_init_device(ATCADevice ca_device) +{ + if (ca_device == NULL) + { + return ATCA_BAD_PARAM; + } + + // if there's already a device created, release it + if (_gDevice) + { + atcab_release(); + } + + _gDevice = ca_device; + + return ATCA_SUCCESS; +} + +/** \brief release (free) the an ATCADevice instance. + * \param[in] device Pointer to the device context pointer + * \return Returns ATCA_SUCCESS . + */ +ATCA_STATUS atcab_release_ext(ATCADevice* device) +{ +#ifdef ATCA_NO_HEAP + ATCA_STATUS status = releaseATCADevice(*device); + if (status != ATCA_SUCCESS) + { + return status; + } + *device = NULL; +#else + deleteATCADevice(device); +#endif + return ATCA_SUCCESS; +} + +/** \brief release (free) the global ATCADevice instance. + * This must be called in order to release or free up the interface. + * \return Returns ATCA_SUCCESS . + */ +ATCA_STATUS atcab_release(void) +{ + return atcab_release_ext(&_gDevice); +} + +/** \brief Get the global device object. + * \return instance of global ATCADevice + */ +ATCADevice atcab_get_device(void) +{ + return _gDevice; +} + +/** \brief Get the selected device type of rthe device context + * + * \param[in] device Device context pointer + * \return Device type if basic api is initialized or ATCA_DEV_UNKNOWN. + */ +ATCADeviceType atcab_get_device_type_ext(ATCADevice device) +{ + ATCADeviceType ret = ATCA_DEV_UNKNOWN; + + if (device && device->mIface.mIfaceCFG) + { + ret = device->mIface.mIfaceCFG->devtype; + } + return ret; +} + +/** \brief Get the current device type configured for the global ATCADevice + * \return Device type if basic api is initialized or ATCA_DEV_UNKNOWN. + */ +ATCADeviceType atcab_get_device_type(void) +{ + return atcab_get_device_type_ext(_gDevice); +} + +/** \brief Get the current device address based on the configured device + * and interface + * \return the device address if applicable else 0xFF + */ +uint8_t atcab_get_device_address(ATCADevice device) +{ + if (device && device->mIface.mIfaceCFG) + { + switch (device->mIface.mIfaceCFG->iface_type) + { + case ATCA_I2C_IFACE: +#ifdef ATCA_ENABLE_DEPRECATED + return device->mIface.mIfaceCFG->atcai2c.slave_address; +#else + return device->mIface.mIfaceCFG->atcai2c.address; +#endif + default: + break; + } + } + return 0xFF; +} + + +/** \brief Check whether the device is cryptoauth device + * \return True if device is cryptoauth device or False. + */ +bool atcab_is_ca_device(ATCADeviceType dev_type) +{ + return (dev_type < TA100) ? true : false; +} + +/** \brief Check whether the device is Trust Anchor device + * \return True if device is Trust Anchor device or False. + */ +bool atcab_is_ta_device(ATCADeviceType dev_type) +{ + return (dev_type == TA100) ? true : false; +} + +#if (ATCA_CA_SUPPORT && ATCA_TA_SUPPORT) || defined(ATCA_USE_ATCAB_FUNCTIONS) || defined(ATCA_ECC204_SUPPORT) + +/** \brief wakeup the CryptoAuth device + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_wakeup(void) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + status = calib_wakeup(_gDevice); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { +#if ATCA_TA_SUPPORT + status = ATCA_SUCCESS; +#endif + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief idle the CryptoAuth device + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_idle(void) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + status = calib_idle(_gDevice); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { +#if ATCA_TA_SUPPORT + status = ATCA_SUCCESS; +#endif + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief invoke sleep on the CryptoAuth device + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_sleep(void) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + status = calib_sleep(_gDevice); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { +#if ATCA_TA_SUPPORT + status = ATCA_SUCCESS; +#endif + } + else + { + status = ATCA_NOT_INITIALIZED; + } + + return status; +} + +/** \brief Gets the size of the specified zone in bytes. + * + * \param[in] zone Zone to get size information from. Config(0), OTP(1), or + * Data(2) which requires a slot. + * \param[in] slot If zone is Data(2), the slot to query for size. + * \param[out] size Zone size is returned here. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_get_zone_size(uint8_t zone, uint16_t slot, size_t* size) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + status = calib_get_zone_size(_gDevice, zone, slot, size); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { +#if ATCA_TA_SUPPORT + status = talib_get_zone_size(_gDevice, zone, slot, size); +#endif + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/* AES commands */ +/** \brief Compute the AES-128 encrypt, decrypt, or GFM calculation. + * \param[in] mode The mode for the AES command. + * \param[in] key_id Key location. Can either be a slot number or + * ATCA_TEMPKEY_KEYID for TempKey. + * \param[in] aes_in Input data to the AES command (16 bytes). + * \param[out] aes_out Output data from the AES command is returned here (16 + * bytes). + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_aes(uint8_t mode, uint16_t key_id, const uint8_t* aes_in, uint8_t* aes_out) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if defined(ATCA_ATECC608_SUPPORT) + status = calib_aes(_gDevice, mode, key_id, aes_in, aes_out); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { + status = ATCA_UNIMPLEMENTED; + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Perform an AES-128 encrypt operation with a key in the device. + * + * \param[in] device Device context pointer + * \param[in] key_id Key location. Can either be a slot number or + * ATCA_TEMPKEY_KEYID for TempKey. + * \param[in] key_block Index of the 16-byte block to use within the key + * location for the actual key. + * \param[in] plaintext Input plaintext to be encrypted (16 bytes). + * \param[out] ciphertext Output ciphertext is returned here (16 bytes). + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_aes_encrypt_ext(ATCADevice device, uint16_t key_id, uint8_t key_block, const uint8_t* plaintext, uint8_t* ciphertext) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type_ext(device); + + if (atcab_is_ca_device(dev_type)) + { +#if defined(ATCA_ATECC608_SUPPORT) + status = calib_aes_encrypt(device, key_id, key_block, plaintext, ciphertext); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { +#if ATCA_TA_SUPPORT + status = talib_aes_encrypt(device, key_id, key_block, plaintext, ciphertext); +#endif + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Perform an AES-128 encrypt operation with a key in the device. + * + * \param[in] key_id Key location. Can either be a slot number or + * ATCA_TEMPKEY_KEYID for TempKey. + * \param[in] key_block Index of the 16-byte block to use within the key + * location for the actual key. + * \param[in] plaintext Input plaintext to be encrypted (16 bytes). + * \param[out] ciphertext Output ciphertext is returned here (16 bytes). + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_aes_encrypt(uint16_t key_id, uint8_t key_block, const uint8_t* plaintext, uint8_t* ciphertext) +{ + return atcab_aes_encrypt_ext(_gDevice, key_id, key_block, plaintext, ciphertext); +} + +/** \brief Perform an AES-128 decrypt operation with a key in the device. + * + * \param[in] device Device context pointer + * \param[in] key_id Key location. Can either be a slot number or + * ATCA_TEMPKEY_KEYID for TempKey. + * \param[in] key_block Index of the 16-byte block to use within the key + * location for the actual key. + * \param[in] ciphertext Input ciphertext to be decrypted (16 bytes). + * \param[out] plaintext Output plaintext is returned here (16 bytes). + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_aes_decrypt_ext(ATCADevice device, uint16_t key_id, uint8_t key_block, const uint8_t* ciphertext, uint8_t* plaintext) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type_ext(device); + + if (atcab_is_ca_device(dev_type)) + { +#if defined(ATCA_ATECC608_SUPPORT) + status = calib_aes_decrypt(device, key_id, key_block, ciphertext, plaintext); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { +#if ATCA_TA_SUPPORT + status = talib_aes_decrypt(device, key_id, key_block, ciphertext, plaintext); +#endif + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Perform an AES-128 decrypt operation with a key in the device. + * + * \param[in] key_id Key location. Can either be a slot number or + * ATCA_TEMPKEY_KEYID for TempKey. + * \param[in] key_block Index of the 16-byte block to use within the key + * location for the actual key. + * \param[in] ciphertext Input ciphertext to be decrypted (16 bytes). + * \param[out] plaintext Output plaintext is returned here (16 bytes). + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_aes_decrypt(uint16_t key_id, uint8_t key_block, const uint8_t* ciphertext, uint8_t* plaintext) +{ + return atcab_aes_decrypt_ext(_gDevice, key_id, key_block, ciphertext, plaintext); +} + +/** \brief Perform a Galois Field Multiply (GFM) operation. + * + * \param[in] h First input value (16 bytes). + * \param[in] input Second input value (16 bytes). + * \param[out] output GFM result is returned here (16 bytes). + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_aes_gfm(const uint8_t* h, const uint8_t* input, uint8_t* output) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if defined(ATCA_ATECC608_SUPPORT) + status = calib_aes_gfm(_gDevice, h, input, output); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { + status = ATCA_UNIMPLEMENTED; + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Initialize context for AES GCM operation with an existing IV, which + * is common when starting a decrypt operation. + * + * \param[in] ctx AES GCM context to be initialized. + * \param[in] key_id Key location. Can either be a slot number or + * ATCA_TEMPKEY_KEYID for TempKey. + * \param[in] key_block Index of the 16-byte block to use within the key + * location for the actual key. + * \param[in] iv Initialization vector. + * \param[in] iv_size Size of IV in bytes. Standard is 12 bytes. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_aes_gcm_init(atca_aes_gcm_ctx_t* ctx, uint16_t key_id, uint8_t key_block, const uint8_t* iv, size_t iv_size) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + status = calib_aes_gcm_init(_gDevice, ctx, key_id, key_block, iv, iv_size); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { + status = ATCA_UNIMPLEMENTED; + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Initialize context for AES GCM operation with a IV composed of a + * random and optional fixed(free) field, which is common when + * starting an encrypt operation. + * + * \param[in] ctx AES CTR context to be initialized. + * \param[in] key_id Key location. Can either be a slot number or + * ATCA_TEMPKEY_KEYID for TempKey. + * \param[in] key_block Index of the 16-byte block to use within the + * key location for the actual key. + * \param[in] rand_size Size of the random field in bytes. Minimum and + * recommended size is 12 bytes. Max is 32 bytes. + * \param[in] free_field Fixed data to include in the IV after the + * random field. Can be NULL if not used. + * \param[in] free_field_size Size of the free field in bytes. + * \param[out] iv Initialization vector is returned here. Its + * size will be rand_size and free_field_size + * combined. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_aes_gcm_init_rand(atca_aes_gcm_ctx_t* ctx, uint16_t key_id, uint8_t key_block, size_t rand_size, + const uint8_t* free_field, size_t free_field_size, uint8_t* iv) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + status = calib_aes_gcm_init_rand(_gDevice, ctx, key_id, key_block, rand_size, free_field, free_field_size, iv); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { + status = ATCA_UNIMPLEMENTED; + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Process Additional Authenticated Data (AAD) using GCM mode and a + * key within the ATECC608 device. + * + * This can be called multiple times. atcab_aes_gcm_init() or + * atcab_aes_gcm_init_rand() should be called before the first use of this + * function. When there is AAD to include, this should be called before + * atcab_aes_gcm_encrypt_update() or atcab_aes_gcm_decrypt_update(). + * + * \param[in] ctx AES GCM context + * \param[in] aad Additional authenticated data to be added + * \param[in] aad_size Size of aad in bytes + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_aes_gcm_aad_update(atca_aes_gcm_ctx_t* ctx, const uint8_t* aad, uint32_t aad_size) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + status = calib_aes_gcm_aad_update(_gDevice, ctx, aad, aad_size); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { + status = ATCA_UNIMPLEMENTED; + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Encrypt data using GCM mode and a key within the ATECC608 device. + * atcab_aes_gcm_init() or atcab_aes_gcm_init_rand() should be called + * before the first use of this function. + * + * \param[in] ctx AES GCM context structure. + * \param[in] plaintext Plaintext to be encrypted (16 bytes). + * \param[in] plaintext_size Size of plaintext in bytes. + * \param[out] ciphertext Encrypted data is returned here. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_aes_gcm_encrypt_update(atca_aes_gcm_ctx_t* ctx, const uint8_t* plaintext, uint32_t plaintext_size, uint8_t* ciphertext) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + status = calib_aes_gcm_encrypt_update(_gDevice, ctx, plaintext, plaintext_size, ciphertext); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { + status = ATCA_UNIMPLEMENTED; + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Complete a GCM encrypt operation returning the authentication tag. + * + * \param[in] ctx AES GCM context structure. + * \param[out] tag Authentication tag is returned here. + * \param[in] tag_size Tag size in bytes (12 to 16 bytes). + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_aes_gcm_encrypt_finish(atca_aes_gcm_ctx_t* ctx, uint8_t* tag, size_t tag_size) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + status = calib_aes_gcm_encrypt_finish(_gDevice, ctx, tag, tag_size); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { + status = ATCA_UNIMPLEMENTED; + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Decrypt data using GCM mode and a key within the ATECC608 device. + * atcab_aes_gcm_init() or atcab_aes_gcm_init_rand() should be called + * before the first use of this function. + * + * \param[in] ctx AES GCM context structure. + * \param[in] ciphertext Ciphertext to be decrypted. + * \param[in] ciphertext_size Size of ciphertext in bytes. + * \param[out] plaintext Decrypted data is returned here. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_aes_gcm_decrypt_update(atca_aes_gcm_ctx_t* ctx, const uint8_t* ciphertext, uint32_t ciphertext_size, uint8_t* plaintext) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + status = calib_aes_gcm_decrypt_update(_gDevice, ctx, ciphertext, ciphertext_size, plaintext); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { + status = ATCA_UNIMPLEMENTED; + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Complete a GCM decrypt operation verifying the authentication tag. + * + * \param[in] ctx AES GCM context structure. + * \param[in] tag Expected authentication tag. + * \param[in] tag_size Size of tag in bytes (12 to 16 bytes). + * \param[out] is_verified Returns whether or not the tag verified. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_aes_gcm_decrypt_finish(atca_aes_gcm_ctx_t* ctx, const uint8_t* tag, size_t tag_size, bool* is_verified) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + status = calib_aes_gcm_decrypt_finish(_gDevice, ctx, tag, tag_size, is_verified); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { + status = ATCA_UNIMPLEMENTED; + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/* CheckMAC command */ + +/** \brief Compares a MAC response with input values + * \param[in] mode Controls which fields within the device are used in + * the message + * \param[in] key_id Key location in the CryptoAuth device to use for the + * MAC + * \param[in] challenge Challenge data (32 bytes) + * \param[in] response MAC response data (32 bytes) + * \param[in] other_data OtherData parameter (13 bytes) + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_checkmac(uint8_t mode, uint16_t key_id, const uint8_t* challenge, const uint8_t* response, const uint8_t* other_data) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + status = calib_checkmac(_gDevice, mode, key_id, challenge, response, other_data); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { + status = ATCA_UNIMPLEMENTED; + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/* Counter command */ + +/** \brief Compute the Counter functions + * \param[in] mode the mode used for the counter + * \param[in] counter_id The counter to be used + * \param[out] counter_value pointer to the counter value returned from device + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_counter(uint8_t mode, uint16_t counter_id, uint32_t* counter_value) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + status = calib_counter(_gDevice, mode, counter_id, counter_value); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { +#if ATCA_TA_SUPPORT + status = talib_counter(_gDevice, mode, counter_id, counter_value); +#endif + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Increments one of the device's monotonic counters + * \param[in] counter_id Counter to be incremented + * \param[out] counter_value New value of the counter is returned here. Can be + * NULL if not needed. + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_counter_increment(uint16_t counter_id, uint32_t* counter_value) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + status = calib_counter_increment(_gDevice, counter_id, counter_value); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { +#if ATCA_TA_SUPPORT + status = talib_counter_increment(_gDevice, counter_id, counter_value); +#endif + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Read one of the device's monotonic counters + * \param[in] counter_id Counter to be read + * \param[out] counter_value Counter value is returned here. + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_counter_read(uint16_t counter_id, uint32_t* counter_value) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + status = calib_counter_read(_gDevice, counter_id, counter_value); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { +#if ATCA_TA_SUPPORT + status = talib_counter_read(_gDevice, counter_id, counter_value); +#endif + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/* DeriveKey command */ + +/** \brief Executes the DeviveKey command for deriving a new key from a + * nonce (TempKey) and an existing key. + * + * \param[in] mode Bit 2 must match the value in TempKey.SourceFlag + * \param[in] key_id Key slot to be written + * \param[in] mac Optional 32 byte MAC used to validate operation. NULL + * if not required. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_derivekey(uint8_t mode, uint16_t key_id, const uint8_t* mac) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + status = calib_derivekey(_gDevice, mode, key_id, mac); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { + status = ATCA_UNIMPLEMENTED; + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/* ECDH command */ + +/** \brief Base function for generating premaster secret key using ECDH. + * \param[in] mode Mode to be used for ECDH computation + * \param[in] key_id Slot of key for ECDH computation + * \param[in] public_key Public key input to ECDH calculation. X and Y + * integers in big-endian format. 64 bytes for P256 + * key. + * \param[out] pms Computed ECDH pre-master secret is returned here (32 + * bytes) if returned directly. Otherwise NULL. + * \param[out] out_nonce Nonce used to encrypt pre-master secret. NULL if + * output encryption not used. + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_ecdh_base(uint8_t mode, uint16_t key_id, const uint8_t* public_key, uint8_t* pms, uint8_t* out_nonce) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#ifdef ATCA_ECC_SUPPORT + status = calib_ecdh_base(_gDevice, mode, key_id, public_key, pms, out_nonce); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { + status = ATCA_UNIMPLEMENTED; + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief ECDH command with a private key in a slot and the premaster secret + * is returned in the clear. + * + * \param[in] key_id Slot of private key for ECDH computation + * \param[in] public_key Public key input to ECDH calculation. X and Y + * integers in big-endian format. 64 bytes for P256 + * key. + * \param[out] pms Computed ECDH premaster secret is returned here. + * 32 bytes. + * + * \return ATCA_SUCCESS on success + */ +ATCA_STATUS atcab_ecdh(uint16_t key_id, const uint8_t* public_key, uint8_t* pms) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#ifdef ATCA_ECC_SUPPORT + status = calib_ecdh(_gDevice, key_id, public_key, pms); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { +#if ATCA_TA_SUPPORT + status = talib_ecdh_compat(_gDevice, key_id, public_key, pms); +#endif + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief ECDH command with a private key in a slot and the premaster secret + * is read from the next slot. + * + * This function only works for even numbered slots with the proper + * configuration. + * + * \param[in] key_id Slot of key for ECDH computation + * \param[in] public_key Public key input to ECDH calculation. X and Y + * integers in big-endian format. 64 bytes for P256 + * key. + * \param[out] pms Computed ECDH premaster secret is returned here + * (32 bytes). + * \param[in] read_key Read key for the premaster secret slot (key_id|1). + * \param[in] read_key_id Read key slot for read_key. + * \param[in] num_in 20 byte host nonce to inject into Nonce calculation + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +#if defined(ATCA_USE_CONSTANT_HOST_NONCE) +ATCA_STATUS atcab_ecdh_enc(uint16_t key_id, const uint8_t* public_key, uint8_t* pms, const uint8_t* read_key, uint16_t read_key_id) +#else +ATCA_STATUS atcab_ecdh_enc(uint16_t key_id, const uint8_t* public_key, uint8_t* pms, const uint8_t* read_key, uint16_t read_key_id, const uint8_t num_in[NONCE_NUMIN_SIZE]) +#endif +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#ifdef ATCA_ECC_SUPPORT +#ifdef ATCA_USE_CONSTANT_HOST_NONCE + status = calib_ecdh_enc(_gDevice, key_id, public_key, pms, read_key, read_key_id); +#else + status = calib_ecdh_enc(_gDevice, key_id, public_key, pms, read_key, read_key_id, num_in); +#endif +#endif + } + else if (atcab_is_ta_device(dev_type)) + { + status = ATCA_UNIMPLEMENTED; + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief ECDH command with a private key in a slot and the premaster secret + * is returned encrypted using the IO protection key. + * + * \param[in] key_id Slot of key for ECDH computation + * \param[in] public_key Public key input to ECDH calculation. X and Y + * integers in big-endian format. 64 bytes for P256 + * key. + * \param[out] pms Computed ECDH premaster secret is returned here + * (32 bytes). + * \param[in] io_key IO protection key. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_ecdh_ioenc(uint16_t key_id, const uint8_t* public_key, uint8_t* pms, const uint8_t* io_key) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#ifdef ATCA_ATECC608_SUPPORT + status = calib_ecdh_ioenc(_gDevice, key_id, public_key, pms, io_key); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { + status = ATCA_UNIMPLEMENTED; + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief ECDH command with a private key in TempKey and the premaster secret + * is returned in the clear. + * + * \param[in] public_key Public key input to ECDH calculation. X and Y + * integers in big-endian format. 64 bytes for P256 + * key. + * \param[out] pms Computed ECDH premaster secret is returned here + * (32 bytes). + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_ecdh_tempkey(const uint8_t* public_key, uint8_t* pms) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#ifdef ATCA_ATECC608_SUPPORT + status = calib_ecdh_tempkey(_gDevice, public_key, pms); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { + status = ATCA_UNIMPLEMENTED; + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief ECDH command with a private key in TempKey and the premaster secret + * is returned encrypted using the IO protection key. + * + * \param[in] public_key Public key input to ECDH calculation. X and Y + * integers in big-endian format. 64 bytes for P256 + * key. + * \param[out] pms Computed ECDH premaster secret is returned here + * (32 bytes). + * \param[in] io_key IO protection key. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_ecdh_tempkey_ioenc(const uint8_t* public_key, uint8_t* pms, const uint8_t* io_key) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#ifdef ATCA_ATECC608_SUPPORT + status = calib_ecdh_tempkey_ioenc(_gDevice, public_key, pms, io_key); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { + status = ATCA_UNIMPLEMENTED; + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/* GenDig command */ + +/** \brief Issues a GenDig command, which performs a SHA256 hash on the source data indicated by zone with the + * contents of TempKey. See the CryptoAuth datasheet for your chip to see what the values of zone + * correspond to. + * \param[in] zone Designates the source of the data to hash with TempKey. + * \param[in] key_id Indicates the key, OTP block, or message order for shared nonce mode. + * \param[in] other_data Four bytes of data for SHA calculation when using a NoMac key, 32 bytes for + * "Shared Nonce" mode, otherwise ignored (can be NULL). + * \param[in] other_data_size Size of other_data in bytes. + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_gendig(uint8_t zone, uint16_t key_id, const uint8_t* other_data, uint8_t other_data_size) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + status = calib_gendig(_gDevice, zone, key_id, other_data, other_data_size); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { + status = ATCA_UNIMPLEMENTED; + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/* GenKey command */ + +/** \brief Issues GenKey command, which can generate a private key, compute a + * public key, nd/or compute a digest of a public key. + * + * \param[in] mode Mode determines what operations the GenKey + * command performs. + * \param[in] key_id Slot to perform the GenKey command on. + * \param[in] other_data OtherData for PubKey digest calculation. Can be set + * to NULL otherwise. + * \param[out] public_key If the mode indicates a public key will be + * calculated, it will be returned here. Format will + * be the X and Y integers in big-endian format. + * 64 bytes for P256 curve. Set to NULL if public key + * isn't required. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_genkey_base(uint8_t mode, uint16_t key_id, const uint8_t* other_data, uint8_t* public_key) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + status = calib_genkey_base(_gDevice, mode, key_id, other_data, public_key); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { + status = ATCA_UNIMPLEMENTED; + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Issues GenKey command, which generates a new random private key in + * slot/handle and returns the public key. + * + * \param[in] key_id Slot number where an ECC private key is configured. + * Can also be ATCA_TEMPKEY_KEYID to generate a private + * key in TempKey. + * \param[out] public_key Public key will be returned here. Format will be + * the X and Y integers in big-endian format. + * 64 bytes for P256 curve. Set to NULL if public key + * isn't required. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_genkey(uint16_t key_id, uint8_t* public_key) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + status = calib_genkey(_gDevice, key_id, public_key); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { +#if ATCA_TA_SUPPORT + status = talib_genkey_compat(_gDevice, key_id, public_key); +#endif + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Uses GenKey command to calculate the public key from an existing + * private key in a slot. + * + * \param[in] key_id Slot number of the private key. + * \param[out] public_key Public key will be returned here. Format will be + * the X and Y integers in big-endian format. + * 64 bytes for P256 curve. Set to NULL if public key + * isn't required. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_get_pubkey_ext(ATCADevice device, uint16_t key_id, uint8_t* public_key) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type_ext(device); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + status = calib_get_pubkey(device, key_id, public_key); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { +#if ATCA_TA_SUPPORT + status = talib_get_pubkey_compat(device, key_id, public_key); +#endif + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Uses GenKey command to calculate the public key from an existing + * private key in a slot. + * + * \param[in] key_id Slot number of the private key. + * \param[out] public_key Public key will be returned here. Format will be + * the X and Y integers in big-endian format. + * 64 bytes for P256 curve. Set to NULL if public key + * isn't required. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_get_pubkey(uint16_t key_id, uint8_t* public_key) +{ + return atcab_get_pubkey_ext(_gDevice, key_id, public_key); +} + +// HMAC command functions + +/** \brief Issues a HMAC command, which computes an HMAC/SHA-256 digest of a + * key stored in the device, a challenge, and other information on the + * device. + * + * \param[in] mode Controls which fields within the device are used in the + * message. + * \param[in] key_id Which key is to be used to generate the response. + * Bits 0:3 only are used to select a slot but all 16 bits + * are used in the HMAC message. + * \param[out] digest HMAC digest is returned in this buffer (32 bytes). + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_hmac(uint8_t mode, uint16_t key_id, uint8_t* digest) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if defined(ATCA_ATSHA204A_SUPPORT) || defined(ATCA_ATECC508A_SUPPORT) + status = calib_hmac(_gDevice, mode, key_id, digest); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { + status = ATCA_UNIMPLEMENTED; + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +// Info command functions + +/** \brief Issues an Info command, which return internal device information and + * can control GPIO and the persistent latch. + * + * \param[in] mode Selects which mode to be used for info command. + * \param[in] param2 Selects the particular fields for the mode. + * \param[out] out_data Response from info command (4 bytes). Can be set to + * NULL if not required. + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_info_base(uint8_t mode, uint16_t param2, uint8_t* out_data) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + status = calib_info_base(_gDevice, mode, param2, out_data); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { + status = ATCA_UNIMPLEMENTED; + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Use the Info command to get the device revision (DevRev). + * \param[out] revision Device revision is returned here (4 bytes). + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_info(uint8_t* revision) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + status = calib_info(_gDevice, revision); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { +#if ATCA_TA_SUPPORT + status = talib_info_compat(_gDevice, revision); +#endif + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Use the Info command to set the persistent latch state for an + * ATECC608 device. + * + * \param[out] state Persistent latch state. Set (true) or clear (false). + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_info_set_latch(bool state) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + status = calib_info_set_latch(_gDevice, state); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { + status = ATCA_UNIMPLEMENTED; + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Use the Info command to get the persistent latch current state for + * an ATECC608 device. + * + * \param[out] state The state is returned here. Set (true) or Cler (false). + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_info_get_latch(bool* state) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + status = calib_info_get_latch(_gDevice, state); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { + status = ATCA_UNIMPLEMENTED; + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +// KDF command functions + +/** \brief Executes the KDF command, which derives a new key in PRF, AES, or + * HKDF modes. + * + * Generally this function combines a source key with an input string and + * creates a result key/digest/array. + * + * \param[in] mode Mode determines KDF algorithm (PRF,AES,HKDF), source + * key location, and target key locations. + * \param[in] key_id Source and target key slots if locations are in the + * EEPROM. Source key slot is the LSB and target key + * slot is the MSB. + * \param[in] details Further information about the computation, depending + * on the algorithm (4 bytes). + * \param[in] message Input value from system (up to 128 bytes). Actual size + * of message is 16 bytes for AES algorithm or is encoded + * in the MSB of the details parameter for other + * algorithms. + * \param[out] out_data Output of the KDF function is returned here. If the + * result remains in the device, this can be NULL. + * \param[out] out_nonce If the output is encrypted, a 32 byte random nonce + * generated by the device is returned here. If output + * encryption is not used, this can be NULL. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_kdf(uint8_t mode, uint16_t key_id, const uint32_t details, const uint8_t* message, uint8_t* out_data, uint8_t* out_nonce) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + status = calib_kdf(_gDevice, mode, key_id, details, message, out_data, out_nonce); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { + status = ATCA_UNIMPLEMENTED; + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/* Lock commands */ + +/** \brief The Lock command prevents future modifications of the Configuration + * and/or Data and OTP zones. If the device is so configured, then + * this command can be used to lock individual data slots. This + * command fails if the designated area is already locked. + * + * \param[in] mode Zone, and/or slot, and summary check (bit 7). + * \param[in] summary_crc CRC of the config or data zones. Ignored for + * slot locks or when mode bit 7 is set. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_lock(uint8_t mode, uint16_t summary_crc) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + status = calib_lock(_gDevice, mode, summary_crc); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { + status = ATCA_UNIMPLEMENTED; + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Unconditionally (no CRC required) lock the config zone. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_lock_config_zone(void) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + if (ECC204 == dev_type) + { +#if defined(ATCA_ECC204_SUPPORT) + status = calib_ecc204_lock_config_zone(_gDevice); +#endif + } + else + { + status = calib_lock_config_zone(_gDevice); + } +#endif + } + else if (atcab_is_ta_device(dev_type)) + { +#if ATCA_TA_SUPPORT + status = talib_lock_config(_gDevice); +#endif + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Lock the config zone with summary CRC. + * + * The CRC is calculated over the entire config zone contents. 48 bytes for TA100, + * 88 bytes for ATSHA devices, 128 bytes for ATECC devices. Lock will fail if the provided + * CRC doesn't match the internally calculated one. + * + * \param[in] summary_crc Expected CRC over the config zone. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_lock_config_zone_crc(uint16_t summary_crc) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + if (ECC204 == dev_type) + { +#if defined(ATCA_ECC204_SUPPORT) + status = ATCA_UNIMPLEMENTED; +#endif + } + else + { + status = calib_lock_config_zone_crc(_gDevice, summary_crc); + } +#endif + } + else if (atcab_is_ta_device(dev_type)) + { +#if ATCA_TA_SUPPORT + status = talib_lock_config_with_crc(_gDevice, summary_crc); +#endif + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Unconditionally (no CRC required) lock the data zone (slots and OTP). + * for CryptoAuth devices and lock the setup for Trust Anchor device. + * + * ConfigZone must be locked and DataZone must be unlocked for the zone to be successfully locked. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_lock_data_zone(void) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + if (ECC204 == dev_type) + { +#if defined(ATCA_ECC204_SUPPORT) + status = calib_ecc204_lock_data_zone(_gDevice); +#endif + } + else + { + status = calib_lock_data_zone(_gDevice); + } +#endif + } + else if (atcab_is_ta_device(dev_type)) + { +#if ATCA_TA_SUPPORT + status = talib_lock_setup(_gDevice); +#endif + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Lock the data zone (slots and OTP) with summary CRC. + * + * The CRC is calculated over the concatenated contents of all the slots and + * OTP at the end. Private keys (KeyConfig.Private=1) are skipped. Lock will + * fail if the provided CRC doesn't match the internally calculated one. + * + * \param[in] summary_crc Expected CRC over the data zone. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_lock_data_zone_crc(uint16_t summary_crc) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + if (ECC204 == dev_type) + { +#if defined(ATCA_ECC204_SUPPORT) + status = ATCA_UNIMPLEMENTED; +#endif + } + else + { + status = calib_lock_data_zone_crc(_gDevice, summary_crc); + } +#endif + } + else if (atcab_is_ta_device(dev_type)) + { + status = ATCA_UNIMPLEMENTED; + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Lock an individual slot in the data zone on an ATECC device. Not + * available for ATSHA devices. Slot must be configured to be slot + * lockable (KeyConfig.Lockable=1) (for cryptoauth devices) or Lock + * an individual handle in shared data element on an Trust Anchor device + * (for Trust Anchor devices). + * + * \param[in] slot Slot to be locked in data zone. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_lock_data_slot(uint16_t slot) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + if (ECC204 == dev_type) + { +#if defined(ATCA_ECC204_SUPPORT) + status = calib_ecc204_lock_data_slot(_gDevice, slot); +#endif + } + else + { + status = calib_lock_data_slot(_gDevice, slot); + } +#endif + } + else if (atcab_is_ta_device(dev_type)) + { +#if ATCA_TA_SUPPORT + status = talib_lock_handle(_gDevice, slot); +#endif + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +// MAC command functions + +/** \brief Executes MAC command, which computes a SHA-256 digest of a key + * stored in the device, a challenge, and other information on the + * device. + * + * \param[in] mode Controls which fields within the device are used in + * the message + * \param[in] key_id Key in the CryptoAuth device to use for the MAC + * \param[in] challenge Challenge message (32 bytes). May be NULL if mode + * indicates a challenge isn't required. + * \param[out] digest MAC response is returned here (32 bytes). + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_mac(uint8_t mode, uint16_t key_id, const uint8_t* challenge, uint8_t* digest) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + status = calib_mac(_gDevice, mode, key_id, challenge, digest); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { + status = ATCA_UNIMPLEMENTED; + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +// Nonce command functions + +/** \brief Executes Nonce command, which loads a random or fixed nonce/data + * into the device for use by subsequent commands. + * + * \param[in] mode Controls the mechanism of the internal RNG or fixed + * write. + * \param[in] zero Param2, normally 0, but can be used to indicate a + * nonce calculation mode (bit 15). + * \param[in] num_in Input value to either be included in the nonce + * calculation in random modes (20 bytes) or to be + * written directly (32 bytes or 64 bytes(ATECC608)) + * in pass-through mode. + * \param[out] rand_out If using a random mode, the internally generated + * 32-byte random number that was used in the nonce + * calculation is returned here. Can be NULL if not + * needed. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_nonce_base(uint8_t mode, uint16_t zero, const uint8_t* num_in, uint8_t* rand_out) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + status = calib_nonce_base(_gDevice, mode, zero, num_in, rand_out); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { + status = ATCA_UNIMPLEMENTED; + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Execute a Nonce command in pass-through mode to initialize TempKey + * to a specified value. + * + * \param[in] num_in Data to be loaded into TempKey (32 bytes). + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_nonce(const uint8_t* num_in) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + status = calib_nonce(_gDevice, num_in); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { + status = ATCA_UNIMPLEMENTED; + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Execute a Nonce command in pass-through mode to load one of the + * device's internal buffers with a fixed value. + * + * For the ATECC608, available targets are TempKey (32 or 64 bytes), Message + * Digest Buffer (32 or 64 bytes), or the Alternate Key Buffer (32 bytes). For + * all other devices, only TempKey (32 bytes) is available. + * + * \param[in] target Target device buffer to load. Can be + * NONCE_MODE_TARGET_TEMPKEY, + * NONCE_MODE_TARGET_MSGDIGBUF, or + * NONCE_MODE_TARGET_ALTKEYBUF. + * \param[in] num_in Data to load into the buffer. + * \param[in] num_in_size Size of num_in in bytes. Can be 32 or 64 bytes + * depending on device and target. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_nonce_load(uint8_t target, const uint8_t* num_in, uint16_t num_in_size) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + status = calib_nonce_load(_gDevice, target, num_in, num_in_size); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { + status = ATCA_UNIMPLEMENTED; + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Execute a Nonce command to generate a random nonce combining a host + * nonce (num_in) and a device random number. + * + * \param[in] num_in Host nonce to be combined with the device random + * number (20 bytes). + * \param[out] rand_out Internally generated 32-byte random number that was + * used in the nonce/challenge calculation is returned + * here. Can be NULL if not needed. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_nonce_rand(const uint8_t* num_in, uint8_t* rand_out) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + status = calib_nonce_rand(_gDevice, num_in, rand_out); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { + status = ATCA_UNIMPLEMENTED; + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Execute a Nonce command in pass-through mode to initialize TempKey + * to a specified value. + * + * \param[in] num_in Data to be loaded into TempKey (32 bytes). + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_challenge(const uint8_t* num_in) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + status = calib_challenge(_gDevice, num_in); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { + status = ATCA_UNIMPLEMENTED; + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Execute a Nonce command to generate a random challenge combining + * a host nonce (num_in) and a device random number. + * + * \param[in] num_in Host nonce to be combined with the device random + * number (20 bytes). + * \param[out] rand_out Internally generated 32-byte random number that was + * used in the nonce/challenge calculation is returned + * here. Can be NULL if not needed. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_challenge_seed_update(const uint8_t* num_in, uint8_t* rand_out) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + status = calib_challenge_seed_update(_gDevice, num_in, rand_out); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { + status = ATCA_UNIMPLEMENTED; + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +// PrivWrite command functions + +/** \brief Executes PrivWrite command, to write externally generated ECC + * private keys into the device. + * + * \param[in] key_id Slot to write the external private key into. + * \param[in] priv_key External private key (36 bytes) to be written. + * The first 4 bytes should be zero for P256 curve. + * \param[in] write_key_id Write key slot. Ignored if write_key is NULL. + * \param[in] write_key Write key (32 bytes). If NULL, perform an + * unencrypted PrivWrite, which is only available when + * the data zone is unlocked. + * \param[in] num_in 20 byte host nonce to inject into Nonce calculation + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +#if defined(ATCA_USE_CONSTANT_HOST_NONCE) +ATCA_STATUS atcab_priv_write(uint16_t key_id, const uint8_t priv_key[36], uint16_t write_key_id, const uint8_t write_key[32]) +#else +ATCA_STATUS atcab_priv_write(uint16_t key_id, const uint8_t priv_key[36], uint16_t write_key_id, const uint8_t write_key[32], const uint8_t num_in[NONCE_NUMIN_SIZE]) +#endif +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#ifdef ATCA_ECC_SUPPORT +#ifdef ATCA_USE_CONSTANT_HOST_NONCE + status = calib_priv_write(_gDevice, key_id, priv_key, write_key_id, write_key); +#else + status = calib_priv_write(_gDevice, key_id, priv_key, write_key_id, write_key, num_in); +#endif /* ATCA_USE_CONSTANT_HOST_NONCE */ +#endif /* ATCA_ECC_SUPPORT */ + } + else if (atcab_is_ta_device(dev_type)) + { + status = ATCA_UNIMPLEMENTED; + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +// Random command functions + +/** \brief Executes Random command, which generates a 32 byte random number + * from the device. + * + * \param[in] device Device context pointer + * \param[out] rand_out 32 bytes of random data is returned here. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_random_ext(ATCADevice device, uint8_t* rand_out) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type_ext(device); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + status = calib_random(device, rand_out); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { +#if ATCA_TA_SUPPORT + status = talib_random_compat(device, rand_out); +#endif + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + + +/** \brief Executes Random command, which generates a 32 byte random number + * from the device. + * + * \param[out] rand_out 32 bytes of random data is returned here. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_random(uint8_t* rand_out) +{ + return atcab_random_ext(_gDevice, rand_out); +} + +// Read command functions + +/** \brief Executes Read command, which reads either 4 or 32 bytes of data from + * a given slot, configuration zone, or the OTP zone. + * + * When reading a slot or OTP, data zone must be locked and the slot + * configuration must not be secret for a slot to be successfully read. + * + * \param[in] zone Zone to be read from device. Options are + * ATCA_ZONE_CONFIG, ATCA_ZONE_OTP, or ATCA_ZONE_DATA. + * \param[in] slot Slot number for data zone and ignored for other zones. + * \param[in] block 32 byte block index within the zone. + * \param[in] offset 4 byte work index within the block. Ignored for 32 byte + * reads. + * \param[out] data Read data is returned here. + * \param[in] len Length of the data to be read. Must be either 4 or 32. + * + * returns ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_read_zone(uint8_t zone, uint16_t slot, uint8_t block, uint8_t offset, uint8_t* data, uint8_t len) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + if (ECC204 == dev_type) + { +#if defined(ATCA_ECC204_SUPPORT) + status = calib_ecc204_read_zone(_gDevice, zone, slot, block, offset, data, len); +#endif + } + else + { + status = calib_read_zone(_gDevice, zone, slot, block, offset, data, len); + } +#endif + } + else if (atcab_is_ta_device(dev_type)) + { + status = ATCA_UNIMPLEMENTED; + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Executes Read command, which reads the configuration zone to see if + * the specified zone is locked. + * + * \param[in] zone The zone to query for locked (use LOCK_ZONE_CONFIG or + * LOCK_ZONE_DATA). + * \param[out] is_locked Lock state returned here. True if locked. + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_is_locked(uint8_t zone, bool* is_locked) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + if (ECC204 == dev_type) + { +#if defined(ATCA_ECC204_SUPPORT) + status = calib_ecc204_is_locked(_gDevice, zone, is_locked); +#endif + } + else + { + status = calib_is_locked(_gDevice, zone, is_locked); + } +#endif + } + else if (atcab_is_ta_device(dev_type)) + { +#if ATCA_TA_SUPPORT + if (LOCK_ZONE_CONFIG == zone) + { + status = talib_is_config_locked(_gDevice, is_locked); + } + else if (LOCK_ZONE_DATA == zone) + { + status = talib_is_setup_locked(_gDevice, is_locked); + } + else + { + status = ATCA_TRACE(ATCA_BAD_PARAM, ""); + } +#endif + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief This function check whether configuration zone is locked or not + * + * \param[out] is_locked Lock state returned here. True if locked. + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_is_config_locked(bool* is_locked) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + if (ECC204 == dev_type) + { +#if defined(ATCA_ECC204_SUPPORT) + status = calib_ecc204_is_locked(_gDevice, ATCA_ZONE_CONFIG, is_locked); +#endif + } + else + { + status = calib_is_locked(_gDevice, LOCK_ZONE_CONFIG, is_locked); + } +#endif + } + else if (atcab_is_ta_device(dev_type)) + { +#if ATCA_TA_SUPPORT + status = talib_is_config_locked(_gDevice, is_locked); +#endif + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief This function check whether data/setup zone is locked or not + * + * \param[out] is_locked Lock state returned here. True if locked. + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_is_data_locked(bool* is_locked) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + if (ECC204 == dev_type) + { +#if defined(ATCA_ECC204_SUPPORT) + status = calib_ecc204_is_locked(_gDevice, ATCA_ZONE_DATA, is_locked); +#endif + } + else + { + status = calib_is_locked(_gDevice, LOCK_ZONE_DATA, is_locked); + } +#endif + } + else if (atcab_is_ta_device(dev_type)) + { +#if ATCA_TA_SUPPORT + status = talib_is_setup_locked(_gDevice, is_locked); +#endif + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief This function check whether slot/handle is locked or not + * + * \param[in] slot Slot to query for locked + * \param[out] is_locked Lock state returned here. True if locked. + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_is_slot_locked(uint16_t slot, bool* is_locked) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + status = calib_is_slot_locked(_gDevice, slot, is_locked); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { +#if ATCA_TA_SUPPORT + status = talib_is_handle_locked(_gDevice, slot, is_locked); +#endif + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + + +/** \brief Check to see if the key is a private key or not + * + * This function will issue the Read command as many times as is required to + * read the requested data. + * + * \param[in] slot Slot number to read from if zone is ATCA_ZONE_DATA(2). + * Ignored for all other zones. + * \param[out] is_private Returned valud if successful. True if key is private. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_is_private_ext(ATCADevice device, uint16_t slot, bool* is_private) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type_ext(device); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + status = calib_is_private(_gDevice, slot, is_private); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { +#if ATCA_TA_SUPPORT + status = talib_is_private(_gDevice, slot, is_private); +#endif + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +ATCA_STATUS atcab_is_private(uint16_t slot, bool* is_private) +{ + return atcab_is_private_ext(_gDevice, slot, is_private); +} + +/** \brief Used to read an arbitrary number of bytes from any zone configured + * for clear reads. + * + * This function will issue the Read command as many times as is required to + * read the requested data. + * + * \param[in] zone Zone to read data from. Option are ATCA_ZONE_CONFIG(0), + * ATCA_ZONE_OTP(1), or ATCA_ZONE_DATA(2). + * \param[in] slot Slot number to read from if zone is ATCA_ZONE_DATA(2). + * Ignored for all other zones. + * \param[in] offset Byte offset within the zone to read from. + * \param[out] data Read data is returned here. + * \param[in] length Number of bytes to read starting from the offset. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_read_bytes_zone(uint8_t zone, uint16_t slot, size_t offset, uint8_t* data, size_t length) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + if (ECC204 == dev_type) + { +#if defined(ATCA_ECC204_SUPPORT) + status = calib_ecc204_read_bytes_zone(_gDevice, zone, slot, offset, data, length); +#endif + } + else + { + status = calib_read_bytes_zone(_gDevice, zone, slot, offset, data, length); + } +#endif + } + else if (atcab_is_ta_device(dev_type)) + { +#if ATCA_TA_SUPPORT + status = talib_read_bytes_zone(_gDevice, zone, slot, offset, data, length); +#endif + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief This function returns serial number of the device. + * + * \param[out] serial_number 9 byte serial number is returned here. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_read_serial_number(uint8_t* serial_number) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + if (ECC204 == dev_type) + { +#if defined(ATCA_ECC204_SUPPORT) + status = calib_ecc204_read_serial_number(_gDevice, serial_number); +#endif + } + else + { + status = calib_read_serial_number(_gDevice, serial_number); + } +#endif + } + else if (atcab_is_ta_device(dev_type)) + { +#if ATCA_TA_SUPPORT + status = talib_info_serial_number_compat(_gDevice, serial_number); +#endif + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Executes Read command to read an ECC P256 public key from a slot + * configured for clear reads. + * + * This function assumes the public key is stored using the ECC public key + * format specified in the datasheet. + * + * \param[in] device Device context pointer + * \param[in] slot Slot number to read from. Only slots 8 to 15 are + * large enough for a public key. + * \param[out] public_key Public key is returned here (64 bytes). Format will + * be the 32 byte X and Y big-endian integers + * concatenated. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_read_pubkey_ext(ATCADevice device, uint16_t slot, uint8_t* public_key) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type_ext(device); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + status = calib_read_pubkey(_gDevice, slot, public_key); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { +#if ATCA_TA_SUPPORT + status = talib_read_pubkey_compat(_gDevice, slot, public_key); +#endif + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Executes Read command to read an ECC P256 public key from a slot + * configured for clear reads. + * + * This function assumes the public key is stored using the ECC public key + * format specified in the datasheet. + * + * \param[in] slot Slot number to read from. Only slots 8 to 15 are + * large enough for a public key. + * \param[out] public_key Public key is returned here (64 bytes). Format will + * be the 32 byte X and Y big-endian integers + * concatenated. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_read_pubkey(uint16_t slot, uint8_t* public_key) +{ + return atcab_read_pubkey_ext(_gDevice, slot, public_key); +} + +/** \brief Executes Read command to read a 64 byte ECDSA P256 signature from a + * slot configured for clear reads. + * + * \param[in] slot Slot number to read from. Only slots 8 to 15 are large + * enough for a signature. + * \param[out] sig Signature will be returned here (64 bytes). Format will be + * the 32 byte R and S big-endian integers concatenated. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_read_sig(uint16_t slot, uint8_t* sig) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + status = calib_read_sig(_gDevice, slot, sig); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { +#if ATCA_TA_SUPPORT + status = talib_read_sig_compat(_gDevice, slot, sig); +#endif + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Executes Read command to read the complete device configuration + * zone. + * + * \param[out] config_data Configuration zone data is returned here. 88 bytes + * for ATSHA devices, 128 bytes for ATECC devices and + * 48 bytes for Trust Anchor devices. + * + * \returns ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_read_config_zone(uint8_t* config_data) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + if (ECC204 == dev_type) + { +#if defined(ATCA_ECC204_SUPPORT) + status = calib_ecc204_read_config_zone(_gDevice, config_data); +#endif + } + else + { + status = calib_read_config_zone(_gDevice, config_data); + } +#endif + } + else if (atcab_is_ta_device(dev_type)) + { +#if ATCA_TA_SUPPORT + status = talib_read_config_zone(_gDevice, config_data); +#endif + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Compares a specified configuration zone with the configuration zone + * currently on the device. + * + * This only compares the static portions of the configuration zone and skips + * those that are unique per device (first 16 bytes) and areas that can change + * after the configuration zone has been locked (e.g. LastKeyUse). + * + * \param[in] config_data Full configuration data to compare the device + * against. + * \param[out] same_config Result is returned here. True if the static portions + * on the configuration zones are the same. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_cmp_config_zone(uint8_t* config_data, bool* same_config) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + if (ECC204 == dev_type) + { +#if defined(ATCA_ECC204_SUPPORT) + status = calib_ecc204_cmp_config_zone(_gDevice, config_data, same_config); +#endif + } + else + { + status = calib_cmp_config_zone(_gDevice, config_data, same_config); + } +#endif + } + else if (atcab_is_ta_device(dev_type)) + { +#if ATCA_TA_SUPPORT + status = talib_cmp_config_zone(_gDevice, config_data, same_config); +#endif + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Executes Read command on a slot configured for encrypted reads and + * decrypts the data to return it as plaintext. + * + * Data zone must be locked for this command to succeed. Can only read 32 byte + * blocks. + * + * \param[in] key_id The slot ID to read from. + * \param[in] block Index of the 32 byte block within the slot to read. + * \param[out] data Decrypted (plaintext) data from the read is returned + * here (32 bytes). + * \param[in] enc_key 32 byte ReadKey for the slot being read. + * \param[in] enc_key_id KeyID of the ReadKey being used. + * \param[in] num_in 20 byte host nonce to inject into Nonce calculation + * + * returns ATCA_SUCCESS on success, otherwise an error code. + */ +#if defined(ATCA_USE_CONSTANT_HOST_NONCE) +ATCA_STATUS atcab_read_enc(uint16_t key_id, uint8_t block, uint8_t* data, const uint8_t* enc_key, const uint16_t enc_key_id) +#else +ATCA_STATUS atcab_read_enc(uint16_t key_id, uint8_t block, uint8_t* data, const uint8_t* enc_key, const uint16_t enc_key_id, const uint8_t num_in[NONCE_NUMIN_SIZE]) +#endif +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT +#if defined(ATCA_USE_CONSTANT_HOST_NONCE) + status = calib_read_enc(_gDevice, key_id, block, data, enc_key, enc_key_id); +#else + status = calib_read_enc(_gDevice, key_id, block, data, enc_key, enc_key_id, num_in); +#endif +#endif + } + else if (atcab_is_ta_device(dev_type)) + { + status = ATCA_UNIMPLEMENTED; + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +// SecureBoot command functions + +/** \brief Executes Secure Boot command, which provides support for secure + * boot of an external MCU or MPU. + * + * \param[in] mode Mode determines what operations the SecureBoot + * command performs. + * \param[in] param2 Not used, must be 0. + * \param[in] digest Digest of the code to be verified (32 bytes). + * \param[in] signature Signature of the code to be verified (64 bytes). Can + * be NULL when using the FullStore mode. + * \param[out] mac Validating MAC will be returned here (32 bytes). Can + * be NULL if not required. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_secureboot(uint8_t mode, uint16_t param2, const uint8_t* digest, const uint8_t* signature, uint8_t* mac) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#ifdef ATCA_ATECC608_SUPPORT + status = calib_secureboot(_gDevice, mode, param2, digest, signature, mac); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { + status = ATCA_UNIMPLEMENTED; + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Executes Secure Boot command with encrypted digest and validated + * MAC response using the IO protection key. + * + * \param[in] mode Mode determines what operations the SecureBoot + * command performs. + * \param[in] digest Digest of the code to be verified (32 bytes). + * This is the plaintext digest (not encrypted). + * \param[in] signature Signature of the code to be verified (64 bytes). Can + * be NULL when using the FullStore mode. + * \param[in] num_in Host nonce (20 bytes). + * \param[in] io_key IO protection key (32 bytes). + * \param[out] is_verified Verify result is returned here. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_secureboot_mac(uint8_t mode, const uint8_t* digest, const uint8_t* signature, const uint8_t* num_in, const uint8_t* io_key, bool* is_verified) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#ifdef ATCA_ATECC608_SUPPORT + status = calib_secureboot_mac(_gDevice, mode, digest, signature, num_in, io_key, is_verified); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { + status = ATCA_UNIMPLEMENTED; + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/* SelfTest Command */ + +/** \brief Executes the SelfTest command, which performs a test of one or more + * of the cryptographic engines within the ATECC608 chip. + * + * \param[in] mode Functions to test. Can be a bit field combining any + * of the following: SELFTEST_MODE_RNG, + * SELFTEST_MODE_ECDSA_VERIFY, SELFTEST_MODE_ECDSA_SIGN, + * SELFTEST_MODE_ECDH, SELFTEST_MODE_AES, + * SELFTEST_MODE_SHA, SELFTEST_MODE_ALL. + * \param[in] param2 Currently unused, should be 0. + * \param[out] result Results are returned here as a bit field. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_selftest(uint8_t mode, uint16_t param2, uint8_t* result) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#ifdef ATCA_ATECC608_SUPPORT + status = calib_selftest(_gDevice, mode, param2, result); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { + status = ATCA_UNIMPLEMENTED; + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/* SHA Command */ + +/** \brief Executes SHA command, which computes a SHA-256 or HMAC/SHA-256 + * digest for general purpose use by the host system. + * + * Only the Start(0) and Compute(1) modes are available for ATSHA devices. + * + * \param[in] mode SHA command mode Start(0), Update/Compute(1), + * End(2), Public(3), HMACstart(4), HMACend(5), + * Read_Context(6), or Write_Context(7). Also + * message digest target location for the + * ATECC608. + * \param[in] length Number of bytes in the message parameter or + * KeySlot for the HMAC key if Mode is + * HMACstart(4) or Public(3). + * \param[in] data_in Message bytes to be hashed or Write_Context if + * restoring a context on the ATECC608. Can be + * NULL if not required by the mode. + * \param[out] data_out Data returned by the command (digest or + * context). + * \param[in,out] data_out_size As input, the size of the data_out buffer. As + * output, the number of bytes returned in + * data_out. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_sha_base(uint8_t mode, uint16_t length, const uint8_t* data_in, uint8_t* data_out, uint16_t* data_out_size) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + status = calib_sha_base(_gDevice, mode, length, data_in, data_out, data_out_size); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { +#if ATCA_TA_SUPPORT + status = talib_sha_base_compat(_gDevice, mode, length, data_in, data_out, data_out_size); +#endif + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Executes SHA command to initialize SHA-256 calculation engine + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_sha_start(void) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + status = calib_sha_start(_gDevice); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { +#if ATCA_TA_SUPPORT + status = talib_sha_start(_gDevice); +#endif + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Executes SHA command to add 64 bytes of message data to the current + * context. + * + * \param[in] message 64 bytes of message data to add to add to operation. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_sha_update(const uint8_t* message) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + status = calib_sha_update(_gDevice, message); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { +#if ATCA_TA_SUPPORT + status = talib_sha_update_compat(_gDevice, message); +#endif + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Executes SHA command to complete SHA-256 or HMAC/SHA-256 operation. + * + * \param[out] digest Digest from SHA-256 or HMAC/SHA-256 will be returned + * here (32 bytes). + * \param[in] length Length of any remaining data to include in hash. Max 64 + * bytes. + * \param[in] message Remaining data to include in hash. NULL if length is 0. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_sha_end(uint8_t* digest, uint16_t length, const uint8_t* message) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + status = calib_sha_end(_gDevice, digest, length, message); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { +#if ATCA_TA_SUPPORT + status = talib_sha_end_compat(_gDevice, digest, length, message); +#endif + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Executes SHA command to read the SHA-256 context back. Only for + * ATECC608 with SHA-256 contexts. HMAC not supported. + * + * \param[out] context Context data is returned here. + * \param[in,out] context_size As input, the size of the context buffer in + * bytes. As output, the size of the returned + * context data. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_sha_read_context(uint8_t* context, uint16_t* context_size) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + status = calib_sha_read_context(_gDevice, context, context_size); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { +#if ATCA_TA_SUPPORT + status = talib_sha_read_context(_gDevice, context, context_size); +#endif + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Executes SHA command to write (restore) a SHA-256 context into the + * the device. Only supported for ATECC608 with SHA-256 contexts. + * + * \param[in] context Context data to be restored. + * \param[in] context_size Size of the context data in bytes. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_sha_write_context(const uint8_t* context, uint16_t context_size) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + status = calib_sha_write_context(_gDevice, context, context_size); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { +#if ATCA_TA_SUPPORT + status = talib_sha_write_context(_gDevice, context, context_size); +#endif + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Use the SHA command to compute a SHA-256 digest. + * + * \param[in] length Size of message parameter in bytes. + * \param[in] message Message data to be hashed. + * \param[out] digest Digest is returned here (32 bytes). + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_sha(uint16_t length, const uint8_t* message, uint8_t* digest) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + status = calib_sha(_gDevice, length, message, digest); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { +#if ATCA_TA_SUPPORT + status = talib_sha(_gDevice, length, message, digest); +#endif + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Use the SHA command to compute a SHA-256 digest. + * + * \param[in] data Message data to be hashed. + * \param[in] data_size Size of data in bytes. + * \param[out] digest Digest is returned here (32 bytes). + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_hw_sha2_256(const uint8_t* data, size_t data_size, uint8_t* digest) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + status = calib_hw_sha2_256(_gDevice, data, data_size, digest); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { +#if ATCA_TA_SUPPORT + status = talib_sha(_gDevice, (uint16_t)data_size, data, digest); +#endif + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Initialize a SHA context for performing a hardware SHA-256 operation + * on a device. Note that only one SHA operation can be run at a time. + * + * \param[in] ctx SHA256 context + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_hw_sha2_256_init(atca_sha256_ctx_t* ctx) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + status = calib_hw_sha2_256_init(_gDevice, ctx); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { + status = ATCA_UNIMPLEMENTED; + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Add message data to a SHA context for performing a hardware SHA-256 + * operation on a device. + * + * \param[in] ctx SHA256 context + * \param[in] data Message data to be added to hash. + * \param[in] data_size Size of data in bytes. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_hw_sha2_256_update(atca_sha256_ctx_t* ctx, const uint8_t* data, size_t data_size) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + status = calib_hw_sha2_256_update(_gDevice, ctx, data, data_size); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { + status = ATCA_UNIMPLEMENTED; + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Finish SHA-256 digest for a SHA context for performing a hardware + * SHA-256 operation on a device. + * + * \param[in] ctx SHA256 context + * \param[out] digest SHA256 digest is returned here (32 bytes) + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_hw_sha2_256_finish(atca_sha256_ctx_t* ctx, uint8_t* digest) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + status = calib_hw_sha2_256_finish(_gDevice, ctx, digest); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { + status = ATCA_UNIMPLEMENTED; + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Executes SHA command to start an HMAC/SHA-256 operation + * + * \param[in] ctx HMAC/SHA-256 context + * \param[in] key_slot Slot key id to use for the HMAC calculation + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_sha_hmac_init(atca_hmac_sha256_ctx_t* ctx, uint16_t key_slot) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + status = calib_sha_hmac_init(_gDevice, ctx, key_slot); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { + status = ATCA_UNIMPLEMENTED; + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Executes SHA command to add an arbitrary amount of message data to + * a HMAC/SHA-256 operation. + * + * \param[in] ctx HMAC/SHA-256 context + * \param[in] data Message data to add + * \param[in] data_size Size of message data in bytes + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_sha_hmac_update(atca_hmac_sha256_ctx_t* ctx, const uint8_t* data, size_t data_size) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + status = calib_sha_hmac_update(_gDevice, ctx, data, data_size); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { + status = ATCA_UNIMPLEMENTED; + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Executes SHA command to complete a HMAC/SHA-256 operation. + * + * \param[in] ctx HMAC/SHA-256 context + * \param[out] digest HMAC/SHA-256 result is returned here (32 bytes). + * \param[in] target Where to save the digest internal to the device. + * For ATECC608, can be SHA_MODE_TARGET_TEMPKEY, + * SHA_MODE_TARGET_MSGDIGBUF, or SHA_MODE_TARGET_OUT_ONLY. + * For all other devices, SHA_MODE_TARGET_TEMPKEY is the + * only option. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_sha_hmac_finish(atca_hmac_sha256_ctx_t* ctx, uint8_t* digest, uint8_t target) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + status = calib_sha_hmac_finish(_gDevice, ctx, digest, target); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { + status = ATCA_UNIMPLEMENTED; + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + + + +/** \brief Use the SHA command to compute an HMAC/SHA-256 operation. + * + * \param[in] device Device context pointer + * \param[in] data Message data to be hashed. + * \param[in] data_size Size of data in bytes. + * \param[in] key_slot Slot key id to use for the HMAC calculation + * \param[out] digest Digest is returned here (32 bytes). + * \param[in] target Where to save the digest internal to the device. + * For ATECC608, can be SHA_MODE_TARGET_TEMPKEY, + * SHA_MODE_TARGET_MSGDIGBUF, or + * SHA_MODE_TARGET_OUT_ONLY. For all other devices, + * SHA_MODE_TARGET_TEMPKEY is the only option. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_sha_hmac_ext(ATCADevice device, const uint8_t* data, size_t data_size, uint16_t key_slot, uint8_t* digest, uint8_t target) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type_ext(device); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + status = calib_sha_hmac(device, data, data_size, key_slot, digest, target); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { +#if ATCA_TA_SUPPORT + status = talib_hmac_compat(device, data, data_size, key_slot, digest, target); +#endif + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Use the SHA command to compute an HMAC/SHA-256 operation. + * + * \param[in] data Message data to be hashed. + * \param[in] data_size Size of data in bytes. + * \param[in] key_slot Slot key id to use for the HMAC calculation + * \param[out] digest Digest is returned here (32 bytes). + * \param[in] target Where to save the digest internal to the device. + * For ATECC608, can be SHA_MODE_TARGET_TEMPKEY, + * SHA_MODE_TARGET_MSGDIGBUF, or + * SHA_MODE_TARGET_OUT_ONLY. For all other devices, + * SHA_MODE_TARGET_TEMPKEY is the only option. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_sha_hmac(const uint8_t* data, size_t data_size, uint16_t key_slot, uint8_t* digest, uint8_t target) +{ + return atcab_sha_hmac_ext(_gDevice, data, data_size, key_slot, digest, target); +} + + +/* Sign command */ + +/** \brief Executes the Sign command, which generates a signature using the + * ECDSA algorithm. + * + * \param[in] mode Mode determines what the source of the message to be + * signed. + * \param[in] key_id Private key slot used to sign the message. + * \param[out] signature Signature is returned here. Format is R and S + * integers in big-endian format. 64 bytes for P256 + * curve. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_sign_base(uint8_t mode, uint16_t key_id, uint8_t* signature) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#ifdef ATCA_ECC_SUPPORT + status = calib_sign_base(_gDevice, mode, key_id, signature); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { + status = ATCA_UNIMPLEMENTED; + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Executes Sign command, to sign a 32-byte external message using the + * private key in the specified slot. The message to be signed + * will be loaded into the Message Digest Buffer to the + * ATECC608 device or TempKey for other devices. + * + * \param[in] device Device context pointer + * \param[in] key_id Slot of the private key to be used to sign the + * message. + * \param[in] msg 32-byte message to be signed. Typically the SHA256 + * hash of the full message. + * \param[out] signature Signature will be returned here. Format is R and S + * integers in big-endian format. 64 bytes for P256 + * curve. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_sign_ext(ATCADevice device, uint16_t key_id, const uint8_t* msg, uint8_t* signature) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type_ext(device); + + if (atcab_is_ca_device(dev_type)) + { +#ifdef ATCA_ECC_SUPPORT + if (ECC204 == dev_type) + { +#if defined(ATCA_ECC204_SUPPORT) + status = calib_ecc204_sign(device, key_id, msg, signature); +#endif + } + else + { + status = calib_sign(device, key_id, msg, signature); + } +#endif + } + else if (atcab_is_ta_device(dev_type)) + { +#if ATCA_TA_SUPPORT + status = talib_sign_compat(device, key_id, msg, signature); +#endif + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Executes Sign command, to sign a 32-byte external message using the + * private key in the specified slot. The message to be signed + * will be loaded into the Message Digest Buffer to the + * ATECC608 device or TempKey for other devices. + * + * \param[in] key_id Slot of the private key to be used to sign the + * message. + * \param[in] msg 32-byte message to be signed. Typically the SHA256 + * hash of the full message. + * \param[out] signature Signature will be returned here. Format is R and S + * integers in big-endian format. 64 bytes for P256 + * curve. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_sign(uint16_t key_id, const uint8_t* msg, uint8_t* signature) +{ + return atcab_sign_ext(_gDevice, key_id, msg, signature); +} + +/** \brief Executes Sign command to sign an internally generated message. + * + * \param[in] key_id Slot of the private key to be used to sign the + * message. + * \param[in] is_invalidate Set to true if the signature will be used with + * the Verify(Invalidate) command. false for all + * other cases. + * \param[in] is_full_sn Set to true if the message should incorporate + * the device's full serial number. + * \param[out] signature Signature is returned here. Format is R and S + * integers in big-endian format. 64 bytes for + * P256 curve. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_sign_internal(uint16_t key_id, bool is_invalidate, bool is_full_sn, uint8_t* signature) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#ifdef ATCA_ECC_SUPPORT + status = calib_sign_internal(_gDevice, key_id, is_invalidate, is_full_sn, signature); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { + status = ATCA_UNIMPLEMENTED; + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/* UpdateExtra command */ + +/** \brief Executes UpdateExtra command to update the values of the two + * extra bytes within the Configuration zone (bytes 84 and 85). + * + * Can also be used to decrement the limited use counter associated with the + * key in slot NewValue. + * + * \param[in] mode Mode determines what operations the UpdateExtra + * command performs. + * \param[in] new_value Value to be written. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_updateextra(uint8_t mode, uint16_t new_value) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + status = calib_updateextra(_gDevice, mode, new_value); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { + status = ATCA_UNIMPLEMENTED; + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/* Verify command */ + +/** \brief Executes the Verify command, which takes an ECDSA [R,S] signature + * and verifies that it is correctly generated from a given message and + * public key. In all cases, the signature is an input to the command. + * + * For the Stored, External, and ValidateExternal Modes, the contents of + * TempKey (or Message Digest Buffer in some cases for the ATECC608) should + * contain the 32 byte message. + * + * \param[in] mode Verify command mode and options + * \param[in] key_id Stored mode, the slot containing the public key to + * be used for the verification. + * ValidateExternal mode, the slot containing the + * public key to be validated. + * External mode, KeyID contains the curve type to be + * used to Verify the signature. + * Validate or Invalidate mode, the slot containing + * the public key to be (in)validated. + * \param[in] signature Signature to be verified. R and S integers in + * big-endian format. 64 bytes for P256 curve. + * \param[in] public_key If mode is External, the public key to be used for + * verification. X and Y integers in big-endian format. + * 64 bytes for P256 curve. NULL for all other modes. + * \param[in] other_data If mode is Validate, the bytes used to generate the + * message for the validation (19 bytes). NULL for all + * other modes. + * \param[out] mac If mode indicates a validating MAC, then the MAC will + * will be returned here. Can be NULL otherwise. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_verify(uint8_t mode, uint16_t key_id, const uint8_t* signature, const uint8_t* public_key, const uint8_t* other_data, uint8_t* mac) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#ifdef ATCA_ECC_SUPPORT + status = calib_verify(_gDevice, mode, key_id, signature, public_key, other_data, mac); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { + status = ATCA_UNIMPLEMENTED; + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Executes the Verify command, which verifies a signature (ECDSA + * verify operation) with all components (message, signature, and + * public key) supplied. The message to be signed will be loaded into + * the Message Digest Buffer to the ATECC608 device or TempKey for + * other devices. + * + * \param[in] device Device context pointer + * \param[in] message 32 byte message to be verified. Typically + * the SHA256 hash of the full message. + * \param[in] signature Signature to be verified. R and S integers in + * big-endian format. 64 bytes for P256 curve. + * \param[in] public_key The public key to be used for verification. X and + * Y integers in big-endian format. 64 bytes for + * P256 curve. + * \param[out] is_verified Boolean whether or not the message, signature, + * public key verified. + * + * \return ATCA_SUCCESS on verification success or failure, because the + * command still completed successfully. + */ +ATCA_STATUS atcab_verify_extern_ext(ATCADevice device, const uint8_t* message, const uint8_t* signature, const uint8_t* public_key, bool* is_verified) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type_ext(device); + + if (atcab_is_ca_device(dev_type)) + { +#ifdef ATCA_ECC_SUPPORT + status = calib_verify_extern(device, message, signature, public_key, is_verified); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { +#if ATCA_TA_SUPPORT + status = talib_verify_extern_compat(device, message, signature, public_key, is_verified); +#endif + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Executes the Verify command, which verifies a signature (ECDSA + * verify operation) with all components (message, signature, and + * public key) supplied. The message to be signed will be loaded into + * the Message Digest Buffer to the ATECC608 device or TempKey for + * other devices. + * + * \param[in] message 32 byte message to be verified. Typically + * the SHA256 hash of the full message. + * \param[in] signature Signature to be verified. R and S integers in + * big-endian format. 64 bytes for P256 curve. + * \param[in] public_key The public key to be used for verification. X and + * Y integers in big-endian format. 64 bytes for + * P256 curve. + * \param[out] is_verified Boolean whether or not the message, signature, + * public key verified. + * + * \return ATCA_SUCCESS on verification success or failure, because the + * command still completed successfully. + */ +ATCA_STATUS atcab_verify_extern(const uint8_t* message, const uint8_t* signature, const uint8_t* public_key, bool* is_verified) +{ + return atcab_verify_extern_ext(_gDevice, message, signature, public_key, is_verified); +} + +/** \brief Executes the Verify command with verification MAC, which verifies a + * signature (ECDSA verify operation) with all components (message, + * signature, and public key) supplied. This function is only available + * on the ATECC608. + * + * \param[in] message 32 byte message to be verified. Typically + * the SHA256 hash of the full message. + * \param[in] signature Signature to be verified. R and S integers in + * big-endian format. 64 bytes for P256 curve. + * \param[in] public_key The public key to be used for verification. X and + * Y integers in big-endian format. 64 bytes for + * P256 curve. + * \param[in] num_in System nonce (32 byte) used for the verification + * MAC. + * \param[in] io_key IO protection key for verifying the validation MAC. + * \param[out] is_verified Boolean whether or not the message, signature, + * public key verified. + * + * \return ATCA_SUCCESS on verification success or failure, because the + * command still completed successfully. + */ +ATCA_STATUS atcab_verify_extern_mac(const uint8_t* message, const uint8_t* signature, const uint8_t* public_key, const uint8_t* num_in, const uint8_t* io_key, bool* is_verified) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#ifdef ATCA_ECC_SUPPORT + status = calib_verify_extern_mac(_gDevice, message, signature, public_key, num_in, io_key, is_verified); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { + status = ATCA_UNIMPLEMENTED; + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Executes the Verify command, which verifies a signature (ECDSA + * verify operation) with a public key stored in the device. The + * message to be signed will be loaded into the Message Digest Buffer + * to the ATECC608 device or TempKey for other devices. + * + * \param[in] device Device context pointer + * \param[in] message 32 byte message to be verified. Typically + * the SHA256 hash of the full message. + * \param[in] signature Signature to be verified. R and S integers in + * big-endian format. 64 bytes for P256 curve. + * \param[in] key_id Slot containing the public key to be used in the + * verification. + * \param[out] is_verified Boolean whether or not the message, signature, + * public key verified. + * + * \return ATCA_SUCCESS on verification success or failure, because the + * command still completed successfully. + */ +ATCA_STATUS atcab_verify_stored_ext(ATCADevice device, const uint8_t* message, const uint8_t* signature, uint16_t key_id, bool* is_verified) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type_ext(device); + + if (atcab_is_ca_device(dev_type)) + { +#ifdef ATCA_ECC_SUPPORT + status = calib_verify_stored(device, message, signature, key_id, is_verified); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { +#if ATCA_TA_SUPPORT + status = talib_verify_stored_compat(device, message, signature, key_id, is_verified); +#endif + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Executes the Verify command, which verifies a signature (ECDSA + * verify operation) with a public key stored in the device. The + * message to be signed will be loaded into the Message Digest Buffer + * to the ATECC608 device or TempKey for other devices. + * + * \param[in] message 32 byte message to be verified. Typically + * the SHA256 hash of the full message. + * \param[in] signature Signature to be verified. R and S integers in + * big-endian format. 64 bytes for P256 curve. + * \param[in] key_id Slot containing the public key to be used in the + * verification. + * \param[out] is_verified Boolean whether or not the message, signature, + * public key verified. + * + * \return ATCA_SUCCESS on verification success or failure, because the + * command still completed successfully. + */ +ATCA_STATUS atcab_verify_stored(const uint8_t* message, const uint8_t* signature, uint16_t key_id, bool* is_verified) +{ + return atcab_verify_stored_ext(_gDevice, message, signature, key_id, is_verified); +} + +/** \brief Executes the Verify command with verification MAC, which verifies a + * signature (ECDSA verify operation) with a public key stored in the + * device. This function is only available on the ATECC608. + * + * \param[in] message 32 byte message to be verified. Typically + * the SHA256 hash of the full message. + * \param[in] signature Signature to be verified. R and S integers in + * big-endian format. 64 bytes for P256 curve. + * \param[in] key_id Slot containing the public key to be used in the + * verification. + * \param[in] num_in System nonce (32 byte) used for the verification + * MAC. + * \param[in] io_key IO protection key for verifying the validation MAC. + * \param[out] is_verified Boolean whether or not the message, signature, + * public key verified. + * + * \return ATCA_SUCCESS on verification success or failure, because the + * command still completed successfully. + */ +ATCA_STATUS atcab_verify_stored_mac(const uint8_t* message, const uint8_t* signature, uint16_t key_id, const uint8_t* num_in, const uint8_t* io_key, bool* is_verified) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#ifdef ATCA_ECC_SUPPORT + status = calib_verify_stored_mac(_gDevice, message, signature, key_id, num_in, io_key, is_verified); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { + status = ATCA_UNIMPLEMENTED; + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Executes the Verify command in Validate mode to validate a public + * key stored in a slot. + * + * This command can only be run after GenKey has been used to create a PubKey + * digest of the public key to be validated in TempKey (mode=0x10). + * + * \param[in] key_id Slot containing the public key to be validated. + * \param[in] signature Signature to be verified. R and S integers in + * big-endian format. 64 bytes for P256 curve. + * \param[in] other_data 19 bytes of data used to build the verification + * message. + * \param[out] is_verified Boolean whether or not the message, signature, + * validation public key verified. + * + * \return ATCA_SUCCESS on verification success or failure, because the + * command still completed successfully. + */ +ATCA_STATUS atcab_verify_validate(uint16_t key_id, const uint8_t* signature, const uint8_t* other_data, bool* is_verified) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#ifdef ATCA_ECC_SUPPORT + status = calib_verify_validate(_gDevice, key_id, signature, other_data, is_verified); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { + status = ATCA_UNIMPLEMENTED; + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Executes the Verify command in Invalidate mode which invalidates a + * previously validated public key stored in a slot. + * + * This command can only be run after GenKey has been used to create a PubKey + * digest of the public key to be invalidated in TempKey (mode=0x10). + * + * \param[in] key_id Slot containing the public key to be invalidated. + * \param[in] signature Signature to be verified. R and S integers in + * big-endian format. 64 bytes for P256 curve. + * \param[in] other_data 19 bytes of data used to build the verification + * message. + * \param[out] is_verified Boolean whether or not the message, signature, + * validation public key verified. + * + * \return ATCA_SUCCESS on verification success or failure, because the + * command still completed successfully. + */ +ATCA_STATUS atcab_verify_invalidate(uint16_t key_id, const uint8_t* signature, const uint8_t* other_data, bool* is_verified) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#ifdef ATCA_ECC_SUPPORT + status = calib_verify_invalidate(_gDevice, key_id, signature, other_data, is_verified); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { + status = ATCA_UNIMPLEMENTED; + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/* Write command functions */ + +/** + * \brief Executes the Write command, which writes either one four byte word or + * a 32-byte block to one of the EEPROM zones on the device. Depending + * upon the value of the WriteConfig byte for this slot, the data may be + * required to be encrypted by the system prior to being sent to the + * device. This command cannot be used to write slots configured as ECC + * private keys. + * + * \param[in] zone Zone/Param1 for the write command. + * \param[in] address Address/Param2 for the write command. + * \param[in] value Plain-text data to be written or cipher-text for + * encrypted writes. 32 or 4 bytes depending on bit 7 in the + * zone. + * \param[in] mac MAC required for encrypted writes (32 bytes). Set to NULL + * if not required. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_write(uint8_t zone, uint16_t address, const uint8_t* value, const uint8_t* mac) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + if (ECC204 == dev_type) + { +#if defined(ATCA_ECC204_SUPPORT) + status = calib_ecc204_write(_gDevice, zone, address, value, mac); +#endif + } + else + { + status = calib_write(_gDevice, zone, address, value, mac); + } +#endif + } + else if (atcab_is_ta_device(dev_type)) + { + status = ATCA_UNIMPLEMENTED; + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Executes the Write command, which writes either 4 or 32 bytes of + * data into a device zone. + * + * \param[in] zone Device zone to write to (0=config, 1=OTP, 2=data). + * \param[in] slot If writing to the data zone, it is the slot to write to, + * otherwise it should be 0. + * \param[in] block 32-byte block to write to. + * \param[in] offset 4-byte word within the specified block to write to. If + * performing a 32-byte write, this should be 0. + * \param[in] data Data to be written. + * \param[in] len Number of bytes to be written. Must be either 4 or 32. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_write_zone(uint8_t zone, uint16_t slot, uint8_t block, uint8_t offset, const uint8_t* data, uint8_t len) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + if (ECC204 == dev_type) + { +#if defined(ATCA_ECC204_SUPPORT) + status = calib_ecc204_write_zone(_gDevice, zone, slot, block, offset, data, len); +#endif + } + else + { + status = calib_write_zone(_gDevice, zone, slot, block, offset, data, len); + } +#endif + } + else if (atcab_is_ta_device(dev_type)) + { +#if ATCA_TA_SUPPORT + status = talib_write_zone(_gDevice, zone, slot, block, offset, data, len); +#endif + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Executes the Write command, which writes data into the + * configuration, otp, or data zones with a given byte offset and + * length. Offset and length must be multiples of a word (4 bytes). + * + * Config zone must be unlocked for writes to that zone. If data zone is + * unlocked, only 32-byte writes are allowed to slots and OTP and the offset + * and length must be multiples of 32 or the write will fail. + * + * \param[in] zone Zone to write data to: ATCA_ZONE_CONFIG(0), + * ATCA_ZONE_OTP(1), or ATCA_ZONE_DATA(2). + * \param[in] slot If zone is ATCA_ZONE_DATA(2), the slot number to + * write to. Ignored for all other zones. + * \param[in] offset_bytes Byte offset within the zone to write to. Must be + * a multiple of a word (4 bytes). + * \param[in] data Data to be written. + * \param[in] length Number of bytes to be written. Must be a multiple + * of a word (4 bytes). + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_write_bytes_zone(uint8_t zone, uint16_t slot, size_t offset_bytes, const uint8_t* data, size_t length) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + if (ECC204 == dev_type) + { +#if defined(ATCA_ECC204_SUPPORT) + status = calib_ecc204_write_bytes_zone(_gDevice, zone, slot, offset_bytes, data, length); +#endif + } + else + { + status = calib_write_bytes_zone(_gDevice, zone, slot, offset_bytes, data, length); + } +#endif + } + else if (atcab_is_ta_device(dev_type)) + { +#if ATCA_TA_SUPPORT + status = talib_write_bytes_zone(_gDevice, zone, slot, offset_bytes, data, length); +#endif + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Uses the write command to write a public key to a slot in the + * proper format. + * + * \param[in] slot Slot number to write. Only slots 8 to 15 are large + * enough to store a public key. + * \param[in] public_key Public key to write into the slot specified. X and Y + * integers in big-endian format. 64 bytes for P256 + * curve. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_write_pubkey(uint16_t slot, const uint8_t* public_key) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + status = calib_write_pubkey(_gDevice, slot, public_key); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { +#if ATCA_TA_SUPPORT + status = talib_write_pubkey_compat(_gDevice, slot, public_key); +#endif + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Executes the Write command, which writes the configuration zone. + * + * First 16 bytes are skipped as they are not writable. LockValue and + * LockConfig are also skipped and can only be changed via the Lock + * command. + * + * This command may fail if UserExtra and/or Selector bytes have + * already been set to non-zero values. + * + * \param[in] config_data Data to the config zone data. This should be 88 + * bytes for SHA devices and 128 bytes for ECC + * devices. + * + * \returns ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_write_config_zone(const uint8_t* config_data) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + if (ECC204 == dev_type) + { +#if defined(ATCA_ECC204_SUPPORT) + status = calib_ecc204_write_config_zone(_gDevice, config_data); +#endif + } + else + { + status = calib_write_config_zone(_gDevice, config_data); + } +#endif + } + else if (atcab_is_ta_device(dev_type)) + { +#if ATCA_TA_SUPPORT + status = talib_write_config_zone(_gDevice, config_data); +#endif + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Executes the Write command, which performs an encrypted write of + * a 32 byte block into given slot. + * + * The function takes clear text bytes and encrypts them for writing over the + * wire. Data zone must be locked and the slot configuration must be set to + * encrypted write for the block to be successfully written. + * + * \param[in] key_id Slot ID to write to. + * \param[in] block Index of the 32 byte block to write in the slot. + * \param[in] data 32 bytes of clear text data to be written to the slot + * \param[in] enc_key WriteKey to encrypt with for writing + * \param[in] enc_key_id The KeyID of the WriteKey + * \param[in] num_in 20 byte host nonce to inject into Nonce calculation + * + * returns ATCA_SUCCESS on success, otherwise an error code. + */ +#if defined(ATCA_USE_CONSTANT_HOST_NONCE) +ATCA_STATUS atcab_write_enc(uint16_t key_id, uint8_t block, const uint8_t* data, const uint8_t* enc_key, const uint16_t enc_key_id) +#else +ATCA_STATUS atcab_write_enc(uint16_t key_id, uint8_t block, const uint8_t* data, const uint8_t* enc_key, const uint16_t enc_key_id, const uint8_t num_in[NONCE_NUMIN_SIZE]) +#endif +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT +#if defined(ATCA_USE_CONSTANT_HOST_NONCE) + status = calib_write_enc(_gDevice, key_id, block, data, enc_key, enc_key_id); +#else + status = calib_write_enc(_gDevice, key_id, block, data, enc_key, enc_key_id, num_in); +#endif +#endif + } + else if (atcab_is_ta_device(dev_type)) + { + status = ATCA_UNIMPLEMENTED; + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +/** \brief Initialize one of the monotonic counters in device with a specific + * value. + * + * The monotonic counters are stored in the configuration zone using a special + * format. This encodes a binary count value into the 8 byte encoded value + * required. Can only be set while the configuration zone is unlocked. + * + * \param[in] counter_id Counter to be written. + * \param[in] counter_value Counter value to set. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_write_config_counter(uint16_t counter_id, uint32_t counter_value) +{ + ATCA_STATUS status = ATCA_UNIMPLEMENTED; + ATCADeviceType dev_type = atcab_get_device_type(); + + if (atcab_is_ca_device(dev_type)) + { +#if ATCA_CA_SUPPORT + status = calib_write_config_counter(_gDevice, counter_id, counter_value); +#endif + } + else if (atcab_is_ta_device(dev_type)) + { + status = ATCA_UNIMPLEMENTED; + } + else + { + status = ATCA_NOT_INITIALIZED; + } + return status; +} + +#endif diff --git a/drivers/ecc108a/cryptoauthlib/atca_basic.h b/drivers/ecc108a/cryptoauthlib/atca_basic.h new file mode 100644 index 0000000..ae12eb4 --- /dev/null +++ b/drivers/ecc108a/cryptoauthlib/atca_basic.h @@ -0,0 +1,647 @@ +/** + * \file + * \brief CryptoAuthLib Basic API methods - a simple crypto authentication API. + * These methods manage a global ATCADevice object behind the scenes. They also + * manage the wake/idle state transitions so callers don't need to. + * + * \copyright (c) 2015-2020 Microchip Technology Inc. and its subsidiaries. + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip software + * and any derivatives exclusively with Microchip products. It is your + * responsibility to comply with third party license terms applicable to your + * use of third party software (including open source software) that may + * accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER + * EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, + * SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE + * OF ANY KIND WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF + * MICROCHIP HAS BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE + * FORESEEABLE. TO THE FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL + * LIABILITY ON ALL CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED + * THE AMOUNT OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR + * THIS SOFTWARE. + */ + +#ifndef ATCA_BASIC_H_ +#define ATCA_BASIC_H_ +/*lint +flb */ + +#include "cryptoauthlib.h" +#ifndef ATCA_TIDAL +#include "crypto/atca_crypto_sw_sha2.h" +#endif // ifndef ATCA_TIDAL + +/** \defgroup atcab_ Basic Crypto API methods (atcab_) + * + * \brief + * These methods provide the most convenient, simple API to CryptoAuth chips + * + @{ */ + +#ifdef __cplusplus +extern "C" { +#endif + +ATCA_DLL ATCADevice _gDevice; + +// Basic global methods +ATCA_STATUS atcab_version(char *ver_str); +ATCA_STATUS atcab_init_ext(ATCADevice* device, ATCAIfaceCfg* cfg); +ATCA_STATUS atcab_init(ATCAIfaceCfg *cfg); +ATCA_STATUS atcab_init_device(ATCADevice ca_device); +ATCA_STATUS atcab_release_ext(ATCADevice* device); +ATCA_STATUS atcab_release(void); +ATCADevice atcab_get_device(void); +ATCADeviceType atcab_get_device_type_ext(ATCADevice device); +ATCADeviceType atcab_get_device_type(void); +uint8_t atcab_get_device_address(ATCADevice device); + +bool atcab_is_ca_device(ATCADeviceType dev_type); +bool atcab_is_ta_device(ATCADeviceType dev_type); + +#define atcab_get_addr(...) calib_get_addr(__VA_ARGS__) +#define atca_execute_command(...) calib_execute_command(__VA_ARGS__) + + +#ifndef ATCA_TIDAL +// AES Mode functions +#include "crypto/atca_crypto_hw_aes.h" +ATCA_STATUS atcab_aes_cbc_init_ext(ATCADevice device, atca_aes_cbc_ctx_t* ctx, uint16_t key_id, uint8_t key_block, const uint8_t* iv); +ATCA_STATUS atcab_aes_cbc_init(atca_aes_cbc_ctx_t* ctx, uint16_t key_id, uint8_t key_block, const uint8_t* iv); +ATCA_STATUS atcab_aes_cbc_encrypt_block(atca_aes_cbc_ctx_t* ctx, const uint8_t* plaintext, uint8_t* ciphertext); +ATCA_STATUS atcab_aes_cbc_decrypt_block(atca_aes_cbc_ctx_t* ctx, const uint8_t* ciphertext, uint8_t* plaintext); + +ATCA_STATUS atcab_aes_cbcmac_init_ext(ATCADevice device, atca_aes_cbcmac_ctx_t* ctx, uint16_t key_id, uint8_t key_block); +ATCA_STATUS atcab_aes_cbcmac_init(atca_aes_cbcmac_ctx_t* ctx, uint16_t key_id, uint8_t key_block); +ATCA_STATUS atcab_aes_cbcmac_update(atca_aes_cbcmac_ctx_t* ctx, const uint8_t* data, uint32_t data_size); +ATCA_STATUS atcab_aes_cbcmac_finish(atca_aes_cbcmac_ctx_t* ctx, uint8_t* mac, uint32_t mac_size); + +ATCA_STATUS atcab_aes_cmac_init_ext(ATCADevice device, atca_aes_cmac_ctx_t* ctx, uint16_t key_id, uint8_t key_block); +ATCA_STATUS atcab_aes_cmac_init(atca_aes_cmac_ctx_t* ctx, uint16_t key_id, uint8_t key_block); +ATCA_STATUS atcab_aes_cmac_update(atca_aes_cmac_ctx_t* ctx, const uint8_t* data, uint32_t data_size); +ATCA_STATUS atcab_aes_cmac_finish(atca_aes_cmac_ctx_t* ctx, uint8_t* cmac, uint32_t cmac_size); + +ATCA_STATUS atcab_aes_ctr_init_ext(ATCADevice device, atca_aes_ctr_ctx_t* ctx, uint16_t key_id, uint8_t key_block, uint8_t counter_size, const uint8_t* iv); +ATCA_STATUS atcab_aes_ctr_init(atca_aes_ctr_ctx_t* ctx, uint16_t key_id, uint8_t key_block, uint8_t counter_size, const uint8_t* iv); +ATCA_STATUS atcab_aes_ctr_init_rand_ext(ATCADevice device, atca_aes_ctr_ctx_t* ctx, uint16_t key_id, uint8_t key_block, uint8_t counter_size, uint8_t* iv); +ATCA_STATUS atcab_aes_ctr_init_rand(atca_aes_ctr_ctx_t* ctx, uint16_t key_id, uint8_t key_block, uint8_t counter_size, uint8_t* iv); +ATCA_STATUS atcab_aes_ctr_block(atca_aes_ctr_ctx_t* ctx, const uint8_t* input, uint8_t* output); +ATCA_STATUS atcab_aes_ctr_encrypt_block(atca_aes_ctr_ctx_t* ctx, const uint8_t* plaintext, uint8_t* ciphertext); +ATCA_STATUS atcab_aes_ctr_decrypt_block(atca_aes_ctr_ctx_t* ctx, const uint8_t* ciphertext, uint8_t* plaintext); +ATCA_STATUS atcab_aes_ctr_increment(atca_aes_ctr_ctx_t* ctx); + +ATCA_STATUS atcab_aes_ccm_init_ext(ATCADevice device, atca_aes_ccm_ctx_t* ctx, uint16_t key_id, uint8_t key_block, uint8_t* iv, size_t iv_size, size_t aad_size, size_t text_size, size_t tag_size); +ATCA_STATUS atcab_aes_ccm_init(atca_aes_ccm_ctx_t* ctx, uint16_t key_id, uint8_t key_block, uint8_t* iv, size_t iv_size, size_t aad_size, size_t text_size, size_t tag_size); +ATCA_STATUS atcab_aes_ccm_init_rand_ext(ATCADevice device, atca_aes_ccm_ctx_t* ctx, uint16_t key_id, uint8_t key_block, uint8_t* iv, size_t iv_size, size_t aad_size, size_t text_size, size_t tag_size); +ATCA_STATUS atcab_aes_ccm_init_rand(atca_aes_ccm_ctx_t* ctx, uint16_t key_id, uint8_t key_block, uint8_t* iv, size_t iv_size, size_t aad_size, size_t text_size, size_t tag_size); +ATCA_STATUS atcab_aes_ccm_aad_update(atca_aes_ccm_ctx_t* ctx, const uint8_t* aad, size_t aad_size); +ATCA_STATUS atcab_aes_ccm_aad_finish(atca_aes_ccm_ctx_t* ctx); +ATCA_STATUS atcab_aes_ccm_encrypt_update(atca_aes_ccm_ctx_t* ctx, const uint8_t* plaintext, uint32_t plaintext_size, uint8_t* ciphertext); +ATCA_STATUS atcab_aes_ccm_decrypt_update(atca_aes_ccm_ctx_t* ctx, const uint8_t* ciphertext, uint32_t ciphertext_size, uint8_t* plaintext); +ATCA_STATUS atcab_aes_ccm_encrypt_finish(atca_aes_ccm_ctx_t* ctx, uint8_t* tag, uint8_t* tag_size); +ATCA_STATUS atcab_aes_ccm_decrypt_finish(atca_aes_ccm_ctx_t* ctx, const uint8_t* tag, bool* is_verified); + +// Hardware Accelerated algorithms +ATCA_STATUS atcab_pbkdf2_sha256_ext(ATCADevice device, const uint32_t iter, const uint16_t slot, const uint8_t* salt, const size_t salt_len, uint8_t* result, size_t result_len); +ATCA_STATUS atcab_pbkdf2_sha256(const uint32_t iter, const uint16_t slot, const uint8_t* salt, const size_t salt_len, uint8_t* result, size_t result_len); +#endif // ifndef ATCA_TIDAL + + +#if ATCA_CA_SUPPORT && !ATCA_TA_SUPPORT && !defined(ATCA_USE_ATCAB_FUNCTIONS) && !defined(ATCA_ECC204_SUPPORT) + +#define atcab_wakeup() calib_wakeup(_gDevice) +#define atcab_idle() calib_idle(_gDevice) +#define atcab_sleep() calib_sleep(_gDevice) +#define _atcab_exit(...) _calib_exit(_gDevice, __VA_ARGS__) +#define atcab_get_zone_size(...) calib_get_zone_size(_gDevice, __VA_ARGS__) + + +// AES command functions +#define atcab_aes(...) calib_aes(_gDevice, __VA_ARGS__) +#define atcab_aes_encrypt(...) calib_aes_encrypt(_gDevice, __VA_ARGS__) +#define atcab_aes_encrypt_ext calib_aes_encrypt +#define atcab_aes_decrypt(...) calib_aes_decrypt(_gDevice, __VA_ARGS__) +#define atcab_aes_decrypt_ext calib_aes_decrypt +#define atcab_aes_gfm(...) calib_aes_gfm(_gDevice, __VA_ARGS__) + +#define atcab_aes_gcm_init(...) calib_aes_gcm_init(_gDevice, __VA_ARGS__) +#define atcab_aes_gcm_init_rand(...) calib_aes_gcm_init_rand(_gDevice, __VA_ARGS__) +#define atcab_aes_gcm_aad_update(...) calib_aes_gcm_aad_update(_gDevice, __VA_ARGS__) +#define atcab_aes_gcm_encrypt_update(...) calib_aes_gcm_encrypt_update(_gDevice, __VA_ARGS__) +#define atcab_aes_gcm_encrypt_finish(...) calib_aes_gcm_encrypt_finish(_gDevice, __VA_ARGS__) +#define atcab_aes_gcm_decrypt_update(...) calib_aes_gcm_decrypt_update(_gDevice, __VA_ARGS__) +#define atcab_aes_gcm_decrypt_finish(...) calib_aes_gcm_decrypt_finish(_gDevice, __VA_ARGS__) + +// CheckMAC command functions +#define atcab_checkmac(...) calib_checkmac(_gDevice, __VA_ARGS__) + +// Counter command functions +#define atcab_counter(...) calib_counter(_gDevice, __VA_ARGS__) +#define atcab_counter_increment(...) calib_counter_increment(_gDevice, __VA_ARGS__) +#define atcab_counter_read(...) calib_counter_read(_gDevice, __VA_ARGS__) + +// DeriveKey command functions +#define atcab_derivekey(...) calib_derivekey(_gDevice, __VA_ARGS__) + +// ECDH command functions +#define atcab_ecdh_base(...) calib_ecdh_base(_gDevice, __VA_ARGS__) +#define atcab_ecdh(...) calib_ecdh(_gDevice, __VA_ARGS__) +#define atcab_ecdh_enc(...) calib_ecdh_enc(_gDevice, __VA_ARGS__) +#define atcab_ecdh_ioenc(...) calib_ecdh_ioenc(_gDevice, __VA_ARGS__) +#define atcab_ecdh_tempkey(...) calib_ecdh_tempkey(_gDevice, __VA_ARGS__) +#define atcab_ecdh_tempkey_ioenc(...) calib_ecdh_tempkey_ioenc(_gDevice, __VA_ARGS__) + +// GenDig command functions +#define atcab_gendig(...) calib_gendig(_gDevice, __VA_ARGS__) + +// GenKey command functions +#define atcab_genkey_base(...) calib_genkey_base(_gDevice, __VA_ARGS__) +#define atcab_genkey(...) calib_genkey(_gDevice, __VA_ARGS__) +#define atcab_get_pubkey(...) calib_get_pubkey(_gDevice, __VA_ARGS__) +#define atcab_get_pubkey_ext calib_get_pubkey + +// HMAC command functions +#define atcab_hmac(...) calib_hmac(_gDevice, __VA_ARGS__) + +// Info command functions +#define atcab_info_base(...) calib_info_base(_gDevice, __VA_ARGS__) +#define atcab_info(...) calib_info(_gDevice, __VA_ARGS__) +#define atcab_info_get_latch(...) calib_info_get_latch(_gDevice, __VA_ARGS__) +#define atcab_info_set_latch(...) calib_info_set_latch(_gDevice, __VA_ARGS__) + +// KDF command functions +#define atcab_kdf(...) calib_kdf(_gDevice, __VA_ARGS__) + +// Lock command functions +#define atcab_lock(...) calib_lock(_gDevice, __VA_ARGS__) +#define atcab_lock_config_zone() calib_lock_config_zone(_gDevice) +#define atcab_lock_config_zone_crc(...) calib_lock_config_zone_crc(_gDevice, __VA_ARGS__) +#define atcab_lock_data_zone() calib_lock_data_zone(_gDevice) +#define atcab_lock_data_zone_crc(...) calib_lock_data_zone_crc(_gDevice, __VA_ARGS__) +#define atcab_lock_data_slot(...) calib_lock_data_slot(_gDevice, __VA_ARGS__) + +// MAC command functions +#define atcab_mac(...) calib_mac(_gDevice, __VA_ARGS__) + +// Nonce command functions +#define atcab_nonce_base(...) calib_nonce_base(_gDevice, __VA_ARGS__) +#define atcab_nonce(...) calib_nonce(_gDevice, __VA_ARGS__) +#define atcab_nonce_load(...) calib_nonce_load(_gDevice, __VA_ARGS__) +#define atcab_nonce_rand(...) calib_nonce_rand(_gDevice, __VA_ARGS__) +#define atcab_challenge(...) calib_challenge(_gDevice, __VA_ARGS__) +#define atcab_challenge_seed_update(...) calib_challenge_seed_update(_gDevice, __VA_ARGS__) + +// PrivWrite command functions +#define atcab_priv_write(...) calib_priv_write(_gDevice, __VA_ARGS__) + + +// Random command functions +#define atcab_random(...) calib_random(_gDevice, __VA_ARGS__) +#define atcab_random_ext calib_random + +// Read command functions +#define atcab_read_zone(...) calib_read_zone(_gDevice, __VA_ARGS__) +#define atcab_is_locked(...) calib_is_locked(_gDevice, __VA_ARGS__) +#define atcab_is_config_locked(...) calib_is_locked(_gDevice, LOCK_ZONE_CONFIG, __VA_ARGS__) +#define atcab_is_data_locked(...) calib_is_locked(_gDevice, LOCK_ZONE_DATA, __VA_ARGS__) +#define atcab_is_slot_locked(...) calib_is_slot_locked(_gDevice, __VA_ARGS__) +#define atcab_is_private(...) calib_is_private(_gDevice, __VA_ARGS__) +#define atcab_is_private_ext calib_is_private +#define atcab_read_bytes_zone(...) calib_read_bytes_zone(_gDevice, __VA_ARGS__) +#define atcab_read_serial_number(...) calib_read_serial_number(_gDevice, __VA_ARGS__) +#define atcab_read_pubkey(...) calib_read_pubkey(_gDevice, __VA_ARGS__) +#define atcab_read_pubkey_ext calib_read_pubkey +#define atcab_read_sig(...) calib_read_sig(_gDevice, __VA_ARGS__) +#define atcab_read_config_zone(...) calib_read_config_zone(_gDevice, __VA_ARGS__) +#define atcab_cmp_config_zone(...) calib_cmp_config_zone(_gDevice, __VA_ARGS__) +#define atcab_read_enc(...) calib_read_enc(_gDevice, __VA_ARGS__) + + +// SecureBoot command functions +#define atcab_secureboot(...) calib_secureboot(_gDevice, __VA_ARGS__) +#define atcab_secureboot_mac(...) calib_secureboot_mac(_gDevice, __VA_ARGS__) + +// SelfTest command functions +#define atcab_selftest(...) calib_selftest(_gDevice, __VA_ARGS__) + +// SHA command functions +#define atcab_sha_base(...) calib_sha_base(_gDevice, __VA_ARGS__) +#define atcab_sha_start() calib_sha_start(_gDevice) +#define atcab_sha_update(...) calib_sha_update(_gDevice, __VA_ARGS__) +#define atcab_sha_end(...) calib_sha_end(_gDevice, __VA_ARGS__) +#define atcab_sha_read_context(...) calib_sha_read_context(_gDevice, __VA_ARGS__) +#define atcab_sha_write_context(...) calib_sha_write_context(_gDevice, __VA_ARGS__) +#define atcab_sha(...) calib_sha(_gDevice, __VA_ARGS__) +#define atcab_hw_sha2_256(...) calib_hw_sha2_256(_gDevice, __VA_ARGS__) +#define atcab_hw_sha2_256_init(...) calib_hw_sha2_256_init(_gDevice, __VA_ARGS__) +#define atcab_hw_sha2_256_update(...) calib_hw_sha2_256_update(_gDevice, __VA_ARGS__) +#define atcab_hw_sha2_256_finish(...) calib_hw_sha2_256_finish(_gDevice, __VA_ARGS__) +#define atcab_sha_hmac_init(...) calib_sha_hmac_init(_gDevice, __VA_ARGS__) +#define atcab_sha_hmac_update(...) calib_sha_hmac_update(_gDevice, __VA_ARGS__) +#define atcab_sha_hmac_finish(...) calib_sha_hmac_finish(_gDevice, __VA_ARGS__) +#define atcab_sha_hmac(...) calib_sha_hmac(_gDevice, __VA_ARGS__) +#define atcab_sha_hmac_ext calib_sha_hmac +#define SHA_CONTEXT_MAX_SIZE (99) + +// Sign command functions +#define atcab_sign_base(...) calib_sign_base(_gDevice, __VA_ARGS__) +#define atcab_sign(...) calib_sign(_gDevice, __VA_ARGS__) +#define atcab_sign_ext calib_sign +#define atcab_sign_internal(...) calib_sign_internal(_gDevice, __VA_ARGS__) + +// UpdateExtra command functions +#define atcab_updateextra(...) calib_updateextra(_gDevice, __VA_ARGS__) + +// Verify command functions +#define atcab_verify(...) calib_verify(_gDevice, __VA_ARGS__) +#define atcab_verify_extern(...) calib_verify_extern(_gDevice, __VA_ARGS__) +#define atcab_verify_extern_ext calib_verify_extern +#define atcab_verify_extern_mac(...) calib_verify_extern_mac(_gDevice, __VA_ARGS__) +#define atcab_verify_stored(...) calib_verify_stored(_gDevice, __VA_ARGS__) +#define atcab_verify_stored_ext calib_verify_stored +#define atcab_verify_stored_mac(...) calib_verify_stored_mac(_gDevice, __VA_ARGS__) +#define atcab_verify_validate(...) calib_verify_validate(_gDevice, __VA_ARGS__) +#define atcab_verify_invalidate(...) calib_verify_invalidate(_gDevice, __VA_ARGS__) + +// Write command functions +#define atcab_write(...) calib_write(_gDevice, __VA_ARGS__) +#define atcab_write_zone(...) calib_write_zone(_gDevice, __VA_ARGS__) +#define atcab_write_bytes_zone(...) calib_write_bytes_zone(_gDevice, __VA_ARGS__) +#define atcab_write_pubkey(...) calib_write_pubkey(_gDevice, __VA_ARGS__) +#define atcab_write_config_zone(...) calib_write_config_zone(_gDevice, __VA_ARGS__) +#define atcab_write_enc(...) calib_write_enc(_gDevice, __VA_ARGS__) +#define atcab_write_config_counter(...) calib_write_config_counter(_gDevice, __VA_ARGS__) + +#elif ATCA_TA_SUPPORT && !ATCA_CA_SUPPORT && !defined(ATCA_USE_ATCAB_FUNCTIONS) + +#define atcab_wakeup(...) (0) +#define atcab_idle(...) (0) +#define atcab_sleep(...) (0) +#define _atcab_exit(...) (1) +#define atcab_get_zone_size(...) talib_get_zone_size(_gDevice, __VA_ARGS__) +//#define atcab_get_addr(...) (1) + +// AES command functions +#define atcab_aes(...) (1) +#define atcab_aes_encrypt(...) talib_aes_encrypt(_gDevice, __VA_ARGS__) +#define atcab_aes_encrypt_ext talib_aes_encrypt +#define atcab_aes_decrypt(...) talib_aes_decrypt(_gDevice, __VA_ARGS__) +#define atcab_aes_decrypt_ext talib_aes_decrypt +#define atcab_aes_gfm(...) (1) + +#define atcab_aes_gcm_init(...) (1) +#define atcab_aes_gcm_init_rand(...) (1) +#define atcab_aes_gcm_aad_update(...) (1) +#define atcab_aes_gcm_encrypt_update(...) (1) +#define atcab_aes_gcm_encrypt_finish(...) (1) +#define atcab_aes_gcm_decrypt_update(...) (1) +#define atcab_aes_gcm_decrypt_finish(...) (1) + +// CheckMAC command functions +#define atcab_checkmac(...) (1) + +// Counter command functions +#define atcab_counter(...) talib_counter(_gDevice, __VA_ARGS__) +#define atcab_counter_increment(...) talib_counter_increment(_gDevice, __VA_ARGS__) +#define atcab_counter_read(...) talib_counter_read(_gDevice, __VA_ARGS__) + +// DeriveKey command functions +#define atcab_derivekey(...) (1) + +// ECDH command functions +#define atcab_ecdh_base(...) (1) +#define atcab_ecdh(...) talib_ecdh_compat(_gDevice, __VA_ARGS__) +#define atcab_ecdh_enc(...) (ATCA_UNIMPLEMENTED) +#define atcab_ecdh_ioenc(...) (ATCA_UNIMPLEMENTED) +#define atcab_ecdh_tempkey(...) (1) +#define atcab_ecdh_tempkey_ioenc(...) (ATCA_UNIMPLEMENTED) + +// GenDig command functions +#define atcab_gendig(...) (1) + +// GenKey command functions +#define atcab_genkey_base(...) (ATCA_UNIMPLEMENTED) +#define atcab_genkey(...) talib_genkey_compat(_gDevice, __VA_ARGS__) +#define atcab_get_pubkey(...) talib_get_pubkey_compat(_gDevice, __VA_ARGS__) +#define atcab_get_pubkey_ext talib_get_pubkey_compat + +// HMAC command functions +#define atcab_hmac(...) (ATCA_UNIMPLEMENTED) + +// Info command functions +#define atcab_info_base(...) talib_info_base(_gDevice, __VA_ARGS__) +#define atcab_info(...) talib_info_compat(_gDevice, __VA_ARGS__) +#define atcab_info_get_latch(...) (1) +#define atcab_info_set_latch(...) (1) +//#define atcab_info_get_latch(...) talib_info_get_latch(_gDevice, __VA_ARGS__) +//#define atcab_info_set_latch(...) talib_info_set_latch(_gDevice, __VA_ARGS__) + +// KDF command functions +#define atcab_kdf(...) (1) + +// Lock command functions +#define atcab_lock(...) (1) +#define atcab_lock_config_zone() talib_lock_config(_gDevice) +#define atcab_lock_config_zone_crc(...) talib_lock_config_with_crc(_gDevice, __VA_ARGS__) +#define atcab_lock_data_zone() talib_lock_setup(_gDevice) +#define atcab_lock_data_zone_crc(...) (1) +#define atcab_lock_data_slot(...) talib_lock_handle(_gDevice, __VA_ARGS__) + +// MAC command functions +#define atcab_mac(...) (ATCA_UNIMPLEMENTED) + +// Nonce command functions +#define atcab_nonce_base(...) (1) +#define atcab_nonce(...) (1) +#define atcab_nonce_load(...) (1) +#define atcab_nonce_rand(...) (1) +#define atcab_challenge(...) (1) +#define atcab_challenge_seed_update(...) (1) + +// PrivWrite command functionsoc +#define atcab_priv_write(...) (1) + +// Random command functions +#define atcab_random(...) talib_random_compat(_gDevice, __VA_ARGS__) +#define atcab_random_ext talib_random_compat + +// Read command functions +#define atcab_read_zone(...) (ATCA_UNIMPLEMENTED) +#define atcab_is_locked(...) talib_is_locked_compat(_gDevice, __VA_ARGS__) +#define atcab_is_config_locked(...) talib_is_config_locked(_gDevice, __VA_ARGS__) +#define atcab_is_data_locked(...) talib_is_setup_locked(_gDevice, __VA_ARGS__) +#define atcab_is_slot_locked(...) talib_is_handle_locked(_gDevice, __VA_ARGS__) +#define atcab_is_private(...) talib_is_private(_gDevice, __VA_ARGS__) +#define atcab_is_private_ext talib_is_private +#define atcab_read_bytes_zone(...) talib_read_bytes_zone(_gDevice, __VA_ARGS__) +#define atcab_read_serial_number(...) talib_info_serial_number_compat(_gDevice, __VA_ARGS__) +#define atcab_read_pubkey(...) talib_read_pubkey_compat(_gDevice, __VA_ARGS__) +#define atcab_read_pubkey_ext talib_read_pubkey_compat +#define atcab_read_sig(...) talib_read_sig_compat(_gDevice, __VA_ARGS__) +#define atcab_read_config_zone(...) talib_read_config_zone(_gDevice, __VA_ARGS__) +#define atcab_cmp_config_zone(...) talib_cmp_config_zone(_gDevice, __VA_ARGS__) +#define atcab_read_enc(...) (ATCA_UNIMPLEMENTED) + + +// SecureBoot command functions +#define atcab_secureboot(...) (ATCA_UNIMPLEMENTED) +#define atcab_secureboot_mac(...) (ATCA_UNIMPLEMENTED) + +// SelfTest command functions +#define atcab_selftest(...) (1) + +// SHA command functions +#define atcab_sha_base(...) talib_sha_base_compat(_gDevice, __VA_ARGS__) +#define atcab_sha_start() talib_sha_start(_gDevice) +#define atcab_sha_update(...) talib_sha_update_compat(_gDevice, __VA_ARGS__) +#define atcab_sha_end(...) talib_sha_end_compat(_gDevice, __VA_ARGS__) +#define atcab_sha_read_context(...) talib_sha_read_context(_gDevice, __VA_ARGS__) +#define atcab_sha_write_context(...) talib_sha_write_context(_gDevice, __VA_ARGS__) +#define atcab_sha(...) talib_sha(_gDevice, __VA_ARGS__) +#define atcab_hw_sha2_256(...) (1) +#define atcab_hw_sha2_256_init(...) (1) +#define atcab_hw_sha2_256_update(...) (1) +#define atcab_hw_sha2_256_finish(...) (1) +#define atcab_sha_hmac_init(...) (ATCA_UNIMPLEMENTED) +#define atcab_sha_hmac_update(...) (ATCA_UNIMPLEMENTED) +#define atcab_sha_hmac_finish(...) (ATCA_UNIMPLEMENTED) +#define atcab_sha_hmac(...) talib_hmac_compat(_gDevice, __VA_ARGS__) +#define atcab_sha_hmac_ext talib_hmac_compat +#define SHA_CONTEXT_MAX_SIZE (109) + +// Sign command functions +#define atcab_sign_base(...) (1) +#define atcab_sign(...) talib_sign_compat(_gDevice, __VA_ARGS__) +#define atcab_sign_ext talib_sign_compat +#define atcab_sign_internal(...) (1) + +// UpdateExtra command functions +#define atcab_updateextra(...) (1) + +// Verify command functions +#define atcab_verify(...) (1) +#define atcab_verify_extern(...) talib_verify_extern_compat(_gDevice, __VA_ARGS__) +#define atcab_verify_extern_ext talib_verify_extern_compat +#define atcab_verify_extern_mac(...) (ATCA_UNIMPLEMENTED) +#define atcab_verify_stored(...) talib_verify_stored_compat(_gDevice, __VA_ARGS__) +#define atcab_verify_stored_ext talib_verify_stored_compat +#define atcab_verify_stored_mac(...) (ATCA_UNIMPLEMENTED) +#define atcab_verify_validate(...) (ATCA_UNIMPLEMENTED) +#define atcab_verify_invalidate(...) (ATCA_UNIMPLEMENTED) + +// Write command functions +#define atcab_write(...) (ATCA_UNIMPLEMENTED) +#define atcab_write_zone(...) talib_write_zone(_gDevice, __VA_ARGS__) +#define atcab_write_bytes_zone(...) talib_write_bytes_zone(_gDevice, __VA_ARGS__) +#define atcab_write_pubkey(...) talib_write_pubkey_compat(_gDevice, __VA_ARGS__) +#define atcab_write_config_zone(...) talib_write_config_zone(_gDevice, __VA_ARGS__) +#define atcab_write_enc(...) (ATCA_UNIMPLEMENTED) +#define atcab_write_config_counter(...) (ATCA_UNIMPLEMENTED) + +#endif + +#if (ATCA_TA_SUPPORT && ATCA_CA_SUPPORT) || defined(ATCA_USE_ATCAB_FUNCTIONS) || defined(ATCA_ECC204_SUPPORT) + +/* Basic global methods */ +ATCA_STATUS _atcab_exit(void); +ATCA_STATUS atcab_wakeup(void); +ATCA_STATUS atcab_idle(void); +ATCA_STATUS atcab_sleep(void); +//ATCA_STATUS atcab_get_addr(uint8_t zone, uint16_t slot, uint8_t block, uint8_t offset, uint16_t* addr); +ATCA_STATUS atcab_get_zone_size(uint8_t zone, uint16_t slot, size_t* size); + +// AES command functions +ATCA_STATUS atcab_aes(uint8_t mode, uint16_t key_id, const uint8_t* aes_in, uint8_t* aes_out); +ATCA_STATUS atcab_aes_encrypt(uint16_t key_id, uint8_t key_block, const uint8_t* plaintext, uint8_t* ciphertext); +ATCA_STATUS atcab_aes_encrypt_ext(ATCADevice device, uint16_t key_id, uint8_t key_block, const uint8_t* plaintext, uint8_t* ciphertext); +ATCA_STATUS atcab_aes_decrypt(uint16_t key_id, uint8_t key_block, const uint8_t* ciphertext, uint8_t* plaintext); +ATCA_STATUS atcab_aes_decrypt_ext(ATCADevice device, uint16_t key_id, uint8_t key_block, const uint8_t* ciphertext, uint8_t* plaintext); +ATCA_STATUS atcab_aes_gfm(const uint8_t* h, const uint8_t* input, uint8_t* output); + +/* AES GCM */ +ATCA_STATUS atcab_aes_gcm_init(atca_aes_gcm_ctx_t* ctx, uint16_t key_id, uint8_t key_block, const uint8_t* iv, size_t iv_size); +ATCA_STATUS atcab_aes_gcm_init_rand(atca_aes_gcm_ctx_t* ctx, uint16_t key_id, uint8_t key_block, size_t rand_size, + const uint8_t* free_field, size_t free_field_size, uint8_t* iv); +ATCA_STATUS atcab_aes_gcm_aad_update(atca_aes_gcm_ctx_t* ctx, const uint8_t* aad, uint32_t aad_size); +ATCA_STATUS atcab_aes_gcm_encrypt_update(atca_aes_gcm_ctx_t* ctx, const uint8_t* plaintext, uint32_t plaintext_size, uint8_t* ciphertext); +ATCA_STATUS atcab_aes_gcm_encrypt_finish(atca_aes_gcm_ctx_t* ctx, uint8_t* tag, size_t tag_size); +ATCA_STATUS atcab_aes_gcm_decrypt_update(atca_aes_gcm_ctx_t* ctx, const uint8_t* ciphertext, uint32_t ciphertext_size, uint8_t* plaintext); +ATCA_STATUS atcab_aes_gcm_decrypt_finish(atca_aes_gcm_ctx_t* ctx, const uint8_t* tag, size_t tag_size, bool* is_verified); + +/* CheckMAC command */ +ATCA_STATUS atcab_checkmac(uint8_t mode, uint16_t key_id, const uint8_t* challenge, const uint8_t* response, const uint8_t* other_data); + +/* Counter command */ +ATCA_STATUS atcab_counter(uint8_t mode, uint16_t counter_id, uint32_t* counter_value); +ATCA_STATUS atcab_counter_increment(uint16_t counter_id, uint32_t* counter_value); +ATCA_STATUS atcab_counter_read(uint16_t counter_id, uint32_t* counter_value); + +/* DeriveKey command */ +ATCA_STATUS atcab_derivekey(uint8_t mode, uint16_t key_id, const uint8_t* mac); + +/* ECDH command */ +ATCA_STATUS atcab_ecdh_base(uint8_t mode, uint16_t key_id, const uint8_t* public_key, uint8_t* pms, uint8_t* out_nonce); +ATCA_STATUS atcab_ecdh(uint16_t key_id, const uint8_t* public_key, uint8_t* pms); + +#if defined(ATCA_USE_CONSTANT_HOST_NONCE) +ATCA_STATUS atcab_ecdh_enc(uint16_t key_id, const uint8_t* public_key, uint8_t* pms, const uint8_t* read_key, uint16_t read_key_id); +#else +ATCA_STATUS atcab_ecdh_enc(uint16_t key_id, const uint8_t* public_key, uint8_t* pms, const uint8_t* read_key, uint16_t read_key_id, const uint8_t num_in[NONCE_NUMIN_SIZE]); +#endif + +ATCA_STATUS atcab_ecdh_ioenc(uint16_t key_id, const uint8_t* public_key, uint8_t* pms, const uint8_t* io_key); +ATCA_STATUS atcab_ecdh_tempkey(const uint8_t* public_key, uint8_t* pms); +ATCA_STATUS atcab_ecdh_tempkey_ioenc(const uint8_t* public_key, uint8_t* pms, const uint8_t* io_key); + +// GenDig command functions +ATCA_STATUS atcab_gendig(uint8_t zone, uint16_t key_id, const uint8_t* other_data, uint8_t other_data_size); + +// GenKey command functions +ATCA_STATUS atcab_genkey_base(uint8_t mode, uint16_t key_id, const uint8_t* other_data, uint8_t* public_key); +ATCA_STATUS atcab_genkey(uint16_t key_id, uint8_t* public_key); +ATCA_STATUS atcab_get_pubkey(uint16_t key_id, uint8_t* public_key); +ATCA_STATUS atcab_get_pubkey_ext(ATCADevice device, uint16_t key_id, uint8_t* public_key); + +// HMAC command functions +ATCA_STATUS atcab_hmac(uint8_t mode, uint16_t key_id, uint8_t* digest); + +// Info command functions +ATCA_STATUS atcab_info_base(uint8_t mode, uint16_t param2, uint8_t* out_data); +ATCA_STATUS atcab_info(uint8_t* revision); +ATCA_STATUS atcab_info_set_latch(bool state); +ATCA_STATUS atcab_info_get_latch(bool* state); + +// KDF command functions +ATCA_STATUS atcab_kdf(uint8_t mode, uint16_t key_id, const uint32_t details, const uint8_t* message, uint8_t* out_data, uint8_t* out_nonce); + +// Lock command functions +ATCA_STATUS atcab_lock(uint8_t mode, uint16_t summary_crc); +ATCA_STATUS atcab_lock_config_zone(void); +ATCA_STATUS atcab_lock_config_zone_crc(uint16_t summary_crc); +ATCA_STATUS atcab_lock_data_zone(void); +ATCA_STATUS atcab_lock_data_zone_crc(uint16_t summary_crc); +ATCA_STATUS atcab_lock_data_slot(uint16_t slot); + +// MAC command functions +ATCA_STATUS atcab_mac(uint8_t mode, uint16_t key_id, const uint8_t* challenge, uint8_t* digest); + +// Nonce command functions +ATCA_STATUS atcab_nonce_base(uint8_t mode, uint16_t zero, const uint8_t* num_in, uint8_t* rand_out); +ATCA_STATUS atcab_nonce(const uint8_t* num_in); +ATCA_STATUS atcab_nonce_load(uint8_t target, const uint8_t* num_in, uint16_t num_in_size); +ATCA_STATUS atcab_nonce_rand(const uint8_t* num_in, uint8_t* rand_out); +ATCA_STATUS atcab_challenge(const uint8_t* num_in); +ATCA_STATUS atcab_challenge_seed_update(const uint8_t* num_in, uint8_t* rand_out); + +// PrivWrite command functions +#if defined(ATCA_USE_CONSTANT_HOST_NONCE) +ATCA_STATUS atcab_priv_write(uint16_t key_id, const uint8_t priv_key[36], uint16_t write_key_id, const uint8_t write_key[32]); +#else +ATCA_STATUS atcab_priv_write(uint16_t key_id, const uint8_t priv_key[36], uint16_t write_key_id, const uint8_t write_key[32], const uint8_t num_in[NONCE_NUMIN_SIZE]); +#endif + +// Random command functions +ATCA_STATUS atcab_random(uint8_t* rand_out); +ATCA_STATUS atcab_random_ext(ATCADevice device, uint8_t* rand_out); + +// Read command functions +ATCA_STATUS atcab_read_zone(uint8_t zone, uint16_t slot, uint8_t block, uint8_t offset, uint8_t* data, uint8_t len); +ATCA_STATUS atcab_is_locked(uint8_t zone, bool* is_locked); +ATCA_STATUS atcab_is_config_locked(bool* is_locked); +ATCA_STATUS atcab_is_data_locked(bool* is_locked); +ATCA_STATUS atcab_is_slot_locked(uint16_t slot, bool* is_locked); +ATCA_STATUS atcab_is_private_ext(ATCADevice device, uint16_t slot, bool* is_private); +ATCA_STATUS atcab_is_private(uint16_t slot, bool* is_private); +ATCA_STATUS atcab_read_bytes_zone(uint8_t zone, uint16_t slot, size_t offset, uint8_t* data, size_t length); +ATCA_STATUS atcab_read_serial_number(uint8_t* serial_number); +ATCA_STATUS atcab_read_pubkey(uint16_t slot, uint8_t* public_key); +ATCA_STATUS atcab_read_pubkey_ext(ATCADevice device, uint16_t slot, uint8_t* public_key); +ATCA_STATUS atcab_read_sig(uint16_t slot, uint8_t* sig); +ATCA_STATUS atcab_read_config_zone(uint8_t* config_data); +ATCA_STATUS atcab_cmp_config_zone(uint8_t* config_data, bool* same_config); + +#if defined(ATCA_USE_CONSTANT_HOST_NONCE) +ATCA_STATUS atcab_read_enc(uint16_t key_id, uint8_t block, uint8_t* data, const uint8_t* enc_key, const uint16_t enc_key_id); +#else +ATCA_STATUS atcab_read_enc(uint16_t key_id, uint8_t block, uint8_t* data, const uint8_t* enc_key, const uint16_t enc_key_id, const uint8_t num_in[NONCE_NUMIN_SIZE]); +#endif + +// SecureBoot command functions +ATCA_STATUS atcab_secureboot(uint8_t mode, uint16_t param2, const uint8_t* digest, const uint8_t* signature, uint8_t* mac); +ATCA_STATUS atcab_secureboot_mac(uint8_t mode, const uint8_t* digest, const uint8_t* signature, const uint8_t* num_in, const uint8_t* io_key, bool* is_verified); + +/* SelfTest Command */ +ATCA_STATUS atcab_selftest(uint8_t mode, uint16_t param2, uint8_t* result); + +/* SHA Command */ +#define SHA_CONTEXT_MAX_SIZE (109) +ATCA_STATUS atcab_sha_base(uint8_t mode, uint16_t length, const uint8_t* data_in, uint8_t* data_out, uint16_t* data_out_size); +ATCA_STATUS atcab_sha_start(void); +ATCA_STATUS atcab_sha_update(const uint8_t* message); +ATCA_STATUS atcab_sha_end(uint8_t* digest, uint16_t length, const uint8_t* message); +ATCA_STATUS atcab_sha_read_context(uint8_t* context, uint16_t* context_size); +ATCA_STATUS atcab_sha_write_context(const uint8_t* context, uint16_t context_size); +ATCA_STATUS atcab_sha(uint16_t length, const uint8_t* message, uint8_t* digest); +ATCA_STATUS atcab_hw_sha2_256(const uint8_t* data, size_t data_size, uint8_t* digest); + +ATCA_STATUS atcab_hw_sha2_256_init(atca_sha256_ctx_t* ctx); +ATCA_STATUS atcab_hw_sha2_256_update(atca_sha256_ctx_t* ctx, const uint8_t* data, size_t data_size); +ATCA_STATUS atcab_hw_sha2_256_finish(atca_sha256_ctx_t* ctx, uint8_t* digest); +ATCA_STATUS atcab_sha_hmac_init(atca_hmac_sha256_ctx_t* ctx, uint16_t key_slot); +ATCA_STATUS atcab_sha_hmac_update(atca_hmac_sha256_ctx_t* ctx, const uint8_t* data, size_t data_size); +ATCA_STATUS atcab_sha_hmac_finish(atca_hmac_sha256_ctx_t* ctx, uint8_t* digest, uint8_t target); + +ATCA_STATUS atcab_sha_hmac(const uint8_t* data, size_t data_size, uint16_t key_slot, uint8_t* digest, uint8_t target); +ATCA_STATUS atcab_sha_hmac_ext(ATCADevice device, const uint8_t* data, size_t data_size, uint16_t key_slot, uint8_t* digest, uint8_t target); + +/* Sign command */ +ATCA_STATUS atcab_sign_base(uint8_t mode, uint16_t key_id, uint8_t* signature); +ATCA_STATUS atcab_sign(uint16_t key_id, const uint8_t* msg, uint8_t* signature); +ATCA_STATUS atcab_sign_ext(ATCADevice device, uint16_t key_id, const uint8_t* msg, uint8_t* signature); +ATCA_STATUS atcab_sign_internal(uint16_t key_id, bool is_invalidate, bool is_full_sn, uint8_t* signature); + +/* UpdateExtra command */ +ATCA_STATUS atcab_updateextra(uint8_t mode, uint16_t new_value); + +/* Verify command */ +ATCA_STATUS atcab_verify(uint8_t mode, uint16_t key_id, const uint8_t* signature, const uint8_t* public_key, const uint8_t* other_data, uint8_t* mac); +ATCA_STATUS atcab_verify_extern(const uint8_t* message, const uint8_t* signature, const uint8_t* public_key, bool* is_verified); +ATCA_STATUS atcab_verify_extern_ext(ATCADevice device, const uint8_t* message, const uint8_t* signature, const uint8_t* public_key, bool* is_verified); +ATCA_STATUS atcab_verify_extern_mac(const uint8_t* message, const uint8_t* signature, const uint8_t* public_key, const uint8_t* num_in, const uint8_t* io_key, bool* is_verified); +ATCA_STATUS atcab_verify_stored(const uint8_t* message, const uint8_t* signature, uint16_t key_id, bool* is_verified); +ATCA_STATUS atcab_verify_stored_ext(ATCADevice device, const uint8_t* message, const uint8_t* signature, uint16_t key_id, bool* is_verified); +ATCA_STATUS atcab_verify_stored_mac(const uint8_t* message, const uint8_t* signature, uint16_t key_id, const uint8_t* num_in, const uint8_t* io_key, bool* is_verified); + +ATCA_STATUS atcab_verify_validate(uint16_t key_id, const uint8_t* signature, const uint8_t* other_data, bool* is_verified); +ATCA_STATUS atcab_verify_invalidate(uint16_t key_id, const uint8_t* signature, const uint8_t* other_data, bool* is_verified); + +/* Write command functions */ +ATCA_STATUS atcab_write(uint8_t zone, uint16_t address, const uint8_t* value, const uint8_t* mac); +ATCA_STATUS atcab_write_zone(uint8_t zone, uint16_t slot, uint8_t block, uint8_t offset, const uint8_t* data, uint8_t len); +ATCA_STATUS atcab_write_bytes_zone(uint8_t zone, uint16_t slot, size_t offset_bytes, const uint8_t* data, size_t length); +ATCA_STATUS atcab_write_pubkey(uint16_t slot, const uint8_t* public_key); +ATCA_STATUS atcab_write_config_zone(const uint8_t* config_data); + +#if defined(ATCA_USE_CONSTANT_HOST_NONCE) +ATCA_STATUS atcab_write_enc(uint16_t key_id, uint8_t block, const uint8_t* data, const uint8_t* enc_key, const uint16_t enc_key_id); +#else +ATCA_STATUS atcab_write_enc(uint16_t key_id, uint8_t block, const uint8_t* data, const uint8_t* enc_key, const uint16_t enc_key_id, const uint8_t num_in[NONCE_NUMIN_SIZE]); +#endif + +ATCA_STATUS atcab_write_config_counter(uint16_t counter_id, uint32_t counter_value); + + +#endif /* ATCA_TA_SUPPORT && ATCA_CA_SUPPORT */ + +#ifdef __cplusplus +} +#endif + +/** @} */ +/*lint -flb*/ +#endif /* ATCA_BASIC_H_ */ diff --git a/drivers/ecc108a/cryptoauthlib/atca_bool.h b/drivers/ecc108a/cryptoauthlib/atca_bool.h new file mode 100644 index 0000000..5707b71 --- /dev/null +++ b/drivers/ecc108a/cryptoauthlib/atca_bool.h @@ -0,0 +1,43 @@ +/** + * \file + * + * \brief bool define for systems that don't have it + * + * \copyright (c) 2015-2020 Microchip Technology Inc. and its subsidiaries. + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip software + * and any derivatives exclusively with Microchip products. It is your + * responsibility to comply with third party license terms applicable to your + * use of third party software (including open source software) that may + * accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER + * EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, + * SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE + * OF ANY KIND WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF + * MICROCHIP HAS BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE + * FORESEEABLE. TO THE FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL + * LIABILITY ON ALL CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED + * THE AMOUNT OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR + * THIS SOFTWARE. + */ + +#ifndef _ATCA_BOOL_H +#define _ATCA_BOOL_H + +#if defined(_MSC_VER) && (_MSC_VER <= 1700) +// VS2012 and earlier don't support stdbool.h + #ifndef __cplusplus + #define bool unsigned char + #define false 0 + #define true 1 + #endif +#else + #include +#endif + +#endif diff --git a/drivers/ecc108a/cryptoauthlib/atca_cfgs.c b/drivers/ecc108a/cryptoauthlib/atca_cfgs.c new file mode 100644 index 0000000..f04f1be --- /dev/null +++ b/drivers/ecc108a/cryptoauthlib/atca_cfgs.c @@ -0,0 +1,226 @@ +/** + * \file + * \brief a set of default configurations for various ATCA devices and interfaces + * + * \copyright (c) 2015-2020 Microchip Technology Inc. and its subsidiaries. + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip software + * and any derivatives exclusively with Microchip products. It is your + * responsibility to comply with third party license terms applicable to your + * use of third party software (including open source software) that may + * accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER + * EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, + * SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE + * OF ANY KIND WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF + * MICROCHIP HAS BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE + * FORESEEABLE. TO THE FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL + * LIABILITY ON ALL CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED + * THE AMOUNT OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR + * THIS SOFTWARE. + */ + +#include +#include "cryptoauthlib.h" +#include "atca_cfgs.h" +#include "atca_iface.h" +#include "atca_device.h" + +/** \defgroup config Configuration (cfg_) + * \brief Logical device configurations describe the CryptoAuth device type and logical interface. + @{ */ + +/* if the number of these configurations grows large, we can #ifdef them based on required device support */ + +#if defined(ATCA_ECC_SUPPORT) && defined(ATCA_HAL_I2C) +/** \brief default configuration for an ECCx08A device */ +ATCAIfaceCfg cfg_ateccx08a_i2c_default = { + .iface_type = ATCA_I2C_IFACE, + .devtype = ATECC608, + { +#ifdef ATCA_ENABLE_DEPRECATED + .atcai2c.slave_address = 0xC0, +#else + .atcai2c.address = 0xC0, +#endif + .atcai2c.bus = 2, +#ifdef __linux__ + .atcai2c.baud = 100000, +#else + .atcai2c.baud = 400000, +#endif + }, + .wake_delay = 1500, + .rx_retries = 20 +}; +#endif + +#if defined(ATCA_ECC_SUPPORT) && defined(ATCA_HAL_SWI) +/** \brief default configuration for an ECCx08A device on the logical SWI bus over UART*/ +ATCAIfaceCfg cfg_ateccx08a_swi_default = { + .iface_type = ATCA_SWI_IFACE, + .devtype = ATECC608, + { + .atcaswi.bus = 4, + }, + .wake_delay = 1500, + .rx_retries = 10 +}; +#endif + +#if defined(ATCA_ECC_SUPPORT) && defined(ATCA_HAL_KIT_UART) +/** \brief default configuration for Kit protocol over the device's async interface */ +ATCAIfaceCfg cfg_ateccx08_kituart_default = { + .iface_type = ATCA_UART_IFACE, + .devtype = ATECC608, + { + .atcauart.port = 0, + .atcauart.baud = 115200, + .atcauart.wordsize = 8, + .atcauart.parity = 2, + .atcauart.stopbits = 1, + }, + .rx_retries = 1, +}; +#endif + +#if defined(ATCA_ECC_SUPPORT) && defined(ATCA_HAL_KIT_HID) +/** \brief default configuration for Kit protocol over the device's async interface */ +ATCAIfaceCfg cfg_ateccx08a_kithid_default = { + .iface_type = ATCA_HID_IFACE, + .devtype = ATECC608, + { + .atcahid.dev_interface = ATCA_KIT_AUTO_IFACE, + .atcahid.dev_identity = 0, + .atcahid.idx = 0, + .atcahid.vid = 0x03EB, + .atcahid.pid = 0x2312, + .atcahid.packetsize = 64, + } +}; +#endif + +#if defined(ATCA_SHA_SUPPORT) && defined(ATCA_HAL_I2C) +/** \brief default configuration for a SHA204A device on the first logical I2C bus */ +ATCAIfaceCfg cfg_atsha20xa_i2c_default = { + .iface_type = ATCA_I2C_IFACE, + .devtype = ATSHA204A, + { +#ifdef ATCA_ENABLE_DEPRECATED + .atcai2c.slave_address = 0xC8, +#else + .atcai2c.address = 0xC8, +#endif + .atcai2c.bus = 2, +#ifdef __linux__ + .atcai2c.baud = 100000, +#else + .atcai2c.baud = 400000, +#endif + }, + .wake_delay = 2560, + .rx_retries = 20 +}; +#endif + +#if defined(ATCA_SHA_SUPPORT) && defined(ATCA_HAL_SWI) +/** \brief default configuration for an SHA20xA device on the logical SWI bus over UART*/ +ATCAIfaceCfg cfg_atsha20xa_swi_default = { + .iface_type = ATCA_SWI_IFACE, + .devtype = ATSHA204A, + { + .atcaswi.bus = 4, + }, + .wake_delay = 2560, + .rx_retries = 10 +}; +#endif + +#if defined(ATCA_SHA_SUPPORT) && defined(ATCA_HAL_KIT_UART) +/** \brief default configuration for Kit protocol over the device's async interface */ +ATCAIfaceCfg cfg_atsha20xa_kituart_default = { + .iface_type = ATCA_UART_IFACE, + .devtype = ATSHA204A, + { + .atcauart.port = 0, + .atcauart.baud = 115200, + .atcauart.wordsize = 8, + .atcauart.parity = 2, + .atcauart.stopbits = 1, + }, + .rx_retries = 1, +}; +#endif + +#if defined(ATCA_SHA_SUPPORT) && defined(ATCA_HAL_KIT_HID) +/** \brief default configuration for Kit protocol over the device's async interface */ +ATCAIfaceCfg cfg_atsha20xa_kithid_default = { + .iface_type = ATCA_HID_IFACE, + .devtype = ATSHA204A, + .atcahid.dev_interface = ATCA_KIT_AUTO_IFACE, + .atcahid.dev_identity = 0, + .atcahid.idx = 0, + .atcahid.vid = 0x03EB, + .atcahid.pid = 0x2312, + .atcahid.packetsize = 64, +}; +#endif + +#if defined(ATCA_ECC_SUPPORT) && defined(ATCA_HAL_I2C) +/** \brief default configuration for an ECC204 device on the first logical I2C bus */ +ATCAIfaceCfg cfg_ecc204_i2c_default = { + .iface_type = ATCA_I2C_IFACE, + .devtype = ECC204, + { +#ifdef ATCA_ENABLE_DEPRECATED + .atcai2c.slave_address = 0x33, +#else + .atcai2c.address = 0x33, +#endif + .atcai2c.bus = 2, +#ifdef __linux__ + .atcai2c.baud = 100000, +#else + .atcai2c.baud = 400000, +#endif + }, + .wake_delay = 1500, + .rx_retries = 20 +}; +#endif + +#if defined(ATCA_ECC_SUPPORT) && defined(ATCA_HAL_SWI) +/** \brief default configuration for an ECC204 device on the logical SWI over GPIO*/ +ATCAIfaceCfg cfg_ecc204_swi_default = { + .iface_type = ATCA_SWI_IFACE, + .devtype = ECC204, + { + .atcaswi.bus = 4, + }, + .wake_delay = 1500, + .rx_retries = 10 +}; +#endif + +#if defined(ATCA_ECC_SUPPORT) && defined(ATCA_HAL_KIT_HID) +/** \brief default configuration for Kit protocol over the device's async interface */ +ATCAIfaceCfg cfg_ecc204_kithid_default = { + .iface_type = ATCA_HID_IFACE, + .devtype = ECC204, + { + .atcahid.dev_interface = ATCA_KIT_AUTO_IFACE, + .atcahid.dev_identity = 0, + .atcahid.idx = 0, + .atcahid.vid = 0x03EB, + .atcahid.pid = 0x2312, + .atcahid.packetsize = 64, + } +}; +#endif + +/** @} */ diff --git a/drivers/ecc108a/cryptoauthlib/atca_cfgs.h b/drivers/ecc108a/cryptoauthlib/atca_cfgs.h new file mode 100644 index 0000000..72f0f4a --- /dev/null +++ b/drivers/ecc108a/cryptoauthlib/atca_cfgs.h @@ -0,0 +1,78 @@ +/** + * \file + * \brief a set of default configurations for various ATCA devices and interfaces + * + * \copyright (c) 2015-2020 Microchip Technology Inc. and its subsidiaries. + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip software + * and any derivatives exclusively with Microchip products. It is your + * responsibility to comply with third party license terms applicable to your + * use of third party software (including open source software) that may + * accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER + * EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, + * SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE + * OF ANY KIND WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF + * MICROCHIP HAS BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE + * FORESEEABLE. TO THE FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL + * LIABILITY ON ALL CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED + * THE AMOUNT OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR + * THIS SOFTWARE. + */ + + +#ifndef ATCA_CFGS_H_ +#define ATCA_CFGS_H_ + +#include "atca_iface.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/** \brief default configuration for an ECCx08A device on the first logical I2C bus */ +extern ATCAIfaceCfg cfg_ateccx08a_i2c_default; + +/** \brief default configuration for an ECCx08A device on the logical SWI bus over UART*/ +extern ATCAIfaceCfg cfg_ateccx08a_swi_default; + +/** \brief default configuration for Kit protocol over a CDC interface */ +extern ATCAIfaceCfg cfg_ateccx08a_kitcdc_default; + +/** \brief default configuration for Kit protocol over a HID interface */ +extern ATCAIfaceCfg cfg_ateccx08a_kithid_default; + + +/** \brief default configuration for a SHA204A device on the first logical I2C bus */ +extern ATCAIfaceCfg cfg_atsha20xa_i2c_default; + +/** \brief default configuration for an SHA20xA device on the logical SWI bus over UART*/ +extern ATCAIfaceCfg cfg_atsha20xa_swi_default; + +/** \brief default configuration for Kit protocol over a CDC interface */ +extern ATCAIfaceCfg cfg_atsha20xa_kitcdc_default; + +/** \brief default configuration for Kit protocol over a HID interface for SHA204 */ +extern ATCAIfaceCfg cfg_atsha20xa_kithid_default; + + +/** \brief default configuration for an ECC204 device on the first logical I2C bus */ +extern ATCAIfaceCfg cfg_ecc204_i2c_default; + +/** \brief default configuration for an ECC204 device on the logical SWI over GPIO*/ +extern ATCAIfaceCfg cfg_ecc204_swi_default; + +/** \brief default configuration for Kit protocol over the device's async interface */ +extern ATCAIfaceCfg cfg_ecc204_kithid_default; + + +#ifdef __cplusplus +} +#endif +#endif /* ATCA_CFGS_H_ */ diff --git a/drivers/ecc108a/cryptoauthlib/atca_compiler.h b/drivers/ecc108a/cryptoauthlib/atca_compiler.h new file mode 100644 index 0000000..c746ccb --- /dev/null +++ b/drivers/ecc108a/cryptoauthlib/atca_compiler.h @@ -0,0 +1,222 @@ +/** + * \file + * \brief CryptoAuthLiub is meant to be portable across architectures, even + * non-Microchip architectures and compiler environments. This file is + * for isolating compiler specific macros. + * + * \copyright (c) 2015-2020 Microchip Technology Inc. and its subsidiaries. + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip software + * and any derivatives exclusively with Microchip products. It is your + * responsibility to comply with third party license terms applicable to your + * use of third party software (including open source software) that may + * accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER + * EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, + * SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE + * OF ANY KIND WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF + * MICROCHIP HAS BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE + * FORESEEABLE. TO THE FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL + * LIABILITY ON ALL CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED + * THE AMOUNT OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR + * THIS SOFTWARE. + */ + + +#ifndef ATCA_COMPILER_H_ +#define ATCA_COMPILER_H_ + +#if defined(__clang__) +/* Clang/LLVM. ---------------------------------------------- */ +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +#define ATCA_UINT16_HOST_TO_LE(x) __builtin_bswap16(x) +#define ATCA_UINT16_LE_TO_HOST(x) __builtin_bswap16(x) +#define ATCA_UINT32_HOST_TO_LE(x) __builtin_bswap32(x) +#define ATCA_UINT16_HOST_TO_BE(x) (x) +#define ATCA_UINT16_BE_TO_HOST(x) (x) +#define ATCA_UINT32_HOST_TO_BE(x) (x) +#define ATCA_UINT32_BE_TO_HOST(x) (x) +#define ATCA_UINT64_HOST_TO_BE(x) (x) +#define ATCA_UINT64_BE_TO_HOST(x) (x) +#else +#define ATCA_UINT16_HOST_TO_LE(x) (x) +#define ATCA_UINT16_LE_TO_HOST(x) (x) +#define ATCA_UINT32_HOST_TO_LE(x) (x) +#define ATCA_UINT16_HOST_TO_BE(x) __builtin_bswap16(x) +#define ATCA_UINT16_BE_TO_HOST(x) __builtin_bswap16(x) +#define ATCA_UINT32_HOST_TO_BE(x) __builtin_bswap32(x) +#define ATCA_UINT32_BE_TO_HOST(x) __builtin_bswap32(x) +#define ATCA_UINT64_HOST_TO_BE(x) __builtin_bswap64(x) +#define ATCA_UINT64_BE_TO_HOST(x) __builtin_bswap64(x) +#endif + +#ifdef WIN32 +#define SHARED_LIB_EXPORT __declspec(dllexport) +#define SHARED_LIB_IMPORT __declspec(dllimport) +#else +#define SHARED_LIB_EXPORT +#define SHARED_LIB_IMPORT extern +#endif + +#elif defined(__XC8) || defined(__XC16) +/* Microchip XC8 and XC16 Compilers ------------------------- */ +#ifndef SIZE_MAX +#define SIZE_MAX 65535 +#endif + +#define ATCA_UINT16_HOST_TO_LE(x) (x) +#define ATCA_UINT16_LE_TO_HOST(x) (x) +#define ATCA_UINT32_HOST_TO_LE(x) (x) +#define ATCA_UINT16_HOST_TO_BE(x) ((((x) & 0x00FF) << 8) | (((x) & 0xFF00) >> 8)) +#define ATCA_UINT16_BE_TO_HOST(x) ((((x) & 0x00FF) << 8) | (((x) & 0xFF00) >> 8)) +#define ATCA_UINT32_HOST_TO_BE(x) ((((x) & 0x000000FF) << 24) | (((x) & 0x0000FF00) << 8) | (((x) & 0x00FF0000) >> 8) | (((x) & 0xFF000000) >> 24)) +#define ATCA_UINT32_BE_TO_HOST(x) ((((x) & 0x000000FF) << 24) | (((x) & 0x0000FF00) << 8) | (((x) & 0x00FF0000) >> 8) | (((x) & 0xFF000000) >> 24)) +#define ATCA_UINT64_HOST_TO_BE(x) ((uint64_t)ATCA_UINT32_HOST_TO_BE((uint32_t)(x)) << 32 + (uint64_t)ATCA_UINT32_HOST_TO_BE((uint32_t)((x) >> 32))) +#define ATCA_UINT64_BE_TO_HOST(x) ((uint64_t)ATCA_UINT32_BE_TO_HOST((uint32_t)(x)) << 32 + (uint64_t)ATCA_UINT32_BE_TO_HOST((uint32_t)((x) >> 32))) +#define SHARED_LIB_EXPORT +#define SHARED_LIB_IMPORT extern + +//#elif defined(__ICC) || defined(__INTEL_COMPILER) +/* Intel ICC/ICPC. ------------------------------------------ */ + +#elif defined(__GNUC__) || defined(__GNUG__) +/* GNU GCC/G++. --------------------------------------------- */ +#if defined(__AVR32__) +#define ATCA_UINT16_HOST_TO_LE(x) __builtin_bswap_16(x) +#define ATCA_UINT16_LE_TO_HOST(x) __builtin_bswap_16(x) +#define ATCA_UINT32_HOST_TO_LE(x) __builtin_bswap_32(x) +#define ATCA_UINT16_HOST_TO_BE(x) (x) +#define ATCA_UINT16_BE_TO_HOST(x) (x) +#define ATCA_UINT32_HOST_TO_BE(x) (x) +#define ATCA_UINT32_BE_TO_HOST(x) (x) +#define ATCA_UINT64_HOST_TO_BE(x) (x) +#define ATCA_UINT64_BE_TO_HOST(x) (x) +#define ATCA_NO_PRAGMA_PACK +#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +#define ATCA_UINT16_HOST_TO_LE(x) __builtin_bswap16(x) +#define ATCA_UINT16_LE_TO_HOST(x) __builtin_bswap16(x) +#define ATCA_UINT16_HOST_TO_BE(x) (x) +#define ATCA_UINT16_BE_TO_HOST(x) (x) +#define ATCA_UINT32_HOST_TO_LE(x) __builtin_bswap32(x) +#define ATCA_UINT32_HOST_TO_BE(x) (x) +#define ATCA_UINT32_BE_TO_HOST(x) (x) +#define ATCA_UINT64_HOST_TO_BE(x) (x) +#define ATCA_UINT64_BE_TO_HOST(x) (x) +#else +#define ATCA_UINT16_HOST_TO_BE(x) __builtin_bswap16(x) +#define ATCA_UINT16_BE_TO_HOST(x) __builtin_bswap16(x) +#define ATCA_UINT16_HOST_TO_LE(x) (x) +#define ATCA_UINT16_LE_TO_HOST(x) (x) +#define ATCA_UINT32_HOST_TO_LE(x) (x) +#define ATCA_UINT32_HOST_TO_BE(x) __builtin_bswap32(x) +#define ATCA_UINT32_BE_TO_HOST(x) __builtin_bswap32(x) +#define ATCA_UINT64_HOST_TO_BE(x) __builtin_bswap64(x) +#define ATCA_UINT64_BE_TO_HOST(x) __builtin_bswap64(x) +#endif + +#ifdef WIN32 +#define SHARED_LIB_EXPORT __declspec(dllexport) +#define SHARED_LIB_IMPORT __declspec(dllimport) +#else +#define SHARED_LIB_EXPORT +#define SHARED_LIB_IMPORT extern +#endif + + +//#elif defined(__HP_cc) || defined(__HP_aCC) +/* Hewlett-Packard C/aC++. ---------------------------------- */ + +//#elif defined(__IBMC__) || defined(__IBMCPP__) +/* IBM XL C/C++. -------------------------------------------- */ + +#elif defined(_MSC_VER) +/* Microsoft Visual Studio. --------------------------------- */ +// MSVC is usually always little-endian architecture +#include +#define ATCA_UINT16_HOST_TO_BE(x) _byteswap_ushort(x) +#define ATCA_UINT16_BE_TO_HOST(x) _byteswap_ushort(x) +#define ATCA_UINT16_HOST_TO_LE(x) (x) +#define ATCA_UINT16_LE_TO_HOST(x) (x) +#define ATCA_UINT32_HOST_TO_LE(x) (x) +#define ATCA_UINT32_HOST_TO_BE(x) _byteswap_ulong(x) +#define ATCA_UINT32_BE_TO_HOST(x) _byteswap_ulong(x) +#define ATCA_UINT64_HOST_TO_BE(x) _byteswap_uint64(x) +#define ATCA_UINT64_BE_TO_HOST(x) _byteswap_uint64(x) +#define strtok_r strtok_s + +#define SHARED_LIB_EXPORT __declspec(dllexport) +#define SHARED_LIB_IMPORT __declspec(dllimport) + +//#elif defined(__PGI) +/* Portland Group PGCC/PGCPP. ------------------------------- */ + +//#elif defined(__SUNPRO_C) || defined(__SUNPRO_CC) +/* Oracle Solaris Studio. ----------------------------------- */ + +#elif defined __CC_ARM +/* ARMCC/RealView ------------------------------------------- */ +#ifdef __BIG_ENDIAN +#define ATCA_UINT16_HOST_TO_LE(x) ((x >> 8) | ((x & 0xFF) << 8)) +#define ATCA_UINT16_LE_TO_HOST(x) ((x >> 8) | ((x & 0xFF) << 8)) +#define ATCA_UINT32_HOST_TO_LE(x) __rev(x) +#define ATCA_UINT32_HOST_TO_BE(x) (x) +#define ATCA_UINT32_BE_TO_HOST(x) (x) +#define ATCA_UINT64_HOST_TO_BE(x) (x) +#define ATCA_UINT64_BE_TO_HOST(x) (x) +#else +#define ATCA_UINT16_HOST_TO_LE(x) (x) +#define ATCA_UINT16_LE_TO_HOST(x) (x) +#define ATCA_UINT32_HOST_TO_LE(x) (x) +#define ATCA_UINT32_HOST_TO_BE(x) __rev(x) +#define ATCA_UINT32_BE_TO_HOST(x) __rev(x) +#define ATCA_UINT64_HOST_TO_BE(x) (((uint64_t)__rev((uint32_t)x) << 32) | (uint64_t)__rev((uint32_t)(x >> 32))) +#define ATCA_UINT64_BE_TO_HOST(x) (((uint64_t)__rev((uint32_t)x) << 32) | (uint64_t)__rev((uint32_t)(x >> 32))) +#endif + +#define SHARED_LIB_EXPORT +#define SHARED_LIB_IMPORT extern + +#elif defined __ICCARM__ +/* IAR ARM ------------------------------------------- */ +#include +#if __LITTLE_ENDIAN__ == 0 +#define ATCA_UINT16_HOST_TO_LE(x) __REV16(x) +#define ATCA_UINT16_LE_TO_HOST(x) __REV16(x) +#define ATCA_UINT32_HOST_TO_LE(x) __REV(x) +#define ATCA_UINT32_HOST_TO_BE(x) (x) +#define ATCA_UINT32_BE_TO_HOST(x) (x) +#define ATCA_UINT64_HOST_TO_BE(x) (x) +#define ATCA_UINT64_BE_TO_HOST(x) (x) +#else +#define ATCA_UINT16_HOST_TO_LE(x) (x) +#define ATCA_UINT16_LE_TO_HOST(x) (x) +#define ATCA_UINT32_HOST_TO_LE(x) (x) +#define ATCA_UINT32_HOST_TO_BE(x) __REV(x) +#define ATCA_UINT32_BE_TO_HOST(x) __REV(x) +#define ATCA_UINT64_HOST_TO_BE(x) (((uint64_t)__REV((uint32_t)x) << 32) | (uint64_t)__REV((uint32_t)(x >> 32))) +#define ATCA_UINT64_BE_TO_HOST(x) (((uint64_t)__REV((uint32_t)x) << 32) | (uint64_t)__REV((uint32_t)(x >> 32))) +#endif + +#define SHARED_LIB_EXPORT +#define SHARED_LIB_IMPORT extern + +#endif + +#ifdef ATCA_BUILD_SHARED_LIBS +#if defined(cryptoauth_EXPORTS) && defined(_WIN32) +#define ATCA_DLL SHARED_LIB_EXPORT +#else +#define ATCA_DLL SHARED_LIB_IMPORT +#endif +#else +#undef SHARED_LIB_EXPORT +#define SHARED_LIB_EXPORT +#define ATCA_DLL extern +#endif + +#endif /* ATCA_COMPILER_H_ */ diff --git a/drivers/ecc108a/cryptoauthlib/atca_config.h b/drivers/ecc108a/cryptoauthlib/atca_config.h new file mode 100644 index 0000000..6787ecc --- /dev/null +++ b/drivers/ecc108a/cryptoauthlib/atca_config.h @@ -0,0 +1,14 @@ +#include "py/mphal.h" + +#define ATCA_ATECC108A_SUPPORT +#define ATCA_POST_DELAY_MSEC 25 +#define ATCA_HAL_I2C + +#define ATCA_PLATFORM_MALLOC malloc +#define ATCA_PLATFORM_FREE free + +#define ATCA_TIDAL + +#define atca_delay_ms mp_hal_delay_ms +#define atca_delay_us mp_hal_delay_us + diff --git a/drivers/ecc108a/cryptoauthlib/atca_debug.c b/drivers/ecc108a/cryptoauthlib/atca_debug.c new file mode 100644 index 0000000..f1cf6d9 --- /dev/null +++ b/drivers/ecc108a/cryptoauthlib/atca_debug.c @@ -0,0 +1,49 @@ +/** + * \file + * \brief Debug/Trace for CryptoAuthLib calls + * + * \copyright (c) 2015-2020 Microchip Technology Inc. and its subsidiaries. + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip software + * and any derivatives exclusively with Microchip products. It is your + * responsibility to comply with third party license terms applicable to your + * use of third party software (including open source software) that may + * accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER + * EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, + * SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE + * OF ANY KIND WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF + * MICROCHIP HAS BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE + * FORESEEABLE. TO THE FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL + * LIABILITY ON ALL CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED + * THE AMOUNT OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR + * THIS SOFTWARE. + */ + +#include + +FILE * g_trace_fp; + +void atca_trace_config(FILE* fp) +{ + g_trace_fp = fp; +} + +ATCA_STATUS atca_trace(ATCA_STATUS status) +{ + return status; +} + +ATCA_STATUS atca_trace_msg(ATCA_STATUS status, const char * msg) +{ + if (ATCA_SUCCESS != status) + { + fprintf(g_trace_fp ? g_trace_fp : stderr, msg, status); + } + return status; +} diff --git a/drivers/ecc108a/cryptoauthlib/atca_debug.h b/drivers/ecc108a/cryptoauthlib/atca_debug.h new file mode 100644 index 0000000..fbe6847 --- /dev/null +++ b/drivers/ecc108a/cryptoauthlib/atca_debug.h @@ -0,0 +1,11 @@ +#ifndef _ATCA_DEBUG_H +#define _ATCA_DEBUG_H + +#include "atca_status.h" + +void atca_trace_config(FILE* fp); + +ATCA_STATUS atca_trace(ATCA_STATUS status); +ATCA_STATUS atca_trace_msg(ATCA_STATUS status, const char * msg); + +#endif /* _ATCA_DEBUG_H */ diff --git a/drivers/ecc108a/cryptoauthlib/atca_device.c b/drivers/ecc108a/cryptoauthlib/atca_device.c new file mode 100644 index 0000000..855e638 --- /dev/null +++ b/drivers/ecc108a/cryptoauthlib/atca_device.c @@ -0,0 +1,134 @@ +/** + * \file + * \brief Microchip CryptoAuth device object + * + * \copyright (c) 2015-2020 Microchip Technology Inc. and its subsidiaries. + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip software + * and any derivatives exclusively with Microchip products. It is your + * responsibility to comply with third party license terms applicable to your + * use of third party software (including open source software) that may + * accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER + * EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, + * SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE + * OF ANY KIND WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF + * MICROCHIP HAS BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE + * FORESEEABLE. TO THE FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL + * LIABILITY ON ALL CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED + * THE AMOUNT OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR + * THIS SOFTWARE. + */ + +#include + +/** \defgroup device ATCADevice (atca_) + * \brief ATCADevice object - composite of command and interface objects + @{ */ + + +#ifndef ATCA_NO_HEAP +/** \brief constructor for a Microchip CryptoAuth device + * \param[in] cfg Interface configuration object + * \return Reference to a new ATCADevice on success. NULL on failure. + */ +ATCADevice newATCADevice(ATCAIfaceCfg *cfg) +{ + ATCADevice ca_dev = NULL; + ATCA_STATUS status; + + if (cfg == NULL) + { + return NULL; + } + + ca_dev = (ATCADevice)hal_malloc(sizeof(*ca_dev)); + if (ca_dev == NULL) + { + return NULL; + } + + memset(ca_dev, 0, sizeof(struct atca_device)); + + status = initATCADevice(cfg, ca_dev); + if (status != ATCA_SUCCESS) + { + hal_free(ca_dev); + ca_dev = NULL; + return NULL; + } + + return ca_dev; +} + +/** \brief destructor for a device NULLs reference after object is freed + * \param[in] ca_dev pointer to a reference to a device + */ +void deleteATCADevice(ATCADevice *ca_dev) +{ + if (ca_dev == NULL) + { + return; + } + + releaseATCADevice(*ca_dev); + + hal_free(*ca_dev); + *ca_dev = NULL; +} +#endif + +/** \brief Initializer for an Microchip CryptoAuth device + * \param[in] cfg pointer to an interface configuration object + * \param[in,out] ca_dev As input, pre-allocated structure to be initialized. + * mCommands and mIface members should point to existing + * structures to be initialized. + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS initATCADevice(ATCAIfaceCfg *cfg, ATCADevice ca_dev) +{ + ATCA_STATUS status; + + if (cfg == NULL || ca_dev == NULL) + { + return ATCA_BAD_PARAM; + } + + status = initATCAIface(cfg, &ca_dev->mIface); + if (status != ATCA_SUCCESS) + { + return status; + } + + return ATCA_SUCCESS; +} + +/** \brief returns a reference to the ATCAIface interface object for the device + * \param[in] dev reference to a device + * \return reference to the ATCAIface object for the device + */ +ATCAIface atGetIFace(ATCADevice dev) +{ + return &dev->mIface; +} + +/** \brief Release any resources associated with the device. + * \param[in] ca_dev Device to release + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS releaseATCADevice(ATCADevice ca_dev) +{ + if (ca_dev == NULL) + { + return ATCA_BAD_PARAM; + } + + return releaseATCAIface(&ca_dev->mIface); +} + +/** @} */ diff --git a/drivers/ecc108a/cryptoauthlib/atca_device.h b/drivers/ecc108a/cryptoauthlib/atca_device.h new file mode 100644 index 0000000..a9ac7f2 --- /dev/null +++ b/drivers/ecc108a/cryptoauthlib/atca_device.h @@ -0,0 +1,312 @@ +/** + * \file + * + * \brief Microchip Crypto Auth device object + * + * \copyright (c) 2015-2020 Microchip Technology Inc. and its subsidiaries. + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip software + * and any derivatives exclusively with Microchip products. It is your + * responsibility to comply with third party license terms applicable to your + * use of third party software (including open source software) that may + * accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER + * EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, + * SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE + * OF ANY KIND WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF + * MICROCHIP HAS BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE + * FORESEEABLE. TO THE FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL + * LIABILITY ON ALL CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED + * THE AMOUNT OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR + * THIS SOFTWARE. + */ + +#ifndef ATCA_DEVICE_H +#define ATCA_DEVICE_H +/*lint +flb */ + +#include "atca_iface.h" +/** \defgroup device ATCADevice (atca_) + @{ */ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef ATCA_NO_PRAGMA_PACK +#pragma pack(push, 1) +#define ATCA_PACKED +#else +#define ATCA_PACKED __attribute__ ((packed)) +#endif + +typedef struct ATCA_PACKED _atsha204a_config +{ + uint32_t SN03; + uint32_t RevNum; + uint32_t SN47; + uint8_t SN8; + uint8_t Reserved0; + uint8_t I2C_Enable; + uint8_t Reserved1; + uint8_t I2C_Address; + uint8_t Reserved2; + uint8_t OTPmode; + uint8_t ChipMode; + uint16_t SlotConfig[16]; + uint16_t Counter[8]; + uint8_t LastKeyUse[16]; + uint8_t UserExtra; + uint8_t Selector; + uint8_t LockValue; + uint8_t LockConfig; +} atsha204a_config_t; + +typedef struct ATCA_PACKED _atecc508a_config +{ + uint32_t SN03; + uint32_t RevNum; + uint32_t SN47; + uint8_t SN8; + uint8_t Reserved0; + uint8_t I2C_Enable; + uint8_t Reserved1; + uint8_t I2C_Address; + uint8_t Reserved2; + uint8_t OTPmode; + uint8_t ChipMode; + uint16_t SlotConfig[16]; + uint8_t Counter0[8]; + uint8_t Counter1[8]; + uint8_t LastKeyUse[16]; + uint8_t UserExtra; + uint8_t Selector; + uint8_t LockValue; + uint8_t LockConfig; + uint16_t SlotLocked; + uint16_t RFU; + uint32_t X509format; + uint16_t KeyConfig[16]; +} atecc508a_config_t; + +typedef struct ATCA_PACKED _atecc608_config +{ + uint32_t SN03; + uint32_t RevNum; + uint32_t SN47; + uint8_t SN8; + uint8_t AES_Enable; + uint8_t I2C_Enable; + uint8_t Reserved1; + uint8_t I2C_Address; + uint8_t Reserved2; + uint8_t CountMatch; + uint8_t ChipMode; + uint16_t SlotConfig[16]; + uint8_t Counter0[8]; + uint8_t Counter1[8]; + uint8_t UseLock; + uint8_t VolatileKeyPermission; + uint16_t SecureBoot; + uint8_t KdflvLoc; + uint16_t KdflvStr; + uint8_t Reserved3[9]; + uint8_t UserExtra; + uint8_t UserExtraAdd; + uint8_t LockValue; + uint8_t LockConfig; + uint16_t SlotLocked; + uint16_t ChipOptions; + uint32_t X509format; + uint16_t KeyConfig[16]; +} atecc608_config_t; + +#ifndef ATCA_NO_PRAGMA_PACK +#pragma pack(pop) +#endif + +/** \brief ATCADeviceState says about device state + */ +typedef enum +{ + ATCA_DEVICE_STATE_UNKNOWN = 0, + ATCA_DEVICE_STATE_SLEEP, + ATCA_DEVICE_STATE_IDLE, + ATCA_DEVICE_STATE_ACTIVE +} ATCADeviceState; + + +/** \brief atca_device is the C object backing ATCADevice. See the atca_device.h file for + * details on the ATCADevice methods + */ +struct atca_device +{ + atca_iface_t mIface; /**< Physical interface */ + uint8_t device_state; /**< Device Power State */ + + uint8_t clock_divider; + uint16_t execution_time_msec; + + uint8_t session_state; /**< Secure Session State */ + uint16_t session_counter; /**< Secure Session Message Count */ + uint16_t session_key_id; /**< Key ID used for a secure sesison */ + uint8_t* session_key; /**< Session Key */ + uint8_t session_key_len; /**< Length of key used for the session in bytes */ + + uint16_t options; /**< Nested command details parameter */ + +}; + +typedef struct atca_device * ATCADevice; + +ATCA_STATUS initATCADevice(ATCAIfaceCfg* cfg, ATCADevice cadev); +ATCADevice newATCADevice(ATCAIfaceCfg *cfg); +ATCA_STATUS releaseATCADevice(ATCADevice ca_dev); +void deleteATCADevice(ATCADevice *ca_dev); + +ATCAIface atGetIFace(ATCADevice dev); + + + +#ifdef __cplusplus +} +#endif +#define ATCA_AES_ENABLE_EN_SHIFT (0) +#define ATCA_AES_ENABLE_EN_MASK (0x01u << ATCA_AES_ENABLE_EN_SHIFT) + +/* I2C */ +#define ATCA_I2C_ENABLE_EN_SHIFT (0) +#define ATCA_I2C_ENABLE_EN_MASK (0x01u << ATCA_I2C_ENABLE_EN_SHIFT) + +/* Counter Match Feature */ +#define ATCA_COUNTER_MATCH_EN_SHIFT (0) +#define ATCA_COUNTER_MATCH_EN_MASK (0x01u << ATCA_COUNTER_MATCH_EN_SHIFT) +#define ATCA_COUNTER_MATCH_KEY_SHIFT (4) +#define ATCA_COUNTER_MATCH_KEY_MASK (0x0Fu << ATCA_COUNTER_MATCH_KEY_SHIFT) +#define ATCA_COUNTER_MATCH_KEY(v) (ATCA_COUNTER_MATCH_KEY_MASK & (v << ATCA_COUNTER_MATCH_KEY_SHIFT)) + +/* ChipMode */ +#define ATCA_CHIP_MODE_I2C_EXTRA_SHIFT (0) +#define ATCA_CHIP_MODE_I2C_EXTRA_MASK (0x01u << ATCA_CHIP_MODE_I2C_EXTRA_SHIFT) +#define ATCA_CHIP_MODE_TTL_EN_SHIFT (1) +#define ATCA_CHIP_MODE_TTL_EN_MASK (0x01u << ATCA_CHIP_MODE_TTL_EN_SHIFT) +#define ATCA_CHIP_MODE_WDG_LONG_SHIFT (2) +#define ATCA_CHIP_MODE_WDG_LONG_MASK (0x01u << ATCA_CHIP_MODE_WDG_LONG_SHIFT) +#define ATCA_CHIP_MODE_CLK_DIV_SHIFT (3) +#define ATCA_CHIP_MODE_CLK_DIV_MASK (0x1Fu << ATCA_CHIP_MODE_CLK_DIV_SHIFT) +#define ATCA_CHIP_MODE_CLK_DIV(v) (ATCA_CHIP_MODE_CLK_DIV_MASK & (v << ATCA_CHIP_MODE_CLK_DIV_SHIFT)) + +/* General Purpose Slot Config (Not ECC Private Keys) */ +#define ATCA_SLOT_CONFIG_READKEY_SHIFT (0) +#define ATCA_SLOT_CONFIG_READKEY_MASK (0x0Fu << ATCA_SLOT_CONFIG_READKEY_SHIFT) +#define ATCA_SLOT_CONFIG_READKEY(v) (ATCA_SLOT_CONFIG_READKEY_MASK & (v << ATCA_SLOT_CONFIG_READKEY_SHIFT)) +#define ATCA_SLOT_CONFIG_NOMAC_SHIFT (4) +#define ATCA_SLOT_CONFIG_NOMAC_MASK (0x01u << ATCA_SLOT_CONFIG_NOMAC_SHIFT) +#define ATCA_SLOT_CONFIG_LIMITED_USE_SHIFT (5) +#define ATCA_SLOT_CONFIG_LIMITED_USE_MASK (0x01u << ATCA_SLOT_CONFIG_LIMITED_USE_SHIFT) +#define ATCA_SLOT_CONFIG_ENCRYPTED_READ_SHIFT (6) +#define ATCA_SLOT_CONFIG_ENCRYPTED_READ_MASK (0x01u << ATCA_SLOT_CONFIG_ENCRYPTED_READ_SHIFT) +#define ATCA_SLOT_CONFIG_IS_SECRET_SHIFT (7) +#define ATCA_SLOT_CONFIG_IS_SECRET_MASK (0x01u << ATCA_SLOT_CONFIG_IS_SECRET_SHIFT) +#define ATCA_SLOT_CONFIG_WRITE_KEY_SHIFT (8) +#define ATCA_SLOT_CONFIG_WRITE_KEY_MASK (0x0Fu << ATCA_SLOT_CONFIG_WRITE_KEY_SHIFT) +#define ATCA_SLOT_CONFIG_WRITE_KEY(v) (ATCA_SLOT_CONFIG_WRITE_KEY_MASK & (v << ATCA_SLOT_CONFIG_WRITE_KEY_SHIFT)) +#define ATCA_SLOT_CONFIG_WRITE_CONFIG_SHIFT (12) +#define ATCA_SLOT_CONFIG_WRITE_CONFIG_MASK (0x0Fu << ATCA_SLOT_CONFIG_WRITE_CONFIG_SHIFT) +#define ATCA_SLOT_CONFIG_WRITE_CONFIG(v) (ATCA_SLOT_CONFIG_WRITE_CONFIG_MASK & (v << ATCA_SLOT_CONFIG_WRITE_CONFIG_SHIFT)) + +/* Slot Config for ECC Private Keys */ +#define ATCA_SLOT_CONFIG_EXT_SIG_SHIFT (0) +#define ATCA_SLOT_CONFIG_EXT_SIG_MASK (0x01u << ATCA_SLOT_CONFIG_EXT_SIG_SHIFT) +#define ATCA_SLOT_CONFIG_INT_SIG_SHIFT (1) +#define ATCA_SLOT_CONFIG_INT_SIG_MASK (0x01u << ATCA_SLOT_CONFIG_INT_SIG_SHIFT) +#define ATCA_SLOT_CONFIG_ECDH_SHIFT (2) +#define ATCA_SLOT_CONFIG_ECDH_MASK (0x01u << ATCA_SLOT_CONFIG_ECDH_SHIFT) +#define ATCA_SLOT_CONFIG_WRITE_ECDH_SHIFT (3) +#define ATCA_SLOT_CONFIG_WRITE_ECDH_MASK (0x01u << ATCA_SLOT_CONFIG_WRITE_ECDH_SHIFT) +#define ATCA_SLOT_CONFIG_GEN_KEY_SHIFT (8) +#define ATCA_SLOT_CONFIG_GEN_KEY_MASK (0x01u << ATCA_SLOT_CONFIG_GEN_KEY_SHIFT) +#define ATCA_SLOT_CONFIG_PRIV_WRITE_SHIFT (9) +#define ATCA_SLOT_CONFIG_PRIV_WRITE_MASK (0x01u << ATCA_SLOT_CONFIG_PRIV_WRITE_SHIFT) + +/* Use Lock */ +#define ATCA_USE_LOCK_ENABLE_SHIFT (0) +#define ATCA_USE_LOCK_ENABLE_MASK (0x0Fu << ATCA_USE_LOCK_ENABLE_SHIFT) +#define ATCA_USE_LOCK_KEY_SHIFT (4) +#define ATCA_USE_LOCK_KEY_MASK (0x0Fu << ATCA_USE_LOCK_KEY_SHIFT) + +/* Voltatile Key Permission */ +#define ATCA_VOL_KEY_PERM_SLOT_SHIFT (0) +#define ATCA_VOL_KEY_PERM_SLOT_MASK (0x0Fu << ATCA_VOL_KEY_PERM_SLOT_SHIFT) +#define ATCA_VOL_KEY_PERM_SLOT(v) (ATCA_VOL_KEY_PERM_SLOT_MASK & (v << ATCA_VOL_KEY_PERM_SLOT_SHIFT)) +#define ATCA_VOL_KEY_PERM_EN_SHIFT (7) +#define ATCA_VOL_KEY_PERM_EN_MASK (0x01u << ATCA_VOL_KEY_PERM_EN_SHIFT) + +/* Secure Boot */ +#define ATCA_SECURE_BOOT_MODE_SHIFT (0) +#define ATCA_SECURE_BOOT_MODE_MASK (0x03u << ATCA_SECURE_BOOT_MODE_SHIFT) +#define ATCA_SECURE_BOOT_MODE(v) (ATCA_SECURE_BOOT_MODE_MASK & (v << ATCA_SECURE_BOOT_MODE_SHIFT)) +#define ATCA_SECURE_BOOT_PERSIST_EN_SHIFT (3) +#define ATCA_SECURE_BOOT_PERSIST_EN_MASK (0x01u << ATCA_SECURE_BOOT_PERSIST_EN_SHIFT) +#define ATCA_SECURE_BOOT_RAND_NONCE_SHIFT (4) +#define ATCA_SECURE_BOOT_RAND_NONCE_MASK (0x01u << ATCA_SECURE_BOOT_RAND_NONCE_SHIFT) +#define ATCA_SECURE_BOOT_DIGEST_SHIFT (8) +#define ATCA_SECURE_BOOT_DIGEST_MASK (0x0Fu << ATCA_SECURE_BOOT_DIGEST_SHIFT) +#define ATCA_SECURE_BOOT_DIGEST(v) (ATCA_SECURE_BOOT_DIGEST_MASK & (v << ATCA_SECURE_BOOT_DIGEST_SHIFT)) +#define ATCA_SECURE_BOOT_PUB_KEY_SHIFT (12) +#define ATCA_SECURE_BOOT_PUB_KEY_MASK (0x0Fu << ATCA_SECURE_BOOT_PUB_KEY_SHIFT) +#define ATCA_SECURE_BOOT_PUB_KEY(v) (ATCA_SECURE_BOOT_PUB_KEY_MASK & (v << ATCA_SECURE_BOOT_PUB_KEY_SHIFT)) + +/* Slot Locked */ +#define ATCA_SLOT_LOCKED(v) ((0x01 << v) & 0xFFFFu) + +/* Chip Options */ +#define ATCA_CHIP_OPT_POST_EN_SHIFT (0) +#define ATCA_CHIP_OPT_POST_EN_MASK (0x01u << ATCA_CHIP_OPT_POST_EN_SHIFT) +#define ATCA_CHIP_OPT_IO_PROT_EN_SHIFT (1) +#define ATCA_CHIP_OPT_IO_PROT_EN_MASK (0x01u << ATCA_CHIP_OPT_IO_PROT_EN_SHIFT) +#define ATCA_CHIP_OPT_KDF_AES_EN_SHIFT (2) +#define ATCA_CHIP_OPT_KDF_AES_EN_MASK (0x01u << ATCA_CHIP_OPT_KDF_AES_EN_SHIFT) +#define ATCA_CHIP_OPT_ECDH_PROT_SHIFT (8) +#define ATCA_CHIP_OPT_ECDH_PROT_MASK (0x03u << ATCA_CHIP_OPT_ECDH_PROT_SHIFT) +#define ATCA_CHIP_OPT_ECDH_PROT(v) (ATCA_CHIP_OPT_ECDH_PROT_MASK & (v << ATCA_CHIP_OPT_ECDH_PROT_SHIFT)) +#define ATCA_CHIP_OPT_KDF_PROT_SHIFT (10) +#define ATCA_CHIP_OPT_KDF_PROT_MASK (0x03u << ATCA_CHIP_OPT_KDF_PROT_SHIFT) +#define ATCA_CHIP_OPT_KDF_PROT(v) (ATCA_CHIP_OPT_KDF_PROT_MASK & (v << ATCA_CHIP_OPT_KDF_PROT_SHIFT)) +#define ATCA_CHIP_OPT_IO_PROT_KEY_SHIFT (12) +#define ATCA_CHIP_OPT_IO_PROT_KEY_MASK (0x0Fu << ATCA_CHIP_OPT_IO_PROT_KEY_SHIFT) +#define ATCA_CHIP_OPT_IO_PROT_KEY(v) (ATCA_CHIP_OPT_IO_PROT_KEY_MASK & (v << ATCA_CHIP_OPT_IO_PROT_KEY_SHIFT)) + +/* Key Config */ +#define ATCA_KEY_CONFIG_OFFSET(x) (96UL + (x) * 2) +#define ATCA_KEY_CONFIG_PRIVATE_SHIFT (0) +#define ATCA_KEY_CONFIG_PRIVATE_MASK (0x01u << ATCA_KEY_CONFIG_PRIVATE_SHIFT) +#define ATCA_KEY_CONFIG_PUB_INFO_SHIFT (1) +#define ATCA_KEY_CONFIG_PUB_INFO_MASK (0x01u << ATCA_KEY_CONFIG_PUB_INFO_SHIFT) +#define ATCA_KEY_CONFIG_KEY_TYPE_SHIFT (2) +#define ATCA_KEY_CONFIG_KEY_TYPE_MASK (0x07u << ATCA_KEY_CONFIG_KEY_TYPE_SHIFT) +#define ATCA_KEY_CONFIG_KEY_TYPE(v) (ATCA_KEY_CONFIG_KEY_TYPE_MASK & (v << ATCA_KEY_CONFIG_KEY_TYPE_SHIFT)) +#define ATCA_KEY_CONFIG_LOCKABLE_SHIFT (5) +#define ATCA_KEY_CONFIG_LOCKABLE_MASK (0x01u << ATCA_KEY_CONFIG_LOCKABLE_SHIFT) +#define ATCA_KEY_CONFIG_REQ_RANDOM_SHIFT (6) +#define ATCA_KEY_CONFIG_REQ_RANDOM_MASK (0x01u << ATCA_KEY_CONFIG_REQ_RANDOM_SHIFT) +#define ATCA_KEY_CONFIG_REQ_AUTH_SHIFT (7) +#define ATCA_KEY_CONFIG_REQ_AUTH_MASK (0x01u << ATCA_KEY_CONFIG_REQ_AUTH_SHIFT) +#define ATCA_KEY_CONFIG_AUTH_KEY_SHIFT (8) +#define ATCA_KEY_CONFIG_AUTH_KEY_MASK (0x0Fu << ATCA_KEY_CONFIG_AUTH_KEY_SHIFT) +#define ATCA_KEY_CONFIG_AUTH_KEY(v) (ATCA_KEY_CONFIG_AUTH_KEY_MASK & (v << ATCA_KEY_CONFIG_AUTH_KEY_SHIFT)) +#define ATCA_KEY_CONFIG_PERSIST_DISABLE_SHIFT (12) +#define ATCA_KEY_CONFIG_PERSIST_DISABLE_MASK (0x01u << ATCA_KEY_CONFIG_PERSIST_DISABLE_SHIFT) +#define ATCA_KEY_CONFIG_RFU_SHIFT (13) +#define ATCA_KEY_CONFIG_RFU_MASK (0x01u << ATCA_KEY_CONFIG_RFU_SHIFT) +#define ATCA_KEY_CONFIG_X509_ID_SHIFT (14) +#define ATCA_KEY_CONFIG_X509_ID_MASK (0x03u << ATCA_KEY_CONFIG_X509_ID_SHIFT) +#define ATCA_KEY_CONFIG_X509_ID(v) (ATCA_KEY_CONFIG_X509_ID_MASK & (v << ATCA_KEY_CONFIG_X509_ID_SHIFT)) +/** @} */ +/*lint -flb*/ +#endif diff --git a/drivers/ecc108a/cryptoauthlib/atca_devtypes.h b/drivers/ecc108a/cryptoauthlib/atca_devtypes.h new file mode 100644 index 0000000..ba8f82d --- /dev/null +++ b/drivers/ecc108a/cryptoauthlib/atca_devtypes.h @@ -0,0 +1,59 @@ +/** + * \file + * \brief Microchip Crypto Auth + * + * \copyright (c) 2015-2020 Microchip Technology Inc. and its subsidiaries. + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip software + * and any derivatives exclusively with Microchip products. It is your + * responsibility to comply with third party license terms applicable to your + * use of third party software (including open source software) that may + * accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER + * EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, + * SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE + * OF ANY KIND WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF + * MICROCHIP HAS BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE + * FORESEEABLE. TO THE FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL + * LIABILITY ON ALL CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED + * THE AMOUNT OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR + * THIS SOFTWARE. + */ + + +#ifndef ATCA_DEVTYPES_H_ +#define ATCA_DEVTYPES_H_ + +/** \defgroup device ATCADevice (atca_) + @{ */ + +#ifdef __cplusplus +extern "C" { +#endif + + +/** \brief The supported Device type in Cryptoauthlib library */ +typedef enum +{ + ATSHA204A = 0, + ATECC108A = 1, + ATECC508A = 2, + ATECC608A = 3, + ATECC608B = 3, + ATECC608 = 3, + ATSHA206A = 4, + ECC204 = 5, + TA100 = 0x10, + ATCA_DEV_UNKNOWN = 0x20 +} ATCADeviceType; + +#ifdef __cplusplus +} +#endif +/** @} */ +#endif /* ATCA_DEVTYPES_H_ */ diff --git a/drivers/ecc108a/cryptoauthlib/atca_helpers.c b/drivers/ecc108a/cryptoauthlib/atca_helpers.c new file mode 100644 index 0000000..6086f34 --- /dev/null +++ b/drivers/ecc108a/cryptoauthlib/atca_helpers.c @@ -0,0 +1,881 @@ +/** + * \file + * \brief Helpers to support the CryptoAuthLib Basic API methods + * + * \copyright (c) 2015-2020 Microchip Technology Inc. and its subsidiaries. + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip software + * and any derivatives exclusively with Microchip products. It is your + * responsibility to comply with third party license terms applicable to your + * use of third party software (including open source software) that may + * accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER + * EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, + * SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE + * OF ANY KIND WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF + * MICROCHIP HAS BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE + * FORESEEABLE. TO THE FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL + * LIABILITY ON ALL CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED + * THE AMOUNT OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR + * THIS SOFTWARE. + */ + +#include +#include +#include +#include "cryptoauthlib.h" +#include "atca_helpers.h" + + + +/* Ruleset: + Index - Meaning + 0 - 62 Character + 1 - 63 Character + 2 - Pad Character (none if 0) + 3 - Maximum line length (no limit if 0) */ +uint8_t atcab_b64rules_default[4] = { '+', '/', '=', 64 }; +uint8_t atcab_b64rules_mime[4] = { '+', '/', '=', 76 }; +uint8_t atcab_b64rules_urlsafe[4] = { '-', '_', 0, 0 }; + + +/** \brief Convert a binary buffer to a hex string for easy reading. + * \param[in] bin Input data to convert. + * \param[in] bin_size Size of data to convert. + * \param[out] hex Buffer that receives hex string. + * \param[in,out] hex_size As input, the size of the hex buffer. + * As output, the size of the output hex. + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_bin2hex(const uint8_t* bin, size_t bin_size, char* hex, size_t* hex_size) +{ + return atcab_bin2hex_(bin, bin_size, hex, hex_size, true, true, true); +} + +static void uint8_to_hex(uint8_t num, char* hex_str) +{ + uint8_t nibble = (num >> 4) & 0x0F; + + if (nibble < 10) + { + *(hex_str++) = '0' + nibble; + } + else + { + *(hex_str++) = 'A' + (nibble - 10); + } + nibble = num & 0x0F; + if (nibble < 10) + { + *(hex_str++) = '0' + nibble; + } + else + { + *(hex_str++) = 'A' + (nibble - 10); + } +} + +static void hex_to_lowercase(char *buffer, size_t length) +{ + size_t index; + + if ((buffer != NULL) && (length > 0)) + { + for (index = 0; index < length; index++) + { + buffer[index] = (uint8_t)(tolower(buffer[index])); + } + } +} + + +static void hex_to_uppercase(char *buffer, size_t length) +{ + size_t index; + + if ((buffer != NULL) && (length > 0)) + { + for (index = 0; index < length; index++) + { + buffer[index] = (uint8_t)(toupper(buffer[index])); + } + } +} + +/** \brief To reverse the input data. + * \param[in] bin Input data to reverse. + * \param[in] bin_size Size of data to reverse. + * \param[out] dest Buffer to store reversed binary data. + * \param[in] dest_size The size of the dest buffer. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_reversal(const uint8_t* bin, size_t bin_size, uint8_t* dest, size_t* dest_size) +{ + size_t last, i; + + // Verify the inputs + if ((bin == NULL) || (dest == NULL)) + { + return ATCA_BAD_PARAM; + } + + if (*dest_size < bin_size) + { + return ATCA_SMALL_BUFFER; + } + + last = bin_size - 1; + + for (i = 0; i < bin_size; i++) + { + dest[i] = bin[last]; + last--; + } + *dest_size = bin_size; + return ATCA_SUCCESS; +} + + +/** \brief Function that converts a binary buffer to a hex string suitable for + * easy reading. + * \param[in] bin Input data to convert. + * \param[in] bin_size Size of data to convert. + * \param[out] hex Buffer that receives hex string. + * \param[in,out] hex_size As input, the size of the hex buffer. + * As output, the size of the output hex. + * \param[in] is_pretty Indicates whether new lines should be + * added for pretty printing. + * \param[in] is_space Convert the output hex with space between it. + * \param[in] is_upper Convert the output hex to upper case. + + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_bin2hex_(const uint8_t* bin, size_t bin_size, char* hex, size_t* hex_size, bool is_pretty, bool is_space, bool is_upper) +{ + size_t i; + size_t cur_hex_size = 0; + size_t max_hex_size; + + // Verify the inputs + if (bin == NULL || hex == NULL || hex_size == NULL) + { + return ATCA_BAD_PARAM; + } + + max_hex_size = *hex_size; + *hex_size = 0; + + // Convert one byte at a time + for (i = 0; i < bin_size; i++) + { + if (cur_hex_size > max_hex_size) + { + break; + } + if (i != 0) + { + if (is_pretty && (i % 16 == 0)) + { + if (cur_hex_size + 2 > max_hex_size) + { + return ATCA_SMALL_BUFFER; + } + memcpy(&hex[cur_hex_size], "\r\n", 2); + cur_hex_size += 2; + } + else + { + if (is_space) + { + if (cur_hex_size + 1 > max_hex_size) + { + return ATCA_SMALL_BUFFER; + } + hex[cur_hex_size] = ' '; + cur_hex_size += 1; + } + } + } + + if (cur_hex_size + 2 > max_hex_size) + { + return ATCA_SMALL_BUFFER; + } + uint8_to_hex(bin[i], &hex[cur_hex_size]); + cur_hex_size += 2; + } + + if (is_upper) + { + hex_to_uppercase(hex, cur_hex_size); + } + else + { + hex_to_lowercase(hex, cur_hex_size); + } + + *hex_size = cur_hex_size; + if (cur_hex_size < max_hex_size) + { + // Since we have room add NULL as a convenience, but don't add to the + // output size. + hex[cur_hex_size] = 0; + } + + return ATCA_SUCCESS; +} + +inline static uint8_t hex_digit_to_num(char c) +{ + if (c >= '0' && c <= '9') + { + return (uint8_t)(c - '0'); + } + if (c >= 'a' && c <= 'f') + { + return (uint8_t)(c - 'a') + 10; + } + if (c >= 'A' && c <= 'F') + { + return (uint8_t)(c - 'A') + 10; + } + return 16; +} + + +ATCA_STATUS atcab_hex2bin_(const char* hex, size_t hex_size, uint8_t* bin, size_t* bin_size, bool is_space) +{ + size_t hex_index; + size_t bin_index = 0; + bool is_upper_nibble = true; + + for (hex_index = 0; hex_index < hex_size; hex_index++) + { + if (!isHexDigit(hex[hex_index])) + { + if (((hex_index + 1) % 3 == 0) && is_space) + { + if (hex[hex_index] != ' ') + { + return ATCA_BAD_PARAM; + } + } + + continue; // Skip any non-hex character + } + if (bin_index >= *bin_size) + { + return ATCA_SMALL_BUFFER; + } + + if (is_upper_nibble) + { + // Upper nibble + bin[bin_index] = (uint8_t)(hex_digit_to_num(hex[hex_index]) << 4); + } + else + { + // Lower nibble + bin[bin_index] += hex_digit_to_num(hex[hex_index]); + bin_index++; + } + is_upper_nibble = !is_upper_nibble; + } + if (!is_upper_nibble) + { + // Didn't end with an even number of hex digits. Assume it was malformed. + return ATCA_BAD_PARAM; + } + *bin_size = bin_index; + + return ATCA_SUCCESS; +} + +/** \brief Function that converts a hex string to binary buffer + * \param[in] hex Input buffer to convert + * \param[in] hex_size Length of buffer to convert + * \param[out] bin Buffer that receives binary + * \param[in,out] bin_size As input, the size of the bin buffer. + * As output, the size of the bin data. + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_hex2bin(const char* hex, size_t hex_size, uint8_t* bin, size_t* bin_size) +{ + return atcab_hex2bin_(hex, hex_size, bin, bin_size, false); +} + +/** + * \brief Checks to see if a character is an ASCII representation of a digit ((c ge '0') and (c le '9')) + * \param[in] c character to check + * \return True if the character is a digit + */ +bool isDigit(char c) +{ + return (c >= '0') && (c <= '9'); +} + +/** + * \brief Checks to see if a character is blank space + * \param[in] c character to check + * \return True if the character is blankspace + */ +bool isBlankSpace(char c) +{ + return (c == '\n') || (c == '\r') || (c == '\t') || (c == ' '); +} + +/** + * \brief Checks to see if a character is an ASCII representation of hex ((c >= 'A') and (c <= 'F')) || ((c >= 'a') and (c <= 'f')) + * \param[in] c character to check + * \return True if the character is a hex + */ +bool isAlpha(char c) +{ + return ((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z')); +} + +/** + * \brief Checks to see if a character is an ASCII representation of hex ((c >= 'A') and (c <= 'F')) || ((c >= 'a') and (c <= 'f')) + * \param[in] c character to check + * \return True if the character is a hex + */ +bool isHexAlpha(char c) +{ + return ((c >= 'A') && (c <= 'F')) || ((c >= 'a') && (c <= 'f')); +} + +/** + * \brief Returns true if this character is a valid hex character or if this is blankspace (The character can be + * included in a valid hexstring). + * \param[in] c character to check + * \return True if the character can be included in a valid hexstring + */ +bool isHex(char c) +{ + return isHexDigit(c) || isBlankSpace(c); +} + +/** + * \brief Returns true if this character is a valid hex character. + * \param[in] c character to check + * \return True if the character can be included in a valid hexstring + */ +bool isHexDigit(char c) +{ + return isDigit(c) || isHexAlpha(c); +} + +/** + * \brief Remove spaces from a ASCII hex string. + * \param[in] ascii_hex Initial hex string to remove blankspace from + * \param[in] ascii_hex_len Length of the initial hex string + * \param[in] packed_hex Resulting hex string without blankspace + * \param[in,out] packed_len In: Size to packed_hex buffer + * Out: Number of bytes in the packed hex string + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS packHex(const char* ascii_hex, size_t ascii_hex_len, char* packed_hex, size_t* packed_len) +{ + size_t i = 0; + size_t j = 0; + + // Verify the inputs + if ((ascii_hex == NULL) || (packed_hex == NULL) || (packed_len == NULL)) + { + return ATCA_BAD_PARAM; + } + // Loop through each character and only add the hex characters + for (i = 0; i < ascii_hex_len; i++) + { + if (isHexDigit(ascii_hex[i])) + { + if (j > *packed_len) + { + break; + } + packed_hex[j++] = ascii_hex[i]; + } + } + *packed_len = j; + + return ATCA_SUCCESS; +} + +#ifdef ATCA_PRINTF +/** \brief Print each hex character in the binary buffer with spaces between bytes + * \param[in] label label to print + * \param[in] binary input buffer to print + * \param[in] bin_len length of buffer to print + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_printbin_label(const char* label, uint8_t* binary, size_t bin_len) +{ + printf("%s", label); + return atcab_printbin(binary, bin_len, true); +} + +/** \brief Print each hex character in the binary buffer with spaces between bytes + * \param[in] binary input buffer to print + * \param[in] bin_len length of buffer to print + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_printbin_sp(uint8_t* binary, size_t bin_len) +{ + return atcab_printbin(binary, bin_len, true); +} + +/** \brief Print each hex character in the binary buffer + * \param[in] binary input buffer to print + * \param[in] bin_len length of buffer to print + * \param[in] add_space indicates whether spaces and returns should be added for pretty printing + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_printbin(uint8_t* binary, size_t bin_len, bool add_space) +{ + size_t i = 0; + size_t line_len = 16; + + // Verify the inputs + if (binary == NULL) + { + return ATCA_BAD_PARAM; + } + + // Set the line length + line_len = add_space ? 16 : 32; + + // Print the bytes + for (i = 0; i < bin_len; i++) + { + // Print the byte + if (add_space) + { + printf("%02X ", binary[i]); + } + else + { + printf("%02X", binary[i]); + } + + // Break at the line_len + if ((i + 1) % line_len == 0) + { + printf("\r\n"); + } + } + // Print the last carriage return + printf("\r\n"); + + return ATCA_SUCCESS; +} +#endif + +/////////////////////////////////////////////////////////////////////////////// +// Base 64 Encode/Decode + +#define B64_IS_EQUAL (uint8_t)64 +#define B64_IS_INVALID (uint8_t)0xFF + +/** + * \brief Returns true if this character is a valid base 64 character or if this is space (A character can be + * included in a valid base 64 string). + * \param[in] c character to check + * \param[in] rules base64 ruleset to use + * \return True if the character can be included in a valid base 64 string + */ +bool isBase64(char c, const uint8_t * rules) +{ + return isBase64Digit(c, rules) || isBlankSpace(c); +} + +/** + * \brief Returns true if this character is a valid base 64 character. + * \param[in] c character to check + * \param[in] rules base64 ruleset to use + * \return True if the character can be included in a valid base 64 string + */ +bool isBase64Digit(char c, const uint8_t * rules) +{ + return isDigit(c) || isAlpha(c) || c == rules[0] || c == rules[1] || c == rules[2]; +} + +/** + * \brief Returns the base 64 index of the given character. + * \param[in] c character to check + * \param[in] rules base64 ruleset to use + * \return the base 64 index of the given character + */ +uint8_t base64Index(char c, const uint8_t * rules) +{ + if ((c >= 'A') && (c <= 'Z')) + { + return (uint8_t)(c - 'A'); + } + if ((c >= 'a') && (c <= 'z')) + { + return (uint8_t)(26 + c - 'a'); + } + if ((c >= '0') && (c <= '9')) + { + return (uint8_t)(52 + c - '0'); + } + if (c == rules[0]) + { + return (uint8_t)62; + } + if (c == rules[1]) + { + return (uint8_t)63; + } + if (c == rules[2]) + { + return B64_IS_EQUAL; + } + return B64_IS_INVALID; +} + +/** + * \brief Returns the base 64 character of the given index. + * \param[in] id index to check + * \param[in] rules base64 ruleset to use + * \return the base 64 character of the given index + */ +char base64Char(uint8_t id, const uint8_t * rules) +{ + if (id < 26) + { + return (char)('A' + id); + } + if ((id >= 26) && (id < 52)) + { + return (char)('a' + id - 26); + } + if ((id >= 52) && (id < 62)) + { + return (char)('0' + id - 52); + } + if (id == 62) + { + return rules[0]; + } + if (id == 63) + { + return rules[1]; + } + + if (id == B64_IS_EQUAL) + { + return rules[2]; + } + return B64_IS_INVALID; +} + +static ATCA_STATUS atcab_base64decode_block(const uint8_t id[4], uint8_t* data, size_t* data_size, size_t data_max_size) +{ + ATCA_STATUS status = ATCA_SUCCESS; + size_t new_bytes = 0; + + do + { + // Make sure padding characters can only be the last two + if ((id[0] == B64_IS_EQUAL) || + (id[1] == B64_IS_EQUAL) || + (id[2] == B64_IS_EQUAL && id[3] != B64_IS_EQUAL)) + { + status = ATCA_TRACE(ATCA_BAD_PARAM, "Base64 chars after end padding"); + break; + } + + // Make sure output buffer has enough space + if (id[2] == B64_IS_EQUAL) + { + new_bytes = 1; + } + else if (id[3] == B64_IS_EQUAL) + { + new_bytes = 2; + } + else + { + new_bytes = 3; + } + if ((*data_size) + new_bytes > data_max_size) + { + status = ATCA_TRACE(ATCA_BAD_PARAM, "decoded buffer too small"); + break; + } + + // Decode into output buffer + data[(*data_size)++] = (uint8_t)((id[0] << 2) | (id[1] >> 4)); + if (id[2] == B64_IS_EQUAL) + { + break; + } + data[(*data_size)++] = (uint8_t)((id[1] << 4) | (id[2] >> 2)); + if (id[3] == B64_IS_EQUAL) + { + break; + } + data[(*data_size)++] = (uint8_t)((id[2] << 6) | id[3]); + } + while (false); + + return status; +} + +/** + * \brief Decode base64 string to data with ruleset option. + * + * \param[in] encoded Base64 string to be decoded. + * \param[in] encoded_size Size of the base64 string in bytes. + * \param[out] data Decoded data will be returned here. + * \param[in,out] data_size As input, the size of the byte_array buffer. + * As output, the length of the decoded data. + * \param[in] rules base64 ruleset to use + */ +ATCA_STATUS atcab_base64decode_(const char* encoded, size_t encoded_size, uint8_t* data, size_t* data_size, const uint8_t * rules) +{ + ATCA_STATUS status = ATCA_SUCCESS; + uint8_t id[4]; + int id_index = 0; + size_t enc_index = 0; + size_t data_max_size; + bool is_done = false; + + do + { + // Check the input parameters + if (encoded == NULL || data == NULL || data_size == NULL || rules == NULL) + { + status = ATCA_TRACE(ATCA_BAD_PARAM, "Null input parameter"); + break; + } + data_max_size = *data_size; + *data_size = 0; + + // Start decoding the input data + for (enc_index = 0; enc_index < encoded_size; enc_index++) + { + if (isBlankSpace(encoded[enc_index])) + { + continue; // Skip any empty characters + } + if (!isBase64Digit(encoded[enc_index], rules)) + { + status = ATCA_TRACE(ATCA_BAD_PARAM, "Invalid base64 character"); + break; + } + if (is_done) + { + // We found valid base64 characters after end padding (equals) + // characters + status = ATCA_TRACE(ATCA_BAD_PARAM, "Base64 chars after end padding"); + break; + } + id[id_index++] = base64Index(encoded[enc_index], rules); + // Process data 4 characters at a time + if (id_index >= 4) + { + id_index = 0; + status = atcab_base64decode_block(id, data, data_size, data_max_size); + if (status != ATCA_SUCCESS) + { + break; + } + + is_done = (id[3] == B64_IS_EQUAL); + } + } + if (status != ATCA_SUCCESS) + { + break; + } + if (id_index) + { + if (id_index < 2) + { + status = ATCA_TRACE(ATCA_BAD_PARAM, "Invalid number of base64 chars"); + break; + } + // End of base64 string, but no padding characters + for (; id_index < 4; id_index++) + { + id[id_index] = B64_IS_EQUAL; + } + status = atcab_base64decode_block(id, data, data_size, data_max_size); + } + } + while (false); + + return status; +} + +/** \brief Encode data as base64 string with ruleset option. */ +ATCA_STATUS atcab_base64encode_( + const uint8_t* data, /**< [in] The input byte array that will be converted to base 64 encoded characters */ + size_t data_size, /**< [in] The length of the byte array */ + char* encoded, /**< [in] The output converted to base 64 encoded characters. */ + size_t* encoded_size, /**< [inout] Input: The size of the encoded buffer, Output: The length of the encoded base 64 character string */ + const uint8_t * rules /**< [in] ruleset to use during encoding */ + ) +{ + ATCA_STATUS status = ATCA_SUCCESS; + size_t data_idx = 0; + size_t b64_idx = 0; + size_t offset = 0; + uint8_t id = 0; + size_t b64_len; + + do + { + // Check the input parameters + if (encoded == NULL || data == NULL || encoded_size == NULL || !rules) + { + status = ATCA_TRACE(ATCA_BAD_PARAM, "Null input parameter"); + break; + } + + // Calculate output length for buffer size check + b64_len = (data_size / 3 + (data_size % 3 != 0)) * 4; // ceil(size/3)*4 + if (rules[3]) + { + // We add newlines to the output + if (rules[3] % 4 != 0) + { + status = ATCA_TRACE(ATCA_BAD_PARAM, "newline rules[3] must be multiple of 4"); + break; + } + b64_len += (b64_len / rules[3]) * 2; + } + b64_len += 1; // terminating null + if (*encoded_size < b64_len) + { + status = ATCA_TRACE(ATCA_SMALL_BUFFER, "Length of encoded buffer too small"); + break; + } + // Initialize the return length to 0 + *encoded_size = 0; + + // Loop through the byte array by 3 then map to 4 base 64 encoded characters + for (data_idx = 0; data_idx < data_size; data_idx += 3) + { + // Add \r\n every n bytes if specified + if (rules[3] && data_idx > 0 && (b64_idx - offset) % rules[3] == 0) + { + // as soon as we do this, we introduce an offset + encoded[b64_idx++] = '\r'; + encoded[b64_idx++] = '\n'; + offset += 2; + } + + id = (data[data_idx] & 0xFC) >> 2; + encoded[b64_idx++] = base64Char(id, rules); + id = (uint8_t)((data[data_idx] & 0x03) << 4); + if (data_idx + 1 < data_size) + { + id |= (data[data_idx + 1] & 0xF0) >> 4; + encoded[b64_idx++] = base64Char(id, rules); + id = (uint8_t)((data[data_idx + 1] & 0x0F) << 2); + if (data_idx + 2 < data_size) + { + id |= (data[data_idx + 2] & 0xC0) >> 6; + encoded[b64_idx++] = base64Char(id, rules); + id = data[data_idx + 2] & 0x3F; + encoded[b64_idx++] = base64Char(id, rules); + } + else + { + encoded[b64_idx++] = base64Char(id, rules); + encoded[b64_idx++] = base64Char(B64_IS_EQUAL, rules); + } + } + else + { + encoded[b64_idx++] = base64Char(id, rules); + encoded[b64_idx++] = base64Char(B64_IS_EQUAL, rules); + encoded[b64_idx++] = base64Char(B64_IS_EQUAL, rules); + } + } + + // Strip any trailing nulls + while (b64_idx > 1 && encoded[b64_idx - 1] == 0) + { + b64_idx--; + } + + // Null terminate end + encoded[b64_idx++] = 0; + + // Set the final encoded length (excluding terminating null) + *encoded_size = b64_idx - 1; + } + while (false); + return status; +} + + +/** + * \brief Encode data as base64 string + * + * \param[in] byte_array Data to be encode in base64. + * \param[in] array_len Size of byte_array in bytes. + * \param[in] encoded Base64 output is returned here. + * \param[in,out] encoded_len As input, the size of the encoded buffer. + * As output, the length of the encoded base64 + * character string. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_base64encode(const uint8_t* byte_array, size_t array_len, char* encoded, size_t* encoded_len) +{ + return atcab_base64encode_(byte_array, array_len, encoded, encoded_len, atcab_b64rules_default); +} + +/** + * \brief Decode base64 string to data + * + * \param[in] encoded Base64 string to be decoded. + * \param[in] encoded_len Size of the base64 string in bytes. + * \param[out] byte_array Decoded data will be returned here. + * \param[in,out] array_len As input, the size of the byte_array buffer. + * As output, the length of the decoded data. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcab_base64decode(const char* encoded, size_t encoded_len, uint8_t* byte_array, size_t* array_len) +{ + return atcab_base64decode_(encoded, encoded_len, byte_array, array_len, atcab_b64rules_default); +} + +/** + * \brief Guaranteed to perform memory writes regardless of optimization level. Matches memset_s signature + */ +int atcab_memset_s(void* dest, size_t destsz, int ch, size_t count) +{ + if (dest == NULL) + { + return -1; + } + if (destsz > SIZE_MAX) + { + return -1; + } + if (count > destsz) + { + return -1; + } + + volatile unsigned char* p = dest; + while (destsz-- && count--) + { + *p++ = (uint8_t)ch; + } + + return 0; +} diff --git a/drivers/ecc108a/cryptoauthlib/atca_helpers.h b/drivers/ecc108a/cryptoauthlib/atca_helpers.h new file mode 100644 index 0000000..447f847 --- /dev/null +++ b/drivers/ecc108a/cryptoauthlib/atca_helpers.h @@ -0,0 +1,83 @@ +/** + * \file + * \brief Helpers to support the CryptoAuthLib Basic API methods + * + * \copyright (c) 2015-2020 Microchip Technology Inc. and its subsidiaries. + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip software + * and any derivatives exclusively with Microchip products. It is your + * responsibility to comply with third party license terms applicable to your + * use of third party software (including open source software) that may + * accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER + * EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, + * SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE + * OF ANY KIND WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF + * MICROCHIP HAS BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE + * FORESEEABLE. TO THE FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL + * LIABILITY ON ALL CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED + * THE AMOUNT OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR + * THIS SOFTWARE. + */ + +#ifndef ATCA_HELPERS_H_ +#define ATCA_HELPERS_H_ + +#include "cryptoauthlib.h" + +/** \ingroup atcab_ + * @{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +ATCA_STATUS atcab_printbin(uint8_t* binary, size_t bin_len, bool add_space); +ATCA_STATUS atcab_bin2hex(const uint8_t* bin, size_t bin_size, char* hex, size_t* hex_size); +ATCA_STATUS atcab_bin2hex_(const uint8_t* bin, size_t bin_size, char* hex, size_t* hex_size, bool is_pretty, bool is_space, bool is_upper); +ATCA_STATUS atcab_hex2bin(const char* ascii_hex, size_t ascii_hex_len, uint8_t* binary, size_t* bin_len); +ATCA_STATUS atcab_hex2bin_(const char* hex, size_t hex_size, uint8_t* bin, size_t* bin_size, bool is_space); +ATCA_STATUS atcab_printbin_sp(uint8_t* binary, size_t bin_len); +ATCA_STATUS atcab_printbin_label(const char* label, uint8_t* binary, size_t bin_len); + + +ATCA_STATUS packHex(const char* ascii_hex, size_t ascii_hex_len, char* packed_hex, size_t* packed_len); +bool isDigit(char c); +bool isBlankSpace(char c); +bool isAlpha(char c); +bool isHexAlpha(char c); +bool isHex(char c); +bool isHexDigit(char c); + +bool isBase64(char c, const uint8_t * rules); +bool isBase64Digit(char c, const uint8_t * rules); +uint8_t base64Index(char c, const uint8_t * rules); +char base64Char(uint8_t id, const uint8_t * rules); + +ATCA_DLL uint8_t atcab_b64rules_default[4]; +ATCA_DLL uint8_t atcab_b64rules_mime[4]; +ATCA_DLL uint8_t atcab_b64rules_urlsafe[4]; + +ATCA_STATUS atcab_base64decode_(const char* encoded, size_t encoded_size, uint8_t* data, size_t* data_size, const uint8_t * rules); +ATCA_STATUS atcab_base64decode(const char* encoded, size_t encoded_size, uint8_t* data, size_t* data_size); + +ATCA_STATUS atcab_base64encode_(const uint8_t* data, size_t data_size, char* encoded, size_t* encoded_size, const uint8_t * rules); +ATCA_STATUS atcab_base64encode(const uint8_t* data, size_t data_size, char* encoded, size_t* encoded_size); + + +ATCA_STATUS atcab_reversal(const uint8_t* bin, size_t bin_size, uint8_t* dest, size_t* dest_size); + +int atcab_memset_s(void* dest, size_t destsz, int ch, size_t count); + +#ifdef __cplusplus +} +#endif + +/** @} */ +#endif /* ATCA_HELPERS_H_ */ diff --git a/drivers/ecc108a/cryptoauthlib/atca_iface.c b/drivers/ecc108a/cryptoauthlib/atca_iface.c new file mode 100644 index 0000000..ab8aeac --- /dev/null +++ b/drivers/ecc108a/cryptoauthlib/atca_iface.c @@ -0,0 +1,438 @@ +/** + * \file + * + * \brief Microchip CryptoAuthLib hardware interface object + * + * \copyright (c) 2015-2020 Microchip Technology Inc. and its subsidiaries. + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip software + * and any derivatives exclusively with Microchip products. It is your + * responsibility to comply with third party license terms applicable to your + * use of third party software (including open source software) that may + * accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER + * EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, + * SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE + * OF ANY KIND WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF + * MICROCHIP HAS BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE + * FORESEEABLE. TO THE FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL + * LIABILITY ON ALL CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED + * THE AMOUNT OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR + * THIS SOFTWARE. + */ + +#include "cryptoauthlib.h" + +/** \defgroup interface ATCAIface (atca_) + * \brief Abstract interface to all CryptoAuth device types. This interface + * connects to the HAL implementation and abstracts the physical details of the + * device communication from all the upper layers of CryptoAuthLib + @{ */ + + +/** \brief Initializer for ATCAIface objects + * \param[in] cfg Logical configuration for the interface + * \param[in] ca_iface Interface structure to initialize. + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS initATCAIface(ATCAIfaceCfg *cfg, ATCAIface ca_iface) +{ + ATCA_STATUS status; + + if (cfg == NULL || ca_iface == NULL) + { + return ATCA_BAD_PARAM; + } + + ca_iface->mIfaceCFG = cfg; + + status = ATCA_TRACE(atinit(ca_iface), "atinit"); + if (status != ATCA_SUCCESS) + { + return status; + } + + return ATCA_SUCCESS; +} + +#ifndef ATCA_NO_HEAP +/** \brief Constructor for ATCAIface objects + * \param[in] cfg Logical configuration for the interface + * \return New interface instance on success. NULL on failure. + */ +ATCAIface newATCAIface(ATCAIfaceCfg *cfg) +{ + ATCAIface ca_iface; + ATCA_STATUS status; + + ca_iface = (ATCAIface)hal_malloc(sizeof(struct atca_iface)); + status = initATCAIface(cfg, ca_iface); + if (status != ATCA_SUCCESS) + { + hal_free(ca_iface); + ca_iface = NULL; + return NULL; + } + + return ca_iface; +} +#endif + +/** \brief Performs the HAL initialization by calling intermediate HAL wrapper + * function. If using the basic API, the atcab_init() function should + * be called instead. + * \param[in] ca_iface Device to interact with. + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atinit(ATCAIface ca_iface) +{ + ATCA_STATUS status = ATCA_COMM_FAIL; + + if (ca_iface) + { + status = ATCA_TRACE(hal_iface_init(ca_iface->mIfaceCFG, &ca_iface->hal, &ca_iface->phy), "Failed to configure HAL"); + + /* Initialize the physical interface if one is required for the hal */ + if (ATCA_SUCCESS == status && ca_iface->phy) + { + if (ca_iface->phy->halinit && ca_iface->phy->halpostinit) + { + if (ATCA_SUCCESS == (status = ATCA_TRACE(ca_iface->phy->halinit(ca_iface, ca_iface->mIfaceCFG), "phyinit"))) + { + status = ATCA_TRACE(ca_iface->phy->halpostinit(ca_iface), "phypostinit"); + } + } + else + { + status = ATCA_TRACE(ATCA_ASSERT_FAILURE, "phy is invalid"); + } + } + + /* Initialize the hal itself */ + if (ATCA_SUCCESS == status) + { + if (ca_iface->hal->halinit && ca_iface->hal->halpostinit) + { + if (ATCA_SUCCESS == (status = ATCA_TRACE(ca_iface->hal->halinit(ca_iface, ca_iface->mIfaceCFG), "halinit"))) + { + status = ATCA_TRACE(ca_iface->hal->halpostinit(ca_iface), "halpostinit"); + } + } + else + { + status = ATCA_TRACE(ATCA_ASSERT_FAILURE, "hal is invalid"); + } + } + } + + return status; +} + +/** \brief Sends the data to the device by calling intermediate HAL wrapper + * function. + * \param[in] ca_iface Device to interact with. + * \param[in] word_address device transaction type + * \param[in] txdata Data to be transmitted to the device. + * \param[in] txlength Number of bytes to be transmitted to the device. + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atsend(ATCAIface ca_iface, uint8_t address, uint8_t *txdata, int txlength) +{ + if (!ca_iface) + { + return ATCA_BAD_PARAM; + } + + if (ca_iface->hal && ca_iface->hal->halsend) + { +#ifdef ATCA_HAL_I2C + if (ATCA_I2C_IFACE == ca_iface->mIfaceCFG->iface_type && 0xFF == address) + { +#ifdef ATCA_ENABLE_DEPRECATED + address = ca_iface->mIfaceCFG->atcai2c.slave_address; +#else + address = ca_iface->mIfaceCFG->atcai2c.address; +#endif + } +#endif + + return ca_iface->hal->halsend(ca_iface, address, txdata, txlength); + } + else + { + return ATCA_NOT_INITIALIZED; + } +} + +/** \brief Receives data from the device by calling intermediate HAL wrapper + * function. + * \param[in] ca_iface Device to interact with. + * \param[in] word_address device transaction type + * \param[out] rxdata Data received will be returned here. + * \param[in,out] rxlength As input, the size of the rxdata buffer. + * As output, the number of bytes received. + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atreceive(ATCAIface ca_iface, uint8_t word_address, uint8_t *rxdata, uint16_t *rxlength) +{ + if (!ca_iface) + { + return ATCA_BAD_PARAM; + } + + if (ca_iface->hal && ca_iface->hal->halreceive) + { + return ca_iface->hal->halreceive(ca_iface, word_address, rxdata, rxlength); + } + else + { + return ATCA_NOT_INITIALIZED; + } +} + + +/** \brief Perform control operations with the underlying hal driver + * \param[in] ca_iface Device to interact with. + * \param[in] option Control parameter identifier + * \param[in] param Optional pointer to parameter value + * \param[in] paramlen Length of the parameter + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atcontrol(ATCAIface ca_iface, uint8_t option, void* param, size_t paramlen) +{ + if (!ca_iface) + { + return ATCA_BAD_PARAM; + } + + + if (ca_iface->hal && ca_iface->hal->halcontrol) + { + return ca_iface->hal->halcontrol(ca_iface, option, param, paramlen); + } + else + { + return ATCA_NOT_INITIALIZED; + } +} + +/** \brief Wakes up the device by calling intermediate HAL wrapper function. The + * atcab_wakeup() function should be used instead. + * \deprecated This function does not have defined behavior when ATCA_HAL_LEGACY_API + * is undefined. + * + * \param[in] ca_iface Device to interact with. + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atwake(ATCAIface ca_iface) +{ + if (!ca_iface) + { + return ATCA_BAD_PARAM; + } + + + if (ca_iface->hal && ca_iface->hal->halcontrol) + { + ATCA_STATUS status = ca_iface->hal->halcontrol(ca_iface, ATCA_HAL_CONTROL_WAKE, NULL, 0); + + if (ATCA_WAKE_FAILED == status) + { + // The device might be performing a POST. Wait for it to complete + // and try again. + atca_delay_ms(ATCA_POST_DELAY_MSEC); + + status = ca_iface->hal->halcontrol(ca_iface, ATCA_HAL_CONTROL_WAKE, NULL, 0); + } + return status; + } + else + { + return ATCA_NOT_INITIALIZED; + } +} + + +/** \brief Puts the device into idle state by calling intermediate HAL wrapper + * function. The atcab_idle() function should be used instead. + * \deprecated This function does not have defined behavior when ATCA_HAL_LEGACY_API + * is undefined. + * \param[in] ca_iface Device to interact with. + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atidle(ATCAIface ca_iface) +{ + if (!ca_iface) + { + return ATCA_BAD_PARAM; + } + + if (ca_iface->hal && ca_iface->hal->halcontrol) + { + ATCA_STATUS status = ca_iface->hal->halcontrol(ca_iface, ATCA_HAL_CONTROL_IDLE, NULL, 0); + atca_delay_ms(1); + return status; + } + else + { + return ATCA_NOT_INITIALIZED; + } +} + +/** \brief Puts the device into sleep state by calling intermediate HAL wrapper + * function. The atcab_sleep() function should be used instead. + * \deprecated This function does not have defined behavior when ATCA_HAL_LEGACY_API + * is undefined. + * \param[in] ca_iface Device to interact with. + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atsleep(ATCAIface ca_iface) +{ + if (!ca_iface) + { + return ATCA_BAD_PARAM; + } + + if (ca_iface->hal && ca_iface->hal->halcontrol) + { + ATCA_STATUS status = ca_iface->hal->halcontrol(ca_iface, ATCA_HAL_CONTROL_SLEEP, NULL, 0); + atca_delay_ms(1); + return status; + } + else + { + return ATCA_NOT_INITIALIZED; + } +} + + +/** \brief Returns the logical interface configuration for the device. + * \param[in] ca_iface Device interface. + * \return Logical interface configuration. + */ +ATCAIfaceCfg * atgetifacecfg(ATCAIface ca_iface) +{ + return ca_iface ? ca_iface->mIfaceCFG : NULL; +} + + +/** \brief Returns the HAL data pointer for the device. + * \param[in] ca_iface Device interface. + * \return HAL data pointer. + */ +void* atgetifacehaldat(ATCAIface ca_iface) +{ + return ca_iface ? ca_iface->hal_data : NULL; +} + +/** \brief Check if the given interface is configured as a "kit protocol" one where + * transactions are atomic + * \return true if the interface is considered a kit + */ +bool atca_iface_is_kit(ATCAIface ca_iface) +{ + bool ret = false; + + if (ca_iface && ca_iface->mIfaceCFG) + { + if (ATCA_HID_IFACE == ca_iface->mIfaceCFG->iface_type || ATCA_KIT_IFACE == ca_iface->mIfaceCFG->iface_type + || ATCA_UART_IFACE == ca_iface->mIfaceCFG->iface_type) + { + ret = true; + } + } + return ret; +} + +/** \brief Check if the given interface is configured as a SWI + * \return true if the interface is considered a kit + */ +bool atca_iface_is_swi(ATCAIface ca_iface) +{ + bool ret = false; + + if (ca_iface && ca_iface->mIfaceCFG) + { + if (ATCA_SWI_IFACE == ca_iface->mIfaceCFG->iface_type || ATCA_SWI_GPIO_IFACE == ca_iface->mIfaceCFG->iface_type) + { + ret = true; + } + } + return ret; +} + +/** \brief Retrive the number of retries for a configured interface */ +int atca_iface_get_retries(ATCAIface ca_iface) +{ + if (ca_iface && ca_iface->mIfaceCFG) + { + return ca_iface->mIfaceCFG->rx_retries; + } + else + { + return 0; + } +} + +/** \brief Retrive the wake/retry delay for a configured interface/device */ +uint16_t atca_iface_get_wake_delay(ATCAIface ca_iface) +{ + if (ca_iface && ca_iface->mIfaceCFG) + { + return ca_iface->mIfaceCFG->wake_delay; + } + else + { + return 1500; + } +} + + +/** \brief Instruct the HAL driver to release any resources associated with + * this interface. + * \param[in] ca_iface Device interface. + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS releaseATCAIface(ATCAIface ca_iface) +{ + ATCA_STATUS status = ATCA_BAD_PARAM; + + if (ca_iface && ca_iface->mIfaceCFG) + { + if (ATCA_SUCCESS == (status = hal_iface_release(ca_iface->mIfaceCFG->iface_type, ca_iface->hal_data))) + { + ca_iface->hal_data = NULL; + } + if (ATCA_CUSTOM_IFACE == ca_iface->mIfaceCFG->iface_type) + { +#ifndef ATCA_NO_HEAP + hal_free(ca_iface->hal); +#endif + ca_iface->hal = NULL; + } + } + return status; +} + +#ifndef ATCA_NO_HEAP +/** \brief Instruct the HAL driver to release any resources associated with + * this interface, then delete the object. + * \param[in] ca_iface Device interface. + */ +void deleteATCAIface(ATCAIface *ca_iface) +{ + if (ca_iface) + { + releaseATCAIface(*ca_iface); + hal_free(*ca_iface); + *ca_iface = NULL; + } +} +#endif + +/** @} */ diff --git a/drivers/ecc108a/cryptoauthlib/atca_iface.h b/drivers/ecc108a/cryptoauthlib/atca_iface.h new file mode 100644 index 0000000..1cb80ea --- /dev/null +++ b/drivers/ecc108a/cryptoauthlib/atca_iface.h @@ -0,0 +1,215 @@ +/** + * \file + * + * \brief Microchip Crypto Auth hardware interface object + * + * \copyright (c) 2015-2020 Microchip Technology Inc. and its subsidiaries. + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip software + * and any derivatives exclusively with Microchip products. It is your + * responsibility to comply with third party license terms applicable to your + * use of third party software (including open source software) that may + * accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER + * EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, + * SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE + * OF ANY KIND WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF + * MICROCHIP HAS BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE + * FORESEEABLE. TO THE FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL + * LIABILITY ON ALL CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED + * THE AMOUNT OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR + * THIS SOFTWARE. + */ + +#ifndef ATCA_IFACE_H +#define ATCA_IFACE_H +/*lint +flb */ + +/** \defgroup interface ATCAIface (atca_) + * \brief Abstract interface to all CryptoAuth device types. This interface + * connects to the HAL implementation and abstracts the physical details of the + * device communication from all the upper layers of CryptoAuthLib + @{ */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include "atca_config.h" +#include "atca_devtypes.h" +#include "atca_status.h" + + +typedef enum +{ + ATCA_I2C_IFACE = 0, /**< Native I2C Driver */ + ATCA_SWI_IFACE = 1, /**< SWI or 1-Wire over UART/USART */ + ATCA_UART_IFACE = 2, /**< Kit v1 over UART/USART */ + ATCA_SPI_IFACE = 3, /**< Native SPI Driver */ + ATCA_HID_IFACE = 4, /**< Kit v1 over HID */ + ATCA_KIT_IFACE = 5, /**< Kit v2 (Binary/Bridging) */ + ATCA_CUSTOM_IFACE = 6, /**< Custom HAL functions provided during interface init */ + ATCA_I2C_GPIO_IFACE = 7, /**< I2C "Bitbang" Driver */ + ATCA_SWI_GPIO_IFACE = 8, /**< SWI or 1-Wire using a GPIO */ + ATCA_SPI_GPIO_IFACE = 9, /**< SWI or 1-Wire using a GPIO */ + + // additional physical interface types here + ATCA_UNKNOWN_IFACE = 0xFE +} ATCAIfaceType; + + +/*The types are used within the kit protocol to identify the correct interface*/ +typedef enum +{ ATCA_KIT_AUTO_IFACE, //Selects the first device if the Kit interface is not defined + ATCA_KIT_I2C_IFACE, + ATCA_KIT_SWI_IFACE, + ATCA_KIT_SPI_IFACE, + ATCA_KIT_UNKNOWN_IFACE } ATCAKitType; + + +/* ATCAIfaceCfg is the configuration object for a device + */ + +typedef struct +{ + + ATCAIfaceType iface_type; // active iface - how to interpret the union below + ATCADeviceType devtype; // explicit device type + + union // each instance of an iface cfg defines a single type of interface + { + struct + { +#ifdef ATCA_ENABLE_DEPRECATED + uint8_t slave_address; // 8-bit slave address +#else + uint8_t address; /**< Device address - the upper 7 bits are the I2c address bits */ +#endif + uint8_t bus; // logical i2c bus number, 0-based - HAL will map this to a pin pair for SDA SCL + uint32_t baud; // typically 400000 + } atcai2c; + + struct + { + uint8_t address; // 7-bit device address + uint8_t bus; // logical SWI bus - HAL will map this to a pin or uart port + } atcaswi; + + struct + { + uint8_t bus; // logical i2c bus number, 0-based - HAL will map this to a spi pheripheral + uint8_t select_pin; // CS pin line typically 0 + uint32_t baud; // typically 16000000 + } atcaspi; + + struct + { + ATCAKitType dev_interface; // Kit interface type + uint8_t dev_identity; // I2C address for the I2C interface device or the bus number for the SWI interface device. + uint8_t port; // Port numbers where supported - otherwise accept the device through config data + uint32_t baud; // typically 115200 + uint8_t wordsize; // usually 8 + uint8_t parity; // 0 == even, 1 == odd, 2 == none + uint8_t stopbits; // 0,1,2 + } atcauart; + + struct + { + int idx; // HID enumeration index + ATCAKitType dev_interface; // Kit interface type + uint8_t dev_identity; // I2C address for the I2C interface device or the bus number for the SWI interface device. + uint32_t vid; // Vendor ID of kit (0x03EB for CK101) + uint32_t pid; // Product ID of kit (0x2312 for CK101) + uint32_t packetsize; // Size of the USB packet + } atcahid; + + struct + { + ATCAKitType dev_interface; // Target Bus Type + uint8_t dev_identity; // Target device identity + uint32_t flags; + } atcakit; + + struct + { + ATCA_STATUS (*halinit)(void *hal, void *cfg); + ATCA_STATUS (*halpostinit)(void *iface); + ATCA_STATUS (*halsend)(void *iface, uint8_t word_address, uint8_t *txdata, int txlength); + ATCA_STATUS (*halreceive)(void *iface, uint8_t word_address, uint8_t* rxdata, uint16_t* rxlength); + ATCA_STATUS (*halwake)(void *iface); + ATCA_STATUS (*halidle)(void *iface); + ATCA_STATUS (*halsleep)(void *iface); + ATCA_STATUS (*halrelease)(void* hal_data); + } atcacustom; + + }; + + uint16_t wake_delay; // microseconds of tWHI + tWLO which varies based on chip type + int rx_retries; // the number of retries to attempt for receiving bytes + void * cfg_data; // opaque data used by HAL in device discovery +} ATCAIfaceCfg; + + + +typedef struct atca_iface * ATCAIface; + +/** \brief HAL Driver Structure + */ +typedef struct +{ + ATCA_STATUS (*halinit)(ATCAIface iface, ATCAIfaceCfg* cfg); + ATCA_STATUS (*halpostinit)(ATCAIface iface); + ATCA_STATUS (*halsend)(ATCAIface iface, uint8_t word_address, uint8_t* txdata, int txlength); + ATCA_STATUS (*halreceive)(ATCAIface iface, uint8_t word_address, uint8_t* rxdata, uint16_t* rxlength); + ATCA_STATUS (*halcontrol)(ATCAIface iface, uint8_t option, void* param, size_t paramlen); + ATCA_STATUS (*halrelease)(void* hal_data); +} ATCAHAL_t; + +/** \brief atca_iface is the context structure for a configured interface + */ +typedef struct atca_iface +{ + ATCAIfaceCfg* mIfaceCFG; /**< Points to previous defined/given Cfg object, the caller manages this */ + ATCAHAL_t* hal; /**< The configured HAL for the interface */ + ATCAHAL_t* phy; /**< When a HAL is not a "native" hal it needs a physical layer to be associated with it */ + void* hal_data; /**< Pointer to HAL specific context/data */ +} atca_iface_t; + +ATCA_STATUS initATCAIface(ATCAIfaceCfg *cfg, ATCAIface ca_iface); +ATCAIface newATCAIface(ATCAIfaceCfg *cfg); +ATCA_STATUS releaseATCAIface(ATCAIface ca_iface); +void deleteATCAIface(ATCAIface *ca_iface); + +// IFace methods +ATCA_STATUS atinit(ATCAIface ca_iface); +ATCA_STATUS atsend(ATCAIface ca_iface, uint8_t word_address, uint8_t *txdata, int txlength); +ATCA_STATUS atreceive(ATCAIface ca_iface, uint8_t word_address, uint8_t *rxdata, uint16_t *rxlength); +ATCA_STATUS atcontrol(ATCAIface ca_iface, uint8_t option, void* param, size_t paramlen); +ATCA_STATUS atwake(ATCAIface ca_iface); +ATCA_STATUS atidle(ATCAIface ca_iface); +ATCA_STATUS atsleep(ATCAIface ca_iface); + +// accessors +ATCAIfaceCfg * atgetifacecfg(ATCAIface ca_iface); +void* atgetifacehaldat(ATCAIface ca_iface); + +/* Utilities */ +bool atca_iface_is_kit(ATCAIface ca_iface); +bool atca_iface_is_swi(ATCAIface ca_iface); +int atca_iface_get_retries(ATCAIface ca_iface); +uint16_t atca_iface_get_wake_delay(ATCAIface ca_iface); + +#ifdef __cplusplus +} +#endif +/*lint -flb*/ +/** @} */ +#endif diff --git a/drivers/ecc108a/cryptoauthlib/atca_status.h b/drivers/ecc108a/cryptoauthlib/atca_status.h new file mode 100644 index 0000000..b3ee1cb --- /dev/null +++ b/drivers/ecc108a/cryptoauthlib/atca_status.h @@ -0,0 +1,91 @@ +/** + * \file + * + * \brief Microchip Crypto Auth status codes + * + * \copyright (c) 2015-2020 Microchip Technology Inc. and its subsidiaries. + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip software + * and any derivatives exclusively with Microchip products. It is your + * responsibility to comply with third party license terms applicable to your + * use of third party software (including open source software) that may + * accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER + * EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, + * SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE + * OF ANY KIND WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF + * MICROCHIP HAS BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE + * FORESEEABLE. TO THE FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL + * LIABILITY ON ALL CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED + * THE AMOUNT OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR + * THIS SOFTWARE. + */ + +#ifndef _ATCA_STATUS_H +#define _ATCA_STATUS_H + +#include +#include "atca_bool.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* all status codes for the ATCA lib are defined here */ + +typedef enum +{ + ATCA_SUCCESS = 0x00, //!< Function succeeded. + ATCA_CONFIG_ZONE_LOCKED = 0x01, + ATCA_DATA_ZONE_LOCKED = 0x02, + ATCA_INVALID_POINTER, + ATCA_INVALID_LENGTH, + ATCA_WAKE_FAILED = 0xD0, //!< response status byte indicates CheckMac failure (status byte = 0x01) + ATCA_CHECKMAC_VERIFY_FAILED = 0xD1, //!< response status byte indicates CheckMac failure (status byte = 0x01) + ATCA_PARSE_ERROR = 0xD2, //!< response status byte indicates parsing error (status byte = 0x03) + ATCA_STATUS_CRC = 0xD4, //!< response status byte indicates DEVICE did not receive data properly (status byte = 0xFF) + ATCA_STATUS_UNKNOWN = 0xD5, //!< response status byte is unknown + ATCA_STATUS_ECC = 0xD6, //!< response status byte is ECC fault (status byte = 0x05) + ATCA_STATUS_SELFTEST_ERROR = 0xD7, //!< response status byte is Self Test Error, chip in failure mode (status byte = 0x07) + ATCA_FUNC_FAIL = 0xE0, //!< Function could not execute due to incorrect condition / state. + ATCA_GEN_FAIL = 0xE1, //!< unspecified error + ATCA_BAD_PARAM = 0xE2, //!< bad argument (out of range, null pointer, etc.) + ATCA_INVALID_ID = 0xE3, //!< invalid device id, id not set + ATCA_INVALID_SIZE = 0xE4, //!< Count value is out of range or greater than buffer size. + ATCA_RX_CRC_ERROR = 0xE5, //!< CRC error in data received from device + ATCA_RX_FAIL = 0xE6, //!< Timed out while waiting for response. Number of bytes received is > 0. + ATCA_RX_NO_RESPONSE = 0xE7, //!< Not an error while the Command layer is polling for a command response. + ATCA_RESYNC_WITH_WAKEUP = 0xE8, //!< Re-synchronization succeeded, but only after generating a Wake-up + ATCA_PARITY_ERROR = 0xE9, //!< for protocols needing parity + ATCA_TX_TIMEOUT = 0xEA, //!< for Microchip PHY protocol, timeout on transmission waiting for master + ATCA_RX_TIMEOUT = 0xEB, //!< for Microchip PHY protocol, timeout on receipt waiting for master + ATCA_TOO_MANY_COMM_RETRIES = 0xEC, //!< Device did not respond too many times during a transmission. Could indicate no device present. + ATCA_SMALL_BUFFER = 0xED, //!< Supplied buffer is too small for data required + ATCA_COMM_FAIL = 0xF0, //!< Communication with device failed. Same as in hardware dependent modules. + ATCA_TIMEOUT = 0xF1, //!< Timed out while waiting for response. Number of bytes received is 0. + ATCA_BAD_OPCODE = 0xF2, //!< opcode is not supported by the device + ATCA_WAKE_SUCCESS = 0xF3, //!< received proper wake token + ATCA_EXECUTION_ERROR = 0xF4, //!< chip was in a state where it could not execute the command, response status byte indicates command execution error (status byte = 0x0F) + ATCA_UNIMPLEMENTED = 0xF5, //!< Function or some element of it hasn't been implemented yet + ATCA_ASSERT_FAILURE = 0xF6, //!< Code failed run-time consistency check + ATCA_TX_FAIL = 0xF7, //!< Failed to write + ATCA_NOT_LOCKED = 0xF8, //!< required zone was not locked + ATCA_NO_DEVICES = 0xF9, //!< For protocols that support device discovery (kit protocol), no devices were found + ATCA_HEALTH_TEST_ERROR = 0xFA, //!< random number generator health test error + ATCA_ALLOC_FAILURE = 0xFB, //!< Couldn't allocate required memory + ATCA_USE_FLAGS_CONSUMED = 0xFC, //!< Use flags on the device indicates its consumed fully + ATCA_NOT_INITIALIZED = 0xFD, //!< The library has not been initialized so the command could not be executed +} ATCA_STATUS; + +#define ATCA_STATUS_AUTH_BIT 0x40 + +#ifdef __cplusplus +} +#endif +#endif diff --git a/drivers/ecc108a/cryptoauthlib/atca_utils_sizes.c b/drivers/ecc108a/cryptoauthlib/atca_utils_sizes.c new file mode 100644 index 0000000..7bf9686 --- /dev/null +++ b/drivers/ecc108a/cryptoauthlib/atca_utils_sizes.c @@ -0,0 +1,109 @@ +/** + * \file + * \brief API to Return structure sizes of cryptoauthlib structures + * + * \copyright (c) 2015-2020 Microchip Technology Inc. and its subsidiaries. + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip software + * and any derivatives exclusively with Microchip products. It is your + * responsibility to comply with third party license terms applicable to your + * use of third party software (including open source software) that may + * accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER + * EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, + * SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE + * OF ANY KIND WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF + * MICROCHIP HAS BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE + * FORESEEABLE. TO THE FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL + * LIABILITY ON ALL CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED + * THE AMOUNT OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR + * THIS SOFTWARE. + */ + +#include "cryptoauthlib.h" + +#define SIZE_OF_API_T(x) size_t x ## _size(void); size_t x ## _size(void) { return sizeof( x ); } +#define SIZE_OF_API_S(x) size_t x ## _size(void); size_t x ## _size(void) { return sizeof(struct x ); } + +#ifndef ATCA_TIDAL +#if ATCA_CA_SUPPORT +#include "atcacert/atcacert_date.h" +#include "atcacert/atcacert_def.h" +/* atcacert_date.h */ +SIZE_OF_API_T(atcacert_tm_utc_t) +SIZE_OF_API_T(atcacert_date_format_t) + +/* atcacert_def.h */ +SIZE_OF_API_T(atcacert_cert_type_t) +SIZE_OF_API_T(atcacert_cert_sn_src_t) +SIZE_OF_API_T(atcacert_device_zone_t) +SIZE_OF_API_T(atcacert_std_cert_element_t) +SIZE_OF_API_T(atcacert_device_loc_t) +SIZE_OF_API_T(atcacert_cert_loc_t) +SIZE_OF_API_T(atcacert_cert_element_t) +SIZE_OF_API_T(atcacert_def_t) +SIZE_OF_API_T(atcacert_build_state_t) +#endif + +/* atcab.h */ +SIZE_OF_API_T(atca_aes_cbc_ctx_t) +SIZE_OF_API_T(atca_aes_cmac_ctx_t) +SIZE_OF_API_T(atca_aes_ctr_ctx_t) +#endif // ifndef ATCA_TIDAL + +#if ATCA_CA_SUPPORT +#include "host/atca_host.h" + +/* atca_host.h */ +SIZE_OF_API_T(atca_temp_key_t) +SIZE_OF_API_S(atca_include_data_in_out) +SIZE_OF_API_T(atca_nonce_in_out_t) +SIZE_OF_API_T(atca_io_decrypt_in_out_t) +SIZE_OF_API_T(atca_verify_mac_in_out_t) +SIZE_OF_API_T(atca_secureboot_enc_in_out_t) +SIZE_OF_API_T(atca_secureboot_mac_in_out_t) +SIZE_OF_API_T(atca_mac_in_out_t) +SIZE_OF_API_S(atca_hmac_in_out) +SIZE_OF_API_T(atca_gen_dig_in_out_t) +SIZE_OF_API_T(atca_write_mac_in_out_t) +SIZE_OF_API_S(atca_derive_key_in_out) +SIZE_OF_API_S(atca_derive_key_mac_in_out) +SIZE_OF_API_S(atca_decrypt_in_out) +SIZE_OF_API_T(atca_check_mac_in_out_t) +SIZE_OF_API_T(atca_verify_in_out_t) +SIZE_OF_API_T(atca_gen_key_in_out_t) +SIZE_OF_API_T(atca_sign_internal_in_out_t) +#endif + +/* atca_bool.h */ +SIZE_OF_API_T(bool) + +/* atca_command.h */ +#if ATCA_CA_SUPPORT +SIZE_OF_API_T(ATCAPacket) +#endif + +/* atca_device.h */ +SIZE_OF_API_S(atca_device) + +/* atca_devtypes.h */ +SIZE_OF_API_T(ATCADeviceType) + +/* calib_execution.h */ +#ifdef ATCA_NO_POLL +#include "calib/calib_execution.h" +SIZE_OF_API_T(device_execution_time_t) +#endif + +/* atca_iface.h */ +SIZE_OF_API_T(ATCAIfaceType) +SIZE_OF_API_T(ATCAIfaceCfg) +SIZE_OF_API_S(atca_iface) + +/* atca_status.h */ +SIZE_OF_API_T(ATCA_STATUS) diff --git a/drivers/ecc108a/cryptoauthlib/atca_version.h b/drivers/ecc108a/cryptoauthlib/atca_version.h new file mode 100644 index 0000000..0a8d8bc --- /dev/null +++ b/drivers/ecc108a/cryptoauthlib/atca_version.h @@ -0,0 +1,38 @@ +/** + * \file + * + * \brief Microchip CryptoAuth Library Version + * + * \copyright (c) 2015-2020 Microchip Technology Inc. and its subsidiaries. + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip software + * and any derivatives exclusively with Microchip products. It is your + * responsibility to comply with third party license terms applicable to your + * use of third party software (including open source software) that may + * accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER + * EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, + * SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE + * OF ANY KIND WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF + * MICROCHIP HAS BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE + * FORESEEABLE. TO THE FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL + * LIABILITY ON ALL CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED + * THE AMOUNT OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR + * THIS SOFTWARE. + */ + +#ifndef _ATCA_VERSION_H +#define _ATCA_VERSION_H + +// Version format yyyymmdd +#define ATCA_LIBRARY_VERSION_DATE "20211006" +#define ATCA_LIBRARY_VERSION_MAJOR 3 +#define ATCA_LIBRARY_VERSION_MINOR 3 +#define ATCA_LIBRARY_VERSION_BUILD 3 + +#endif /* _ATCA_VERSION_H */ diff --git a/drivers/ecc108a/cryptoauthlib/calib/README.md b/drivers/ecc108a/cryptoauthlib/calib/README.md new file mode 100644 index 0000000..0c4bdef --- /dev/null +++ b/drivers/ecc108a/cryptoauthlib/calib/README.md @@ -0,0 +1,17 @@ +calib directory - Purpose +========================= +The purpose of this directory is to contain the files implementing the APIs for +a basic interface to the core CryptoAuthLib library. + +High-level functions like these make it very convenient to use the library when +standard configurations and defaults are in play. They are the easiest to use +when developing examples or trying to understand the "flow" of an +authentication operation without getting overwhelmed by the details. + +This makes simple jobs easy and if you need more sophistication and power, you +can employ the full power of the CryptoAuthLib object model. + +See the Doxygen documentation in cryptoauthlib/docs for details on the API of +the calib commands. + +@ingroup calib_ diff --git a/drivers/ecc108a/cryptoauthlib/calib/calib_aes_gcm.h b/drivers/ecc108a/cryptoauthlib/calib/calib_aes_gcm.h new file mode 100644 index 0000000..c242f59 --- /dev/null +++ b/drivers/ecc108a/cryptoauthlib/calib/calib_aes_gcm.h @@ -0,0 +1,76 @@ +/** + * \file + * \brief Unity tests for the cryptoauthlib AES GCM functions. + * + * \copyright (c) 2015-2020 Microchip Technology Inc. and its subsidiaries. + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip software + * and any derivatives exclusively with Microchip products. It is your + * responsibility to comply with third party license terms applicable to your + * use of third party software (including open source software) that may + * accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER + * EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, + * SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE + * OF ANY KIND WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF + * MICROCHIP HAS BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE + * FORESEEABLE. TO THE FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL + * LIABILITY ON ALL CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED + * THE AMOUNT OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR + * THIS SOFTWARE. + */ +#ifndef CALIB_BASIC_AES_GCM_H_ +#define CALIB_BASIC_AES_GCM_H_ + +/** \ingroup atcab_ + * @{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#define ATCA_AES_GCM_IV_STD_LENGTH 12 + +extern const char* atca_basic_aes_gcm_version; + +/** Context structure for AES GCM operations. + */ + +typedef struct atca_aes_gcm_ctx +{ + uint16_t key_id; //!< Key location. Can either be a slot number or ATCA_TEMPKEY_KEYID for TempKey. + uint8_t key_block; //!< Index of the 16-byte block to use within the key location for the actual key. + uint8_t cb[AES_DATA_SIZE]; //!< Counter block, comprises of nonce + count value (16 bytes). + uint32_t data_size; //!< Size of the data being encrypted/decrypted in bytes. + uint32_t aad_size; //!< Size of the additional authenticated data in bytes. + uint8_t h[AES_DATA_SIZE]; //!< Subkey for ghash functions in GCM. + uint8_t j0[AES_DATA_SIZE]; //!< Precounter block generated from IV. + uint8_t y[AES_DATA_SIZE]; //!< Current GHASH output + uint8_t partial_aad[AES_DATA_SIZE]; //!< Partial blocks of data waiting to be processed + uint32_t partial_aad_size; //!< Amount of data in the partial block buffer + uint8_t enc_cb[AES_DATA_SIZE]; //!< Last encrypted counter block + uint8_t ciphertext_block[AES_DATA_SIZE]; //!< Last ciphertext block +} atca_aes_gcm_ctx_t; + +ATCA_STATUS calib_aes_gcm_init(ATCADevice device, atca_aes_gcm_ctx_t* ctx, uint16_t key_id, uint8_t key_block, const uint8_t* iv, size_t iv_size); +ATCA_STATUS calib_aes_gcm_init_rand(ATCADevice device, atca_aes_gcm_ctx_t* ctx, uint16_t key_id, uint8_t key_block, size_t rand_size, + const uint8_t* free_field, size_t free_field_size, uint8_t* iv); +ATCA_STATUS calib_aes_gcm_aad_update(ATCADevice device, atca_aes_gcm_ctx_t* ctx, const uint8_t* aad, uint32_t aad_size); +ATCA_STATUS calib_aes_gcm_encrypt_update(ATCADevice device, atca_aes_gcm_ctx_t* ctx, const uint8_t* plaintext, uint32_t plaintext_size, uint8_t* ciphertext); +ATCA_STATUS calib_aes_gcm_encrypt_finish(ATCADevice device, atca_aes_gcm_ctx_t* ctx, uint8_t* tag, size_t tag_size); +ATCA_STATUS calib_aes_gcm_decrypt_update(ATCADevice device, atca_aes_gcm_ctx_t* ctx, const uint8_t* ciphertext, uint32_t ciphertext_size, uint8_t* plaintext); +ATCA_STATUS calib_aes_gcm_decrypt_finish(ATCADevice device, atca_aes_gcm_ctx_t* ctx, const uint8_t* tag, size_t tag_size, bool* is_verified); + +#ifdef __cplusplus +} +#endif + +/** @} */ + +#endif diff --git a/drivers/ecc108a/cryptoauthlib/calib/calib_basic.c b/drivers/ecc108a/cryptoauthlib/calib/calib_basic.c new file mode 100644 index 0000000..cf37063 --- /dev/null +++ b/drivers/ecc108a/cryptoauthlib/calib/calib_basic.c @@ -0,0 +1,373 @@ +/** + * \file + * \brief CryptoAuthLib Basic API methods. These methods provide a simpler way + * to access the core crypto methods. + * + * \copyright (c) 2015-2020 Microchip Technology Inc. and its subsidiaries. + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip software + * and any derivatives exclusively with Microchip products. It is your + * responsibility to comply with third party license terms applicable to your + * use of third party software (including open source software) that may + * accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER + * EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, + * SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE + * OF ANY KIND WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF + * MICROCHIP HAS BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE + * FORESEEABLE. TO THE FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL + * LIABILITY ON ALL CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED + * THE AMOUNT OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR + * THIS SOFTWARE. + */ + +#include "cryptoauthlib.h" + +/** \brief basic API methods are all prefixed with atcab_ (CryptoAuthLib Basic) + * the fundamental premise of the basic API is it is based on a single interface + * instance and that instance is global, so all basic API commands assume that + * one global device is the one to operate on. + */ + +ATCA_STATUS calib_wakeup_i2c(ATCADevice device) +{ + ATCA_STATUS status = ATCA_BAD_PARAM; + uint8_t second_byte = 0x01; // I2C general call should not interpreted as an addr write + ATCAIface iface = atGetIFace(device); + + if (iface) + { + int retries = atca_iface_get_retries(iface); + uint8_t address = atcab_get_device_address(device); + uint32_t temp; + uint32_t wake; + uint16_t rxlen; + + do + { + if (100000UL < iface->mIfaceCFG->atcai2c.baud) + { + temp = 100000UL; + status = atcontrol(iface, ATCA_HAL_CHANGE_BAUD, &temp, sizeof(temp)); + if (ATCA_UNIMPLEMENTED == status) + { + status = atcontrol(iface, ATCA_HAL_CONTROL_WAKE, NULL, 0); + break; + } + } + else + { + status = ATCA_SUCCESS; + } + + #ifdef ATCA_ECC204_SUPPORT + if (ECC204 == atcab_get_device_type_ext(device)) + { + (void)atsend(iface, address, NULL, 0); + } + else + { + + (void)atsend(iface, 0x00, &second_byte, sizeof(second_byte)); + } + #else + (void)atsend(iface, 0x00, &second_byte, sizeof(second_byte)); + #endif + atca_delay_us(atca_iface_get_wake_delay(iface)); + + rxlen = sizeof(wake); + if (ATCA_SUCCESS == status) + { + status = atreceive(iface, address, (uint8_t*)&wake, &rxlen); + } + + if ((ATCA_SUCCESS == status) && (100000UL < iface->mIfaceCFG->atcai2c.baud)) + { + temp = iface->mIfaceCFG->atcai2c.baud; + status = atcontrol(iface, ATCA_HAL_CHANGE_BAUD, &temp, sizeof(temp)); + } + + if (ATCA_SUCCESS == status) + { + status = hal_check_wake((uint8_t*)&wake, rxlen); + } + } + while (0 < retries-- && ATCA_SUCCESS != status); + } + return status; +} + +/** \brief wakeup the CryptoAuth device + * \param[in] device Device context pointer + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_wakeup(ATCADevice device) +{ + ATCA_STATUS status = ATCA_BAD_PARAM; + ATCAIface iface = atGetIFace(device); + + if (iface && iface->mIfaceCFG) + { +#ifdef ATCA_HAL_LEGACY_API + status = atwake(iface); +#else + if (atca_iface_is_kit(iface) || atca_iface_is_swi(&device->mIface)) + { + status = atwake(iface); + } + else if (ATCA_I2C_IFACE == iface->mIfaceCFG->iface_type) + { + status = calib_wakeup_i2c(device); + } + else + { + status = ATCA_SUCCESS; + } +#endif + } + + return status; +} + +/** \brief idle the CryptoAuth device + * \param[in] device Device context pointer + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_idle(ATCADevice device) +{ + ATCA_STATUS status = ATCA_BAD_PARAM; + +#ifdef ATCA_HAL_LEGACY_API + status = atidle(&device->mIface); +#else + if (atca_iface_is_kit(&device->mIface) || atca_iface_is_swi(&device->mIface)) + { + status = atidle(&device->mIface); + } + else + { + if (ECC204 != atcab_get_device_type_ext(device)) + { + uint8_t command = 0x02; + status = atsend(&device->mIface, atcab_get_device_address(device), &command, 1); + } + else + { + status = ATCA_SUCCESS; + } + } +#endif + return status; +} + +/** \brief invoke sleep on the CryptoAuth device + * \param[in] device Device context pointer + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_sleep(ATCADevice device) +{ + ATCA_STATUS status = ATCA_BAD_PARAM; + +#ifdef ATCA_HAL_LEGACY_API + status = atsleep(&device->mIface); +#else + if (atca_iface_is_kit(&device->mIface) || atca_iface_is_swi(&device->mIface)) + { + status = atsleep(&device->mIface); + } + else + { + uint8_t command = 0x01; + status = atsend(&device->mIface, atcab_get_device_address(device), &command, 1); + } +#endif + return status; +} + +/** \brief common cleanup code which idles the device after any operation + * \param[in] device Device context pointer + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS _calib_exit(ATCADevice device) +{ + return calib_idle(device); +} + + +/** \brief Compute the address given the zone, slot, block, and offset + * \param[in] zone Zone to get address from. Config(0), OTP(1), or + * Data(2) which requires a slot. + * \param[in] slot Slot Id number for data zone and zero for other zones. + * \param[in] block Block number within the data or configuration or OTP zone . + * \param[in] offset Offset Number within the block of data or configuration or OTP zone. + * \param[out] addr Pointer to the address of data or configuration or OTP zone. + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_get_addr(uint8_t zone, uint16_t slot, uint8_t block, uint8_t offset, uint16_t* addr) +{ + ATCA_STATUS status = ATCA_SUCCESS; + uint8_t mem_zone = zone & 0x03; + + if (addr == NULL) + { + return ATCA_TRACE(ATCA_BAD_PARAM, "NULL pointer received"); + } + if ((mem_zone != ATCA_ZONE_CONFIG) && (mem_zone != ATCA_ZONE_DATA) && (mem_zone != ATCA_ZONE_OTP)) + { + return ATCA_TRACE(ATCA_BAD_PARAM, "Invalid zone received"); + } + do + { + // Initialize the addr to 00 + *addr = 0; + // Mask the offset + offset = offset & (uint8_t)0x07; + if ((mem_zone == ATCA_ZONE_CONFIG) || (mem_zone == ATCA_ZONE_OTP)) + { + *addr = ((uint16_t)block) << 3; + *addr |= offset; + } + else // ATCA_ZONE_DATA + { + *addr = slot << 3; + *addr |= offset; + *addr |= ((uint16_t)block) << 8; + } + } + while (0); + + return status; +} + +/** \brief Compute the address given the zone, slot, block, and offset for ECC204 device + * \param[in] zone Zone to get address from. Config(1) or + * Data(0) which requires a slot. + * \param[in] slot Slot Id number for data zone and zero for other zones. + * \param[in] block Block number within the data zone . + * \param[in] offset Aalways zero. + * \param[out] addr Pointer to the address of data or configuration zone. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_ecc204_get_addr(uint8_t zone, uint16_t slot, uint8_t block, uint8_t offset, uint16_t* addr) +{ + ATCA_STATUS status = ATCA_SUCCESS; + + ((void)zone); + ((void)offset); + + if (addr == NULL) + { + return ATCA_TRACE(ATCA_BAD_PARAM, "NULL pointer received"); + } + + // Initialize the addr to 00 + *addr = 0; + *addr = slot << 3; + *addr |= ((uint16_t)block) << 8; + + return status; +} + +/** \brief Gets the size of the specified zone in bytes. + * + * \param[in] device Device context pointer + * \param[in] zone Zone to get size information from. Config(0), OTP(1), or + * Data(2) which requires a slot. + * \param[in] slot If zone is Data(2), the slot to query for size. + * \param[out] size Zone size is returned here. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_get_zone_size(ATCADevice device, uint8_t zone, uint16_t slot, size_t* size) +{ + ATCA_STATUS status = ATCA_SUCCESS; + + if ((device == NULL) || (size == NULL)) + { + return ATCA_TRACE(ATCA_BAD_PARAM, "NULL pointer received"); + } + + if (device->mIface.mIfaceCFG->devtype == ATSHA204A) + { + switch (zone) + { + case ATCA_ZONE_CONFIG: *size = 88; break; + case ATCA_ZONE_OTP: *size = 64; break; + case ATCA_ZONE_DATA: *size = 32; break; + default: status = ATCA_TRACE(ATCA_BAD_PARAM, "Invalid zone received"); break; + } + } + else if (device->mIface.mIfaceCFG->devtype == ATSHA206A) + { + switch (zone) + { + case ATCA_ZONE_CONFIG: *size = 88; break; + case ATCA_ZONE_OTP: *size = 0; break; + case ATCA_ZONE_DATA: *size = 32; break; + default: status = ATCA_TRACE(ATCA_BAD_PARAM, "Invalid zone received"); break; + } + } +#ifdef ATCA_ECC204_SUPPORT + else if (ECC204 == device->mIface.mIfaceCFG->devtype) + { + switch (zone) + { + case ATCA_ZONE_CONFIG: *size = 64; break; + case ATCA_ZONE_DATA: + if ((0 == slot) || (3 == slot)) + { + *size = 32; + } + else if (1 == slot) + { + *size = 320; + } + else if (2 == slot) + { + *size = 64; + } + else + { + status = ATCA_TRACE(ATCA_BAD_PARAM, "Invalid slot received"); + } + break; + default: status = ATCA_TRACE(ATCA_BAD_PARAM, "Invalid zone received"); break; + } + } +#endif + else + { + switch (zone) + { + case ATCA_ZONE_CONFIG: *size = 128; break; + case ATCA_ZONE_OTP: *size = 64; break; + case ATCA_ZONE_DATA: + if (slot < 8) + { + *size = 36; + } + else if (slot == 8) + { + *size = 416; + } + else if (slot < 16) + { + *size = 72; + } + else + { + status = ATCA_TRACE(ATCA_BAD_PARAM, "Invalid slot received"); + } + break; + default: status = ATCA_TRACE(ATCA_BAD_PARAM, "Invalid zone received"); break; + } + } + + return status; +} diff --git a/drivers/ecc108a/cryptoauthlib/calib/calib_basic.h b/drivers/ecc108a/cryptoauthlib/calib/calib_basic.h new file mode 100644 index 0000000..f5d3c35 --- /dev/null +++ b/drivers/ecc108a/cryptoauthlib/calib/calib_basic.h @@ -0,0 +1,236 @@ +#ifndef _CALIB_H +#define _CALIB_H + +#include "calib_command.h" +#include "calib_execution.h" + +/** \defgroup calib_ Basic Crypto API methods for CryptoAuth Devices (calib_) + * + * \brief + * These methods provide a simple API to CryptoAuth chips + * + @{ */ + +#ifdef __cplusplus +extern "C" { +#endif + +ATCA_STATUS calib_wakeup(ATCADevice device); +ATCA_STATUS calib_idle(ATCADevice device); +ATCA_STATUS calib_sleep(ATCADevice device); +ATCA_STATUS _calib_exit(ATCADevice device); +ATCA_STATUS calib_get_addr(uint8_t zone, uint16_t slot, uint8_t block, uint8_t offset, uint16_t* addr); +ATCA_STATUS calib_get_zone_size(ATCADevice device, uint8_t zone, uint16_t slot, size_t* size); +ATCA_STATUS calib_ecc204_get_addr(uint8_t zone, uint16_t slot, uint8_t block, uint8_t offset, uint16_t* addr); + +/* Helper Functions */ +ATCA_STATUS calib_is_locked(ATCADevice device, uint8_t zone, bool* is_locked); +ATCA_STATUS calib_is_slot_locked(ATCADevice device, uint16_t slot, bool* is_locked); +ATCA_STATUS calib_is_private(ATCADevice device, uint16_t slot, bool* is_private); +ATCA_STATUS calib_ecc204_is_locked(ATCADevice device, uint8_t zone, bool* is_locked); +ATCA_STATUS calib_ecc204_is_data_locked(ATCADevice device, bool* is_locked); +ATCA_STATUS calib_ecc204_is_config_locked(ATCADevice device, bool* is_locked); + +//AES command functions +ATCA_STATUS calib_aes(ATCADevice device, uint8_t mode, uint16_t key_id, const uint8_t* aes_in, uint8_t* aes_out); +ATCA_STATUS calib_aes_encrypt(ATCADevice device, uint16_t key_id, uint8_t key_block, const uint8_t* plaintext, uint8_t* ciphertext); +ATCA_STATUS calib_aes_decrypt(ATCADevice device, uint16_t key_id, uint8_t key_block, const uint8_t* ciphertext, uint8_t* plaintext); +ATCA_STATUS calib_aes_gfm(ATCADevice device, const uint8_t* h, const uint8_t* input, uint8_t* output); + +//CheckMAC command functions +ATCA_STATUS calib_checkmac(ATCADevice device, uint8_t mode, uint16_t key_id, const uint8_t *challenge, const uint8_t *response, const uint8_t *other_data); + +// Counter command functions +ATCA_STATUS calib_counter(ATCADevice device, uint8_t mode, uint16_t counter_id, uint32_t* counter_value); +ATCA_STATUS calib_counter_increment(ATCADevice device, uint16_t counter_id, uint32_t* counter_value); +ATCA_STATUS calib_counter_read(ATCADevice device, uint16_t counter_id, uint32_t* counter_value); + +// DeriveKey command functions +ATCA_STATUS calib_derivekey(ATCADevice device, uint8_t mode, uint16_t key_id, const uint8_t* mac); + +// ECDH command functions +ATCA_STATUS calib_ecdh_base(ATCADevice device, uint8_t mode, uint16_t key_id, const uint8_t* public_key, uint8_t* pms, uint8_t* out_nonce); +ATCA_STATUS calib_ecdh(ATCADevice device, uint16_t key_id, const uint8_t* public_key, uint8_t* pms); + +#if defined(ATCA_USE_CONSTANT_HOST_NONCE) +ATCA_STATUS calib_ecdh_enc(ATCADevice device, uint16_t key_id, const uint8_t* public_key, uint8_t* pms, const uint8_t* read_key, uint16_t read_key_id); +#else +ATCA_STATUS calib_ecdh_enc(ATCADevice device, uint16_t key_id, const uint8_t* public_key, uint8_t* pms, const uint8_t* read_key, uint16_t read_key_id, const uint8_t num_in[NONCE_NUMIN_SIZE]); +#endif + +ATCA_STATUS calib_ecdh_ioenc(ATCADevice device, uint16_t key_id, const uint8_t* public_key, uint8_t* pms, const uint8_t* io_key); +ATCA_STATUS calib_ecdh_tempkey(ATCADevice device, const uint8_t* public_key, uint8_t* pms); +ATCA_STATUS calib_ecdh_tempkey_ioenc(ATCADevice device, const uint8_t* public_key, uint8_t* pms, const uint8_t* io_key); + +// GenDig command functions +ATCA_STATUS calib_gendig(ATCADevice device, uint8_t zone, uint16_t key_id, const uint8_t *other_data, uint8_t other_data_size); + +// GenKey command functions +ATCA_STATUS calib_genkey_base(ATCADevice device, uint8_t mode, uint16_t key_id, const uint8_t* other_data, uint8_t* public_key); +ATCA_STATUS calib_genkey(ATCADevice device, uint16_t key_id, uint8_t* public_key); +ATCA_STATUS calib_get_pubkey(ATCADevice device, uint16_t key_id, uint8_t* public_key); +ATCA_STATUS calib_genkey_mac(ATCADevice device, uint8_t* public_key, uint8_t* mac); + +// HMAC command functions +ATCA_STATUS calib_hmac(ATCADevice device, uint8_t mode, uint16_t key_id, uint8_t* digest); + +// Info command functions +ATCA_STATUS calib_info_base(ATCADevice device, uint8_t mode, uint16_t param2, uint8_t* out_data); +ATCA_STATUS calib_info(ATCADevice device, uint8_t* revision); +ATCA_STATUS calib_info_set_latch(ATCADevice device, bool state); +ATCA_STATUS calib_info_get_latch(ATCADevice device, bool* state); +ATCA_STATUS calib_info_privkey_valid(ATCADevice device, uint16_t key_id, uint8_t* is_valid); +ATCA_STATUS calib_info_lock_status(ATCADevice device, uint16_t param2, uint8_t* is_locked); + +// KDF command functions +ATCA_STATUS calib_kdf(ATCADevice device, uint8_t mode, uint16_t key_id, const uint32_t details, const uint8_t* message, uint8_t* out_data, uint8_t* out_nonce); + +// Lock command functions +ATCA_STATUS calib_lock(ATCADevice device, uint8_t mode, uint16_t summary_crc); +ATCA_STATUS calib_lock_config_zone(ATCADevice device); +ATCA_STATUS calib_lock_config_zone_crc(ATCADevice device, uint16_t summary_crc); +ATCA_STATUS calib_lock_data_zone(ATCADevice device); +ATCA_STATUS calib_lock_data_zone_crc(ATCADevice device, uint16_t summary_crc); +ATCA_STATUS calib_lock_data_slot(ATCADevice device, uint16_t slot); +// Lock ECC204 command functions +ATCA_STATUS calib_ecc204_lock_config_slot(ATCADevice device, uint16_t slot, uint16_t summary_crc); +ATCA_STATUS calib_ecc204_lock_config_zone(ATCADevice device); +ATCA_STATUS calib_ecc204_lock_data_slot(ATCADevice device, uint16_t slot); +ATCA_STATUS calib_ecc204_lock_data_zone(ATCADevice device); + +// MAC command functions +ATCA_STATUS calib_mac(ATCADevice device, uint8_t mode, uint16_t key_id, const uint8_t* challenge, uint8_t* digest); + +// Nonce command functions +ATCA_STATUS calib_nonce_base(ATCADevice device, uint8_t mode, uint16_t zero, const uint8_t *num_in, uint8_t* rand_out); +ATCA_STATUS calib_nonce(ATCADevice device, const uint8_t *num_in); +ATCA_STATUS calib_nonce_load(ATCADevice device, uint8_t target, const uint8_t *num_in, uint16_t num_in_size); +ATCA_STATUS calib_nonce_rand(ATCADevice device, const uint8_t *num_in, uint8_t* rand_out); +ATCA_STATUS calib_challenge(ATCADevice device, const uint8_t *num_in); +ATCA_STATUS calib_challenge_seed_update(ATCADevice device, const uint8_t *num_in, uint8_t* rand_out); +ATCA_STATUS calib_nonce_gen_session_key(ATCADevice device, uint16_t param2, uint8_t* num_in, + uint8_t* rand_out); + +// PrivWrite command functions + +#if defined(ATCA_USE_CONSTANT_HOST_NONCE) +ATCA_STATUS calib_priv_write(ATCADevice device, uint16_t key_id, const uint8_t priv_key[36], uint16_t write_key_id, const uint8_t write_key[32]); +#else +ATCA_STATUS calib_priv_write(ATCADevice device, uint16_t key_id, const uint8_t priv_key[36], uint16_t write_key_id, const uint8_t write_key[32], const uint8_t num_in[NONCE_NUMIN_SIZE]); +#endif +// Random command functions +ATCA_STATUS calib_random(ATCADevice device, uint8_t* rand_out); + +// Read command functions +ATCA_STATUS calib_read_zone(ATCADevice device, uint8_t zone, uint16_t slot, uint8_t block, uint8_t offset, uint8_t *data, uint8_t len); +ATCA_STATUS calib_read_bytes_zone(ATCADevice device, uint8_t zone, uint16_t slot, size_t offset, uint8_t *data, size_t length); +ATCA_STATUS calib_read_serial_number(ATCADevice device, uint8_t* serial_number); +ATCA_STATUS calib_read_pubkey(ATCADevice device, uint16_t slot, uint8_t *public_key); +ATCA_STATUS calib_read_sig(ATCADevice device, uint16_t slot, uint8_t *sig); +ATCA_STATUS calib_read_config_zone(ATCADevice device, uint8_t* config_data); +ATCA_STATUS calib_cmp_config_zone(ATCADevice device, uint8_t* config_data, bool* same_config); +// ECC204 Read command functions +ATCA_STATUS calib_ecc204_read_zone(ATCADevice device, uint8_t zone, uint16_t slot, uint8_t block, size_t offset, + uint8_t* data, uint8_t len); +ATCA_STATUS calib_ecc204_read_config_zone(ATCADevice device, uint8_t* config_data); +ATCA_STATUS calib_ecc204_read_serial_number(ATCADevice device, uint8_t* serial_number); +ATCA_STATUS calib_ecc204_read_bytes_zone(ATCADevice device, uint8_t zone, uint16_t slot, + size_t block, uint8_t* data, size_t length); +ATCA_STATUS calib_ecc204_cmp_config_zone(ATCADevice device, uint8_t* config_data, bool* same_config); + +#if defined(ATCA_USE_CONSTANT_HOST_NONCE) +ATCA_STATUS calib_read_enc(ATCADevice device, uint16_t key_id, uint8_t block, uint8_t *data, const uint8_t* enc_key, const uint16_t enc_key_id); +#else +ATCA_STATUS calib_read_enc(ATCADevice device, uint16_t key_id, uint8_t block, uint8_t *data, const uint8_t* enc_key, const uint16_t enc_key_id, const uint8_t num_in[NONCE_NUMIN_SIZE]); +#endif + +// SecureBoot command functions +ATCA_STATUS calib_secureboot(ATCADevice device, uint8_t mode, uint16_t param2, const uint8_t* digest, const uint8_t* signature, uint8_t* mac); +ATCA_STATUS calib_secureboot_mac(ATCADevice device, uint8_t mode, const uint8_t* digest, const uint8_t* signature, const uint8_t* num_in, const uint8_t* io_key, bool* is_verified); + +// SelfTest command functions +ATCA_STATUS calib_selftest(ATCADevice device, uint8_t mode, uint16_t param2, uint8_t* result); + +// SHA command functions +typedef struct atca_sha256_ctx +{ + uint32_t total_msg_size; //!< Total number of message bytes processed + uint32_t block_size; //!< Number of bytes in current block + uint8_t block[ATCA_SHA256_BLOCK_SIZE * 2]; //!< Unprocessed message storage +} atca_sha256_ctx_t; + +typedef atca_sha256_ctx_t atca_hmac_sha256_ctx_t; + +ATCA_STATUS calib_sha_base(ATCADevice device, uint8_t mode, uint16_t length, const uint8_t* data_in, uint8_t* data_out, uint16_t* data_out_size); +ATCA_STATUS calib_sha_start(ATCADevice device); +ATCA_STATUS calib_sha_update(ATCADevice device, const uint8_t* message); +ATCA_STATUS calib_sha_end(ATCADevice device, uint8_t *digest, uint16_t length, const uint8_t *message); +ATCA_STATUS calib_sha_read_context(ATCADevice device, uint8_t* context, uint16_t* context_size); +ATCA_STATUS calib_sha_write_context(ATCADevice device, const uint8_t* context, uint16_t context_size); +ATCA_STATUS calib_sha(ATCADevice device, uint16_t length, const uint8_t *message, uint8_t *digest); +ATCA_STATUS calib_hw_sha2_256(ATCADevice device, const uint8_t * data, size_t data_size, uint8_t* digest); +ATCA_STATUS calib_hw_sha2_256_init(ATCADevice device, atca_sha256_ctx_t* ctx); +ATCA_STATUS calib_hw_sha2_256_update(ATCADevice device, atca_sha256_ctx_t* ctx, const uint8_t* data, size_t data_size); +ATCA_STATUS calib_hw_sha2_256_finish(ATCADevice device, atca_sha256_ctx_t* ctx, uint8_t* digest); +ATCA_STATUS calib_sha_hmac_init(ATCADevice device, atca_hmac_sha256_ctx_t* ctx, uint16_t key_slot); +ATCA_STATUS calib_sha_hmac_update(ATCADevice device, atca_hmac_sha256_ctx_t* ctx, const uint8_t* data, size_t data_size); +ATCA_STATUS calib_sha_hmac_finish(ATCADevice device, atca_hmac_sha256_ctx_t* ctx, uint8_t* digest, uint8_t target); +ATCA_STATUS calib_sha_hmac(ATCADevice device, const uint8_t * data, size_t data_size, uint16_t key_slot, uint8_t* digest, uint8_t target); + +// Sign command functions +ATCA_STATUS calib_sign_base(ATCADevice device, uint8_t mode, uint16_t key_id, uint8_t *signature); +ATCA_STATUS calib_sign(ATCADevice device, uint16_t key_id, const uint8_t *msg, uint8_t *signature); +ATCA_STATUS calib_sign_internal(ATCADevice device, uint16_t key_id, bool is_invalidate, bool is_full_sn, uint8_t *signature); +// ECC204 Sign command functions +ATCA_STATUS calib_ecc204_sign(ATCADevice device, uint16_t key_id, const uint8_t* msg, uint8_t* signature); + +// UpdateExtra command functions +ATCA_STATUS calib_updateextra(ATCADevice device, uint8_t mode, uint16_t new_value); + +// Verify command functions +ATCA_STATUS calib_verify(ATCADevice device, uint8_t mode, uint16_t key_id, const uint8_t* signature, const uint8_t* public_key, const uint8_t* other_data, uint8_t* mac); +ATCA_STATUS calib_verify_extern(ATCADevice device, const uint8_t *message, const uint8_t *signature, const uint8_t *public_key, bool *is_verified); +ATCA_STATUS calib_verify_extern_mac(ATCADevice device, const uint8_t *message, const uint8_t* signature, const uint8_t* public_key, const uint8_t* num_in, const uint8_t* io_key, bool* is_verified); +ATCA_STATUS calib_verify_stored(ATCADevice device, const uint8_t *message, const uint8_t *signature, uint16_t key_id, bool *is_verified); +ATCA_STATUS calib_verify_stored_mac(ATCADevice device, const uint8_t *message, const uint8_t *signature, uint16_t key_id, const uint8_t* num_in, const uint8_t* io_key, bool* is_verified); +ATCA_STATUS calib_verify_validate(ATCADevice device, uint16_t key_id, const uint8_t *signature, const uint8_t *other_data, bool *is_verified); +ATCA_STATUS calib_verify_invalidate(ATCADevice device, uint16_t key_id, const uint8_t *signature, const uint8_t *other_data, bool *is_verified); + +// Write command functions +ATCA_STATUS calib_write(ATCADevice device, uint8_t zone, uint16_t address, const uint8_t *value, const uint8_t *mac); +ATCA_STATUS calib_write_zone(ATCADevice device, uint8_t zone, uint16_t slot, uint8_t block, uint8_t offset, const uint8_t *data, uint8_t len); +ATCA_STATUS calib_write_bytes_zone(ATCADevice device, uint8_t zone, uint16_t slot, size_t offset_bytes, const uint8_t *data, size_t length); +ATCA_STATUS calib_write_pubkey(ATCADevice device, uint16_t slot, const uint8_t *public_key); +ATCA_STATUS calib_write_config_zone(ATCADevice device, const uint8_t* config_data); +// ECC204 Write command functions +ATCA_STATUS calib_ecc204_write(ATCADevice device, uint8_t zone, uint16_t address, const uint8_t *value, + const uint8_t *mac); +ATCA_STATUS calib_ecc204_write_zone(ATCADevice device, uint8_t zone, uint16_t slot, uint8_t block, + uint8_t offset, const uint8_t *data, uint8_t len); +ATCA_STATUS calib_ecc204_write_config_zone(ATCADevice device, const uint8_t* config_data); +ATCA_STATUS calib_ecc204_write_bytes_zone(ATCADevice device, uint8_t zone, uint16_t slot, size_t block, + const uint8_t *data, size_t length); + +#if defined(ATCA_USE_CONSTANT_HOST_NONCE) +ATCA_STATUS calib_write_enc(ATCADevice device, uint16_t key_id, uint8_t block, const uint8_t *data, const uint8_t* enc_key, const uint16_t enc_key_id); +#else +ATCA_STATUS calib_write_enc(ATCADevice device, uint16_t key_id, uint8_t block, const uint8_t *data, const uint8_t* enc_key, const uint16_t enc_key_id, const uint8_t num_in[NONCE_NUMIN_SIZE]); +#endif + +#if defined(ATCA_USE_CONSTANT_HOST_NONCE) +ATCA_STATUS calib_ecc204_write_enc(ATCADevice device, uint16_t slot, uint8_t* data, uint8_t* transport_key, + uint8_t key_id); +#else +ATCA_STATUS calib_ecc204_write_enc(ATCADevice device, uint16_t slot, uint8_t* data, uint8_t* transport_key, + uint8_t key_id, uint8_t num_in[NONCE_NUMIN_SIZE]); +#endif + +ATCA_STATUS calib_write_config_counter(ATCADevice device, uint16_t counter_id, uint32_t counter_value); + +#ifdef __cplusplus +} +#endif + +/** @} */ + +#endif diff --git a/drivers/ecc108a/cryptoauthlib/calib/calib_command.c b/drivers/ecc108a/cryptoauthlib/calib/calib_command.c new file mode 100644 index 0000000..f8d6fc5 --- /dev/null +++ b/drivers/ecc108a/cryptoauthlib/calib/calib_command.c @@ -0,0 +1,766 @@ +/** + * \file + * \brief Microchip CryptoAuthentication device command builder - this is the main object that builds the command + * byte strings for the given device. It does not execute the command. The basic flow is to call + * a command method to build the command you want given the parameters and then send that byte string + * through the device interface. + * + * The primary goal of the command builder is to wrap the given parameters with the correct packet size and CRC. + * The caller should first fill in the parameters required in the ATCAPacket parameter given to the command. + * The command builder will deal with the mechanics of creating a valid packet using the parameter information. + * + * \copyright (c) 2015-2020 Microchip Technology Inc. and its subsidiaries. + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip software + * and any derivatives exclusively with Microchip products. It is your + * responsibility to comply with third party license terms applicable to your + * use of third party software (including open source software) that may + * accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER + * EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, + * SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE + * OF ANY KIND WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF + * MICROCHIP HAS BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE + * FORESEEABLE. TO THE FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL + * LIABILITY ON ALL CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED + * THE AMOUNT OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR + * THIS SOFTWARE. + */ + +#include "cryptoauthlib.h" + +/** \brief ATCACommand CheckMAC method + * \param[in] ca_cmd instance + * \param[in] packet pointer to the packet containing the command being built + * \return ATCA_SUCCESS + */ +ATCA_STATUS atCheckMAC(ATCADeviceType device_type, ATCAPacket *packet) +{ + ((void)device_type); + + // Set the opcode & parameters + packet->opcode = ATCA_CHECKMAC; + packet->txsize = CHECKMAC_COUNT; + atCalcCrc(packet); + return ATCA_SUCCESS; +} + +/** \brief ATCACommand Counter method + * \param[in] ca_cmd instance + * \param[in] packet pointer to the packet containing the command being built + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atCounter(ATCADeviceType device_type, ATCAPacket *packet) +{ + ((void)device_type); + + // Set the opcode & parameters + packet->opcode = ATCA_COUNTER; + packet->txsize = COUNTER_COUNT; + atCalcCrc(packet); + return ATCA_SUCCESS; +} + +/** \brief ATCACommand DeriveKey method + * \param[in] ca_cmd instance + * \param[in] packet pointer to the packet containing the command being built + * \param[in] has_mac hasMAC determines if MAC data is present in the packet input + * \return ATCA_SUCCESS + */ +ATCA_STATUS atDeriveKey(ATCADeviceType device_type, ATCAPacket *packet, bool has_mac) +{ + ((void)device_type); + + // Set the opcode & parameters + packet->opcode = ATCA_DERIVE_KEY; + + // hasMAC must be given since the packet does not have any implicit information to + // know if it has a mac or not unless the size is preset + if (has_mac) + { + packet->txsize = DERIVE_KEY_COUNT_LARGE; + } + else + { + packet->txsize = DERIVE_KEY_COUNT_SMALL; + } + atCalcCrc(packet); + return ATCA_SUCCESS; +} + +/** \brief ATCACommand ECDH method + * \param[in] ca_cmd instance + * \param[in] packet pointer to the packet containing the command being built + * \return ATCA_SUCCESS + */ +ATCA_STATUS atECDH(ATCADeviceType device_type, ATCAPacket *packet) +{ + ((void)device_type); + + // Set the opcode & parameters + packet->opcode = ATCA_ECDH; + packet->txsize = ECDH_COUNT; + atCalcCrc(packet); + return ATCA_SUCCESS; +} + +/** \brief ATCACommand Generate Digest method + * \param[in] ca_cmd instance + * \param[in] packet pointer to the packet containing the command being built + * \param[in] is_no_mac_key Should be true if GenDig is being run on a slot that has its SlotConfig.NoMac bit set + * \return ATCA_SUCCESS + */ +ATCA_STATUS atGenDig(ATCADeviceType device_type, ATCAPacket *packet, bool is_no_mac_key) +{ + ((void)device_type); + + // Set the opcode & parameters + packet->opcode = ATCA_GENDIG; + + if (packet->param1 == GENDIG_ZONE_SHARED_NONCE) // shared nonce mode + { + packet->txsize = GENDIG_COUNT + 32; + } + else if (is_no_mac_key) + { + packet->txsize = GENDIG_COUNT + 4; // noMac keys use 4 bytes of OtherData in calculation + } + else + { + packet->txsize = GENDIG_COUNT; + } + atCalcCrc(packet); + return ATCA_SUCCESS; +} + +/** \brief ATCACommand Generate Key method + * \param[in] ca_cmd instance + * \param[in] packet pointer to the packet containing the command being built + * \return ATCA_SUCCESS + */ +ATCA_STATUS atGenKey(ATCADeviceType device_type, ATCAPacket *packet) +{ + ((void)device_type); + + // Set the opcode & parameters + packet->opcode = ATCA_GENKEY; + + if (packet->param1 & GENKEY_MODE_PUBKEY_DIGEST) + { + packet->txsize = GENKEY_COUNT_DATA; + } + else + { + packet->txsize = GENKEY_COUNT; + } + atCalcCrc(packet); + return ATCA_SUCCESS; +} + +/** \brief ATCACommand HMAC method + * \param[in] ca_cmd instance + * \param[in] packet pointer to the packet containing the command being built + * \return ATCA_SUCCESS + */ +ATCA_STATUS atHMAC(ATCADeviceType device_type, ATCAPacket *packet) +{ + ((void)device_type); + + // Set the opcode & parameters + packet->opcode = ATCA_HMAC; + packet->txsize = HMAC_COUNT; + atCalcCrc(packet); + return ATCA_SUCCESS; +} + +/** \brief ATCACommand Info method + * \param[in] ca_cmd instance + * \param[in] packet pointer to the packet containing the command being built + * \return ATCA_SUCCESS + */ +ATCA_STATUS atInfo(ATCADeviceType device_type, ATCAPacket *packet) +{ + ((void)device_type); + + // Set the opcode & parameters + packet->opcode = ATCA_INFO; + packet->txsize = INFO_COUNT; + atCalcCrc(packet); + return ATCA_SUCCESS; +} + +/** \brief ATCACommand Lock method + * \param[in] ca_cmd instance + * \param[in] packet pointer to the packet containing the command being built + * \return ATCA_SUCCESS + */ +ATCA_STATUS atLock(ATCADeviceType device_type, ATCAPacket *packet) +{ + ((void)device_type); + + // Set the opcode & parameters + packet->opcode = ATCA_LOCK; + packet->txsize = LOCK_COUNT; + atCalcCrc(packet); + return ATCA_SUCCESS; +} + +/** \brief ATCACommand MAC method + * \param[in] ca_cmd instance + * \param[in] packet pointer to the packet containing the command being built + * \return ATCA_SUCCESS + */ +ATCA_STATUS atMAC(ATCADeviceType device_type, ATCAPacket *packet) +{ + ((void)device_type); + + // Set the opcode & parameters + // variable packet size + packet->opcode = ATCA_MAC; + if (!(packet->param1 & MAC_MODE_BLOCK2_TEMPKEY)) + { + packet->txsize = MAC_COUNT_LONG; + } + else + { + packet->txsize = MAC_COUNT_SHORT; + } + atCalcCrc(packet); + return ATCA_SUCCESS; +} + +/** \brief ATCACommand Nonce method + * \param[in] ca_cmd instance + * \param[in] packet pointer to the packet containing the command being built + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atNonce(ATCADeviceType device_type, ATCAPacket *packet) +{ + ((void)device_type); + + // Set the opcode & parameters + // variable packet size + uint8_t calc_mode = packet->param1 & NONCE_MODE_MASK; + + packet->opcode = ATCA_NONCE; + + if ((calc_mode == NONCE_MODE_SEED_UPDATE || calc_mode == NONCE_MODE_NO_SEED_UPDATE)) + { + // Calculated nonce mode, 20 byte NumInm + packet->txsize = NONCE_COUNT_SHORT; + } + else if (calc_mode == NONCE_MODE_PASSTHROUGH) + { + // PAss-through nonce mode + if ((packet->param1 & NONCE_MODE_INPUT_LEN_MASK) == NONCE_MODE_INPUT_LEN_64) + { + // 64 byte NumIn + packet->txsize = NONCE_COUNT_LONG_64; + } + else + { + // 32 byte NumIn + packet->txsize = NONCE_COUNT_LONG; + } + } + else + { + return ATCA_TRACE(ATCA_BAD_PARAM, "atNonce - failed; Invalid mode received"); + } + atCalcCrc(packet); + return ATCA_SUCCESS; +} + +/** \brief ATCACommand Pause method + * \param[in] ca_cmd instance + * \param[in] packet pointer to the packet containing the command being built + * \return ATCA_SUCCESS + */ +ATCA_STATUS atPause(ATCADeviceType device_type, ATCAPacket *packet) +{ + ((void)device_type); + + // Set the opcode & parameters + packet->opcode = ATCA_PAUSE; + packet->txsize = PAUSE_COUNT; + atCalcCrc(packet); + return ATCA_SUCCESS; +} + +/** \brief ATCACommand PrivWrite method + * \param[in] ca_cmd instance + * \param[in] packet pointer to the packet containing the command being built + * \return ATCA_SUCCESS + */ +ATCA_STATUS atPrivWrite(ATCADeviceType device_type, ATCAPacket *packet) +{ + ((void)device_type); + + // Set the opcode & parameters + packet->opcode = ATCA_PRIVWRITE; + packet->txsize = PRIVWRITE_COUNT; + atCalcCrc(packet); + return ATCA_SUCCESS; +} + +/** \brief ATCACommand Random method + * \param[in] ca_cmd instance + * \param[in] packet pointer to the packet containing the command being built + * \return ATCA_SUCCESS + */ +ATCA_STATUS atRandom(ATCADeviceType device_type, ATCAPacket *packet) +{ + ((void)device_type); + + // Set the opcode & parameters + packet->opcode = ATCA_RANDOM; + packet->txsize = RANDOM_COUNT; + atCalcCrc(packet); + return ATCA_SUCCESS; +} + +/** \brief ATCACommand Read method + * \param[in] ca_cmd instance + * \param[in] packet pointer to the packet containing the command being built + * \return ATCA_SUCCESS + */ +ATCA_STATUS atRead(ATCADeviceType device_type, ATCAPacket *packet) +{ + ((void)device_type); + + // Set the opcode & parameters + packet->opcode = ATCA_READ; + packet->txsize = READ_COUNT; + atCalcCrc(packet); + return ATCA_SUCCESS; +} + +/** \brief ATCACommand SecureBoot method + * \param[in] ca_cmd instance + * \param[in] packet pointer to the packet containing the command being built + * \return ATCA_SUCCESS + */ +ATCA_STATUS atSecureBoot(ATCADeviceType device_type, ATCAPacket *packet) +{ + ((void)device_type); + + packet->opcode = ATCA_SECUREBOOT; + packet->txsize = ATCA_CMD_SIZE_MIN; + + //variable transmit size based on mode encoding + switch (packet->param1 & SECUREBOOT_MODE_MASK) + { + case SECUREBOOT_MODE_FULL: + case SECUREBOOT_MODE_FULL_COPY: + packet->txsize += (SECUREBOOT_DIGEST_SIZE + SECUREBOOT_SIGNATURE_SIZE); + break; + + case SECUREBOOT_MODE_FULL_STORE: + packet->txsize += SECUREBOOT_DIGEST_SIZE; + break; + + default: + return ATCA_TRACE(ATCA_BAD_PARAM, "atSecureBoot - failed; Invalid mode received"); + break; + } + atCalcCrc(packet); + return ATCA_SUCCESS; +} + +/** \brief ATCACommand SHA method + * \param[in] ca_cmd instance + * \param[in] packet pointer to the packet containing the command being built + * \param[in] write_context_size the length of the sha write_context data + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atSHA(ATCADeviceType device_type, ATCAPacket *packet, uint16_t write_context_size) +{ + ((void)device_type); + + // Set the opcode & parameters + packet->opcode = ATCA_SHA; + + switch (packet->param1 & SHA_MODE_MASK) + { + case SHA_MODE_SHA256_START: // START + case SHA_MODE_HMAC_START: + case 0x03: // SHA_MODE_SHA256_PUBLIC || SHA_MODE_ECC204_HMAC_START + packet->txsize = ATCA_CMD_SIZE_MIN; + break; + + case SHA_MODE_SHA256_UPDATE: // UPDATE + packet->txsize = (uint8_t)(ATCA_CMD_SIZE_MIN + packet->param2); + break; + + case SHA_MODE_SHA256_END: // END + case SHA_MODE_HMAC_END: + // check the given packet for a size variable in param2. If it is > 0, it should + // be 0-63, incorporate that size into the packet + packet->txsize = (uint8_t)(ATCA_CMD_SIZE_MIN + packet->param2); + break; + + case SHA_MODE_READ_CONTEXT: + packet->txsize = ATCA_CMD_SIZE_MIN; + break; + + case SHA_MODE_WRITE_CONTEXT: + packet->txsize = (uint8_t)(ATCA_CMD_SIZE_MIN + write_context_size); + break; + } + + atCalcCrc(packet); + return ATCA_SUCCESS; +} + +/** \brief ATCACommand Sign method + * \param[in] ca_cmd instance + * \param[in] packet pointer to the packet containing the command being built + * \return ATCA_SUCCESS + */ +ATCA_STATUS atSign(ATCADeviceType device_type, ATCAPacket *packet) +{ + // Set the opcode & parameters + packet->opcode = ATCA_SIGN; + packet->txsize = SIGN_COUNT; + if (ECC204 == device_type) + { + packet->txsize += ATCA_SHA_DIGEST_SIZE; + } + atCalcCrc(packet); + return ATCA_SUCCESS; +} + +/** \brief ATCACommand UpdateExtra method + * \param[in] ca_cmd instance + * \param[in] packet pointer to the packet containing the command being built + * \return ATCA_SUCCESS + */ +ATCA_STATUS atUpdateExtra(ATCADeviceType device_type, ATCAPacket *packet) +{ + ((void)device_type); + + // Set the opcode & parameters + packet->opcode = ATCA_UPDATE_EXTRA; + packet->txsize = UPDATE_COUNT; + atCalcCrc(packet); + return ATCA_SUCCESS; +} + +/** \brief ATCACommand ECDSA Verify method + * \param[in] ca_cmd instance + * \param[in] packet pointer to the packet containing the command being built + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS atVerify(ATCADeviceType device_type, ATCAPacket *packet) +{ + ((void)device_type); + + // Set the opcode & parameters + packet->opcode = ATCA_VERIFY; + + // variable packet size based on mode + switch (packet->param1 & VERIFY_MODE_MASK) + { + case VERIFY_MODE_STORED: + packet->txsize = VERIFY_256_STORED_COUNT; + break; + + case VERIFY_MODE_VALIDATE_EXTERNAL: + packet->txsize = VERIFY_256_EXTERNAL_COUNT; + break; + + case VERIFY_MODE_EXTERNAL: + packet->txsize = VERIFY_256_EXTERNAL_COUNT; + break; + + case VERIFY_MODE_VALIDATE: + case VERIFY_MODE_INVALIDATE: + packet->txsize = VERIFY_256_VALIDATE_COUNT; + break; + + default: + return ATCA_TRACE(ATCA_BAD_PARAM, "atVerify - failed; Invalid mode received"); + } + + atCalcCrc(packet); + return ATCA_SUCCESS; +} + +/** \brief ATCACommand Write method + * \param[in] ca_cmd instance + * \param[in] packet pointer to the packet containing the command being built + * \param[in] has_mac Flag to indicate whether a mac is present or not + * \return ATCA_SUCCESS + */ +ATCA_STATUS atWrite(ATCADeviceType device_type, ATCAPacket *packet, bool has_mac) +{ + // Set the opcode & parameters + packet->opcode = ATCA_WRITE; + + packet->txsize = 7; + if (ECC204 == device_type) + { +#ifdef ATCA_ECC204_SUPPORT + if (ATCA_ECC204_ZONE_CONFIG == packet->param1) + { + packet->txsize += 16; + } + else if (ATCA_ECC204_ZONE_DATA == packet->param1) + { + packet->txsize += ATCA_BLOCK_SIZE; + } +#else + return ATCA_UNIMPLEMENTED; +#endif + } + else + { + if (packet->param1 & ATCA_ZONE_READWRITE_32) + { + packet->txsize += ATCA_BLOCK_SIZE; + } + else + { + packet->txsize += ATCA_WORD_SIZE; + } + } + + if (has_mac) + { + packet->txsize += WRITE_MAC_SIZE; + } + atCalcCrc(packet); + return ATCA_SUCCESS; +} + +/** \brief ATCACommand AES method + * \param[in] ca_cmd instance + * \param[in] packet pointer to the packet containing the command being built + * \return ATCA_SUCCESS + */ +ATCA_STATUS atAES(ATCADeviceType device_type, ATCAPacket *packet) +{ + ((void)device_type); + + // Set the opcode & parameters + packet->opcode = ATCA_AES; + packet->txsize = ATCA_CMD_SIZE_MIN; + + if ((packet->param1 & AES_MODE_OP_MASK) == AES_MODE_GFM) + { + packet->txsize += ATCA_AES_GFM_SIZE; + } + else + { + packet->txsize += AES_DATA_SIZE; + } + atCalcCrc(packet); + return ATCA_SUCCESS; +} + +/** \brief ATCACommand AES method + * \param[in] ca_cmd instance + * \param[in] packet pointer to the packet containing the command being built + * \return ATCA_SUCCESS + */ +ATCA_STATUS atSelfTest(ATCADeviceType device_type, ATCAPacket *packet) +{ + ((void)device_type); + + // Set the opcode & parameters + packet->opcode = ATCA_SELFTEST; + packet->txsize = ATCA_CMD_SIZE_MIN; + atCalcCrc(packet); + return ATCA_SUCCESS; +} + +/** \brief ATCACommand KDF method + * \param[in] ca_cmd Instance + * \param[in] packet Pointer to the packet containing the command being + * built. + * \return ATCA_SUCCESS + */ +ATCA_STATUS atKDF(ATCADeviceType device_type, ATCAPacket *packet) +{ + ((void)device_type); + + // Set the opcode & parameters + packet->opcode = ATCA_KDF; + + // Set TX size + if ((packet->param1 & KDF_MODE_ALG_MASK) == KDF_MODE_ALG_AES) + { + // AES algorithm has a fixed message size + packet->txsize = ATCA_CMD_SIZE_MIN + KDF_DETAILS_SIZE + AES_DATA_SIZE; + } + else + { + // All other algorithms encode message size in the last byte of details + packet->txsize = ATCA_CMD_SIZE_MIN + KDF_DETAILS_SIZE + packet->data[3]; + } + atCalcCrc(packet); + return ATCA_SUCCESS; +} + + +/** \brief Calculates CRC over the given raw data and returns the CRC in + * little-endian byte order. + * + * \param[in] length Size of data not including the CRC byte positions + * \param[in] data Pointer to the data over which to compute the CRC + * \param[out] crc_le Pointer to the place where the two-bytes of CRC will be + * returned in little-endian byte order. + */ +void atCRC(size_t length, const uint8_t *data, uint8_t *crc_le) +{ + size_t counter; + uint16_t crc_register = 0; + uint16_t polynom = 0x8005; + uint8_t shift_register; + uint8_t data_bit, crc_bit; + + for (counter = 0; counter < length; counter++) + { + for (shift_register = 0x01; shift_register > 0x00; shift_register <<= 1) + { + data_bit = (data[counter] & shift_register) ? 1 : 0; + crc_bit = crc_register >> 15; + crc_register <<= 1; + if (data_bit != crc_bit) + { + crc_register ^= polynom; + } + } + } + crc_le[0] = (uint8_t)(crc_register & 0x00FF); + crc_le[1] = (uint8_t)(crc_register >> 8); +} + + +/** \brief This function calculates CRC and adds it to the correct offset in the packet data + * \param[in] packet Packet to calculate CRC data for + */ + +void atCalcCrc(ATCAPacket *packet) +{ + uint8_t length, *crc; + + packet->param2 = ATCA_UINT16_HOST_TO_LE(packet->param2); + + length = packet->txsize - ATCA_CRC_SIZE; + // computer pointer to CRC in the packet + crc = &(packet->txsize) + length; + + // stuff CRC into packet + atCRC(length, &(packet->txsize), crc); +} + + +/** \brief This function checks the consistency of a response. + * \param[in] response pointer to response + * \return ATCA_SUCCESS on success, otherwise ATCA_RX_CRC_ERROR + */ + +ATCA_STATUS atCheckCrc(const uint8_t *response) +{ + uint8_t crc[ATCA_CRC_SIZE]; + uint8_t count = response[ATCA_COUNT_IDX]; + + count -= ATCA_CRC_SIZE; + atCRC(count, response, crc); + + return (crc[0] == response[count] && crc[1] == response[count + 1]) ? ATCA_SUCCESS : ATCA_RX_CRC_ERROR; +} + + +/** \brief determines if a given device type is a SHA device or a superset of a SHA device + * \param[in] device_type Type of device to check for family type + * \return boolean indicating whether the given device is a SHA family device. + */ + +bool atIsSHAFamily(ATCADeviceType device_type) +{ + switch (device_type) + { + case ATSHA204A: + case ATSHA206A: + return true; + break; + + default: + return false; + break; + } +} + +/** \brief determines if a given device type is an ECC device or a superset of a ECC device + * \param[in] device_type Type of device to check for family type + * \return boolean indicating whether the given device is an ECC family device. + */ +bool atIsECCFamily(ATCADeviceType device_type) +{ + switch (device_type) + { + case ATECC108A: + case ATECC508A: + case ATECC608: + case ECC204: + return true; + break; + default: + return false; + break; + } +} + +/** \brief checks for basic error frame in data + * \param[in] data pointer to received data - expected to be in the form of a CA device response frame + * \return ATCA_SUCCESS on success, otherwise an error code. + */ + +ATCA_STATUS isATCAError(uint8_t *data) +{ + if (data[0] == 0x04) // error packets are always 4 bytes long + { + switch (data[1]) + { + case 0x00: //No Error + return ATCA_SUCCESS; + case 0x01: // checkmac or verify failed + return ATCA_CHECKMAC_VERIFY_FAILED; + break; + case 0x03: // command received byte length, opcode or parameter was illegal + return ATCA_PARSE_ERROR; + break; + case 0x05: // computation error during ECC processing causing invalid results + return ATCA_STATUS_ECC; + break; + case 0x07: // chip is in self test failure mode + return ATCA_STATUS_SELFTEST_ERROR; + break; + case 0x08: //random number generator health test error + return ATCA_HEALTH_TEST_ERROR; + case 0x0f: // chip can't execute the command + return ATCA_EXECUTION_ERROR; + break; + case 0x11: // chip was successfully woken up + return ATCA_WAKE_SUCCESS; + break; + case 0xff: // bad crc found (command not properly received by device) or other comm error + return ATCA_STATUS_CRC; + break; + default: + return ATCA_GEN_FAIL; + break; + } + } + else + { + return ATCA_SUCCESS; + } +} diff --git a/drivers/ecc108a/cryptoauthlib/calib/calib_command.h b/drivers/ecc108a/cryptoauthlib/calib/calib_command.h new file mode 100644 index 0000000..949eb67 --- /dev/null +++ b/drivers/ecc108a/cryptoauthlib/calib/calib_command.h @@ -0,0 +1,744 @@ +/** + * \file + * \brief Microchip Crypto Auth device command object - this is a command builder only, it does + * not send the command. The result of a command method is a fully formed packet, ready to send + * to the ATCAIFace object to dispatch. + * + * This command object supports the ATSHA and ATECC device family. + * The command list is a superset of all device commands for this family. The command object + * differentiates the packet contents based on specific device type within the family. + * + * \copyright (c) 2015-2020 Microchip Technology Inc. and its subsidiaries. + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip software + * and any derivatives exclusively with Microchip products. It is your + * responsibility to comply with third party license terms applicable to your + * use of third party software (including open source software) that may + * accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER + * EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, + * SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE + * OF ANY KIND WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF + * MICROCHIP HAS BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE + * FORESEEABLE. TO THE FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL + * LIABILITY ON ALL CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED + * THE AMOUNT OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR + * THIS SOFTWARE. + */ +/* lint --e{755} */ + +#ifndef CALIB_COMMAND_H +#define CALIB_COMMAND_H + +#include +#ifdef __cplusplus +extern "C" { +#endif + + + +/* add ATCACommand declarations here + * + * since these are still C functions, not classes, naming is an important + * consideration to keep the namespace from colliding with other 3rd party + * libraries or even ourselves/ASF. + * + * Basic conventions: + * all methods start with the prefix 'at' + * all method names must be unique, obviously + * all method implementations should be proceeded by their Doxygen comment header + * + **/ + + +// this is the ATCACommand parameter structure. The caller to the command method must +// initialize param1, param2 and data if appropriate. The command method will fill in the rest +// and initialize the packet so it's ready to send via the ATCAIFace. +// this particular structure mimics the ATSHA and ATECC family device's command structures + +// Note: pack @ 2 is required, @ 1 causes word alignment crash (though it should not), a known bug in GCC. +// @2, the wire still has the intended byte alignment with arm-eabi. this is likely the least portable part of atca + + +#ifdef ATCA_NO_PRAGMA_PACK +typedef struct __attribute__ ((packed)) +#else +#pragma pack( push, ATCAPacket, 2 ) +typedef struct +#endif +/** \brief an ATCA packet structure. This is a superset of the packet transmitted on the wire. It's also + * used as a buffer for receiving the response + */ +{ + + // used for transmit/send + uint8_t _reserved; // used by HAL layer as needed (I/O tokens, Word address values) + + //--- start of packet i/o frame---- + uint8_t txsize; + uint8_t opcode; + uint8_t param1; // often same as mode + uint16_t param2; + uint8_t data[192]; // includes 2-byte CRC. data size is determined by largest possible data section of any + // command + crc (see: x08 verify data1 + data2 + data3 + data4) + // this is an explicit design trade-off (space) resulting in simplicity in use + // and implementation + //--- end of packet i/o frame + + // used for receive + uint8_t execTime; // execution time of command by opcode + + // structure should be packed since it will be transmitted over the wire + // this method varies by compiler. As new compilers are supported, add their structure packing method here + +} ATCAPacket; + +#ifndef ATCA_NO_PRAGMA_PACK +#pragma pack( pop, ATCAPacket) +#endif + + +ATCA_STATUS atCheckMAC(ATCADeviceType device_type, ATCAPacket *packet); +ATCA_STATUS atCounter(ATCADeviceType device_type, ATCAPacket *packet); +ATCA_STATUS atDeriveKey(ATCADeviceType device_type, ATCAPacket *packet, bool has_mac); +ATCA_STATUS atECDH(ATCADeviceType device_type, ATCAPacket *packet); +ATCA_STATUS atGenDig(ATCADeviceType device_type, ATCAPacket *packet, bool is_no_mac_key); +ATCA_STATUS atGenKey(ATCADeviceType device_type, ATCAPacket *packet); +ATCA_STATUS atHMAC(ATCADeviceType device_type, ATCAPacket *packet); +ATCA_STATUS atInfo(ATCADeviceType device_type, ATCAPacket *packet); +ATCA_STATUS atLock(ATCADeviceType device_type, ATCAPacket *packet); +ATCA_STATUS atMAC(ATCADeviceType device_type, ATCAPacket *packet); +ATCA_STATUS atNonce(ATCADeviceType device_type, ATCAPacket *packet); +ATCA_STATUS atPause(ATCADeviceType device_type, ATCAPacket *packet); +ATCA_STATUS atPrivWrite(ATCADeviceType device_type, ATCAPacket *packet); +ATCA_STATUS atRandom(ATCADeviceType device_type, ATCAPacket *packet); +ATCA_STATUS atRead(ATCADeviceType device_type, ATCAPacket *packet); +ATCA_STATUS atSecureBoot(ATCADeviceType device_type, ATCAPacket *packet); +ATCA_STATUS atSHA(ATCADeviceType device_type, ATCAPacket *packet, uint16_t write_context_size); +ATCA_STATUS atSign(ATCADeviceType device_type, ATCAPacket *packet); +ATCA_STATUS atUpdateExtra(ATCADeviceType device_type, ATCAPacket *packet); +ATCA_STATUS atVerify(ATCADeviceType device_type, ATCAPacket *packet); +ATCA_STATUS atWrite(ATCADeviceType device_type, ATCAPacket *packet, bool has_mac); +ATCA_STATUS atAES(ATCADeviceType device_type, ATCAPacket *packet); +ATCA_STATUS atSelfTest(ATCADeviceType device_type, ATCAPacket *packet); +ATCA_STATUS atKDF(ATCADeviceType device_type, ATCAPacket *packet); + +bool atIsSHAFamily(ATCADeviceType device_type); +bool atIsECCFamily(ATCADeviceType device_type); +ATCA_STATUS isATCAError(uint8_t *data); + + +// command helpers +void atCRC(size_t length, const uint8_t *data, uint8_t *crc_le); +void atCalcCrc(ATCAPacket *pkt); +ATCA_STATUS atCheckCrc(const uint8_t *response); + + +/* command definitions */ + +//! minimum number of bytes in command (from count byte to second CRC byte) +#define ATCA_CMD_SIZE_MIN ((uint8_t)7) +//! maximum size of command packet (Verify) +#define ATCA_CMD_SIZE_MAX ((uint8_t)4 * 36 + 7) +//! status byte for success +#define CMD_STATUS_SUCCESS ((uint8_t)0x00) +//! status byte after wake-up +#define CMD_STATUS_WAKEUP ((uint8_t)0x11) +//! command parse error +#define CMD_STATUS_BYTE_PARSE ((uint8_t)0x03) +//! command ECC error +#define CMD_STATUS_BYTE_ECC ((uint8_t)0x05) +//! command execution error +#define CMD_STATUS_BYTE_EXEC ((uint8_t)0x0F) +//! communication error +#define CMD_STATUS_BYTE_COMM ((uint8_t)0xFF) + +/** \name Opcodes for Crypto Authentication device commands + @{ */ +#define ATCA_CHECKMAC ((uint8_t)0x28) //!< CheckMac command op-code +#define ATCA_DERIVE_KEY ((uint8_t)0x1C) //!< DeriveKey command op-code +#define ATCA_INFO ((uint8_t)0x30) //!< Info command op-code +#define ATCA_GENDIG ((uint8_t)0x15) //!< GenDig command op-code +#define ATCA_GENKEY ((uint8_t)0x40) //!< GenKey command op-code +#define ATCA_HMAC ((uint8_t)0x11) //!< HMAC command op-code +#define ATCA_LOCK ((uint8_t)0x17) //!< Lock command op-code +#define ATCA_MAC ((uint8_t)0x08) //!< MAC command op-code +#define ATCA_NONCE ((uint8_t)0x16) //!< Nonce command op-code +#define ATCA_PAUSE ((uint8_t)0x01) //!< Pause command op-code +#define ATCA_PRIVWRITE ((uint8_t)0x46) //!< PrivWrite command op-code +#define ATCA_RANDOM ((uint8_t)0x1B) //!< Random command op-code +#define ATCA_READ ((uint8_t)0x02) //!< Read command op-code +#define ATCA_SIGN ((uint8_t)0x41) //!< Sign command op-code +#define ATCA_UPDATE_EXTRA ((uint8_t)0x20) //!< UpdateExtra command op-code +#define ATCA_VERIFY ((uint8_t)0x45) //!< GenKey command op-code +#define ATCA_WRITE ((uint8_t)0x12) //!< Write command op-code +#define ATCA_ECDH ((uint8_t)0x43) //!< ECDH command op-code +#define ATCA_COUNTER ((uint8_t)0x24) //!< Counter command op-code +#define ATCA_SHA ((uint8_t)0x47) //!< SHA command op-code +#define ATCA_AES ((uint8_t)0x51) //!< AES command op-code +#define ATCA_KDF ((uint8_t)0x56) //!< KDF command op-code +#define ATCA_SECUREBOOT ((uint8_t)0x80) //!< Secure Boot command op-code +#define ATCA_SELFTEST ((uint8_t)0x77) //!< Self test command op-code + + + + +/** @} */ + + +/** \name Definitions of Data and Packet Sizes + @{ */ +#define ATCA_BLOCK_SIZE (32) //!< size of a block +#define ATCA_WORD_SIZE (4) //!< size of a word +#define ATCA_PUB_KEY_PAD (4) //!< size of the public key pad +#define ATCA_SERIAL_NUM_SIZE (9) //!< number of bytes in the device serial number +#define ATCA_RSP_SIZE_VAL ((uint8_t)7) //!< size of response packet containing four bytes of data +#define ATCA_KEY_COUNT (16) //!< number of keys +#define ATCA_ECC_CONFIG_SIZE (128) //!< size of configuration zone +#define ATCA_SHA_CONFIG_SIZE (88) //!< size of configuration zone +#define ATCA_ECC204_CONFIG_SIZE (64) //!< size of ECC204 configuration zone +#define ATCA_ECC204_CONFIG_SLOT_SIZE (16) //!< size of ECC204 configuration slot size +#define ATCA_OTP_SIZE (64) //!< size of OTP zone +#define ATCA_DATA_SIZE (ATCA_KEY_COUNT * ATCA_KEY_SIZE) //!< size of data zone +#define ATCA_AES_GFM_SIZE ATCA_BLOCK_SIZE //!< size of GFM data + +#define ATCA_CHIPMODE_OFFSET (19) //!< ChipMode byte offset within the configuration zone +#define ATCA_CHIPMODE_I2C_ADDRESS_FLAG ((uint8_t)0x01) //!< ChipMode I2C Address in UserExtraAdd flag +#define ATCA_CHIPMODE_TTL_ENABLE_FLAG ((uint8_t)0x02) //!< ChipMode TTLenable flag +#define ATCA_CHIPMODE_WATCHDOG_MASK ((uint8_t)0x04) //!< ChipMode watchdog duration mask +#define ATCA_CHIPMODE_WATCHDOG_SHORT ((uint8_t)0x00) //!< ChipMode short watchdog (~1.3s) +#define ATCA_CHIPMODE_WATCHDOG_LONG ((uint8_t)0x04) //!< ChipMode long watchdog (~13s) +#define ATCA_CHIPMODE_CLOCK_DIV_MASK ((uint8_t)0xF8) //!< ChipMode clock divider mask +#define ATCA_CHIPMODE_CLOCK_DIV_M0 ((uint8_t)0x00) //!< ChipMode clock divider M0 +#define ATCA_CHIPMODE_CLOCK_DIV_M1 ((uint8_t)0x28) //!< ChipMode clock divider M1 +#define ATCA_CHIPMODE_CLOCK_DIV_M2 ((uint8_t)0x68) //!< ChipMode clock divider M2 + +#define ATCA_COUNT_SIZE ((uint8_t)1) //!< Number of bytes in the command packet Count +#define ATCA_CRC_SIZE ((uint8_t)2) //!< Number of bytes in the command packet CRC +#define ATCA_PACKET_OVERHEAD (ATCA_COUNT_SIZE + ATCA_CRC_SIZE) //!< Number of bytes in the command packet + +#define ATCA_PUB_KEY_SIZE (64) //!< size of a p256 public key +#define ATCA_PRIV_KEY_SIZE (32) //!< size of a p256 private key +#define ATCA_SIG_SIZE (64) //!< size of a p256 signature +#define ATCA_KEY_SIZE (32) //!< size of a symmetric SHA key +#define RSA2048_KEY_SIZE (256) //!< size of a RSA private key + +#define ATCA_RSP_SIZE_MIN ((uint8_t)4) //!< minimum number of bytes in response +#define ATCA_RSP_SIZE_4 ((uint8_t)7) //!< size of response packet containing 4 bytes data +#define ATCA_RSP_SIZE_72 ((uint8_t)75) //!< size of response packet containing 64 bytes data +#define ATCA_RSP_SIZE_64 ((uint8_t)67) //!< size of response packet containing 64 bytes data +#define ATCA_RSP_SIZE_32 ((uint8_t)35) //!< size of response packet containing 32 bytes data +#define ATCA_RSP_SIZE_16 ((uint8_t)19) //!< size of response packet containing 16 bytes data +#define ATCA_RSP_SIZE_MAX ((uint8_t)75) //!< maximum size of response packet (GenKey and Verify command) + +#define OUTNONCE_SIZE (32) //!< Size of the OutNonce response expected from several commands + +/** \name Definitions for Command Parameter Ranges + @{ */ +#define ATCA_KEY_ID_MAX ((uint8_t)15) //!< maximum value for key id +#define ATCA_OTP_BLOCK_MAX ((uint8_t)1) //!< maximum value for OTP block +/** @} */ + +/** \name Definitions for Indexes Common to All Commands + @{ */ +#define ATCA_COUNT_IDX (0) //!< command packet index for count +#define ATCA_OPCODE_IDX (1) //!< command packet index for op-code +#define ATCA_PARAM1_IDX (2) //!< command packet index for first parameter +#define ATCA_PARAM2_IDX (3) //!< command packet index for second parameter +#define ATCA_DATA_IDX (5) //!< command packet index for data load +#define ATCA_RSP_DATA_IDX (1) //!< buffer index of data in response +/** @} */ + +/** \name Definitions for Zone and Address Parameters + @{ */ +#define ATCA_ZONE_MASK ((uint8_t)0x03) //!< Zone mask +#define ATCA_ZONE_ENCRYPTED ((uint8_t)0x40) //!< Zone bit 6 set: Write is encrypted with an unlocked data zone. +#define ATCA_ZONE_READWRITE_32 ((uint8_t)0x80) //!< Zone bit 7 set: Access 32 bytes, otherwise 4 bytes. +#define ATCA_ADDRESS_MASK_CONFIG (0x001F) //!< Address bits 5 to 7 are 0 for Configuration zone. +#define ATCA_ADDRESS_MASK_OTP (0x000F) //!< Address bits 4 to 7 are 0 for OTP zone. +#define ATCA_ADDRESS_MASK (0x007F) //!< Address bit 7 to 15 are always 0. +#define ATCA_TEMPKEY_KEYID (0xFFFF) //!< KeyID when referencing TempKey +/** @} */ + +/** \name Definitions for Key types + @{ */ +#define ATCA_B283_KEY_TYPE 0 //!< B283 NIST ECC key +#define ATCA_K283_KEY_TYPE 1 //!< K283 NIST ECC key +#define ATCA_P256_KEY_TYPE 4 //!< P256 NIST ECC key +#define ATCA_AES_KEY_TYPE 6 //!< AES-128 Key +#define ATCA_SHA_KEY_TYPE 7 //!< SHA key or other data +/** @} */ + +/** \name Definitions for the AES Command + @{ */ +#define AES_MODE_IDX ATCA_PARAM1_IDX //!< AES command index for mode +#define AES_KEYID_IDX ATCA_PARAM2_IDX //!< AES command index for key id +#define AES_INPUT_IDX ATCA_DATA_IDX //!< AES command index for input data +#define AES_COUNT (23) //!< AES command packet size +#define AES_MODE_MASK ((uint8_t)0xC7) //!< AES mode bits 3 to 5 are 0 +#define AES_MODE_KEY_BLOCK_MASK ((uint8_t)0xC0) //!< AES mode mask for key block field +#define AES_MODE_OP_MASK ((uint8_t)0x07) //!< AES mode operation mask +#define AES_MODE_ENCRYPT ((uint8_t)0x00) //!< AES mode: Encrypt +#define AES_MODE_DECRYPT ((uint8_t)0x01) //!< AES mode: Decrypt +#define AES_MODE_GFM ((uint8_t)0x03) //!< AES mode: GFM calculation +#define AES_MODE_KEY_BLOCK_POS (6) //!< Bit shift for key block in mode +#define AES_DATA_SIZE (16) //!< size of AES encrypt/decrypt data +#define AES_RSP_SIZE ATCA_RSP_SIZE_16 //!< AES command response packet size +/** @} */ + +/** \name Definitions for the CheckMac Command + @{ */ +#define CHECKMAC_MODE_IDX ATCA_PARAM1_IDX //!< CheckMAC command index for mode +#define CHECKMAC_KEYID_IDX ATCA_PARAM2_IDX //!< CheckMAC command index for key identifier +#define CHECKMAC_CLIENT_CHALLENGE_IDX ATCA_DATA_IDX //!< CheckMAC command index for client challenge +#define CHECKMAC_CLIENT_RESPONSE_IDX (37) //!< CheckMAC command index for client response +#define CHECKMAC_DATA_IDX (69) //!< CheckMAC command index for other data +#define CHECKMAC_COUNT (84) //!< CheckMAC command packet size +#define CHECKMAC_MODE_CHALLENGE ((uint8_t)0x00) //!< CheckMAC mode 0: first SHA block from key id +#define CHECKMAC_MODE_BLOCK2_TEMPKEY ((uint8_t)0x01) //!< CheckMAC mode bit 0: second SHA block from TempKey +#define CHECKMAC_MODE_BLOCK1_TEMPKEY ((uint8_t)0x02) //!< CheckMAC mode bit 1: first SHA block from TempKey +#define CHECKMAC_MODE_SOURCE_FLAG_MATCH ((uint8_t)0x04) //!< CheckMAC mode bit 2: match TempKey.SourceFlag +#define CHECKMAC_MODE_INCLUDE_OTP_64 ((uint8_t)0x20) //!< CheckMAC mode bit 5: include first 64 OTP bits +#define CHECKMAC_MODE_MASK ((uint8_t)0x27) //!< CheckMAC mode bits 3, 4, 6, and 7 are 0. +#define CHECKMAC_CLIENT_CHALLENGE_SIZE (32) //!< CheckMAC size of client challenge +#define CHECKMAC_CLIENT_RESPONSE_SIZE (32) //!< CheckMAC size of client response +#define CHECKMAC_OTHER_DATA_SIZE (13) //!< CheckMAC size of "other data" +#define CHECKMAC_CLIENT_COMMAND_SIZE (4) //!< CheckMAC size of client command header size inside "other data" +#define CHECKMAC_CMD_MATCH (0) //!< CheckMAC return value when there is a match +#define CHECKMAC_CMD_MISMATCH (1) //!< CheckMAC return value when there is a mismatch +#define CHECKMAC_RSP_SIZE ATCA_RSP_SIZE_MIN //!< CheckMAC response packet size +/** @} */ + +/** \name Definitions for the Counter command + @{ */ +#define COUNTER_COUNT ATCA_CMD_SIZE_MIN +#define COUNTER_MODE_IDX ATCA_PARAM1_IDX //!< Counter command index for mode +#define COUNTER_KEYID_IDX ATCA_PARAM2_IDX //!< Counter command index for key id +#define COUNTER_MODE_MASK ((uint8_t)0x01) //!< Counter mode bits 1 to 7 are 0 +#define COUNTER_MAX_VALUE ((uint32_t)2097151) //!< Counter maximum value of the counter +#define COUNTER_MODE_READ ((uint8_t)0x00) //!< Counter command mode for reading +#define COUNTER_MODE_INCREMENT ((uint8_t)0x01) //!< Counter command mode for incrementing +#define COUNTER_RSP_SIZE ATCA_RSP_SIZE_4 //!< Counter command response packet size +#define COUNTER_SIZE ATCA_RSP_SIZE_MIN //!< Counter size in binary +/** @} */ + +/** \name Definitions for the DeriveKey Command + @{ */ +#define DERIVE_KEY_RANDOM_IDX ATCA_PARAM1_IDX //!< DeriveKey command index for random bit +#define DERIVE_KEY_TARGETKEY_IDX ATCA_PARAM2_IDX //!< DeriveKey command index for target slot +#define DERIVE_KEY_MAC_IDX ATCA_DATA_IDX //!< DeriveKey command index for optional MAC +#define DERIVE_KEY_COUNT_SMALL ATCA_CMD_SIZE_MIN //!< DeriveKey command packet size without MAC +#define DERIVE_KEY_MODE ((uint8_t)0x04) //!< DeriveKey command mode set to 4 as in datasheet +#define DERIVE_KEY_COUNT_LARGE (39) //!< DeriveKey command packet size with MAC +#define DERIVE_KEY_RANDOM_FLAG ((uint8_t)4) //!< DeriveKey 1. parameter; has to match TempKey.SourceFlag +#define DERIVE_KEY_MAC_SIZE (32) //!< DeriveKey MAC size +#define DERIVE_KEY_RSP_SIZE ATCA_RSP_SIZE_MIN //!< DeriveKey response packet size +/** @} */ + +/** \name Definitions for the ECDH Command + @{ */ +#define ECDH_PREFIX_MODE ((uint8_t)0x00) +#define ECDH_COUNT (ATCA_CMD_SIZE_MIN + ATCA_PUB_KEY_SIZE) +#define ECDH_MODE_SOURCE_MASK ((uint8_t)0x01) +#define ECDH_MODE_SOURCE_EEPROM_SLOT ((uint8_t)0x00) +#define ECDH_MODE_SOURCE_TEMPKEY ((uint8_t)0x01) +#define ECDH_MODE_OUTPUT_MASK ((uint8_t)0x02) +#define ECDH_MODE_OUTPUT_CLEAR ((uint8_t)0x00) +#define ECDH_MODE_OUTPUT_ENC ((uint8_t)0x02) +#define ECDH_MODE_COPY_MASK ((uint8_t)0x0C) +#define ECDH_MODE_COPY_COMPATIBLE ((uint8_t)0x00) +#define ECDH_MODE_COPY_EEPROM_SLOT ((uint8_t)0x04) +#define ECDH_MODE_COPY_TEMP_KEY ((uint8_t)0x08) +#define ECDH_MODE_COPY_OUTPUT_BUFFER ((uint8_t)0x0C) +#define ECDH_KEY_SIZE ATCA_BLOCK_SIZE //!< ECDH output data size +#define ECDH_RSP_SIZE ATCA_RSP_SIZE_64 //!< ECDH command packet size +/** @} */ + +/** \name Definitions for the GenDig Command + @{ */ +#define GENDIG_ZONE_IDX ATCA_PARAM1_IDX //!< GenDig command index for zone +#define GENDIG_KEYID_IDX ATCA_PARAM2_IDX //!< GenDig command index for key id +#define GENDIG_DATA_IDX ATCA_DATA_IDX //!< GenDig command index for optional data +#define GENDIG_COUNT ATCA_CMD_SIZE_MIN //!< GenDig command packet size without "other data" +#define GENDIG_ZONE_CONFIG ((uint8_t)0) //!< GenDig zone id config. Use KeyID to specify any of the four 256-bit blocks of the Configuration zone. +#define GENDIG_ZONE_OTP ((uint8_t)1) //!< GenDig zone id OTP. Use KeyID to specify either the first or second 256-bit block of the OTP zone. +#define GENDIG_ZONE_DATA ((uint8_t)2) //!< GenDig zone id data. Use KeyID to specify a slot in the Data zone or a transport key in the hardware array. +#define GENDIG_ZONE_SHARED_NONCE ((uint8_t)3) //!< GenDig zone id shared nonce. KeyID specifies the location of the input value in the message generation. +#define GENDIG_ZONE_COUNTER ((uint8_t)4) //!< GenDig zone id counter. KeyID specifies the monotonic counter ID to be included in the message generation. +#define GENDIG_ZONE_KEY_CONFIG ((uint8_t)5) //!< GenDig zone id key config. KeyID specifies the slot for which the configuration information is to be included in the message generation. +#define GENDIG_RSP_SIZE ATCA_RSP_SIZE_MIN //!< GenDig command response packet size +/** @} */ + +/** \name Definitions for the GenKey Command + @{ */ +#define GENKEY_MODE_IDX ATCA_PARAM1_IDX //!< GenKey command index for mode +#define GENKEY_KEYID_IDX ATCA_PARAM2_IDX //!< GenKey command index for key id +#define GENKEY_DATA_IDX (5) //!< GenKey command index for other data +#define GENKEY_COUNT ATCA_CMD_SIZE_MIN //!< GenKey command packet size without "other data" +#define GENKEY_COUNT_DATA (10) //!< GenKey command packet size with "other data" +#define GENKEY_OTHER_DATA_SIZE (3) //!< GenKey size of "other data" +#define GENKEY_MODE_MASK ((uint8_t)0x1C) //!< GenKey mode bits 0 to 1 and 5 to 7 are 0 +#define GENKEY_MODE_PRIVATE ((uint8_t)0x04) //!< GenKey mode: private key generation +#define GENKEY_MODE_PUBLIC ((uint8_t)0x00) //!< GenKey mode: public key calculation +#define GENKEY_MODE_DIGEST ((uint8_t)0x08) //!< GenKey mode: PubKey digest will be created after the public key is calculated +#define GENKEY_MODE_PUBKEY_DIGEST ((uint8_t)0x10) //!< GenKey mode: Calculate PubKey digest on the public key in KeyId +#define GENKEY_MODE_MAC ((uint8_t)0x20) //!< Genkey mode: Calculate MAC of public key + session key +#define GENKEY_PRIVATE_TO_TEMPKEY ((uint16_t)0xFFFF) //!< GenKey Create private key and store to tempkey (608 only) +#define GENKEY_RSP_SIZE_SHORT ATCA_RSP_SIZE_MIN //!< GenKey response packet size in Digest mode +#define GENKEY_RSP_SIZE_LONG ATCA_RSP_SIZE_64 //!< GenKey response packet size when returning a public key +/** @} */ + +/** \name Definitions for the HMAC Command + @{ */ +#define HMAC_MODE_IDX ATCA_PARAM1_IDX //!< HMAC command index for mode +#define HMAC_KEYID_IDX ATCA_PARAM2_IDX //!< HMAC command index for key id +#define HMAC_COUNT ATCA_CMD_SIZE_MIN //!< HMAC command packet size +#define HMAC_MODE_FLAG_TK_RAND ((uint8_t)0x00) //!< HMAC mode bit 2: The value of this bit must match the value in TempKey.SourceFlag or the command will return an error. +#define HMAC_MODE_FLAG_TK_NORAND ((uint8_t)0x04) //!< HMAC mode bit 2: The value of this bit must match the value in TempKey.SourceFlag or the command will return an error. +#define HMAC_MODE_FLAG_OTP88 ((uint8_t)0x10) //!< HMAC mode bit 4: Include the first 88 OTP bits (OTP[0] through OTP[10]) in the message.; otherwise, the corresponding message bits are set to zero. Not applicable for ATECC508A. +#define HMAC_MODE_FLAG_OTP64 ((uint8_t)0x20) //!< HMAC mode bit 5: Include the first 64 OTP bits (OTP[0] through OTP[7]) in the message.; otherwise, the corresponding message bits are set to zero. If Mode[4] is set, the value of this mode bit is ignored. Not applicable for ATECC508A. +#define HMAC_MODE_FLAG_FULLSN ((uint8_t)0x40) //!< HMAC mode bit 6: If set, include the 48 bits SN[2:3] and SN[4:7] in the message.; otherwise, the corresponding message bits are set to zero. +#define HMAC_MODE_MASK ((uint8_t)0x74) //!< HMAC mode bits 0, 1, 3, and 7 are 0. +#define HMAC_DIGEST_SIZE (32) //!< HMAC size of digest response +#define HMAC_RSP_SIZE ATCA_RSP_SIZE_32 //!< HMAC command response packet size +/** @} */ + +/** \name Definitions for the Info Command + @{ */ +#define INFO_PARAM1_IDX ATCA_PARAM1_IDX //!< Info command index for 1. parameter +#define INFO_PARAM2_IDX ATCA_PARAM2_IDX //!< Info command index for 2. parameter +#define INFO_COUNT ATCA_CMD_SIZE_MIN //!< Info command packet size +#define INFO_MODE_REVISION ((uint8_t)0x00) //!< Info mode Revision +#define INFO_MODE_KEY_VALID ((uint8_t)0x01) //!< Info mode KeyValid +#define INFO_MODE_STATE ((uint8_t)0x02) //!< Info mode State +#define INFO_MODE_LOCK_STATUS ((uint8_t)0x02) //!< Info mode Lock status for ECC204 device +#define INFO_MODE_GPIO ((uint8_t)0x03) //!< Info mode GPIO +#define INFO_MODE_VOL_KEY_PERMIT ((uint8_t)0x04) //!< Info mode GPIO +#define INFO_MODE_MAX ((uint8_t)0x03) //!< Info mode maximum value +#define INFO_NO_STATE ((uint8_t)0x00) //!< Info mode is not the state mode. +#define INFO_OUTPUT_STATE_MASK ((uint8_t)0x01) //!< Info output state mask +#define INFO_DRIVER_STATE_MASK ((uint8_t)0x02) //!< Info driver state mask +#define INFO_PARAM2_SET_LATCH_STATE ((uint16_t)0x0002) //!< Info param2 to set the persistent latch state. +#define INFO_PARAM2_LATCH_SET ((uint16_t)0x0001) //!< Info param2 to set the persistent latch +#define INFO_PARAM2_LATCH_CLEAR ((uint16_t)0x0000) //!< Info param2 to clear the persistent latch +#define INFO_SIZE ((uint8_t)0x04) //!< Info return size +#define INFO_RSP_SIZE ATCA_RSP_SIZE_VAL //!< Info command response packet size +/** @} */ + +/** \name Definitions for the KDF Command + @{ */ +#define KDF_MODE_IDX ATCA_PARAM1_IDX //!< KDF command index for mode +#define KDF_KEYID_IDX ATCA_PARAM2_IDX //!< KDF command index for key id +#define KDF_DETAILS_IDX ATCA_DATA_IDX //!< KDF command index for details +#define KDF_DETAILS_SIZE 4 //!< KDF details (param3) size +#define KDF_MESSAGE_IDX (ATCA_DATA_IDX + KDF_DETAILS_SIZE) + +#define KDF_MODE_SOURCE_MASK ((uint8_t)0x03) //!< KDF mode source key mask +#define KDF_MODE_SOURCE_TEMPKEY ((uint8_t)0x00) //!< KDF mode source key in TempKey +#define KDF_MODE_SOURCE_TEMPKEY_UP ((uint8_t)0x01) //!< KDF mode source key in upper TempKey +#define KDF_MODE_SOURCE_SLOT ((uint8_t)0x02) //!< KDF mode source key in a slot +#define KDF_MODE_SOURCE_ALTKEYBUF ((uint8_t)0x03) //!< KDF mode source key in alternate key buffer + +#define KDF_MODE_TARGET_MASK ((uint8_t)0x1C) //!< KDF mode target key mask +#define KDF_MODE_TARGET_TEMPKEY ((uint8_t)0x00) //!< KDF mode target key in TempKey +#define KDF_MODE_TARGET_TEMPKEY_UP ((uint8_t)0x04) //!< KDF mode target key in upper TempKey +#define KDF_MODE_TARGET_SLOT ((uint8_t)0x08) //!< KDF mode target key in slot +#define KDF_MODE_TARGET_ALTKEYBUF ((uint8_t)0x0C) //!< KDF mode target key in alternate key buffer +#define KDF_MODE_TARGET_OUTPUT ((uint8_t)0x10) //!< KDF mode target key in output buffer +#define KDF_MODE_TARGET_OUTPUT_ENC ((uint8_t)0x14) //!< KDF mode target key encrypted in output buffer + +#define KDF_MODE_ALG_MASK ((uint8_t)0x60) //!< KDF mode algorithm mask +#define KDF_MODE_ALG_PRF ((uint8_t)0x00) //!< KDF mode PRF algorithm +#define KDF_MODE_ALG_AES ((uint8_t)0x20) //!< KDF mode AES algorithm +#define KDF_MODE_ALG_HKDF ((uint8_t)0x40) //!< KDF mode HKDF algorithm + +#define KDF_DETAILS_PRF_KEY_LEN_MASK ((uint32_t)0x00000003) //!< KDF details for PRF, source key length mask +#define KDF_DETAILS_PRF_KEY_LEN_16 ((uint32_t)0x00000000) //!< KDF details for PRF, source key length is 16 bytes +#define KDF_DETAILS_PRF_KEY_LEN_32 ((uint32_t)0x00000001) //!< KDF details for PRF, source key length is 32 bytes +#define KDF_DETAILS_PRF_KEY_LEN_48 ((uint32_t)0x00000002) //!< KDF details for PRF, source key length is 48 bytes +#define KDF_DETAILS_PRF_KEY_LEN_64 ((uint32_t)0x00000003) //!< KDF details for PRF, source key length is 64 bytes + +#define KDF_DETAILS_PRF_TARGET_LEN_MASK ((uint32_t)0x00000100) //!< KDF details for PRF, target length mask +#define KDF_DETAILS_PRF_TARGET_LEN_32 ((uint32_t)0x00000000) //!< KDF details for PRF, target length is 32 bytes +#define KDF_DETAILS_PRF_TARGET_LEN_64 ((uint32_t)0x00000100) //!< KDF details for PRF, target length is 64 bytes + +#define KDF_DETAILS_PRF_AEAD_MASK ((uint32_t)0x00000600) //!< KDF details for PRF, AEAD processing mask +#define KDF_DETAILS_PRF_AEAD_MODE0 ((uint32_t)0x00000000) //!< KDF details for PRF, AEAD no processing +#define KDF_DETAILS_PRF_AEAD_MODE1 ((uint32_t)0x00000200) //!< KDF details for PRF, AEAD First 32 go to target, second 32 go to output buffer + +#define KDF_DETAILS_AES_KEY_LOC_MASK ((uint32_t)0x00000003) //!< KDF details for AES, key location mask + +#define KDF_DETAILS_HKDF_MSG_LOC_MASK ((uint32_t)0x00000003) //!< KDF details for HKDF, message location mask +#define KDF_DETAILS_HKDF_MSG_LOC_SLOT ((uint32_t)0x00000000) //!< KDF details for HKDF, message location in slot +#define KDF_DETAILS_HKDF_MSG_LOC_TEMPKEY ((uint32_t)0x00000001) //!< KDF details for HKDF, message location in TempKey +#define KDF_DETAILS_HKDF_MSG_LOC_INPUT ((uint32_t)0x00000002) //!< KDF details for HKDF, message location in input parameter +#define KDF_DETAILS_HKDF_MSG_LOC_IV ((uint32_t)0x00000003) //!< KDF details for HKDF, message location is a special IV function +#define KDF_DETAILS_HKDF_ZERO_KEY ((uint32_t)0x00000004) //!< KDF details for HKDF, key is 32 bytes of zero +/** @} */ + +/** \name Definitions for the Lock Command + @{ */ +#define LOCK_ZONE_IDX ATCA_PARAM1_IDX //!< Lock command index for zone +#define LOCK_SUMMARY_IDX ATCA_PARAM2_IDX //!< Lock command index for summary +#define LOCK_COUNT ATCA_CMD_SIZE_MIN //!< Lock command packet size +#define LOCK_ZONE_CONFIG ((uint8_t)0x00) //!< Lock zone is Config +#define LOCK_ZONE_DATA ((uint8_t)0x01) //!< Lock zone is OTP or Data +#define LOCK_ZONE_DATA_SLOT ((uint8_t)0x02) //!< Lock slot of Data +#define LOCK_ECC204_ZONE_DATA ((uint8_t)0x00) //!< Lock ECC204 Data zone by slot +#define LOCK_ECC204_ZONE_CONFIG ((uint8_t)0x01) //!< Lock ECC204 configuration zone by slot +#define LOCK_ZONE_NO_CRC ((uint8_t)0x80) //!< Lock command: Ignore summary. +#define LOCK_ZONE_MASK (0xBF) //!< Lock parameter 1 bits 6 are 0. +#define ATCA_UNLOCKED (0x55) //!< Value indicating an unlocked zone +#define ATCA_LOCKED (0x00) //!< Value indicating a locked zone +#define LOCK_RSP_SIZE ATCA_RSP_SIZE_MIN //!< Lock command response packet size +/** @} */ + +/** \name Definitions for the MAC Command + @{ */ +#define MAC_MODE_IDX ATCA_PARAM1_IDX //!< MAC command index for mode +#define MAC_KEYID_IDX ATCA_PARAM2_IDX //!< MAC command index for key id +#define MAC_CHALLENGE_IDX ATCA_DATA_IDX //!< MAC command index for optional challenge +#define MAC_COUNT_SHORT ATCA_CMD_SIZE_MIN //!< MAC command packet size without challenge +#define MAC_COUNT_LONG (39) //!< MAC command packet size with challenge +#define MAC_MODE_CHALLENGE ((uint8_t)0x00) //!< MAC mode 0: first SHA block from data slot +#define MAC_MODE_BLOCK2_TEMPKEY ((uint8_t)0x01) //!< MAC mode bit 0: second SHA block from TempKey +#define MAC_MODE_BLOCK1_TEMPKEY ((uint8_t)0x02) //!< MAC mode bit 1: first SHA block from TempKey +#define MAC_MODE_SOURCE_FLAG_MATCH ((uint8_t)0x04) //!< MAC mode bit 2: match TempKey.SourceFlag +#define MAC_MODE_PTNONCE_TEMPKEY ((uint8_t)0x06) //!< MAC mode bit 0: second SHA block from TempKey +#define MAC_MODE_PASSTHROUGH ((uint8_t)0x07) //!< MAC mode bit 0-2: pass-through mode +#define MAC_MODE_INCLUDE_OTP_88 ((uint8_t)0x10) //!< MAC mode bit 4: include first 88 OTP bits +#define MAC_MODE_INCLUDE_OTP_64 ((uint8_t)0x20) //!< MAC mode bit 5: include first 64 OTP bits +#define MAC_MODE_INCLUDE_SN ((uint8_t)0x40) //!< MAC mode bit 6: include serial number +#define MAC_CHALLENGE_SIZE (32) //!< MAC size of challenge +#define MAC_SIZE (32) //!< MAC size of response +#define MAC_MODE_MASK ((uint8_t)0x77) //!< MAC mode bits 3 and 7 are 0. +#define MAC_RSP_SIZE ATCA_RSP_SIZE_32 //!< MAC command response packet size +/** @} */ + +/** \name Definitions for the Nonce Command + @{ */ +#define NONCE_MODE_IDX ATCA_PARAM1_IDX //!< Nonce command index for mode +#define NONCE_PARAM2_IDX ATCA_PARAM2_IDX //!< Nonce command index for 2. parameter +#define NONCE_INPUT_IDX ATCA_DATA_IDX //!< Nonce command index for input data +#define NONCE_COUNT_SHORT (ATCA_CMD_SIZE_MIN + 20) //!< Nonce command packet size for 20 bytes of NumIn +#define NONCE_COUNT_LONG (ATCA_CMD_SIZE_MIN + 32) //!< Nonce command packet size for 32 bytes of NumIn +#define NONCE_COUNT_LONG_64 (ATCA_CMD_SIZE_MIN + 64) //!< Nonce command packet size for 64 bytes of NumIn +#define NONCE_MODE_MASK ((uint8_t)0x03) //!< Nonce mode bits 2 to 7 are 0. +#define NONCE_MODE_SEED_UPDATE ((uint8_t)0x00) //!< Nonce mode: update seed +#define NONCE_MODE_NO_SEED_UPDATE ((uint8_t)0x01) //!< Nonce mode: do not update seed +#define NONCE_MODE_INVALID ((uint8_t)0x02) //!< Nonce mode 2 is invalid. +#define NONCE_MODE_PASSTHROUGH ((uint8_t)0x03) //!< Nonce mode: pass-through +#define NONCE_MODE_GEN_SESSION_KEY ((uint8_t)0x02) //!< NOnce mode: Generate session key in ECC204 device + +#define NONCE_MODE_INPUT_LEN_MASK ((uint8_t)0x20) //!< Nonce mode: input size mask +#define NONCE_MODE_INPUT_LEN_32 ((uint8_t)0x00) //!< Nonce mode: input size is 32 bytes +#define NONCE_MODE_INPUT_LEN_64 ((uint8_t)0x20) //!< Nonce mode: input size is 64 bytes + +#define NONCE_MODE_TARGET_MASK ((uint8_t)0xC0) //!< Nonce mode: target mask +#define NONCE_MODE_TARGET_TEMPKEY ((uint8_t)0x00) //!< Nonce mode: target is TempKey +#define NONCE_MODE_TARGET_MSGDIGBUF ((uint8_t)0x40) //!< Nonce mode: target is Message Digest Buffer +#define NONCE_MODE_TARGET_ALTKEYBUF ((uint8_t)0x80) //!< Nonce mode: target is Alternate Key Buffer + +#define NONCE_ZERO_CALC_MASK ((uint16_t)0x8000) //!< Nonce zero (param2): calculation mode mask +#define NONCE_ZERO_CALC_RANDOM ((uint16_t)0x0000) //!< Nonce zero (param2): calculation mode random, use RNG in calculation and return RNG output +#define NONCE_ZERO_CALC_TEMPKEY ((uint16_t)0x8000) //!< Nonce zero (param2): calculation mode TempKey, use TempKey in calculation and return new TempKey value + +#define NONCE_NUMIN_SIZE (20) //!< Nonce NumIn size for random modes +#define NONCE_NUMIN_SIZE_PASSTHROUGH (32) //!< Nonce NumIn size for 32-byte pass-through mode + +#define NONCE_RSP_SIZE_SHORT ATCA_RSP_SIZE_MIN //!< Nonce command response packet size with no output +#define NONCE_RSP_SIZE_LONG ATCA_RSP_SIZE_32 //!< Nonce command response packet size with output +/** @} */ + +/** \name Definitions for the Pause Command + @{ */ +#define PAUSE_SELECT_IDX ATCA_PARAM1_IDX //!< Pause command index for Selector +#define PAUSE_PARAM2_IDX ATCA_PARAM2_IDX //!< Pause command index for 2. parameter +#define PAUSE_COUNT ATCA_CMD_SIZE_MIN //!< Pause command packet size +#define PAUSE_RSP_SIZE ATCA_RSP_SIZE_MIN //!< Pause command response packet size +/** @} */ + +/** \name Definitions for the PrivWrite Command + @{ */ +#define PRIVWRITE_ZONE_IDX ATCA_PARAM1_IDX //!< PrivWrite command index for zone +#define PRIVWRITE_KEYID_IDX ATCA_PARAM2_IDX //!< PrivWrite command index for KeyID +#define PRIVWRITE_VALUE_IDX ( 5) //!< PrivWrite command index for value +#define PRIVWRITE_MAC_IDX (41) //!< PrivWrite command index for MAC +#define PRIVWRITE_COUNT (75) //!< PrivWrite command packet size +#define PRIVWRITE_ZONE_MASK ((uint8_t)0x40) //!< PrivWrite zone bits 0 to 5 and 7 are 0. +#define PRIVWRITE_MODE_ENCRYPT ((uint8_t)0x40) //!< PrivWrite mode: encrypted +#define PRIVWRITE_RSP_SIZE ATCA_RSP_SIZE_MIN //!< PrivWrite command response packet size +/** @} */ + +/** \name Definitions for the Random Command + @{ */ +#define RANDOM_MODE_IDX ATCA_PARAM1_IDX //!< Random command index for mode +#define RANDOM_PARAM2_IDX ATCA_PARAM2_IDX //!< Random command index for 2. parameter +#define RANDOM_COUNT ATCA_CMD_SIZE_MIN //!< Random command packet size +#define RANDOM_SEED_UPDATE ((uint8_t)0x00) //!< Random mode for automatic seed update +#define RANDOM_NO_SEED_UPDATE ((uint8_t)0x01) //!< Random mode for no seed update +#define RANDOM_NUM_SIZE ((uint8_t)32) //!< Number of bytes in the data packet of a random command +#define RANDOM_RSP_SIZE ATCA_RSP_SIZE_32 //!< Random command response packet size +/** @} */ + +/** \name Definitions for the Read Command + @{ */ +#define READ_ZONE_IDX ATCA_PARAM1_IDX //!< Read command index for zone +#define READ_ADDR_IDX ATCA_PARAM2_IDX //!< Read command index for address +#define READ_COUNT ATCA_CMD_SIZE_MIN //!< Read command packet size +#define READ_ZONE_MASK ((uint8_t)0x83) //!< Read zone bits 2 to 6 are 0. +#define READ_4_RSP_SIZE ATCA_RSP_SIZE_VAL //!< Read command response packet size when reading 4 bytes +#define READ_32_RSP_SIZE ATCA_RSP_SIZE_32 //!< Read command response packet size when reading 32 bytes +/** @} */ + +/** \name Definitions for the SecureBoot Command + @{ */ +#define SECUREBOOT_MODE_IDX ATCA_PARAM1_IDX //!< SecureBoot command index for mode +#define SECUREBOOT_DIGEST_SIZE (32) //!< SecureBoot digest input size +#define SECUREBOOT_SIGNATURE_SIZE (64) //!< SecureBoot signature input size +#define SECUREBOOT_COUNT_DIG (ATCA_CMD_SIZE_MIN + SECUREBOOT_DIGEST_SIZE) //!< SecureBoot command packet size for just a digest +#define SECUREBOOT_COUNT_DIG_SIG (ATCA_CMD_SIZE_MIN + SECUREBOOT_DIGEST_SIZE + SECUREBOOT_SIGNATURE_SIZE) //!< SecureBoot command packet size for a digest and signature +#define SECUREBOOT_MAC_SIZE (32) //!< SecureBoot MAC output size +#define SECUREBOOT_RSP_SIZE_NO_MAC ATCA_RSP_SIZE_MIN //!< SecureBoot response packet size for no MAC +#define SECUREBOOT_RSP_SIZE_MAC (ATCA_PACKET_OVERHEAD + SECUREBOOT_MAC_SIZE) //!< SecureBoot response packet size with MAC + +#define SECUREBOOT_MODE_MASK ((uint8_t)0x07) //!< SecureBoot mode mask +#define SECUREBOOT_MODE_FULL ((uint8_t)0x05) //!< SecureBoot mode Full +#define SECUREBOOT_MODE_FULL_STORE ((uint8_t)0x06) //!< SecureBoot mode FullStore +#define SECUREBOOT_MODE_FULL_COPY ((uint8_t)0x07) //!< SecureBoot mode FullCopy +#define SECUREBOOT_MODE_PROHIBIT_FLAG ((uint8_t)0x40) //!< SecureBoot mode flag to prohibit SecureBoot until next power cycle +#define SECUREBOOT_MODE_ENC_MAC_FLAG ((uint8_t)0x80) //!< SecureBoot mode flag for encrypted digest and returning validating MAC + +#define SECUREBOOTCONFIG_OFFSET (70) //!< SecureBootConfig byte offset into the configuration zone +#define SECUREBOOTCONFIG_MODE_MASK ((uint16_t)0x0003) //!< Mask for SecureBootMode field in SecureBootConfig value +#define SECUREBOOTCONFIG_MODE_DISABLED ((uint16_t)0x0000) //!< Disabled SecureBootMode in SecureBootConfig value +#define SECUREBOOTCONFIG_MODE_FULL_BOTH ((uint16_t)0x0001) //!< Both digest and signature always required SecureBootMode in SecureBootConfig value +#define SECUREBOOTCONFIG_MODE_FULL_SIG ((uint16_t)0x0002) //!< Signature stored SecureBootMode in SecureBootConfig value +#define SECUREBOOTCONFIG_MODE_FULL_DIG ((uint16_t)0x0003) //!< Digest stored SecureBootMode in SecureBootConfig value +/** @} */ + +/** \name Definitions for the SelfTest Command + @{ */ +#define SELFTEST_MODE_IDX ATCA_PARAM1_IDX //!< SelfTest command index for mode +#define SELFTEST_COUNT ATCA_CMD_SIZE_MIN //!< SelfTest command packet size +#define SELFTEST_MODE_RNG ((uint8_t)0x01) //!< SelfTest mode RNG DRBG function +#define SELFTEST_MODE_ECDSA_SIGN_VERIFY ((uint8_t)0x02) //!< SelfTest mode ECDSA verify function +#define SELFTEST_MODE_ECDH ((uint8_t)0x08) //!< SelfTest mode ECDH function +#define SELFTEST_MODE_AES ((uint8_t)0x10) //!< SelfTest mode AES encrypt function +#define SELFTEST_MODE_SHA ((uint8_t)0x20) //!< SelfTest mode SHA function +#define SELFTEST_MODE_ALL ((uint8_t)0x3B) //!< SelfTest mode all algorithms +#define SELFTEST_RSP_SIZE ATCA_RSP_SIZE_MIN //!< SelfTest command response packet size +/** @} */ + +/** \name Definitions for the SHA Command + @{ */ +#define SHA_COUNT_SHORT ATCA_CMD_SIZE_MIN +#define SHA_COUNT_LONG ATCA_CMD_SIZE_MIN //!< Just a starting size +#define ATCA_SHA_DIGEST_SIZE (32) +#define SHA_DATA_MAX (64) + +#define SHA_MODE_MASK ((uint8_t)0x07) //!< Mask the bit 0-2 +#define SHA_MODE_SHA256_START ((uint8_t)0x00) //!< Initialization, does not accept a message +#define SHA_MODE_SHA256_UPDATE ((uint8_t)0x01) //!< Add 64 bytes in the meesage to the SHA context +#define SHA_MODE_SHA256_END ((uint8_t)0x02) //!< Complete the calculation and return the digest +#define SHA_MODE_SHA256_PUBLIC ((uint8_t)0x03) //!< Add 64 byte ECC public key in the slot to the SHA context +#define SHA_MODE_HMAC_START ((uint8_t)0x04) //!< Initialization, HMAC calculation +#define SHA_MODE_ECC204_HMAC_START ((uint8_t)0x03) //!< Initialization, HMAC calculation for ECC204 +#define SHA_MODE_HMAC_UPDATE ((uint8_t)0x01) //!< Add 64 bytes in the meesage to the SHA context +#define SHA_MODE_HMAC_END ((uint8_t)0x05) //!< Complete the HMAC computation and return digest +#define SHA_MODE_608_HMAC_END ((uint8_t)0x02) //!< Complete the HMAC computation and return digest... Different command on 608 +#define SHA_MODE_ECC204_HMAC_END ((uint8_t)0x02) //!< Complete the HMAC computation and return digest... Different mode on ECC204 +#define SHA_MODE_READ_CONTEXT ((uint8_t)0x06) //!< Read current SHA-256 context out of the device +#define SHA_MODE_WRITE_CONTEXT ((uint8_t)0x07) //!< Restore a SHA-256 context into the device +#define SHA_MODE_TARGET_MASK ((uint8_t)0xC0) //!< Resulting digest target location mask + +#define SHA_RSP_SIZE ATCA_RSP_SIZE_32 //!< SHA command response packet size +#define SHA_RSP_SIZE_SHORT ATCA_RSP_SIZE_MIN //!< SHA command response packet size only status code +#define SHA_RSP_SIZE_LONG ATCA_RSP_SIZE_32 //!< SHA command response packet size +/** @} */ + +/** @} *//** \name Definitions for the Sign Command + @{ */ +#define SIGN_MODE_IDX ATCA_PARAM1_IDX //!< Sign command index for mode +#define SIGN_KEYID_IDX ATCA_PARAM2_IDX //!< Sign command index for key id +#define SIGN_COUNT ATCA_CMD_SIZE_MIN //!< Sign command packet size +#define SIGN_MODE_MASK ((uint8_t)0xE1) //!< Sign mode bits 1 to 4 are 0 +#define SIGN_MODE_INTERNAL ((uint8_t)0x00) //!< Sign mode 0: internal +#define SIGN_MODE_INVALIDATE ((uint8_t)0x01) //!< Sign mode bit 1: Signature will be used for Verify(Invalidate) +#define SIGN_MODE_INCLUDE_SN ((uint8_t)0x40) //!< Sign mode bit 6: include serial number +#define SIGN_MODE_EXTERNAL ((uint8_t)0x80) //!< Sign mode bit 7: external +#define SIGN_MODE_SOURCE_MASK ((uint8_t)0x20) //!< Sign mode message source mask +#define SIGN_MODE_SOURCE_TEMPKEY ((uint8_t)0x00) //!< Sign mode message source is TempKey +#define SIGN_MODE_SOURCE_MSGDIGBUF ((uint8_t)0x20) //!< Sign mode message source is the Message Digest Buffer +#define SIGN_RSP_SIZE ATCA_RSP_SIZE_MAX //!< Sign command response packet size +/** @} */ + +/** \name Definitions for the UpdateExtra Command + @{ */ +#define UPDATE_MODE_IDX ATCA_PARAM1_IDX //!< UpdateExtra command index for mode +#define UPDATE_VALUE_IDX ATCA_PARAM2_IDX //!< UpdateExtra command index for new value +#define UPDATE_COUNT ATCA_CMD_SIZE_MIN //!< UpdateExtra command packet size +#define UPDATE_MODE_USER_EXTRA ((uint8_t)0x00) //!< UpdateExtra mode update UserExtra (config byte 84) +#define UPDATE_MODE_SELECTOR ((uint8_t)0x01) //!< UpdateExtra mode update Selector (config byte 85) +#define UPDATE_MODE_USER_EXTRA_ADD UPDATE_MODE_SELECTOR //!< UpdateExtra mode update UserExtraAdd (config byte 85) +#define UPDATE_MODE_DEC_COUNTER ((uint8_t)0x02) //!< UpdateExtra mode: decrement counter +#define UPDATE_RSP_SIZE ATCA_RSP_SIZE_MIN //!< UpdateExtra command response packet size +/** @} */ + +/** \name Definitions for the Verify Command + @{ */ +#define VERIFY_MODE_IDX ATCA_PARAM1_IDX //!< Verify command index for mode +#define VERIFY_KEYID_IDX ATCA_PARAM2_IDX //!< Verify command index for key id +#define VERIFY_DATA_IDX ( 5) //!< Verify command index for data +#define VERIFY_256_STORED_COUNT ( 71) //!< Verify command packet size for 256-bit key in stored mode +#define VERIFY_283_STORED_COUNT ( 79) //!< Verify command packet size for 283-bit key in stored mode +#define VERIFY_256_VALIDATE_COUNT ( 90) //!< Verify command packet size for 256-bit key in validate mode +#define VERIFY_283_VALIDATE_COUNT ( 98) //!< Verify command packet size for 283-bit key in validate mode +#define VERIFY_256_EXTERNAL_COUNT (135) //!< Verify command packet size for 256-bit key in external mode +#define VERIFY_283_EXTERNAL_COUNT (151) //!< Verify command packet size for 283-bit key in external mode +#define VERIFY_256_KEY_SIZE ( 64) //!< Verify key size for 256-bit key +#define VERIFY_283_KEY_SIZE ( 72) //!< Verify key size for 283-bit key +#define VERIFY_256_SIGNATURE_SIZE ( 64) //!< Verify signature size for 256-bit key +#define VERIFY_283_SIGNATURE_SIZE ( 72) //!< Verify signature size for 283-bit key +#define VERIFY_OTHER_DATA_SIZE ( 19) //!< Verify size of "other data" +#define VERIFY_MODE_MASK ((uint8_t)0x07) //!< Verify mode bits 3 to 7 are 0 +#define VERIFY_MODE_STORED ((uint8_t)0x00) //!< Verify mode: stored +#define VERIFY_MODE_VALIDATE_EXTERNAL ((uint8_t)0x01) //!< Verify mode: validate external +#define VERIFY_MODE_EXTERNAL ((uint8_t)0x02) //!< Verify mode: external +#define VERIFY_MODE_VALIDATE ((uint8_t)0x03) //!< Verify mode: validate +#define VERIFY_MODE_INVALIDATE ((uint8_t)0x07) //!< Verify mode: invalidate +#define VERIFY_MODE_SOURCE_MASK ((uint8_t)0x20) //!< Verify mode message source mask +#define VERIFY_MODE_SOURCE_TEMPKEY ((uint8_t)0x00) //!< Verify mode message source is TempKey +#define VERIFY_MODE_SOURCE_MSGDIGBUF ((uint8_t)0x20) //!< Verify mode message source is the Message Digest Buffer +#define VERIFY_MODE_MAC_FLAG ((uint8_t)0x80) //!< Verify mode: MAC +#define VERIFY_KEY_B283 ((uint16_t)0x0000) //!< Verify key type: B283 +#define VERIFY_KEY_K283 ((uint16_t)0x0001) //!< Verify key type: K283 +#define VERIFY_KEY_P256 ((uint16_t)0x0004) //!< Verify key type: P256 +#define VERIFY_RSP_SIZE ATCA_RSP_SIZE_MIN //!< Verify command response packet size +#define VERIFY_RSP_SIZE_MAC ATCA_RSP_SIZE_32 //!< Verify command response packet size with validating MAC +/** @} */ + +/** \name Definitions for the Write Command + @{ */ +#define WRITE_ZONE_IDX ATCA_PARAM1_IDX //!< Write command index for zone +#define WRITE_ADDR_IDX ATCA_PARAM2_IDX //!< Write command index for address +#define WRITE_VALUE_IDX ATCA_DATA_IDX //!< Write command index for data +#define WRITE_MAC_VS_IDX ( 9) //!< Write command index for MAC following short data +#define WRITE_MAC_VL_IDX (37) //!< Write command index for MAC following long data +#define WRITE_MAC_SIZE (32) //!< Write MAC size +#define WRITE_ZONE_MASK ((uint8_t)0xC3) //!< Write zone bits 2 to 5 are 0. +#define WRITE_ZONE_WITH_MAC ((uint8_t)0x40) //!< Write zone bit 6: write encrypted with MAC +#define WRITE_ZONE_OTP ((uint8_t)1) //!< Write zone id OTP +#define WRITE_ZONE_DATA ((uint8_t)2) //!< Write zone id data +#define WRITE_RSP_SIZE ATCA_RSP_SIZE_MIN //!< Write command response packet size +/** @} */ + +#ifdef __cplusplus +} +#endif +#endif diff --git a/drivers/ecc108a/cryptoauthlib/calib/calib_derivekey.c b/drivers/ecc108a/cryptoauthlib/calib/calib_derivekey.c new file mode 100644 index 0000000..dfc3881 --- /dev/null +++ b/drivers/ecc108a/cryptoauthlib/calib/calib_derivekey.c @@ -0,0 +1,86 @@ +/** + * \file + * \brief CryptoAuthLib Basic API methods for DeriveKey command. + * + * The DeriveKey command combines the current value of a key with the nonce + * stored in TempKey using SHA-256 and derives a new key. + * + * \note List of devices that support this command - ATSHA204A, ATECC108A, + * ATECC508A, and ATECC608A/B. There are differences in the modes that they + * support. Refer to device datasheets for full details. + * + * \copyright (c) 2015-2020 Microchip Technology Inc. and its subsidiaries. + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip software + * and any derivatives exclusively with Microchip products. It is your + * responsibility to comply with third party license terms applicable to your + * use of third party software (including open source software) that may + * accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER + * EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, + * SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE + * OF ANY KIND WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF + * MICROCHIP HAS BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE + * FORESEEABLE. TO THE FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL + * LIABILITY ON ALL CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED + * THE AMOUNT OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR + * THIS SOFTWARE. + */ + +#include "cryptoauthlib.h" + +/** \brief Executes the DeviveKey command for deriving a new key from a + * nonce (TempKey) and an existing key. + * + * \param[in] device Device context pointer + * \param[in] mode Bit 2 must match the value in TempKey.SourceFlag + * \param[in] target_key Key slot to be written + * \param[in] mac Optional 32 byte MAC used to validate operation. NULL + * if not required. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_derivekey(ATCADevice device, uint8_t mode, uint16_t target_key, const uint8_t* mac) +{ + ATCAPacket packet; + ATCA_STATUS status = ATCA_GEN_FAIL; + + do + { + if (device == NULL) + { + status = ATCA_TRACE(ATCA_BAD_PARAM, "NULL pointer received"); + break; + } + + // build a deriveKey command (pass through mode) + packet.param1 = mode; + packet.param2 = target_key; + + if (mac != NULL) + { + memcpy(packet.data, mac, MAC_SIZE); + } + + if ((status = atDeriveKey(atcab_get_device_type_ext(device), &packet, mac != NULL)) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "atDeriveKey - failed"); + break; + } + + if ((status = atca_execute_command(&packet, device)) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "calib_derivekey - execution failed"); + break; + } + + } + while (0); + + return status; +} diff --git a/drivers/ecc108a/cryptoauthlib/calib/calib_execution.c b/drivers/ecc108a/cryptoauthlib/calib/calib_execution.c new file mode 100644 index 0000000..0cc5833 --- /dev/null +++ b/drivers/ecc108a/cryptoauthlib/calib/calib_execution.c @@ -0,0 +1,563 @@ +/** + * \file + * \brief Implements an execution handler that executes a given command on a + * device and returns the results. + * + * This implementation wraps Polling and No polling (simple wait) schemes into + * a single method and use it across the library. Polling is used by default, + * however, by defining the ATCA_NO_POLL symbol the code will instead wait an + * estimated max execution time before requesting the result. + * + * \copyright (c) 2015-2020 Microchip Technology Inc. and its subsidiaries. + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip software + * and any derivatives exclusively with Microchip products. It is your + * responsibility to comply with third party license terms applicable to your + * use of third party software (including open source software) that may + * accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER + * EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, + * SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE + * OF ANY KIND WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF + * MICROCHIP HAS BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE + * FORESEEABLE. TO THE FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL + * LIABILITY ON ALL CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED + * THE AMOUNT OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR + * THIS SOFTWARE. + */ + +#include "cryptoauthlib.h" + + +#if defined(ATCA_NO_POLL) && defined(ATCA_ATECC608_SUPPORT) && !defined(ATCA_ATECC608A_SUPPORT) +#warning "ATECC608B supports only polling mode, if you are using an ATECC608A specify ATCA_ATECC608A_SUPPORT manually" +#endif + + +#ifdef ATCA_NO_POLL +// *INDENT-OFF* - Preserve time formatting from the code formatter +/*Execution times for ATSHA204A supported commands...*/ +static const device_execution_time_t device_execution_time_204[] = { + { ATCA_CHECKMAC, 38}, + { ATCA_DERIVE_KEY, 62}, + { ATCA_GENDIG, 43}, + { ATCA_HMAC, 69}, + { ATCA_INFO, 2}, + { ATCA_LOCK, 24}, + { ATCA_MAC, 35}, + { ATCA_NONCE, 60}, + { ATCA_PAUSE, 2}, + { ATCA_RANDOM, 50}, + { ATCA_READ, 5}, + { ATCA_SHA, 22}, + { ATCA_UPDATE_EXTRA, 12}, + { ATCA_WRITE, 42} +}; + +/*Execution times for ATSHA206A supported commands...*/ +static const device_execution_time_t device_execution_time_206[] = { + { ATCA_DERIVE_KEY, 62}, + { ATCA_INFO, 2}, + { ATCA_MAC, 35}, + { ATCA_READ, 5}, + { ATCA_WRITE, 42} +}; + +/*Execution times for ATECC108A supported commands...*/ +static const device_execution_time_t device_execution_time_108[] = { + { ATCA_CHECKMAC, 13}, + { ATCA_COUNTER, 20}, + { ATCA_DERIVE_KEY, 50}, + { ATCA_GENDIG, 11}, + { ATCA_GENKEY, 115}, + { ATCA_HMAC, 23}, + { ATCA_INFO, 2}, + { ATCA_LOCK, 32}, + { ATCA_MAC, 14}, + { ATCA_NONCE, 29}, + { ATCA_PAUSE, 3}, + { ATCA_PRIVWRITE, 48}, + { ATCA_RANDOM, 23}, + { ATCA_READ, 5}, + { ATCA_SHA, 9}, + { ATCA_SIGN, 60}, + { ATCA_UPDATE_EXTRA, 10}, + { ATCA_VERIFY, 72}, + { ATCA_WRITE, 26} +}; + +/*Execution times for ATECC508A supported commands...*/ +static const device_execution_time_t device_execution_time_508[] = { + { ATCA_CHECKMAC, 13}, + { ATCA_COUNTER, 20}, + { ATCA_DERIVE_KEY, 50}, + { ATCA_ECDH, 58}, + { ATCA_GENDIG, 11}, + { ATCA_GENKEY, 115}, + { ATCA_HMAC, 23}, + { ATCA_INFO, 2}, + { ATCA_LOCK, 32}, + { ATCA_MAC, 14}, + { ATCA_NONCE, 29}, + { ATCA_PAUSE, 3}, + { ATCA_PRIVWRITE, 48}, + { ATCA_RANDOM, 23}, + { ATCA_READ, 5}, + { ATCA_SHA, 9}, + { ATCA_SIGN, 60}, + { ATCA_UPDATE_EXTRA, 10}, + { ATCA_VERIFY, 72}, + { ATCA_WRITE, 26} +}; + +/*Execution times for ATECC608-M0 supported commands...*/ +static const device_execution_time_t device_execution_time_608_m0[] = { + { ATCA_AES, 27}, + { ATCA_CHECKMAC, 40}, + { ATCA_COUNTER, 25}, + { ATCA_DERIVE_KEY, 50}, + { ATCA_ECDH, 75}, + { ATCA_GENDIG, 25}, + { ATCA_GENKEY, 115}, + { ATCA_INFO, 5}, + { ATCA_KDF, 165}, + { ATCA_LOCK, 35}, + { ATCA_MAC, 55}, + { ATCA_NONCE, 20}, + { ATCA_PRIVWRITE, 50}, + { ATCA_RANDOM, 23}, + { ATCA_READ, 5}, + { ATCA_SECUREBOOT, 80}, + { ATCA_SELFTEST, 250}, + { ATCA_SHA, 36}, + { ATCA_SIGN, 115}, + { ATCA_UPDATE_EXTRA, 10}, + { ATCA_VERIFY, 105}, + { ATCA_WRITE, 45} +}; + +/*Execution times for ATECC608-M1 supported commands...*/ +static const device_execution_time_t device_execution_time_608_m1[] = { + { ATCA_AES, 27}, + { ATCA_CHECKMAC, 40}, + { ATCA_COUNTER, 25}, + { ATCA_DERIVE_KEY, 50}, + { ATCA_ECDH, 172}, + { ATCA_GENDIG, 35}, + { ATCA_GENKEY, 215}, + { ATCA_INFO, 5}, + { ATCA_KDF, 165}, + { ATCA_LOCK, 35}, + { ATCA_MAC, 55}, + { ATCA_NONCE, 20}, + { ATCA_PRIVWRITE, 50}, + { ATCA_RANDOM, 23}, + { ATCA_READ, 5}, + { ATCA_SECUREBOOT, 160}, + { ATCA_SELFTEST, 625}, + { ATCA_SHA, 42}, + { ATCA_SIGN, 220}, + { ATCA_UPDATE_EXTRA, 10}, + { ATCA_VERIFY, 295}, + { ATCA_WRITE, 45} +}; + +/*Execution times for ATECC608-M2 supported commands...*/ +static const device_execution_time_t device_execution_time_608_m2[] = { + { ATCA_AES, 27}, + { ATCA_CHECKMAC, 40}, + { ATCA_COUNTER, 25}, + { ATCA_DERIVE_KEY, 50}, + { ATCA_ECDH, 531}, + { ATCA_GENDIG, 35}, + { ATCA_GENKEY, 653}, + { ATCA_INFO, 5}, + { ATCA_KDF, 165}, + { ATCA_LOCK, 35}, + { ATCA_MAC, 55}, + { ATCA_NONCE, 20}, + { ATCA_PRIVWRITE, 50}, + { ATCA_RANDOM, 23}, + { ATCA_READ, 5}, + { ATCA_SECUREBOOT, 480}, + { ATCA_SELFTEST, 2324}, + { ATCA_SHA, 75}, + { ATCA_SIGN, 665}, + { ATCA_UPDATE_EXTRA, 10}, + { ATCA_VERIFY, 1085}, + { ATCA_WRITE, 45} +}; + +/*Execution times for ECC204 supported commands...*/ +static const device_execution_time_t device_execution_time_ecc204[] = { + { ATCA_COUNTER, 1}, + { ATCA_GENKEY, 100}, + { ATCA_INFO, 1}, + { ATCA_LOCK, 6}, + { ATCA_NONCE, 35}, + { ATCA_READ, 1}, + { ATCA_SELFTEST, 110}, + { ATCA_SHA, 4}, + { ATCA_SIGN, 100}, + { ATCA_WRITE, 10} +}; +// *INDENT-ON* +#endif + +#ifdef ATCA_NO_POLL +/** \brief return the typical execution time for the given command + * \param[in] opcode Opcode value of the command + * \param[in] ca_cmd Command object for which the execution times are associated + * \return ATCA_SUCCESS + */ +ATCA_STATUS calib_get_execution_time(uint8_t opcode, ATCADevice device) +{ + ATCA_STATUS status = ATCA_SUCCESS; + const device_execution_time_t *execution_times; + uint8_t i, no_of_commands; + + + switch (device->mIface.mIfaceCFG->devtype) + { + case ATSHA204A: + execution_times = device_execution_time_204; + no_of_commands = sizeof(device_execution_time_204) / sizeof(device_execution_time_t); + break; + + case ATSHA206A: + execution_times = device_execution_time_206; + no_of_commands = sizeof(device_execution_time_206) / sizeof(device_execution_time_t); + break; + + case ATECC108A: + execution_times = device_execution_time_108; + no_of_commands = sizeof(device_execution_time_108) / sizeof(device_execution_time_t); + break; + + case ATECC508A: + execution_times = device_execution_time_508; + no_of_commands = sizeof(device_execution_time_508) / sizeof(device_execution_time_t); + break; + + case ATECC608: + if (device->clock_divider == ATCA_CHIPMODE_CLOCK_DIV_M1) + { + execution_times = device_execution_time_608_m1; + no_of_commands = sizeof(device_execution_time_608_m1) / sizeof(device_execution_time_t); + } + else if (device->clock_divider == ATCA_CHIPMODE_CLOCK_DIV_M2) + { + execution_times = device_execution_time_608_m2; + no_of_commands = sizeof(device_execution_time_608_m2) / sizeof(device_execution_time_t); + } + else + { + // Assume default M0 clock divider + execution_times = device_execution_time_608_m0; + no_of_commands = sizeof(device_execution_time_608_m0) / sizeof(device_execution_time_t); + } + break; + + case ECC204: + execution_times = device_execution_time_ecc204; + no_of_commands = sizeof(device_execution_time_ecc204) / sizeof(device_execution_time_t); + break; + + default: + no_of_commands = 0; + execution_times = NULL; + break; + } + + device->execution_time_msec = ATCA_UNSUPPORTED_CMD; + + for (i = 0; i < no_of_commands; i++) + { + if (execution_times[i].opcode == opcode) + { + device->execution_time_msec = execution_times[i].execution_time_msec; + break; + } + } + + if (device->execution_time_msec == ATCA_UNSUPPORTED_CMD) + { + status = ATCA_BAD_OPCODE; + } + + return status; +} +#endif + +ATCA_STATUS calib_execute_send(ATCADevice device, uint8_t device_address, uint8_t* txdata, uint16_t txlength) +{ + ATCA_STATUS status = ATCA_COMM_FAIL; + + if (!txdata || !txlength) + { + return ATCA_TRACE(ATCA_BAD_PARAM, "NULL pointer encountered"); + } + +#ifdef ATCA_HAL_LEGACY_API + ((void)device_address); + status = atsend(&device->mIface, 0xFF, (uint8_t*)txdata, (int)txlength - 1); +#else + if (atca_iface_is_kit(&device->mIface)) + { + status = atsend(&device->mIface, 0xFF, (uint8_t*)txdata, (int)txlength - 1); + } + else + { + status = atcontrol(&device->mIface, ATCA_HAL_CONTROL_SELECT, NULL, 0); + if (ATCA_UNIMPLEMENTED == status || ATCA_SUCCESS == status) + { + /* Send the command packet to the device */ + status = atsend(&device->mIface, device_address, (uint8_t*)txdata, (int)txlength); + } + (void)atcontrol(&device->mIface, ATCA_HAL_CONTROL_DESELECT, NULL, 0); + } +#endif + + return status; +} + + +ATCA_STATUS calib_execute_receive(ATCADevice device, uint8_t device_address, uint8_t* rxdata, uint16_t* rxlength) +{ + ATCA_STATUS status = ATCA_COMM_FAIL; + + if ((NULL == rxlength) || (NULL == rxdata)) + { + return ATCA_TRACE(ATCA_BAD_PARAM, "NULL pointer encountered"); + } + +#ifdef ATCA_HAL_LEGACY_API + ((void)device_address); + status = atreceive(&device->mIface, 0, rxdata, rxlength); +#else + uint16_t read_length = 1; + uint8_t word_address; + + if (atca_iface_is_kit(&device->mIface)) + { + status = atreceive(&device->mIface, 0, rxdata, rxlength); + } + else + { + do + { + status = atcontrol(&device->mIface, ATCA_HAL_CONTROL_SELECT, NULL, 0); + if (ATCA_UNIMPLEMENTED != status && ATCA_SUCCESS != status) + { + break; + } + + /*Send Word address to device...*/ + if (ATCA_SWI_IFACE == device->mIface.mIfaceCFG->iface_type) + { + word_address = CALIB_SWI_FLAG_TX; + } + else + { + word_address = 0; + } + + // Skip word address send for ECC204 device + if (ECC204 != device->mIface.mIfaceCFG->devtype) + { + if (ATCA_SUCCESS != (status = atsend(&device->mIface, device_address, &word_address, sizeof(word_address)))) + { + break; + } + } + + /* Read length bytes to know number of bytes to read */ + status = atreceive(&device->mIface, device_address, rxdata, &read_length); + if (ATCA_SUCCESS != status) + { + ATCA_TRACE(status, "atreceive - failed"); + break; + } + + /*Calculate bytes to read based on device response*/ + read_length = rxdata[0]; + + if (read_length > *rxlength) + { + status = ATCA_TRACE(ATCA_SMALL_BUFFER, "rxdata is small buffer"); + break; + } + + if (read_length < 4) + { + status = ATCA_TRACE(ATCA_RX_FAIL, "packet size is invalid"); + break; + } + + /* Read given length bytes from device */ + read_length -= 1; + + status = atreceive(&device->mIface, device_address, &rxdata[1], &read_length); + + if (ATCA_SUCCESS != status) + { + status = ATCA_TRACE(status, "atreceive - failed"); + break; + } + + read_length += 1; + + *rxlength = read_length; + } + while (0); + + (void)atcontrol(&device->mIface, ATCA_HAL_CONTROL_DESELECT, NULL, 0); + } +#endif + + return status; +} + +/** \brief Wakes up device, sends the packet, waits for command completion, + * receives response, and puts the device into the idle state. + * + * \param[in,out] packet As input, the packet to be sent. As output, the + * data buffer in the packet structure will contain the + * response. + * \param[in] device CryptoAuthentication device to send the command to. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_execute_command(ATCAPacket* packet, ATCADevice device) +{ + ATCA_STATUS status; + uint32_t execution_or_wait_time; + uint32_t max_delay_count; + uint16_t rxsize; + uint8_t device_address = atcab_get_device_address(device); + int retries = 1; + + do + { +#ifdef ATCA_NO_POLL + if ((status = calib_get_execution_time(packet->opcode, device)) != ATCA_SUCCESS) + { + return status; + } + execution_or_wait_time = device->execution_time_msec; + max_delay_count = 0; +#else + execution_or_wait_time = ATCA_POLLING_INIT_TIME_MSEC; + max_delay_count = ATCA_POLLING_MAX_TIME_MSEC / ATCA_POLLING_FREQUENCY_TIME_MSEC; +#endif + retries = atca_iface_get_retries(&device->mIface); + do + { + if (ATCA_DEVICE_STATE_ACTIVE != device->device_state) + { + if (ATCA_SUCCESS == (status = calib_wakeup(device))) + { + device->device_state = ATCA_DEVICE_STATE_ACTIVE; + } + } + + /* Send the command packet to the device */ + if (ATCA_I2C_IFACE == device->mIface.mIfaceCFG->iface_type) + { + packet->_reserved = 0x03; + } + else if (ATCA_SWI_IFACE == device->mIface.mIfaceCFG->iface_type) + { + packet->_reserved = CALIB_SWI_FLAG_CMD; + } + else if ((ATCA_SWI_GPIO_IFACE == device->mIface.mIfaceCFG->iface_type) && (ECC204 == device->mIface.mIfaceCFG->devtype)) + { + packet->_reserved = 0x03; + } + if (ATCA_RX_NO_RESPONSE == (status = calib_execute_send(device, device_address, (uint8_t*)packet, packet->txsize + 1))) + { + device->device_state = ATCA_DEVICE_STATE_UNKNOWN; + } + else + { + if (ATCA_DEVICE_STATE_ACTIVE != device->device_state) + { + device->device_state = ATCA_DEVICE_STATE_ACTIVE; + } + retries = 0; + } + + } + while (0 < retries--); + + if (ATCA_SUCCESS != status) + { + break; + } + + // Delay for execution time or initial wait before polling + atca_delay_ms(execution_or_wait_time); + + do + { + memset(packet->data, 0, sizeof(packet->data)); + // receive the response + rxsize = sizeof(packet->data); + + if (ATCA_SUCCESS == (status = calib_execute_receive(device, device_address, packet->data, &rxsize))) + { + break; + } + +#ifndef ATCA_NO_POLL + // delay for polling frequency time + atca_delay_ms(ATCA_POLLING_FREQUENCY_TIME_MSEC); +#endif + } + while (max_delay_count-- > 0); + + if (status != ATCA_SUCCESS) + { + break; + } + + // Check response size + if (rxsize < 4) + { + if (rxsize > 0) + { + status = ATCA_RX_FAIL; + } + else + { + status = ATCA_RX_NO_RESPONSE; + } + break; + } + + if ((status = atCheckCrc(packet->data)) != ATCA_SUCCESS) + { + break; + } + + if ((status = isATCAError(packet->data)) != ATCA_SUCCESS) + { + break; + } + } + while (0); + + // Skip Idle for ECC204 device + if (ECC204 != device->mIface.mIfaceCFG->devtype) + { + (void)calib_idle(device); + device->device_state = ATCA_DEVICE_STATE_IDLE; + } + + return status; +} diff --git a/drivers/ecc108a/cryptoauthlib/calib/calib_execution.h b/drivers/ecc108a/cryptoauthlib/calib/calib_execution.h new file mode 100644 index 0000000..df94884 --- /dev/null +++ b/drivers/ecc108a/cryptoauthlib/calib/calib_execution.h @@ -0,0 +1,78 @@ +/** + * \file + * \brief Defines an execution handler that executes a given command on a + * device and returns the results. + * + * The basic flow is to wake the device, send the command, wait/poll for + * completion, and finally receives the response from the device and does + * basic checks before returning to caller. + * + * This handler supports the ATSHA and ATECC device family. + * + * \copyright (c) 2015-2020 Microchip Technology Inc. and its subsidiaries. + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip software + * and any derivatives exclusively with Microchip products. It is your + * responsibility to comply with third party license terms applicable to your + * use of third party software (including open source software) that may + * accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER + * EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, + * SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE + * OF ANY KIND WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF + * MICROCHIP HAS BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE + * FORESEEABLE. TO THE FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL + * LIABILITY ON ALL CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED + * THE AMOUNT OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR + * THIS SOFTWARE. + */ + + +#ifndef CALIB_EXECUTION_H +#define CALIB_EXECUTION_H + +#include "atca_status.h" +#include "calib_command.h" +#include "atca_device.h" +#include "atca_config.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ATCA_UNSUPPORTED_CMD ((uint16_t)0xFFFF) + +#define CALIB_SWI_FLAG_WAKE 0x00 //!< flag preceding a command +#define CALIB_SWI_FLAG_CMD 0x77 //!< flag preceding a command +#define CALIB_SWI_FLAG_TX 0x88 //!< flag requesting a response +#define CALIB_SWI_FLAG_IDLE 0xBB //!< flag requesting to go into Idle mode +#define CALIB_SWI_FLAG_SLEEP 0xCC //!< flag requesting to go into Sleep mode + +#ifdef ATCA_NO_POLL +/** \brief Structure to hold the device execution time and the opcode for the + * corresponding command + */ +typedef struct +{ + uint8_t opcode; + uint16_t execution_time_msec; +}device_execution_time_t; + +ATCA_STATUS calib_get_execution_time(uint8_t opcode, ATCADevice device); +#endif + +#ifndef ATCA_HAL_LEGACY_API +ATCA_STATUS calib_execute_receive(ATCADevice device, uint8_t device_address, uint8_t* rxdata, uint16_t* rxlength); +#endif + +ATCA_STATUS calib_execute_command(ATCAPacket* packet, ATCADevice device); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/drivers/ecc108a/cryptoauthlib/calib/calib_gendig.c b/drivers/ecc108a/cryptoauthlib/calib/calib_gendig.c new file mode 100644 index 0000000..ec00966 --- /dev/null +++ b/drivers/ecc108a/cryptoauthlib/calib/calib_gendig.c @@ -0,0 +1,92 @@ +/** + * \file + * \brief CryptoAuthLib Basic API methods for GenDig command. + * + * The GenDig command uses SHA-256 to combine a stored value with the contents + * of TempKey, which must have been valid prior to the execution of this + * command. + * + * \note List of devices that support this command - ATSHA204A, ATECC108A, + * ATECC508A, and ATECC608A/B. There are differences in the modes that + * they support. Refer to device datasheets for full details. + * + * \copyright (c) 2015-2020 Microchip Technology Inc. and its subsidiaries. + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip software + * and any derivatives exclusively with Microchip products. It is your + * responsibility to comply with third party license terms applicable to your + * use of third party software (including open source software) that may + * accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER + * EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, + * SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE + * OF ANY KIND WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF + * MICROCHIP HAS BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE + * FORESEEABLE. TO THE FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL + * LIABILITY ON ALL CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED + * THE AMOUNT OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR + * THIS SOFTWARE. + */ + +#include "cryptoauthlib.h" + +/** \brief Issues a GenDig command, which performs a SHA256 hash on the source data indicated by zone with the + * contents of TempKey. See the CryptoAuth datasheet for your chip to see what the values of zone + * correspond to. + * \param[in] device Device context pointer + * \param[in] zone Designates the source of the data to hash with TempKey. + * \param[in] key_id Indicates the key, OTP block, or message order for shared nonce mode. + * \param[in] other_data Four bytes of data for SHA calculation when using a NoMac key, 32 bytes for + * "Shared Nonce" mode, otherwise ignored (can be NULL). + * \param[in] other_data_size Size of other_data in bytes. + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_gendig(ATCADevice device, uint8_t zone, uint16_t key_id, const uint8_t *other_data, uint8_t other_data_size) +{ + ATCAPacket packet; + ATCA_STATUS status = ATCA_GEN_FAIL; + bool is_no_mac_key = false; + + if ((device == NULL) || (other_data_size > 0 && other_data == NULL)) + { + return ATCA_TRACE(ATCA_BAD_PARAM, "NULL pointer received"); + } + + do + { + // build gendig command + packet.param1 = zone; + packet.param2 = key_id; + + if (packet.param1 == GENDIG_ZONE_SHARED_NONCE && other_data_size >= ATCA_BLOCK_SIZE) + { + memcpy(&packet.data[0], &other_data[0], ATCA_BLOCK_SIZE); + } + else if (packet.param1 == GENDIG_ZONE_DATA && other_data_size >= ATCA_WORD_SIZE) + { + memcpy(&packet.data[0], &other_data[0], ATCA_WORD_SIZE); + is_no_mac_key = true; + } + + if ((status = atGenDig(atcab_get_device_type_ext(device), &packet, is_no_mac_key)) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "atGenDig - failed"); + break; + } + + if ((status = atca_execute_command(&packet, device)) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "calib_gendig - execution failed"); + break; + } + + } + while (0); + + return status; +} diff --git a/drivers/ecc108a/cryptoauthlib/calib/calib_genkey.c b/drivers/ecc108a/cryptoauthlib/calib/calib_genkey.c new file mode 100644 index 0000000..539f4b6 --- /dev/null +++ b/drivers/ecc108a/cryptoauthlib/calib/calib_genkey.c @@ -0,0 +1,199 @@ +/** + * \file + * \brief CryptoAuthLib Basic API methods for GenKey command. + * + * The GenKey command is used for creating ECC private keys, generating ECC + * public keys, and for digest calculations involving public keys. + * + * \note List of devices that support this command - ATECC108A, ATECC508A, + * ATECC608A/B. There are differences in the modes that they support. + * Refer to device datasheets for full details. + * + * \copyright (c) 2015-2020 Microchip Technology Inc. and its subsidiaries. + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip software + * and any derivatives exclusively with Microchip products. It is your + * responsibility to comply with third party license terms applicable to your + * use of third party software (including open source software) that may + * accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER + * EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, + * SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE + * OF ANY KIND WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF + * MICROCHIP HAS BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE + * FORESEEABLE. TO THE FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL + * LIABILITY ON ALL CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED + * THE AMOUNT OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR + * THIS SOFTWARE. + */ + +#include "cryptoauthlib.h" + +/** \brief Issues GenKey command, which can generate a private key, compute a + * public key, nd/or compute a digest of a public key. + * + * \param[in] device Device context pointer + * \param[in] mode Mode determines what operations the GenKey + * command performs. + * \param[in] key_id Slot to perform the GenKey command on. + * \param[in] other_data OtherData for PubKey digest calculation. Can be set + * to NULL otherwise. + * \param[out] public_key If the mode indicates a public key will be + * calculated, it will be returned here. Format will + * be the X and Y integers in big-endian format. + * 64 bytes for P256 curve. Set to NULL if public key + * isn't required. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_genkey_base(ATCADevice device, uint8_t mode, uint16_t key_id, const uint8_t* other_data, uint8_t* public_key) +{ + ATCAPacket packet; + ATCA_STATUS status = ATCA_GEN_FAIL; + + do + { + if (device == NULL) + { + status = ATCA_TRACE(ATCA_BAD_PARAM, "NULL pointer received"); + break; + } + + // Build GenKey command + packet.param1 = mode; + packet.param2 = key_id; + if (other_data) + { + memcpy(packet.data, other_data, GENKEY_OTHER_DATA_SIZE); + } + + if ((status = atGenKey(atcab_get_device_type_ext(device), &packet)) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "atGenKey - failed"); + break; + } + + if ((status = atca_execute_command(&packet, device)) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "calib_genkey_base - execution failed"); + break; + } + + if (public_key != NULL) + { + if (packet.data[ATCA_COUNT_IDX] == (ATCA_PUB_KEY_SIZE + ATCA_PACKET_OVERHEAD)) + { + memcpy(public_key, &packet.data[ATCA_RSP_DATA_IDX], ATCA_PUB_KEY_SIZE); + } + else + { + status = ATCA_TRACE(ATCA_RX_FAIL, "Received response failure"); + } + } + } + while (0); + + return status; +} + +/** \brief Issues GenKey command, which generates a new random private key in + * slot and returns the public key. + * + * \param[in] device Device context pointer + * \param[in] key_id Slot number where an ECC private key is configured. + * Can also be ATCA_TEMPKEY_KEYID to generate a private + * key in TempKey. + * \param[out] public_key Public key will be returned here. Format will be + * the X and Y integers in big-endian format. + * 64 bytes for P256 curve. Set to NULL if public key + * isn't required. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_genkey(ATCADevice device, uint16_t key_id, uint8_t *public_key) +{ + return calib_genkey_base(device, GENKEY_MODE_PRIVATE, key_id, NULL, public_key); +} + +/** \brief Uses GenKey command to calculate the public key from an existing + * private key in a slot. + * + * \param[in] device Device context pointer + * \param[in] key_id Slot number of the private key. + * \param[out] public_key Public key will be returned here. Format will be + * the X and Y integers in big-endian format. + * 64 bytes for P256 curve. Set to NULL if public key + * isn't required. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_get_pubkey(ATCADevice device, uint16_t key_id, uint8_t *public_key) +{ + return calib_genkey_base(device, GENKEY_MODE_PUBLIC, key_id, NULL, public_key); +} + +/** \brief Uses Genkey command to calculate SHA256 digest MAC of combining public key + * and session key + * + * \param[in] device Device Context pointer + * \param[out] public_key Public key will be returned here. Format will be + * the X and Y integers in big-endian format. + * 64 bytes for P256 curve. + * \param[out] mac Combine public key referenced by keyID with current value + * of session key, calculate a SHA256 digest and return that MAC here. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_genkey_mac(ATCADevice device, uint8_t* public_key, uint8_t* mac) +{ + ATCAPacket packet; + ATCA_STATUS status = ATCA_BAD_PARAM; + + if (device) + { + packet.param1 = GENKEY_MODE_MAC; + packet.param2 = (uint16_t)0x00; + + status = atGenKey(atcab_get_device_type_ext(device), &packet); + if (ATCA_SUCCESS == status) + { + status = atca_execute_command(&packet, device); + } + + if (ATCA_SUCCESS == status) + { + if ((ATCA_PUB_KEY_SIZE + ATCA_PACKET_OVERHEAD + MAC_SIZE) == packet.data[ATCA_COUNT_IDX]) + { + if (public_key) + { + memcpy(public_key, &packet.data[ATCA_RSP_DATA_IDX], ATCA_PUB_KEY_SIZE); + } + if (mac) + { + memcpy(mac, &packet.data[ATCA_RSP_DATA_IDX + ATCA_PUB_KEY_SIZE], MAC_SIZE); + } + } + else + { + status = ATCA_TRACE(ATCA_RX_FAIL, "Received response failure"); + } + + } + else + { + ATCA_TRACE(status, "calib_genkey_mac - failed"); + } + + } + else + { + ATCA_TRACE(status, "NULL pointer encountered"); + } + + return status; +} diff --git a/drivers/ecc108a/cryptoauthlib/calib/calib_helpers.c b/drivers/ecc108a/cryptoauthlib/calib/calib_helpers.c new file mode 100644 index 0000000..96e0b6e --- /dev/null +++ b/drivers/ecc108a/cryptoauthlib/calib/calib_helpers.c @@ -0,0 +1,264 @@ +/** + * \file + * \brief CryptoAuthLib Basic API - Helper Functions to + * + * \copyright (c) 2015-2020 Microchip Technology Inc. and its subsidiaries. + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip software + * and any derivatives exclusively with Microchip products. It is your + * responsibility to comply with third party license terms applicable to your + * use of third party software (including open source software) that may + * accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER + * EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, + * SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE + * OF ANY KIND WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF + * MICROCHIP HAS BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE + * FORESEEABLE. TO THE FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL + * LIABILITY ON ALL CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED + * THE AMOUNT OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR + * THIS SOFTWARE. + */ + +#include "cryptoauthlib.h" + +/** \brief Executes Read command, which reads the configuration zone to see if + * the specified slot is locked. + * + * \param[in] device Device context pointer + * \param[in] slot Slot to query for locked (slot 0-15) + * \param[out] is_locked Lock state returned here. True if locked. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_is_slot_locked(ATCADevice device, uint16_t slot, bool* is_locked) +{ + ATCA_STATUS status = ATCA_GEN_FAIL; + uint8_t data[ATCA_WORD_SIZE]; + uint16_t slot_locked; + + do + { + if ((slot > 15) || (is_locked == NULL)) + { + status = ATCA_TRACE(ATCA_BAD_PARAM, "Either Invalid slot or NULL pointer received"); + break; + } + + // Read the word with the lock bytes ( SlotLock[2], RFU[2] ) (config block = 2, word offset = 6) + if ((status = calib_read_zone(device, ATCA_ZONE_CONFIG, 0, 2 /*block*/, 6 /*offset*/, data, ATCA_WORD_SIZE)) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "calib_read_zone - failed"); + break; + } + + slot_locked = ((uint16_t)data[0]) | ((uint16_t)data[1] << 8); + *is_locked = ((slot_locked & (1 << slot)) == 0); + } + while (0); + + return status; +} + +/** \brief Executes Read command, which reads the configuration zone to see if + * the specified zone is locked. + * + * \param[in] device Device context pointer + * \param[in] zone The zone to query for locked (use LOCK_ZONE_CONFIG or + * LOCK_ZONE_DATA). + * \param[out] is_locked Lock state returned here. True if locked. + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_is_locked(ATCADevice device, uint8_t zone, bool* is_locked) +{ + ATCA_STATUS status = ATCA_GEN_FAIL; + uint8_t data[ATCA_WORD_SIZE]; + + do + { + if (is_locked == NULL) + { + status = ATCA_TRACE(ATCA_BAD_PARAM, "NULL pointer received"); + break; + } + + // Read the word with the lock bytes (UserExtra, Selector, LockValue, LockConfig) (config block = 2, word offset = 5) + if ((status = calib_read_zone(device, ATCA_ZONE_CONFIG, 0, 2 /*block*/, 5 /*offset*/, data, ATCA_WORD_SIZE)) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "calib_read_zone - failed"); + break; + } + + // Determine the index into the word_data based on the zone we are querying for + switch (zone) + { + case LOCK_ZONE_CONFIG: *is_locked = (data[3] != 0x55); break; + case LOCK_ZONE_DATA: *is_locked = (data[2] != 0x55); break; + default: status = ATCA_TRACE(ATCA_BAD_PARAM, "Invalid zone received"); break; + } + } + while (0); + + return status; +} + + +#ifdef ATCA_ECC204_SUPPORT +/** \brief Use Info command to check ECC204 Config zone lock status + * + * \param[in] device Device context pointer + * \param[out] is_locked return lock status + * + * \return ATCA_SUCCESS on success, otherwise an error code + */ +ATCA_STATUS calib_ecc204_is_config_locked(ATCADevice device, bool* is_locked) +{ + ATCA_STATUS status = ATCA_SUCCESS; + uint16_t param2; + uint8_t slot = 0; + + if (NULL == is_locked) + { + return ATCA_TRACE(ATCA_BAD_PARAM, "NULL pointer encountered"); + } + + while (slot <= 3) + { + param2 = ATCA_ECC204_ZONE_CONFIG | (slot << 1); + if (ATCA_SUCCESS != (status = calib_info_lock_status(device, param2, (uint8_t*)is_locked))) + { + *is_locked = false; + break; + } + + if (*is_locked) + { + slot += 1; // increment slot + } + else + { + break; + } + } + + return status; +} + +/** \brief Use Info command to check ECC204 Data zone lock status + * + * \param[in] device Device context pointer + * \param[out] is_locked return lock status + * + * \return ATCA_SUCCESS on success, otherwise an error code + */ +ATCA_STATUS calib_ecc204_is_data_locked(ATCADevice device, bool* is_locked) +{ + ATCA_STATUS status = ATCA_SUCCESS; + uint16_t param2; + uint8_t slot = 0; + + if (NULL == is_locked) + { + return ATCA_TRACE(ATCA_BAD_PARAM, "NULL pointer encountered"); + } + + while (slot <= 3) + { + param2 = ATCA_ECC204_ZONE_DATA | (slot << 1); + if (ATCA_SUCCESS != (status = calib_info_lock_status(device, param2, (uint8_t*)is_locked))) + { + *is_locked = false; + break; + } + + if (*is_locked) + { + slot += 1; // increment slot + } + else + { + break; + } + } + + return status; +} + +/** \brief Use Info command to check config/data is locked or not + * + * \param[in] device Device contect pointer + * \param[in] zone Config/Data zone + * \param[out] is_locked return lock status here + * + * \return ATCA_SUCCESS on success, otherwise an error code + */ +ATCA_STATUS calib_ecc204_is_locked(ATCADevice device, uint8_t zone, bool* is_locked) +{ + ATCA_STATUS status = ATCA_SUCCESS; + + if (ATCA_ZONE_CONFIG == zone) + { + status = calib_ecc204_is_config_locked(device, is_locked); + } + else if (ATCA_ZONE_DATA == zone) + { + status = calib_ecc204_is_data_locked(device, is_locked); + } + else + { + status = ATCA_TRACE(ATCA_BAD_PARAM, "Invalid zone received"); + } + + return status; +} + +#endif + + +/** \brief Check if a slot is a private key + * + * \param[in] device Device context pointer + * \param[in] slot Slot to query (slot 0-15) + * \param[out] is_private return true if private + * + * \return ATCA_SUCCESS on success, otherwise an error code + */ +ATCA_STATUS calib_is_private(ATCADevice device, uint16_t slot, bool* is_private) +{ + ATCA_STATUS status = ATCA_BAD_PARAM; + ATCADeviceType dev_type = atcab_get_device_type_ext(device); + + if (device && is_private) + { + switch (dev_type) + { + case ATECC108A: + /* fallthrough */ + case ATECC508A: + /* fallthrough */ + case ATECC608: + { + uint8_t key_config[2] = { 0 }; + if (ATCA_SUCCESS == (status = calib_read_bytes_zone(device, ATCA_ZONE_CONFIG, 0, ATCA_KEY_CONFIG_OFFSET((size_t)slot), key_config, sizeof(key_config)))) + { + *is_private = (key_config[0] & ATCA_KEY_CONFIG_PRIVATE_MASK); + } + break; + } +#ifdef ATCA_ECC204_SUPPORT + case ECC204: + break; +#endif + default: + *is_private = false; + break; + } + } + + return status; +} diff --git a/drivers/ecc108a/cryptoauthlib/calib/calib_info.c b/drivers/ecc108a/cryptoauthlib/calib/calib_info.c new file mode 100644 index 0000000..7f4a287 --- /dev/null +++ b/drivers/ecc108a/cryptoauthlib/calib/calib_info.c @@ -0,0 +1,204 @@ +/** + * \file + * \brief CryptoAuthLib Basic API methods for Info command. + * + * Info command returns a variety of static and dynamic information about the + * device and its state. Also is used to control the GPIO pin and the persistent + * latch. + * + * \note The ATSHA204A refers to this command as DevRev instead of Info, + * however, the OpCode and operation is the same. + * + * \note List of devices that support this command - ATSHA204A, ATECC108A, + * ATECC508A & ATECC608A/B. There are differences in the modes that they + * support. Refer to device datasheets for full details. + * + * \copyright (c) 2015-2020 Microchip Technology Inc. and its subsidiaries. + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip software + * and any derivatives exclusively with Microchip products. It is your + * responsibility to comply with third party license terms applicable to your + * use of third party software (including open source software) that may + * accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER + * EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, + * SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE + * OF ANY KIND WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF + * MICROCHIP HAS BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE + * FORESEEABLE. TO THE FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL + * LIABILITY ON ALL CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED + * THE AMOUNT OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR + * THIS SOFTWARE. + */ + +#include "cryptoauthlib.h" + +/** \brief Issues an Info command, which return internal device information and + * can control GPIO and the persistent latch. + * + * \param[in] device Device context pointer + * \param[in] mode Selects which mode to be used for info command. + * \param[in] param2 Selects the particular fields for the mode. + * \param[out] out_data Response from info command (4 bytes). Can be set to + * NULL if not required. + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_info_base(ATCADevice device, uint8_t mode, uint16_t param2, uint8_t* out_data) +{ + ATCAPacket packet; + ATCA_STATUS status = ATCA_GEN_FAIL; + + if (device == NULL) + { + return ATCA_TRACE(ATCA_BAD_PARAM, "NULL pointer received"); + } + + // build an info command + packet.param1 = mode; + packet.param2 = param2; + + do + { + if ((status = atInfo(atcab_get_device_type_ext(device), &packet)) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "atInfo - failed"); + break; + } + + if ((status = atca_execute_command(&packet, device)) != ATCA_SUCCESS) + { + // For ECC204, Lock status and Key valid modes return their status in first byte. + // So, need to consider 01 as valid response as it presents lock/keyvalid status. + if (((INFO_MODE_LOCK_STATUS == mode) || (INFO_MODE_KEY_VALID == mode)) + && (ECC204 == device->mIface.mIfaceCFG->devtype)) + { + if (status == ATCA_CHECKMAC_VERIFY_FAILED) + { + status = ATCA_SUCCESS; + } + } + else + { + ATCA_TRACE(status, "calib_info_base - execution failed"); + break; + } + } + + uint8_t response = packet.data[ATCA_COUNT_IDX]; + + if (response && out_data) + { + if (((INFO_MODE_LOCK_STATUS == mode) || (INFO_MODE_KEY_VALID == mode)) + && (ECC204 == device->mIface.mIfaceCFG->devtype)) + { + memcpy(out_data, &packet.data[ATCA_RSP_DATA_IDX], 1); + } + else if (response >= 7) + { + memcpy(out_data, &packet.data[ATCA_RSP_DATA_IDX], 4); + } + else + { + // do nothing + } + + } + } + while (0); + + return status; +} + +/** \brief Use the Info command to get the device revision (DevRev). + * \param[in] device Device context pointer + * \param[out] revision Device revision is returned here (4 bytes). + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_info(ATCADevice device, uint8_t* revision) +{ + if (revision == NULL) + { + return ATCA_TRACE(ATCA_BAD_PARAM, "NULL pointer received"); + } + + return calib_info_base(device, INFO_MODE_REVISION, 0, revision); +} + +/** \brief Use the Info command to get the persistent latch current state for + * an ATECC608 device. + * + * \param[in] device Device context pointer + * \param[out] state The state is returned here. Set (true) or Cler (false). + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ + +ATCA_STATUS calib_info_get_latch(ATCADevice device, bool* state) +{ + ATCA_STATUS status = ATCA_GEN_FAIL; + uint8_t out_data[4]; + + if (state == NULL) + { + return ATCA_TRACE(ATCA_BAD_PARAM, "NULL pointer received"); + } + + if (ATCA_SUCCESS != (status = calib_info_base(device, INFO_MODE_VOL_KEY_PERMIT, 0, out_data))) + { + return ATCA_TRACE(status, "calib_info_base - failed"); + } + + *state = (out_data[0] == 1); + + return status; +} + +/** \brief Use the Info command to set the persistent latch state for an + * ATECC608 device. + * + * \param[in] device Device context pointer + * \param[out] state Persistent latch state. Set (true) or clear (false). + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_info_set_latch(ATCADevice device, bool state) +{ + uint16_t param2 = INFO_PARAM2_SET_LATCH_STATE; + + param2 |= state ? INFO_PARAM2_LATCH_SET : INFO_PARAM2_LATCH_CLEAR; + return calib_info_base(device, INFO_MODE_VOL_KEY_PERMIT, param2, NULL); +} + +/** \brief Use Info command to check ECC Private key stored in key slot is valid or not + * + * \param[in] device Device context pointer + * \param[in] key_id ECC private key slot id + * For ECC204, key_id is 0x00 + * \param[out] is_valid return private key is valid or invalid + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_info_privkey_valid(ATCADevice device, uint16_t key_id, uint8_t* is_valid) +{ + return calib_info_base(device, INFO_MODE_KEY_VALID, key_id, is_valid); +} + +#ifdef ATCA_ECC204_SUPPORT +/** \brief Use Info command to ECC204 config/data zone lock status + * + * \param[in] device Device context pointer + * \param[in] param2 selects the zone and slot + * \param[out] is_locked return lock status here + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_info_lock_status(ATCADevice device, uint16_t param2, uint8_t* is_locked) +{ + return calib_info_base(device, INFO_MODE_LOCK_STATUS, param2, is_locked); +} +#endif diff --git a/drivers/ecc108a/cryptoauthlib/calib/calib_lock.c b/drivers/ecc108a/cryptoauthlib/calib/calib_lock.c new file mode 100644 index 0000000..f7fee5f --- /dev/null +++ b/drivers/ecc108a/cryptoauthlib/calib/calib_lock.c @@ -0,0 +1,258 @@ +/** + * \file + * \brief CryptoAuthLib Basic API methods for Lock command. + * + * The Lock command prevents future modifications of the Configuration zone, + * enables configured policies for Data and OTP zones, and can render + * individual slots read-only regardless of configuration. + * + * \note List of devices that support this command - ATSHA204A, ATECC108A, + * ATECC508A, ATECC608A/B. There are differences in the modes that they + * support. Refer to device datasheets for full details. + * + * \copyright (c) 2015-2020 Microchip Technology Inc. and its subsidiaries. + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip software + * and any derivatives exclusively with Microchip products. It is your + * responsibility to comply with third party license terms applicable to your + * use of third party software (including open source software) that may + * accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER + * EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, + * SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE + * OF ANY KIND WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF + * MICROCHIP HAS BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE + * FORESEEABLE. TO THE FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL + * LIABILITY ON ALL CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED + * THE AMOUNT OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR + * THIS SOFTWARE. + */ + +#include "cryptoauthlib.h" + +/** \brief The Lock command prevents future modifications of the Configuration + * and/or Data and OTP zones. If the device is so configured, then + * this command can be used to lock individual data slots. This + * command fails if the designated area is already locked. + * + * \param[in] device Device context pointer + * \param[in] mode Zone, and/or slot, and summary check (bit 7). + * \param[in] summary_crc CRC of the config or data zones. Ignored for + * slot locks or when mode bit 7 is set. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_lock(ATCADevice device, uint8_t mode, uint16_t summary_crc) +{ + ATCAPacket packet; + ATCA_STATUS status = ATCA_GEN_FAIL; + + if (device == NULL) + { + return ATCA_TRACE(ATCA_BAD_PARAM, "NULL pointer received"); + } + + // build command for lock zone and send + memset(&packet, 0, sizeof(packet)); + packet.param1 = mode; + packet.param2 = summary_crc; + + do + { + if ((status = atLock(atcab_get_device_type_ext(device), &packet)) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "atLock - failed"); + break; + } + + if ((status = atca_execute_command(&packet, device)) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "calib_lock - execution failed"); + break; + } + + } + while (0); + + return status; +} + +/** \brief Unconditionally (no CRC required) lock the config zone. + * + * \param[in] device Device context pointer + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_lock_config_zone(ATCADevice device) +{ + return calib_lock(device, LOCK_ZONE_NO_CRC | LOCK_ZONE_CONFIG, 0); +} + +/** \brief Lock the config zone with summary CRC. + * + * The CRC is calculated over the entire config zone contents. 88 bytes for + * ATSHA devices, 128 bytes for ATECC devices. Lock will fail if the provided + * CRC doesn't match the internally calculated one. + * + * \param[in] device Device context pointer + * \param[in] summary_crc Expected CRC over the config zone. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_lock_config_zone_crc(ATCADevice device, uint16_t summary_crc) +{ + return calib_lock(device, LOCK_ZONE_CONFIG, summary_crc); +} + +/** \brief Unconditionally (no CRC required) lock the data zone (slots and OTP). + * + * ConfigZone must be locked and DataZone must be unlocked for the zone to be successfully locked. + * + * \param[in] device Device context pointer + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_lock_data_zone(ATCADevice device) +{ + return calib_lock(device, LOCK_ZONE_NO_CRC | LOCK_ZONE_DATA, 0); +} + +/** \brief Lock the data zone (slots and OTP) with summary CRC. + * + * The CRC is calculated over the concatenated contents of all the slots and + * OTP at the end. Private keys (KeyConfig.Private=1) are skipped. Lock will + * fail if the provided CRC doesn't match the internally calculated one. + * + * \param[in] device Device context pointer + * \param[in] summary_crc Expected CRC over the data zone. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_lock_data_zone_crc(ATCADevice device, uint16_t summary_crc) +{ + return calib_lock(device, LOCK_ZONE_DATA, summary_crc); +} + +/** \brief Lock an individual slot in the data zone on an ATECC device. Not + * available for ATSHA devices. Slot must be configured to be slot + * lockable (KeyConfig.Lockable=1). + * + * \param[in] device Device context pointer + * \param[in] slot Slot to be locked in data zone. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_lock_data_slot(ATCADevice device, uint16_t slot) +{ + return calib_lock(device, ((uint8_t)slot << 2) | LOCK_ZONE_DATA_SLOT, 0); +} + +/** \brief Use Lock command to lock individual configuration zone slots + * + * \param[in] device Device context pointer + * \param[in] slot The slot number to be locked + * \param[in] summary_crc CRC calculated over all 16 bytes within the selected + * slot of the configuration zone. + * + * \return ATCA_SUCCESS on success, otherwise an error code + */ +ATCA_STATUS calib_ecc204_lock_config_slot(ATCADevice device, uint16_t slot, uint16_t summary_crc) +{ + uint8_t mode = (uint8_t)(LOCK_ECC204_ZONE_CONFIG | (slot << 1)); + + if (!summary_crc) + { + mode |= LOCK_ZONE_NO_CRC; + } + + return calib_lock(device, mode, summary_crc); +} + +/** \brief Use lock command to lock complete configuration zone + * + * \param[in] device Device context pointer + * + * \return ATCA_SUCCESS on success, otherwise an error code + */ +ATCA_STATUS calib_ecc204_lock_config_zone(ATCADevice device) +{ + ATCA_STATUS status = ATCA_GEN_FAIL; + uint8_t slot = 0; + uint8_t mode; + + while (slot <= 3) + { + mode = (uint8_t)(LOCK_ZONE_NO_CRC | LOCK_ECC204_ZONE_CONFIG | (slot << 1)); + + if (ATCA_SUCCESS != (status = calib_lock(device, mode, 0))) + { + // ECC204 returns execution error if slot is already locked. + // Consider already locked status as valid while locking the config zone. + if (status == ATCA_EXECUTION_ERROR) + { + status = ATCA_SUCCESS; + } + else + { + ATCA_TRACE(status, "calib_ecc204_lock_config_zone - failed"); + break; + } + } + + slot += 1; //Increment slot + } + + return status; +} + +/** \brief Use lock command to lock data zone slot + * + * \param[in] device Device context pointer + * \param[in] slot The slot number to be locked + * + * \return ATCA_SUCCESS on success, otherwise an error code + */ +ATCA_STATUS calib_ecc204_lock_data_slot(ATCADevice device, uint16_t slot) +{ + return calib_lock(device, (uint8_t)(LOCK_ECC204_ZONE_DATA | (slot << 1)), 0); +} + +/** \brief Use lock command to lock complete Data zone + * + * \param[in] device Device context pointer + * + * \return ATCA_SUCCESS on success, otherwise an error code + */ +ATCA_STATUS calib_ecc204_lock_data_zone(ATCADevice device) +{ + ATCA_STATUS status = ATCA_GEN_FAIL; + uint8_t slot = 0; + uint8_t mode; + + while (slot <= 3) + { + mode = LOCK_ZONE_NO_CRC | LOCK_ECC204_ZONE_DATA | (slot << 1); + + if (ATCA_SUCCESS != (status = calib_lock(device, mode, 0))) + { + // ECC204 returns execution error if slot is already locked. + // Consider already locked status as valid while locking the config zone. + if (status == ATCA_EXECUTION_ERROR) + { + status = ATCA_SUCCESS; + } + else + { + ATCA_TRACE(status, "calib_ecc204_lock_data_zone - failed"); + break; + } + } + + slot += 1; //Increment slot + } + + return status; +} diff --git a/drivers/ecc108a/cryptoauthlib/calib/calib_nonce.c b/drivers/ecc108a/cryptoauthlib/calib/calib_nonce.c new file mode 100644 index 0000000..35e65fd --- /dev/null +++ b/drivers/ecc108a/cryptoauthlib/calib/calib_nonce.c @@ -0,0 +1,236 @@ +/** + * \file + * \brief CryptoAuthLib Basic API methods for Nonce command. + * + * The Nonce command generates a nonce for use by a subsequent commands of the + * device by combining an internally generated random number with an input value + * from the system. + * + * \note List of devices that support this command - ATSHA204A, ATECC108A, + * ATECC508A, and ATECC608A/B. There are differences in the modes that they + * support. Refer to device datasheets for full details. + * + * \copyright (c) 2015-2020 Microchip Technology Inc. and its subsidiaries. + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip software + * and any derivatives exclusively with Microchip products. It is your + * responsibility to comply with third party license terms applicable to your + * use of third party software (including open source software) that may + * accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER + * EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, + * SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE + * OF ANY KIND WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF + * MICROCHIP HAS BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE + * FORESEEABLE. TO THE FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL + * LIABILITY ON ALL CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED + * THE AMOUNT OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR + * THIS SOFTWARE. + */ + +#include "cryptoauthlib.h" + +/** \brief Executes Nonce command, which loads a random or fixed nonce/data + * into the device for use by subsequent commands. + * + * \param[in] device Device context pointer + * \param[in] mode Controls the mechanism of the internal RNG or fixed + * write. + * \param[in] param2 Param2, normally 0, but can be used to indicate a + * nonce calculation mode (bit 15). + * For ECC204, represent tarnsport key id greater than + * or equal to 0x8000 + * \param[in] num_in Input value to either be included in the nonce + * calculation in random modes (20 bytes) or to be + * written directly (32 bytes or 64 bytes(ATECC608)) + * in pass-through mode. + * \param[out] rand_out If using a random mode, the internally generated + * 32-byte random number that was used in the nonce + * calculation is returned here. Can be NULL if not + * needed. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_nonce_base(ATCADevice device, uint8_t mode, uint16_t param2, const uint8_t *num_in, uint8_t* rand_out) +{ + ATCAPacket packet; + ATCA_STATUS status = ATCA_GEN_FAIL; + uint8_t nonce_mode = mode & NONCE_MODE_MASK; + + do + { + if (device == NULL) + { + status = ATCA_TRACE(ATCA_BAD_PARAM, "NULL pointer received"); + break; + } + + // build a nonce command + packet.param1 = mode; + packet.param2 = param2; + + // Copy the right amount of NumIn data + if ((nonce_mode == NONCE_MODE_SEED_UPDATE) || (nonce_mode == NONCE_MODE_NO_SEED_UPDATE) + || (NONCE_MODE_GEN_SESSION_KEY == nonce_mode)) + { + memcpy(packet.data, num_in, NONCE_NUMIN_SIZE); + } + else if (nonce_mode == NONCE_MODE_PASSTHROUGH) + { + if ((mode & NONCE_MODE_INPUT_LEN_MASK) == NONCE_MODE_INPUT_LEN_64) + { + memcpy(packet.data, num_in, 64); + } + else + { + memcpy(packet.data, num_in, 32); + } + } + else + { + return ATCA_TRACE(ATCA_BAD_PARAM, "Invalid nonce mode received"); + } + + if ((status = atNonce(atcab_get_device_type_ext(device), &packet)) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "atNonce - failed"); + break; + } + + if ((status = atca_execute_command(&packet, device)) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "calib_nonce_base - execution failed"); + break; + } + + if ((rand_out != NULL) && (packet.data[ATCA_COUNT_IDX] >= 35)) + { + memcpy(&rand_out[0], &packet.data[ATCA_RSP_DATA_IDX], 32); + } + + } + while (0); + + return status; +} + + +/** \brief Execute a Nonce command in pass-through mode to initialize TempKey + * to a specified value. + * + * \param[in] device Device context pointer + * \param[in] num_in Data to be loaded into TempKey (32 bytes). + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_nonce(ATCADevice device, const uint8_t *num_in) +{ + return calib_nonce_base(device, NONCE_MODE_PASSTHROUGH, 0, num_in, NULL); +} + + +/** \brief Execute a Nonce command in pass-through mode to load one of the + * device's internal buffers with a fixed value. + * + * For the ATECC608, available targets are TempKey (32 or 64 bytes), Message + * Digest Buffer (32 or 64 bytes), or the Alternate Key Buffer (32 bytes). For + * all other devices, only TempKey (32 bytes) is available. + * + * \param[in] device Device context pointer + * \param[in] target Target device buffer to load. Can be + * NONCE_MODE_TARGET_TEMPKEY, + * NONCE_MODE_TARGET_MSGDIGBUF, or + * NONCE_MODE_TARGET_ALTKEYBUF. + * \param[in] num_in Data to load into the buffer. + * \param[in] num_in_size Size of num_in in bytes. Can be 32 or 64 bytes + * depending on device and target. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_nonce_load(ATCADevice device, uint8_t target, const uint8_t *num_in, uint16_t num_in_size) +{ + uint8_t mode = NONCE_MODE_PASSTHROUGH | (NONCE_MODE_TARGET_MASK & target); + + if (num_in_size == 32) + { + mode |= NONCE_MODE_INPUT_LEN_32; + } + else if (num_in_size == 64) + { + mode |= NONCE_MODE_INPUT_LEN_64; + } + else + { + return ATCA_TRACE(ATCA_BAD_PARAM, "Invalid size received"); + } + + return calib_nonce_base(device, mode, 0, num_in, NULL); +} + +/** \brief Execute a Nonce command to generate a random nonce combining a host + * nonce (num_in) and a device random number. + * + * \param[in] device Device context pointer + * \param[in] num_in Host nonce to be combined with the device random + * number (20 bytes). + * \param[out] rand_out Internally generated 32-byte random number that was + * used in the nonce/challenge calculation is returned + * here. Can be NULL if not needed. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_nonce_rand(ATCADevice device, const uint8_t *num_in, uint8_t* rand_out) +{ + return calib_nonce_base(device, NONCE_MODE_SEED_UPDATE, 0, num_in, rand_out); +} + +/** \brief Execute a Nonce command in pass-through mode to initialize TempKey + * to a specified value. + * + * \param[in] device Device context pointer + * \param[in] num_in Data to be loaded into TempKey (32 bytes). + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_challenge(ATCADevice device, const uint8_t *num_in) +{ + return calib_nonce_base(device, NONCE_MODE_PASSTHROUGH, 0, num_in, NULL); +} + +/** \brief Execute a Nonce command to generate a random challenge combining + * a host nonce (num_in) and a device random number. + * + * \param[in] device Device context pointer + * \param[in] num_in Host nonce to be combined with the device random + * number (20 bytes). + * \param[out] rand_out Internally generated 32-byte random number that was + * used in the nonce/challenge calculation is returned + * here. Can be NULL if not needed. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_challenge_seed_update(ATCADevice device, const uint8_t *num_in, uint8_t* rand_out) +{ + return calib_nonce_base(device, NONCE_MODE_SEED_UPDATE, 0, num_in, rand_out); +} + +/** \brief Use Nonce command to generate session key for use by a subsequent write command + * This Mode only supports in ECC204 device. + * \param[in] device Device context pointer + * \param[in] param2 Key id points to transport key + * \param[in] num_in Input value from host system + * \param[out] rand_out Internally generate random number of 32 bytes + * returned here + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_nonce_gen_session_key(ATCADevice device, uint16_t param2, uint8_t* num_in, + uint8_t* rand_out) +{ + return calib_nonce_base(device, NONCE_MODE_GEN_SESSION_KEY, param2, num_in, rand_out); +} diff --git a/drivers/ecc108a/cryptoauthlib/calib/calib_privwrite.c b/drivers/ecc108a/cryptoauthlib/calib/calib_privwrite.c new file mode 100644 index 0000000..f17c8c4 --- /dev/null +++ b/drivers/ecc108a/cryptoauthlib/calib/calib_privwrite.c @@ -0,0 +1,193 @@ +/** + * \file + * \brief CryptoAuthLib Basic API methods for PrivWrite command. + * + * The PrivWrite command is used to write externally generated ECC private keys + * into the device. + * + * \note List of devices that support this command - ATECC108A, ATECC508A, and + * ATECC608A/B. There are differences in the modes that they support. Refer + * to device datasheets for full details. + * + * \copyright (c) 2015-2020 Microchip Technology Inc. and its subsidiaries. + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip software + * and any derivatives exclusively with Microchip products. It is your + * responsibility to comply with third party license terms applicable to your + * use of third party software (including open source software) that may + * accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER + * EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, + * SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE + * OF ANY KIND WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF + * MICROCHIP HAS BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE + * FORESEEABLE. TO THE FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL + * LIABILITY ON ALL CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED + * THE AMOUNT OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR + * THIS SOFTWARE. + */ + +#include "cryptoauthlib.h" +#include "host/atca_host.h" + +/** \brief Executes PrivWrite command, to write externally generated ECC + * private keys into the device. + * + * \param[in] device Device context pointer + * \param[in] key_id Slot to write the external private key into. + * \param[in] priv_key External private key (36 bytes) to be written. + * The first 4 bytes should be zero for P256 curve. + * \param[in] write_key_id Write key slot. Ignored if write_key is NULL. + * \param[in] write_key Write key (32 bytes). If NULL, perform an + * unencrypted PrivWrite, which is only available when + * the data zone is unlocked. + * \param[in] num_in 20 byte host nonce to inject into Nonce calculation + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +// FIXME do we want this function? +#ifndef ATCA_TIDAL +#if defined(ATCA_USE_CONSTANT_HOST_NONCE) +ATCA_STATUS calib_priv_write(ATCADevice device, uint16_t key_id, const uint8_t priv_key[36], uint16_t write_key_id, const uint8_t write_key[32]) +{ + uint8_t num_in[NONCE_NUMIN_SIZE] = { 0 }; + +#else +ATCA_STATUS calib_priv_write(ATCADevice device, uint16_t key_id, const uint8_t priv_key[36], uint16_t write_key_id, const uint8_t write_key[32], const uint8_t num_in[NONCE_NUMIN_SIZE]) +{ +#endif + ATCAPacket packet; + ATCA_STATUS status = ATCA_GEN_FAIL; + atca_nonce_in_out_t nonce_params; + atca_gen_dig_in_out_t gen_dig_param; + atca_write_mac_in_out_t host_mac_param; + atca_temp_key_t temp_key; + uint8_t serial_num[32]; // Buffer is larger than the 9 bytes required to make reads easier + uint8_t rand_out[RANDOM_NUM_SIZE] = { 0 }; + uint8_t cipher_text[36] = { 0 }; + uint8_t host_mac[MAC_SIZE] = { 0 }; + uint8_t other_data[4] = { 0 }; + + if ((device == NULL) || (priv_key == NULL) || (key_id > 15)) + { + return ATCA_TRACE(ATCA_BAD_PARAM, "Either NULL pointer or invalid slot received"); + } + + do + { + if (write_key == NULL) + { + // Caller requested an unencrypted PrivWrite, which is only allowed when the data zone is unlocked + // build an PrivWrite command + packet.param1 = 0x00; // Mode is unencrypted write + packet.param2 = key_id; // Key ID + memcpy(&packet.data[0], priv_key, 36); // Private key + memset(&packet.data[36], 0, 32); // MAC (ignored for unencrypted write) + } + else + { + // Read the device SN + if ((status = calib_read_zone(device, ATCA_ZONE_CONFIG, 0, 0, 0, serial_num, 32)) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "calib_read_zone - failed"); + break; + } + // Make the SN continuous by moving SN[4:8] right after SN[0:3] + memmove(&serial_num[4], &serial_num[8], 5); + + // Send the random Nonce command + if ((status = calib_nonce_rand(device, num_in, rand_out)) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "calib_nonce_rand - failed"); + break; + } + + // Calculate Tempkey + memset(&temp_key, 0, sizeof(temp_key)); + memset(&nonce_params, 0, sizeof(nonce_params)); + nonce_params.mode = NONCE_MODE_SEED_UPDATE; + nonce_params.zero = 0; + nonce_params.num_in = &num_in[0]; + nonce_params.rand_out = rand_out; + nonce_params.temp_key = &temp_key; + if ((status = atcah_nonce(&nonce_params)) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "atcah_nonce - failed"); + break; + } + + // Supply OtherData so GenDig behavior is the same for keys with SlotConfig.NoMac set + other_data[0] = ATCA_GENDIG; + other_data[1] = GENDIG_ZONE_DATA; + other_data[2] = (uint8_t)(write_key_id); + other_data[3] = (uint8_t)(write_key_id >> 8); + + // Send the GenDig command + if ((status = calib_gendig(device, GENDIG_ZONE_DATA, write_key_id, other_data, sizeof(other_data))) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "calib_gendig - failed"); + break; + } + + // Calculate Tempkey + // NoMac bit isn't being considered here on purpose to remove having to read SlotConfig. + // OtherData is built to get the same result regardless of the NoMac bit. + memset(&gen_dig_param, 0, sizeof(gen_dig_param)); + gen_dig_param.zone = GENDIG_ZONE_DATA; + gen_dig_param.sn = serial_num; + gen_dig_param.key_id = write_key_id; + gen_dig_param.is_key_nomac = false; + gen_dig_param.stored_value = write_key; + gen_dig_param.other_data = other_data; + gen_dig_param.temp_key = &temp_key; + if ((status = atcah_gen_dig(&gen_dig_param)) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "atcah_gen_dig - failed"); + break; + } + + // Calculate Auth MAC and cipher text + memset(&host_mac_param, 0, sizeof(host_mac_param)); + host_mac_param.zone = PRIVWRITE_MODE_ENCRYPT; + host_mac_param.key_id = key_id; + host_mac_param.sn = serial_num; + host_mac_param.input_data = &priv_key[0]; + host_mac_param.encrypted_data = cipher_text; + host_mac_param.auth_mac = host_mac; + host_mac_param.temp_key = &temp_key; + if ((status = atcah_privwrite_auth_mac(&host_mac_param)) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "atcah_privwrite_auth_mac - failed"); + break; + } + + // build a write command for encrypted writes + packet.param1 = PRIVWRITE_MODE_ENCRYPT; // Mode is encrypted write + packet.param2 = key_id; // Key ID + memcpy(&packet.data[0], cipher_text, sizeof(cipher_text)); + memcpy(&packet.data[sizeof(cipher_text)], host_mac, sizeof(host_mac)); + } + + if ((status = atPrivWrite(atcab_get_device_type_ext(device), &packet)) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "atPrivWrite - failed"); + break; + } + + if ((status = atca_execute_command(&packet, device)) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "calib_priv_write - execution failed"); + break; + } + + } + while (0); + + return status; +} +#endif // ifndef ATCA_TIDAL diff --git a/drivers/ecc108a/cryptoauthlib/calib/calib_random.c b/drivers/ecc108a/cryptoauthlib/calib/calib_random.c new file mode 100644 index 0000000..90516a1 --- /dev/null +++ b/drivers/ecc108a/cryptoauthlib/calib/calib_random.c @@ -0,0 +1,88 @@ +/** + * \file + * \brief CryptoAuthLib Basic API methods for Random command. + * + * The Random command generates a random number for use by the system. + * + * \note List of devices that support this command - ATSHA204A, ATECC108A, + * ATECC508A, ATECC608A/B. There are differences in the modes that they + * support. Refer to device datasheets for full details. + * + * \copyright (c) 2015-2020 Microchip Technology Inc. and its subsidiaries. + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip software + * and any derivatives exclusively with Microchip products. It is your + * responsibility to comply with third party license terms applicable to your + * use of third party software (including open source software) that may + * accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER + * EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, + * SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE + * OF ANY KIND WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF + * MICROCHIP HAS BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE + * FORESEEABLE. TO THE FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL + * LIABILITY ON ALL CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED + * THE AMOUNT OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR + * THIS SOFTWARE. + */ + +#include "cryptoauthlib.h" + +/** \brief Executes Random command, which generates a 32 byte random number + * from the CryptoAuth device. + * + * \param[in] device Device context pointer + * \param[out] rand_out 32 bytes of random data is returned here. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_random(ATCADevice device, uint8_t *rand_out) +{ + ATCAPacket packet; + ATCA_STATUS status = ATCA_GEN_FAIL; + + do + { + if (device == NULL) + { + status = ATCA_TRACE(ATCA_BAD_PARAM, "NULL pointer received"); + break; + } + + // build an random command + packet.param1 = RANDOM_SEED_UPDATE; + packet.param2 = 0x0000; + + if ((status = atRandom(atcab_get_device_type_ext(device), &packet)) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "atRandom - failed"); + break; + } + + if ((status = atca_execute_command(&packet, device)) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "calib_random - execution failed"); + break; + } + + if (packet.data[ATCA_COUNT_IDX] != RANDOM_RSP_SIZE) + { + status = ATCA_TRACE(ATCA_RX_FAIL, "Unexpected response size"); + break; + } + + if (rand_out) + { + memcpy(rand_out, &packet.data[ATCA_RSP_DATA_IDX], RANDOM_NUM_SIZE); + } + } + while (0); + + + return status; +} diff --git a/drivers/ecc108a/cryptoauthlib/calib/calib_read.c b/drivers/ecc108a/cryptoauthlib/calib/calib_read.c new file mode 100644 index 0000000..315a7e7 --- /dev/null +++ b/drivers/ecc108a/cryptoauthlib/calib/calib_read.c @@ -0,0 +1,923 @@ +/** + * \file + * \brief CryptoAuthLib Basic API methods for Read command. + * + * The Read command reads words either 4-byte words or 32-byte blocks from one + * of the memory zones of the device. The data may optionally be encrypted + * before being returned to the system. + * + * \note List of devices that support this command - ATSHA204A, ATECC108A, + * ATECC508A, ATECC608A/B. There are differences in the modes that they + * support. Refer to device datasheets for full details. + * + * \copyright (c) 2015-2020 Microchip Technology Inc. and its subsidiaries. + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip software + * and any derivatives exclusively with Microchip products. It is your + * responsibility to comply with third party license terms applicable to your + * use of third party software (including open source software) that may + * accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER + * EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, + * SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE + * OF ANY KIND WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF + * MICROCHIP HAS BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE + * FORESEEABLE. TO THE FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL + * LIABILITY ON ALL CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED + * THE AMOUNT OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR + * THIS SOFTWARE. + */ + +#include "cryptoauthlib.h" +#include "host/atca_host.h" + +/** \brief Executes Read command, which reads either 4 or 32 bytes of data from + * a given slot, configuration zone, or the OTP zone. + * + * When reading a slot or OTP, data zone must be locked and the slot + * configuration must not be secret for a slot to be successfully read. + * + * \param[in] device Device context pointer + * \param[in] zone Zone to be read from device. Options are + * ATCA_ZONE_CONFIG, ATCA_ZONE_OTP, or ATCA_ZONE_DATA. + * \param[in] slot Slot number for data zone and ignored for other zones. + * \param[in] block 32 byte block index within the zone. + * \param[in] offset 4 byte work index within the block. Ignored for 32 byte + * reads. + * \param[out] data Read data is returned here. + * \param[in] len Length of the data to be read. Must be either 4 or 32. + * + * returns ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_read_zone(ATCADevice device, uint8_t zone, uint16_t slot, uint8_t block, uint8_t offset, uint8_t *data, uint8_t len) +{ + ATCAPacket packet; + ATCA_STATUS status = ATCA_GEN_FAIL; + uint16_t addr; + + do + { + // Check the input parameters + if ((device == NULL) || (data == NULL)) + { + status = ATCA_TRACE(ATCA_BAD_PARAM, "NULL pointer received"); + break; + } + + if (len != 4 && len != 32) + { + status = ATCA_TRACE(ATCA_BAD_PARAM, "Invalid length received"); + break; + } + + // The get address function checks the remaining variables + if ((status = calib_get_addr(zone, slot, block, offset, &addr)) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "calib_get_addr - failed"); + break; + } + + // If there are 32 bytes to read, then OR the bit into the mode + if (len == ATCA_BLOCK_SIZE) + { + zone = zone | ATCA_ZONE_READWRITE_32; + } + + // build a read command + packet.param1 = zone; + packet.param2 = addr; + + if ((status = atRead(atcab_get_device_type_ext(device), &packet)) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "atRead - failed"); + break; + } + + if ((status = atca_execute_command(&packet, device)) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "calib_read_zone - execution failed"); + break; + } + + memcpy(data, &packet.data[1], len); + } + while (0); + + return status; +} +/** \brief Executes Read command, which reads the 9 byte serial number of the + * device from the config zone. + * + * \param[in] device Device context pointer + * \param[out] serial_number 9 byte serial number is returned here. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_read_serial_number(ATCADevice device, uint8_t* serial_number) +{ + ATCA_STATUS status = ATCA_GEN_FAIL; + uint8_t read_buf[ATCA_BLOCK_SIZE]; + + if (!serial_number) + { + return ATCA_TRACE(ATCA_BAD_PARAM, "NULL pointer received"); + } + + do + { + if ((status = calib_read_zone(device, ATCA_ZONE_CONFIG, 0, 0, 0, read_buf, ATCA_BLOCK_SIZE)) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "calib_read_zone - failed"); + break; + } + memcpy(&serial_number[0], &read_buf[0], 4); + memcpy(&serial_number[4], &read_buf[8], 5); + } + while (0); + + return status; +} + + +/** \brief Executes Read command on a slot configured for encrypted reads and + * decrypts the data to return it as plaintext. + * + * Data zone must be locked for this command to succeed. Can only read 32 byte + * blocks. + * + * \param[in] device Device context pointer + * \param[in] key_id The slot ID to read from. + * \param[in] block Index of the 32 byte block within the slot to read. + * \param[out] data Decrypted (plaintext) data from the read is returned + * here (32 bytes). + * \param[in] enc_key 32 byte ReadKey for the slot being read. + * \param[in] enc_key_id KeyID of the ReadKey being used. + * \param[in] num_in 20 byte host nonce to inject into Nonce calculation + * + * returns ATCA_SUCCESS on success, otherwise an error code. + */ +// FIXME do we want this function? +#ifndef ATCA_TIDAL +#if defined(ATCA_USE_CONSTANT_HOST_NONCE) +ATCA_STATUS calib_read_enc(ATCADevice device, uint16_t key_id, uint8_t block, uint8_t *data, const uint8_t* enc_key, const uint16_t enc_key_id) +{ + uint8_t num_in[NONCE_NUMIN_SIZE] = { 0 }; + +#else +ATCA_STATUS calib_read_enc(ATCADevice device, uint16_t key_id, uint8_t block, uint8_t *data, const uint8_t* enc_key, const uint16_t enc_key_id, const uint8_t num_in[NONCE_NUMIN_SIZE]) +{ +#endif + ATCA_STATUS status = ATCA_GEN_FAIL; + uint8_t zone = ATCA_ZONE_DATA | ATCA_ZONE_READWRITE_32; + atca_nonce_in_out_t nonce_params; + atca_gen_dig_in_out_t gen_dig_param; + atca_temp_key_t temp_key; + uint8_t serial_num[32]; + uint8_t rand_out[RANDOM_NUM_SIZE] = { 0 }; + uint8_t other_data[4] = { 0 }; + int i = 0; + + do + { + // Verify inputs parameters + if (data == NULL || enc_key == NULL) + { + status = ATCA_TRACE(ATCA_BAD_PARAM, "NULL pointer received"); + break; + } + + // Read the device SN + if ((status = calib_read_zone(device, ATCA_ZONE_CONFIG, 0, 0, 0, serial_num, 32)) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "calib_read_zone - failed"); + break; + } + // Make the SN continuous by moving SN[4:8] right after SN[0:3] + memmove(&serial_num[4], &serial_num[8], 5); + + // Send the random Nonce command + if ((status = calib_nonce_rand(device, num_in, rand_out)) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "Nonce failed"); break; + } + + // Calculate Tempkey + memset(&temp_key, 0, sizeof(temp_key)); + memset(&nonce_params, 0, sizeof(nonce_params)); + nonce_params.mode = NONCE_MODE_SEED_UPDATE; + nonce_params.zero = 0; + nonce_params.num_in = (uint8_t*)&num_in[0]; + nonce_params.rand_out = rand_out; + nonce_params.temp_key = &temp_key; + if ((status = atcah_nonce(&nonce_params)) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "Calc TempKey failed"); break; + } + + // Supply OtherData so GenDig behavior is the same for keys with SlotConfig.NoMac set + other_data[0] = ATCA_GENDIG; + other_data[1] = GENDIG_ZONE_DATA; + other_data[2] = (uint8_t)(enc_key_id); + other_data[3] = (uint8_t)(enc_key_id >> 8); + + // Send the GenDig command + if ((status = calib_gendig(device, GENDIG_ZONE_DATA, enc_key_id, other_data, sizeof(other_data))) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "GenDig failed"); break; + } + + // Calculate Tempkey + // NoMac bit isn't being considered here on purpose to remove having to read SlotConfig. + // OtherData is built to get the same result regardless of the NoMac bit. + memset(&gen_dig_param, 0, sizeof(gen_dig_param)); + gen_dig_param.key_id = enc_key_id; + gen_dig_param.is_key_nomac = false; + gen_dig_param.sn = serial_num; + gen_dig_param.stored_value = enc_key; + gen_dig_param.zone = GENDIG_ZONE_DATA; + gen_dig_param.other_data = other_data; + gen_dig_param.temp_key = &temp_key; + if ((status = atcah_gen_dig(&gen_dig_param)) != ATCA_SUCCESS) + { + ATCA_TRACE(status, ""); break; + } + + // Read Encrypted + if ((status = calib_read_zone(device, zone, key_id, block, 0, data, ATCA_BLOCK_SIZE)) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "Read encrypted failed"); break; + } + + // Decrypt + for (i = 0; i < ATCA_BLOCK_SIZE; i++) + { + data[i] = data[i] ^ temp_key.value[i]; + } + + status = ATCA_SUCCESS; + + } + while (0); + + + return status; +} +#endif // ifndef ATCA_TIDAL + +/** \brief Executes Read command to read the complete device configuration + * zone. + * + * \param[in] device Device context pointer + * \param[out] config_data Configuration zone data is returned here. 88 bytes + * for ATSHA devices, 128 bytes for ATECC devices. + * + * \returns ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_read_config_zone(ATCADevice device, uint8_t* config_data) +{ + ATCA_STATUS status = ATCA_GEN_FAIL; + + do + { + // Verify the inputs + if (config_data == NULL) + { + status = ATCA_TRACE(ATCA_BAD_PARAM, "NULL pointer received"); + break; + } + + if (atIsSHAFamily(device->mIface.mIfaceCFG->devtype)) + { + status = calib_read_bytes_zone(device, ATCA_ZONE_CONFIG, 0, 0x00, config_data, ATCA_SHA_CONFIG_SIZE); + } + else + { + status = calib_read_bytes_zone(device, ATCA_ZONE_CONFIG, 0, 0x00, config_data, ATCA_ECC_CONFIG_SIZE); + } + + if (status != ATCA_SUCCESS) + { + ATCA_TRACE(status, "calib_read_bytes_zone - failed"); + break; + } + + } + while (0); + + return status; +} + +/** \brief Compares a specified configuration zone with the configuration zone + * currently on the device. + * + * This only compares the static portions of the configuration zone and skips + * those that are unique per device (first 16 bytes) and areas that can change + * after the configuration zone has been locked (e.g. LastKeyUse). + * + * \param[in] device Device context pointer + * \param[in] config_data Full configuration data to compare the device + * against. + * \param[out] same_config Result is returned here. True if the static portions + * on the configuration zones are the same. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_cmp_config_zone(ATCADevice device, uint8_t* config_data, bool* same_config) +{ + ATCA_STATUS status = ATCA_GEN_FAIL; + uint8_t device_config_data[ATCA_ECC_CONFIG_SIZE]; /** Max for all configs */ + size_t config_size = 0; + + do + { + // Check the inputs + if ((config_data == NULL) || (same_config == NULL)) + { + status = ATCA_TRACE(ATCA_BAD_PARAM, "NULL pointer received"); + break; + } + // Set the boolean to false + *same_config = false; + + // Read all of the configuration bytes from the device + if ((status = calib_read_config_zone(device, device_config_data)) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "Read config zone failed"); break; + } + + /* Get the config size of the device being tested */ + if (ATCA_SUCCESS != (status = calib_get_zone_size(device, ATCA_ZONE_CONFIG, 0, &config_size))) + { + ATCA_TRACE(status, "Failed to get config zone size"); break; + } + + /* Compare the lower writable bytes (16-51) */ + if (memcmp(&device_config_data[16], &config_data[16], 52 - 16)) + { + /* Difference found */ + break; + } + + if (ATECC608 == device->mIface.mIfaceCFG->devtype) + { + /* Skip Counter[0], Counter[1], which can change during operation */ + + /* Compare UseLock through Reserved (68 --> 83) */ + if (memcmp(&device_config_data[68], &config_data[68], 84 - 68)) + { + /* Difference found */ + break; + } + + /* Skip UserExtra, UserExtraAdd, LockValue, LockConfig, and SlotLocked */ + + } + else + { + /* Skip the counter & LastKeyUse bytes [52-83] */ + /* Skip User Extra & Selector [84-85] */ + /* Skip all lock bytes [86-89] */ + } + + if (90 < config_size) + { + /* Compare the upper writable bytes (90-config_size) */ + if (memcmp(&device_config_data[90], &config_data[90], config_size - 90)) + { + /* Difference found */ + break; + } + } + + /* All Matched */ + *same_config = true; + } + while (0); + + return status; +} + + +/** \brief Executes Read command to read a 64 byte ECDSA P256 signature from a + * slot configured for clear reads. + * + * \param[in] device Device context pointer + * \param[in] slot Slot number to read from. Only slots 8 to 15 are large + * enough for a signature. + * \param[out] sig Signature will be returned here (64 bytes). Format will be + * the 32 byte R and S big-endian integers concatenated. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_read_sig(ATCADevice device, uint16_t slot, uint8_t* sig) +{ + ATCA_STATUS status = ATCA_GEN_FAIL; + + do + { + // Check the value of the slot + if (sig == NULL) + { + status = ATCA_TRACE(ATCA_BAD_PARAM, "NULL pointer received"); + break; + } + + if (slot < 8 || slot > 15) + { + status = ATCA_TRACE(ATCA_BAD_PARAM, "Invalid slot received"); + break; + } + + // Read the first block + if ((status = calib_read_zone(device, ATCA_ZONE_DATA, slot, 0, 0, &sig[0], ATCA_BLOCK_SIZE)) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "calib_read_zone - failed"); + break; + } + + // Read the second block + if ((status = calib_read_zone(device, ATCA_ZONE_DATA, slot, 1, 0, &sig[ATCA_BLOCK_SIZE], ATCA_BLOCK_SIZE)) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "calib_read_zone - failed"); + break; + } + } + while (0); + + return status; +} + +/** \brief Executes Read command to read an ECC P256 public key from a slot + * configured for clear reads. + * + * This function assumes the public key is stored using the ECC public key + * format specified in the datasheet. + * + * \param[in] device Device context pointer + * \param[in] slot Slot number to read from. Only slots 8 to 15 are + * large enough for a public key. + * \param[out] public_key Public key is returned here (64 bytes). Format will + * be the 32 byte X and Y big-endian integers + * concatenated. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_read_pubkey(ATCADevice device, uint16_t slot, uint8_t *public_key) +{ + ATCA_STATUS status = ATCA_GEN_FAIL; + uint8_t read_buf[ATCA_BLOCK_SIZE]; + uint8_t block = 0; + uint8_t offset = 0; + uint8_t cpy_index = 0; + uint8_t cpy_size = 0; + uint8_t read_index = 0; + + // Check the pointers + if (public_key == NULL) + { + return ATCA_TRACE(ATCA_BAD_PARAM, "NULL pointer received"); + } + // Check the value of the slot + if (slot < 8 || slot > 0xF) + { + return ATCA_TRACE(ATCA_BAD_PARAM, "Invalid slot received"); + } + + do + { + // The 64 byte P256 public key gets written to a 72 byte slot in the following pattern + // | Block 1 | Block 2 | Block 3 | + // | Pad: 4 Bytes | PubKey[0:27] | PubKey[28:31] | Pad: 4 Bytes | PubKey[32:55] | PubKey[56:63] | + + // Read the block + block = 0; + if ((status = calib_read_zone(device, ATCA_ZONE_DATA, slot, block, offset, read_buf, ATCA_BLOCK_SIZE)) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "calib_read_zone - failed"); + break; + } + + // Copy. Account for 4 byte pad + cpy_size = ATCA_BLOCK_SIZE - ATCA_PUB_KEY_PAD; + read_index = ATCA_PUB_KEY_PAD; + memcpy(&public_key[cpy_index], &read_buf[read_index], cpy_size); + cpy_index += cpy_size; + + // Read the next block + block = 1; + if ((status = calib_read_zone(device, ATCA_ZONE_DATA, slot, block, offset, read_buf, ATCA_BLOCK_SIZE)) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "calib_read_zone - failed"); + break; + } + + // Copy. First four bytes + cpy_size = ATCA_PUB_KEY_PAD; + read_index = 0; + memcpy(&public_key[cpy_index], &read_buf[read_index], cpy_size); + cpy_index += cpy_size; + // Copy. Skip four bytes + read_index = ATCA_PUB_KEY_PAD + ATCA_PUB_KEY_PAD; + cpy_size = ATCA_BLOCK_SIZE - read_index; + memcpy(&public_key[cpy_index], &read_buf[read_index], cpy_size); + cpy_index += cpy_size; + + // Read the next block + block = 2; + if ((status = calib_read_zone(device, ATCA_ZONE_DATA, slot, block, offset, read_buf, ATCA_BLOCK_SIZE)) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "calib_read_zone - failed"); + break; + } + + // Copy. The remaining 8 bytes + cpy_size = ATCA_PUB_KEY_PAD + ATCA_PUB_KEY_PAD; + read_index = 0; + memcpy(&public_key[cpy_index], &read_buf[read_index], cpy_size); + + } + while (0); + + return status; +} + +/** \brief Used to read an arbitrary number of bytes from any zone configured + * for clear reads. + * + * This function will issue the Read command as many times as is required to + * read the requested data. + * + * \param[in] device Device context pointer + * \param[in] zone Zone to read data from. Option are ATCA_ZONE_CONFIG(0), + * ATCA_ZONE_OTP(1), or ATCA_ZONE_DATA(2). + * \param[in] slot Slot number to read from if zone is ATCA_ZONE_DATA(2). + * Ignored for all other zones. + * \param[in] offset Byte offset within the zone to read from. + * \param[out] data Read data is returned here. + * \param[in] length Number of bytes to read starting from the offset. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_read_bytes_zone(ATCADevice device, uint8_t zone, uint16_t slot, size_t offset, uint8_t *data, size_t length) +{ + ATCA_STATUS status = ATCA_GEN_FAIL; + size_t zone_size = 0; + uint8_t read_buf[32]; + size_t data_idx = 0; + size_t cur_block = 0; + size_t cur_offset = 0; + uint8_t read_size = ATCA_BLOCK_SIZE; + size_t read_buf_idx = 0; + size_t copy_length = 0; + size_t read_offset = 0; + + if (zone != ATCA_ZONE_CONFIG && zone != ATCA_ZONE_OTP && zone != ATCA_ZONE_DATA) + { + return ATCA_TRACE(ATCA_BAD_PARAM, "Invalid zone received"); + } + if (zone == ATCA_ZONE_DATA && slot > 15) + { + return ATCA_TRACE(ATCA_BAD_PARAM, "Invalid slot received"); + } + if (length == 0) + { + return ATCA_SUCCESS; // Always succeed reading 0 bytes + } + if (data == NULL) + { + return ATCA_TRACE(ATCA_BAD_PARAM, "NULL pointer received"); + } + + do + { + if (ATCA_SUCCESS != (status = calib_get_zone_size(device, zone, slot, &zone_size))) + { + ATCA_TRACE(status, "calib_get_zone_size - failed"); + break; + } + if (offset + length > zone_size) + { + return ATCA_TRACE(ATCA_BAD_PARAM, "Invalid parameter received"); // Can't read past the end of a zone + + } + cur_block = offset / ATCA_BLOCK_SIZE; + + while (data_idx < length) + { + if (read_size == ATCA_BLOCK_SIZE && zone_size - cur_block * ATCA_BLOCK_SIZE < ATCA_BLOCK_SIZE) + { + // We have less than a block to read and can't read past the end of the zone, switch to word reads + read_size = ATCA_WORD_SIZE; + cur_offset = ((data_idx + offset) / ATCA_WORD_SIZE) % (ATCA_BLOCK_SIZE / ATCA_WORD_SIZE); + } + + // Read next chunk of data + if (ATCA_SUCCESS != (status = calib_read_zone(device, zone, slot, (uint8_t)cur_block, (uint8_t)cur_offset, read_buf, read_size))) + { + ATCA_TRACE(status, "calib_read_zone - falied"); + break; + } + + // Calculate where in the read buffer we need data from + read_offset = cur_block * ATCA_BLOCK_SIZE + cur_offset * ATCA_WORD_SIZE; + if (read_offset < offset) + { + read_buf_idx = offset - read_offset; // Read data starts before the requested chunk + } + else + { + read_buf_idx = 0; // Read data is within the requested chunk + + } + // Calculate how much data from the read buffer we want to copy + if (length - data_idx < read_size - read_buf_idx) + { + copy_length = length - data_idx; + } + else + { + copy_length = read_size - read_buf_idx; + } + + memcpy(&data[data_idx], &read_buf[read_buf_idx], copy_length); + data_idx += copy_length; + if (read_size == ATCA_BLOCK_SIZE) + { + cur_block += 1; + } + else + { + cur_offset += 1; + } + } + if (status != ATCA_SUCCESS) + { + break; + } + } + while (false); + + return status; +} + +#if defined(ATCA_ECC204_SUPPORT) +/** \brief Use Read command to reads words 16 bytes from one of the slots in the EEPROM Configuration + * zone or 32 bytes in Data zone. + * + * \param[in] device Device context pointer + * \param[in] zone Selects config or data zone + * \param[in] slot select slot in config or data zone + * \param[in] block select the lock in given slot + * \param[in] offset 16 byte work index within the block. Ignored for 32 byte + * reads. + * \param[out] data Read data is returned here. + * \param[in] len Length of the data to be read. Must be either 16 or 32. + * + * \return ATCA_SUCCESS on success, otherwise an error code + */ +ATCA_STATUS calib_ecc204_read_zone(ATCADevice device, uint8_t zone, uint16_t slot, uint8_t block, size_t offset, + uint8_t* data, uint8_t len) +{ + ATCA_STATUS status = ATCA_SUCCESS; + ATCAPacket packet; + uint16_t addr; + uint8_t read_zone; + + (void)offset; + + read_zone = (zone == ATCA_ZONE_CONFIG) ? ATCA_ECC204_ZONE_CONFIG : ATCA_ECC204_ZONE_DATA; + + + if ((NULL == device) || (NULL == data)) + { + status = ATCA_TRACE(ATCA_BAD_PARAM, "Encountered Null pointer"); + } + else if (ATCA_ZONE_DATA == zone) + { + if (32 != len) + { + status = ATCA_TRACE(ATCA_BAD_PARAM, "Invalid parameter received"); + } + else if ((0x00 == slot) || (0x03 == slot)) + { + status = ATCA_TRACE(ATCA_BAD_PARAM, "Invalid slot number received"); + } + else + { + read_zone = ATCA_ECC204_ZONE_DATA; + } + } + else if (ATCA_ECC204_ZONE_CONFIG == zone) + { + if (16 != len) + { + status = ATCA_TRACE(ATCA_BAD_PARAM, "Invalid parameter received"); + } + else + { + read_zone = ATCA_ECC204_ZONE_CONFIG; + } + } + + if (ATCA_SUCCESS == status) + { + if (ATCA_SUCCESS != (status = calib_ecc204_get_addr(read_zone, slot, block, 0, &addr))) + { + ATCA_TRACE(status, "Address Encoding failed"); + } + + if (ATCA_SUCCESS == status) + { + // Build packets + packet.param1 = read_zone; + packet.param2 = addr; + + (void)atRead(atcab_get_device_type_ext(device), &packet); + + // Execute read command + if (ATCA_SUCCESS != (status = atca_execute_command(&packet, device))) + { + ATCA_TRACE(status, "Read command failed"); + } + else + { + memcpy(data, &packet.data[ATCA_RSP_DATA_IDX], len); + } + + } + } + + return status; +} + +/** \brief Use Read command to read configuration zone of ECC204 device + * + * \param[in] device Device context pointer + * \param[out] config_data returns config data of 64 bytes + * + * \return ATCA_SUCCESS on success, otherwise an error code + */ +ATCA_STATUS calib_ecc204_read_config_zone(ATCADevice device, uint8_t* config_data) +{ + ATCA_STATUS status = ATCA_GEN_FAIL; + uint8_t slot = 0; + + while (slot <= 3) + { + if (ATCA_SUCCESS != (status = calib_ecc204_read_zone(device, ATCA_ECC204_ZONE_CONFIG, + slot, 0, 0, + &config_data[ATCA_ECC204_CONFIG_SLOT_SIZE * slot], + ATCA_ECC204_CONFIG_SLOT_SIZE))) + { + ATCA_TRACE(status, "calib_ecc204_read_zone - failed"); + break; + } + slot += 1; // Increment slot to read next slot + } + + return status; +} + +/** \brief Use Read command to read serial number of device + * + * \param[in] device Device context pointer + * \param[in] serial_number 9 bytes ECC204 device serial number return here + * + * \return ATCA_SUCCESS on success, otherwise an error code + */ +ATCA_STATUS calib_ecc204_read_serial_number(ATCADevice device, uint8_t* serial_number) +{ + ATCA_STATUS status = ATCA_GEN_FAIL; + uint8_t read_buf[ATCA_ECC204_CONFIG_SLOT_SIZE]; + + + status = calib_ecc204_read_zone(device, ATCA_ECC204_ZONE_CONFIG, 0, 0, 0, read_buf, + ATCA_ECC204_CONFIG_SLOT_SIZE); + + if (ATCA_SUCCESS == status) + { + memcpy(serial_number, read_buf, ATCA_SERIAL_NUM_SIZE); + } + + return status; +} + +/** \brief Used to read an arbitrary number of bytes from any zone configured + * for clear reads. This function supports only for ECC204 device. + * + * This function will issue the Read command as many times as is required to + * read the requested data. + * + * \param[in] device Device context pointer + * \param[in] zone Zone to read data from. Option are ATCA_ZONE_CONFIG(1), + * or ATCA_ZONE_DATA(0). + * \param[in] slot Slot number to read from + * Ignored for all other zones. + * \param[in] offset Byte offset within the zone to read from. + * \param[out] data Read data is returned here. + * \param[in] length Number of bytes to read starting from the offset. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_ecc204_read_bytes_zone(ATCADevice device, uint8_t zone, uint16_t slot, + size_t offset, uint8_t* data, size_t length) +{ + ATCA_STATUS status = ATCA_GEN_FAIL; + uint8_t block_size = (zone == ATCA_ECC204_ZONE_CONFIG) ? ATCA_ECC204_CONFIG_SLOT_SIZE : ATCA_BLOCK_SIZE; + uint8_t no_of_blocks; + uint8_t data_idx = 0; + size_t cur_block = 0; + + if ((NULL == device) || (NULL == data)) + { + return ATCA_TRACE(ATCA_BAD_PARAM, "Encountered NULL pointer"); + } + else if ((ATCA_ECC204_ZONE_DATA == zone) && (((length > 64) && (2 == slot)) || + ((length > 320) && (1 == slot)) || (3 == slot) || (0 == slot))) + { + return ATCA_TRACE(ATCA_BAD_PARAM, "Invalid parameter received"); + } + else if (0 == length) + { + return ATCA_SUCCESS; + } + + cur_block = offset / block_size; + no_of_blocks = (uint8_t)(length / block_size); + while (no_of_blocks--) + { + if (ATCA_SUCCESS != (status = calib_ecc204_read_zone(device, zone, slot, cur_block, 0, + &data[block_size * data_idx], + block_size))) + { + ATCA_TRACE(status, "calib_ecc204_read_zone failed"); + break; + } + + if (zone == ATCA_ECC204_ZONE_CONFIG) + { + slot += 1; + } + else + { + cur_block += 1; + } + data_idx += 1; // increment data index + } + + return status; +} + +/** \brief Compares a specified configuration zone with the configuration zone + * currently on the ECC204 device. + * + * This only compares the static portions of the configuration zone and skips + * those that are unique per device (first 16 bytes) and areas that can change + * after the configuration zone has been locked (e.g. Counter). + * + * \param[in] device Device context pointer + * \param[in] config_data Full configuration data to compare the device + * against. + * \param[out] same_config Result is returned here. True if the static portions + * on the configuration zones are the same. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_ecc204_cmp_config_zone(ATCADevice device, uint8_t* config_data, bool* same_config) +{ + ATCA_STATUS status = ATCA_SUCCESS; + uint8_t device_config_data[ATCA_ECC204_CONFIG_SIZE]; + + if ((NULL == device) || (NULL == config_data) || (NULL == same_config)) + { + status = ATCA_TRACE(ATCA_BAD_PARAM, "NULL pointer encountered"); + } + + if (ATCA_SUCCESS == status) + { + *same_config = false; + if (ATCA_SUCCESS != (status = calib_ecc204_read_config_zone(device, device_config_data))) + { + ATCA_TRACE(status, "calib_ecc204_read_config_zone - failed"); + } + } + + if (ATCA_SUCCESS == status) + { + // compare slot 1 and slot 3 data && skip first 16 bytes and counter value + if (!((memcmp(&device_config_data[16], &config_data[16], ATCA_ECC204_CONFIG_SLOT_SIZE)) || + (memcmp(&device_config_data[48], &config_data[48], ATCA_ECC204_CONFIG_SLOT_SIZE)))) + { + *same_config = true; + } + } + + return status; +} + +#endif diff --git a/drivers/ecc108a/cryptoauthlib/calib/calib_sha.c b/drivers/ecc108a/cryptoauthlib/calib/calib_sha.c new file mode 100644 index 0000000..a8d9c23 --- /dev/null +++ b/drivers/ecc108a/cryptoauthlib/calib/calib_sha.c @@ -0,0 +1,544 @@ +/** + * \file + * \brief CryptoAuthLib Basic API methods for SHA command. + * + * The SHA command Computes a SHA-256 or HMAC/SHA digest for general purpose + * use by the host system. + * + * \note List of devices that support this command - ATSHA204A, ATECC108A, + * ATECC508A, and ATECC608A/B. There are differences in the modes that they + * support. Refer to device datasheets for full details. + * + * \copyright (c) 2015-2020 Microchip Technology Inc. and its subsidiaries. + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip software + * and any derivatives exclusively with Microchip products. It is your + * responsibility to comply with third party license terms applicable to your + * use of third party software (including open source software) that may + * accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER + * EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, + * SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE + * OF ANY KIND WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF + * MICROCHIP HAS BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE + * FORESEEABLE. TO THE FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL + * LIABILITY ON ALL CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED + * THE AMOUNT OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR + * THIS SOFTWARE. + */ + +#include "cryptoauthlib.h" + +typedef struct +{ + uint32_t total_msg_size; //!< Total number of message bytes processed + uint32_t block_size; //!< Number of bytes in current block + uint8_t block[ATCA_SHA256_BLOCK_SIZE * 2]; //!< Unprocessed message storage +} hw_sha256_ctx; + +/** \brief Executes SHA command, which computes a SHA-256 or HMAC/SHA-256 + * digest for general purpose use by the host system. + * + * Only the Start(0) and Compute(1) modes are available for ATSHA devices. + * + * \param[in] device Device context pointer + * \param[in] mode SHA command mode Start(0), Update/Compute(1), + * End(2), Public(3), HMACstart(4), HMACend(5), + * Read_Context(6), or Write_Context(7). Also + * message digest target location for the + * ATECC608. + * \param[in] length Number of bytes in the message parameter or + * KeySlot for the HMAC key if Mode is + * HMACstart(4) or Public(3). + * \param[in] message Message bytes to be hashed or Write_Context if + * restoring a context on the ATECC608. Can be + * NULL if not required by the mode. + * \param[out] data_out Data returned by the command (digest or + * context). + * \param[in,out] data_out_size As input, the size of the data_out buffer. As + * output, the number of bytes returned in + * data_out. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_sha_base(ATCADevice device, uint8_t mode, uint16_t length, const uint8_t* message, uint8_t* data_out, uint16_t* data_out_size) +{ + ATCAPacket packet; + ATCA_STATUS status = ATCA_GEN_FAIL; + uint8_t cmd_mode = (mode & SHA_MODE_MASK); + + if (device == NULL) + { + return ATCA_TRACE(ATCA_BAD_PARAM, "NULL pointer received"); + } + if (cmd_mode != SHA_MODE_SHA256_PUBLIC && cmd_mode != SHA_MODE_HMAC_START && + cmd_mode != SHA_MODE_ECC204_HMAC_START && length > 0 && message == NULL) + { + return ATCA_TRACE(ATCA_BAD_PARAM, "NULL pointer received"); // message data indicated, but nothing provided + } + if (data_out != NULL && data_out_size == NULL) + { + return ATCA_TRACE(ATCA_BAD_PARAM, "NULL pointer received"); + } + + do + { + //Build Command + packet.param1 = mode; + packet.param2 = length; + + if (cmd_mode != SHA_MODE_SHA256_PUBLIC && cmd_mode != SHA_MODE_HMAC_START && + cmd_mode != SHA_MODE_ECC204_HMAC_START) + { + memcpy(packet.data, message, (size_t)length); + } + + if ((status = atSHA(atcab_get_device_type_ext(device), &packet, length)) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "atSHA - failed"); + break; + } + + if ((status = atca_execute_command(&packet, device)) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "calib_sha_base - exection failed"); + break; + } + + if ((data_out != NULL) && (packet.data[ATCA_COUNT_IDX] > 4)) + { + if (packet.data[ATCA_COUNT_IDX] - ATCA_PACKET_OVERHEAD > *data_out_size) + { + status = ATCA_SMALL_BUFFER; + break; + } + *data_out_size = packet.data[ATCA_COUNT_IDX] - ATCA_PACKET_OVERHEAD; + memcpy(data_out, &packet.data[ATCA_RSP_DATA_IDX], *data_out_size); + } + } + while (0); + + return status; +} + +/** \brief Executes SHA command to initialize SHA-256 calculation engine + * \param[in] device Device context pointer + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_sha_start(ATCADevice device) +{ + return calib_sha_base(device, SHA_MODE_SHA256_START, 0, NULL, NULL, NULL); +} + +/** \brief Executes SHA command to add 64 bytes of message data to the current + * context. + * + * \param[in] device Device context pointer + * \param[in] message 64 bytes of message data to add to add to operation. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_sha_update(ATCADevice device, const uint8_t *message) +{ + return calib_sha_base(device, SHA_MODE_SHA256_UPDATE, 64, message, NULL, NULL); +} + +/** \brief Executes SHA command to complete SHA-256 or HMAC/SHA-256 operation. + * + * \param[in] device Device context pointer + * \param[out] digest Digest from SHA-256 or HMAC/SHA-256 will be returned + * here (32 bytes). + * \param[in] length Length of any remaining data to include in hash. Max 64 + * bytes. + * \param[in] message Remaining data to include in hash. NULL if length is 0. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_sha_end(ATCADevice device, uint8_t *digest, uint16_t length, const uint8_t *message) +{ + uint16_t digest_size = 32; + + return calib_sha_base(device, SHA_MODE_SHA256_END, length, message, digest, &digest_size); +} + +/** \brief Executes SHA command to read the SHA-256 context back. Only for + * ATECC608 with SHA-256 contexts. HMAC not supported. + * + * \param[in] device Device context pointer + * \param[out] context Context data is returned here. + * \param[in,out] context_size As input, the size of the context buffer in + * bytes. As output, the size of the returned + * context data. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_sha_read_context(ATCADevice device, uint8_t* context, uint16_t* context_size) +{ + return calib_sha_base(device, SHA_MODE_READ_CONTEXT, 0, NULL, context, context_size); +} + +/** \brief Executes SHA command to write (restore) a SHA-256 context into the + * the device. Only supported for ATECC608 with SHA-256 contexts. + * + * \param[in] device Device context pointer + * \param[in] context Context data to be restored. + * \param[in] context_size Size of the context data in bytes. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_sha_write_context(ATCADevice device, const uint8_t* context, uint16_t context_size) +{ + return calib_sha_base(device, SHA_MODE_WRITE_CONTEXT, context_size, context, NULL, NULL); +} + +/** \brief Use the SHA command to compute a SHA-256 digest. + * + * \param[in] device Device context pointer + * \param[in] length Size of message parameter in bytes. + * \param[in] message Message data to be hashed. + * \param[out] digest Digest is returned here (32 bytes). + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_sha(ATCADevice device, uint16_t length, const uint8_t *message, uint8_t *digest) +{ + return calib_hw_sha2_256(device, message, length, digest); +} + +/** \brief Initialize a SHA context for performing a hardware SHA-256 operation + * on a device. Note that only one SHA operation can be run at a time. + * + * \param[in] device Device context pointer + * \param[in] ctx SHA256 context + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_hw_sha2_256_init(ATCADevice device, atca_sha256_ctx_t* ctx) +{ + memset(ctx, 0, sizeof(*ctx)); + return calib_sha_start(device); +} + +/** \brief Add message data to a SHA context for performing a hardware SHA-256 + * operation on a device. + * + * \param[in] device Device context pointer + * \param[in] ctx SHA256 context + * \param[in] data Message data to be added to hash. + * \param[in] data_size Size of data in bytes. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_hw_sha2_256_update(ATCADevice device, atca_sha256_ctx_t* ctx, const uint8_t* data, size_t data_size) +{ + ATCA_STATUS status = ATCA_SUCCESS; + uint32_t block_count; + uint32_t rem_size = ATCA_SHA256_BLOCK_SIZE - ctx->block_size; + size_t copy_size = data_size > rem_size ? (size_t)rem_size : data_size; + uint32_t i = 0; + + // Copy data into current block + memcpy(&ctx->block[ctx->block_size], data, copy_size); + + if (ctx->block_size + data_size < ATCA_SHA256_BLOCK_SIZE) + { + // Not enough data to finish off the current block + ctx->block_size += (uint32_t)data_size; + return ATCA_SUCCESS; + } + + // Process the current block + if (ATCA_SUCCESS != (status = calib_sha_update(device, ctx->block))) + { + return ATCA_TRACE(status, "calib_sha_update - failed"); + } + + // Process any additional blocks + data_size -= copy_size; // Adjust to the remaining message bytes + block_count = (uint32_t)(data_size / ATCA_SHA256_BLOCK_SIZE); + for (i = 0; i < block_count; i++) + { + if (ATCA_SUCCESS != (status = calib_sha_update(device, &data[copy_size + i * ATCA_SHA256_BLOCK_SIZE]))) + { + return ATCA_TRACE(status, "calib_sha_update - failed"); + } + } + + // Save any remaining data + ctx->total_msg_size += (block_count + 1) * ATCA_SHA256_BLOCK_SIZE; + ctx->block_size = data_size % ATCA_SHA256_BLOCK_SIZE; + memcpy(ctx->block, &data[copy_size + block_count * ATCA_SHA256_BLOCK_SIZE], (size_t)ctx->block_size); + + return ATCA_SUCCESS; +} + +/** \brief Finish SHA-256 digest for a SHA context for performing a hardware + * SHA-256 operation on a device. + * + * \param[in] device Device context pointer + * \param[in] ctx SHA256 context + * \param[out] digest SHA256 digest is returned here (32 bytes) + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_hw_sha2_256_finish(ATCADevice device, atca_sha256_ctx_t* ctx, uint8_t* digest) +{ + ATCA_STATUS status = ATCA_SUCCESS; + uint32_t msg_size_bits; + uint32_t pad_zero_count; + uint16_t digest_size; + + if (device == NULL) + { + return ATCA_TRACE(ATCA_BAD_PARAM, "NULL pointer received"); + } + + if (device->mIface.mIfaceCFG->devtype == ATSHA204A) + { + // ATSHA204A only implements the raw 64-byte block operation, but + // doesn't add in the final footer information. So we do that manually + // here. + + // Calculate the total message size in bits + ctx->total_msg_size += ctx->block_size; + msg_size_bits = (ctx->total_msg_size * 8); + + // Calculate the number of padding zero bytes required between the 1 bit byte and the ATCA_SHA256_BLOCK_SIZEbit message size in bits. + pad_zero_count = (ATCA_SHA256_BLOCK_SIZE - ((ctx->block_size + 9) % ATCA_SHA256_BLOCK_SIZE)) % ATCA_SHA256_BLOCK_SIZE; + + // Append a single 1 bit + ctx->block[ctx->block_size++] = 0x80; + + // Add padding zeros plus upper 4 bytes of total message size in bits (only supporting 32bit message bit counts) + memset(&ctx->block[ctx->block_size], 0, (size_t)pad_zero_count + 4); + ctx->block_size += pad_zero_count + 4; + + // Add the total message size in bits to the end of the current block. Technically this is + // supposed to be 8 bytes. This shortcut will reduce the max message size to 536,870,911 bytes. + ctx->block[ctx->block_size++] = (uint8_t)(msg_size_bits >> 24); + ctx->block[ctx->block_size++] = (uint8_t)(msg_size_bits >> 16); + ctx->block[ctx->block_size++] = (uint8_t)(msg_size_bits >> 8); + ctx->block[ctx->block_size++] = (uint8_t)(msg_size_bits >> 0); + + digest_size = 32; + if (ATCA_SUCCESS != (status = calib_sha_base(device, SHA_MODE_SHA256_UPDATE, ATCA_SHA256_BLOCK_SIZE, ctx->block, digest, &digest_size))) + { + return ATCA_TRACE(status, "calib_sha_base - failed"); + } + + if (ctx->block_size > ATCA_SHA256_BLOCK_SIZE) + { + digest_size = 32; + if (ATCA_SUCCESS != (status = calib_sha_base(device, SHA_MODE_SHA256_UPDATE, ATCA_SHA256_BLOCK_SIZE, &ctx->block[ATCA_SHA256_BLOCK_SIZE], digest, &digest_size))) + { + return ATCA_TRACE(status, "calib_sha_base - failed"); + } + } + } + else + { + if (ATCA_SUCCESS != (status = calib_sha_end(device, digest, (uint16_t)ctx->block_size, ctx->block))) + { + return ATCA_TRACE(status, "calib_sha_end - failed"); + } + } + + return ATCA_SUCCESS; +} + +/** \brief Use the SHA command to compute a SHA-256 digest. + * + * \param[in] device Device context pointer + * \param[in] data Message data to be hashed. + * \param[in] data_size Size of data in bytes. + * \param[out] digest Digest is returned here (32 bytes). + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_hw_sha2_256(ATCADevice device, const uint8_t * data, size_t data_size, uint8_t* digest) +{ + ATCA_STATUS status = ATCA_SUCCESS; + atca_sha256_ctx_t ctx; + + if (ATCA_SUCCESS != (status = calib_hw_sha2_256_init(device, &ctx))) + { + return ATCA_TRACE(status, "calib_hw_sha2_256_init - failed"); + } + + if (ATCA_SUCCESS != (status = calib_hw_sha2_256_update(device, &ctx, data, data_size))) + { + return ATCA_TRACE(status, "calib_hw_sha2_256_update - failed"); + } + + if (ATCA_SUCCESS != (status = calib_hw_sha2_256_finish(device, &ctx, digest))) + { + return ATCA_TRACE(status, "calib_hw_sha2_256_finish - failed"); + } + + return ATCA_SUCCESS; +} + +/** \brief Executes SHA command to start an HMAC/SHA-256 operation + * + * \param[in] device Device context pointer + * \param[in] ctx HMAC/SHA-256 context + * \param[in] key_slot Slot key id to use for the HMAC calculation + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_sha_hmac_init(ATCADevice device, atca_hmac_sha256_ctx_t* ctx, uint16_t key_slot) +{ + uint8_t mode = SHA_MODE_HMAC_START; + + if ((NULL == ctx) || (NULL == device)) + { + return ATCA_TRACE(ATCA_BAD_PARAM, "NULL pointer encountered"); + } + + memset(ctx, 0, sizeof(*ctx)); + + if (ECC204 == device->mIface.mIfaceCFG->devtype) + { + mode = SHA_MODE_ECC204_HMAC_START; + } + + return calib_sha_base(device, mode, key_slot, NULL, NULL, NULL); +} + +/** \brief Executes SHA command to add an arbitrary amount of message data to + * a HMAC/SHA-256 operation. + * + * \param[in] device Device context pointer + * \param[in] ctx HMAC/SHA-256 context + * \param[in] data Message data to add + * \param[in] data_size Size of message data in bytes + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_sha_hmac_update(ATCADevice device, atca_hmac_sha256_ctx_t* ctx, const uint8_t* data, size_t data_size) +{ + ATCA_STATUS status = ATCA_SUCCESS; + uint32_t block_count; + uint32_t rem_size = ATCA_SHA256_BLOCK_SIZE - ctx->block_size; + size_t copy_size = data_size > rem_size ? (size_t)rem_size : data_size; + uint32_t i = 0; + + // Copy data into current block + memcpy(&ctx->block[ctx->block_size], data, copy_size); + + if (ctx->block_size + data_size < ATCA_SHA256_BLOCK_SIZE) + { + // Not enough data to finish off the current block + ctx->block_size += (uint32_t)data_size; + return ATCA_SUCCESS; + } + + // Process the current block + if (ATCA_SUCCESS != (status = calib_sha_base(device, SHA_MODE_HMAC_UPDATE, ATCA_SHA256_BLOCK_SIZE, ctx->block, NULL, NULL))) + { + return ATCA_TRACE(status, "calib_sha_base - failed"); + } + + // Process any additional blocks + data_size -= copy_size; // Adjust to the remaining message bytes + block_count = (uint32_t)(data_size / ATCA_SHA256_BLOCK_SIZE); + for (i = 0; i < block_count; i++) + { + if (ATCA_SUCCESS != (status = calib_sha_base(device, SHA_MODE_HMAC_UPDATE, ATCA_SHA256_BLOCK_SIZE, &data[copy_size + i * ATCA_SHA256_BLOCK_SIZE], NULL, NULL))) + { + return ATCA_TRACE(status, "calib_sha_base - failed"); + } + } + + // Save any remaining data + ctx->total_msg_size += (block_count + 1) * ATCA_SHA256_BLOCK_SIZE; + ctx->block_size = data_size % ATCA_SHA256_BLOCK_SIZE; + memcpy(ctx->block, &data[copy_size + block_count * ATCA_SHA256_BLOCK_SIZE], (size_t)ctx->block_size); + + return ATCA_SUCCESS; +} + +/** \brief Executes SHA command to complete a HMAC/SHA-256 operation. + * + * \param[in] device Device context pointer + * \param[in] ctx HMAC/SHA-256 context + * \param[out] digest HMAC/SHA-256 result is returned here (32 bytes). + * \param[in] target Where to save the digest internal to the device. + * For ATECC608, can be SHA_MODE_TARGET_TEMPKEY, + * SHA_MODE_TARGET_MSGDIGBUF, or SHA_MODE_TARGET_OUT_ONLY. + * For all other devices, SHA_MODE_TARGET_TEMPKEY is the + * only option. + * For ECC204, target is ignored (0x00) + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_sha_hmac_finish(ATCADevice device, atca_hmac_sha256_ctx_t *ctx, uint8_t* digest, uint8_t target) +{ + uint8_t mode = SHA_MODE_HMAC_END; + uint16_t digest_size = 32; + + if (device == NULL) + { + return ATCA_TRACE(ATCA_BAD_PARAM, "NULL pointer received"); + } + + if (ATECC608A == device->mIface.mIfaceCFG->devtype) + { + mode = SHA_MODE_608_HMAC_END; + } + else if (ECC204 == device->mIface.mIfaceCFG->devtype) + { + mode = SHA_MODE_ECC204_HMAC_END; + } + else if (target != SHA_MODE_TARGET_TEMPKEY) + { + return ATCA_TRACE(ATCA_BAD_PARAM, "Invalid target received"); + } + + mode |= target; + + return calib_sha_base(device, mode, (uint16_t)ctx->block_size, ctx->block, digest, &digest_size); +} + +/** \brief Use the SHA command to compute an HMAC/SHA-256 operation. + * + * \param[in] device Device context pointer + * \param[in] data Message data to be hashed. + * \param[in] data_size Size of data in bytes. + * \param[in] key_slot Slot key id to use for the HMAC calculation + * \param[out] digest Digest is returned here (32 bytes). + * \param[in] target Where to save the digest internal to the device. + * For ATECC608, can be SHA_MODE_TARGET_TEMPKEY, + * SHA_MODE_TARGET_MSGDIGBUF, or + * SHA_MODE_TARGET_OUT_ONLY. For all other devices, + * SHA_MODE_TARGET_TEMPKEY is the only option. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_sha_hmac(ATCADevice device, const uint8_t * data, size_t data_size, uint16_t key_slot, uint8_t* digest, uint8_t target) +{ + ATCA_STATUS status = ATCA_SUCCESS; + atca_hmac_sha256_ctx_t ctx; + + if (ATCA_SUCCESS != (status = calib_sha_hmac_init(device, &ctx, key_slot))) + { + return ATCA_TRACE(status, "calib_sha_hmac_init - failed"); + } + + if (ATCA_SUCCESS != (status = calib_sha_hmac_update(device, &ctx, data, data_size))) + { + return ATCA_TRACE(status, "calib_sha_hmac_update - failed"); + } + + if (ATCA_SUCCESS != (status = calib_sha_hmac_finish(device, &ctx, digest, target))) + { + return ATCA_TRACE(status, "calib_sha_hmac_finish - failed"); + } + + return ATCA_SUCCESS; +} \ No newline at end of file diff --git a/drivers/ecc108a/cryptoauthlib/calib/calib_sign.c b/drivers/ecc108a/cryptoauthlib/calib/calib_sign.c new file mode 100644 index 0000000..940abf2 --- /dev/null +++ b/drivers/ecc108a/cryptoauthlib/calib/calib_sign.c @@ -0,0 +1,252 @@ +/** + * \file + * \brief CryptoAuthLib Basic API methods for Sign command. + * + * The Sign command generates a signature using the private key in slot with + * ECDSA algorithm. + * + * \note List of devices that support this command - ATECC108A, ATECC508A, and + * ATECC608A/B. There are differences in the modes that they support. Refer + * to device datasheets for full details. + * + * \copyright (c) 2015-2020 Microchip Technology Inc. and its subsidiaries. + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip software + * and any derivatives exclusively with Microchip products. It is your + * responsibility to comply with third party license terms applicable to your + * use of third party software (including open source software) that may + * accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER + * EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, + * SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE + * OF ANY KIND WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF + * MICROCHIP HAS BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE + * FORESEEABLE. TO THE FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL + * LIABILITY ON ALL CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED + * THE AMOUNT OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR + * THIS SOFTWARE. + */ + +#include "cryptoauthlib.h" + +/** \brief Executes the Sign command, which generates a signature using the + * ECDSA algorithm. + * + * \param[in] device Device context pointer + * \param[in] mode Mode determines what the source of the message to be + * signed. + * \param[in] key_id Private key slot used to sign the message. + * \param[out] signature Signature is returned here. Format is R and S + * integers in big-endian format. 64 bytes for P256 + * curve. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_sign_base(ATCADevice device, uint8_t mode, uint16_t key_id, uint8_t *signature) +{ + ATCAPacket packet; + ATCA_STATUS status = ATCA_GEN_FAIL; + + if ((device == NULL) || (signature == NULL)) + { + return ATCA_TRACE(ATCA_BAD_PARAM, "NULL pointer received"); + } + + do + { + // Build sign command + packet.param1 = mode; + packet.param2 = key_id; + if ((status = atSign(atcab_get_device_type_ext(device), &packet)) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "atSign - failed"); + break; + } + + if ((status = atca_execute_command(&packet, device)) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "calib_sign_base - execution failed"); + break; + } + + if (signature != NULL) + { + if (packet.data[ATCA_COUNT_IDX] == (ATCA_SIG_SIZE + ATCA_PACKET_OVERHEAD)) + { + memcpy(signature, &packet.data[ATCA_RSP_DATA_IDX], ATCA_SIG_SIZE); + } + else + { + status = ATCA_RX_FAIL; + } + + } + } + while (0); + + return status; +} + +/** \brief Executes Sign command, to sign a 32-byte external message using the + * private key in the specified slot. The message to be signed + * will be loaded into the Message Digest Buffer to the + * ATECC608 device or TempKey for other devices. + * + * \param[in] device Device context pointer + * \param[in] key_id Slot of the private key to be used to sign the + * message. + * \param[in] msg 32-byte message to be signed. Typically the SHA256 + * hash of the full message. + * \param[out] signature Signature will be returned here. Format is R and S + * integers in big-endian format. 64 bytes for P256 + * curve. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_sign(ATCADevice device, uint16_t key_id, const uint8_t *msg, uint8_t *signature) +{ + ATCA_STATUS status = ATCA_GEN_FAIL; + uint8_t nonce_target = NONCE_MODE_TARGET_TEMPKEY; + uint8_t sign_source = SIGN_MODE_SOURCE_TEMPKEY; + + do + { + // Make sure RNG has updated its seed + if ((status = calib_random(device, NULL)) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "calib_random - failed"); + break; + } + + // Load message into device + if (ATECC608 == device->mIface.mIfaceCFG->devtype) + { + // Use the Message Digest Buffer for the ATECC608 + nonce_target = NONCE_MODE_TARGET_MSGDIGBUF; + sign_source = SIGN_MODE_SOURCE_MSGDIGBUF; + } + if ((status = calib_nonce_load(device, nonce_target, msg, 32)) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "calib_nonce_load - failed"); + break; + } + + // Sign the message + if ((status = calib_sign_base(device, SIGN_MODE_EXTERNAL | sign_source, key_id, signature)) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "calib_sign_base - failed"); + break; + } + } + while (0); + + return status; +} + +/** \brief Executes Sign command to sign an internally generated message. + * + * \param[in] device Device context pointer + * \param[in] key_id Slot of the private key to be used to sign the + * message. + * \param[in] is_invalidate Set to true if the signature will be used with + * the Verify(Invalidate) command. false for all + * other cases. + * \param[in] is_full_sn Set to true if the message should incorporate + * the device's full serial number. + * \param[out] signature Signature is returned here. Format is R and S + * integers in big-endian format. 64 bytes for + * P256 curve. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_sign_internal(ATCADevice device, uint16_t key_id, bool is_invalidate, bool is_full_sn, uint8_t *signature) +{ + ATCA_STATUS status = ATCA_GEN_FAIL; + uint8_t mode = SIGN_MODE_INTERNAL; + + do + { + // Sign the message + if (is_invalidate) + { + mode |= SIGN_MODE_INVALIDATE; + } + + if (is_full_sn) + { + mode |= SIGN_MODE_INCLUDE_SN; + } + + if ((status = calib_sign_base(device, mode, key_id, signature)) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "calib_sign_base - failed"); + break; + } + + } + while (0); + + return status; +} + +/** \brief Execute sign command to sign the 32 bytes message digest using private key + * mentioned in slot. + * + * \param[in] device Device context pointer + * \param[in] key_id points to private key slot + * \param[in] msg 32 bytes message digest + * \param[out] signature Signature is returned here. Format is R and S + * integers in big-endian format. 64 bytes for + * P256 curve. + * + * \return ATCA_SUCCESS on success, otherwise an error code + */ +ATCA_STATUS calib_ecc204_sign(ATCADevice device, uint16_t key_id, const uint8_t* msg, uint8_t* signature) +{ + ATCA_STATUS status = ATCA_SUCCESS; + ATCAPacket packet; + + packet.param1 = 0x00; + packet.param2 = key_id; + + if ((NULL == device) || (NULL == msg)) + { + status = ATCA_TRACE(ATCA_BAD_PARAM, "NULL pointer encountered"); + } + + if (ATCA_SUCCESS == status) + { + // copy message digest into i/o buffer + memcpy(packet.data, msg, ATCA_SHA256_DIGEST_SIZE); + + (void)atSign(atcab_get_device_type_ext(device), &packet); + + if (ATCA_SUCCESS != (status = atca_execute_command(&packet, device))) + { + ATCA_TRACE(status, "calib_ecc204_sign - execution failed"); + } + } + + if (ATCA_SUCCESS == status) + { + if (signature != NULL) + { + if (packet.data[ATCA_COUNT_IDX] == (ATCA_SIG_SIZE + ATCA_PACKET_OVERHEAD)) + { + memcpy(signature, &packet.data[ATCA_RSP_DATA_IDX], ATCA_SIG_SIZE); + } + else + { + status = ATCA_RX_FAIL; + } + + } + } + + return status; +} diff --git a/drivers/ecc108a/cryptoauthlib/calib/calib_updateextra.c b/drivers/ecc108a/cryptoauthlib/calib/calib_updateextra.c new file mode 100644 index 0000000..7f55bea --- /dev/null +++ b/drivers/ecc108a/cryptoauthlib/calib/calib_updateextra.c @@ -0,0 +1,84 @@ +/** + * \file + * \brief CryptoAuthLib Basic API methods for UpdateExtra command. + * + * The UpdateExtra command is used to update the values of the two extra bytes + * within the Configuration zone after the Configuration zone has been locked. + * + * \note List of devices that support this command - ATSHA204A, ATECC108A, + * ATECC508A, and ATECC608A/B. There are differences in the modes that they + * support. Refer to device datasheets for full details. + * + * \copyright (c) 2015-2020 Microchip Technology Inc. and its subsidiaries. + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip software + * and any derivatives exclusively with Microchip products. It is your + * responsibility to comply with third party license terms applicable to your + * use of third party software (including open source software) that may + * accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER + * EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, + * SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE + * OF ANY KIND WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF + * MICROCHIP HAS BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE + * FORESEEABLE. TO THE FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL + * LIABILITY ON ALL CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED + * THE AMOUNT OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR + * THIS SOFTWARE. + */ + +#include "cryptoauthlib.h" + +/** \brief Executes UpdateExtra command to update the values of the two + * extra bytes within the Configuration zone (bytes 84 and 85). + * + * Can also be used to decrement the limited use counter associated with the + * key in slot NewValue. + * + * \param[in] device Device context pointer + * \param[in] mode Mode determines what operations the UpdateExtra + * command performs. + * \param[in] new_value Value to be written. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_updateextra(ATCADevice device, uint8_t mode, uint16_t new_value) +{ + ATCAPacket packet; + ATCA_STATUS status = ATCA_GEN_FAIL; + + do + { + if (device == NULL) + { + status = ATCA_TRACE(ATCA_BAD_PARAM, "NULL pointer received"); + break; + } + + // Build command + memset(&packet, 0, sizeof(packet)); + packet.param1 = mode; + packet.param2 = new_value; + + if ((status = atUpdateExtra(atcab_get_device_type_ext(device), &packet)) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "atUpdateExtra - failed"); + break; + } + + if ((status = atca_execute_command(&packet, device)) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "calib_updateextra - execution failed"); + break; + } + + } + while (0); + + return status; +} diff --git a/drivers/ecc108a/cryptoauthlib/calib/calib_verify.c b/drivers/ecc108a/cryptoauthlib/calib/calib_verify.c new file mode 100644 index 0000000..f51c5f1 --- /dev/null +++ b/drivers/ecc108a/cryptoauthlib/calib/calib_verify.c @@ -0,0 +1,463 @@ +/** + * \file + * \brief CryptoAuthLib Basic API methods for Verify command. + * + * The Verify command takes an ECDSA [R,S] signature and verifies that it is + * correctly generated given an input message digest and public key. + * + * \note List of devices that support this command - ATECC108A, ATECC508A, and + * ATECC608A/B. There are differences in the modes that they support. Refer + * to device datasheet for full details. + * + * \copyright (c) 2015-2020 Microchip Technology Inc. and its subsidiaries. + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip software + * and any derivatives exclusively with Microchip products. It is your + * responsibility to comply with third party license terms applicable to your + * use of third party software (including open source software) that may + * accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER + * EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, + * SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE + * OF ANY KIND WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF + * MICROCHIP HAS BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE + * FORESEEABLE. TO THE FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL + * LIABILITY ON ALL CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED + * THE AMOUNT OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR + * THIS SOFTWARE. + */ + +#include "cryptoauthlib.h" +#include "host/atca_host.h" + +/** \brief Executes the Verify command, which takes an ECDSA [R,S] signature + * and verifies that it is correctly generated from a given message and + * public key. In all cases, the signature is an input to the command. + * + * For the Stored, External, and ValidateExternal Modes, the contents of + * TempKey (or Message Digest Buffer in some cases for the ATECC608) should + * contain the 32 byte message. + * + * \param[in] device Device context pointer + * \param[in] mode Verify command mode and options + * \param[in] key_id Stored mode, the slot containing the public key to + * be used for the verification. + * ValidateExternal mode, the slot containing the + * public key to be validated. + * External mode, KeyID contains the curve type to be + * used to Verify the signature. + * Validate or Invalidate mode, the slot containing + * the public key to be (in)validated. + * \param[in] signature Signature to be verified. R and S integers in + * big-endian format. 64 bytes for P256 curve. + * \param[in] public_key If mode is External, the public key to be used for + * verification. X and Y integers in big-endian format. + * 64 bytes for P256 curve. NULL for all other modes. + * \param[in] other_data If mode is Validate, the bytes used to generate the + * message for the validation (19 bytes). NULL for all + * other modes. + * \param[out] mac If mode indicates a validating MAC, then the MAC will + * will be returned here. Can be NULL otherwise. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_verify(ATCADevice device, uint8_t mode, uint16_t key_id, const uint8_t* signature, const uint8_t* public_key, const uint8_t* other_data, uint8_t* mac) +{ + ATCAPacket packet; + ATCA_STATUS status = ATCA_GEN_FAIL; + uint8_t verify_mode = (mode & VERIFY_MODE_MASK); + + do + { + if ((device == NULL) || (verify_mode == VERIFY_MODE_EXTERNAL && public_key == NULL) || + (signature == NULL)) + { + status = ATCA_TRACE(ATCA_BAD_PARAM, "NULL pointer recived"); + break; + } + + if ((verify_mode == VERIFY_MODE_VALIDATE || verify_mode == VERIFY_MODE_INVALIDATE) && other_data == NULL) + { + status = ATCA_TRACE(ATCA_BAD_PARAM, "NULL pointer recived"); + break; + } + + // Build the verify command + packet.param1 = mode; + packet.param2 = key_id; + memcpy(&packet.data[0], signature, ATCA_SIG_SIZE); + if (verify_mode == VERIFY_MODE_EXTERNAL) + { + memcpy(&packet.data[ATCA_SIG_SIZE], public_key, ATCA_PUB_KEY_SIZE); + } + else if (other_data) + { + memcpy(&packet.data[ATCA_SIG_SIZE], other_data, VERIFY_OTHER_DATA_SIZE); + } + + if ((status = atVerify(atcab_get_device_type_ext(device), &packet)) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "atVerify - failed"); + break; + } + + if ((status = atca_execute_command(&packet, device)) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "calib_verify - execution failed"); + break; + } + + // The Verify command may return MAC if requested + if ((mac != NULL) && (packet.data[ATCA_COUNT_IDX] >= (ATCA_PACKET_OVERHEAD + MAC_SIZE))) + { + memcpy(mac, &packet.data[ATCA_RSP_DATA_IDX], MAC_SIZE); + } + + } + while (false); + + return status; +} + +/** \brief Executes the Verify command with verification MAC for the External + * or Stored Verify modes.. This function is only available on the + * ATECC608. + * + * \param[in] device Device context pointer + * \param[in] mode Verify command mode. Can be VERIFY_MODE_EXTERNAL or + * VERIFY_MODE_STORED. + * \param[in] key_id For VERIFY_MODE_STORED mode, the slot containing the + * public key to be used for the verification. + * For VERIFY_MODE_EXTERNAL mode, KeyID contains the + * curve type to be used to Verify the signature. Only + * VERIFY_KEY_P256 supported. + * \param[in] message 32 byte message to be verified. Typically + * the SHA256 hash of the full message. + * \param[in] signature Signature to be verified. R and S integers in + * big-endian format. 64 bytes for P256 curve. + * \param[in] public_key For VERIFY_MODE_EXTERNAL mode, the public key to be + * used for verification. X and Y integers in + * big-endian format. 64 bytes for P256 curve. Null for + * VERIFY_MODE_STORED mode. + * \param[in] num_in System nonce (32 byte) used for the verification + * MAC. + * \param[in] io_key IO protection key for verifying the validation MAC. + * \param[out] is_verified Boolean whether or not the message, signature, + * public key verified. + * + * \return ATCA_SUCCESS on verification success or failure, because the + * command still completed successfully. + */ +static ATCA_STATUS calib_verify_extern_stored_mac(ATCADevice device, uint8_t mode, uint16_t key_id, const uint8_t* message, const uint8_t* signature, const uint8_t* public_key, const uint8_t* num_in, const uint8_t* io_key, bool* is_verified) +{ + ATCA_STATUS status = ATCA_GEN_FAIL; + uint8_t msg_dig_buf[64]; + atca_verify_mac_in_out_t verify_mac_params; + uint8_t mac[SECUREBOOT_MAC_SIZE]; + uint8_t host_mac[SECUREBOOT_MAC_SIZE]; + + do + { + if ((is_verified == NULL) || (signature == NULL) || (message == NULL) || (num_in == NULL) + || (io_key == NULL) || ((mode & VERIFY_MODE_MASK) == VERIFY_MODE_EXTERNAL && public_key == NULL)) + { + return ATCA_TRACE(ATCA_BAD_PARAM, "NULL pointer recived"); + } + + *is_verified = false; + + // When using the message digest buffer as the message source, the + // second 32 bytes in the buffer will be the MAC system nonce. + memcpy(&msg_dig_buf[0], message, 32); + memcpy(&msg_dig_buf[32], num_in, 32); + if ((status = calib_nonce_load(device, NONCE_MODE_TARGET_MSGDIGBUF, msg_dig_buf, 64)) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "calib_nonce_load - failed"); + break; + } + + // Calculate the expected MAC + memset(&verify_mac_params, 0, sizeof(verify_mac_params)); + verify_mac_params.mode = mode | VERIFY_MODE_SOURCE_MSGDIGBUF | VERIFY_MODE_MAC_FLAG; + verify_mac_params.key_id = key_id; + verify_mac_params.signature = signature; + verify_mac_params.msg_dig_buf = msg_dig_buf; + verify_mac_params.io_key = io_key; + verify_mac_params.temp_key = NULL; + verify_mac_params.sn = NULL; + verify_mac_params.mac = host_mac; + /* if (ATCA_SUCCESS != (status = atcah_verify_mac(&verify_mac_params))) + { + ATCA_TRACE(status, "atcah_verify_mac - failed"); + break; + }*/ + + if (ATCA_SUCCESS != (status = calib_verify(device, verify_mac_params.mode, verify_mac_params.key_id, signature, public_key, NULL, mac))) + { + if (status == ATCA_CHECKMAC_VERIFY_FAILED) + { + status = ATCA_SUCCESS; // Verify failed, but command succeeded + } + break; + } + + *is_verified = (memcmp(host_mac, mac, MAC_SIZE) == 0); + } + while (0); + + return status; +} + +/** \brief Executes the Verify command, which verifies a signature (ECDSA + * verify operation) with all components (message, signature, and + * public key) supplied. The message to be signed will be loaded into + * the Message Digest Buffer to the ATECC608 device or TempKey for + * other devices. + * + * \param[in] device Device context pointer + * \param[in] message 32 byte message to be verified. Typically + * the SHA256 hash of the full message. + * \param[in] signature Signature to be verified. R and S integers in + * big-endian format. 64 bytes for P256 curve. + * \param[in] public_key The public key to be used for verification. X and + * Y integers in big-endian format. 64 bytes for + * P256 curve. + * \param[out] is_verified Boolean whether or not the message, signature, + * public key verified. + * + * \return ATCA_SUCCESS on verification success or failure, because the + * command still completed successfully. + */ +ATCA_STATUS calib_verify_extern(ATCADevice device, const uint8_t *message, const uint8_t *signature, const uint8_t *public_key, bool *is_verified) +{ + ATCA_STATUS status = ATCA_GEN_FAIL; + uint8_t nonce_target = NONCE_MODE_TARGET_TEMPKEY; + uint8_t verify_source = VERIFY_MODE_SOURCE_TEMPKEY; + + if ((device == NULL) || (is_verified == NULL) || (signature == NULL) || (message == NULL) || + (public_key == NULL)) + { + return ATCA_TRACE(ATCA_BAD_PARAM, "NULL pointer received"); + } + + *is_verified = false; + + do + { + // Load message into device + if (ATECC608 == device->mIface.mIfaceCFG->devtype) + { + // Use the Message Digest Buffer for the ATECC608 + nonce_target = NONCE_MODE_TARGET_MSGDIGBUF; + verify_source = VERIFY_MODE_SOURCE_MSGDIGBUF; + } + if ((status = calib_nonce_load(device, nonce_target, message, 32)) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "calib_nonce_load - failed"); + break; + } + + status = calib_verify(device, VERIFY_MODE_EXTERNAL | verify_source, VERIFY_KEY_P256, signature, public_key, NULL, NULL); + + *is_verified = (status == ATCA_SUCCESS); + if (status == ATCA_CHECKMAC_VERIFY_FAILED) + { + status = ATCA_SUCCESS; // Verify failed, but command succeeded + } + } + while (0); + + return status; +} + +/** \brief Executes the Verify command with verification MAC, which verifies a + * signature (ECDSA verify operation) with all components (message, + * signature, and public key) supplied. This function is only available + * on the ATECC608. + * + * \param[in] device Device context pointer + * \param[in] message 32 byte message to be verified. Typically + * the SHA256 hash of the full message. + * \param[in] signature Signature to be verified. R and S integers in + * big-endian format. 64 bytes for P256 curve. + * \param[in] public_key The public key to be used for verification. X and + * Y integers in big-endian format. 64 bytes for + * P256 curve. + * \param[in] num_in System nonce (32 byte) used for the verification + * MAC. + * \param[in] io_key IO protection key for verifying the validation MAC. + * \param[out] is_verified Boolean whether or not the message, signature, + * public key verified. + * + * \return ATCA_SUCCESS on verification success or failure, because the + * command still completed successfully. + */ +ATCA_STATUS calib_verify_extern_mac(ATCADevice device, const uint8_t *message, const uint8_t* signature, const uint8_t* public_key, const uint8_t* num_in, const uint8_t* io_key, bool* is_verified) +{ + return calib_verify_extern_stored_mac(device, VERIFY_MODE_EXTERNAL, VERIFY_KEY_P256, message, signature, public_key, num_in, io_key, is_verified); +} + +/** \brief Executes the Verify command, which verifies a signature (ECDSA + * verify operation) with a public key stored in the device. The + * message to be signed will be loaded into the Message Digest Buffer + * to the ATECC608 device or TempKey for other devices. + * + * \param[in] device Device context pointer + * \param[in] message 32 byte message to be verified. Typically + * the SHA256 hash of the full message. + * \param[in] signature Signature to be verified. R and S integers in + * big-endian format. 64 bytes for P256 curve. + * \param[in] key_id Slot containing the public key to be used in the + * verification. + * \param[out] is_verified Boolean whether or not the message, signature, + * public key verified. + * + * \return ATCA_SUCCESS on verification success or failure, because the + * command still completed successfully. + */ +ATCA_STATUS calib_verify_stored(ATCADevice device, const uint8_t *message, const uint8_t *signature, uint16_t key_id, bool *is_verified) +{ + ATCA_STATUS status = ATCA_GEN_FAIL; + uint8_t nonce_target = NONCE_MODE_TARGET_TEMPKEY; + uint8_t verify_source = VERIFY_MODE_SOURCE_TEMPKEY; + + if ((device == NULL) || (is_verified == NULL) || (signature == NULL) || (message == NULL)) + { + return ATCA_TRACE(ATCA_BAD_PARAM, "NULL pointer received"); + } + + *is_verified = false; + + do + { + // Load message into device + if (ATECC608 == device->mIface.mIfaceCFG->devtype) + { + // Use the Message Digest Buffer for the ATECC608 + nonce_target = NONCE_MODE_TARGET_MSGDIGBUF; + verify_source = VERIFY_MODE_SOURCE_MSGDIGBUF; + } + if ((status = calib_nonce_load(device, nonce_target, message, 32)) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "calib_nonce_load - failed"); + break; + } + + status = calib_verify(device, VERIFY_MODE_STORED | verify_source, key_id, signature, NULL, NULL, NULL); + + *is_verified = (status == ATCA_SUCCESS); + if (status == ATCA_CHECKMAC_VERIFY_FAILED) + { + status = ATCA_SUCCESS; // Verify failed, but command succeeded + } + } + while (0); + + return status; +} + +/** \brief Executes the Verify command with verification MAC, which verifies a + * signature (ECDSA verify operation) with a public key stored in the + * device. This function is only available on the ATECC608. + * + * \param[in] device Device context pointer + * \param[in] message 32 byte message to be verified. Typically + * the SHA256 hash of the full message. + * \param[in] signature Signature to be verified. R and S integers in + * big-endian format. 64 bytes for P256 curve. + * \param[in] key_id Slot containing the public key to be used in the + * verification. + * \param[in] num_in System nonce (32 byte) used for the verification + * MAC. + * \param[in] io_key IO protection key for verifying the validation MAC. + * \param[out] is_verified Boolean whether or not the message, signature, + * public key verified. + * + * \return ATCA_SUCCESS on verification success or failure, because the + * command still completed successfully. + */ +ATCA_STATUS calib_verify_stored_mac(ATCADevice device, const uint8_t *message, const uint8_t *signature, uint16_t key_id, const uint8_t* num_in, const uint8_t* io_key, bool* is_verified) +{ + return calib_verify_extern_stored_mac(device, VERIFY_MODE_STORED, key_id, message, signature, NULL, num_in, io_key, is_verified); +} + +/** \brief Executes the Verify command in Validate mode to validate a public + * key stored in a slot. + * + * This command can only be run after GenKey has been used to create a PubKey + * digest of the public key to be validated in TempKey (mode=0x10). + * + * \param[in] device Device context pointer + * \param[in] key_id Slot containing the public key to be validated. + * \param[in] signature Signature to be verified. R and S integers in + * big-endian format. 64 bytes for P256 curve. + * \param[in] other_data 19 bytes of data used to build the verification + * message. + * \param[out] is_verified Boolean whether or not the message, signature, + * validation public key verified. + * + * \return ATCA_SUCCESS on verification success or failure, because the + * command still completed successfully. + */ +ATCA_STATUS calib_verify_validate(ATCADevice device, uint16_t key_id, const uint8_t *signature, const uint8_t *other_data, bool *is_verified) +{ + ATCA_STATUS status = ATCA_SUCCESS; + + if ((device == NULL) || (signature == NULL) || (other_data == NULL) || (is_verified == NULL)) + { + return ATCA_TRACE(ATCA_BAD_PARAM, "NULL pointer received"); + } + + status = calib_verify(device, VERIFY_MODE_VALIDATE, key_id, signature, NULL, other_data, NULL); + + *is_verified = (status == ATCA_SUCCESS); + if (status == ATCA_CHECKMAC_VERIFY_FAILED) + { + status = ATCA_SUCCESS; // Verify failed, but command succeeded + } + + return status; +} + +/** \brief Executes the Verify command in Invalidate mode which invalidates a + * previously validated public key stored in a slot. + * + * This command can only be run after GenKey has been used to create a PubKey + * digest of the public key to be invalidated in TempKey (mode=0x10). + * + * \param[in] device Device context pointer + * \param[in] key_id Slot containing the public key to be invalidated. + * \param[in] signature Signature to be verified. R and S integers in + * big-endian format. 64 bytes for P256 curve. + * \param[in] other_data 19 bytes of data used to build the verification + * message. + * \param[out] is_verified Boolean whether or not the message, signature, + * validation public key verified. + * + * \return ATCA_SUCCESS on verification success or failure, because the + * command still completed successfully. + */ +ATCA_STATUS calib_verify_invalidate(ATCADevice device, uint16_t key_id, const uint8_t *signature, const uint8_t *other_data, bool *is_verified) +{ + ATCA_STATUS status = ATCA_SUCCESS; + + if ((device == NULL) || (signature == NULL) || (other_data == NULL) || (is_verified == NULL)) + { + return ATCA_TRACE(ATCA_BAD_PARAM, "NULL pointer received"); + } + + status = calib_verify(device, VERIFY_MODE_INVALIDATE, key_id, signature, NULL, other_data, NULL); + + *is_verified = (status == ATCA_SUCCESS); + if (status == ATCA_CHECKMAC_VERIFY_FAILED) + { + status = ATCA_SUCCESS; // Verify failed, but command succeeded + } + + return status; +} \ No newline at end of file diff --git a/drivers/ecc108a/cryptoauthlib/calib/calib_write.c b/drivers/ecc108a/cryptoauthlib/calib/calib_write.c new file mode 100644 index 0000000..acd9aa0 --- /dev/null +++ b/drivers/ecc108a/cryptoauthlib/calib/calib_write.c @@ -0,0 +1,887 @@ +/** + * \file + * \brief CryptoAuthLib Basic API methods for Write command. + * + * The Write command writes either one 4-byte word or a 32-byte block to one of + * the EEPROM zones on the device. Depending upon the value of the WriteConfig + * byte for a slot, the data may be required to be encrypted by the system prior + * to being sent to the device + * + * \note List of devices that support this command - ATSHA204A, ATECC108A, + * ATECC508A, and ATECC608A/B. There are differences in the modes that they + * support. Refer to device datasheets for full details. + * + * \copyright (c) 2015-2020 Microchip Technology Inc. and its subsidiaries. + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip software + * and any derivatives exclusively with Microchip products. It is your + * responsibility to comply with third party license terms applicable to your + * use of third party software (including open source software) that may + * accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER + * EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, + * SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE + * OF ANY KIND WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF + * MICROCHIP HAS BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE + * FORESEEABLE. TO THE FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL + * LIABILITY ON ALL CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED + * THE AMOUNT OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR + * THIS SOFTWARE. + */ + +#include "cryptoauthlib.h" +#include "host/atca_host.h" + +/** + * \brief Executes the Write command, which writes either one four byte word or + * a 32-byte block to one of the EEPROM zones on the device. Depending + * upon the value of the WriteConfig byte for this slot, the data may be + * required to be encrypted by the system prior to being sent to the + * device. This command cannot be used to write slots configured as ECC + * private keys. + * + * \param[in] device Device context pointer + * \param[in] zone Zone/Param1 for the write command. + * \param[in] address Address/Param2 for the write command. + * \param[in] value Plain-text data to be written or cipher-text for + * encrypted writes. 32 or 4 bytes depending on bit 7 in the + * zone. + * \param[in] mac MAC required for encrypted writes (32 bytes). Set to NULL + * if not required. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_write(ATCADevice device, uint8_t zone, uint16_t address, const uint8_t *value, const uint8_t *mac) +{ + ATCAPacket packet; + ATCA_STATUS status = ATCA_GEN_FAIL; + + if ((device == NULL) || (value == NULL)) + { + return ATCA_TRACE(ATCA_BAD_PARAM, "NULL pointer received"); + } + + do + { + // Build the write command + packet.param1 = zone; + packet.param2 = address; + if (zone & ATCA_ZONE_READWRITE_32) + { + // 32-byte write + memcpy(packet.data, value, 32); + // Only 32-byte writes can have a MAC + if (mac) + { + memcpy(&packet.data[32], mac, 32); + } + } + else + { + // 4-byte write + memcpy(packet.data, value, 4); + } + + if ((status = atWrite(atcab_get_device_type_ext(device), &packet, mac && (zone & ATCA_ZONE_READWRITE_32))) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "atWrite - failed"); + break; + } + + if ((status = atca_execute_command(&packet, device)) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "calib_write - execution failed"); + break; + } + + } + while (0); + + return status; +} + +/** \brief Executes the Write command, which writes either 4 or 32 bytes of + * data into a device zone. + * + * \param[in] device Device context pointer + * \param[in] zone Device zone to write to (0=config, 1=OTP, 2=data). + * \param[in] slot If writing to the data zone, it is the slot to write to, + * otherwise it should be 0. + * \param[in] block 32-byte block to write to. + * \param[in] offset 4-byte word within the specified block to write to. If + * performing a 32-byte write, this should be 0. + * \param[in] data Data to be written. + * \param[in] len Number of bytes to be written. Must be either 4 or 32. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_write_zone(ATCADevice device, uint8_t zone, uint16_t slot, uint8_t block, uint8_t offset, const uint8_t *data, uint8_t len) +{ + ATCA_STATUS status = ATCA_GEN_FAIL; + uint16_t addr; + + // Check the input parameters + if (data == NULL) + { + return ATCA_TRACE(ATCA_BAD_PARAM, "NULL pointer received"); + } + + if (len != 4 && len != 32) + { + return ATCA_TRACE(ATCA_BAD_PARAM, "Invalid length received"); + } + + do + { + // The get address function checks the remaining variables + if ((status = calib_get_addr(zone, slot, block, offset, &addr)) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "calib_get_addr - failed"); + break; + } + + // If there are 32 bytes to write, then xor the bit into the mode + if (len == ATCA_BLOCK_SIZE) + { + zone = zone | ATCA_ZONE_READWRITE_32; + } + + status = calib_write(device, zone, addr, data, NULL); + + } + while (0); + + return status; +} + + +/** \brief Executes the Write command, which performs an encrypted write of + * a 32 byte block into given slot. + * + * The function takes clear text bytes and encrypts them for writing over the + * wire. Data zone must be locked and the slot configuration must be set to + * encrypted write for the block to be successfully written. + * + * \param[in] device Device context pointer + * \param[in] key_id Slot ID to write to. + * \param[in] block Index of the 32 byte block to write in the slot. + * \param[in] data 32 bytes of clear text data to be written to the slot + * \param[in] enc_key WriteKey to encrypt with for writing + * \param[in] enc_key_id The KeyID of the WriteKey + * \param[in] num_in 20 byte host nonce to inject into Nonce calculation + * + * returns ATCA_SUCCESS on success, otherwise an error code. + */ + +#if defined(ATCA_USE_CONSTANT_HOST_NONCE) +ATCA_STATUS calib_write_enc(ATCADevice device, uint16_t key_id, uint8_t block, const uint8_t *data, const uint8_t* enc_key, const uint16_t enc_key_id) +{ + uint8_t num_in[NONCE_NUMIN_SIZE] = { 0 }; + +#else +ATCA_STATUS calib_write_enc(ATCADevice device, uint16_t key_id, uint8_t block, const uint8_t *data, const uint8_t* enc_key, const uint16_t enc_key_id, const uint8_t num_in[NONCE_NUMIN_SIZE]) +{ +#endif + return ATCA_EXECUTION_ERROR; + /* + ATCA_STATUS status = ATCA_GEN_FAIL; + uint8_t zone = ATCA_ZONE_DATA | ATCA_ZONE_READWRITE_32; + atca_nonce_in_out_t nonce_params; + atca_gen_dig_in_out_t gen_dig_param; + atca_write_mac_in_out_t write_mac_param; + atca_temp_key_t temp_key; + uint8_t serial_num[32]; + uint8_t rand_out[RANDOM_NUM_SIZE] = { 0 }; + uint8_t cipher_text[ATCA_KEY_SIZE] = { 0 }; + uint8_t mac[WRITE_MAC_SIZE] = { 0 }; + uint8_t other_data[4] = { 0 }; + uint16_t addr; + + do + { + // Verify inputs parameters + if (data == NULL || enc_key == NULL) + { + status = ATCA_TRACE(ATCA_BAD_PARAM, "NULL pointer received"); + break; + } + + // Read the device SN + if ((status = calib_read_zone(device, ATCA_ZONE_CONFIG, 0, 0, 0, serial_num, 32)) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "calib_read_zone - failed"); + break; + } + // Make the SN continuous by moving SN[4:8] right after SN[0:3] + memmove(&serial_num[4], &serial_num[8], 5); + + + // Random Nonce inputs + memset(&temp_key, 0, sizeof(temp_key)); + memset(&nonce_params, 0, sizeof(nonce_params)); + nonce_params.mode = NONCE_MODE_SEED_UPDATE; + nonce_params.zero = 0; + nonce_params.num_in = &num_in[0]; + nonce_params.rand_out = rand_out; + nonce_params.temp_key = &temp_key; + + // Send the random Nonce command + if ((status = calib_nonce_rand(device, num_in, rand_out)) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "Nonce failed"); + break; + } + + // Calculate Tempkey + if ((status = atcah_nonce(&nonce_params)) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "Calc TempKey failed"); + break; + } + + // Supply OtherData so GenDig behavior is the same for keys with SlotConfig.NoMac set + other_data[0] = ATCA_GENDIG; + other_data[1] = GENDIG_ZONE_DATA; + other_data[2] = (uint8_t)(enc_key_id); + other_data[3] = (uint8_t)(enc_key_id >> 8); + + // Send the GenDig command + if ((status = calib_gendig(device, GENDIG_ZONE_DATA, enc_key_id, other_data, sizeof(other_data))) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "GenDig failed"); + break; + } + + // Calculate Tempkey + // NoMac bit isn't being considered here on purpose to remove having to read SlotConfig. + // OtherData is built to get the same result regardless of the NoMac bit. + memset(&gen_dig_param, 0, sizeof(gen_dig_param)); + gen_dig_param.key_id = enc_key_id; + gen_dig_param.is_key_nomac = false; + gen_dig_param.sn = serial_num; + gen_dig_param.stored_value = enc_key; + gen_dig_param.zone = GENDIG_ZONE_DATA; + gen_dig_param.other_data = other_data; + gen_dig_param.temp_key = &temp_key; + if ((status = atcah_gen_dig(&gen_dig_param)) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "atcah_gen_dig() failed"); + break; + } + + // The get address function checks the remaining variables + if ((status = calib_get_addr(ATCA_ZONE_DATA, key_id, block, 0, &addr)) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "Get address failed"); + break; + } + + // Setting bit 6 to indicate input data is encrypted + write_mac_param.zone = zone | ATCA_ZONE_ENCRYPTED; + write_mac_param.key_id = addr; + write_mac_param.sn = serial_num; + write_mac_param.input_data = data; + write_mac_param.encrypted_data = cipher_text; + write_mac_param.auth_mac = mac; + write_mac_param.temp_key = &temp_key; + + if ((status = atcah_write_auth_mac(&write_mac_param)) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "Calculate Auth MAC failed"); + break; + } + + status = calib_write(device, write_mac_param.zone, write_mac_param.key_id, write_mac_param.encrypted_data, write_mac_param.auth_mac); + + } + while (0); + + return status;*/ +} + +/** \brief Executes the Write command, which writes the configuration zone. + * + * First 16 bytes are skipped as they are not writable. LockValue and + * LockConfig are also skipped and can only be changed via the Lock + * command. + * + * This command may fail if UserExtra and/or Selector bytes have + * already been set to non-zero values. + * + * \param[in] device Device context pointer + * \param[in] config_data Data to the config zone data. This should be 88 + * bytes for SHA devices and 128 bytes for ECC + * devices. + * + * \returns ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_write_config_zone(ATCADevice device, const uint8_t* config_data) +{ + ATCA_STATUS status = ATCA_GEN_FAIL; + size_t config_size = 0; + + if (config_data == NULL) + { + return ATCA_TRACE(ATCA_BAD_PARAM, "NULL pointer received"); + } + + do + { + // Get config zone size for the device + if (ATCA_SUCCESS != (status = calib_get_zone_size(device, ATCA_ZONE_CONFIG, 0, &config_size))) + { + ATCA_TRACE(status, "calib_get_zone_size - failed"); + break; + } + + // Write config zone excluding UserExtra and Selector + if (ATCA_SUCCESS != (status = calib_write_bytes_zone(device, ATCA_ZONE_CONFIG, 0, 16, &config_data[16], config_size - 16))) + { + ATCA_TRACE(status, "calib_write_bytes_zone - failed"); + break; + } + + // Write the UserExtra and Selector. This may fail if either value is already non-zero. + if (ATCA_SUCCESS != (status = calib_updateextra(device, UPDATE_MODE_USER_EXTRA, config_data[84]))) + { + ATCA_TRACE(status, "calib_updateextra - failed"); + break; + } + + if (ATCA_SUCCESS != (status = calib_updateextra(device, UPDATE_MODE_SELECTOR, config_data[85]))) + { + ATCA_TRACE(status, "calib_updateextra - failed"); + break; + } + } + while (0); + + return status; +} + +/** \brief Uses the write command to write a public key to a slot in the + * proper format. + * + * \param[in] device Device context pointer + * \param[in] slot Slot number to write. Only slots 8 to 15 are large + * enough to store a public key. + * \param[in] public_key Public key to write into the slot specified. X and Y + * integers in big-endian format. 64 bytes for P256 + * curve. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_write_pubkey(ATCADevice device, uint16_t slot, const uint8_t *public_key) +{ + ATCA_STATUS status = ATCA_SUCCESS; + uint8_t public_key_formatted[ATCA_BLOCK_SIZE * 3]; + uint8_t block; + + // Check the pointers + if (public_key == NULL) + { + return ATCA_TRACE(ATCA_BAD_PARAM, "NULL pointer received"); + } + + // The 64 byte P256 public key gets written to a 72 byte slot in the following pattern + // | Block 1 | Block 2 | Block 3 | + // | Pad: 4 Bytes | PubKey[0:27] | PubKey[28:31] | Pad: 4 Bytes | PubKey[32:55] | PubKey[56:63] | + + memset(public_key_formatted, 0, sizeof(public_key_formatted)); + memcpy(&public_key_formatted[4], &public_key[0], 32); // Move X to padded position + memcpy(&public_key_formatted[40], &public_key[32], 32); // Move Y to padded position + + // Using this instead of calib_write_zone_bytes, as that function doesn't work when + // the data zone is unlocked + for (block = 0; block < 3; block++) + { + if (ATCA_SUCCESS != (status = calib_write_zone(device, ATCA_ZONE_DATA, slot, block, 0, &public_key_formatted[ATCA_BLOCK_SIZE * block], ATCA_BLOCK_SIZE))) + { + ATCA_TRACE(status, "calib_write_zone - failed"); + break; + } + } + + return status; +} + +/** \brief Executes the Write command, which writes data into the + * configuration, otp, or data zones with a given byte offset and + * length. Offset and length must be multiples of a word (4 bytes). + * + * Config zone must be unlocked for writes to that zone. If data zone is + * unlocked, only 32-byte writes are allowed to slots and OTP and the offset + * and length must be multiples of 32 or the write will fail. + * + * \param[in] device Device context pointer + * \param[in] zone Zone to write data to: ATCA_ZONE_CONFIG(0), + * ATCA_ZONE_OTP(1), or ATCA_ZONE_DATA(2). + * \param[in] slot If zone is ATCA_ZONE_DATA(2), the slot number to + * write to. Ignored for all other zones. + * \param[in] offset_bytes Byte offset within the zone to write to. Must be + * a multiple of a word (4 bytes). + * \param[in] data Data to be written. + * \param[in] length Number of bytes to be written. Must be a multiple + * of a word (4 bytes). + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_write_bytes_zone(ATCADevice device, uint8_t zone, uint16_t slot, size_t offset_bytes, const uint8_t *data, size_t length) +{ + ATCA_STATUS status = ATCA_GEN_FAIL; + size_t zone_size = 0; + size_t data_idx = 0; + size_t cur_block = 0; + size_t cur_word = 0; + + if (zone != ATCA_ZONE_CONFIG && zone != ATCA_ZONE_OTP && zone != ATCA_ZONE_DATA) + { + return ATCA_TRACE(ATCA_BAD_PARAM, "Invalid zone received"); + } + if (zone == ATCA_ZONE_DATA && slot > 15) + { + return ATCA_TRACE(ATCA_BAD_PARAM, "Invalid slot received"); + } + if (length == 0) + { + return ATCA_SUCCESS; // Always succeed writing 0 bytes + } + if (data == NULL) + { + return ATCA_TRACE(ATCA_BAD_PARAM, "NULL pointer received"); + } + if (offset_bytes % ATCA_WORD_SIZE != 0 || length % ATCA_WORD_SIZE != 0) + { + return ATCA_TRACE(ATCA_BAD_PARAM, "Either Invalid length or offset received"); + } + + do + { + if (ATCA_SUCCESS != (status = calib_get_zone_size(device, zone, slot, &zone_size))) + { + ATCA_TRACE(status, "calib_get_zone_size - failed"); + break; + } + if (offset_bytes + length > zone_size) + { + return ATCA_TRACE(ATCA_BAD_PARAM, "Invalid parameter received"); + } + + cur_block = offset_bytes / ATCA_BLOCK_SIZE; + cur_word = (offset_bytes % ATCA_BLOCK_SIZE) / ATCA_WORD_SIZE; + + while (data_idx < length) + { + // The last item makes sure we handle the selector, user extra, and lock bytes in the config properly + if (cur_word == 0 && length - data_idx >= ATCA_BLOCK_SIZE && !(zone == ATCA_ZONE_CONFIG && cur_block == 2)) + { + if (ATCA_SUCCESS != (status = calib_write_zone(device, zone, slot, (uint8_t)cur_block, 0, &data[data_idx], ATCA_BLOCK_SIZE))) + { + ATCA_TRACE(status, "calib_write_zone - failed"); + break; + } + data_idx += ATCA_BLOCK_SIZE; + cur_block += 1; + } + else + { + // Skip trying to change UserExtra, Selector, LockValue, and LockConfig which require the UpdateExtra command to change + if (!(zone == ATCA_ZONE_CONFIG && cur_block == 2 && cur_word == 5)) + { + if (ATCA_SUCCESS != (status = calib_write_zone(device, zone, slot, (uint8_t)cur_block, (uint8_t)cur_word, &data[data_idx], ATCA_WORD_SIZE))) + { + ATCA_TRACE(status, "calib_write_zone - failed"); + break; + } + } + data_idx += ATCA_WORD_SIZE; + cur_word += 1; + if (cur_word == ATCA_BLOCK_SIZE / ATCA_WORD_SIZE) + { + cur_block += 1; + cur_word = 0; + } + } + } + } + while (false); + + return status; +} + +/** \brief Initialize one of the monotonic counters in device with a specific + * value. + * + * The monotonic counters are stored in the configuration zone using a special + * format. This encodes a binary count value into the 8 byte encoded value + * required. Can only be set while the configuration zone is unlocked. + * + * \param[in] device Device context pointer + * \param[in] counter_id Counter to be written. + * \param[in] counter_value Counter value to set. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_write_config_counter(ATCADevice device, uint16_t counter_id, uint32_t counter_value) +{ + uint16_t lin_a, lin_b, bin_a, bin_b; + uint8_t bytes[8]; + uint8_t idx = 0; + ATCA_STATUS status = ATCA_GEN_FAIL; + + if (counter_id > 1 || counter_value > COUNTER_MAX_VALUE) + { + return ATCA_TRACE(ATCA_BAD_PARAM, "Either invalid counter id or counter value received"); + } + + lin_a = 0xFFFF >> (counter_value % 32); + lin_b = 0xFFFF >> ((counter_value >= 16) ? (counter_value - 16) % 32 : 0); + bin_a = (uint16_t)(counter_value / 32); + bin_b = (counter_value >= 16) ? ((uint16_t)((counter_value - 16) / 32)) : 0; + + bytes[idx++] = lin_a >> 8; + bytes[idx++] = lin_a & 0xFF; + bytes[idx++] = lin_b >> 8; + bytes[idx++] = lin_b & 0xFF; + + bytes[idx++] = bin_a >> 8; + bytes[idx++] = bin_a & 0xFF; + bytes[idx++] = bin_b >> 8; + bytes[idx++] = bin_b & 0xFF; + + status = calib_write_bytes_zone(device, ATCA_ZONE_CONFIG, 0, 52 + counter_id * 8, bytes, sizeof(bytes)); + + return status; +} + +#if defined(ATCA_ECC204_SUPPORT) +/** \brief Execute write command to write either 16 byte or 32 byte to one of the EEPROM zones + * on the ECC204 device. + * + * \param[in] device Device context pointer + * \param[in] zone Zone/Param1 for the write command. + * \param[in] address Address/Param2 for the write command. + * \param[in] value Plain-text data to be written or cipher-text for + * encrypted writes. 32 or 16 bytes depending on zone. + * \param[in] mac MAC required for encrypted writes (32 bytes). Set to NULL + * if not required. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS calib_ecc204_write(ATCADevice device, uint8_t zone, uint16_t address, const uint8_t *value, + const uint8_t *mac) +{ + ATCA_STATUS status = ATCA_SUCCESS; + ATCAPacket packet; + + if ((NULL == device) && (NULL == value)) + { + status = ATCA_TRACE(ATCA_BAD_PARAM, "NULL pointer encountered"); + } + + if (ATCA_SUCCESS == status) + { + packet.param1 = zone; + packet.param2 = address; + + if (ATCA_ECC204_ZONE_CONFIG == zone) + { + memcpy(packet.data, value, 16); + } + else if (ATCA_ECC204_ZONE_DATA == zone) + { + memcpy(packet.data, value, ATCA_BLOCK_SIZE); + } + else + { + status = ATCA_TRACE(ATCA_BAD_PARAM, "Invalid zone received"); + } + + if (ATCA_SUCCESS == status) + { + if (mac && (ATCA_ECC204_ZONE_DATA == zone)) + { + memcpy(&packet.data[ATCA_BLOCK_SIZE], mac, MAC_SIZE); + } + + (void)atWrite(atcab_get_device_type_ext(device), &packet, mac && (ATCA_ECC204_ZONE_DATA == zone)); + } + } + + if (ATCA_SUCCESS == status) + { + if (ATCA_SUCCESS != (status = atca_execute_command(&packet, device))) + { + ATCA_TRACE(status, "calib_ecc204_write - execution failed"); + } + } + + return status; + +} + +/** \brief Execute write command to write data into configuration zone or data zone + * This function only support ECC204 device + * + * \param[in] device Device context pointer + * \param[in] zone Device zone to write (config=1, data=0) + * \param[in] slot the slot number to be witten + * \param[in] block 32-byte block to write + * \param[in] offset ignore for ECC204 device + * \param[in] data Data to be written into slot + * \param[in] len Number of bytes to be written. Must be either 16 or 32. + * + * \return ATCA_SUCCESS on success, otherwise an error code + */ +ATCA_STATUS calib_ecc204_write_zone(ATCADevice device, uint8_t zone, uint16_t slot, uint8_t block, + uint8_t offset, const uint8_t *data, uint8_t len) +{ + ATCA_STATUS status = ATCA_SUCCESS; + uint16_t addr; + uint8_t write_zone = (zone == ATCA_ZONE_CONFIG) ? ATCA_ECC204_ZONE_CONFIG : ATCA_ECC204_ZONE_DATA; + + ((void)offset); + + if ((NULL == device) && (NULL == data)) + { + status = ATCA_TRACE(ATCA_BAD_PARAM, "NULL pointer encountered"); + } + else if (((ATCA_ECC204_ZONE_CONFIG == write_zone) && (16 != len)) || + ((ATCA_ECC204_ZONE_DATA == write_zone) && (ATCA_BLOCK_SIZE != len))) + { + status = ATCA_TRACE(ATCA_BAD_PARAM, "Invalid length received"); + } + + if (ATCA_SUCCESS == status) + { + if (ATCA_SUCCESS != (status = calib_ecc204_get_addr(write_zone, slot, block, 0, &addr))) + { + ATCA_TRACE(status, "calib_ecc204_get_addr - failed"); + } + + if (ATCA_SUCCESS == status) + { + status = calib_ecc204_write(device, write_zone, addr, data, NULL); + } + } + + return status; +} + +/** \brief Use write command to write configuration data into ECC204 config zone + * + * \param[in] device Device context pointer + * \param[in] config_data configuration data + * + * \return ATCA_SUCCESS on success, otherwise an error code + */ +ATCA_STATUS calib_ecc204_write_config_zone(ATCADevice device, const uint8_t* config_data) +{ + ATCA_STATUS status = ATCA_SUCCESS; + uint8_t slot = 1; + + if ((NULL == device) || (NULL == config_data)) + { + status = ATCA_TRACE(ATCA_BAD_PARAM, "NULL pointer encountered"); + } + + if (ATCA_SUCCESS == status) + { + while (slot <= 3) + { + if (ATCA_SUCCESS != (status = calib_ecc204_write_zone(device, ATCA_ECC204_ZONE_CONFIG, slot, + 0, 0, &config_data[16 * slot], 16))) + { + ATCA_TRACE(status, "calib_ecc204_write_zone - failed"); + } + slot += 1; // Increment slot + } + } + + return status; +} + +/** \brief Executes write command, performs an encrypted write of a 32 byte block into given slot. + * + * \param[in] device Device context pointer + * \param[in] slot key slot to be written + * \param[in] data 32 bytes of clear text data + * \param[in] transport_key Transport key + * \param[in] key_id Transport key id + * \param[in] num_in 20 byte host nonce to inject into Nonce calculation + * + * \return ATCA_SUCCESS on success, otherwise an error code + */ +#if defined(ATCA_USE_CONSTANT_HOST_NONCE) +ATCA_STATUS calib_ecc204_write_enc(ATCADevice device, uint16_t slot, uint8_t* data, uint8_t* transport_key, + uint8_t transport_key_id) +{ + uint8_t num_in[NONCE_NUMIN_SIZE] = { 0 }; + +#else +ATCA_STATUS calib_ecc204_write_enc(ATCADevice device, uint16_t slot, uint8_t* data, uint8_t* transport_key, + uint8_t transport_key_id, uint8_t num_in[NONCE_NUMIN_SIZE]) +{ +#endif + ATCA_STATUS status = ATCA_SUCCESS; + atca_nonce_in_out_t nonce_params; + atca_write_mac_in_out_t write_mac_param; + atca_temp_key_t temp_key; + atca_session_key_in_out_t session_key_params; + uint8_t rand_out[RANDOM_NUM_SIZE] = { 0 }; + uint8_t serial_number[ATCA_SERIAL_NUM_SIZE] = { 0 }; + uint8_t session_key[ATCA_KEY_SIZE] = { 0 }; + uint8_t cipher_text[ATCA_KEY_SIZE] = { 0 }; + uint8_t mac[WRITE_MAC_SIZE] = { 0 }; + uint16_t addr; + + if ((NULL == data) || (NULL == transport_key)) + { + return ATCA_TRACE(ATCA_BAD_PARAM, "NULL pointer encounteed"); + } + + do + { + // Read device serial number + if (ATCA_SUCCESS != (status = calib_ecc204_read_serial_number(device, serial_number))) + { + ATCA_TRACE(status, "Read serial number failed"); + break; + } + + // Generate session key on device + if (ATCA_SUCCESS != (status = calib_nonce_gen_session_key(device, transport_key_id, num_in, rand_out))) + { + ATCA_TRACE(status, "Session key generation failed"); + break; + } + + // Random Nonce inputs + memset(&temp_key, 0, sizeof(temp_key)); + memset(&nonce_params, 0, sizeof(nonce_params)); + nonce_params.mode = NONCE_MODE_SEED_UPDATE; + nonce_params.zero = transport_key_id; + nonce_params.num_in = &num_in[0]; + nonce_params.rand_out = rand_out; + nonce_params.temp_key = &temp_key; + + // Calculate Nonce + if ((status = atcah_nonce(&nonce_params)) != ATCA_SUCCESS) + { + ATCA_TRACE(status, "Calculate nonce failed"); + break; + } + + // Session key inputs + memset(&session_key_params, 0, sizeof(session_key_params)); + session_key_params.transport_key = transport_key; + session_key_params.transport_key_id = transport_key_id; + session_key_params.sn = serial_number; + session_key_params.nonce = temp_key.value; + session_key_params.session_key = session_key; + + // calculate session key on host + if (ATCA_SUCCESS != (status = atcah_gen_session_key(&session_key_params))) + { + ATCA_TRACE(status, "Host session key generation failed"); + break; + } + + if (ATCA_SUCCESS != (status = calib_ecc204_get_addr(ATCA_ECC204_ZONE_DATA, slot, 0, 0, &addr))) + { + ATCA_TRACE(status, "Calculate slot address failed"); + break; + } + + // copy session key into temp variable + memcpy(temp_key.value, session_key, ATCA_KEY_SIZE); + + // Write mac inputs + write_mac_param.zone = ATCA_ECC204_ZONE_DATA; + write_mac_param.key_id = addr; + write_mac_param.sn = serial_number; + write_mac_param.input_data = data; + write_mac_param.encrypted_data = cipher_text; + write_mac_param.auth_mac = mac; + write_mac_param.temp_key = &temp_key; + + // calculate MAC on host + if (ATCA_SUCCESS != (status = atcah_ecc204_write_auth_mac(&write_mac_param))) + { + ATCA_TRACE(status, "Data encryption failed"); + break; + } + + status = calib_ecc204_write(device, write_mac_param.zone, write_mac_param.key_id, write_mac_param.encrypted_data, write_mac_param.auth_mac); + } + while (0); + + return status; +} + +/** \brief Use Write command to write bytes + * + * This function will issue the write command as many times as is required to + * read the requested data. + * + * \param[in] device Device context pointer + * \param[in] zone It accepts only ATCA_ZONE_DATA for ECC204 device + * \param[in] slot slot number to write to. + * \param[in] block offset bytes ignored + * \param[in] data data to be written + * \param[in] length number of bytes to e written + * + * \return ATCA_SUCCESS on success, otheriwse an error code + */ +ATCA_STATUS calib_ecc204_write_bytes_zone(ATCADevice device, uint8_t zone, uint16_t slot, size_t block, + const uint8_t *data, size_t length) +{ + ATCA_STATUS status = ATCA_SUCCESS; + uint8_t block_size = ATCA_BLOCK_SIZE; + uint8_t no_of_blocks; + uint8_t data_idx = 0; + + if ((NULL == device) || (NULL == data)) + { + return ATCA_TRACE(ATCA_BAD_PARAM, "Encountered NULL pointer"); + } + else if (ATCA_ZONE_DATA == zone) + { + if (((length > 64) && (2 == slot)) || ((length > 320) && (1 == slot)) || (0 == slot)) + { + return ATCA_TRACE(ATCA_BAD_PARAM, "Invalid parameter received"); + } + } + else if (ATCA_ZONE_CONFIG == zone) + { + return ATCA_TRACE(ATCA_BAD_PARAM, "Invalid parameter received"); + } + else if (0 == length) + { + return ATCA_SUCCESS; + } + + no_of_blocks = (uint8_t)(length / block_size); + while (no_of_blocks--) + { + if (ATCA_SUCCESS != (status = calib_ecc204_write_zone(device, zone, slot, (uint8_t)block, 0, + &data[block_size * data_idx], + block_size))) + { + ATCA_TRACE(status, "calib_ecc204_write_zone failed"); + break; + } + + block += 1; // Read next block + data_idx += 1; // increment data index + } + + return status; +} + +#endif diff --git a/drivers/ecc108a/cryptoauthlib/cryptoauthlib.h b/drivers/ecc108a/cryptoauthlib/cryptoauthlib.h new file mode 100644 index 0000000..0839afd --- /dev/null +++ b/drivers/ecc108a/cryptoauthlib/cryptoauthlib.h @@ -0,0 +1,130 @@ +/** + * \file + * \brief Single aggregation point for all CryptoAuthLib header files + * + * \copyright (c) 2015-2020 Microchip Technology Inc. and its subsidiaries. + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip software + * and any derivatives exclusively with Microchip products. It is your + * responsibility to comply with third party license terms applicable to your + * use of third party software (including open source software) that may + * accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER + * EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, + * SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE + * OF ANY KIND WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF + * MICROCHIP HAS BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE + * FORESEEABLE. TO THE FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL + * LIABILITY ON ALL CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED + * THE AMOUNT OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR + * THIS SOFTWARE. + */ + +#ifndef _ATCA_LIB_H +#define _ATCA_LIB_H + +#include +#include +#include +#include +#include +#include + +/** Library Configuration File - All build attributes should be included in + atca_config.h */ +#include "atca_config.h" +#include "atca_compiler.h" +#include "atca_version.h" + +/* Configuration Macros to detect device classes */ +#if defined(ATCA_ATSHA204A_SUPPORT) || defined(ATCA_ATSHA206A_SUPPORT) +#define ATCA_SHA_SUPPORT 1 +#endif + +/* Make sure all configuration options work */ +#if defined(ATCA_ATECC608A_SUPPORT) && !defined(ATCA_ATECC608_SUPPORT) +#define ATCA_ATECC608_SUPPORT +#endif + +#if defined(ATCA_ATECC108A_SUPPORT) || defined(ATCA_ATECC508A_SUPPORT) \ + || defined(ATCA_ATECC608_SUPPORT) +#define ATCA_ECC_SUPPORT 1 +#endif + +/* Classic Cryptoauth Devices */ +#if defined(ATCA_SHA_SUPPORT) || defined(ATCA_ECC_SUPPORT) || defined(ATCA_ECC204_SUPPORT) +#define ATCA_CA_SUPPORT 1 +#else +#define ATCA_CA_SUPPORT 0 +#endif + +/* New Trust Anchor Devices */ +#if defined(ATCA_TA100_SUPPORT) +#define ATCA_TA_SUPPORT 1 +#else +#define ATCA_TA_SUPPORT 0 +#endif + +#include "atca_status.h" +#include "atca_debug.h" +#include "atca_iface.h" +#include "atca_helpers.h" +#include "hal/atca_hal.h" + +/* Common Cryptographic Definitions */ +#define ATCA_SHA256_BLOCK_SIZE (64) +#define ATCA_SHA256_DIGEST_SIZE (32) + +#define ATCA_AES128_BLOCK_SIZE (16) +#define ATCA_AES128_KEY_SIZE (16) + +#define ATCA_ECCP256_KEY_SIZE (32) +#define ATCA_ECCP256_PUBKEY_SIZE (64) +#define ATCA_ECCP256_SIG_SIZE (64) + +#define ATCA_ZONE_CONFIG ((uint8_t)0x00) +#define ATCA_ZONE_OTP ((uint8_t)0x01) +#define ATCA_ZONE_DATA ((uint8_t)0x02) + +#if defined(ATCA_ECC204_SUPPORT) +#define ATCA_ECC204_ZONE_DATA ((uint8_t)0x00) +#define ATCA_ECC204_ZONE_CONFIG ((uint8_t)0x01) +#endif + +/** Place resulting digest both in Output buffer and TempKey */ +#define SHA_MODE_TARGET_TEMPKEY ((uint8_t)0x00) +/** Place resulting digest both in Output buffer and Message Digest Buffer */ +#define SHA_MODE_TARGET_MSGDIGBUF ((uint8_t)0x40) +/** Place resulting digest both in Output buffer ONLY */ +#define SHA_MODE_TARGET_OUT_ONLY ((uint8_t)0xC0) + +#if ATCA_CA_SUPPORT || defined(ATCA_USE_ATCAB_FUNCTIONS) +#include "atca_cfgs.h" +#include "atca_device.h" +#include "calib/calib_basic.h" +#include "calib/calib_command.h" +#include "calib/calib_aes_gcm.h" +#endif + +#if ATCA_TA_SUPPORT +#include "talib/talib_status.h" +#include "talib/talib_basic.h" +#endif + +#include "atca_basic.h" + +#define ATCA_STRINGIFY(x) #x +#define ATCA_TOSTRING(x) ATCA_STRINGIFY(x) + +#ifdef ATCA_PRINTF + #define ATCA_TRACE(s, m) atca_trace_msg(s, __FILE__ ":" ATCA_TOSTRING(__LINE__) ":%x:" m "\n") +#else + #define ATCA_TRACE(s, m) atca_trace(s) +#endif + +#endif diff --git a/drivers/ecc108a/cryptoauthlib/hal/atca_hal.c b/drivers/ecc108a/cryptoauthlib/hal/atca_hal.c new file mode 100644 index 0000000..d7fa1ef --- /dev/null +++ b/drivers/ecc108a/cryptoauthlib/hal/atca_hal.c @@ -0,0 +1,548 @@ +/** + * \file + * \brief low-level HAL - methods used to setup indirection to physical layer interface. + * this level does the dirty work of abstracting the higher level ATCAIFace methods from the + * low-level physical interfaces. Its main goal is to keep low-level details from bleeding into + * the logical interface implemetation. + * + * \copyright (c) 2015-2020 Microchip Technology Inc. and its subsidiaries. + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip software + * and any derivatives exclusively with Microchip products. It is your + * responsibility to comply with third party license terms applicable to your + * use of third party software (including open source software) that may + * accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER + * EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, + * SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE + * OF ANY KIND WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF + * MICROCHIP HAS BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE + * FORESEEABLE. TO THE FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL + * LIABILITY ON ALL CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED + * THE AMOUNT OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR + * THIS SOFTWARE. + */ + + +/* when incorporating ATCA HAL into your application, you need to adjust the #defines in atca_hal.h to include + * and exclude appropriate interfaces - this optimizes memory use when not using a specific iface implementation in your application */ + +#include "cryptoauthlib.h" +#include "atca_hal.h" + +#ifndef ATCA_MAX_HAL_CACHE +#define ATCA_MAX_HAL_CACHE +#endif + +#ifdef ATCA_HAL_I2C +static ATCAHAL_t hal_i2c = { + hal_i2c_init, + hal_i2c_post_init, + hal_i2c_send, + hal_i2c_receive, + hal_i2c_control, + hal_i2c_release +}; +#endif + +#ifdef ATCA_HAL_SWI_UART +static ATCAHAL_t hal_swi_uart = { + hal_swi_init, + hal_swi_post_init, + hal_swi_send, + hal_swi_receive, + hal_swi_control, + hal_swi_release +}; +#endif + +#if defined(ATCA_HAL_SWI_GPIO) || defined(ATCA_HAL_SWI_BB) +static ATCAHAL_t hal_swi_gpio = { + hal_swi_gpio_init, + hal_swi_gpio_post_init, + hal_swi_gpio_send, + hal_swi_gpio_receive, + hal_swi_gpio_control, + hal_swi_gpio_release +}; +#endif + +#if defined(ATCA_HAL_UART) || defined(ATCA_HAL_SWI_UART) || defined(ATCA_HAL_KIT_UART) +static ATCAHAL_t hal_uart = { + hal_uart_init, + hal_uart_post_init, + hal_uart_send, + hal_uart_receive, + hal_uart_control, + hal_uart_release +}; +#endif + +#ifdef ATCA_HAL_SPI +static ATCAHAL_t hal_spi = { + hal_spi_init, + hal_spi_post_init, + hal_spi_send, + hal_spi_receive, + hal_spi_control, + hal_spi_release +}; +#endif + +#if defined(ATCA_HAL_GPIO) || defined(ATCA_HAL_BB) +static ATCAHAL_t hal_gpio = { + hal_gpio_init, + hal_gpio_post_init, + hal_gpio_send, /* Set IO State */ + hal_gpio_receive, /* Read IO State */ + hal_gpio_control, + hal_gpio_release +}; +#endif + +#ifdef ATCA_HAL_KIT_HID +static ATCAHAL_t hal_hid = { + hal_kit_hid_init, + hal_kit_hid_post_init, + hal_kit_hid_send, + hal_kit_hid_receive, + hal_kit_hid_control, + hal_kit_hid_release +}; +#endif + +#if defined(ATCA_HAL_KIT_HID) || defined(ATCA_HAL_KIT_UART) +#include "kit_protocol.h" +static ATCAHAL_t hal_kit_v1 = { + kit_init, + kit_post_init, + kit_send, + kit_receive, + kit_control, + kit_release +}; +#endif + +#ifdef ATCA_HAL_KIT_BRIDGE +static ATCAHAL_t hal_kit_bridge = { + hal_kit_init, + hal_kit_post_init, + hal_kit_send, + hal_kit_receive, + hal_kit_control, + hal_kit_release +}; +#endif + +#ifdef ATCA_HAL_CUSTOM +static ATCAHAL_t hal_custom; +#endif + +/** \brief Structure that holds the hal/phy maping for different interface types + */ +typedef struct +{ + uint8_t iface_type; /**< */ + ATCAHAL_t* hal; /**< */ + ATCAHAL_t* phy; /**< Physical interface for the specific HAL*/ +} atca_hal_list_entry_t; + + +static atca_hal_list_entry_t atca_registered_hal_list[ATCA_MAX_HAL_CACHE] = { +#ifdef ATCA_HAL_I2C + { ATCA_I2C_IFACE, &hal_i2c, NULL }, +#endif +#ifdef ATCA_HAL_SWI_UART + { ATCA_SWI_IFACE, &hal_swi_uart, &hal_uart }, +#endif +#ifdef ATCA_HAL_KIT_UART + { ATCA_UART_IFACE, &hal_kit_v1, &hal_uart }, +#elif defined(ATCA_HAL_UART) + { ATCA_UART_IFACE, &hal_uart, NULL }, +#endif +#ifdef ATCA_HAL_SPI + { ATCA_SPI_IFACE, &hal_spi, NULL }, +#endif +#ifdef ATCA_HAL_KIT_HID + { ATCA_HID_IFACE, &hal_kit_v1, &hal_hid }, +#endif +#ifdef ATCA_HAL_KIT_BRIDGE + { ATCA_KIT_IFACE, &hal_kit_bridge, NULL }, +#endif +#if defined(ATCA_HAL_SWI_GPIO) || defined(ATCA_HAL_SWI_BB) + { ATCA_SWI_GPIO_IFACE, &hal_swi_gpio, &hal_gpio }, +#endif +}; + +static const size_t atca_registered_hal_list_size = sizeof(atca_registered_hal_list) / sizeof(atca_hal_list_entry_t); + + +/** \brief Internal function to get a value from the hal cache + * \param[in] iface_type - the type of physical interface to register + * \param[out] hal pointer to the existing ATCAHAL_t structure + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +static ATCA_STATUS hal_iface_get_registered(ATCAIfaceType iface_type, ATCAHAL_t** hal, ATCAHAL_t **phy) +{ + ATCA_STATUS status = ATCA_BAD_PARAM; + + if (hal && phy) + { + size_t i; + for (i = 0; i < atca_registered_hal_list_size; i++) + { + if (iface_type == atca_registered_hal_list[i].iface_type) + { + break; + } + } + + if (i < atca_registered_hal_list_size) + { + *hal = atca_registered_hal_list[i].hal; + *phy = atca_registered_hal_list[i].phy; + status = ATCA_SUCCESS; + } + else + { + status = ATCA_GEN_FAIL; + } + } + + return status; +} + +/** \brief Internal function to set a value in the hal cache + * \param[in] iface_type - the type of physical interface to register + * \param[in] hal pointer to the existing ATCAHAL_t structure + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +static ATCA_STATUS hal_iface_set_registered(ATCAIfaceType iface_type, ATCAHAL_t* hal, ATCAHAL_t* phy) +{ + ATCA_STATUS status = ATCA_BAD_PARAM; + + if (hal) + { + size_t i; + size_t empty = atca_registered_hal_list_size; + for (i = 0; i < atca_registered_hal_list_size; i++) + { + if (iface_type == atca_registered_hal_list[i].iface_type) + { + break; + } + else if (empty == atca_registered_hal_list_size) + { + if (!atca_registered_hal_list[i].hal && !atca_registered_hal_list[i].phy) + { + empty = i; + } + } + } + + if (i < atca_registered_hal_list_size) + { + atca_registered_hal_list[i].hal = hal; + atca_registered_hal_list[i].phy = phy; + status = ATCA_SUCCESS; + } + else if (empty < atca_registered_hal_list_size) + { + atca_registered_hal_list[empty].hal = hal; + atca_registered_hal_list[empty].hal = phy; + status = ATCA_SUCCESS; + } + else + { + status = ATCA_ALLOC_FAILURE; + } + + } + + return status; +} + +/** \brief Register/Replace a HAL with a + * \param[in] iface_type - the type of physical interface to register + * \param[in] hal pointer to the new ATCAHAL_t structure to register + * \param[out] old pointer to the existing ATCAHAL_t structure + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS hal_iface_register_hal(ATCAIfaceType iface_type, ATCAHAL_t *hal, ATCAHAL_t **old_hal, ATCAHAL_t* phy, ATCAHAL_t** old_phy) +{ + ATCA_STATUS status; + + status = (old_hal && old_phy) ? hal_iface_get_registered(iface_type, old_hal, old_phy) : ATCA_SUCCESS; + + if (ATCA_SUCCESS == status) + { + status = hal_iface_set_registered(iface_type, hal, phy); + } + + return ATCA_SUCCESS; +} + +/** \brief Standard HAL API for ATCA to initialize a physical interface + * \param[in] cfg pointer to ATCAIfaceCfg object + * \param[in] hal pointer to ATCAHAL_t intermediate data structure + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS hal_iface_init(ATCAIfaceCfg *cfg, ATCAHAL_t **hal, ATCAHAL_t **phy) +{ + ATCA_STATUS status = ATCA_BAD_PARAM; + + if (cfg && hal) + { + status = hal_iface_get_registered(cfg->iface_type, hal, phy); + +#ifdef ATCA_HAL_CUSTOM + if (ATCA_CUSTOM_IFACE == cfg->iface_type) + { + *hal = hal_malloc(sizeof(ATCAHAL_t)); + if (*hal) + { + (*hal)->halinit = cfg->atcacustom.halinit; + (*hal)->halpostinit = cfg->atcacustom.halpostinit; + (*hal)->halreceive = cfg->atcacustom.halreceive; + (*hal)->halsend = cfg->atcacustom.halsend; + (*hal)->halcontrol = hal_custom_control; + (*hal)->halrelease = cfg->atcacustom.halrelease; + status = ATCA_SUCCESS; + } + else + { + status = ATCA_ALLOC_FAILURE; + } + } +#endif + } + + return status; +} + +/** \brief releases a physical interface, HAL knows how to interpret hal_data + * \param[in] iface_type - the type of physical interface to release + * \param[in] hal_data - pointer to opaque hal data maintained by HAL implementation for this interface type + * \return ATCA_SUCCESS on success, otherwise an error code. + */ + +ATCA_STATUS hal_iface_release(ATCAIfaceType iface_type, void *hal_data) +{ + ATCA_STATUS status; + ATCAHAL_t * hal; + ATCAHAL_t* phy; + + status = hal_iface_get_registered(iface_type, &hal, &phy); + + if (ATCA_SUCCESS == status) + { + if (hal && hal->halrelease) + { + status = hal->halrelease(hal_data); + } + + if (phy && phy->halrelease) + { + ATCA_STATUS phy_status = phy->halrelease(hal_data); + + if (ATCA_SUCCESS == status) + { + status = phy_status; + } + } + } + + return status; +} + +/** \brief Utility function for hal_wake to check the reply. + * \param[in] response Wake response to be checked. + * \param[in] response_size Size of the response to check. + * \return ATCA_SUCCESS for expected wake, ATCA_STATUS_SELFTEST_ERROR if the + * power on self test failed, ATCA_WAKE_FAILED for other failures. + */ +ATCA_STATUS hal_check_wake(const uint8_t* response, int response_size) +{ + const uint8_t expected_response[4] = { 0x04, 0x11, 0x33, 0x43 }; + const uint8_t selftest_fail_resp[4] = { 0x04, 0x07, 0xC4, 0x40 }; + + if (response_size != 4) + { + return ATCA_WAKE_FAILED; + } + if (memcmp(response, expected_response, 4) == 0) + { + return ATCA_SUCCESS; + } + if (memcmp(response, selftest_fail_resp, 4) == 0) + { + return ATCA_STATUS_SELFTEST_ERROR; + } + return ATCA_WAKE_FAILED; +} + +/** \brief Utility function for hal_wake to check the reply. + * \param[in] word_address Command to check + * \return true if the word_address is considered a command + */ +uint8_t hal_is_command_word(uint8_t word_address) +{ + return 0xFF == word_address || 0x03 == word_address || 0x10 == word_address; +} + + +#if !defined(ATCA_NO_HEAP) && defined(ATCA_TESTS_ENABLED) && defined(ATCA_PLATFORM_MALLOC) + +void* (*g_hal_malloc_f)(size_t) = ATCA_PLATFORM_MALLOC; +void (*g_hal_free_f)(void*) = ATCA_PLATFORM_FREE; + +void* hal_malloc(size_t size) +{ + return g_hal_malloc_f(size); +} + +void hal_free(void* ptr) +{ + g_hal_free_f(ptr); +} + +void hal_test_set_memory_f(void* (*malloc_func)(size_t), void (*free_func)(void*)) +{ + g_hal_malloc_f = malloc_func; + g_hal_free_f = free_func; +} + +#endif + +#if defined(ATCA_HAL_LEGACY_API) && defined(ATCA_HAL_I2C) +ATCA_STATUS hal_i2c_control(ATCAIface iface, uint8_t option, void* param, size_t paramlen) +{ + (void)param; + (void)paramlen; + + switch (option) + { + case ATCA_HAL_CONTROL_WAKE: + return hal_i2c_wake(iface); + case ATCA_HAL_CONTROL_IDLE: + return hal_i2c_idle(iface); + case ATCA_HAL_CONTROL_SLEEP: + return hal_i2c_sleep(iface); + case ATCA_HAL_CONTROL_SELECT: + /* fallthrough */ + case ATCA_HAL_CONTROL_DESELECT: + return ATCA_SUCCESS; + default: + return ATCA_BAD_PARAM; + } +} +#endif + +#if defined(ATCA_HAL_LEGACY_API) && defined(ATCA_HAL_SWI) +ATCA_STATUS hal_swi_control(ATCAIface iface, uint8_t option, void* param, size_t paramlen) +{ + (void)param; + (void)paramlen; + + switch (option) + { + case ATCA_HAL_CONTROL_WAKE: + return hal_swi_wake(iface); + case ATCA_HAL_CONTROL_IDLE: + return hal_swi_idle(iface); + case ATCA_HAL_CONTROL_SLEEP: + return hal_swi_sleep(iface); + case ATCA_HAL_CONTROL_SELECT: + /* fallthrough */ + case ATCA_HAL_CONTROL_DESELECT: + return ATCA_SUCCESS; + default: + return ATCA_BAD_PARAM; + } +} +#endif + +#if defined(ATCA_HAL_LEGACY_API) && defined(ATCA_HAL_UART) +ATCA_STATUS hal_uart_control(ATCAIface iface, uint8_t option, void* param, size_t paramlen) +{ + (void)param; + (void)paramlen; + + switch (option) + { + case ATCA_HAL_CONTROL_WAKE: + return hal_uart_wake(iface); + case ATCA_HAL_CONTROL_IDLE: + return hal_uart_idle(iface); + case ATCA_HAL_CONTROL_SLEEP: + return hal_uart_sleep(iface); + case ATCA_HAL_CONTROL_SELECT: + /* fallthrough */ + case ATCA_HAL_CONTROL_DESELECT: + return ATCA_SUCCESS; + default: + return ATCA_BAD_PARAM; + } +} +#endif + +#if defined(ATCA_HAL_LEGACY_API) && defined(ATCA_HAL_SPI) +ATCA_STATUS hal_spi_control(ATCAIface iface, uint8_t option, void* param, size_t paramlen) +{ + (void)param; + (void)paramlen; + + if (iface) + { + switch (option) + { + case ATCA_HAL_CONTROL_WAKE: + return hal_spi_wake(iface); + case ATCA_HAL_CONTROL_IDLE: + return hal_spi_idle(iface); + case ATCA_HAL_CONTROL_SLEEP: + return hal_spi_sleep(iface); + case ATCA_HAL_CONTROL_SELECT: + /* fallthrough */ + case ATCA_HAL_CONTROL_DESELECT: + return ATCA_SUCCESS; + default: + break; + } + } + return ATCA_BAD_PARAM; +} +#endif + +#if defined(ATCA_HAL_CUSTOM) +ATCA_STATUS hal_custom_control(ATCAIface iface, uint8_t option, void* param, size_t paramlen) +{ + (void)param; + (void)paramlen; + + if (iface && iface->mIfaceCFG) + { + switch (option) + { + case ATCA_HAL_CONTROL_WAKE: + return iface->mIfaceCFG->atcacustom.halwake(iface); + case ATCA_HAL_CONTROL_IDLE: + return iface->mIfaceCFG->atcacustom.halidle(iface); + case ATCA_HAL_CONTROL_SLEEP: + return iface->mIfaceCFG->atcacustom.halsleep(iface); + case ATCA_HAL_CONTROL_SELECT: + /* fallthrough */ + case ATCA_HAL_CONTROL_DESELECT: + return ATCA_SUCCESS; + default: + break; + } + } + return ATCA_BAD_PARAM; +} +#endif diff --git a/drivers/ecc108a/cryptoauthlib/hal/atca_hal.h b/drivers/ecc108a/cryptoauthlib/hal/atca_hal.h new file mode 100644 index 0000000..9999256 --- /dev/null +++ b/drivers/ecc108a/cryptoauthlib/hal/atca_hal.h @@ -0,0 +1,238 @@ +/** + * \file + * \brief low-level HAL - methods used to setup indirection to physical layer interface + * + * \copyright (c) 2015-2020 Microchip Technology Inc. and its subsidiaries. + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip software + * and any derivatives exclusively with Microchip products. It is your + * responsibility to comply with third party license terms applicable to your + * use of third party software (including open source software) that may + * accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER + * EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, + * SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE + * OF ANY KIND WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF + * MICROCHIP HAS BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE + * FORESEEABLE. TO THE FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL + * LIABILITY ON ALL CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED + * THE AMOUNT OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR + * THIS SOFTWARE. + */ + + +#ifndef ATCA_HAL_H_ +#define ATCA_HAL_H_ + +#include + +#include "atca_config.h" + +#include "atca_status.h" +#include "atca_iface.h" + + +/** \defgroup hal_ Hardware abstraction layer (hal_) + * + * \brief + * These methods define the hardware abstraction layer for communicating with a CryptoAuth device + * + @{ */ + +typedef struct +{ + ATCA_STATUS (*send)(void* ctx, uint8_t* txdata, uint16_t txlen); /**< Must be a blocking send */ + ATCA_STATUS (*recv)(void* ctx, uint8_t* rxdata, uint16_t* rxlen); /**< Must be a blocking receive */ + void* (*packet_alloc)(size_t bytes); /**< Allocate a phy packet */ + void (*packet_free)(void* packet); /**< Free a phy packet */ + void* hal_data; /**< Physical layer context */ +} atca_hal_kit_phy_t; + +#ifdef __cplusplus +extern "C" { +#endif + +ATCA_STATUS hal_iface_init(ATCAIfaceCfg *, ATCAHAL_t** hal, ATCAHAL_t** phy); +ATCA_STATUS hal_iface_release(ATCAIfaceType, void* hal_data); + +ATCA_STATUS hal_check_wake(const uint8_t* response, int response_size); + +#ifdef ATCA_HAL_I2C +ATCA_STATUS hal_i2c_init(ATCAIface iface, ATCAIfaceCfg *cfg); +ATCA_STATUS hal_i2c_post_init(ATCAIface iface); +ATCA_STATUS hal_i2c_send(ATCAIface iface, uint8_t word_address, uint8_t *txdata, int txlength); +ATCA_STATUS hal_i2c_receive(ATCAIface iface, uint8_t word_address, uint8_t *rxdata, uint16_t *rxlength); +ATCA_STATUS hal_i2c_control(ATCAIface iface, uint8_t option, void* param, size_t paramlen); +#ifdef ATCA_LEGACY_HAL +ATCA_STATUS hal_i2c_wake(ATCAIface iface); +ATCA_STATUS hal_i2c_idle(ATCAIface iface); +ATCA_STATUS hal_i2c_sleep(ATCAIface iface); +#endif +ATCA_STATUS hal_i2c_release(void *hal_data); +#endif + +#ifdef ATCA_HAL_SWI_UART +ATCA_STATUS hal_swi_init(ATCAIface iface, ATCAIfaceCfg *cfg); +ATCA_STATUS hal_swi_post_init(ATCAIface iface); +ATCA_STATUS hal_swi_send(ATCAIface iface, uint8_t word_address, uint8_t *txdata, int txlength); +ATCA_STATUS hal_swi_receive(ATCAIface iface, uint8_t word_address, uint8_t *rxdata, uint16_t *rxlength); +ATCA_STATUS hal_swi_control(ATCAIface iface, uint8_t option, void* param, size_t paramlen); +#ifdef ATCA_LEGACY_HAL +ATCA_STATUS hal_swi_wake(ATCAIface iface); +ATCA_STATUS hal_swi_idle(ATCAIface iface); +ATCA_STATUS hal_swi_sleep(ATCAIface iface); +#endif +ATCA_STATUS hal_swi_release(void *hal_data); +#endif + +#if defined(ATCA_HAL_SWI_GPIO) || defined(ATCA_HAL_SWI_BB) +ATCA_STATUS hal_swi_gpio_init(ATCAIface iface, ATCAIfaceCfg *cfg); +ATCA_STATUS hal_swi_gpio_post_init(ATCAIface iface); +ATCA_STATUS hal_swi_gpio_send(ATCAIface iface, uint8_t word_address, uint8_t *txdata, int txlength); +ATCA_STATUS hal_swi_gpio_receive(ATCAIface iface, uint8_t word_address, uint8_t *rxdata, uint16_t *rxlength); +ATCA_STATUS hal_swi_gpio_release(void *hal_data); +ATCA_STATUS hal_swi_gpio_control(ATCAIface iface, uint8_t option, void* param, size_t paramlen); +#endif + +#if defined(ATCA_HAL_GPIO) || defined(ATCA_HAL_BB) +ATCA_STATUS hal_gpio_init(ATCAIface iface, ATCAIfaceCfg *cfg); +ATCA_STATUS hal_gpio_post_init(ATCAIface iface); +ATCA_STATUS hal_gpio_send(ATCAIface iface, uint8_t word_address, uint8_t* pin_state, int unused_param); +ATCA_STATUS hal_gpio_receive(ATCAIface iface, uint8_t word_address, uint8_t* pin_state, uint16_t* unused_param); +ATCA_STATUS hal_gpio_release(void *hal_data); +ATCA_STATUS hal_gpio_control(ATCAIface iface, uint8_t option, void* param, size_t paramlen); +#endif + +#if defined(ATCA_HAL_SWI_UART) || defined(ATCA_HAL_KIT_UART) || defined(ATCA_HAL_UART) +ATCA_STATUS hal_uart_init(ATCAIface iface, ATCAIfaceCfg *cfg); +ATCA_STATUS hal_uart_post_init(ATCAIface iface); +ATCA_STATUS hal_uart_send(ATCAIface iface, uint8_t word_address, uint8_t *txdata, int txlength); +ATCA_STATUS hal_uart_receive(ATCAIface iface, uint8_t word_address, uint8_t *rxdata, uint16_t *rxlength); +ATCA_STATUS hal_uart_control(ATCAIface iface, uint8_t option, void* param, size_t paramlen); +#ifdef ATCA_LEGACY_HAL +ATCA_STATUS hal_uart_wake(ATCAIface iface); +ATCA_STATUS hal_uart_idle(ATCAIface iface); +ATCA_STATUS hal_uart_sleep(ATCAIface iface); +#endif +ATCA_STATUS hal_uart_release(void *hal_data); +#endif + +#ifdef ATCA_HAL_SPI +ATCA_STATUS hal_spi_init(ATCAIface iface, ATCAIfaceCfg *cfg); +ATCA_STATUS hal_spi_post_init(ATCAIface iface); +ATCA_STATUS hal_spi_send(ATCAIface iface, uint8_t word_address, uint8_t *txdata, int txlength); +ATCA_STATUS hal_spi_receive(ATCAIface iface, uint8_t word_address, uint8_t *rxdata, uint16_t *rxlength); +ATCA_STATUS hal_spi_control(ATCAIface iface, uint8_t option, void* param, size_t paramlen); +#ifdef ATCA_LEGACY_HAL +ATCA_STATUS hal_spi_wake(ATCAIface iface); +ATCA_STATUS hal_spi_idle(ATCAIface iface); +ATCA_STATUS hal_spi_sleep(ATCAIface iface); +#endif +ATCA_STATUS hal_spi_release(void *hal_data); +#endif + +#ifdef ATCA_HAL_KIT_HID +ATCA_STATUS hal_kit_hid_init(ATCAIface iface, ATCAIfaceCfg *cfg); +ATCA_STATUS hal_kit_hid_post_init(ATCAIface iface); +ATCA_STATUS hal_kit_hid_send(ATCAIface iface, uint8_t word_address, uint8_t *txdata, int txlength); +ATCA_STATUS hal_kit_hid_receive(ATCAIface iface, uint8_t word_address, uint8_t *rxdata, uint16_t *rxlength); +ATCA_STATUS hal_kit_hid_control(ATCAIface iface, uint8_t option, void* param, size_t paramlen); +ATCA_STATUS hal_kit_hid_release(void *hal_data); +#endif + +#ifdef ATCA_HAL_KIT_BRIDGE +ATCA_STATUS hal_kit_init(ATCAIface iface, ATCAIfaceCfg* cfg); +ATCA_STATUS hal_kit_post_init(ATCAIface iface); +ATCA_STATUS hal_kit_send(ATCAIface iface, uint8_t word_address, uint8_t* txdata, int txlength); +ATCA_STATUS hal_kit_receive(ATCAIface iface, uint8_t word_address, uint8_t* rxdata, uint16_t* rxlength); +ATCA_STATUS hal_kit_control(ATCAIface iface, uint8_t option, void* param, size_t paramlen); +ATCA_STATUS hal_kit_release(void* hal_data); +#endif + +#ifdef ATCA_HAL_CUSTOM +ATCA_STATUS hal_custom_control(ATCAIface iface, uint8_t option, void* param, size_t paramlen); +#endif + +/* Polling defaults if not overwritten by the configuration */ +#ifndef ATCA_POLLING_INIT_TIME_MSEC +#define ATCA_POLLING_INIT_TIME_MSEC 1 +#endif + +#ifndef ATCA_POLLING_FREQUENCY_TIME_MSEC +#define ATCA_POLLING_FREQUENCY_TIME_MSEC 2 +#endif + +#ifndef ATCA_POLLING_MAX_TIME_MSEC +#define ATCA_POLLING_MAX_TIME_MSEC 2500 +#endif + +/* */ +typedef enum +{ + ATCA_HAL_CONTROL_WAKE = 0, + ATCA_HAL_CONTROL_IDLE = 1, + ATCA_HAL_CONTROL_SLEEP = 2, + ATCA_HAL_CONTROL_RESET = 3, + ATCA_HAL_CONTROL_SELECT = 4, + ATCA_HAL_CONTROL_DESELECT = 5, + ATCA_HAL_CHANGE_BAUD = 6, + ATCA_HAL_FLUSH_BUFFER = 7, + ATCA_HAL_CONTROL_DIRECTION = 8 +} ATCA_HAL_CONTROL; + +/** \brief Timer API for legacy implementations */ +#ifndef atca_delay_ms +void atca_delay_ms(uint32_t ms); +#endif + +#ifndef atca_delay_us +void atca_delay_us(uint32_t us); +#endif + +/** \brief Timer API implemented at the HAL level */ +void hal_rtos_delay_ms(uint32_t ms); +void hal_delay_ms(uint32_t ms); +void hal_delay_us(uint32_t us); + +/** \brief Optional hal interfaces */ +ATCA_STATUS hal_create_mutex(void ** ppMutex, char* pName); +ATCA_STATUS hal_destroy_mutex(void * pMutex); +ATCA_STATUS hal_lock_mutex(void * pMutex); +ATCA_STATUS hal_unlock_mutex(void * pMutex); + +#ifndef ATCA_NO_HEAP +#ifdef ATCA_TESTS_ENABLED +void hal_test_set_memory_f(void* (*malloc_func)(size_t), void (*free_func)(void*)); +#endif + +#if defined(ATCA_TESTS_ENABLED) || !defined(ATCA_PLATFORM_MALLOC) +void* hal_malloc(size_t size); +void hal_free(void* ptr); +#else +#define hal_malloc ATCA_PLATFORM_MALLOC +#define hal_free ATCA_PLATFORM_FREE +#endif +#endif + +#ifdef memset_s +#define hal_memset_s memset_s +#else +#define hal_memset_s atcab_memset_s +#endif + + +ATCA_STATUS hal_iface_register_hal(ATCAIfaceType iface_type, ATCAHAL_t *hal, ATCAHAL_t **old_hal, ATCAHAL_t* phy, ATCAHAL_t** old_phy); +uint8_t hal_is_command_word(uint8_t word_address); + +#ifdef __cplusplus +} +#endif + +/** @} */ + +#endif /* ATCA_HAL_H_ */ diff --git a/drivers/ecc108a/cryptoauthlib/hal/hal_esp32_mp_softi2c.c b/drivers/ecc108a/cryptoauthlib/hal/hal_esp32_mp_softi2c.c new file mode 100644 index 0000000..2479762 --- /dev/null +++ b/drivers/ecc108a/cryptoauthlib/hal/hal_esp32_mp_softi2c.c @@ -0,0 +1,132 @@ +#include +#include +#include +#include "extmod/machine_i2c.h" +#include "ports/esp32/modmachine.h" +#include "esp_err.h" +#include "esp_log.h" +#include "cryptoauthlib.h" +#include "py/runtime.h" + +#define SCL_S 41 +#define SDA_S 42 + +const char* TAG = "HAL_SOFTI2C"; + +/* For debugging on the production board over USB */ +#define HAL_SOFTI2C_DEBUG 0 +#define hal_softi2c_debug_print(fmt, ...) \ + do { if (HAL_SOFTI2C_DEBUG) mp_printf(fmt, __VA_ARGS__); } while (0) + + +/** \brief method to change the bus speed of I2C + * \param[in] iface interface on which to change bus speed + * \param[in] speed baud rate (typically 100000 or 400000) + */ +ATCA_STATUS hal_i2c_change_baud(ATCAIface iface, uint32_t speed) +{ + // FIXME: implement + return ATCA_SUCCESS; +} + +ATCA_STATUS hal_i2c_init(ATCAIface iface, ATCAIfaceCfg *cfg) +{ + hal_softi2c_debug_print(&mp_plat_print, "hal_i2c_init\n"); + + // FIXME: read cfg + + mp_obj_t pin_scl = mp_obj_new_int(SCL_S); + mp_obj_t pin_sda = mp_obj_new_int(SDA_S); + mp_obj_t args[] = { + machine_pin_type.make_new(&machine_pin_type, 1, 0, &pin_scl), + machine_pin_type.make_new(&machine_pin_type, 1, 0, &pin_sda), + /* following is actually mp_map_elem_t[] */ + MP_ROM_QSTR(MP_QSTR_freq), mp_obj_new_int(100000), + }; + mp_obj_t i2c = mp_machine_soft_i2c_type.make_new(&mp_machine_soft_i2c_type, 2, 1, args); + hal_softi2c_debug_print(&mp_plat_print, "SoftI2C(pin_scl, pin_sda, freq=freq) returned 0x%x\n", i2c); + iface->hal_data = (void *) i2c; + + return ATCA_SUCCESS; +} + +/** \brief HAL implementation of I2C post init + * \param[in] iface instance + * \return ATCA_SUCCESS + */ +ATCA_STATUS hal_i2c_post_init(ATCAIface iface) +{ + return ATCA_SUCCESS; +} + +/** \brief HAL implementation of I2C send + * \param[in] iface instance + * \param[in] word_address device transaction type + * \param[in] txdata pointer to space to bytes to send + * \param[in] txlength number of bytes to send + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS hal_i2c_send(ATCAIface iface, uint8_t address, uint8_t *txdata, int txlength) +{ + hal_softi2c_debug_print(&mp_plat_print, "hal_i2c_send\n"); + mp_obj_base_t *i2c = (mp_obj_base_t *) iface->hal_data; + mp_machine_i2c_p_t *i2c_p = (mp_machine_i2c_p_t *) mp_machine_soft_i2c_type.protocol; + + mp_machine_i2c_buf_t buf = {.len = txlength, .buf = (uint8_t *)txdata}; + int acks = i2c_p->transfer(i2c, address, 1, &buf, MP_MACHINE_I2C_FLAG_STOP); + hal_softi2c_debug_print(&mp_plat_print, "i2c.transfer returned 0x%x\n", acks); + if (acks < 0) { + return acks; + } + return ATCA_SUCCESS; +} + +/** \brief HAL implementation of I2C receive function + * \param[in] iface Device to interact with. + * \param[in] address Device address + * \param[out] rxdata Data received will be returned here. + * \param[in,out] rxlength As input, the size of the rxdata buffer. + * As output, the number of bytes received. + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS hal_i2c_receive(ATCAIface iface, uint8_t address, uint8_t *rxdata, uint16_t *rxlength) +{ + hal_softi2c_debug_print(&mp_plat_print, "hal_i2c_receive\n"); + mp_obj_base_t *i2c = (mp_obj_base_t *) iface->hal_data; + mp_machine_i2c_p_t *i2c_p = (mp_machine_i2c_p_t *) mp_machine_soft_i2c_type.protocol; + + mp_machine_i2c_buf_t buf = {.len = *rxlength, .buf = rxdata}; + int status = i2c_p->transfer(i2c, address, 1, &buf, MP_MACHINE_I2C_FLAG_READ | MP_MACHINE_I2C_FLAG_STOP); + hal_softi2c_debug_print(&mp_plat_print, "i2c.transfer returned 0x%x\n", status); + if (status < 0) { + return status; + } + // rxlength will be the number of bytes received + return ATCA_SUCCESS; +} + +/** \brief manages reference count on given bus and releases resource if no more refences exist + * \param[in] hal_data - opaque pointer to hal data structure - known only to the HAL implementation + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS hal_i2c_release(void *hal_data) +{ + hal_softi2c_debug_print(&mp_plat_print, "hal_i2c_release\n"); + mp_obj_base_t *i2c = (mp_obj_base_t *) hal_data; + // FIXME: delete i2c + return ATCA_SUCCESS; +} + +/** \brief Perform control operations for the kit protocol + * \param[in] iface Interface to interact with. + * \param[in] option Control parameter identifier + * \param[in] param Optional pointer to parameter value + * \param[in] paramlen Length of the parameter + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +ATCA_STATUS hal_i2c_control(ATCAIface iface, uint8_t option, void* param, size_t paramlen) +{ + hal_softi2c_debug_print(&mp_plat_print, "hal_i2c_control\n"); + mp_obj_base_t *i2c = (mp_obj_base_t *) iface->hal_data; + return ATCA_UNIMPLEMENTED; +} diff --git a/drivers/ecc108a/cryptoauthlib/host/atca_host.c b/drivers/ecc108a/cryptoauthlib/host/atca_host.c new file mode 100644 index 0000000..93dfc81 --- /dev/null +++ b/drivers/ecc108a/cryptoauthlib/host/atca_host.c @@ -0,0 +1,1739 @@ +/** + * \file + * \brief Host side methods to support CryptoAuth computations + * + * \copyright (c) 2015-2020 Microchip Technology Inc. and its subsidiaries. + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip software + * and any derivatives exclusively with Microchip products. It is your + * responsibility to comply with third party license terms applicable to your + * use of third party software (including open source software) that may + * accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER + * EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, + * SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE + * OF ANY KIND WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF + * MICROCHIP HAS BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE + * FORESEEABLE. TO THE FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL + * LIABILITY ON ALL CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED + * THE AMOUNT OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR + * THIS SOFTWARE. + */ + +#include "atca_host.h" +#ifndef ATCA_TIDAL +#include "crypto/atca_crypto_sw_sha2.h" +#endif // ifndef ATCA_TIDAL + +#if ATCA_CA_SUPPORT + +/** \brief This function copies otp and sn data into a command buffer. + * + * \param[in,out] param pointer to parameter structure + * \return pointer to command buffer byte that was copied last + */ +#if ATCAH_INCLUDE_DATA +uint8_t *atcah_include_data(struct atca_include_data_in_out *param) +{ + if (param->mode & MAC_MODE_INCLUDE_OTP_88) + { + memcpy(param->p_temp, param->otp, 11); // use OTP[0:10], Mode:5 is overridden + param->p_temp += 11; + } + else + { + if (param->mode & MAC_MODE_INCLUDE_OTP_64) + { + memcpy(param->p_temp, param->otp, 8); // use 8 bytes OTP[0:7] for (6) + } + else + { + memset(param->p_temp, 0, 8); // use 8 zeros for (6) + } + param->p_temp += 8; + + memset(param->p_temp, 0, 3); // use 3 zeros for (7) + param->p_temp += 3; + } + + // (8) 1 byte SN[8] + *param->p_temp++ = param->sn[8]; + + // (9) 4 bytes SN[4:7] or zeros + if (param->mode & MAC_MODE_INCLUDE_SN) + { + memcpy(param->p_temp, ¶m->sn[4], 4); //use SN[4:7] for (9) + } + else + { + memset(param->p_temp, 0, 4); //use zeros for (9) + } + param->p_temp += 4; + + // (10) 2 bytes SN[0:1] + *param->p_temp++ = param->sn[0]; + *param->p_temp++ = param->sn[1]; + + // (11) 2 bytes SN[2:3] or zeros + if (param->mode & MAC_MODE_INCLUDE_SN) + { + memcpy(param->p_temp, ¶m->sn[2], 2); //use SN[2:3] for (11) + } + else + { + memset(param->p_temp, 0, 2); //use zeros for (9) + } + param->p_temp += 2; + + return param->p_temp; +} +#endif /* ATCAH_INCLUDE_DATA */ + +/** \brief This function calculates host side nonce with the parameters passed. + * \param[in,out] param pointer to parameter structure + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +#if ATCAH_NONCE +ATCA_STATUS atcah_nonce(struct atca_nonce_in_out *param) +{ + uint8_t temporary[ATCA_MSG_SIZE_NONCE]; + uint8_t *p_temp; + uint8_t calc_mode = param->mode & NONCE_MODE_MASK; + + // Check parameters + if (param->temp_key == NULL || param->num_in == NULL) + { + return ATCA_BAD_PARAM; + } + + // Calculate or pass-through the nonce to TempKey->Value + if ((calc_mode == NONCE_MODE_SEED_UPDATE) || (calc_mode == NONCE_MODE_NO_SEED_UPDATE)) + { + // RandOut is only required for these modes + if (param->rand_out == NULL) + { + return ATCA_BAD_PARAM; + } + + if ((param->zero & NONCE_ZERO_CALC_MASK) == NONCE_ZERO_CALC_TEMPKEY) + { + // Nonce calculation mode. Actual value of TempKey has been returned in RandOut + memcpy(param->temp_key->value, param->rand_out, 32); + + // TempKey flags aren't changed + } + else + { + // Calculate nonce using SHA-256 (refer to data sheet) + p_temp = temporary; + + memcpy(p_temp, param->rand_out, RANDOM_NUM_SIZE); + p_temp += RANDOM_NUM_SIZE; + + memcpy(p_temp, param->num_in, NONCE_NUMIN_SIZE); + p_temp += NONCE_NUMIN_SIZE; + + *p_temp++ = ATCA_NONCE; + *p_temp++ = param->mode; + *p_temp++ = 0x00; + + // Calculate SHA256 to get the nonce + atcac_sw_sha2_256(temporary, ATCA_MSG_SIZE_NONCE, param->temp_key->value); + + // Update TempKey flags + param->temp_key->source_flag = 0; // Random + param->temp_key->key_id = 0; + param->temp_key->gen_dig_data = 0; + param->temp_key->no_mac_flag = 0; + param->temp_key->valid = 1; + } + + // Update TempKey to only 32 bytes + param->temp_key->is_64 = 0; + } + else if ((param->mode & NONCE_MODE_MASK) == NONCE_MODE_PASSTHROUGH) + { + + if ((param->mode & NONCE_MODE_TARGET_MASK) == NONCE_MODE_TARGET_TEMPKEY) + { + // Pass-through mode for TempKey (other targets have no effect on TempKey) + if ((param->mode & NONCE_MODE_INPUT_LEN_MASK) == NONCE_MODE_INPUT_LEN_64) + { + memcpy(param->temp_key->value, param->num_in, 64); + param->temp_key->is_64 = 1; + } + else + { + memcpy(param->temp_key->value, param->num_in, 32); + param->temp_key->is_64 = 0; + } + + // Update TempKey flags + param->temp_key->source_flag = 1; // Not Random + param->temp_key->key_id = 0; + param->temp_key->gen_dig_data = 0; + param->temp_key->no_mac_flag = 0; + param->temp_key->valid = 1; + } + else //In the case of ECC608, passthrough message may be stored in message digest buffer/ Alternate Key buffer + { + + // Update TempKey flags + param->temp_key->source_flag = 1; //Not Random + param->temp_key->key_id = 0; + param->temp_key->gen_dig_data = 0; + param->temp_key->no_mac_flag = 0; + param->temp_key->valid = 0; + + } + } + else if ((NONCE_MODE_GEN_SESSION_KEY == calc_mode) && (param->zero >= 0x8000)) + { + // Calculate nonce using SHA-256 (refer to data sheet) + p_temp = temporary; + + memcpy(p_temp, param->rand_out, RANDOM_NUM_SIZE); + p_temp += RANDOM_NUM_SIZE; + + memcpy(p_temp, param->num_in, NONCE_NUMIN_SIZE); + p_temp += NONCE_NUMIN_SIZE; + + *p_temp++ = ATCA_NONCE; + *p_temp++ = param->mode; + *p_temp++ = (param->zero) & 0xFF; + + // Calculate SHA256 to get the nonce + atcac_sw_sha2_256(temporary, ATCA_MSG_SIZE_NONCE, param->temp_key->value); + } + else + { + return ATCA_BAD_PARAM; + } + + return ATCA_SUCCESS; +} +#endif /* atcah_nonce */ + +/** \brief Decrypt data that's been encrypted by the IO protection key. + * The ECDH and KDF commands on the ATECC608 are the only ones that + * support this operation. + * + * \param[in,out] param Parameters required to perform the operation. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +#if ATCAH_IO_DECRYPT +ATCA_STATUS atcah_io_decrypt(struct atca_io_decrypt_in_out *param) +{ + atcac_sha2_256_ctx ctx; + uint8_t key[ATCA_KEY_SIZE]; + size_t block = 0; + int i; + + if (param == NULL || param->io_key == NULL || param->out_nonce == NULL || param->data == NULL) + { + return ATCA_BAD_PARAM; + } + if (param->data_size % ATCA_BLOCK_SIZE != 0) + { + return ATCA_BAD_PARAM; + } + + for (block = 0; block < param->data_size / ATCA_BLOCK_SIZE; block++) + { + // Calculate key for block + atcac_sw_sha2_256_init(&ctx); + atcac_sw_sha2_256_update(&ctx, param->io_key, 32); + atcac_sw_sha2_256_update(&ctx, ¶m->out_nonce[block * 16], 16); + atcac_sw_sha2_256_finish(&ctx, key); + + // Decrypt block + for (i = 0; i < ATCA_BLOCK_SIZE; i++) + { + param->data[block * ATCA_BLOCK_SIZE + i] ^= key[i]; + } + } + + return ATCA_SUCCESS; +} +#endif /* ATCAH_IO_DECRYPT */ + +/** \brief Calculate the expected MAC on the host side for the Verify command. + * + * \param[in,out] param Data required to perform the operation. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +#if ATCAH_VERIFY_MAC +ATCA_STATUS atcah_verify_mac(atca_verify_mac_in_out_t *param) +{ + uint8_t verify_mode = (param->mode & VERIFY_MODE_MASK); + uint8_t verify_source = (param->mode & VERIFY_MODE_SOURCE_MASK); + atcac_sha2_256_ctx ctx; + uint8_t message[32]; + const uint8_t* nonce = NULL; + uint8_t input_params[4]; + const uint8_t sign_opcode = ATCA_SIGN; + + // Check parameters + if (param->signature == NULL || param->msg_dig_buf == NULL || param->io_key == NULL) + { + return ATCA_BAD_PARAM; + } + + // Get the verify message + if (verify_mode == VERIFY_MODE_VALIDATE || verify_mode == VERIFY_MODE_INVALIDATE) + { + if (param->other_data == NULL || param->temp_key == NULL || param->sn == NULL) + { + return ATCA_BAD_PARAM; + } + + // Message is calculated based on TempKey and OtherData + atcac_sw_sha2_256_init(&ctx); + atcac_sw_sha2_256_update(&ctx, param->temp_key->value, 32); + atcac_sw_sha2_256_update(&ctx, &sign_opcode, 1); + atcac_sw_sha2_256_update(&ctx, ¶m->other_data[0], 10); + atcac_sw_sha2_256_update(&ctx, ¶m->sn[8], 1); + atcac_sw_sha2_256_update(&ctx, ¶m->other_data[10], 4); + atcac_sw_sha2_256_update(&ctx, ¶m->sn[0], 2); + atcac_sw_sha2_256_update(&ctx, ¶m->other_data[14], 5); + atcac_sw_sha2_256_finish(&ctx, message); + } + else if (verify_source == VERIFY_MODE_SOURCE_MSGDIGBUF) + { + // Message source is the first 32 bytes of the message digest buffer + memcpy(message, param->msg_dig_buf, 32); + } + else + { + // Message source is the first 32 bytes of TempKey + if (param->temp_key == NULL) + { + return ATCA_BAD_PARAM; + } + memcpy(message, param->temp_key->value, 32); + } + + // Get the system nonce + if (verify_source == VERIFY_MODE_SOURCE_MSGDIGBUF) + { + nonce = ¶m->msg_dig_buf[32]; // System nonce is the second 32 bytes of the message digest buffer + } + else + { + nonce = ¶m->msg_dig_buf[0]; // System nonce is the first 32 bytes of the message digest buffer + + } + // Calculate MAC + atcac_sw_sha2_256_init(&ctx); + atcac_sw_sha2_256_update(&ctx, param->io_key, ATCA_KEY_SIZE); // IO protection key + atcac_sw_sha2_256_update(&ctx, message, 32); // Verify message + atcac_sw_sha2_256_update(&ctx, nonce, 32); // Host (system) nonce + atcac_sw_sha2_256_update(&ctx, param->signature, 64); // Signature + + // Add Verify input parameters + input_params[0] = ATCA_VERIFY; // Verify Opcode + input_params[1] = param->mode; // Verify Mode (Param1) + input_params[2] = (uint8_t)(param->key_id >> 0); // Verify Param2 (LSB) + input_params[3] = (uint8_t)(param->key_id >> 8); // Verify Param2 (MSB) + atcac_sw_sha2_256_update(&ctx, input_params, sizeof(input_params)); + + // Calculate SHA256 to get mac + atcac_sw_sha2_256_finish(&ctx, param->mac); + + return ATCA_SUCCESS; +} +#endif + +/** \brief Encrypts the digest for the SecureBoot command when using the + * encrypted digest / validating mac option. + * + * \param[in,out] param Data required to perform the operation. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +#if ATCAH_SECUREBOOT_ENC +ATCA_STATUS atcah_secureboot_enc(atca_secureboot_enc_in_out_t* param) +{ + atcac_sha2_256_ctx ctx; + size_t i; + + // Check parameters + if (param->digest == NULL || param->temp_key == NULL || param->hashed_key == NULL || param->io_key == NULL || param->digest_enc == NULL) + { + return ATCA_BAD_PARAM; + } + + // Calculate key for encrypting digest + atcac_sw_sha2_256_init(&ctx); + atcac_sw_sha2_256_update(&ctx, param->io_key, ATCA_KEY_SIZE); + atcac_sw_sha2_256_update(&ctx, param->temp_key->value, ATCA_KEY_SIZE); + atcac_sw_sha2_256_finish(&ctx, param->hashed_key); + + // Encrypt digest (XOR with key) + for (i = 0; i < SECUREBOOT_DIGEST_SIZE; i++) + { + param->digest_enc[i] = param->digest[i] ^ param->hashed_key[i]; + } + + return ATCA_SUCCESS; +} +#endif /* ATCAH_SECUREBOOT_ENC */ + +/** \brief Calculates the expected MAC returned from the SecureBoot command + * when verification is a success. + * + * The result of this function (param->mac) should be compared with the actual + * MAC returned to validate the response. + * + * \param[in,out] param Data required to perform the operation. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +#if ATCAH_SECUREBOOT_MAC +ATCA_STATUS atcah_secureboot_mac(atca_secureboot_mac_in_out_t *param) +{ + atcac_sha2_256_ctx ctx; + uint8_t input_params[4]; + + if (param->hashed_key == NULL || param->digest == NULL || param->mac == NULL) + { + return ATCA_BAD_PARAM; + } + + // Calculate MAC + atcac_sw_sha2_256_init(&ctx); + atcac_sw_sha2_256_update(&ctx, param->hashed_key, ATCA_KEY_SIZE); + atcac_sw_sha2_256_update(&ctx, param->digest, SECUREBOOT_DIGEST_SIZE); + + // Signature is only skipped when running the SecureBoot command in + // FullStore mode and SecureBootMode from the configuration zone is set to + // FullDig + if (!((param->mode & SECUREBOOT_MODE_MASK) == SECUREBOOT_MODE_FULL_STORE && + (param->secure_boot_config & SECUREBOOTCONFIG_MODE_MASK) == SECUREBOOTCONFIG_MODE_FULL_DIG)) + { + if (param->signature == NULL) + { + return ATCA_BAD_PARAM; + } + atcac_sw_sha2_256_update(&ctx, param->signature, SECUREBOOT_SIGNATURE_SIZE); + } + + // Add SecureBoot input parameters + input_params[0] = ATCA_SECUREBOOT; // SecureBoot Opcode + input_params[1] = param->mode; // SecureBoot Mode (Param1) + input_params[2] = (uint8_t)(param->param2 >> 0); // SecureBoot Param2 (LSB) + input_params[3] = (uint8_t)(param->param2 >> 8); // SecureBoot Param2 (MSB) + atcac_sw_sha2_256_update(&ctx, input_params, sizeof(input_params)); + + atcac_sw_sha2_256_finish(&ctx, param->mac); + + return ATCA_SUCCESS; +} +#endif /* ATCAH_SECUREBOOT_MAC */ + + +/** \brief This function generates an SHA-256 digest (MAC) of a key, challenge, and other information. + + The resulting digest will match with the one generated by the device when executing a MAC command. + The TempKey (if used) should be valid (temp_key.valid = 1) before executing this function. + + * \param[in,out] param pointer to parameter structure + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +#if ATCAH_MAC +ATCA_STATUS atcah_mac(struct atca_mac_in_out *param) +{ + uint8_t temporary[ATCA_MSG_SIZE_MAC]; + uint8_t *p_temp; + struct atca_include_data_in_out include_data; + + // Initialize struct + include_data.otp = param->otp; + include_data.sn = param->sn; + include_data.mode = param->mode; + + // Check parameters + if (!param->response + || (param->mode & ~MAC_MODE_MASK) + || (!(param->mode & MAC_MODE_BLOCK1_TEMPKEY) && !param->key) + || (!(param->mode & MAC_MODE_BLOCK2_TEMPKEY) && !param->challenge) + || ((param->mode & MAC_MODE_USE_TEMPKEY_MASK) && !param->temp_key) + || (((param->mode & MAC_MODE_INCLUDE_OTP_64) || (param->mode & MAC_MODE_INCLUDE_OTP_88)) && !param->otp) + || ((param->mode & MAC_MODE_INCLUDE_SN) && !param->sn) + ) + { + return ATCA_BAD_PARAM; + } + + // Check TempKey fields validity if TempKey is used + if (((param->mode & MAC_MODE_USE_TEMPKEY_MASK) != 0) + // TempKey.CheckFlag must be 0 and TempKey.Valid must be 1 + && (param->temp_key->no_mac_flag || (param->temp_key->valid != 1) + // If either mode parameter bit 0 or bit 1 are set, mode parameter bit 2 must match temp_key.source_flag. + // Logical not (!) is used to evaluate the expression to TRUE / FALSE first before comparison (!=). + || (!(param->mode & MAC_MODE_SOURCE_FLAG_MATCH) != !(param->temp_key->source_flag))) + ) + { + // Invalidate TempKey, then return + param->temp_key->valid = 0; + return ATCA_EXECUTION_ERROR; + } + + // Start calculation + p_temp = temporary; + + // (1) first 32 bytes + memcpy(p_temp, param->mode & MAC_MODE_BLOCK1_TEMPKEY ? param->temp_key->value : param->key, ATCA_KEY_SIZE); // use Key[KeyID] + p_temp += ATCA_KEY_SIZE; + + // (2) second 32 bytes + memcpy(p_temp, param->mode & MAC_MODE_BLOCK2_TEMPKEY ? param->temp_key->value : param->challenge, ATCA_KEY_SIZE); // use Key[KeyID] + p_temp += ATCA_KEY_SIZE; + + // (3) 1 byte opcode + *p_temp++ = ATCA_MAC; + + // (4) 1 byte mode parameter + *p_temp++ = param->mode; + + // (5) 2 bytes keyID + *p_temp++ = param->key_id & 0xFF; + *p_temp++ = (param->key_id >> 8) & 0xFF; + + include_data.p_temp = p_temp; + atcah_include_data(&include_data); + + // Calculate SHA256 to get the MAC digest + atcac_sw_sha2_256(temporary, ATCA_MSG_SIZE_MAC, param->response); + + // Update TempKey fields + if (param->temp_key) + { + param->temp_key->valid = 0; + } + + return ATCA_SUCCESS; +} +#endif /* ATCAH_MAC */ + + + +/** \brief This function performs the checkmac operation to generate client response on the host side . + * \param[in,out] param Input and output parameters + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +#if ATCAH_CHECK_MAC +ATCA_STATUS atcah_check_mac(struct atca_check_mac_in_out *param) +{ + uint8_t msg[ATCA_MSG_SIZE_MAC]; + bool is_temp_key_req = false; + + // Check parameters + if (param == NULL || param->other_data == NULL || param->sn == NULL || param->client_resp == NULL) + { + return ATCA_BAD_PARAM; + } + + if ((param->mode & CHECKMAC_MODE_BLOCK1_TEMPKEY) || (param->mode & CHECKMAC_MODE_BLOCK2_TEMPKEY)) + { + is_temp_key_req = true; // Message uses TempKey + } + else if ((param->mode == 0x01 || param->mode == 0x05) && param->target_key != NULL) + { + is_temp_key_req = true; // CheckMac copy will be performed + + } + if (is_temp_key_req && param->temp_key == NULL) + { + return ATCA_BAD_PARAM; + } + if (!(param->mode & CHECKMAC_MODE_BLOCK1_TEMPKEY) && param->slot_key == NULL) + { + return ATCA_BAD_PARAM; + } + if (!(param->mode & CHECKMAC_MODE_BLOCK2_TEMPKEY) && param->client_chal == NULL) + { + return ATCA_BAD_PARAM; + } + if ((param->mode & CHECKMAC_MODE_INCLUDE_OTP_64) && param->otp == NULL) + { + return ATCA_BAD_PARAM; + } + + if ((param->mode & CHECKMAC_MODE_BLOCK1_TEMPKEY) || (param->mode & CHECKMAC_MODE_BLOCK2_TEMPKEY)) + { + // This will use TempKey in message, check validity + if (!param->temp_key->valid) + { + return ATCA_EXECUTION_ERROR; // TempKey is not valid + } + if (((param->mode >> 2) & 0x01) != param->temp_key->source_flag) + { + return ATCA_EXECUTION_ERROR; // TempKey SourceFlag doesn't match bit 2 of the mode + } + } + + // Build the message + memset(msg, 0, sizeof(msg)); + if (param->mode & CHECKMAC_MODE_BLOCK1_TEMPKEY) + { + memcpy(&msg[0], param->temp_key->value, 32); + } + else + { + memcpy(&msg[0], param->slot_key, 32); + } + if (param->mode & CHECKMAC_MODE_BLOCK2_TEMPKEY) + { + memcpy(&msg[32], param->temp_key->value, 32); + } + else + { + memcpy(&msg[32], param->client_chal, 32); + } + memcpy(&msg[64], ¶m->other_data[0], 4); + if (param->mode & CHECKMAC_MODE_INCLUDE_OTP_64) + { + memcpy(&msg[68], param->otp, 8); + } + memcpy(&msg[76], ¶m->other_data[4], 3); + msg[79] = param->sn[8]; + memcpy(&msg[80], ¶m->other_data[7], 4); + memcpy(&msg[84], ¶m->sn[0], 2); + memcpy(&msg[86], ¶m->other_data[11], 2); + + // Calculate the client response + atcac_sw_sha2_256(msg, sizeof(msg), param->client_resp); + + // Update TempKey fields + if ((param->mode == 0x01 || param->mode == 0x05) && param->target_key != NULL) + { + // CheckMac Copy will be performed + memcpy(param->temp_key->value, param->target_key, ATCA_KEY_SIZE); + param->temp_key->gen_dig_data = 0; + param->temp_key->source_flag = 1; + param->temp_key->valid = 1; + } + + return ATCA_SUCCESS; +} +#endif /* ATCAH_CHECK_MAC */ + +/** \brief This function generates an HMAC / SHA-256 hash of a key and other information. + + The resulting hash will match with the one generated in the device by an HMAC command. + The TempKey has to be valid (temp_key.valid = 1) before executing this function. + + * \param[in,out] param pointer to parameter structure + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +#if ATCAH_HMAC +ATCA_STATUS atcah_hmac(struct atca_hmac_in_out *param) +{ + // Local Variables + struct atca_include_data_in_out include_data; + uint8_t temporary[ATCA_HMAC_BLOCK_SIZE + ATCA_MSG_SIZE_HMAC]; + uint8_t i = 0; + uint8_t *p_temp = NULL; + + // Check parameters + if (!param->response || !param->key || !param->temp_key + || (param->mode & ~HMAC_MODE_MASK) + || (((param->mode & MAC_MODE_INCLUDE_OTP_64) || (param->mode & MAC_MODE_INCLUDE_OTP_88)) && !param->otp) + || (!param->sn) + ) + { + return ATCA_BAD_PARAM; + } + + // Check TempKey fields validity (TempKey is always used) + if ( // TempKey.CheckFlag must be 0 and TempKey.Valid must be 1 + param->temp_key->no_mac_flag || (param->temp_key->valid != 1) + // The mode parameter bit 2 must match temp_key.source_flag. + // Logical not (!) is used to evaluate the expression to TRUE / FALSE first before comparison (!=). + || (!(param->mode & MAC_MODE_SOURCE_FLAG_MATCH) != !(param->temp_key->source_flag)) + ) + { + // Invalidate TempKey, then return + param->temp_key->valid = 0; + return ATCA_EXECUTION_ERROR; + } + + // Start first calculation (inner) + p_temp = temporary; + + // XOR key with ipad + for (i = 0; i < ATCA_KEY_SIZE; i++) + { + *p_temp++ = param->key[i] ^ 0x36; + } + + // zero pad key out to block size + // Refer to fips-198 , length Key = 32 bytes, Block size = 512 bits = 64 bytes. + // So the Key must be padded with zeros. + memset(p_temp, 0x36, ATCA_HMAC_BLOCK_SIZE - ATCA_KEY_SIZE); + p_temp += ATCA_HMAC_BLOCK_SIZE - ATCA_KEY_SIZE; + + // Next append the stream of data 'text' + memset(p_temp, 0, ATCA_KEY_SIZE); + p_temp += ATCA_KEY_SIZE; + + memcpy(p_temp, param->temp_key->value, ATCA_KEY_SIZE); + p_temp += ATCA_KEY_SIZE; + + *p_temp++ = ATCA_HMAC; + *p_temp++ = param->mode; + *p_temp++ = (uint8_t)(param->key_id >> 0); + *p_temp++ = (uint8_t)(param->key_id >> 8); + + include_data.otp = param->otp; + include_data.sn = param->sn; + include_data.mode = param->mode; + include_data.p_temp = p_temp; + atcah_include_data(&include_data); + + // Calculate SHA256 + // H((K0^ipad):text), use param.response for temporary storage + atcac_sw_sha2_256(temporary, ATCA_HMAC_BLOCK_SIZE + ATCA_MSG_SIZE_HMAC, param->response); + + + // Start second calculation (outer) + p_temp = temporary; + + // XOR K0 with opad + for (i = 0; i < ATCA_KEY_SIZE; i++) + { + *p_temp++ = param->key[i] ^ 0x5C; + } + + // zero pad key out to block size + // Refer to fips-198 , length Key = 32 bytes, Block size = 512 bits = 64 bytes. + // So the Key must be padded with zeros. + memset(p_temp, 0x5C, ATCA_HMAC_BLOCK_SIZE - ATCA_KEY_SIZE); + p_temp += ATCA_HMAC_BLOCK_SIZE - ATCA_KEY_SIZE; + + // Append result from last calculation H((K0 ^ ipad) || text) + memcpy(p_temp, param->response, ATCA_SHA_DIGEST_SIZE); + p_temp += ATCA_SHA_DIGEST_SIZE; + + // Calculate SHA256 to get the resulting HMAC + atcac_sw_sha2_256(temporary, ATCA_HMAC_BLOCK_SIZE + ATCA_SHA_DIGEST_SIZE, param->response); + + // Update TempKey fields + param->temp_key->valid = 0; + + return ATCA_SUCCESS; +} +#endif /* ATCAH_HMAC */ + +/** \brief This function combines the current TempKey with a stored value. + + The stored value can be a data slot, OTP page, configuration zone, or hardware transport key. + The TempKey generated by this function will match with the TempKey in the device generated + when executing a GenDig command. + The TempKey should be valid (temp_key.valid = 1) before executing this function. + To use this function, an application first sends a GenDig command with a chosen stored value to the device. + This stored value must be known by the application and is passed to this GenDig calculation function. + The function calculates a new TempKey and returns it. + + * \param[in,out] param pointer to parameter structure + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +#if ATCAH_GENDIG +ATCA_STATUS atcah_gen_dig(struct atca_gen_dig_in_out *param) +{ + uint8_t temporary[ATCA_MSG_SIZE_GEN_DIG]; + uint8_t *p_temp; + + // Check parameters + if (param->sn == NULL || param->temp_key == NULL) + { + return ATCA_BAD_PARAM; + } + if ((param->zone <= GENDIG_ZONE_DATA) && (param->stored_value == NULL)) + { + return ATCA_BAD_PARAM; // Stored value cannot be null for Config,OTP and Data + } + + if ((param->zone == GENDIG_ZONE_SHARED_NONCE || (param->zone == GENDIG_ZONE_DATA && param->is_key_nomac)) && param->other_data == NULL) + { + return ATCA_BAD_PARAM; // Other data is required in these cases + } + + if (param->zone > 5) + { + return ATCA_BAD_PARAM; // Unknown zone + + } + // Start calculation + p_temp = temporary; + + + // (1) 32 bytes inputKey + if (param->zone == GENDIG_ZONE_SHARED_NONCE) + { + if (param->key_id & 0x8000) + { + memcpy(p_temp, param->temp_key->value, ATCA_KEY_SIZE); // 32 bytes TempKey + } + else + { + memcpy(p_temp, param->other_data, ATCA_KEY_SIZE); // 32 bytes other data + + } + } + else if (param->zone == GENDIG_ZONE_COUNTER || param->zone == GENDIG_ZONE_KEY_CONFIG) + { + memset(p_temp, 0x00, ATCA_KEY_SIZE); // 32 bytes of zero. + + } + else + { + memcpy(p_temp, param->stored_value, ATCA_KEY_SIZE); // 32 bytes of stored data + + } + + p_temp += ATCA_KEY_SIZE; + + + if (param->zone == GENDIG_ZONE_DATA && param->is_key_nomac) + { + // If a key has the SlotConfig.NoMac bit set, then opcode and parameters come from OtherData + memcpy(p_temp, param->other_data, 4); + p_temp += 4; + } + else + { + // (2) 1 byte Opcode + *p_temp++ = ATCA_GENDIG; + + // (3) 1 byte Param1 (zone) + *p_temp++ = param->zone; + + // (4) 1 byte LSB of Param2 (keyID) + *p_temp++ = (uint8_t)(param->key_id & 0xFF); + if (param->zone == GENDIG_ZONE_SHARED_NONCE) + { + //(4) 1 byte zero for shared nonce mode + *p_temp++ = 0; + } + else + { + //(4) 1 byte MSB of Param2 (keyID) for other modes + *p_temp++ = (uint8_t)(param->key_id >> 8); + } + } + + // (5) 1 byte SN[8] + *p_temp++ = param->sn[8]; + + // (6) 2 bytes SN[0:1] + *p_temp++ = param->sn[0]; + *p_temp++ = param->sn[1]; + + + // (7) + if (param->zone == GENDIG_ZONE_COUNTER) + { + *p_temp++ = 0; + *p_temp++ = (uint8_t)(param->counter & 0xFF); // (7) 4 bytes of counter + *p_temp++ = (uint8_t)(param->counter >> 8); + *p_temp++ = (uint8_t)(param->counter >> 16); + *p_temp++ = (uint8_t)(param->counter >> 24); + + memset(p_temp, 0x00, 20); // (7) 20 bytes of zero + p_temp += 20; + + } + else if (param->zone == GENDIG_ZONE_KEY_CONFIG) + { + *p_temp++ = 0; + *p_temp++ = param->slot_conf & 0xFF; // (7) 2 bytes of Slot config + *p_temp++ = (uint8_t)(param->slot_conf >> 8); + + *p_temp++ = param->key_conf & 0xFF; + *p_temp++ = (uint8_t)(param->key_conf >> 8); // (7) 2 bytes of key config + + *p_temp++ = param->slot_locked; // (7) 1 byte of slot locked + + memset(p_temp, 0x00, 19); // (7) 19 bytes of zero + p_temp += 19; + + + } + else + { + + memset(p_temp, 0, ATCA_GENDIG_ZEROS_SIZE); // (7) 25 zeros + p_temp += ATCA_GENDIG_ZEROS_SIZE; + + } + + + + + if (param->zone == GENDIG_ZONE_SHARED_NONCE && (param->key_id & 0x8000)) + { + memcpy(p_temp, param->other_data, ATCA_KEY_SIZE); // (8) 32 bytes OtherData + p_temp += ATCA_KEY_SIZE; + + } + else + { + memcpy(p_temp, param->temp_key->value, ATCA_KEY_SIZE); // (8) 32 bytes TempKey + p_temp += ATCA_KEY_SIZE; + + } + + + + + + // Calculate SHA256 to get the new TempKey + atcac_sw_sha2_256(temporary, (p_temp - temporary), param->temp_key->value); + + // Update TempKey fields + param->temp_key->valid = 1; + + if ((param->zone == GENDIG_ZONE_DATA) && (param->key_id <= 15)) + { + param->temp_key->gen_dig_data = 1; + param->temp_key->key_id = (param->key_id & 0xF); // mask lower 4-bit only + if (param->is_key_nomac == 1) + { + param->temp_key->no_mac_flag = 1; + } + } + else + { + param->temp_key->gen_dig_data = 0; + param->temp_key->key_id = 0; + } + + return ATCA_SUCCESS; +} +#endif /* ATCAH_GENDIG */ + +/** \brief This function generates mac with session key with a plain text. + * \param[in,out] param pointer to parameter structure + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +#if ATCAH_GEN_MAC +ATCA_STATUS atcah_gen_mac(struct atca_gen_dig_in_out *param) +{ + uint8_t temporary[ATCA_MSG_SIZE_GEN_DIG]; + uint8_t *p_temp; + + // Check parameters + if (!param->stored_value || !param->temp_key) + { + return ATCA_BAD_PARAM; + } + + // Check TempKey fields validity (TempKey is always used) + if ( // TempKey.CheckFlag must be 0 and TempKey.Valid must be 1 + param->temp_key->no_mac_flag || (param->temp_key->valid != 1) + ) + { + // Invalidate TempKey, then return + param->temp_key->valid = 0; + return ATCA_EXECUTION_ERROR; + } + + // Start calculation + p_temp = temporary; + + // (1) 32 bytes SessionKey + // (Config[KeyID] or OTP[KeyID] or Data.slot[KeyID] or TransportKey[KeyID]) + memcpy(p_temp, param->temp_key->value, ATCA_KEY_SIZE); + p_temp += ATCA_KEY_SIZE; + + // (2) 1 byte Opcode + *p_temp++ = ATCA_WRITE; + + // (3) 1 byte Param1 (zone) + *p_temp++ = param->zone; + + // (4) 2 bytes Param2 (keyID) + *p_temp++ = param->key_id & 0xFF; + *p_temp++ = (param->key_id >> 8) & 0xFF; + + // (5) 1 byte SN[8] + *p_temp++ = param->sn[8]; + + // (6) 2 bytes SN[0:1] + *p_temp++ = param->sn[0]; + *p_temp++ = param->sn[1]; + + // (7) 25 zeros + memset(p_temp, 0, ATCA_GENDIG_ZEROS_SIZE); + p_temp += ATCA_GENDIG_ZEROS_SIZE; + + // (8) 32 bytes PlainText + memcpy(p_temp, param->stored_value, ATCA_KEY_SIZE); + + // Calculate SHA256 to get the new TempKey + atcac_sw_sha2_256(temporary, ATCA_MSG_SIZE_GEN_DIG, param->temp_key->value); + + // Update TempKey fields + param->temp_key->valid = 1; + + if ((param->zone == GENDIG_ZONE_DATA) && (param->key_id <= 15)) + { + param->temp_key->gen_dig_data = 1; + param->temp_key->key_id = (param->key_id & 0xF); // mask lower 4-bit only + } + else + { + param->temp_key->gen_dig_data = 0; + param->temp_key->key_id = 0; + } + + return ATCA_SUCCESS; +} +#endif /* ATCAH_GEN_MAC */ + +/** \brief This function calculates the input MAC for the Write command. + + The Write command will need an input MAC if SlotConfig.WriteConfig.Encrypt is set. + + * \param[in,out] param pointer to parameter structure + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +#if ATCAH_WRITE_AUTH_MAC +ATCA_STATUS atcah_write_auth_mac(struct atca_write_mac_in_out *param) +{ + uint8_t mac_input[ATCA_MSG_SIZE_ENCRYPT_MAC]; + uint8_t i; + uint8_t *p_temp; + + // Check parameters + if (!param->input_data || !param->temp_key) + { + return ATCA_BAD_PARAM; + } + + // Check TempKey fields validity (TempKey is always used) + if ( // TempKey.CheckFlag must be 0 and TempKey.Valid must be 1 + param->temp_key->no_mac_flag || (param->temp_key->valid != 1) + ) + { + // Invalidate TempKey, then return + param->temp_key->valid = 0; + return ATCA_EXECUTION_ERROR; + } + // Encrypt by XOR-ing Data with the TempKey + for (i = 0; i < 32; i++) + { + param->encrypted_data[i] = param->input_data[i] ^ param->temp_key->value[i]; + } + + // If the pointer *mac is provided by the caller then calculate input MAC + if (param->auth_mac) + { + // Start calculation + p_temp = mac_input; + + // (1) 32 bytes TempKey + memcpy(p_temp, param->temp_key->value, ATCA_KEY_SIZE); + p_temp += ATCA_KEY_SIZE; + + // (2) 1 byte Opcode + *p_temp++ = ATCA_WRITE; + + // (3) 1 byte Param1 (zone) + *p_temp++ = param->zone; + + // (4) 2 bytes Param2 (keyID) + *p_temp++ = param->key_id & 0xFF; + *p_temp++ = (param->key_id >> 8) & 0xFF; + + // (5) 1 byte SN[8] + *p_temp++ = param->sn[8]; + + // (6) 2 bytes SN[0:1] + *p_temp++ = param->sn[0]; + *p_temp++ = param->sn[1]; + + // (7) 25 zeros + memset(p_temp, 0, ATCA_WRITE_MAC_ZEROS_SIZE); + p_temp += ATCA_WRITE_MAC_ZEROS_SIZE; + + // (8) 32 bytes PlainText + memcpy(p_temp, param->input_data, ATCA_KEY_SIZE); + + // Calculate SHA256 to get MAC + atcac_sw_sha2_256(mac_input, sizeof(mac_input), param->auth_mac); + } + + return ATCA_SUCCESS; +} +#endif /* ATCAH_WRITE_AUTH_MAC */ + +/** \brief This function calculates the input MAC for the PrivWrite command. + + The PrivWrite command will need an input MAC if SlotConfig.WriteConfig.Encrypt is set. + + * \param[in,out] param pointer to parameter structure + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +#if ATCAH_PRIVWRITE_AUTH_MAC +ATCA_STATUS atcah_privwrite_auth_mac(struct atca_write_mac_in_out *param) +{ + uint8_t mac_input[ATCA_MSG_SIZE_PRIVWRITE_MAC]; + uint8_t i = 0; + uint8_t *p_temp = NULL; + uint8_t session_key2[32]; + + // Check parameters + if (!param->input_data || !param->temp_key) + { + return ATCA_BAD_PARAM; + } + + // Check TempKey fields validity (TempKey is always used) + if ( // TempKey.CheckFlag must be 0 and TempKey.Valid must be 1 + param->temp_key->no_mac_flag || (param->temp_key->valid != 1) + ) + { + // Invalidate TempKey, then return + param->temp_key->valid = 0; + return ATCA_EXECUTION_ERROR; + } + + + /* Encrypt by XOR-ing Data with the TempKey + */ + + // Encrypt the next 28 bytes of the cipher text, which is the first part of the private key. + for (i = 0; i < 32; i++) + { + param->encrypted_data[i] = param->input_data[i] ^ param->temp_key->value[i]; + } + + // Calculate the new key for the last 4 bytes of the cipher text + atcac_sw_sha2_256(param->temp_key->value, 32, session_key2); + + // Encrypt the last 4 bytes of the cipher text, which is the remaining part of the private key + for (i = 32; i < 36; i++) + { + param->encrypted_data[i] = param->input_data[i] ^ session_key2[i - 32]; + } + + // If the pointer *mac is provided by the caller then calculate input MAC + if (param->auth_mac) + { + // Start calculation + p_temp = mac_input; + + // (1) 32 bytes TempKey + memcpy(p_temp, param->temp_key->value, ATCA_KEY_SIZE); + p_temp += ATCA_KEY_SIZE; + + // (2) 1 byte Opcode + *p_temp++ = ATCA_PRIVWRITE; + + // (3) 1 byte Param1 (zone) + *p_temp++ = param->zone; + + // (4) 2 bytes Param2 (keyID) + *p_temp++ = param->key_id & 0xFF; + *p_temp++ = (param->key_id >> 8) & 0xFF; + + // (5) 1 byte SN[8] + *p_temp++ = param->sn[8]; + + // (6) 2 bytes SN[0:1] + *p_temp++ = param->sn[0]; + *p_temp++ = param->sn[1]; + + // (7) 21 zeros + memset(p_temp, 0, ATCA_PRIVWRITE_MAC_ZEROS_SIZE); + p_temp += ATCA_PRIVWRITE_MAC_ZEROS_SIZE; + + // (8) 36 bytes PlainText (Private Key) + memcpy(p_temp, param->input_data, ATCA_PRIVWRITE_PLAIN_TEXT_SIZE); + + // Calculate SHA256 to get the new TempKey + atcac_sw_sha2_256(mac_input, sizeof(mac_input), param->auth_mac); + } + + return ATCA_SUCCESS; +} +#endif /* ATCAH_PRIVWRITE_AUTH_MAC */ + +/** \brief This function derives a key with a key and TempKey. + + Used in conjunction with DeriveKey command, the key derived by this function will match the key in the device. + Two kinds of operation are supported: +
    +
  • Roll Key operation: target_key and parent_key parameters should be set to point to the same location (TargetKey).
  • +
  • Create Key operation: target_key should be set to point to TargetKey, parent_key should be set to point to ParentKey.
  • +
+ After executing this function, the initial value of target_key will be overwritten with the derived key. + The TempKey should be valid (temp_key.valid = 1) before executing this function. + + * \param[in,out] param pointer to parameter structure + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +#if ATCAH_DERIVE_KEY +ATCA_STATUS atcah_derive_key(struct atca_derive_key_in_out *param) +{ + uint8_t temporary[ATCA_MSG_SIZE_DERIVE_KEY]; + uint8_t *p_temp; + + // Check parameters + if (!param->parent_key || !param->target_key || !param->temp_key + || (param->mode & ~DERIVE_KEY_RANDOM_FLAG) || (param->target_key_id > ATCA_KEY_ID_MAX)) + { + return ATCA_BAD_PARAM; + } + + + // Check TempKey fields validity (TempKey is always used) + if ( // TempKey.CheckFlag must be 0 and TempKey.Valid must be 1 + param->temp_key->no_mac_flag || (param->temp_key->valid != 1) + // The random parameter bit 2 must match temp_key.source_flag + // Logical not (!) is used to evaluate the expression to TRUE / FALSE first before comparison (!=). + || (!(param->mode & DERIVE_KEY_RANDOM_FLAG) != !(param->temp_key->source_flag)) + ) + { + // Invalidate TempKey, then return + param->temp_key->valid = 0; + return ATCA_EXECUTION_ERROR; + } + + // Start calculation + p_temp = temporary; + + // (1) 32 bytes parent key + memcpy(p_temp, param->parent_key, ATCA_KEY_SIZE); + p_temp += ATCA_KEY_SIZE; + + // (2) 1 byte Opcode + *p_temp++ = ATCA_DERIVE_KEY; + + // (3) 1 byte Param1 (random) + *p_temp++ = param->mode; + + // (4) 2 bytes Param2 (keyID) + *p_temp++ = param->target_key_id & 0xFF; + *p_temp++ = (param->target_key_id >> 8) & 0xFF; + + // (5) 1 byte SN[8] + *p_temp++ = param->sn[8]; + + // (6) 2 bytes SN[0:1] + *p_temp++ = param->sn[0]; + *p_temp++ = param->sn[1]; + + // (7) 25 zeros + memset(p_temp, 0, ATCA_DERIVE_KEY_ZEROS_SIZE); + p_temp += ATCA_DERIVE_KEY_ZEROS_SIZE; + + // (8) 32 bytes TempKey + memcpy(p_temp, param->temp_key->value, ATCA_KEY_SIZE); + p_temp += ATCA_KEY_SIZE; + + // Calculate SHA256 to get the derived key. + atcac_sw_sha2_256(temporary, ATCA_MSG_SIZE_DERIVE_KEY, param->target_key); + + // Update TempKey fields + param->temp_key->valid = 0; + + return ATCA_SUCCESS; +} +#endif /* ATCAH_DERIVE_KEY */ + +/** \brief This function calculates the input MAC for a DeriveKey command. + + The DeriveKey command will need an input MAC if SlotConfig[TargetKey].Bit15 is set. + + * \param[in,out] param pointer to parameter structure + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +#if ATCAH_DERIVE_KEY_MAC +ATCA_STATUS atcah_derive_key_mac(struct atca_derive_key_mac_in_out *param) +{ + uint8_t temporary[ATCA_MSG_SIZE_DERIVE_KEY_MAC]; + uint8_t *p_temp; + + // Check parameters + if (!param->parent_key || !param->mac || (param->mode & ~DERIVE_KEY_RANDOM_FLAG) + || (param->target_key_id > ATCA_KEY_ID_MAX)) + { + return ATCA_BAD_PARAM; + } + + // Start calculation + p_temp = temporary; + + // (1) 32 bytes parent key + memcpy(p_temp, param->parent_key, ATCA_KEY_SIZE); + p_temp += ATCA_KEY_SIZE; + + // (2) 1 byte Opcode + *p_temp++ = ATCA_DERIVE_KEY; + + // (3) 1 byte Param1 (random) + *p_temp++ = param->mode; + + // (4) 2 bytes Param2 (keyID) + *p_temp++ = param->target_key_id & 0xFF; + *p_temp++ = (param->target_key_id >> 8) & 0xFF; + + // (5) 1 byte SN[8] + *p_temp++ = param->sn[8]; + + // (6) 2 bytes SN[0:1] + *p_temp++ = param->sn[0]; + *p_temp++ = param->sn[1]; + + // Calculate SHA256 to get the input MAC for DeriveKey command + atcac_sw_sha2_256(temporary, ATCA_MSG_SIZE_DERIVE_KEY_MAC, param->mac); + + return ATCA_SUCCESS; +} +#endif /* ATCAH_DERIVE_KEY_MAC */ + +/** \brief This function decrypts 32-byte encrypted data received with the Read command. + + To use this function, first the nonce must be valid and synchronized between device and application. + The application sends a GenDig command to the Device, using a key specified by SlotConfig.ReadKey. + The device updates its TempKey. + The application then updates its own TempKey using the GenDig calculation function, using the same key. + The application sends a Read command to the device for a user zone configured with EncryptRead. + The device encrypts 32-byte zone content, and outputs it to the host. + The application passes these encrypted data to this decryption function. The function decrypts the data and returns them. + TempKey must be updated by GenDig using a ParentKey as specified by SlotConfig.ReadKey before executing this function. + The decryption function does not check whether the TempKey has been generated by a correct ParentKey for the corresponding zone. + Therefore to get a correct result, the application has to make sure that prior GenDig calculation was done using correct ParentKey. + + * \param[in,out] param pointer to parameter structure + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +#if ATCAH_DECRYPT +ATCA_STATUS atcah_decrypt(struct atca_decrypt_in_out *param) +{ + uint8_t i; + + // Check parameters + if (!param->crypto_data || !param->temp_key) + { + return ATCA_BAD_PARAM; + } + + // Check TempKey fields validity + // Note that if temp_key.key_id is not checked, + // we cannot make sure if the key used in previous GenDig IS equal to + // the key pointed by SlotConfig.ReadKey in the device. + if ( // TempKey.CheckFlag must be 0 + param->temp_key->no_mac_flag + // TempKey.Valid must be 1 + || (param->temp_key->valid != 1) + // TempKey.GenData must be 1 + || (param->temp_key->gen_dig_data != 1) + // TempKey.SourceFlag must be 0 (random) + || param->temp_key->source_flag + ) + { + // Invalidate TempKey, then return + param->temp_key->valid = 0; + return ATCA_EXECUTION_ERROR; + } + + // Decrypt by XOR-ing Data with the TempKey + for (i = 0; i < ATCA_KEY_SIZE; i++) + { + param->crypto_data[i] ^= param->temp_key->value[i]; + } + + // Update TempKey fields + param->temp_key->valid = 0; + + return ATCA_SUCCESS; +} +#endif /* ATCAH_DECRYPT */ + +/** \brief This function creates a SHA256 digest on a little-endian system. + * + * \param[in] len byte length of message + * \param[in] message pointer to message + * \param[out] digest SHA256 of message + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +#if ATCAH_SHA256 +ATCA_STATUS atcah_sha256(int32_t len, const uint8_t *message, uint8_t *digest) +{ + return (ATCA_STATUS)atcac_sw_sha2_256(message, len, digest); +} +#endif /* ATCAH_SHA256 */ + +/** \brief Calculate the PubKey digest created by GenKey and saved to TempKey. + * + * \param[in,out] param GenKey parameters required to calculate the PubKey + * digest. Digest is return in the temp_key parameter. + * + \return ATCA_SUCCESS on success, otherwise an error code. + */ +#if ATCAH_GEN_KEY_MSG +ATCA_STATUS atcah_gen_key_msg(struct atca_gen_key_in_out *param) +{ + uint8_t msg[128]; + + if (param == NULL || param->public_key == NULL || param->sn == NULL || param->temp_key == NULL) + { + return ATCA_BAD_PARAM; + } + if (param->public_key_size == 0 || param->public_key_size > 88) + { + return ATCA_BAD_PARAM; + } + + memset(msg, 0, sizeof(msg)); + memcpy(&msg[0], param->temp_key->value, 32); + msg[32] = ATCA_GENKEY; + + if (param->mode & GENKEY_MODE_PUBKEY_DIGEST) + { + // Calculate PubKey digest of stored public key, takes priority over other bits + if (param->other_data == NULL) + { + return ATCA_BAD_PARAM; + } + memcpy(&msg[33], param->other_data, 3); // OtherData replaces mode and key_id in message + } + else if (param->mode & GENKEY_MODE_DIGEST) + { + msg[33] = param->mode; + msg[34] = (uint8_t)(param->key_id >> 0); + msg[35] = (uint8_t)(param->key_id >> 8); + } + else + { + // Mode indicates no PubKey digest was requested. + // No change to TempKey. + return ATCA_SUCCESS; + } + + msg[36] = param->sn[8]; + memcpy(&msg[37], ¶m->sn[0], 2); + + // Copy public key into end of message + memcpy(&msg[sizeof(msg) - param->public_key_size], param->public_key, param->public_key_size); + + atcac_sw_sha2_256(msg, sizeof(msg), param->temp_key->value); + param->temp_key->gen_dig_data = 0; + param->temp_key->gen_key_data = 1; + param->temp_key->key_id = param->key_id; + + return ATCA_SUCCESS; +} +#endif /* ATCAH_GEN_KEY_MSG */ + +/** \brief Populate the slot_config, key_config, and is_slot_locked fields in + * the atca_sign_internal_in_out structure from the provided config + * zone. + * + * The atca_sign_internal_in_out structure has a number of fields + * (slot_config, key_config, is_slot_locked) that can be determined + * automatically from the current state of TempKey and the full config + * zone. + * + * \param[in,out] param Sign(Internal) parameters to be filled out. Only + * slot_config, key_config, and is_slot_locked will be + * set. + * \param[in] device_type The type of the device. + * \param[in] config Full 128 byte config zone for the device. + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +#if ATCAH_CONFIG_TO_SIGN_INTERNAL +ATCA_STATUS atcah_config_to_sign_internal(ATCADeviceType device_type, struct atca_sign_internal_in_out *param, const uint8_t* config) +{ + const uint8_t* value = NULL; + uint16_t slot_locked = 0; + + if (param == NULL || config == NULL || param->temp_key == NULL) + { + return ATCA_BAD_PARAM; + } + + // SlotConfig[TempKeyFlags.keyId] + value = &config[20 + param->temp_key->key_id * 2]; + param->slot_config = (uint16_t)value[0] | ((uint16_t)value[1] << 8); + + // KeyConfig[TempKeyFlags.keyId] + value = &config[96 + param->temp_key->key_id * 2]; + param->key_config = (uint16_t)value[0] | ((uint16_t)value[1] << 8); + + if (device_type == ATECC108A && param->temp_key->key_id < 8) + { + value = &config[52 + param->temp_key->key_id * 2]; + param->use_flag = value[0]; + param->update_count = value[1]; + } + else + { + param->use_flag = 0x00; + param->update_count = 0x00; + } + + //SlotLocked:TempKeyFlags.keyId + slot_locked = (uint16_t)config[88] | ((uint16_t)config[89] << 8); + param->is_slot_locked = (slot_locked & (1 << param->temp_key->key_id)) ? false : true; + + return ATCA_SUCCESS; +} +#endif /* ATCAH_CONFIG_TO_SIGN_INTERNAL */ + +/** \brief Builds the full message that would be signed by the Sign(Internal) + * command. + * + * Additionally, the function will optionally output the OtherData data + * required by the Verify(In/Validate) command as well as the SHA256 digest of + * the full message. + * + * \param[out] device_type Device type to perform the calculation for. + * \param[out] param Input data and output buffers required. + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +#if ATCAH_SIGN_INTERNAL_MSG +ATCA_STATUS atcah_sign_internal_msg(ATCADeviceType device_type, struct atca_sign_internal_in_out *param) +{ + uint8_t msg[55]; + + if (param == NULL || param->temp_key == NULL || param->sn == NULL) + { + return ATCA_BAD_PARAM; + } + + memset(msg, 0, sizeof(msg)); + memcpy(&msg[0], param->temp_key->value, 32); + msg[32] = ATCA_SIGN; // Sign OpCode + msg[33] = param->mode; // Sign Mode + msg[34] = (uint8_t)(param->key_id >> 0); // Sign KeyID + msg[35] = (uint8_t)(param->key_id >> 8); + msg[36] = (uint8_t)(param->slot_config >> 0); // SlotConfig[TempKeyFlags.keyId] + msg[37] = (uint8_t)(param->slot_config >> 8); + msg[38] = (uint8_t)(param->key_config >> 0); // KeyConfig[TempKeyFlags.keyId] + msg[39] = (uint8_t)(param->key_config >> 8); + + //TempKeyFlags (b0-3: keyId, b4: sourceFlag, b5: GenDigData, b6: GenKeyData, b7: NoMacFlag) + msg[40] |= ((param->temp_key->key_id & 0x0F) << 0); + msg[40] |= ((param->temp_key->source_flag & 0x01) << 4); + msg[40] |= ((param->temp_key->gen_dig_data & 0x01) << 5); + msg[40] |= ((param->temp_key->gen_key_data & 0x01) << 6); + msg[40] |= ((param->temp_key->no_mac_flag & 0x01) << 7); + + if (device_type == ATECC108A && param->temp_key->key_id < 8) + { + msg[41] = param->use_flag; // UseFlag[TempKeyFlags.keyId] + msg[42] = param->update_count; // UpdateCount[TempKeyFlags.keyId] + } + else + { + msg[41] = 0x00; + msg[42] = 0x00; + } + + // Serial Number + msg[43] = param->sn[8]; + memcpy(&msg[48], ¶m->sn[0], 2); + if (param->mode & SIGN_MODE_INCLUDE_SN) + { + memcpy(&msg[44], ¶m->sn[4], 4); + memcpy(&msg[50], ¶m->sn[2], 2); + } + + // The bit within the SlotLocked field corresponding to the last key used in the TempKey computation is in the LSB + msg[52] = param->is_slot_locked ? 0x00 : 0x01; + + // If the slot contains a public key corresponding to a supported curve, and if PubInfo indicates this key must be + // validated before being used by Verify, and if the validity bits have a value of 0x05, then the PubKey Valid byte + // will be 0x01. In all other cases, it will be 0. + msg[53] = param->for_invalidate ? 0x01 : 0x00; + + msg[54] = 0x00; + + if (param->message) + { + memcpy(param->message, msg, sizeof(msg)); + } + if (param->verify_other_data) + { + memcpy(¶m->verify_other_data[0], &msg[33], 10); + memcpy(¶m->verify_other_data[10], &msg[44], 4); + memcpy(¶m->verify_other_data[14], &msg[50], 5); + } + if (param->digest) + { + return (ATCA_STATUS)atcac_sw_sha2_256(msg, sizeof(msg), param->digest); + } + else + { + return ATCA_SUCCESS; + } +} +#endif /* ATCAH_SIGN_INTERNAL_MSG */ + +/** \brief Builds the counter match value that needs to be stored in a slot. + * + * \param[in] counter_value Counter value to be used for the counter + * match. This must be a multiple of 32. + * \param[out] counter_match_value Data to be stored in the beginning of a + * counter match slot will be returned here + * (8 bytes). + * + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +#if ATCAH_ENCODE_COUNTER_MATCH +ATCA_STATUS atcah_encode_counter_match(uint32_t counter_value, uint8_t * counter_match_value) +{ + if ((counter_value > COUNTER_MAX_VALUE) || (counter_value % 32 != 0) || (counter_match_value == NULL)) + { + return ATCA_BAD_PARAM; + } + + // Counter match value is stored in little-endian unsigned format + counter_match_value[0] = (uint8_t)((counter_value >> 0) & 0xFF); + counter_match_value[1] = (uint8_t)((counter_value >> 8) & 0xFF); + counter_match_value[2] = (uint8_t)((counter_value >> 16) & 0xFF); + counter_match_value[3] = (uint8_t)((counter_value >> 24) & 0xFF); + + // Counter match value should be repeated in the next 4 bytes + memcpy(counter_match_value + 4, counter_match_value, 4); + + return ATCA_SUCCESS; +} +#endif /* ATCAH_ENCODE_COUNTER_MATCAH */ + +/** \brief This function calculates the input MAC for the ECC204 Write command. + + The Write command will need an input MAC if SlotConfig3.bit0 is set. + + * \param[in,out] param pointer to parameter structure + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +#if ATCAH_WRITE_AUTH_MAC +ATCA_STATUS atcah_ecc204_write_auth_mac(struct atca_write_mac_in_out *param) +{ + uint8_t mac_input[ATCA_MSG_SIZE_ENCRYPT_MAC]; + uint8_t i; + uint8_t *p_temp; + + // Check parameters + if ((NULL == param->input_data) || (NULL == param->temp_key)) + { + return ATCA_BAD_PARAM; + } + + // Encrypt by XOR-ing Data with the session key + for (i = 0; i < 32; i++) + { + param->encrypted_data[i] = param->input_data[i] ^ param->temp_key->value[i]; + } + + // If the pointer *mac is provided by the caller then calculate input MAC + if (param->auth_mac) + { + // Start calculation + p_temp = mac_input; + + // (1) 32 bytes TempKey + memcpy(p_temp, param->temp_key->value, ATCA_KEY_SIZE); + p_temp += ATCA_KEY_SIZE; + + // (2) 1 byte Opcode + *p_temp++ = ATCA_WRITE; + + // (3) 1 byte Param1 (zone) + *p_temp++ = param->zone; + + // (4) 2 bytes Param2 (keyID) + *p_temp++ = (param->key_id >> 8) & 0xFF; + *p_temp++ = param->key_id & 0xFF; + + // (5) 1 byte SN[8] + *p_temp++ = param->sn[8]; + + // (6) 2 bytes SN[0:1] + *p_temp++ = param->sn[0]; + *p_temp++ = param->sn[1]; + + // (7) 25 zeros + memset(p_temp, 0, ATCA_WRITE_MAC_ZEROS_SIZE); + p_temp += ATCA_WRITE_MAC_ZEROS_SIZE; + + // (8) 32 bytes PlainText + memcpy(p_temp, param->input_data, ATCA_KEY_SIZE); + + // Calculate SHA256 to get MAC + atcac_sw_sha2_256(mac_input, sizeof(mac_input), param->auth_mac); + } + + return ATCA_SUCCESS; +} +#endif /* ATCAH_WRITE_AUTH_MAC */ + +/** \brief This function calculates the session key for the ECC204. + * + * \param[in,out] param pointer to parameter structure + * \return ATCA_SUCCESS on success, otherwise an error code. + */ +#if ATCAH_GEN_SESSION_KEY +ATCA_STATUS atcah_gen_session_key(struct atca_session_key_in_out *param) +{ + uint8_t session_key_input[ATCA_MSG_SIZE_SESSION_KEY]; + uint8_t *p_temp; + + if ((NULL == param->transport_key) || (NULL == param->nonce) || (NULL == param->session_key)) + { + return ATCA_BAD_PARAM; + } + + p_temp = session_key_input; + + // (1) 32 bytes of transport key + memcpy(p_temp, param->transport_key, ATCA_KEY_SIZE); + p_temp += ATCA_KEY_SIZE; + + // (2) 0x15 + *p_temp++ = 0x15; + + // (3) 0x00 + *p_temp++ = 0x00; + + // (4) 2bytes of transport key id + *p_temp++ = param->transport_key_id & 0xFF; + *p_temp++ = (param->transport_key_id >> 8) & 0xFF; + + // (5) 1 byte SN[8] + *p_temp++ = param->sn[8]; + + // (6) 2 bytes SN[0:1] + *p_temp++ = param->sn[0]; + *p_temp++ = param->sn[1]; + + // (7) 25 zeros + memset(p_temp, 0, ATCA_WRITE_MAC_ZEROS_SIZE); + p_temp += ATCA_WRITE_MAC_ZEROS_SIZE; + + // (8) 32 bytes nonce + memcpy(p_temp, param->nonce, 32); + + // Calculate SHA256 to get MAC + atcac_sw_sha2_256(session_key_input, sizeof(session_key_input), param->session_key); + + return ATCA_SUCCESS; +} +#endif /* ATCAH_GEN_SESSION_KEY */ + +#endif \ No newline at end of file diff --git a/drivers/ecc108a/cryptoauthlib/host/atca_host.h b/drivers/ecc108a/cryptoauthlib/host/atca_host.h new file mode 100644 index 0000000..2cfcc95 --- /dev/null +++ b/drivers/ecc108a/cryptoauthlib/host/atca_host.h @@ -0,0 +1,465 @@ +/** + * \file + * \brief Definitions and Prototypes for ATCA Utility Functions + * + * \copyright (c) 2015-2020 Microchip Technology Inc. and its subsidiaries. + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip software + * and any derivatives exclusively with Microchip products. It is your + * responsibility to comply with third party license terms applicable to your + * use of third party software (including open source software) that may + * accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER + * EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, + * SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE + * OF ANY KIND WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF + * MICROCHIP HAS BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE + * FORESEEABLE. TO THE FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL + * LIABILITY ON ALL CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED + * THE AMOUNT OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR + * THIS SOFTWARE. + */ + + +#ifndef ATCA_HOST_H +#define ATCA_HOST_H + +#include +#include "cryptoauthlib.h" // contains definitions used by chip and these routines +#include "calib/calib_basic.h" + +/** \defgroup atcah Host side crypto methods (atcah_) + * + * \brief + * Use these functions if your system does not use an ATCADevice as a host but + * implements the host in firmware. The functions provide host-side cryptographic functionality + * for an ATECC client device. They are intended to accompany the CryptoAuthLib functions. + * They can be called directly from an application, or integrated into an API. + * + * Modern compilers can garbage-collect unused functions. If your compiler does not support this feature, + * you can just discard this module from your project if you do use an ATECC as a host. Or, if you don't, + * delete the functions you do not use. + @{ */ + +/** \name Definitions for ATECC Message Sizes to Calculate a SHA256 Hash + + * \brief "||" is the concatenation operator. + * The number in braces is the length of the hash input value in bytes. + @{ */ + +//! RandOut{32} || NumIn{20} || OpCode{1} || Mode{1} || LSB of Param2{1} +#define ATCA_MSG_SIZE_NONCE (55) + + +/** \brief (Key or TempKey){32} || (Challenge or TempKey){32} || OpCode{1} || Mode{1} || Param2{2} +|| (OTP0_7 or 0){8} || (OTP8_10 or 0){3} || SN8{1} || (SN4_7 or 0){4} || SN0_1{2} || (SN2_3 or 0){2} +*/ +#define ATCA_MSG_SIZE_MAC (88) +#define ATCA_MSG_SIZE_HMAC (88) + +//! KeyId{32} || OpCode{1} || Param1{1} || Param2{2} || SN8{1} || SN0_1{2} || 0{25} || TempKey{32} +#define ATCA_MSG_SIZE_GEN_DIG (96) + + +//! KeyId{32} || OpCode{1} || Param1{1} || Param2{2} || SN8{1} || SN0_1{2} || 0{25} || TempKey{32} +#define ATCA_MSG_SIZE_DERIVE_KEY (96) + + +//! KeyId{32} || OpCode{1} || Param1{1} || Param2{2} || SN8{1} || SN0_1{2} +#define ATCA_MSG_SIZE_DERIVE_KEY_MAC (39) + +//! KeyId{32} || OpCode{1} || Param1{1} || Param2{2}|| SN8{1} || SN0_1{2} || 0{25} || TempKey{32} +#define ATCA_MSG_SIZE_ENCRYPT_MAC (96) + +//! TransportKey{32} || 0x15{1} || 0x00{1} || KeyId{2} || SN8{1} || SN0_1{2} || 0{25} || Nonce{32} +#define ATCA_MSG_SIZE_SESSION_KEY (96) + +//! KeyId{32} || OpCode{1} || Param1{1} || Param2{2}|| SN8{1} || SN0_1{2} || 0{21} || PlainText{36} +#define ATCA_MSG_SIZE_PRIVWRITE_MAC (96) + +#define ATCA_COMMAND_HEADER_SIZE ( 4) +#define ATCA_GENDIG_ZEROS_SIZE (25) +#define ATCA_WRITE_MAC_ZEROS_SIZE (25) +#define ATCA_PRIVWRITE_MAC_ZEROS_SIZE (21) +#define ATCA_PRIVWRITE_PLAIN_TEXT_SIZE (36) +#define ATCA_DERIVE_KEY_ZEROS_SIZE (25) +#define ATCA_HMAC_BLOCK_SIZE (64) +#define ENCRYPTION_KEY_SIZE (64) + +/** @} */ + +/** \name Default Fixed Byte Values of Serial Number (SN[0:1] and SN[8]) + @{ */ +#define ATCA_SN_0_DEF (0x01) +#define ATCA_SN_1_DEF (0x23) +#define ATCA_SN_8_DEF (0xEE) +/** @} */ + + +/** \name Definition for TempKey Mode + @{ */ +//! mode mask for MAC command when using TempKey +#define MAC_MODE_USE_TEMPKEY_MASK ((uint8_t)0x03) +/** @} */ + +/** \brief Structure to hold TempKey fields + */ +typedef struct atca_temp_key +{ + uint8_t value[ATCA_KEY_SIZE * 2]; //!< Value of TempKey (64 bytes for ATECC608 only) + unsigned key_id : 4; //!< If TempKey was derived from a slot or transport key (GenDig or GenKey), that key ID is saved here. + unsigned source_flag : 1; //!< Indicates id TempKey started from a random nonce (0) or not (1). + unsigned gen_dig_data : 1; //!< TempKey was derived from the GenDig command. + unsigned gen_key_data : 1; //!< TempKey was derived from the GenKey command (ATECC devices only). + unsigned no_mac_flag : 1; //!< TempKey was derived from a key that has the NoMac bit set preventing the use of the MAC command. Known as CheckFlag in ATSHA devices). + unsigned valid : 1; //!< TempKey is valid. + uint8_t is_64; //!< TempKey has 64 bytes of valid data +} atca_temp_key_t; + + +/** \struct atca_include_data_in_out + * \brief Input / output parameters for function atca_include_data(). + * \var atca_include_data_in_out::p_temp + * \brief [out] pointer to output buffer + * \var atca_include_data_in_out::otp + * \brief [in] pointer to one-time-programming data + * \var atca_include_data_in_out::sn + * \brief [in] pointer to serial number data + */ +struct atca_include_data_in_out +{ + uint8_t * p_temp; + const uint8_t *otp; + const uint8_t *sn; + uint8_t mode; +}; + + +/** \struct atca_nonce_in_out + * \brief Input/output parameters for function atca_nonce(). + * \var atca_nonce_in_out::mode + * \brief [in] Mode parameter used in Nonce command (Param1). + * \var atca_nonce_in_out::zero + * \brief [in] Zero parameter used in Nonce command (Param2). + * \var atca_nonce_in_out::num_in + * \brief [in] Pointer to 20-byte NumIn data used in Nonce command. + * \var atca_nonce_in_out::rand_out + * \brief [in] Pointer to 32-byte RandOut data from Nonce command. + * \var atca_nonce_in_out::temp_key + * \brief [in,out] Pointer to TempKey structure. + */ +typedef struct atca_nonce_in_out +{ + uint8_t mode; + uint16_t zero; + const uint8_t * num_in; + const uint8_t * rand_out; + struct atca_temp_key *temp_key; +} atca_nonce_in_out_t; + + +typedef struct atca_io_decrypt_in_out +{ + const uint8_t* io_key; //!< IO protection key (32 bytes). + const uint8_t* out_nonce; //!< OutNonce returned from command (32 bytes). + uint8_t* data; //!< As input, encrypted data. As output, decrypted data. + size_t data_size; //!< Size of data in bytes (32 or 64). +} atca_io_decrypt_in_out_t; + +typedef struct atca_verify_mac +{ + uint8_t mode; //!< Mode (Param1) parameter used in Verify command. + uint16_t key_id; //!< KeyID (Param2) used in Verify command. + const uint8_t* signature; //!< Signature used in Verify command (64 bytes). + const uint8_t* other_data; //!< OtherData used in Verify command (19 bytes). + const uint8_t* msg_dig_buf; //!< Message digest buffer (64 bytes). + const uint8_t* io_key; //!< IO protection key value (32 bytes). + const uint8_t* sn; //!< Serial number (9 bytes). + const atca_temp_key_t* temp_key; //!< TempKey + uint8_t* mac; //!< Calculated verification MAC is returned here (32 bytes). +} atca_verify_mac_in_out_t; + + +typedef struct atca_secureboot_enc_in_out +{ + const uint8_t* io_key; //!< IO protection key value (32 bytes) + const struct atca_temp_key* temp_key; //!< Current value of TempKey + const uint8_t* digest; //!< Plaintext digest as input + uint8_t* hashed_key; //!< Calculated key is returned here (32 bytes) + uint8_t* digest_enc; //!< Encrypted (ciphertext) digest is return here (32 bytes) +} atca_secureboot_enc_in_out_t; + + +typedef struct atca_secureboot_mac_in_out +{ + uint8_t mode; //!< SecureBoot mode (param1) + uint16_t param2; //!< SecureBoot param2 + uint16_t secure_boot_config; //!< SecureBootConfig value from configuration zone + const uint8_t* hashed_key; //!< Hashed key. SHA256(IO Protection Key | TempKey) + const uint8_t* digest; //!< Digest (unencrypted) + const uint8_t* signature; //!< Signature (can be NULL if not required) + uint8_t* mac; //!< MAC is returned here +} atca_secureboot_mac_in_out_t; + +/** \struct atca_mac_in_out + * \brief Input/output parameters for function atca_mac(). + * \var atca_mac_in_out::mode + * \brief [in] Mode parameter used in MAC command (Param1). + * \var atca_mac_in_out::key_id + * \brief [in] KeyID parameter used in MAC command (Param2). + * \var atca_mac_in_out::challenge + * \brief [in] Pointer to 32-byte Challenge data used in MAC command, depending on mode. + * \var atca_mac_in_out::key + * \brief [in] Pointer to 32-byte key used to generate MAC digest. + * \var atca_mac_in_out::otp + * \brief [in] Pointer to 11-byte OTP, optionally included in MAC digest, depending on mode. + * \var atca_mac_in_out::sn + * \brief [in] Pointer to 9-byte SN, optionally included in MAC digest, depending on mode. + * \var atca_mac_in_out::response + * \brief [out] Pointer to 32-byte SHA-256 digest (MAC). + * \var atca_mac_in_out::temp_key + * \brief [in,out] Pointer to TempKey structure. + */ + + + +typedef struct atca_mac_in_out +{ + uint8_t mode; + uint16_t key_id; + const uint8_t * challenge; + const uint8_t * key; + const uint8_t * otp; + const uint8_t * sn; + uint8_t * response; + struct atca_temp_key *temp_key; +} atca_mac_in_out_t; + + +/** \struct atca_hmac_in_out + * \brief Input/output parameters for function atca_hmac(). + * \var atca_hmac_in_out::mode + * \brief [in] Mode parameter used in HMAC command (Param1). + * \var atca_hmac_in_out::key_id + * \brief [in] KeyID parameter used in HMAC command (Param2). + * \var atca_hmac_in_out::key + * \brief [in] Pointer to 32-byte key used to generate HMAC digest. + * \var atca_hmac_in_out::otp + * \brief [in] Pointer to 11-byte OTP, optionally included in HMAC digest, depending on mode. + * \var atca_hmac_in_out::sn + * \brief [in] Pointer to 9-byte SN, optionally included in HMAC digest, depending on mode. + * \var atca_hmac_in_out::response + * \brief [out] Pointer to 32-byte SHA-256 HMAC digest. + * \var atca_hmac_in_out::temp_key + * \brief [in,out] Pointer to TempKey structure. + */ +struct atca_hmac_in_out +{ + uint8_t mode; + uint16_t key_id; + const uint8_t * key; + const uint8_t * otp; + const uint8_t * sn; + uint8_t * response; + struct atca_temp_key *temp_key; +}; + + +/** + * \brief Input/output parameters for function atcah_gen_dig(). + */ +typedef struct atca_gen_dig_in_out +{ + uint8_t zone; //!< [in] Zone/Param1 for the GenDig command + uint16_t key_id; //!< [in] KeyId/Param2 for the GenDig command + uint16_t slot_conf; //!< [in] Slot config for the GenDig command + uint16_t key_conf; //!< [in] Key config for the GenDig command + uint8_t slot_locked; //!< [in] slot locked for the GenDig command + uint32_t counter; //!< [in] counter for the GenDig command + bool is_key_nomac; //!< [in] Set to true if the slot pointed to be key_id has the SotConfig.NoMac bit set + const uint8_t * sn; //!< [in] Device serial number SN[0:8]. Only SN[0:1] and SN[8] are required though. + const uint8_t * stored_value; //!< [in] 32-byte slot value, config block, OTP block as specified by the Zone/KeyId parameters + const uint8_t * other_data; //!< [in] 32-byte value for shared nonce zone, 4-byte value if is_key_nomac is true, ignored and/or NULL otherwise + struct atca_temp_key *temp_key; //!< [inout] Current state of TempKey +} atca_gen_dig_in_out_t; + +/** + * \brief Input/output parameters for function atcah_write_auth_mac() and atcah_privwrite_auth_mac(). + */ +typedef struct atca_write_mac_in_out +{ + uint8_t zone; //!< Zone/Param1 for the Write or PrivWrite command + uint16_t key_id; //!< KeyID/Param2 for the Write or PrivWrite command + const uint8_t * sn; //!< Device serial number SN[0:8]. Only SN[0:1] and SN[8] are required though. + const uint8_t * input_data; //!< Data to be encrypted. 32 bytes for Write command, 36 bytes for PrivWrite command. + uint8_t * encrypted_data; //!< Encrypted version of input_data will be returned here. 32 bytes for Write command, 36 bytes for PrivWrite command. + uint8_t * auth_mac; //!< Write MAC will be returned here. 32 bytes. + struct atca_temp_key *temp_key; //!< Current state of TempKey. +} atca_write_mac_in_out_t; + +/** + * \brief Input/output parameters for function atcah_derive_key(). + */ +struct atca_derive_key_in_out +{ + uint8_t mode; //!< Mode (param 1) of the derive key command + uint16_t target_key_id; //!< Key ID (param 2) of the target slot to run the command on + const uint8_t * sn; //!< Device serial number SN[0:8]. Only SN[0:1] and SN[8] are required though. + const uint8_t * parent_key; //!< Parent key to be used in the derive key calculation (32 bytes). + uint8_t * target_key; //!< Derived key will be returned here (32 bytes). + struct atca_temp_key *temp_key; //!< Current state of TempKey. +}; + + +/** + * \brief Input/output parameters for function atcah_derive_key_mac(). + */ +struct atca_derive_key_mac_in_out +{ + uint8_t mode; //!< Mode (param 1) of the derive key command + uint16_t target_key_id; //!< Key ID (param 2) of the target slot to run the command on + const uint8_t *sn; //!< Device serial number SN[0:8]. Only SN[0:1] and SN[8] are required though. + const uint8_t *parent_key; //!< Parent key to be used in the derive key calculation (32 bytes). + uint8_t * mac; //!< DeriveKey MAC will be returned here. +}; + + +/** \struct atca_decrypt_in_out + * \brief Input/output parameters for function atca_decrypt(). + * \var atca_decrypt_in_out::crypto_data + * \brief [in,out] Pointer to 32-byte data. Input encrypted data from Read command (Contents field), output decrypted. + * \var atca_decrypt_in_out::temp_key + * \brief [in,out] Pointer to TempKey structure. + */ +struct atca_decrypt_in_out +{ + uint8_t * crypto_data; + struct atca_temp_key *temp_key; +}; + + +/** \brief Input/output parameters for function atcah_check_mac(). + */ +typedef struct atca_check_mac_in_out +{ + uint8_t mode; //!< [in] CheckMac command Mode + uint16_t key_id; //!< [in] CheckMac command KeyID + const uint8_t *sn; //!< [in] Device serial number SN[0:8]. Only SN[0:1] and SN[8] are required though. + const uint8_t *client_chal; //!< [in] ClientChal data, 32 bytes. Can be NULL if mode[0] is 1. + uint8_t * client_resp; //!< [out] Calculated ClientResp will be returned here. + const uint8_t *other_data; //!< [in] OtherData, 13 bytes + const uint8_t *otp; //!< [in] First 8 bytes of the OTP zone data. Can be NULL is mode[5] is 0. + const uint8_t *slot_key; //!< [in] 32 byte key value in the slot specified by slot_id. Can be NULL if mode[1] is 1. + /// [in] If this is not NULL, it assumes CheckMac copy is enabled for the specified key_id (ReadKey=0). If key_id + /// is even, this should be the 32-byte key value for the slot key_id+1, otherwise this should be set to slot_key. + const uint8_t * target_key; + struct atca_temp_key *temp_key; //!< [in,out] Current state of TempKey. Required if mode[0] or mode[1] are 1. +} atca_check_mac_in_out_t; + + +/** \struct atca_verify_in_out + * \brief Input/output parameters for function atcah_verify(). + * \var atca_verify_in_out::curve_type + * \brief [in] Curve type used in Verify command (Param2). + * \var atca_verify_in_out::signature + * \brief [in] Pointer to ECDSA signature to be verified + * \var atca_verify_in_out::public_key + * \brief [in] Pointer to the public key to be used for verification + * \var atca_verify_in_out::temp_key + * \brief [in,out] Pointer to TempKey structure. + */ +typedef struct atca_verify_in_out +{ + uint16_t curve_type; + const uint8_t * signature; + const uint8_t * public_key; + struct atca_temp_key *temp_key; +} atca_verify_in_out_t; + +/** \brief Input/output parameters for calculating the PubKey digest put into + * TempKey by the GenKey command with the + * atcah_gen_key_msg() function. + */ +typedef struct atca_gen_key_in_out +{ + uint8_t mode; //!< [in] GenKey Mode + uint16_t key_id; //!< [in] GenKey KeyID + const uint8_t * public_key; //!< [in] Public key to be used in the PubKey digest. X and Y integers in big-endian format. 64 bytes for P256 curve. + size_t public_key_size; //!< [in] Total number of bytes in the public key. 64 bytes for P256 curve. + const uint8_t * other_data; //!< [in] 3 bytes required when bit 4 of the mode is set. Can be NULL otherwise. + const uint8_t * sn; //!< [in] Device serial number SN[0:8] (9 bytes). Only SN[0:1] and SN[8] are required though. + struct atca_temp_key *temp_key; //!< [in,out] As input the current state of TempKey. As output, the resulting PubKEy digest. +} atca_gen_key_in_out_t; + +/** \brief Input/output parameters for calculating the message and digest used + * by the Sign(internal) command. Used with the + * atcah_sign_internal_msg() function. + */ +typedef struct atca_sign_internal_in_out +{ + uint8_t mode; //!< [in] Sign Mode + uint16_t key_id; //!< [in] Sign KeyID + uint16_t slot_config; //!< [in] SlotConfig[TempKeyFlags.keyId] + uint16_t key_config; //!< [in] KeyConfig[TempKeyFlags.keyId] + uint8_t use_flag; //!< [in] UseFlag[TempKeyFlags.keyId], 0x00 for slots 8 and above and for ATECC508A + uint8_t update_count; //!< [in] UpdateCount[TempKeyFlags.keyId], 0x00 for slots 8 and above and for ATECC508A + bool is_slot_locked; //!< [in] Is TempKeyFlags.keyId slot locked. + bool for_invalidate; //!< [in] Set to true if this will be used for the Verify(Invalidate) command. + const uint8_t * sn; //!< [in] Device serial number SN[0:8] (9 bytes) + const struct atca_temp_key *temp_key; //!< [in] The current state of TempKey. + uint8_t* message; //!< [out] Full 55 byte message the Sign(internal) command will build. Can be NULL if not required. + uint8_t* verify_other_data; //!< [out] The 19 byte OtherData bytes to be used with the Verify(In/Validate) command. Can be NULL if not required. + uint8_t* digest; //!< [out] SHA256 digest of the full 55 byte message. Can be NULL if not required. +} atca_sign_internal_in_out_t; + +/** \brief Input/Output paramters for calculating the session key + * by the nonce command. Used with the atcah_gen_session_key() function. + */ +typedef struct atca_session_key_in_out +{ + uint8_t* transport_key; + uint16_t transport_key_id; + const uint8_t* sn; + uint8_t* nonce; + uint8_t* session_key; +}atca_session_key_in_out_t; + +#ifdef __cplusplus +extern "C" { +#endif + +ATCA_STATUS atcah_nonce(struct atca_nonce_in_out *param); +ATCA_STATUS atcah_mac(struct atca_mac_in_out *param); +ATCA_STATUS atcah_check_mac(struct atca_check_mac_in_out *param); +ATCA_STATUS atcah_hmac(struct atca_hmac_in_out *param); +ATCA_STATUS atcah_gen_dig(struct atca_gen_dig_in_out *param); +ATCA_STATUS atcah_gen_mac(struct atca_gen_dig_in_out *param); +ATCA_STATUS atcah_write_auth_mac(struct atca_write_mac_in_out *param); +ATCA_STATUS atcah_privwrite_auth_mac(struct atca_write_mac_in_out *param); +ATCA_STATUS atcah_derive_key(struct atca_derive_key_in_out *param); +ATCA_STATUS atcah_derive_key_mac(struct atca_derive_key_mac_in_out *param); +ATCA_STATUS atcah_decrypt(struct atca_decrypt_in_out *param); +ATCA_STATUS atcah_sha256(int32_t len, const uint8_t *message, uint8_t *digest); +uint8_t *atcah_include_data(struct atca_include_data_in_out *param); +ATCA_STATUS atcah_gen_key_msg(struct atca_gen_key_in_out *param); +ATCA_STATUS atcah_config_to_sign_internal(ATCADeviceType device_type, struct atca_sign_internal_in_out *param, const uint8_t* config); +ATCA_STATUS atcah_sign_internal_msg(ATCADeviceType device_type, struct atca_sign_internal_in_out *param); +ATCA_STATUS atcah_verify_mac(atca_verify_mac_in_out_t *param); +ATCA_STATUS atcah_secureboot_enc(atca_secureboot_enc_in_out_t* param); +ATCA_STATUS atcah_secureboot_mac(atca_secureboot_mac_in_out_t *param); +ATCA_STATUS atcah_encode_counter_match(uint32_t counter, uint8_t * counter_match); +ATCA_STATUS atcah_io_decrypt(struct atca_io_decrypt_in_out *param); +ATCA_STATUS atcah_ecc204_write_auth_mac(struct atca_write_mac_in_out *param); +ATCA_STATUS atcah_gen_session_key(atca_session_key_in_out_t *param); +#ifdef __cplusplus +} +#endif + +/** @} */ + +#endif //ATCA_HOST_H diff --git a/drivers/ecc108a/ecc108a.c b/drivers/ecc108a/ecc108a.c new file mode 100644 index 0000000..4bbf08e --- /dev/null +++ b/drivers/ecc108a/ecc108a.c @@ -0,0 +1,271 @@ +#include "py/runtime.h" +#include "atca_iface.h" +#include "atca_basic.h" +#include "py/objstr.h" +#include + + +bool inited = false; +ATCAIfaceCfg cfg_atecc108a_i2c_default = { + .iface_type = ATCA_I2C_IFACE, + .devtype = ATECC108A, + { + .atcai2c.address = 0x60, + .atcai2c.bus = 0, + /* This is overridden in hal/hal_esp32_i2c.c but also read elsewhere */ + .atcai2c.baud = 100000, + }, + .wake_delay = 1500, + .rx_retries = 2 +}; + + +void assert_ATCA_SUCCESS(ATCA_STATUS status) { + if (status != ATCA_SUCCESS) { + mp_raise_OSError(status); + } +} + + +STATIC mp_obj_t ecc108a_init() { + assert_ATCA_SUCCESS(atcab_init(&cfg_atecc108a_i2c_default)); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(ecc108a_init_obj, ecc108a_init); + + +STATIC mp_obj_t ecc108a_read_config() { + atcab_wakeup(); + uint8_t buf[128] = {0}; + assert_ATCA_SUCCESS(atcab_read_config_zone(&buf)); + return mp_obj_new_bytearray(sizeof(buf), buf); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(ecc108a_read_config_obj, ecc108a_read_config); + + +STATIC mp_obj_t ecc108a_get_serial_number() { + uint8_t serial[9] = { 0 }; + char serial_str[27] = ""; + + atcab_wakeup(); + assert_ATCA_SUCCESS(atcab_read_serial_number(&serial)); + + sprintf(&serial_str, "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", + serial[0], serial[1], serial[2], + serial[3], serial[4], serial[5], + serial[6], serial[7], serial[8] + ); + + return mp_obj_new_str(serial_str, 26); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(ecc108a_get_serial_number_obj, ecc108a_get_serial_number); + + +STATIC mp_obj_t ecc108a_provision_slot() { + // Retrieve the current configuration zone and patch it + /* + Desired state from ecc108a_tools.run() + Key n: + SlotLocked: 1 (unlocked) + SlotConfig: 6083 + KeyConfig: 0033 + UseFlag: 0xff + */ + uint8_t buf[128] = {0}; + assert_ATCA_SUCCESS(atcab_read_config_zone(&buf)); + + uint16_t slot_config = + ATCA_SLOT_CONFIG_EXT_SIG_MASK | + ATCA_SLOT_CONFIG_INT_SIG_MASK | + ATCA_SLOT_CONFIG_IS_SECRET_MASK | + ATCA_SLOT_CONFIG_WRITE_KEY(0) | + ATCA_SLOT_CONFIG_WRITE_CONFIG(0b0110) + ; + uint16_t key_config = + ATCA_KEY_CONFIG_PRIVATE_MASK | + ATCA_KEY_CONFIG_PUB_INFO_MASK | + ATCA_KEY_CONFIG_KEY_TYPE(4) | + ATCA_KEY_CONFIG_LOCKABLE_MASK + ; + + // Patch this config into each of the 16 slots + for (uint8_t i = 0; i < 16 ; i++) { + uint16_t *target_slot_config = &buf[20 + 2*i]; + *target_slot_config = slot_config; + + uint16_t *target_key_config = &buf[96 + 2*i]; + *target_key_config = key_config; + } + + assert_ATCA_SUCCESS(atcab_write_config_zone(&buf)); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(ecc108a_provision_slot_obj, ecc108a_provision_slot); + + +STATIC mp_obj_t ecc108a_lock_data_zone() { + assert_ATCA_SUCCESS(atcab_lock_data_zone()); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(ecc108a_lock_data_zone_obj, ecc108a_lock_data_zone); + + +STATIC mp_obj_t ecc108a_lock_config_zone() { + assert_ATCA_SUCCESS(atcab_lock_config_zone()); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(ecc108a_lock_config_zone_obj, ecc108a_lock_config_zone); + + +STATIC mp_obj_t ecc108a_lock_slot(mp_obj_t slot_id) { + uint8_t slot = mp_obj_get_int(slot_id); + assert_ATCA_SUCCESS(atcab_lock_data_slot(slot)); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(ecc108a_lock_slot_obj, ecc108a_lock_slot); + + +STATIC mp_obj_t ecc108a_genkey(mp_obj_t slot_id) { + uint8_t pubkey[64] = { 0 }; + uint8_t slot = mp_obj_get_int(slot_id); + + atcab_wakeup(); + assert_ATCA_SUCCESS(atcab_genkey(slot, &pubkey)); + + // Return X, Y tuple + mp_obj_t tuple[2]; + tuple[0] = mp_obj_new_bytes(pubkey, 32); + tuple[1] = mp_obj_new_bytes(pubkey+32, 32); + return mp_obj_new_tuple(2, tuple); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(ecc108a_genkey_obj, ecc108a_genkey); + + +STATIC mp_obj_t ecc108a_get_pubkey(mp_obj_t slot_id) { + uint8_t pubkey[64] = { 0 }; + uint8_t slot = mp_obj_get_int(slot_id); + + atcab_wakeup(); + assert_ATCA_SUCCESS(atcab_get_pubkey(slot, &pubkey)); + + // Return X, Y tuple + mp_obj_t tuple[2]; + tuple[0] = mp_obj_new_bytes(pubkey, 32); + tuple[1] = mp_obj_new_bytes(pubkey+32, 32); + return mp_obj_new_tuple(2, tuple); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(ecc108a_get_pubkey_obj, ecc108a_get_pubkey); + + +STATIC mp_obj_t ecc108a_sign(mp_obj_t slot_id, mp_obj_t message) { + uint8_t signature[64] = { 0 }; + uint8_t slot = mp_obj_get_int(slot_id); + + mp_check_self(mp_obj_is_str_or_bytes(message)); + GET_STR_DATA_LEN(message, digest, digest_len); + + atcab_wakeup(); + assert_ATCA_SUCCESS(atcab_sign(slot, digest, signature)); + atcab_sleep(); + + // Return R, S tuple + mp_obj_t tuple[2]; + tuple[0] = mp_obj_new_bytes(signature, 32); + tuple[1] = mp_obj_new_bytes(signature+32, 32); + return mp_obj_new_tuple(2, tuple); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(ecc108a_sign_obj, ecc108a_sign); + +STATIC mp_obj_t ecc108a_full_sign(mp_obj_t slot_id, mp_obj_t message) { + uint8_t digest[32] = { 0 }; + uint8_t signature[64] = { 0 }; + uint8_t slot = mp_obj_get_int(slot_id); + + mp_check_self(mp_obj_is_str_or_bytes(message)); + GET_STR_DATA_LEN(message, msg, msg_len); + + atcab_wakeup(); + atcab_hw_sha2_256(msg, msg_len, digest); + assert_ATCA_SUCCESS(atcab_sign(slot, digest, signature)); + atcab_sleep(); + + // Return R, S tuple + mp_obj_t tuple[2]; + tuple[0] = mp_obj_new_bytes(signature, 32); + tuple[1] = mp_obj_new_bytes(signature+32, 32); + return mp_obj_new_tuple(2, tuple); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(ecc108a_full_sign_obj, ecc108a_full_sign); + +/* +import ecc108a, hashlib +slot = 6 +key = ecc108a.get_pubkey(slot) +message = "This is a test message" +hash = hashlib.sha256(message).digest() +sig = ecc108a.sign(slot, hash) +ecc108a.verify(hash, sig, key) + +*/ +//msg = b'a'*32 ; ecc108a.verify(msg, (msg, msg), (msg, msg)) +// ecc108a.verify(msg, (b'\xfd\x0c \xc4\xe0\x1c\x1d\xd8H\xf6\xb5?12\xc2\x1e>k\xc8\xb3\xc4=\x0e\xdb#v\xba\xffn\x94<\x7f', b'\xcb~\xe0`\t\xc9Yf\xfd\xdbc2\xe7\xf4\x81\xf0\x13\xc8\xb90\xd7\xa3~XI\xb4c\xa9H\x00Wu'), (b'\xe4`\xb2\x93\xc9\x9bj\x9f\xfbD\x94\x1di\x9b\x96\xc7\x91F\x1c\xcf\x7f\xe4\xbe\x8d4%P\xe3\xbb\x18;\xa0', b'\x0b\x9e\xe8\xf1\x9cB\x06\xb1\x93\xd2&\x7f\xdb\x03\x17\xa82\x99\x18\x1f\t\x1cz\xe5H\xb2\xb3}fM\xabe')) + + + +STATIC mp_obj_t ecc108a_verify(mp_obj_t message, mp_obj_t signature, mp_obj_t public_key) { + // All the strings are length 32, but we need to pass a *pointer* to a size t. + const size_t thirtytwo = 32; + + // Extract the pubkey + mp_obj_tuple_t *public_key_tuple = MP_OBJ_TO_PTR(public_key); + const char *x = mp_obj_str_get_data(public_key_tuple->items[0], &thirtytwo); + const char *y = mp_obj_str_get_data(public_key_tuple->items[1], &thirtytwo); + uint8_t key[64]; + memcpy(&key[0], x, 32); + memcpy(&key[32], y, 32); + + // Extract the signature + mp_obj_tuple_t *signature_tuple = MP_OBJ_TO_PTR(signature); + const char *r = mp_obj_str_get_data(signature_tuple->items[0], &thirtytwo); + const char *s = mp_obj_str_get_data(signature_tuple->items[1], &thirtytwo); + uint8_t sig[64]; + memcpy(&sig[0], r, 32); + memcpy(&sig[32], s, 32); + + // Extract the message (this is a SHA256 of the user-facing 'message') + const char *message_data = mp_obj_str_get_data(message, &thirtytwo); + + // Verify on-chip + bool verified; + assert_ATCA_SUCCESS(atcab_verify_extern((const uint8_t*) message_data, &sig, &key, &verified)); + if (verified) + return mp_const_true; + else + return mp_const_false; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(ecc108a_verify_obj, ecc108a_verify); + + +STATIC const mp_rom_map_elem_t ecc108a_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ecc108a) }, + { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&ecc108a_init_obj) }, + { MP_ROM_QSTR(MP_QSTR_read_config), MP_ROM_PTR(&ecc108a_read_config_obj) }, + { MP_ROM_QSTR(MP_QSTR_provision_slot), MP_ROM_PTR(&ecc108a_provision_slot_obj) }, + { MP_ROM_QSTR(MP_QSTR_lock_data_zone), MP_ROM_PTR(&ecc108a_lock_data_zone_obj) }, + { MP_ROM_QSTR(MP_QSTR_lock_config_zone), MP_ROM_PTR(&ecc108a_lock_config_zone_obj) }, + { MP_ROM_QSTR(MP_QSTR_lock_slot), MP_ROM_PTR(&ecc108a_lock_slot_obj) }, + { MP_ROM_QSTR(MP_QSTR_get_serial_number), MP_ROM_PTR(&ecc108a_get_serial_number_obj) }, + { MP_ROM_QSTR(MP_QSTR_genkey), MP_ROM_PTR(&ecc108a_genkey_obj) }, + { MP_ROM_QSTR(MP_QSTR_get_pubkey), MP_ROM_PTR(&ecc108a_get_pubkey_obj) }, + { MP_ROM_QSTR(MP_QSTR_sign), MP_ROM_PTR(&ecc108a_sign_obj) }, + { MP_ROM_QSTR(MP_QSTR_full_sign), MP_ROM_PTR(&ecc108a_full_sign_obj) }, + { MP_ROM_QSTR(MP_QSTR_verify), MP_ROM_PTR(&ecc108a_verify_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(ecc108a_module_globals, ecc108a_module_globals_table); + +const mp_obj_module_t ecc108a_user_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&ecc108a_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_ecc108a, ecc108a_user_module, 1); diff --git a/drivers/ecc108a/micropython.cmake b/drivers/ecc108a/micropython.cmake new file mode 100644 index 0000000..dcbc38a --- /dev/null +++ b/drivers/ecc108a/micropython.cmake @@ -0,0 +1,59 @@ +# Create an INTERFACE library for our C module. +add_library(usermod_ecc108a INTERFACE) + +# Add our source files to the lib +target_sources(usermod_ecc108a INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/ecc108a.c + ${CMAKE_CURRENT_LIST_DIR}/cryptoauthlib/atca_basic.c + ${CMAKE_CURRENT_LIST_DIR}/cryptoauthlib/atca_cfgs.c + ${CMAKE_CURRENT_LIST_DIR}/cryptoauthlib/atca_debug.c + ${CMAKE_CURRENT_LIST_DIR}/cryptoauthlib/atca_device.c + ${CMAKE_CURRENT_LIST_DIR}/cryptoauthlib/atca_helpers.c + ${CMAKE_CURRENT_LIST_DIR}/cryptoauthlib/atca_iface.c + ${CMAKE_CURRENT_LIST_DIR}/cryptoauthlib/atca_utils_sizes.c + ${CMAKE_CURRENT_LIST_DIR}/cryptoauthlib/cryptoauthlib.h + ${CMAKE_CURRENT_LIST_DIR}/cryptoauthlib/host/atca_host.c +# ${CMAKE_CURRENT_LIST_DIR}/cryptoauthlib/calib/calib_hmac.c + ${CMAKE_CURRENT_LIST_DIR}/cryptoauthlib/calib/calib_lock.c +# ${CMAKE_CURRENT_LIST_DIR}/cryptoauthlib/calib/calib_secureboot.c +# ${CMAKE_CURRENT_LIST_DIR}/cryptoauthlib/calib/calib_mac.c + ${CMAKE_CURRENT_LIST_DIR}/cryptoauthlib/calib/calib_nonce.c + ${CMAKE_CURRENT_LIST_DIR}/cryptoauthlib/calib/calib_updateextra.c + ${CMAKE_CURRENT_LIST_DIR}/cryptoauthlib/calib/calib_write.c + ${CMAKE_CURRENT_LIST_DIR}/cryptoauthlib/calib/calib_read.c +# ${CMAKE_CURRENT_LIST_DIR}/cryptoauthlib/calib/calib_aes_gcm.c +# ${CMAKE_CURRENT_LIST_DIR}/cryptoauthlib/calib/calib_ecdh.c + ${CMAKE_CURRENT_LIST_DIR}/cryptoauthlib/calib/calib_gendig.c + ${CMAKE_CURRENT_LIST_DIR}/cryptoauthlib/calib/calib_derivekey.c + ${CMAKE_CURRENT_LIST_DIR}/cryptoauthlib/calib/calib_genkey.c + ${CMAKE_CURRENT_LIST_DIR}/cryptoauthlib/calib/calib_execution.c + ${CMAKE_CURRENT_LIST_DIR}/cryptoauthlib/calib/calib_verify.c + ${CMAKE_CURRENT_LIST_DIR}/cryptoauthlib/calib/calib_sign.c + ${CMAKE_CURRENT_LIST_DIR}/cryptoauthlib/calib/calib_helpers.c +# ${CMAKE_CURRENT_LIST_DIR}/cryptoauthlib/calib/calib_counter.c +# ${CMAKE_CURRENT_LIST_DIR}/cryptoauthlib/calib/calib_selftest.c + ${CMAKE_CURRENT_LIST_DIR}/cryptoauthlib/calib/calib_basic.c + ${CMAKE_CURRENT_LIST_DIR}/cryptoauthlib/calib/calib_random.c + ${CMAKE_CURRENT_LIST_DIR}/cryptoauthlib/calib/calib_command.c + ${CMAKE_CURRENT_LIST_DIR}/cryptoauthlib/calib/calib_sha.c + ${CMAKE_CURRENT_LIST_DIR}/cryptoauthlib/calib/calib_privwrite.c +# ${CMAKE_CURRENT_LIST_DIR}/cryptoauthlib/calib/calib_kdf.c +# ${CMAKE_CURRENT_LIST_DIR}/cryptoauthlib/calib/calib_aes.c +# ${CMAKE_CURRENT_LIST_DIR}/cryptoauthlib/calib/calib_checkmac.c + ${CMAKE_CURRENT_LIST_DIR}/cryptoauthlib/calib/calib_info.c + ${CMAKE_CURRENT_LIST_DIR}/cryptoauthlib/hal/atca_hal.c +# ${CMAKE_CURRENT_LIST_DIR}/cryptoauthlib/hal/hal_esp32_i2c.c + ${CMAKE_CURRENT_LIST_DIR}/cryptoauthlib/hal/hal_esp32_mp_softi2c.c +) + +target_include_directories(usermod_tidal_usb INTERFACE + ${CMAKE_CURRENT_LIST_DIR} + ${CMAKE_CURRENT_LIST_DIR}/cryptoauthlib + ${CMAKE_CURRENT_LIST_DIR}/cryptoauthlib/calib + ${CMAKE_CURRENT_LIST_DIR}/cryptoauthlib/hal + ${CMAKE_CURRENT_LIST_DIR}/cryptoauthlib/host +) + +# Link our INTERFACE library to the usermod target. +target_link_libraries(usermod INTERFACE usermod_ecc108a) + diff --git a/drivers/micropython.cmake b/drivers/micropython.cmake index 8158dc2..0e74971 100644 --- a/drivers/micropython.cmake +++ b/drivers/micropython.cmake @@ -9,6 +9,8 @@ include(${CMAKE_CURRENT_LIST_DIR}/tidal_usb/micropython.cmake) # Add the st7789 driver include(${CMAKE_CURRENT_LIST_DIR}/../st7789/st7789/micropython.cmake) +# ECC108A driver +include(${CMAKE_CURRENT_LIST_DIR}/ecc108a/micropython.cmake) list(APPEND EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/components/esp_http_client" diff --git a/drivers/tidal_helpers/tidal_helpers.c b/drivers/tidal_helpers/tidal_helpers.c index a45807b..ade93ac 100644 --- a/drivers/tidal_helpers/tidal_helpers.c +++ b/drivers/tidal_helpers/tidal_helpers.c @@ -13,6 +13,7 @@ #include "esp32s2/rom/usb/usb_persist.h" #include "esp_wpa2.h" #include "driver/ledc.h" +#include "tidal_usb_u2f_shared_variables.h" // static const char *TAG = "tidal_helpers"; @@ -22,6 +23,67 @@ typedef struct _machine_pin_obj_t { gpio_num_t id; } machine_pin_obj_t; +STATIC mp_obj_t tidal_helper_authentication_operation() { + return mp_obj_new_int(authentication_operation); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(tidal_helper_authentication_operation_obj, tidal_helper_authentication_operation); + +STATIC mp_obj_t tidal_helper_authentication_requested() { + mp_obj_t response[3]; + response[0] = mp_const_none; + response[1] = mp_const_none; + response[2] = mp_const_none; + + // Is the current operation a request? + if (authentication_operation == AUTHENTICATE_REQUEST || authentication_operation == REGISTER_REQUEST) { + response[0] = mp_const_true; + } else { + response[0] = mp_const_false; + } + // What slot is in use + if (authentication_operation_slot == 99) { + response[1] = mp_const_none; + } else { + response[1] = mp_obj_new_int(authentication_operation_slot); + } + // What's the application parameter + response[2] = mp_obj_new_bytes(authentication_application_parameter, 32); + return mp_obj_new_tuple(3, response); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(tidal_helper_authentication_requested_obj, tidal_helper_authentication_requested); + +STATIC mp_obj_t tidal_helper_authentication_mismatch() { + authentication_operation = KEY_MISMATCH; + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(tidal_helper_authentication_mismatch_obj, tidal_helper_authentication_mismatch); + +STATIC mp_obj_t tidal_helper_authentication_approve(mp_obj_t state) { + if (state == mp_const_true) { + if (authentication_operation == AUTHENTICATE_REQUEST) + authentication_operation = AUTHENTICATE_APPROVED; + if (authentication_operation == REGISTER_REQUEST) + authentication_operation = REGISTER_APPROVED; + } + if (state == mp_const_false) + authentication_operation = USER_REFUSED; + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(tidal_helper_authentication_approve_obj, tidal_helper_authentication_approve); + + +STATIC mp_obj_t tidal_helper_authentication_slot(mp_obj_t slot) { + if (mp_obj_is_int(slot)) { + authentication_operation_slot = mp_obj_get_int(slot); + return slot; + } else { + mp_raise_ValueError(slot); + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(tidal_helper_authentication_slot_obj, tidal_helper_authentication_slot); + + STATIC gpio_num_t get_pin(mp_obj_t pin_obj) { if (mp_obj_is_int(pin_obj)) { return (gpio_num_t)mp_obj_get_int(pin_obj); @@ -387,6 +449,11 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(tidal_set_backlight_pwm_obj, tidal_set_backligh STATIC const mp_rom_map_elem_t tidal_helpers_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ota) }, + { MP_ROM_QSTR(MP_QSTR_get_authentication_operation), MP_ROM_PTR(&tidal_helper_authentication_operation_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_authentication_approval), MP_ROM_PTR(&tidal_helper_authentication_approve_obj) }, + { MP_ROM_QSTR(MP_QSTR_get_authentication_requested), MP_ROM_PTR(&tidal_helper_authentication_requested_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_authentication_mismatch), MP_ROM_PTR(&tidal_helper_authentication_mismatch_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_authentication_slot), MP_ROM_PTR(&tidal_helper_authentication_slot_obj) }, { MP_ROM_QSTR(MP_QSTR_get_variant), MP_ROM_PTR(&tidal_helper_get_variant_obj) }, { MP_ROM_QSTR(MP_QSTR_usb_connected), MP_ROM_PTR(&tidal_helper_usb_connected_obj) }, { MP_ROM_QSTR(MP_QSTR_usb_suspended), MP_ROM_PTR(&tidal_helper_usb_suspended_obj) }, diff --git a/drivers/tidal_usb/micropython.cmake b/drivers/tidal_usb/micropython.cmake index 83e539d..f0dbe57 100644 --- a/drivers/tidal_usb/micropython.cmake +++ b/drivers/tidal_usb/micropython.cmake @@ -3,10 +3,13 @@ add_library(usermod_tidal_usb INTERFACE) # Add our source files to the lib target_sources(usermod_tidal_usb INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/tidal_authentication.c ${CMAKE_CURRENT_LIST_DIR}/tidal_usb.c ${CMAKE_CURRENT_LIST_DIR}/tidal_usb_hid.c ${CMAKE_CURRENT_LIST_DIR}/tidal_usb_console.c ${CMAKE_CURRENT_LIST_DIR}/tidal_usb_u2f.c + ${CMAKE_CURRENT_LIST_DIR}/tidal_usb_u2f_shared_variables.c + ${CMAKE_CURRENT_LIST_DIR}/u2f_crypto.c ) # Add the current directory as an include directory. diff --git a/drivers/tidal_usb/tidal_usb_console.c b/drivers/tidal_usb/tidal_usb_console.c index 780fe1c..aaad04e 100644 --- a/drivers/tidal_usb/tidal_usb_console.c +++ b/drivers/tidal_usb/tidal_usb_console.c @@ -1,10 +1,12 @@ #include "py/runtime.h" #include "py/mphal.h" #include "tinyusb.h" -#include "tusb_cdc_acm.h" #include "tusb_console.h" #include "tidal_usb_console.h" +#if CONFIG_TINYUSB_CDC_ENABLED +#include "tusb_cdc_acm.h" + // USB serial read callbacks from mp static uint8_t usb_rx_buf[CONFIG_TINYUSB_CDC_RX_BUFSIZE]; static uint8_t usb_cdc_connected; @@ -35,7 +37,7 @@ void usb_callback_line_state_changed(int itf, cdcacm_event_t *event) { // If dtr && rts are both true, the CDC is connected to a HOST. usb_cdc_connected = dtr && rts; - + // TODO: identify pattern change that esp-idf uses to signal bootloader reset } @@ -63,13 +65,20 @@ void tidal_configure_usb_console() { }; tusb_cdc_acm_init(&amc_cfg); } +#else +void tidal_configure_usb_console() { } +void usb_tx_strn(const char *str, size_t len) { } +#endif + // Expose connected state in Python STATIC mp_obj_t tidal_cdc_connected() { + #if CONFIG_USB_CDC_ENABLED if (usb_cdc_connected) return mp_const_true; else + #endif return mp_const_false; } STATIC MP_DEFINE_CONST_FUN_OBJ_0(tidal_cdc_connected_obj, tidal_cdc_connected); diff --git a/drivers/tidal_usb/tidal_usb_hid.c b/drivers/tidal_usb/tidal_usb_hid.c index c50a060..c957271 100644 --- a/drivers/tidal_usb/tidal_usb_hid.c +++ b/drivers/tidal_usb/tidal_usb_hid.c @@ -1,4 +1,5 @@ #include "py/runtime.h" +#include "esp_log.h" #include "tinyusb.h" #include "tusb_hid.h" #include "usb.h" @@ -8,10 +9,12 @@ #include "tidal_usb_u2f.h" #endif +static const char *TAG = "tidalHID"; + void tud_hid_set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize) { printf("REPORT: %d %d %d\n", itf, report_id, report_type); #if CFG_TUD_U2FHID - if (itf == 1) { + if (hid_mode == 1) { // This is the U2F device handle_report_u2f(itf, report_id, report_type, buffer, bufsize); } @@ -22,7 +25,11 @@ void tud_hid_set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t rep // report, so buttons are considered held until they're explicitly released void tud_hid_report_complete_cb(uint8_t itf, uint8_t const *report, uint8_t len) { - // no-op + ESP_LOGW(TAG, "HID report complete"); + #if CFG_TUD_U2FHID + // Also send any pending U2F reports + pop_and_send_report(); + #endif } // Send up to 6 keyboard scancodes diff --git a/drivers/tidal_usb/tidal_usb_u2f.c b/drivers/tidal_usb/tidal_usb_u2f.c index cd84f56..a362278 100644 --- a/drivers/tidal_usb/tidal_usb_u2f.c +++ b/drivers/tidal_usb/tidal_usb_u2f.c @@ -1,19 +1,56 @@ #include "descriptors_control.h" #include "tidal_usb_u2f.h" #include "u2f_hid.h" +#include "u2f.h" #include "esp_log.h" +#include "u2f_crypto.h" +#include +#include "py/builtin.h" -#if CFG_TUD_U2FHID +#include "tidal_usb_console.h" + +#if CONFIG_TINYUSB_U2FHID_ENABLED static const char *TAG = "tidalU2F"; uint8_t *in_progress_packet = NULL; bool packet_needs_free = false; -uint16_t expected_size = NULL; -uint16_t current_index = NULL; +uint16_t expected_size = 0; +uint16_t current_index = 0; + +u2f_hid_msg upcoming_report_ring[REPORT_RING_SIZE] = { 0 }; +bool upcoming_report_ring_waiting[REPORT_RING_SIZE] = { false }; +uint8_t write_head = 0; +uint8_t read_head = 0; + +void push_report(u2f_hid_msg *msg) { + if (upcoming_report_ring_waiting[write_head]) { + // There's already a report waiting to be picked up. This is a fatal error. + ESP_LOGE(TAG, "U2F outbound report buffer full"); + } + ESP_LOGI(TAG, "Writing into buffer at position %d", write_head); + memcpy(&upcoming_report_ring[write_head], msg, sizeof(u2f_hid_msg)); + upcoming_report_ring_waiting[write_head] = true; + write_head = (write_head + 1) % REPORT_RING_SIZE; + return; +} -bool handle_message_fragment(uint8_t *buffer) { - const u2f_hid_msg *msg = buffer; +void pop_and_send_report() { + if (!upcoming_report_ring_waiting[read_head]) { + // There's no report waiting to be picked up. This is a fatal error. + ESP_LOGW(TAG, "U2F outbound report buffer empty"); + return; + } + ESP_LOGI(TAG, "Sending pending report from position %d", read_head); + u2f_report(&upcoming_report_ring[read_head]); + upcoming_report_ring_waiting[read_head] = false; + memset(&upcoming_report_ring[read_head], 0, sizeof(u2f_hid_msg)); + read_head = (read_head + 1) % REPORT_RING_SIZE; +} + + +bool handle_message_fragment(const uint8_t *buffer) { + const u2f_hid_msg *msg = (u2f_hid_msg *) buffer; if (in_progress_packet != NULL) { // This is a continuation packet uint16_t bytes_to_copy = min(sizeof(msg->cont.data), expected_size-current_index); @@ -31,23 +68,24 @@ bool handle_message_fragment(uint8_t *buffer) { // This is a command packet, check if we need to set // up a continuation uint16_t msg_size = (msg->init.BCNTH << 8) + msg->init.BCNTL; - ESP_LOGI(TAG, "Got message of size %d", msg_size); + ESP_LOGI(TAG, "Got initial packet of size %d for message %x", msg_size, msg->init.CMD); if (msg_size <= HID_RPT_SIZE) { // No, it's small enough ESP_LOGI(TAG, "Using existing buffer"); - in_progress_packet = msg; + in_progress_packet = buffer; expected_size = msg_size; packet_needs_free = false; return true; } else { // This is the first fragment, set up a big enough buffer ESP_LOGI(TAG, "Allocating larger buffer"); - in_progress_packet = malloc(msg_size); - expected_size = msg_size; + // The expected size is the reported data size plus the header on the initial packet + expected_size = msg_size + 7; + in_progress_packet = malloc(expected_size); // Copy the entire message, including the header uint16_t bytes_to_copy = HID_RPT_SIZE; - memcpy(in_progress_packet + current_index, buffer, bytes_to_copy); + memcpy(in_progress_packet, buffer, bytes_to_copy); current_index = bytes_to_copy; packet_needs_free = true; return false; @@ -64,7 +102,7 @@ void handle_report_u2f(uint8_t itf, uint8_t report_id, hid_report_type_t report_ const u2f_hid_msg *msg = in_progress_packet; uint16_t msg_size = (msg->init.BCNTH << 8) + msg->init.BCNTL; - ESP_LOGI(TAG, "Handling command %02x (%d)", msg->init.CMD, msg->init.CMD); + ESP_LOGI(TAG, "Handling command %02x", msg->init.CMD); if (msg->init.CMD == U2FHID_INIT) { // INIT command handle_u2f_init((const u2fhid_init_request *) msg->init.data); @@ -73,15 +111,25 @@ void handle_report_u2f(uint8_t itf, uint8_t report_id, hid_report_type_t report_ } else if (msg->init.CMD == U2FHID_WINK) { handle_u2f_wink(); } else { + ESP_LOGE(TAG, "Unknown U2FHID transport command %02x", msg->init.CMD); // Unknown report type, log it. - printf("REPORT: %d %d %d\n", itf, report_id, report_type); + /*printf("REPORT: %d %d %d\n", itf, report_id, report_type); for (int i=0;iCID, + .init.CMD = U2FHID_ERROR, + .init.BCNTH = 0, + .init.BCNTL = 1, + .init.data = { ERR_INVALID_CMD } + }; + u2f_report(&response); + } if (packet_needs_free) { @@ -89,7 +137,8 @@ void handle_report_u2f(uint8_t itf, uint8_t report_id, hid_report_type_t report_ free(in_progress_packet); packet_needs_free = false; } - in_progress_packet = expected_size = current_index = NULL; + in_progress_packet = NULL; + expected_size = current_index = 0; } @@ -106,7 +155,7 @@ void handle_u2f_init(u2fhid_init_request const* init_request) { .DEVICE_VER_MAJOR = 0x03, .DEVICE_VER_MINOR = 0x04, .DEVICE_VER_BUILD = 0x05, - .CAPABILITIES = CAPFLAG_WINK + .CAPABILITIES = 0 //CAPFLAG_WINK }; // Allocate a response report @@ -118,21 +167,188 @@ void handle_u2f_init(u2fhid_init_request const* init_request) { }; memcpy(response.init.data, &response_data, 17); - // Cast this to a char array so we can debug print it - uint8_t *as_buf = (uint8_t *) &response; - for (int i=0;i> 8; + response_data.data[write_head++] = U2F_SW_NO_ERROR & 0xFF; + ESP_LOGI(TAG, "Built %d byte register response", write_head); + response_data.size = write_head; + //realloc(response_data.data, write_head); + /* + printf("RAW data:"); + for (int i=0;i> 8; + response_data.data[1] = U2F_SW_CONDITIONS_NOT_SATISFIED & 0xFF; + return response_data; + } + + ESP_LOGI(TAG, "Allocating container"); + arbitrary_size_container response_data = { + .size=0, + .data=malloc(185) + }; + memset(response_data.data, 0x00, 185); + size_t write_head = 0; + + // Set reserved byte + ESP_LOGI(TAG, "Setting presence bit"); + response_data.data[write_head++] = 0x01; + // The next 4 bytes are a counter, increment it for this handle and return + //uint32_t counter_value = 1; + //set_counter(authenticate_params->key_handle[0], &counter_value); + response_data.data[write_head++] = 0x00;//(counter_value >> 24) && 0xFF; + response_data.data[write_head++] = 0x00;//(counter_value >> 16) && 0xFF; + response_data.data[write_head++] = 0x00;//(counter_value >> 8) && 0xFF; + response_data.data[write_head++] = 0x01;//(counter_value >> 0) && 0xFF; + + // Create the signature out of the application parameter, the user presence byte, the counter then the challenge param + ESP_LOGI(TAG, "Setting signature"); + uint8_t signature_input[69] = { 0 }; + memcpy(signature_input + 0, authenticate_params->application_param, 32); + memcpy(signature_input + 32, response_data.data, 5); + memcpy(signature_input + 37, authenticate_params->challenge_param, 32); + + // Ensure the signature material didn't change while we computed it + if (memcmp(signature_input, authentication_value_to_sign, authentication_length_to_sign) == 0) { + write_head += der_encode_signature(authentication_signature, response_data.data + write_head); + //write_head += get_signature(authenticate_params->key_handle[0], 69, signature_input, response_data.data + write_head); + response_data.data[write_head++] = U2F_SW_NO_ERROR >> 8; + response_data.data[write_head++] = U2F_SW_NO_ERROR & 0xFF; + ESP_LOGI(TAG, "Built %d byte authenticate response", write_head); + response_data.size = write_head; + } else { + response_data.data[0] = U2F_SW_CONDITIONS_NOT_SATISFIED >> 8; + response_data.data[1] = U2F_SW_CONDITIONS_NOT_SATISFIED & 0xFF; + response_data.size = 2; + } + + //write_head += get_signature(authenticate_params->key_handle[0], 69, signature_input, response_data.data + write_head); + + ///uint8_t fakesig[64] = { 0x11, 0xce, 0x96, 0x46, 0xf3, 0xc6, 0xa0, 0xc9, 0x84, 0x3c, 0x89, 0xa8, 0xd7, 0xb0, 0xab, 0x48, 0xf1, 0xba, 0xab, 0xa9, 0x9e, 0x65, 0x1c, 0x7e, 0x67, 0x3a, 0x1d, 0xba, 0x99, 0xa4, 0xb9, 0x16, 0x61, 0x53, 0x32, 0x66, 0x31, 0x65, 0xbb, 0xb3, 0x20, 0x94, 0xa3, 0xd1, 0x9d, 0xeb, 0xcd, 0xa3, 0xac, 0x73, 0x71, 0x9e, 0x3d, 0xec, 0x54, 0x0d, 0xe7, 0xf1, 0x9a, 0xd5, 0x05, 0xd2, 0xf2, 0xb9 }; + //memcpy(response_data.data + write_head+3, authentication_signature, 64); + //write_head += 70; + // Set the status epilogue + //realloc(response_data.data, write_head); + + + /*printf("RAW data:"); + for (int i=0;isize); + uint16_t place = 0; + uint8_t seq = 0; + u2f_hid_msg response = { + .CID = TIDAL_CHANNEL, + .init.CMD = U2FHID_MSG, + .init.BCNTH = (response_data->size >> 8), + .init.BCNTL = (response_data->size & 0xFF), + .init.data = { 0 } + }; + memcpy(response.init.data, response_data->data + place, sizeof(response.init.data)); + ESP_LOGI(TAG, "Sending initial packet %d/%d", place, response_data->size); + place += sizeof(response.init.data); + while (place < response_data->size) { + u2f_hid_msg next_report = { + .CID = TIDAL_CHANNEL, + .cont.SEQ = seq++, + .cont.data = { 0 } + }; + memcpy(next_report.cont.data, response_data->data + place, sizeof(next_report.cont.data)); + ESP_LOGI(TAG, "Sending continuation packet %d/%d", place, response_data->size); + place += sizeof(next_report.cont.data); + push_report(&next_report); + } u2f_report(&response); } void handle_u2f_msg(uint8_t *buffer, uint16_t bufsize) { - ESP_LOGE(TAG, "Not implemented"); - printf("U2FMSG:"); + /*printf("U2FMSG:"); for (int i=0;iINS == U2F_REGISTER) { + ESP_LOGI(TAG, "Will process U2F_REGISTER raw message (Instruction %x)", raw_message->INS); + u2f_raw_register_request_body *register_params = (u2f_raw_register_request_body *) raw_message->extended_form.data; + + if (authentication_operation == NO_OPERATION || authentication_operation == REGISTER_REQUEST) { + // Set shared variables with micropython + authentication_operation = REGISTER_REQUEST; + memcpy(authentication_application_parameter, register_params->application_param, 32); + authentication_length_to_sign = 131; + authentication_value_to_sign[0] = 0; + memcpy(authentication_value_to_sign, register_params, 65); + + // The user needs to allow this, send back the conditions + // not satisfied status as the only body + ESP_LOGI(TAG, "Awaiting user interaction, reporting conditions not satisfied"); + u2f_hid_msg response = { + .CID = TIDAL_CHANNEL, + .init.CMD = U2FHID_MSG, + .init.BCNTH = 0, + .init.BCNTL = 0x02, + .init.data = { + (U2F_SW_CONDITIONS_NOT_SATISFIED >> 8), + (U2F_SW_CONDITIONS_NOT_SATISFIED & 0xff) + } + }; + u2f_report(&response); + } else if (authentication_operation == REGISTER_APPROVED) { + // Reset the interaction flag + arbitrary_size_container response_data = process_register_command(register_params); + send_multipart_response(&response_data); + authentication_operation = NO_OPERATION; + authentication_operation_slot = 99; + free(response_data.data); + } + } else if (raw_message->INS == U2F_AUTHENTICATE) { + +/* arbitrary_size_container response_data = { + .size=185, + .data=malloc(185) }; - u2f_report(&response); + size_t write_head = 0; + + memcpy(response_data.data, raw_message->extended_form.data, 70); + response_data.size = 70; + realloc(response_data.data, 70); + */ + + ESP_LOGI(TAG, "Will process U2F_AUTHENTICATE raw message (Instruction %x)", raw_message->INS); + u2f_raw_authenticate_request_body *authenticate_params = (u2f_raw_authenticate_request_body *) raw_message->extended_form.data; + + if (authentication_operation == KEY_MISMATCH) { + u2f_hid_msg response = { + .CID = TIDAL_CHANNEL, + .init.CMD = U2FHID_MSG, + .init.BCNTH = 0, + .init.BCNTL = 0x02, + .init.data = { + (U2F_SW_WRONG_DATA >> 8), + (U2F_SW_WRONG_DATA & 0xff) + } + }; + u2f_report(&response); + authentication_operation = NO_OPERATION; + } else if (authentication_operation == AUTHENTICATE_APPROVED) { + arbitrary_size_container response_data = process_authenticate_command(raw_message->P1, authenticate_params); + send_multipart_response(&response_data); + free(response_data.data); + authentication_operation = NO_OPERATION; + authentication_operation_slot = 99; + } + else { + // The user needs to allow this, send back the conditions + // not satisfied status as the only body + + // Set shared variables with micropython + authentication_operation_slot = authenticate_params->key_handle[0]; + memcpy(authentication_application_parameter, authenticate_params->application_param, 32); + + authentication_length_to_sign = 69; + memcpy(authentication_value_to_sign, authenticate_params->application_param, 32); + authentication_value_to_sign[32] = 0x01; + authentication_value_to_sign[33] = 0x00; + authentication_value_to_sign[34] = 0x00; + authentication_value_to_sign[35] = 0x00; + authentication_value_to_sign[36] = 0x01; + memcpy(authentication_value_to_sign + 37, authenticate_params->challenge_param, 32); + + authentication_operation = AUTHENTICATE_REQUEST; + + /*uint8_t signature_input[69] = { 0 }; + memcpy(signature_input + 0, authenticate_params->application_param, 32); + memcpy(signature_input + 32, response_data.data, 5); + memcpy(signature_input + 37, authenticate_params->challenge_param, 32); + //write_head += get_signature(authenticate_params->key_handle[0], 69, signature_input, response_data.data + write_head); +*/ + + ESP_LOGI(TAG, "Awaiting user interaction, reporting conditions not satisfied"); + u2f_hid_msg response = { + .CID = TIDAL_CHANNEL, + .init.CMD = U2FHID_MSG, + .init.BCNTH = 0, + .init.BCNTL = 0x02, + .init.data = { + (U2F_SW_CONDITIONS_NOT_SATISFIED >> 8), + (U2F_SW_CONDITIONS_NOT_SATISFIED & 0xff) + } + }; + u2f_report(&response); + } + } else { + ESP_LOGE(TAG, "Got U2F raw message, but instruction %x is not known", raw_message->INS); + } } void handle_u2f_wink() { @@ -163,10 +485,23 @@ void handle_u2f_wink() { }; u2f_report(&response); } + + void u2f_report(u2f_hid_msg *cmd) { - // This is wrong, but I'm just trying to make it match - // what a real one is doing for now, to help debug. + // For some reason, sending with a report ID of 0 is causing + // timeout then -ENOENT, so as long as the 0th byte of the + // buffer isn't a 0 we can do a really filthy hack and put that + // in the report id field. + // Sit Deus propitius huic potatori + uint8_t *as_buf = (uint8_t *) cmd; - //tud_hid_n_report(ITF_NUM_HID_2, 0, as_buf, HID_RPT_SIZE-1); + /*printf("Sending response packet"); + for (int i=0;i +#include +#include +#include "tidal_usb_u2f_shared_variables.h" + +enum authentication_state authentication_operation = NO_OPERATION; +uint8_t authentication_operation_slot = 99; +uint8_t authentication_application_parameter[32] = { 0 }; +size_t authentication_length_to_sign = 0; +uint8_t authentication_value_to_sign[256] = { 0 }; +uint8_t authentication_signature[64] = { 0 }; \ No newline at end of file diff --git a/drivers/tidal_usb/tidal_usb_u2f_shared_variables.h b/drivers/tidal_usb/tidal_usb_u2f_shared_variables.h new file mode 100644 index 0000000..a9255b3 --- /dev/null +++ b/drivers/tidal_usb/tidal_usb_u2f_shared_variables.h @@ -0,0 +1,19 @@ +#include +#include + +enum authentication_state { + NO_OPERATION, + REGISTER_REQUEST, + REGISTER_APPROVED, + AUTHENTICATE_REQUEST, + AUTHENTICATE_APPROVED, + USER_REFUSED, + KEY_MISMATCH, +}; +enum authentication_state authentication_operation; +uint8_t authentication_operation_slot; +uint8_t authentication_application_parameter[32]; +size_t authentication_length_to_sign; +uint8_t authentication_value_to_sign[256]; +uint8_t authentication_signature[64]; +uint8_t authentication_pubkey[64]; diff --git a/drivers/tidal_usb/u2f.h b/drivers/tidal_usb/u2f.h new file mode 100644 index 0000000..cd764e1 --- /dev/null +++ b/drivers/tidal_usb/u2f.h @@ -0,0 +1,111 @@ +/** + * Copyright FIDO Alliance, 2017 + * + * Licensed under CC-BY: + * https://creativecommons.org/licenses/by/4.0/legalcode + * + * Editor: Jakob Ehrensvard, Yubico, jakob@yubico.com + */ + +#ifndef __U2F_H_INCLUDED__ +#define __U2F_H_INCLUDED__ + +#ifdef _MSC_VER // Windows +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long int uint64_t; +#else +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +// General constants + +#define U2F_EC_KEY_SIZE 32 // EC key size in bytes +#define U2F_EC_POINT_SIZE ((U2F_EC_KEY_SIZE * 2) + 1) // Size of EC point +#define U2F_MAX_KH_SIZE 128 // Max size of key handle +#define U2F_MAX_ATT_CERT_SIZE 2048 // Max size of attestation certificate +#define U2F_MAX_EC_SIG_SIZE 72 // Max size of DER coded EC signature +#define U2F_CTR_SIZE 4 // Size of counter field +#define U2F_APPID_SIZE 32 // Size of application id +#define U2F_CHAL_SIZE 32 // Size of challenge + +#define ENC_SIZE(x) ((x + 7) & 0xfff8) + +// EC (uncompressed) point + +#define U2F_POINT_UNCOMPRESSED 0x04 // Uncompressed point format + +typedef struct __attribute__((packed)) { + uint8_t pointFormat; // Point type + uint8_t x[U2F_EC_KEY_SIZE]; // X-value + uint8_t y[U2F_EC_KEY_SIZE]; // Y-value +} U2F_EC_POINT; + +// U2F native commands + +#define U2F_REGISTER 0x01 // Registration command +#define U2F_AUTHENTICATE 0x02 // Authenticate/sign command +#define U2F_VERSION 0x03 // Read version string command + +#define U2F_VENDOR_FIRST 0x40 // First vendor defined command +#define U2F_VENDOR_LAST 0xbf // Last vendor defined command + +// U2F_CMD_REGISTER command defines + +#define U2F_REGISTER_ID 0x05 // Version 2 registration identifier +#define U2F_REGISTER_HASH_ID 0x00 // Version 2 hash identintifier + +typedef struct __attribute__((packed)) { + uint8_t chal[U2F_CHAL_SIZE]; // Challenge + uint8_t appId[U2F_APPID_SIZE]; // Application id +} U2F_REGISTER_REQ; + +typedef struct __attribute__((packed)) { + uint8_t registerId; // Registration identifier (U2F_REGISTER_ID_V2) + U2F_EC_POINT pubKey; // Generated public key + uint8_t keyHandleLen; // Length of key handle + uint8_t keyHandleCertSig[ + U2F_MAX_KH_SIZE + // Key handle + U2F_MAX_ATT_CERT_SIZE + // Attestation certificate + U2F_MAX_EC_SIG_SIZE]; // Registration signature +} U2F_REGISTER_RESP; + +// U2F_CMD_AUTHENTICATE command defines + +// Authentication control byte + +#define U2F_AUTH_ENFORCE 0x03 // Enforce user presence and sign +#define U2F_AUTH_CHECK_ONLY 0x07 // Check only +#define U2F_AUTH_FLAG_TUP 0x01 // Test of user presence set + +typedef struct __attribute__((packed)) { + uint8_t chal[U2F_CHAL_SIZE]; // Challenge + uint8_t appId[U2F_APPID_SIZE]; // Application id + uint8_t keyHandleLen; // Length of key handle + uint8_t keyHandle[U2F_MAX_KH_SIZE]; // Key handle +} U2F_AUTHENTICATE_REQ; + +typedef struct __attribute__((packed)) { + uint8_t flags; // U2F_AUTH_FLAG_ values + uint8_t ctr[U2F_CTR_SIZE]; // Counter field (big-endian) + uint8_t sig[U2F_MAX_EC_SIG_SIZE]; // Signature +} U2F_AUTHENTICATE_RESP; + +// Command status responses + +#define U2F_SW_NO_ERROR 0x9000 // SW_NO_ERROR +#define U2F_SW_WRONG_DATA 0x6A80 // SW_WRONG_DATA +#define U2F_SW_CONDITIONS_NOT_SATISFIED 0x6985 // SW_CONDITIONS_NOT_SATISFIED +#define U2F_SW_COMMAND_NOT_ALLOWED 0x6986 // SW_COMMAND_NOT_ALLOWED +#define U2F_SW_INS_NOT_SUPPORTED 0x6D00 // SW_INS_NOT_SUPPORTED + +#ifdef __cplusplus +} +#endif + +#endif // __U2F_H_INCLUDED__ \ No newline at end of file diff --git a/drivers/tidal_usb/u2f_crypto.c b/drivers/tidal_usb/u2f_crypto.c new file mode 100644 index 0000000..32f0b9a --- /dev/null +++ b/drivers/tidal_usb/u2f_crypto.c @@ -0,0 +1,132 @@ +#include "u2f_crypto.h" +#include "esp_log.h" +#include "atca_iface.h" +#include "atca_basic.h" +#include +#include +#include +#include "tidal_usb_u2f_shared_variables.h" + +static const char *TAG = "tidalU2F"; + + +arbitrary_size_container get_attestation_certificate() { + unsigned char attestation_der[] = { + 0x30, 0x82, 0x02, 0x4a, 0x30, 0x82, 0x01, 0xef, 0x02, 0x14, 0x11, 0xca, + 0xcc, 0x12, 0x88, 0x94, 0x28, 0x5c, 0xa8, 0x14, 0xc5, 0xd7, 0x9b, 0xb6, + 0x6f, 0x29, 0xb4, 0x73, 0xb8, 0xdd, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, + 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x30, 0x81, 0xa5, 0x31, 0x0b, 0x30, + 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x47, 0x42, 0x31, 0x0f, + 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x06, 0x4c, 0x6f, 0x6e, + 0x64, 0x6f, 0x6e, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x07, + 0x0c, 0x06, 0x4c, 0x6f, 0x6e, 0x64, 0x6f, 0x6e, 0x31, 0x22, 0x30, 0x20, + 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x19, 0x45, 0x6c, 0x65, 0x63, 0x74, + 0x72, 0x6f, 0x6d, 0x61, 0x67, 0x6e, 0x65, 0x74, 0x69, 0x63, 0x20, 0x46, + 0x69, 0x65, 0x6c, 0x64, 0x20, 0x4c, 0x74, 0x64, 0x31, 0x12, 0x30, 0x10, + 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x09, 0x42, 0x61, 0x64, 0x67, 0x65, + 0x54, 0x65, 0x61, 0x6d, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, + 0x03, 0x0c, 0x11, 0x54, 0x69, 0x44, 0x41, 0x4c, 0x20, 0x41, 0x74, 0x74, + 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x31, 0x20, 0x30, 0x1e, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, + 0x11, 0x62, 0x61, 0x64, 0x67, 0x65, 0x40, 0x65, 0x6d, 0x66, 0x63, 0x61, + 0x6d, 0x70, 0x2e, 0x6f, 0x72, 0x67, 0x30, 0x20, 0x17, 0x0d, 0x32, 0x32, + 0x31, 0x30, 0x33, 0x30, 0x31, 0x37, 0x34, 0x32, 0x34, 0x34, 0x5a, 0x18, + 0x0f, 0x32, 0x30, 0x35, 0x37, 0x31, 0x30, 0x33, 0x30, 0x31, 0x37, 0x34, + 0x32, 0x34, 0x34, 0x5a, 0x30, 0x81, 0xa5, 0x31, 0x0b, 0x30, 0x09, 0x06, + 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x47, 0x42, 0x31, 0x0f, 0x30, 0x0d, + 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x06, 0x4c, 0x6f, 0x6e, 0x64, 0x6f, + 0x6e, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x06, + 0x4c, 0x6f, 0x6e, 0x64, 0x6f, 0x6e, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, + 0x55, 0x04, 0x0a, 0x0c, 0x19, 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x6f, + 0x6d, 0x61, 0x67, 0x6e, 0x65, 0x74, 0x69, 0x63, 0x20, 0x46, 0x69, 0x65, + 0x6c, 0x64, 0x20, 0x4c, 0x74, 0x64, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, + 0x55, 0x04, 0x0b, 0x0c, 0x09, 0x42, 0x61, 0x64, 0x67, 0x65, 0x54, 0x65, + 0x61, 0x6d, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, + 0x11, 0x54, 0x69, 0x44, 0x41, 0x4c, 0x20, 0x41, 0x74, 0x74, 0x65, 0x73, + 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x31, 0x20, 0x30, 0x1e, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x11, 0x62, + 0x61, 0x64, 0x67, 0x65, 0x40, 0x65, 0x6d, 0x66, 0x63, 0x61, 0x6d, 0x70, + 0x2e, 0x6f, 0x72, 0x67, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, + 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, + 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0x70, 0x33, 0xf2, 0x6e, 0x2d, + 0xc2, 0xc0, 0xd1, 0xf8, 0xa7, 0x03, 0x3e, 0xa6, 0x4e, 0x47, 0x1a, 0xe1, + 0x02, 0xfe, 0xf3, 0xdb, 0xc9, 0xbd, 0xb7, 0x3e, 0x69, 0x59, 0xa1, 0xa5, + 0x49, 0x8b, 0x8e, 0x9a, 0x2a, 0xbd, 0xb2, 0xf5, 0xa8, 0xcb, 0xa0, 0x4a, + 0xb4, 0x6a, 0x84, 0x32, 0x09, 0x80, 0x95, 0x21, 0x99, 0x3d, 0x29, 0xe1, + 0xe0, 0xc2, 0xdf, 0xbd, 0xd5, 0x08, 0x22, 0x7a, 0xd8, 0x2d, 0xe2, 0x30, + 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x03, + 0x49, 0x00, 0x30, 0x46, 0x02, 0x21, 0x00, 0xd6, 0x6b, 0x12, 0x6c, 0xa9, + 0xf4, 0x56, 0x80, 0xf5, 0x58, 0xb9, 0xc3, 0xdf, 0xd5, 0xb2, 0xdf, 0x49, + 0x25, 0x44, 0x55, 0x81, 0xa8, 0x96, 0x88, 0x6b, 0x49, 0x03, 0x0b, 0xdb, + 0xa6, 0x75, 0x7f, 0x02, 0x21, 0x00, 0xa2, 0x46, 0x8d, 0xbd, 0x42, 0xda, + 0x2d, 0xda, 0x09, 0x0d, 0x68, 0xd8, 0x1b, 0x9f, 0x00, 0xea, 0xef, 0xfa, + 0x2f, 0x77, 0x1b, 0xc7, 0xe2, 0x36, 0x8c, 0x0b, 0xae, 0x34, 0x6b, 0x8c, + 0x4d, 0xc5 + }; + unsigned int attestation_der_len = 590; + + ESP_LOGI(TAG, "Allocating %d bytes", attestation_der_len); + arbitrary_size_container response_data = { + .size=attestation_der_len, + .data=malloc(attestation_der_len), + }; + memcpy(response_data.data, &attestation_der, attestation_der_len); + return response_data; +} + +size_t get_signature(uint8_t handle, size_t signature_length, uint8_t *signature_input, uint8_t *target) { + uint8_t digest[32] = { 0 }; + + uint8_t signature[64] = { 0 }; + + ESP_LOGI(TAG, "Calculating digest"); + atcab_wakeup(); + atcab_hw_sha2_256(signature_input, signature_length, digest); + ESP_LOGI(TAG, "Signing data"); + atcab_sleep(); + atcab_wakeup(); + atcab_sign(handle, digest, signature); + atcab_sleep(); + return der_encode_signature(signature, target); +} + + +size_t der_encode_signature(uint8_t signature[64], uint8_t target[70]) { + // Compound structure + target[0] = 0x30; + + // Length + target[1] = (32 + 2) * 2; + + // r + target[2] = 0x02; + target[3] = 32; + memcpy(&target[4], &signature[0], 32); + + // s + target[36] = 0x02; + target[37] = 32; + memcpy(&target[38], &signature[32], 32); + + return target[1] + 2; +} + + + +void set_counter(uint8_t handle, uint32_t *target) { + *target = 1; + //atcab_counter_increment(handle, target); +} + +void set_pubkey(uint8_t handle, uint8_t *target, bool regenerate) { + // This is an uncompressed key - in DER format?s + target[0] = 0x04; + if (regenerate) + atcab_genkey(handle, target + 1); + else + atcab_get_pubkey(handle, target + 1); +} + +uint8_t allocate_handle() { + return authentication_operation_slot; +} \ No newline at end of file diff --git a/drivers/tidal_usb/u2f_crypto.h b/drivers/tidal_usb/u2f_crypto.h new file mode 100644 index 0000000..5585ded --- /dev/null +++ b/drivers/tidal_usb/u2f_crypto.h @@ -0,0 +1,15 @@ +#include +#include +#include + +typedef struct arbitrary_size_container { + size_t size; + uint8_t *data; +} arbitrary_size_container; + +arbitrary_size_container get_attestation_certificate(); +size_t get_signature(uint8_t handle, size_t signature_length, uint8_t *signature_input, uint8_t *target); +void set_counter(uint8_t handle, uint32_t *target); +void set_pubkey(uint8_t handle, uint8_t *target, bool regenerate); +uint8_t allocate_handle(); +size_t der_encode_signature(uint8_t *signature, uint8_t *target); \ No newline at end of file diff --git a/esp-iot-solution.diff b/esp-iot-solution.diff index 83f959c..15f3547 100644 --- a/esp-iot-solution.diff +++ b/esp-iot-solution.diff @@ -38,8 +38,8 @@ index 2b6e48c..c4ca8c5 100644 #define CFG_TUD_CDC CONFIG_TINYUSB_CDC_PORT_NUM #define CFG_TUD_MSC CONFIG_TINYUSB_MSC_ENABLED -#define CFG_TUD_HID CONFIG_TINYUSB_HID_ENABLED -+#define CFG_TUD_HID_KBD 1 -+#define CFG_TUD_U2FHID false ++#define CFG_TUD_HID_KBD false ++#define CFG_TUD_U2FHID 1 +#define CFG_TUD_HID 1 #define CFG_TUD_MIDI CONFIG_TINYUSB_MIDI_ENABLED #define CFG_TUD_CUSTOM_CLASS CONFIG_TINYUSB_CUSTOM_CLASS_ENABLED diff --git a/keys/attestation.crt b/keys/attestation.crt new file mode 100644 index 0000000..3d80394 --- /dev/null +++ b/keys/attestation.crt @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIICSjCCAe8CFBHKzBKIlChcqBTF15u2bym0c7jdMAoGCCqGSM49BAMCMIGlMQsw +CQYDVQQGEwJHQjEPMA0GA1UECAwGTG9uZG9uMQ8wDQYDVQQHDAZMb25kb24xIjAg +BgNVBAoMGUVsZWN0cm9tYWduZXRpYyBGaWVsZCBMdGQxEjAQBgNVBAsMCUJhZGdl +VGVhbTEaMBgGA1UEAwwRVGlEQUwgQXR0ZXN0YXRpb24xIDAeBgkqhkiG9w0BCQEW +EWJhZGdlQGVtZmNhbXAub3JnMCAXDTIyMTAzMDE3NDI0NFoYDzIwNTcxMDMwMTc0 +MjQ0WjCBpTELMAkGA1UEBhMCR0IxDzANBgNVBAgMBkxvbmRvbjEPMA0GA1UEBwwG +TG9uZG9uMSIwIAYDVQQKDBlFbGVjdHJvbWFnbmV0aWMgRmllbGQgTHRkMRIwEAYD +VQQLDAlCYWRnZVRlYW0xGjAYBgNVBAMMEVRpREFMIEF0dGVzdGF0aW9uMSAwHgYJ +KoZIhvcNAQkBFhFiYWRnZUBlbWZjYW1wLm9yZzBZMBMGByqGSM49AgEGCCqGSM49 +AwEHA0IABHAz8m4twsDR+KcDPqZORxrhAv7z28m9tz5pWaGlSYuOmiq9svWoy6BK +tGqEMgmAlSGZPSnh4MLfvdUIInrYLeIwCgYIKoZIzj0EAwIDSQAwRgIhANZrEmyp +9FaA9Vi5w9/Vst9JJURVgaiWiGtJAwvbpnV/AiEAokaNvULaLdoJDWjYG58A6u/6 +L3cbx+I2jAuuNGuMTcU= +-----END CERTIFICATE----- diff --git a/keys/attestation.csr b/keys/attestation.csr new file mode 100644 index 0000000..6846993 --- /dev/null +++ b/keys/attestation.csr @@ -0,0 +1,10 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIIBYjCCAQgCAQAwgaUxCzAJBgNVBAYTAkdCMQ8wDQYDVQQIDAZMb25kb24xDzAN +BgNVBAcMBkxvbmRvbjEiMCAGA1UECgwZRWxlY3Ryb21hZ25ldGljIEZpZWxkIEx0 +ZDESMBAGA1UECwwJQmFkZ2VUZWFtMRowGAYDVQQDDBFUaURBTCBBdHRlc3RhdGlv +bjEgMB4GCSqGSIb3DQEJARYRYmFkZ2VAZW1mY2FtcC5vcmcwWTATBgcqhkjOPQIB +BggqhkjOPQMBBwNCAARwM/JuLcLA0finAz6mTkca4QL+89vJvbc+aVmhpUmLjpoq +vbL1qMugSrRqhDIJgJUhmT0p4eDC373VCCJ62C3ioAAwCgYIKoZIzj0EAwIDSAAw +RQIgdBVG25oY7umbvnhQxlXSAKWgAUWspzSNziQYrTbYugACIQCVI+P3E5QPAlLy +AQau36WadR9IA7RLqtXfYxvcbbbWpg== +-----END CERTIFICATE REQUEST----- diff --git a/keys/attestation.der b/keys/attestation.der new file mode 100644 index 0000000..5b83728 Binary files /dev/null and b/keys/attestation.der differ diff --git a/keys/attestation.h b/keys/attestation.h new file mode 100644 index 0000000..9f6b833 --- /dev/null +++ b/keys/attestation.h @@ -0,0 +1,53 @@ +unsigned char attestation_der[] = { + 0x30, 0x82, 0x02, 0x4a, 0x30, 0x82, 0x01, 0xef, 0x02, 0x14, 0x11, 0xca, + 0xcc, 0x12, 0x88, 0x94, 0x28, 0x5c, 0xa8, 0x14, 0xc5, 0xd7, 0x9b, 0xb6, + 0x6f, 0x29, 0xb4, 0x73, 0xb8, 0xdd, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, + 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x30, 0x81, 0xa5, 0x31, 0x0b, 0x30, + 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x47, 0x42, 0x31, 0x0f, + 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x06, 0x4c, 0x6f, 0x6e, + 0x64, 0x6f, 0x6e, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x07, + 0x0c, 0x06, 0x4c, 0x6f, 0x6e, 0x64, 0x6f, 0x6e, 0x31, 0x22, 0x30, 0x20, + 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x19, 0x45, 0x6c, 0x65, 0x63, 0x74, + 0x72, 0x6f, 0x6d, 0x61, 0x67, 0x6e, 0x65, 0x74, 0x69, 0x63, 0x20, 0x46, + 0x69, 0x65, 0x6c, 0x64, 0x20, 0x4c, 0x74, 0x64, 0x31, 0x12, 0x30, 0x10, + 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x09, 0x42, 0x61, 0x64, 0x67, 0x65, + 0x54, 0x65, 0x61, 0x6d, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, + 0x03, 0x0c, 0x11, 0x54, 0x69, 0x44, 0x41, 0x4c, 0x20, 0x41, 0x74, 0x74, + 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x31, 0x20, 0x30, 0x1e, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, + 0x11, 0x62, 0x61, 0x64, 0x67, 0x65, 0x40, 0x65, 0x6d, 0x66, 0x63, 0x61, + 0x6d, 0x70, 0x2e, 0x6f, 0x72, 0x67, 0x30, 0x20, 0x17, 0x0d, 0x32, 0x32, + 0x31, 0x30, 0x33, 0x30, 0x31, 0x37, 0x34, 0x32, 0x34, 0x34, 0x5a, 0x18, + 0x0f, 0x32, 0x30, 0x35, 0x37, 0x31, 0x30, 0x33, 0x30, 0x31, 0x37, 0x34, + 0x32, 0x34, 0x34, 0x5a, 0x30, 0x81, 0xa5, 0x31, 0x0b, 0x30, 0x09, 0x06, + 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x47, 0x42, 0x31, 0x0f, 0x30, 0x0d, + 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x06, 0x4c, 0x6f, 0x6e, 0x64, 0x6f, + 0x6e, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x06, + 0x4c, 0x6f, 0x6e, 0x64, 0x6f, 0x6e, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, + 0x55, 0x04, 0x0a, 0x0c, 0x19, 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x6f, + 0x6d, 0x61, 0x67, 0x6e, 0x65, 0x74, 0x69, 0x63, 0x20, 0x46, 0x69, 0x65, + 0x6c, 0x64, 0x20, 0x4c, 0x74, 0x64, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, + 0x55, 0x04, 0x0b, 0x0c, 0x09, 0x42, 0x61, 0x64, 0x67, 0x65, 0x54, 0x65, + 0x61, 0x6d, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, + 0x11, 0x54, 0x69, 0x44, 0x41, 0x4c, 0x20, 0x41, 0x74, 0x74, 0x65, 0x73, + 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x31, 0x20, 0x30, 0x1e, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x11, 0x62, + 0x61, 0x64, 0x67, 0x65, 0x40, 0x65, 0x6d, 0x66, 0x63, 0x61, 0x6d, 0x70, + 0x2e, 0x6f, 0x72, 0x67, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, + 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, + 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0x70, 0x33, 0xf2, 0x6e, 0x2d, + 0xc2, 0xc0, 0xd1, 0xf8, 0xa7, 0x03, 0x3e, 0xa6, 0x4e, 0x47, 0x1a, 0xe1, + 0x02, 0xfe, 0xf3, 0xdb, 0xc9, 0xbd, 0xb7, 0x3e, 0x69, 0x59, 0xa1, 0xa5, + 0x49, 0x8b, 0x8e, 0x9a, 0x2a, 0xbd, 0xb2, 0xf5, 0xa8, 0xcb, 0xa0, 0x4a, + 0xb4, 0x6a, 0x84, 0x32, 0x09, 0x80, 0x95, 0x21, 0x99, 0x3d, 0x29, 0xe1, + 0xe0, 0xc2, 0xdf, 0xbd, 0xd5, 0x08, 0x22, 0x7a, 0xd8, 0x2d, 0xe2, 0x30, + 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x03, + 0x49, 0x00, 0x30, 0x46, 0x02, 0x21, 0x00, 0xd6, 0x6b, 0x12, 0x6c, 0xa9, + 0xf4, 0x56, 0x80, 0xf5, 0x58, 0xb9, 0xc3, 0xdf, 0xd5, 0xb2, 0xdf, 0x49, + 0x25, 0x44, 0x55, 0x81, 0xa8, 0x96, 0x88, 0x6b, 0x49, 0x03, 0x0b, 0xdb, + 0xa6, 0x75, 0x7f, 0x02, 0x21, 0x00, 0xa2, 0x46, 0x8d, 0xbd, 0x42, 0xda, + 0x2d, 0xda, 0x09, 0x0d, 0x68, 0xd8, 0x1b, 0x9f, 0x00, 0xea, 0xef, 0xfa, + 0x2f, 0x77, 0x1b, 0xc7, 0xe2, 0x36, 0x8c, 0x0b, 0xae, 0x34, 0x6b, 0x8c, + 0x4d, 0xc5 +}; +unsigned int attestation_der_len = 590; diff --git a/keys/private.pem b/keys/private.pem new file mode 100644 index 0000000..fd4cbf9 --- /dev/null +++ b/keys/private.pem @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIDOln7OQO6fHoGDxGW7nQH5DzlnkxxGCfSgeJFb9w7LjoAoGCCqGSM49 +AwEHoUQDQgAEcDPybi3CwNH4pwM+pk5HGuEC/vPbyb23PmlZoaVJi46aKr2y9ajL +oEq0aoQyCYCVIZk9KeHgwt+91Qgietgt4g== +-----END EC PRIVATE KEY----- diff --git a/keys/public.pem b/keys/public.pem new file mode 100644 index 0000000..5fde78d --- /dev/null +++ b/keys/public.pem @@ -0,0 +1,4 @@ +-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEcDPybi3CwNH4pwM+pk5HGuEC/vPb +yb23PmlZoaVJi46aKr2y9ajLoEq0aoQyCYCVIZk9KeHgwt+91Qgietgt4g== +-----END PUBLIC KEY----- diff --git a/micropython.diff b/micropython.diff index 1c42864..1d2e002 100644 --- a/micropython.diff +++ b/micropython.diff @@ -1,18 +1,31 @@ -diff --git a/ports/esp32/CMakeLists.txt b/ports/esp32/CMakeLists.txt -index 8b2f09a72..bc4af823a 100644 ---- a/ports/esp32/CMakeLists.txt -+++ b/ports/esp32/CMakeLists.txt -@@ -18,6 +18,10 @@ if(NOT EXISTS ${MICROPY_BOARD_DIR}/mpconfigboard.cmake) - message(FATAL_ERROR "Invalid MICROPY_BOARD specified: ${MICROPY_BOARD}") - endif() +diff --git a/ports/esp32/Makefile b/ports/esp32/Makefile +index c8ca9262c..e6224a2a3 100644 +--- a/ports/esp32/Makefile ++++ b/ports/esp32/Makefile +@@ -30,6 +30,11 @@ ifdef FROZEN_MANIFEST + IDFPY_FLAGS += -D MICROPY_FROZEN_MANIFEST=$(FROZEN_MANIFEST) + endif ++ifdef USER_COMPONENTS ++ IDFPY_FLAGS += -DEXTRA_COMPONENT_DIRS=${USER_COMPONENTS} ++endif + -+set(IDF_TARGET esp32s3) -+include($ENV{IOT_SOLUTION_PATH}/component.cmake) + - # Include main IDF cmake file. - include($ENV{IDF_PATH}/tools/cmake/project.cmake) + all: + idf.py $(IDFPY_FLAGS) build + @$(PYTHON) makeimg.py \ +diff --git a/ports/esp32/main.c b/ports/esp32/main.c +index a1c27c0a2..56b145478 100644 +--- a/ports/esp32/main.c ++++ b/ports/esp32/main.c +@@ -95,6 +95,7 @@ void mp_task(void *pvParameter) { + #else + uart_stdout_init(); + #endif ++ + machine_init(); + size_t mp_task_heap_size; diff --git a/ports/esp32/mphalport.c b/ports/esp32/mphalport.c index 41e6e6ec0..761e93c01 100644 --- a/ports/esp32/mphalport.c diff --git a/modules/app.py b/modules/app.py index 3ef2981..9d0a2f1 100644 --- a/modules/app.py +++ b/modules/app.py @@ -121,7 +121,7 @@ def completion(val): self.present_window(keyboard) # Doesn't return until all finished return result[0] - def yes_no_prompt(self, title, yes_prompt=None, no_prompt=None): + def yes_no_prompt(self, title, yes_prompt=None, no_prompt=None, font=None): """Returns True or False, or None if the user dismissed the dialog with the back button""" win = self.window result = [None] @@ -137,7 +137,7 @@ def no(): (yes_prompt or "Yes", yes), (no_prompt or "No", no), ) - menu = DialogWindow(win.fg, win.bg, None, None, title, choices, None, buttons) + menu = DialogWindow(win.fg, win.bg, None, None, title, choices, font, buttons) self.present_window(menu) return result[0] diff --git a/modules/app_launcher/__init__.py b/modules/app_launcher/__init__.py index 6e61727..b0c26fb 100644 --- a/modules/app_launcher/__init__.py +++ b/modules/app_launcher/__init__.py @@ -11,6 +11,7 @@ import os import functools import settings +import authenticator def path_isfile(path): # Wow totally an elegant way to do os.path.isfile... @@ -82,6 +83,7 @@ def list_core_apps(self): core_app_info = [ ("App store", "app_store", "Store"), ("USB Keyboard", "hid", "USBKeyboard"), + ("Authenticator", "authenticator", "Authenticator"), ("Name Badge", "hello", "Hello"), ("Torch", "torch", "Torch"), ("Logo", "emflogo", "EMFLogo"), diff --git a/modules/authenticator/__init__.py b/modules/authenticator/__init__.py new file mode 100644 index 0000000..c954952 --- /dev/null +++ b/modules/authenticator/__init__.py @@ -0,0 +1,161 @@ +from app import MenuApp, Menu +from buttons import Buttons +from textwindow import TextWindow, DialogWindow +from scheduler import get_scheduler +import time +import settings +import tidal +import orientation +import _thread +import vga2_bold_16x32 +import tidal_authentication +import binascii +import machine +import ecc108a +import hashlib + + +class PromptPermission(DialogWindow): + + def __init__(self, slot_id=None, *args, **kwargs): + self.slot_id = slot_id + return super(*args, **kwargs) + + def redraw(self): + self.cls() + self.println("Press the joystick to approve") + + + +class Authenticator(MenuApp): + APP_ID = "authenticator" + TITLE = "Authenticator" + + @property + def CHOICES(self): + # The crypto chip has 16 slots + choices = [self.make_slot_select(slot_id) for slot_id in range(16)] + print(choices) + return choices + + def make_slot_select(self, slot_id): + name = settings.get(f"auth_slot_{slot_id}_name", None) + if name and len(name) > 5: + name = name[:5] + if not name: + name = f"Unused slot {slot_id}" + registered_date = settings.get(f"auth_slot_{slot_id}_registered", "") + return (name + " " + registered_date, print) + + def reallocate_slot(self, slot_id): + settings.delete(f"auth_slot_{slot_id}_name") + settings.delete(f"auth_slot_{slot_id}_registered") + settings.save() + +def cycle_leds(): + tidal.led_power_on() + tidal.led[0] = (tidal.led[0][2], ) + tidal.led[0][0:2] + tidal.led.write() + + +def trigger_wink(slot_id=None): + from app_launcher import Launcher + launcher = Launcher._singleton + authenticator_app = launcher._apps.get('authenticator.Authenticator') + if authenticator_app is None: + authenticator_app = launcher._apps['authenticator.Authenticator'] = Authenticator() + schedule = get_scheduler() + schedule.switch_app(authenticator_app) + tidal.led_power_on(True) + tidal.led[0] = (255, 255, 0) + flasher = authenticator_app.periodic(250, cycle_leds) + okay = authenticator_app.yes_no_prompt("Sign %d?" % slot_id, font=vga2_bold_16x32) + tidal.led[0] = (0, 0, 0) + tidal.led.write() + tidal.led_power_on(False) + flasher.cancel() + return okay + +prompting = False +def allow_interrupt_when_authenticating(): + def check_for_wink(): + global prompting + requested, slot_id, application_param = tidal_authentication.get_authentication_requested() + operation = tidal_authentication.get_authentication_operation() + if requested == False: + return + elif prompting: + # An earlier periodic invocation is asking + return + else: + prompting = True + name = binascii.hexlify(application_param).decode('latin-1') + + #slot_id = 6 + + if operation == 1: # Registration request + # Re-use an old slot for this application, if possible + if slot_id is None: + for i in range(16): + if settings.get(f"auth_slot_{i}_name", "") == name: + slot_id = i + break + + # If not, find an unused slot + if slot_id is None: + for i in range(16): + if not settings.get(f"auth_slot_{i}_name", ""): + slot_id = i + break + + # No slots available, cancel the approval + if slot_id is None: + tidal_authentication.set_authentication_approval(False) + prompting = False + return + else: + tidal_authentication.set_authentication_slot(slot_id) + elif operation == 3: # Authenticate request + if slot_id: + # Check the application parameter matches + expected_name = settings.get(f"auth_slot_{slot_id}_name", "") + if name != expected_name: + tidal_authentication.set_authentication_mismatch() + prompting = False + return + + # Prompt the user for permission + response = trigger_wink(slot_id) + if response: + # If approved, save the metadata + settings.set(f"auth_slot_{slot_id}_name", f"{name}") + settings.set(f"auth_slot_{slot_id}_registered", "%04d-%02d-%02d" % (machine.RTC().datetime()[:3])) + settings.save() + + try: + to_sign = tidal_authentication.get_to_sign() + if tidal_authentication.get_authentication_operation() == 1: + # This is a register request, overwrite the handle at byte 66 + to_sign = bytearray(to_sign) + to_sign[66] = slot_id + to_sign = bytes(to_sign) + # This is registration, so generate a key + ecc108a.genkey(slot_id) + print("material", to_sign) + print("slot", slot_id) + pubkey = ecc108a.get_pubkey(slot_id) + tidal_authentication.set_pubkey(pubkey) + print("pubkey", pubkey) + ecc108a.init() + ecc108a.read_config() + sig = hashlib.sha256(to_sign).digest() + signature = ecc108a.sign(slot_id, sig) + print("sig", signature) + tidal_authentication.set_signature(signature) + tidal_authentication.set_authentication_approval(response) + except OSError: + # Retry + pass + prompting = False + scheduler = get_scheduler() + scheduler.periodic(2500, check_for_wink) \ No newline at end of file diff --git a/modules/boot.py b/modules/boot.py index 589110e..c99aabc 100644 --- a/modules/boot.py +++ b/modules/boot.py @@ -2,6 +2,7 @@ import tidal import tidal_helpers +import ecc108a from esp32 import Partition # sleep_sel just gets in the way of using lightsleep @@ -10,6 +11,7 @@ # Initialize USB early on tidal.usb.initialize() tidal.init_lcd() +ecc108a.init() if tidal.BUTTON_FRONT.value() == 0: # Boot to the recovery menu @@ -23,6 +25,7 @@ from app_launcher import Launcher menu = Launcher() + Launcher._singleton = menu try: os.mkdir("/apps") diff --git a/modules/ecc108a_tools/__init__.py b/modules/ecc108a_tools/__init__.py new file mode 100644 index 0000000..1774086 --- /dev/null +++ b/modules/ecc108a_tools/__init__.py @@ -0,0 +1,61 @@ +import ecc108a + +lock_state = {0x55: 'unlocked', 0: 'locked'} + +def run(): + ecc108a.init() + config = ecc108a.read_config() + + serial_number = config[0:4] + config[8:13] + serial_number_str = ' '.join(f'{c:02x}' for c in serial_number) + print(f"Serial number: {serial_number_str}") + print(f"Revision number: {hex(int.from_bytes(config[4:8], 'little'))}") + print(f"Reserved 1: {hex(config[13])}") + print(f"Interface mode: {'I2C' if config[14] & 1 else 'SWI'}") + print(f"Reserved 2: {hex(config[14] & 0xfe)} {hex(config[15])}") + if config[14] & 1: + print(f"I2C address: {hex(config[16])}") + else: + print(f"GPIO mode: {hex(config[16])}") + print(f"Reserved 3: {hex(config[17])}") + otp_modes = {0xaa: 'read-only', 0x55: 'consumption', 0x0: 'legacy'} + print(f"OTP mode: {hex(config[18])} ({otp_modes.get(config[18], 'unknown')})") + print(f"Selector mode: {'only zero' if config[19] & 1 else 'unlimited'}") + print(f"TTL reference mode: {'VCC' if config[19] & 2 else 'fixed'}") + print(f"Watchdog timeout: {'10s' if config[19] & 4 else '1.3s'}") + print(f"Reserved 4: {hex(config[19] & 0xf8)}") + + print(f"UserExtra: {hex(config[84])}") + print(f"Selector: {hex(config[85])}") + print(f"LockValue: {hex(config[86])} ({lock_state.get(config[86])})") + print(f"LockConfig: {hex(config[87])} ({lock_state.get(config[87])})") + + slotlocked = int.from_bytes(config[88:90], 'little') + + print(f"Reserved 5: {hex(config[90])}") # "must be zero" actually 0xffff, and changes when keys are modified somehow + print(f"Reserved 6: {hex(config[91])}") + + for cert in range(4): + print(f"X509format[{cert}].PublicPosition: {config[92 + cert] & 0xf}") + print(f"X509format[{cert}].TemplateLength: {(config[92 + cert] >> 4) & 0xf}") + + for key in range(16): + print(f"Key {key}:") + print(f" SlotLocked: {'1 (unlocked)' if slotlocked & (1 << key) else '0 (locked)'}") + + slotconfig = int.from_bytes(config[key * 2:][20:22], 'little') + keyconfig = int.from_bytes(config[key * 2:][96:98], 'little') + print(f" SlotConfig: {slotconfig:04x}") + print(f" KeyConfig: {keyconfig:04x}") + + if key < 8: + print(f" UseFlag: {hex(config[52 + key * 2])}") + update_count = config[53 + key * 2] + print(f" UpdateCount: {config[53 + key * 2]}") + + if key == 15: + keyuse_str = ' '.join(f'{c:02x}' for c in config[68:84]) + print(f" Key use: {keyuse_str}") + + # If private, Sign, GenKey and PrivWrite are the only commands that work, and vice versa + diff --git a/modules/scheduler.py b/modules/scheduler.py index 336f743..49329ea 100644 --- a/modules/scheduler.py +++ b/modules/scheduler.py @@ -4,6 +4,7 @@ import time import uasyncio import wifi +import esp32 _scheduler = None @@ -98,6 +99,16 @@ def enter(self): enter_level = self._level deactivated_app = None while True: + if first_time: + try: + hid_mode = esp32.NVS("tidal").get_i32("enable_u2f") + except OSError: + hid_mode = 0 + if hid_mode == 1: + # Add the U2F periodic task + import authenticator + authenticator.allow_interrupt_when_authenticating() + while self.check_for_interrupts() or first_time: first_time = False uasyncio.run_until_complete() diff --git a/modules/settings.py b/modules/settings.py index f486087..3836c28 100644 --- a/modules/settings.py +++ b/modules/settings.py @@ -25,6 +25,13 @@ def set(k, v): _settings[k] = v _modified = True +def delete(k): + global _modified + if _settings is None: + load() + del _settings[k] + _modified = True + def save(): global _settings, _modified if _settings is None: diff --git a/modules/settings_app/__init__.py b/modules/settings_app/__init__.py index 00e7e2f..1f9dd24 100644 --- a/modules/settings_app/__init__.py +++ b/modules/settings_app/__init__.py @@ -151,6 +151,7 @@ def refresh(self): (5, 15, 30, 60, 5*60, 10*60, 30*60, 60*60, 8*60*60)), self.make_choice("USB sleep delay", None, "usb_nosleep_time", 15, fmt_time, (15, 30, 60)), self.make_choice("UART menu app", None, "uart_menu_app", True, fmt_on_off, (True, False)), + self.make_nvs_choice("MFA mode", None, "enable_u2f", True, fmt_on_off, (True, False)), self.make_nvs_choice("REPL on SDA/SCL", None, "uart_sdascl", 0, fmt_on_off, (1, 0)), ) diff --git a/scripts/build.sh b/scripts/build.sh index 89076b9..a598953 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -12,5 +12,5 @@ cd ports/esp32/boards ln -sfn /firmware/tildamk6 ./tildamk6 cd .. -make submodules BOARD=tildamk6 USER_C_MODULES=/firmware/drivers/micropython.cmake -make BOARD=tildamk6 USER_C_MODULES=/firmware/drivers/micropython.cmake $@ +make submodules BOARD=tildamk6 USER_COMPONENTS=/firmware/components/tinyusb USER_C_MODULES=/firmware/drivers/micropython.cmake +make BOARD=tildamk6 USER_COMPONENTS=/firmware/components/tinyusb USER_C_MODULES=/firmware/drivers/micropython.cmake $@ diff --git a/tildamk6/sdkconfig.board b/tildamk6/sdkconfig.board index a141a1e..9c6c018 100644 --- a/tildamk6/sdkconfig.board +++ b/tildamk6/sdkconfig.board @@ -13,21 +13,33 @@ CONFIG_APP_EXCLUDE_PROJECT_VER_VAR=n CONFIG_APP_EXCLUDE_PROJECT_NAME_VAR=n CONFIG_APP_PROJECT_VER_FROM_CONFIG=y +# General USB configuration CONFIG_USB_ENABLED=y CONFIG_TINYUSB=y -CONFIG_TINYUSB_HID_ENABLED=y -CONFIG_TINYUSB_CDC_ENABLED=y -CONFIG_TINYUSB_HID_BUFSIZE=128 +CONFIG_TINYUSB_HID_BUFSIZE=256 CONFIG_TINYUSB_DESC_HID_STRING="TiDAL badge" CONFIG_TINYUSB_DESC_MANUFACTURER_STRING="Electromagnetic Field" CONFIG_TINYUSB_DESC_PRODUCT_STRING="TiDAL" - CONFIG_TINYUSB_DESC_CUSTOM_VID=0x16D0 CONFIG_TINYUSB_DESC_CUSTOM_PID=0x1114 CONFIG_TINYUSB_DESC_USE_ESPRESSIF_VID= CONFIG_TINYUSB_DESC_USE_DEFAULT_PID= -#CONFIG_LOG_DEFAULT_LEVEL_INFO=y -#CONFIG_LOG_DEFAULT_LEVEL_ERROR=n +# USB feature flags +CONFIG_USB_CDC_ENABLED=y +CONFIG_TINYUSB_CDC_ENABLED=y +CONFIG_TINYUSB_HID_ENABLED=y +CONFIG_TINYUSB_HIDKEYBOARD_ENABLED=y +CONFIG_TINYUSB_U2FHID_ENABLED=y +CONFIG_TINYUSB_MSC_ENABLED=n +CONFIG_TINYUSB_MIDI_ENABLED=n +CONFIG_TINYUSB_CUSTOM_CLASS_ENABLED=n +CONFIG_TINYUSB_NET_ENABLED=n +CONFIG_TINYUSB_BTH_ENABLED=n +CONFIG_TINYUSB_DFU_ENABLED=n + +# Development experimental flags +CONFIG_LOG_DEFAULT_LEVEL_INFO=y +CONFIG_LOG_DEFAULT_LEVEL_ERROR=n #CONFIG_FREERTOS_USE_TICKLESS_IDLE=y #CONFIG_PM_DFS_INIT_AUTO=y