#include <stdlib.h>
#include "atcacert_client.h"
#include "atcacert_pem.h"
#include "cryptoauthlib.h"
#include "basic/atca_basic.h"
static int floor_div(int a, int b)
{
int d = a / b;
int r = a % b;
return r ? (d - ((a < 0) ^ (b < 0))) : d;
}
int atcacert_get_response(uint8_t device_private_key_slot,
const uint8_t challenge[32],
uint8_t response[64])
{
if (device_private_key_slot > 15 || challenge == NULL || response == NULL)
{
return ATCACERT_E_BAD_PARAMS;
}
return atcab_sign(device_private_key_slot, challenge, response);
}
int atcacert_read_device_loc(const atcacert_device_loc_t* device_loc,
uint8_t* data)
{
int ret = 0;
if (device_loc->zone == DEVZONE_DATA && device_loc->is_genkey)
{
uint8_t public_key[ATCA_PUB_KEY_SIZE];
if (device_loc->offset + device_loc->count > sizeof(public_key))
{
return ATCACERT_E_BAD_PARAMS;
}
ret = atcab_get_pubkey(device_loc->slot, public_key);
if (ret != ATCA_SUCCESS)
{
return ret;
}
memcpy(data, &public_key[device_loc->offset], device_loc->count);
}
else
{
size_t count = device_loc->count;
size_t zone_size;
ret = atcab_get_zone_size(device_loc->zone, device_loc->slot, &zone_size);
if (ret != ATCA_SUCCESS)
{
return ret;
}
if (device_loc->offset + device_loc->count > (uint16_t)zone_size)
{
if (device_loc->offset > zone_size)
{
return ATCACERT_E_BAD_PARAMS;
}
count = zone_size - device_loc->offset;
}
ret = atcab_read_bytes_zone(
device_loc->zone,
device_loc->slot,
device_loc->offset,
data,
count);
if (ret != ATCA_SUCCESS)
{
return ret;
}
}
return ATCACERT_E_SUCCESS;
}
int atcacert_read_cert(const atcacert_def_t* cert_def,
const uint8_t ca_public_key[64],
uint8_t* cert,
size_t* cert_size)
{
int ret = 0;
atcacert_device_loc_t device_locs[16];
size_t device_locs_count = 0;
size_t i = 0;
atcacert_build_state_t build_state;
if (cert_def == NULL || cert == NULL || cert_size == NULL)
{
return ATCACERT_E_BAD_PARAMS;
}
ret = atcacert_get_device_locs(
cert_def,
device_locs,
&device_locs_count,
sizeof(device_locs) / sizeof(device_locs[0]),
ATCA_BLOCK_SIZE);
if (ret != ATCACERT_E_SUCCESS)
{
return ret;
}
ret = atcacert_cert_build_start(&build_state, cert_def, cert, cert_size, ca_public_key);
if (ret != ATCACERT_E_SUCCESS)
{
return ret;
}
for (i = 0; i < device_locs_count; i++)
{
static uint8_t data[416];
ret = atcacert_read_device_loc(&device_locs[i], data);
if (ret != ATCACERT_E_SUCCESS)
{
return ret;
}
ret = atcacert_cert_build_process(&build_state, &device_locs[i], data);
if (ret != ATCACERT_E_SUCCESS)
{
return ret;
}
}
ret = atcacert_cert_build_finish(&build_state);
if (ret != ATCACERT_E_SUCCESS)
{
return ret;
}
return ATCACERT_E_SUCCESS;
}
int atcacert_write_cert(const atcacert_def_t* cert_def,
const uint8_t* cert,
size_t cert_size)
{
int ret = 0;
atcacert_device_loc_t device_locs[16];
size_t device_locs_count = 0;
size_t i = 0;
if (cert_def == NULL || cert == NULL)
{
return ATCACERT_E_BAD_PARAMS;
}
ret = atcacert_get_device_locs(
cert_def,
device_locs,
&device_locs_count,
sizeof(device_locs) / sizeof(device_locs[0]),
ATCA_BLOCK_SIZE);
if (ret != ATCACERT_E_SUCCESS)
{
return ret;
}
for (i = 0; i < device_locs_count; i++)
{
int end_block;
int start_block;
static uint8_t data[416];
int block;
if (device_locs[i].zone == DEVZONE_CONFIG)
{
continue; }
if (device_locs[i].zone == DEVZONE_DATA && device_locs[i].is_genkey)
{
continue;
}
ret = atcacert_get_device_data(cert_def, cert, cert_size, &device_locs[i], data);
if (ret != ATCACERT_E_SUCCESS)
{
return ret;
}
start_block = device_locs[i].offset / ATCA_BLOCK_SIZE;
end_block = floor_div((int)(device_locs[i].offset + device_locs[i].count) - 1, ATCA_BLOCK_SIZE);
for (block = start_block; block <= end_block; block++)
{
ret = atcab_write_zone(
device_locs[i].zone,
device_locs[i].slot,
(uint8_t)block,
0,
&data[(block - start_block) * ATCA_BLOCK_SIZE],
ATCA_BLOCK_SIZE);
if (ret != ATCA_SUCCESS)
{
return ret;
}
}
}
return ATCACERT_E_SUCCESS;
}
int atcacert_create_csr_pem(const atcacert_def_t* csr_def, char* csr, size_t* csr_size)
{
ATCA_STATUS status = ATCA_SUCCESS;
size_t csr_max_size;
size_t csr_der_size;
if (csr_def == NULL || csr == NULL || csr_size == NULL)
{
return ATCACERT_E_BAD_PARAMS;
}
csr_max_size = *csr_size;
*csr_size = 0;
csr_der_size = csr_max_size;
status = atcacert_create_csr(csr_def, (uint8_t*)csr, &csr_der_size);
if (status != ATCACERT_E_SUCCESS)
{
return status;
}
memmove(csr + (csr_max_size - csr_der_size), csr, csr_der_size);
*csr_size = csr_max_size;
status = atcacert_encode_pem_csr((uint8_t*)(csr + (csr_max_size - csr_der_size)), csr_der_size, csr, csr_size);
if (status != ATCACERT_E_SUCCESS)
{
return status;
}
return ATCACERT_E_SUCCESS;
}
int atcacert_create_csr(const atcacert_def_t* csr_def, uint8_t* csr, size_t* csr_size)
{
ATCA_STATUS status = ATCA_SUCCESS;
uint8_t pub_key[ATCA_PUB_KEY_SIZE] = { 0 };
uint8_t sig[ATCA_SIG_SIZE] = { 0 };
const atcacert_device_loc_t* pub_dev_loc = NULL;
const atcacert_cert_loc_t* pub_loc = NULL;
uint16_t key_slot = 0;
uint16_t priv_key_slot = 0;
uint8_t tbs_digest[ATCA_BLOCK_SIZE] = { 0 };
size_t csr_max_size = 0;
do
{
if (csr_def == NULL || csr == NULL || csr == NULL || csr_size == NULL)
{
status = ATCACERT_E_BAD_PARAMS;
BREAK(status, "Null input parameter");
}
if (*csr_size < csr_def->cert_template_size)
{
status = ATCACERT_E_BUFFER_TOO_SMALL;
BREAK(status, "CSR buffer size too small");
}
memcpy(csr, csr_def->cert_template, csr_def->cert_template_size);
csr_max_size = *csr_size;
*csr_size = csr_def->cert_template_size;
pub_loc = &(csr_def->std_cert_elements[STDCERT_PUBLIC_KEY]);
pub_dev_loc = &(csr_def->public_key_dev_loc);
key_slot = pub_dev_loc->slot;
priv_key_slot = csr_def->private_key_slot;
if (pub_dev_loc->is_genkey)
{
status = atcab_get_pubkey(key_slot, pub_key);
if (status != ATCA_SUCCESS)
{
BREAK(status, "Could not generate public key");
}
}
else
{
status = atcab_read_pubkey(key_slot, pub_key);
if (status != ATCA_SUCCESS)
{
BREAK(status, "Could not read public key");
}
}
status = atcacert_set_cert_element(csr_def, pub_loc, csr, *csr_size, pub_key, ATCA_PUB_KEY_SIZE);
if (status != ATCA_SUCCESS)
{
BREAK(status, "Setting CSR public key failed");
}
status = atcacert_get_tbs_digest(csr_def, csr, *csr_size, tbs_digest);
if (status != ATCA_SUCCESS)
{
BREAK(status, "Get TBS digest failed");
}
status = atcab_sign(priv_key_slot, tbs_digest, sig);
if (status != ATCA_SUCCESS)
{
BREAK(status, "Signing CSR failed");
}
status = atcacert_set_signature(csr_def, csr, csr_size, csr_max_size, sig);
if (status != ATCA_SUCCESS)
{
BREAK(status, "Setting CSR signature failed");
}
}
while (false);
return status;
}