/** * \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" #include "crypto/atca_crypto_sw_sha2.h" /** \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 */ 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; } /** \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. */ 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 ECC608A, 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 { return ATCA_BAD_PARAM; } return ATCA_SUCCESS; } /** \brief Decrypt data that's been encrypted by the IO protection key. * The ECDH and KDF commands on the ATECC608A are the only ones that * support this operation. * * \param[inout] param Parameters required to perform the operation. * * \return ATCA_SUCCESS on success, otherwise an error code. */ 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; } /** \brief Calculate the expected MAC on the host side for the Verify command. * * \param[inout] param Data required to perform the operation. * * \return ATCA_SUCCESS on success, otherwise an error code. */ 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; } /** \brief Encrypts the digest for the SecureBoot command when using the * encrypted digest / validating mac option. * * \param[inout] param Data required to perform the operation. * * \return ATCA_SUCCESS on success, otherwise an error code. */ 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; } /** \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[inout] param Data required to perform the operation. * * \return ATCA_SUCCESS on success, otherwise an error code. */ 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; } /** \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. */ 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; } /** \brief This function performs the checkmac operation to generate client response on the host side . * \param[inout] param Input and output parameters * \return ATCA_SUCCESS on success, otherwise an error code. */ 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; } /** \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. */ ATCA_STATUS atcah_hmac(struct atca_hmac_in_out *param) { // Local Variables struct atca_include_data_in_out include_data; uint8_t temporary[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, HMAC_BLOCK_SIZE - ATCA_KEY_SIZE); p_temp += 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, 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, HMAC_BLOCK_SIZE - ATCA_KEY_SIZE); p_temp += 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, HMAC_BLOCK_SIZE + ATCA_SHA_DIGEST_SIZE, param->response); // Update TempKey fields param->temp_key->valid = 0; return ATCA_SUCCESS; } /** \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. */ 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; } /** \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. */ 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; } /** \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. */ 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; } /** \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. */ 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; } /** \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: