/** * \file * * \brief Provides APIs to use with ATSHA206A device. * * \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 "cryptoauthlib.h" #include "api_206a.h" /** \brief Computes the diversified key based on the parent key provided and device serial number * \param[in] parent_key parent key to be diversified * \param[out] diversified_key diversified parent key * \return ATCA_SUCCESS on success, otherwise an error code. */ ATCA_STATUS sha206a_diversify_parent_key(uint8_t* parent_key, uint8_t* diversified_key) { ATCA_STATUS status; uint8_t sha_data[96]; uint8_t* p_temp = sha_data; uint8_t param1 = 0x0; //These are fixed values.. Should match with value used in provisioning uint16_t param2 = 0x0; uint8_t sn[9]; if ((parent_key == NULL) || (diversified_key == NULL)) { return ATCA_BAD_PARAM; } do { if ((status = atcab_read_serial_number(sn)) != ATCA_SUCCESS) { break; } memcpy(p_temp, parent_key, ATCA_KEY_SIZE); p_temp += ATCA_KEY_SIZE; *p_temp++ = ATCA_DERIVE_KEY; *p_temp++ = param1; *p_temp++ = param2 & 0xFF; *p_temp++ = (param2 >> 8) & 0xFF; *p_temp++ = sn[8]; *p_temp++ = sn[0]; *p_temp++ = sn[1]; memset(p_temp, 0, 25); p_temp += 25; memcpy(p_temp, sn, sizeof(sn)); p_temp += sizeof(sn); memset(p_temp, 0, 23); if ((status = atcac_sw_sha2_256(sha_data, sizeof(sha_data), diversified_key)) != ATCA_SUCCESS) { break; } } while (0); return status; } /** \brief Generates the derived key based on the parent key and other parameters provided * \param[in] parent_key Input data contains device's parent key * \param[out] derived_key Output data derived from parent key * \param[in] param1 Input data to be used in derive key calculation * \param[in] param2 Input data to be used in derive key calculation * \return ATCA_SUCCESS on success, otherwise an error code. */ ATCA_STATUS sha206a_generate_derive_key(uint8_t* parent_key, uint8_t* derived_key, uint8_t param1, uint16_t param2) { ATCA_STATUS status; uint8_t sha_data[96]; uint8_t* p_temp = sha_data; uint8_t sn[9]; if ((parent_key == NULL) || (derived_key == NULL) || (param1 == 0xFF)) { return ATCA_BAD_PARAM; } do { if ((status = atcab_derivekey(param1, param2, NULL)) != ATCA_SUCCESS) { break; } if ((status = atcab_read_serial_number(sn)) != ATCA_SUCCESS) { break; } memcpy(p_temp, parent_key, ATCA_KEY_SIZE); p_temp += ATCA_KEY_SIZE; *p_temp++ = ATCA_DERIVE_KEY; *p_temp++ = param1; *p_temp++ = param2 & 0xFF; *p_temp++ = (param2 >> 8) & 0xFF; *p_temp++ = sn[8]; *p_temp++ = sn[0]; *p_temp++ = sn[1]; memset(p_temp, 0, (25 + 32)); if ((status = atcac_sw_sha2_256(sha_data, sizeof(sha_data), derived_key)) != ATCA_SUCCESS) { break; } } while (0); return status; } /** \brief Generates the response based on Key and Challenge provided * \param[in] key Input data contains device's key * \param[in] challenge Input data to be used in challenge response calculation * \param[out] response response derived from key and challenge * \return ATCA_SUCCESS on success, otherwise an error code. */ ATCA_STATUS sha206a_generate_challenge_response_pair(uint8_t* key, uint8_t* challenge, uint8_t* response) { ATCA_STATUS status; uint8_t sha_data[88]; uint8_t* p_temp = sha_data; uint8_t param1 = 0x0; //Should use the same value while authenticating with device uint16_t param2 = 0x0; uint8_t sn[9]; if ((key == NULL) || (challenge == NULL) || (response == NULL)) { return ATCA_BAD_PARAM; } do { if ((status = atcab_read_serial_number(sn)) != ATCA_SUCCESS) { break; } memcpy(p_temp, key, ATCA_KEY_SIZE); p_temp += ATCA_KEY_SIZE; memcpy(p_temp, challenge, ATCA_KEY_SIZE); p_temp += ATCA_KEY_SIZE; *p_temp++ = ATCA_MAC; *p_temp++ = param1; *p_temp++ = param2 & 0xFF; *p_temp++ = (param2 >> 8) & 0xFF; memset(p_temp, 0, (8 + 3)); p_temp += (8 + 3); *p_temp++ = sn[8]; memset(p_temp, 0, 4); p_temp += 4; *p_temp++ = sn[0]; *p_temp++ = sn[1]; memset(p_temp, 0, 2); if ((status = atcac_sw_sha2_256(sha_data, sizeof(sha_data), response)) != ATCA_SUCCESS) { break; } } while (0); return status; } /** \brief verifies the challenge and provided response using key in device * \param[in] challenge Challenge to be used in the response calculations * \param[in] expected_response Expected response from the device. * \param[out] is_authenticated result of expected of response and calcualted response * \return ATCA_SUCCESS on success, otherwise an error code. */ ATCA_STATUS sha206a_authenticate(uint8_t* challenge, uint8_t* expected_response, uint8_t* is_authenticated) { ATCA_STATUS status; uint8_t is_consumed; uint8_t digest[ATCA_KEY_SIZE]; if ((challenge == NULL) || (expected_response == NULL) || (is_authenticated == NULL)) { return ATCA_BAD_PARAM; } do { if ((status = sha206a_verify_device_consumption(&is_consumed)) != ATCA_SUCCESS) { break; } if ((is_consumed & ATCA_SHA206A_PKEY_CONSUMPTION_MASK) == ATCA_SHA206A_PKEY_CONSUMPTION_MASK) { status = ATCA_USE_FLAGS_CONSUMED; break; } else if ((is_consumed & ATCA_SHA206A_DKEY_CONSUMPTION_MASK) == ATCA_SHA206A_DKEY_CONSUMPTION_MASK) { if ((status = atcab_derivekey(0xFF, 0x0, NULL)) != ATCA_SUCCESS) { break; } } if ((status = atcab_mac(0x00, ATCA_SHA206A_SYMMETRIC_KEY_ID_SLOT, challenge, digest)) != ATCA_SUCCESS) { break; } } while (0); *is_authenticated = false; if ((status == ATCA_SUCCESS) && (memcmp(digest, expected_response, ATCA_KEY_SIZE) == 0)) { *is_authenticated = true; } return status; } /** \brief verifies the device is fully consumed or not based on Parent and Derived Key use flags. * \param[out] is_consumed result of device consumption * \return ATCA_SUCCESS on success, otherwise an error code. */ ATCA_STATUS sha206a_verify_device_consumption(uint8_t* is_consumed) { ATCA_STATUS status; uint8_t is_dk_valid; uint8_t is_pk_valid = false; if (is_consumed == NULL) { return ATCA_BAD_PARAM; } *is_consumed = ATCA_SHA206A_DKEY_CONSUMPTION_MASK | ATCA_SHA206A_PKEY_CONSUMPTION_MASK; do { if ((status = sha206a_check_dk_useflag_validity(&is_dk_valid)) != ATCA_SUCCESS) { break; } if (is_dk_valid) { *is_consumed = 0; } else { if ((status = sha206a_check_pk_useflag_validity(&is_pk_valid)) != ATCA_SUCCESS) { break; } if (is_pk_valid) { *is_consumed &= ~ATCA_SHA206A_PKEY_CONSUMPTION_MASK; } } } while (0); return status; } /** \brief verifies Derived Key use flags for consumption * \param[out] is_consumed indicates if DK is available for consumption. * \return ATCA_SUCCESS on success, otherwise an error code. */ ATCA_STATUS sha206a_check_dk_useflag_validity(uint8_t* is_consumed) { ATCA_STATUS status; uint8_t dk_available_count; if (is_consumed == NULL) { return ATCA_BAD_PARAM; } *is_consumed = true; if ((status = sha206a_get_dk_useflag_count(&dk_available_count)) != ATCA_SUCCESS) { return status; } if (!dk_available_count) { *is_consumed = false; } return status; } /** \brief verifies Parent Key use flags for consumption * \param[out] is_consumed indicates if PK is available for consumption * \return ATCA_SUCCESS on success, otherwise an error code */ ATCA_STATUS sha206a_check_pk_useflag_validity(uint8_t* is_consumed) { ATCA_STATUS status; uint8_t pk_available_count; if (is_consumed == NULL) { return ATCA_BAD_PARAM; } *is_consumed = true; if ((status = sha206a_get_pk_useflag_count(&pk_available_count)) != ATCA_SUCCESS) { return status; } if (!pk_available_count) { *is_consumed = false; } return status; } /** \brief calculates available Derived Key use counts * \param[out] dk_available_count counts available bit's as 1 * \return ATCA_SUCCESS on success, otherwise an error code. */ ATCA_STATUS sha206a_get_dk_useflag_count(uint8_t* dk_available_count) { ATCA_STATUS status; uint8_t read_buf[4]; uint8_t bit_count; if (dk_available_count == NULL) { return ATCA_BAD_PARAM; } *dk_available_count = 0; if ((status = atcab_read_bytes_zone(ATCA_ZONE_CONFIG, 0, 64, read_buf, sizeof(read_buf))) != ATCA_SUCCESS) { return status; } for (bit_count = 0; bit_count < 8; bit_count++) { if (!(read_buf[2] & 0x01)) { break; } read_buf[2] >>= 1; } *dk_available_count = bit_count; return status; } /** \brief calculates available Parent Key use counts * \param[out] pk_available_count counts available bit's as 1 * \return ATCA_SUCCESS on success, otherwise an error code. */ ATCA_STATUS sha206a_get_pk_useflag_count(uint8_t* pk_available_count) { ATCA_STATUS status; uint8_t read_buf[16]; uint8_t byte_count; uint8_t bit_count; if (pk_available_count == NULL) { return ATCA_BAD_PARAM; } *pk_available_count = 0; if ((status = atcab_read_bytes_zone(ATCA_ZONE_CONFIG, 0, 68, read_buf, sizeof(read_buf))) != ATCA_SUCCESS) { return status; } for (bit_count = 0, byte_count = 16; bit_count < sizeof(read_buf) * 8; bit_count++) { if (bit_count % 8 == 0) { byte_count--; } if (!(read_buf[byte_count] & 0x01)) { break; } read_buf[byte_count] >>= 1; } *pk_available_count = bit_count; return status; } /** \brief Read Derived Key slot update count. It will be wraps around 256 * \param[out] dk_update_count returns number of times the slot has been updated with derived key * \return ATCA_SUCCESS on success, otherwise an error code. */ ATCA_STATUS sha206a_get_dk_update_count(uint8_t* dk_update_count) { ATCA_STATUS status; uint8_t read_buf[4]; if (dk_update_count == NULL) { return ATCA_BAD_PARAM; } *dk_update_count = 0; if ((status = atcab_read_bytes_zone(ATCA_ZONE_CONFIG, 0, 64, read_buf, sizeof(read_buf))) != ATCA_SUCCESS) { return status; } *dk_update_count = read_buf[3]; return status; } /** \brief Update the data store slot with user data and lock it if necessary * \param[in] slot Slot number to be written with data * \param[in] data Pointer that holds the data * \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] len data length * \param[in] lock_after_write set 1 to lock slot after write, otherwise 0 * \return ATCA_SUCCESS on success, otherwise an error code. */ ATCA_STATUS sha206a_write_data_store(uint8_t slot, uint8_t* data, uint8_t block, uint8_t offset, uint8_t len, bool lock_after_write) { ATCA_STATUS status; uint8_t zone = ATCA_ZONE_DATA; if ((slot < SHA206A_DATA_STORE0) || (slot > SHA206A_DATA_STORE2) || (slot == SHA206A_DATA_STORE0 && lock_after_write == true) || (data == NULL)) { return ATCA_BAD_PARAM; } if (lock_after_write) { zone |= ATCA_SHA206A_ZONE_WRITE_LOCK; } status = atcab_write_zone(zone, slot, block, offset, data, len); return status; } /** \brief Read the data stored in Data store * \param[in] slot Slot number to read from * \param[in] data Pointer to hold slot data data * \param[in] offset Byte offset within the zone to read from. * \param[in] len data length * \return ATCA_SUCCESS on success, otherwise an error code. */ ATCA_STATUS sha206a_read_data_store(uint8_t slot, uint8_t* data, uint8_t offset, uint8_t len) { ATCA_STATUS status; uint8_t zone = ATCA_ZONE_DATA; if ((slot < SHA206A_DATA_STORE0) || (slot > SHA206A_DATA_STORE2) || (data == NULL)) { return ATCA_BAD_PARAM; } status = atcab_read_bytes_zone(zone, slot, offset, data, len); return status; } /** \brief Returns the lock status of the given data store * \param[in] slot Slot number of the data store * \param[out] is_locked lock status of the data store * \return ATCA_SUCCESS on success, otherwise an error code. */ ATCA_STATUS sha206a_get_data_store_lock_status(uint8_t slot, uint8_t* is_locked) { ATCA_STATUS status; uint16_t read_buf[2]; if ((is_locked == NULL) || (slot < SHA206A_DATA_STORE1) || (slot > SHA206A_DATA_STORE2)) { return ATCA_BAD_PARAM; } *is_locked = true; if ((status = atcab_read_bytes_zone(ATCA_ZONE_CONFIG, 0, 4, read_buf, sizeof(read_buf))) != ATCA_SUCCESS) { return status; } if (slot == SHA206A_DATA_STORE1) { *is_locked = read_buf[0] == 0x5555 ? false : true; } else { *is_locked = read_buf[1] == 0x5555 ? false : true; } return status; }