demo/secure_element/commands.c

/*******************************************************************************
* # License
* Copyright 2019 Silicon Laboratories Inc. www.silabs.com
*******************************************************************************
*
* The licensor of this software is Silicon Laboratories Inc. Your use of this
* software is governed by the terms of Silicon Labs Master Software License
* Agreement (MSLA) available at
* www.silabs.com/about-us/legal/master-software-license-agreement. This
* software is distributed to you in Source Code format and is governed by the
* sections of the MSLA applicable to Source Code.
*
******************************************************************************/
#include "common.h"
GOS_CMD_CREATE_GETTER(se, status, "se.status", SC2('s','e','s'), 0);
GOS_CMD_CREATE_COMMAND(se, init, "se_init", "sei", 0, 0, true);
GOS_CMD_CREATE_COMMAND(se, reset, "se_reset", "ser", 0, 0, true);
GOS_CMD_CREATE_COMMAND(se, gen_csr, "se_gen_csr", "seg", 0, 0, true);
GOS_CMD_CREATE_COMMAND(se, save_creds, "se_save_creds", "ses", 1, 1, true);
GOS_CMD_CREATE_COMMAND(se, dump_creds, "se_dump_creds", "sedc", 0, 0, true);
/*************************************************************************************************
* Return the current SE status
*/
static gos_cmd_result_t se_get_status_command(int argc, char **argv)
{
uint8_t response_buffer[128];
MSGPACK_INIT_WITH_BUFFER(msgpack, response_buffer, sizeof(response_buffer));
gos_msgpack_write_dict_bool(&msgpack, "provisioned", is_provisioned);
gos_msgpack_write_dict_bool(&msgpack, "configured", is_configured);
return gos_cmd_write_response(GOS_CMD_SUCCESS, response_buffer, MSGPACK_BUFFER_USED(&msgpack));
}
/*************************************************************************************************
* Initialize the SE and return details about it
*/
static gos_cmd_result_t se_init_command(int argc, char **argv)
{
gos_result_t result;
uint8_t public_key[ATCA_PUB_KEY_SIZE];
uint8_t response_buffer[256];
MSGPACK_INIT_WITH_BUFFER(msgpack, response_buffer, sizeof(response_buffer));
// Calculate the device's public key
if(GOS_FAILED(result, gos_atca_calculate_public_key(SE_DEVICE_KEY_SLOT, public_key)))
{
create_response_message(&msgpack, "Failed to generate public key, err:%d", result);
}
else
{
char temp_str[48];
gos_msgpack_write_dict_str(&msgpack, "mac", temp_str);
gos_msgpack_write_dict_dict(&msgpack, "se", 3);
gos_msgpack_write_dict_str(&msgpack, "serial_number", temp_str);
gos_msgpack_write_dict_bin(&msgpack, "public_key", public_key, ATCA_PUB_KEY_SIZE);
gos_msgpack_write_dict_bool(&msgpack, "provisioned", is_provisioned);
gos_msgpack_write_dict_bool(&msgpack, "configured", is_configured);
}
return gos_cmd_write_response(GOS_CMD_RESULT(result), response_buffer, MSGPACK_BUFFER_USED(&msgpack));
}
/*************************************************************************************************
* Reset the SE to 'unprovisioned'
*/
static gos_cmd_result_t se_reset_command(int argc, char **argv)
{
gos_result_t result;
uint8_t metadata[SLOT8_SIZE];
uint8_t response_buffer[256];
MSGPACK_INIT_WITH_BUFFER(msgpack, response_buffer, sizeof(response_buffer));
memset(metadata, 0, sizeof(metadata));
// Reset the metadata 'slot'
// This will clear the 'provision_flag' from the metadata
if(GOS_FAILED(result, gos_atca_write_data_zone(SE_METADATA_SLOT, metadata, sizeof(metadata))))
{
create_response_message(&msgpack, "Failed to clear metadata, err:%d", result);
}
else
{
create_response_message(&msgpack, "Success");
}
return gos_cmd_write_response(GOS_CMD_RESULT(result), response_buffer, MSGPACK_BUFFER_USED(&msgpack));
}
/*************************************************************************************************
* Generate a certificate signing request (CSR) for the device certificate
*/
static gos_cmd_result_t se_gen_csr_command(int argc, char **argv)
{
gos_result_t result;
uint8_t csr_buffer[1024];
gos_buffer_t buffer =
{
.data = csr_buffer,
.size = sizeof(csr_buffer)
};
uint8_t response_buffer[sizeof(csr_buffer)+32];
MSGPACK_INIT_WITH_BUFFER(msgpack, response_buffer, sizeof(response_buffer));
// Invoke the SE to generate a Certificate Signing Request (CSR)
if(GOS_FAILED(result, gos_atca_generate_csr(&g_csr_def_3_device, &buffer)))
{
create_response_message(&msgpack, "Failed to generate device cert CSR, err:%d", result);
}
else
{
gos_msgpack_write_dict_bin(&msgpack, "csr", buffer.data, buffer.size);
}
return gos_cmd_write_response(GOS_CMD_RESULT(result), response_buffer, MSGPACK_BUFFER_USED(&msgpack));
}
/*************************************************************************************************
* Save the generated credentials to the SE
*/
static gos_cmd_result_t se_save_creds_command(int argc, char **argv)
{
#define CREDS_PAYLOAD_TIMEOUT_MS 15000
gos_result_t result;
uint8_t *payload = NULL;
gos_msgpack_object_t *root_obj = NULL;
uint8_t response_buffer[256];
intmax_t payload_length;
MSGPACK_INIT_WITH_BUFFER(msgpack, response_buffer, sizeof(response_buffer));
// Get the size of the payload
if(GOS_FAILED(result, str_parse_int(argv[0], &payload_length, 1, 4096)))
{
create_response_message(&msgpack, "Failed to payload length argument, err:%d", result);
}
// Allocate a buffer to hold the payload
else if(GOS_FAILED(result, gos_malloc("creds_payload", &payload, (uint32_t)payload_length)))
{
create_response_message(&msgpack, "Failed allocate buffer for payload, err:%d", result);
}
// Read the payload from the command
else if(GOS_FAILED(result, gos_cmd_read_data(payload, (uint32_t)payload_length, NULL, CREDS_PAYLOAD_TIMEOUT_MS)))
{
create_response_message(&msgpack, "Failed to read payload, err:%d", result);
}
// Decode the msgpack payload into msgpack objects
else if(GOS_FAILED(result, gos_msgpack_deserialize_with_buffer(&root_obj, payload, (uint32_t)payload_length, MSGPACK_FLAGS_NONE)))
{
create_response_message(&msgpack, "Failed to decode msgpack payload, err:%d", result);
}
else
{
uint8_t temp_buffer[2048];
uint8_t public_key[ATCA_PUB_KEY_SIZE];
gos_buffer_t buffer;
se_metadata_t metadata;
const uint8_t *cert_data;
uint32_t cert_length;
const gos_msgpack_object_bin_t *signer_ca_pub_obj = MSGPACK_DICT_BIN(root_obj, "signer_ca_pub");
const gos_msgpack_object_bin_t *signer_cert_obj = MSGPACK_DICT_BIN(root_obj, "signer_cert");
const gos_msgpack_object_bin_t *device_cert_obj = MSGPACK_DICT_BIN(root_obj, "device_cert");
const gos_msgpack_object_str_t *hostname_obj = MSGPACK_DICT_STR(root_obj, "hostname");
// Ensure the necessary fields are were in the payload
if(signer_ca_pub_obj == NULL ||
signer_cert_obj == NULL ||
device_cert_obj == NULL ||
hostname_obj == NULL)
{
result = GOS_BADARG;
create_response_message(&msgpack, "Payload missing required fields");
goto exit;
}
// Write the root CA cert's public key to the corresponding slot
MSGPACK_BIN(signer_ca_pub_obj, public_key, sizeof(public_key));
if(GOS_FAILED(result, gos_atca_write_public_key(SE_SIGNER_CA_PUBLIC_KEY_SLOT, public_key)))
{
create_response_message(&msgpack, "Failed to write signer CA public key, err:%d", result);
goto exit;
}
// Write the signer cert
cert_data = MSGPACK_BIN_VALUE(signer_cert_obj);
cert_length = MSGPACK_BIN_LENGTH(signer_cert_obj);
if(GOS_FAILED(result, gos_atca_write_cert(&g_cert_def_1_signer, cert_data, cert_length)))
{
create_response_message(&msgpack, "Failed to write signer cert, err:%d", result);
goto exit;
}
// Read back the signer cert
buffer.data = temp_buffer;
buffer.size = sizeof(temp_buffer);
if(GOS_FAILED(result, gos_atca_read_cert(&g_cert_def_1_signer, public_key, &buffer)))
{
create_response_message(&msgpack, "Failed to read back signer cert, err:%d", result);
goto exit;
}
// Validate the signer cert against the root CA's public key
if(GOS_FAILED(result, gos_atca_verify_cert(&g_cert_def_1_signer, public_key, buffer.data, buffer.size)))
{
create_response_message(&msgpack, "Failed to verify signer cert, err:%d", result);
goto exit;
}
// Extract the public key from the signer cert
if(GOS_FAILED(result, gos_atca_get_cert_public_key(&g_cert_def_1_signer, buffer.data, buffer.size, public_key)))
{
create_response_message(&msgpack, "Failed to retrieve public key from signer cert, err:%d", result);
goto exit;
}
// Write the device cert
cert_data = MSGPACK_BIN_VALUE(device_cert_obj);
cert_length = MSGPACK_BIN_LENGTH(device_cert_obj);
if(GOS_FAILED(result, gos_atca_write_cert(&g_cert_def_2_device, cert_data, cert_length)))
{
create_response_message(&msgpack, "Failed to write device cert, err:%d", result);
goto exit;
}
// Read back the device cert
buffer.data = temp_buffer;
buffer.size = sizeof(temp_buffer);
if(GOS_FAILED(result, gos_atca_read_cert(&g_cert_def_2_device, public_key, &buffer)))
{
create_response_message(&msgpack, "Failed to read back device cert, err:%d", result);
goto exit;
}
// Validate the device cert against the signer cert's public key
if(GOS_FAILED(result, gos_atca_verify_cert(&g_cert_def_2_device, public_key, buffer.data, buffer.size)))
{
create_response_message(&msgpack, "Failed to verify device cert, err:%d", result);
goto exit;
}
// Read the currently stored metadata
if(GOS_FAILED(result, gos_atca_read_data_zone(SE_METADATA_SLOT, &metadata, sizeof(metadata))))
{
create_response_message(&msgpack, "Failed to read metadata, err:%d", result);
goto exit;
}
// Copy the hostname and set the 'provision_flag'
char hostname[SLOT8_HOSTNAME_SIZE];
MSGPACK_STR(hostname_obj, hostname, sizeof(hostname));
memset(metadata.hostname, 0, sizeof(metadata.hostname));
metadata.hostname_size = strlen(hostname);
memcpy(metadata.hostname, hostname, metadata.hostname_size);
metadata.provision_flag = SLOT8_PROVISIONED_FLAG_VALUE;
// Write the updated metadata to the SE
if(GOS_FAILED(result, gos_atca_write_data_zone(SE_METADATA_SLOT, &metadata, sizeof(metadata))))
{
create_response_message(&msgpack, "Failed to write metadata, err:%d", result);
goto exit;
}
// At this point all the credentials have been written to the SE
// and the SE is fully provisioned
}
exit:
gos_free(payload);
if(result == GOS_SUCCESS)
{
create_response_message(&msgpack, "Success");
}
return gos_cmd_write_response(GOS_CMD_RESULT(result), response_buffer, MSGPACK_BUFFER_USED(&msgpack));
}
/*************************************************************************************************
* Dump the public credentials stored in the SE to the log bus
*/
static gos_cmd_result_t se_dump_creds_command(int argc, char **argv)
{
uint8_t temp_buffer[2048];
uint8_t public_key[ATCA_PUB_KEY_SIZE];
gos_result_t result;
gos_buffer_t buffer =
{
.data = temp_buffer,
.size = sizeof(temp_buffer)
};
if(GOS_FAILED(result, gos_atca_get_public_key(SE_SIGNER_CA_PUBLIC_KEY_SLOT, public_key)))
{
goto exit;
}
GOS_DUMP_UINT8_BUFFER(public_key, ATCA_PUB_KEY_SIZE, "Signer CA public key");
if(GOS_FAILED(result, gos_atca_read_cert(&g_cert_def_1_signer, public_key, &buffer)))
{
goto exit;
}
GOS_DUMP_UINT8_BUFFER(buffer.data, buffer.size, "Signer cert");
if(GOS_FAILED(result, gos_atca_get_cert_public_key(&g_cert_def_1_signer, buffer.data, buffer.size, public_key)))
{
goto exit;
}
GOS_DUMP_UINT8_BUFFER(public_key, ATCA_PUB_KEY_SIZE, "Signer cert public key");
buffer.data = temp_buffer;
buffer.size = sizeof(temp_buffer);
if(GOS_FAILED(result, gos_atca_read_cert(&g_cert_def_2_device, public_key, &buffer)))
{
goto exit;
}
GOS_DUMP_UINT8_BUFFER(buffer.data, buffer.size, "Device cert");
if(GOS_FAILED(result, gos_atca_get_cert_public_key(&g_cert_def_2_device, buffer.data, buffer.size, public_key)))
{
goto exit;
}
GOS_DUMP_UINT8_BUFFER(public_key, ATCA_PUB_KEY_SIZE, "Device cert public key");
exit:
return GOS_CMD_RESULT(result);
}
/*************************************************************************************************/
static void create_response_message(gos_msgpack_context_t *msgpack, const char *fmt, ...)
{
char fmt_buffer[256];
va_list args;
va_start(args, fmt);
vsnprintf(fmt_buffer, sizeof(fmt_buffer), fmt, args);
va_end(args);
gos_msgpack_write_dict_str(msgpack, "msg", fmt_buffer);
}