/** * \file * \brief Client side cert i/o methods. These declarations deal with the client-side, the node being authenticated, * of the authentication process. It is assumed the client has an ECC CryptoAuthentication device * (e.g. ATECC508A) and the certificates are stored on that 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 "atcacert_client.h" #include "atcacert_pem.h" #include "cryptoauthlib.h" #include "basic/atca_basic.h" // Perform floor integer division (-1 / 2 == -1) instead of truncate towards zero (-1 / 2 == 0) 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; // Cert data isn't written to the config zone, only read } if (device_locs[i].zone == DEVZONE_DATA && device_locs[i].is_genkey) { continue; // Public key is generated not written } 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; // Check the pointers if (csr_def == NULL || csr == NULL || csr_size == NULL) { return ATCACERT_E_BAD_PARAMS; } csr_max_size = *csr_size; *csr_size = 0; // Create DER CSR 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; } // Move the DER CSR to the end of the buffer, so we can encode it into // PEM in place. 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 { // Check the pointers if (csr_def == NULL || csr == NULL || csr == NULL || csr_size == NULL) { status = ATCACERT_E_BAD_PARAMS; BREAK(status, "Null input parameter"); } // Check the csr buffer size if (*csr_size < csr_def->cert_template_size) { status = ATCACERT_E_BUFFER_TOO_SMALL; BREAK(status, "CSR buffer size too small"); } // Copy the CSR template into the CSR that will be returned memcpy(csr, csr_def->cert_template, csr_def->cert_template_size); csr_max_size = *csr_size; *csr_size = csr_def->cert_template_size; // Get a few elements from the csr_def structure 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; // Get the public key from the device if (pub_dev_loc->is_genkey) { // Calculate the public key from the private key status = atcab_get_pubkey(key_slot, pub_key); if (status != ATCA_SUCCESS) { BREAK(status, "Could not generate public key"); } } else { // Read the public key from a slot status = atcab_read_pubkey(key_slot, pub_key); if (status != ATCA_SUCCESS) { BREAK(status, "Could not read public key"); } } // Insert the public key into the CSR template 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"); } // Get the CSR TBS digest status = atcacert_get_tbs_digest(csr_def, csr, *csr_size, tbs_digest); if (status != ATCA_SUCCESS) { BREAK(status, "Get TBS digest failed"); } // Sign the TBS digest status = atcab_sign(priv_key_slot, tbs_digest, sig); if (status != ATCA_SUCCESS) { BREAK(status, "Signing CSR failed"); } // Insert the signature into the CSR template status = atcacert_set_signature(csr_def, csr, csr_size, csr_max_size, sig); if (status != ATCA_SUCCESS) { BREAK(status, "Setting CSR signature failed"); } // The exact size of the csr cannot be determined until after adding the signature // it is returned in the csr_size parameter. (*csr_size = *csr_size;) } while (false); return status; }