#include "atca_basic.h"
#include "atca_execution.h"
#include "host/atca_host.h"
ATCA_STATUS atcab_write(uint8_t zone, uint16_t address, const uint8_t *value, const uint8_t *mac)
{
ATCAPacket packet;
ATCACommand ca_cmd = _gDevice->mCommands;
ATCA_STATUS status = ATCA_GEN_FAIL;
if (value == NULL)
{
return ATCA_BAD_PARAM;
}
do
{
packet.param1 = zone;
packet.param2 = address;
if (zone & ATCA_ZONE_READWRITE_32)
{
memcpy(packet.data, value, 32);
if (mac)
{
memcpy(&packet.data[32], mac, 32);
}
}
else
{
memcpy(packet.data, value, 4);
}
if ((status = atWrite(ca_cmd, &packet, mac && (zone & ATCA_ZONE_READWRITE_32))) != ATCA_SUCCESS)
{
break;
}
if ((status = atca_execute_command(&packet, _gDevice)) != ATCA_SUCCESS)
{
break;
}
}
while (0);
return status;
}
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_GEN_FAIL;
uint16_t addr;
if (data == NULL)
{
return ATCA_BAD_PARAM;
}
if (len != 4 && len != 32)
{
return ATCA_BAD_PARAM;
}
do
{
if ((status = atcab_get_addr(zone, slot, block, offset, &addr)) != ATCA_SUCCESS)
{
break;
}
if (len == ATCA_BLOCK_SIZE)
{
zone = zone | ATCA_ZONE_READWRITE_32;
}
status = atcab_write(zone, addr, data, NULL);
}
while (0);
return status;
}
#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)
{
uint8_t num_in[NONCE_NUMIN_SIZE] = { 0 };
#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_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
{
if (data == NULL || enc_key == NULL)
{
status = ATCA_BAD_PARAM;
break;
}
if ((status = atcab_read_zone(ATCA_ZONE_CONFIG, 0, 0, 0, serial_num, 32)) != ATCA_SUCCESS)
{
break;
}
memmove(&serial_num[4], &serial_num[8], 5);
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 = atcab_nonce_rand(num_in, rand_out)) != ATCA_SUCCESS)
{
BREAK(status, "Nonce failed");
}
if ((status = atcah_nonce(&nonce_params)) != ATCA_SUCCESS)
{
BREAK(status, "Calc TempKey failed");
}
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);
if ((status = atcab_gendig(GENDIG_ZONE_DATA, enc_key_id, other_data, sizeof(other_data))) != ATCA_SUCCESS)
{
BREAK(status, "GenDig failed");
}
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)
{
BREAK(status, "atcah_gen_dig() failed");
}
if ((status = atcab_get_addr(ATCA_ZONE_DATA, key_id, block, 0, &addr)) != ATCA_SUCCESS)
{
BREAK(status, "Get address failed");
}
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)
{
BREAK(status, "Calculate Auth MAC failed");
}
status = atcab_write(write_mac_param.zone, write_mac_param.key_id, write_mac_param.encrypted_data, write_mac_param.auth_mac);
}
while (0);
return status;
}
ATCA_STATUS atcab_write_config_zone(const uint8_t* config_data)
{
ATCA_STATUS status = ATCA_GEN_FAIL;
size_t config_size = 0;
if (config_data == NULL)
{
return ATCA_BAD_PARAM;
}
do
{
status = atcab_get_zone_size(ATCA_ZONE_CONFIG, 0, &config_size);
if (status != ATCA_SUCCESS)
{
break;
}
status = atcab_write_bytes_zone(ATCA_ZONE_CONFIG, 0, 16, &config_data[16], config_size - 16);
if (status != ATCA_SUCCESS)
{
break;
}
status = atcab_updateextra(UPDATE_MODE_USER_EXTRA, config_data[84]);
if (status != ATCA_SUCCESS)
{
break;
}
status = atcab_updateextra(UPDATE_MODE_SELECTOR, config_data[85]);
if (status != ATCA_SUCCESS)
{
break;
}
}
while (0);
return status;
}
ATCA_STATUS atcab_write_pubkey(uint16_t slot, const uint8_t *public_key)
{
ATCA_STATUS status = ATCA_SUCCESS;
uint8_t public_key_formatted[ATCA_BLOCK_SIZE * 3];
int block;
if (public_key == NULL)
{
return ATCA_BAD_PARAM;
}
memset(public_key_formatted, 0, sizeof(public_key_formatted));
memcpy(&public_key_formatted[4], &public_key[0], 32); memcpy(&public_key_formatted[40], &public_key[32], 32);
for (block = 0; block < 3; block++)
{
status = atcab_write_zone(ATCA_ZONE_DATA, slot, block, 0, &public_key_formatted[ATCA_BLOCK_SIZE * block], ATCA_BLOCK_SIZE);
if (status != ATCA_SUCCESS)
{
break;
}
}
return status;
}
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_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_BAD_PARAM;
}
if (zone == ATCA_ZONE_DATA && slot > 15)
{
return ATCA_BAD_PARAM;
}
if (length == 0)
{
return ATCA_SUCCESS; }
if (data == NULL)
{
return ATCA_BAD_PARAM;
}
if (offset_bytes % ATCA_WORD_SIZE != 0 || length % ATCA_WORD_SIZE != 0)
{
return ATCA_BAD_PARAM;
}
do
{
status = atcab_get_zone_size(zone, slot, &zone_size);
if (status != ATCA_SUCCESS)
{
break;
}
if (offset_bytes + length > zone_size)
{
return ATCA_BAD_PARAM;
}
cur_block = offset_bytes / ATCA_BLOCK_SIZE;
cur_word = (offset_bytes % ATCA_BLOCK_SIZE) / ATCA_WORD_SIZE;
while (data_idx < length)
{
if (cur_word == 0 && length - data_idx >= ATCA_BLOCK_SIZE && !(zone == ATCA_ZONE_CONFIG && cur_block == 2))
{
status = atcab_write_zone(zone, slot, (uint8_t)cur_block, 0, &data[data_idx], ATCA_BLOCK_SIZE);
if (status != ATCA_SUCCESS)
{
break;
}
data_idx += ATCA_BLOCK_SIZE;
cur_block += 1;
}
else
{
if (!(zone == ATCA_ZONE_CONFIG && cur_block == 2 && cur_word == 5))
{
status = atcab_write_zone(zone, slot, (uint8_t)cur_block, (uint8_t)cur_word, &data[data_idx], ATCA_WORD_SIZE);
if (status != ATCA_SUCCESS)
{
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;
}
ATCA_STATUS atcab_write_config_counter(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_BAD_PARAM;
}
lin_a = 0xFFFF >> (counter_value % 32);
lin_b = 0xFFFF >> ((counter_value >= 16) ? (counter_value - 16) % 32 : 0);
bin_a = counter_value / 32;
bin_b = (counter_value >= 16) ? ((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 = atcab_write_bytes_zone(ATCA_ZONE_CONFIG, 0, 52 + counter_id * 8, bytes, sizeof(bytes));
return status;
}