nuttx-apps/crypto/controlse/controlse_main.c
2023-08-22 10:12:31 +08:00

738 lines
23 KiB
C

/****************************************************************************
* apps/crypto/controlse/controlse_main.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/* Copyright 2023 NXP */
/****************************************************************************
* Included Files
****************************************************************************/
#include <errno.h>
#include <fcntl.h>
#include <libgen.h>
#define MBEDTLS_ALLOW_PRIVATE_ACCESS
#include <mbedtls/sha256.h>
#include <mbedtls/x509_crt.h>
#include <mbedtls/x509_csr.h>
#include <nuttx/config.h>
#include <nuttx/crypto/se05x.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
#ifdef CONFIG_STACK_COLORATION
#include <nuttx/arch.h>
#include <nuttx/sched.h>
#endif
#include "x509_utils.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define EOT 0x04
#define DEFAULT_SETTINGS \
{ \
.se05x_dev_filename = default_se05x_device, 0 \
}
#define TBS_HASH_BUFFER_SIZE 32
#define SIGNATURE_BUFFER_SIZE 300
#define SYMM_KEY_BUFFER_SIZE 300
#define RAW_KEY_BUFFER_SIZE 600
#define DEFAULT_BUFFER_SIZE 1000
/****************************************************************************
* Private Types
****************************************************************************/
typedef enum
{
KEYSTORE_NO_ACTION = 0,
KEYSTORE_READ,
KEYSTORE_WRITE,
KEYSTORE_GENERATE,
KEYSTORE_DELETE,
KEYSTORE_DERIVE_SYMM_KEY,
KEYSTORE_CREATE_SIGNATURE,
KEYSTORE_VERIFY_SIGNATURE,
KEYSTORE_SIGN_CSR,
KEYSTORE_VERIFY_CERTIFICATE,
KEYSTORE_GET_INFO,
KEYSTORE_GET_UID,
} keystore_operation;
struct settings_t
{
FAR const char *se05x_dev_filename;
FAR char *input_filename;
FAR char *signature_filename;
bool skip_process;
keystore_operation operation;
bool raw_data_in_device;
bool interface_with_pem;
uint32_t key_id;
uint32_t private_key_id;
bool show_stack_used;
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/****************************************************************************
* Private Data
****************************************************************************/
static const char default_se05x_device[] = "/dev/se05x";
static const char enter_key_hex[] = "enter key(hex)";
static const char enter_data_pem[] = "enter data(pem)";
static const char enter_data_raw[] = "enter data(raw)";
/****************************************************************************
* Public Data
****************************************************************************/
/****************************************************************************
* Private Functions
****************************************************************************/
static void print_usage(FAR FILE *f, FAR char *prg)
{
fprintf(f, "%s - Control SE05x Secure Element\n", prg);
fprintf(f, "\nUsage: %s [options] <secure element device>\n", prg);
fprintf(f, "Options:\n");
fprintf(f, " -r <id> (read item from keystore at <id>)\n");
fprintf(f, " -w <id> (set item in keystore at <id>)\n");
fprintf(f, " -g <id> (generate keypair at <id>)\n");
fprintf(f, " -d <id> (delete key at <id>)\n");
fprintf(f, " -s <id> (create signature for data\n");
fprintf(f, " with key at <id>)\n");
fprintf(f, " -S <id> (Sign CSR with key at <id>)\n");
fprintf(f, " -v <id> (verify signature for data\n");
fprintf(f, " with key at <id>)\n");
fprintf(f, " -V <id> (verify CRT with key at <id>\n");
fprintf(f, " -a <id> (derive symm key\n");
fprintf(f, " from public key <id>\n");
fprintf(f, " and private key)\n");
fprintf(f, " -t (interface using raw data\n");
fprintf(f, " use with -r, -w)\n");
fprintf(f, " -c (interface with PEM format\n");
fprintf(f, " internally using DER\n");
fprintf(f, " use with -r, -w)\n");
fprintf(f, " -p <id> (select private key\n");
fprintf(f, " use with -a)\n");
fprintf(f, " -n <file> (Read input from file)\n");
fprintf(f, " -N <file> (Read signature from file)\n");
fprintf(f, " -i (show generic information)\n");
fprintf(f, " -u (show UID)\n");
fprintf(f, " -m (show used stack memory space)\n");
fprintf(f, " -h (show this help)\n");
fprintf(f, "\n");
}
static int set_operation(FAR struct settings_t *settings,
keystore_operation operation, FAR char *key_id_text)
{
int result = -EPERM;
if (settings->operation == KEYSTORE_NO_ACTION)
{
settings->operation = operation;
settings->key_id = 0;
if (key_id_text != NULL)
{
settings->key_id = (uint32_t)strtoul(key_id_text, NULL, 0);
}
result = 0;
}
return result;
}
static int parse_arguments(int argc, FAR char *argv[],
FAR struct settings_t *settings)
{
int result = 0;
int opt;
FAR char *prg = basename(argv[0]);
while (
((opt = getopt(argc, argv, "iug:w:r:d:s:v:S:V:tca:p:n:N:mh")) != -1) &&
(result == 0))
{
switch (opt)
{
case 'i':
result = set_operation(settings, KEYSTORE_GET_INFO, optarg);
break;
case 'u':
result = set_operation(settings, KEYSTORE_GET_UID, optarg);
break;
case 'g':
result = set_operation(settings, KEYSTORE_GENERATE, optarg);
break;
case 'r':
result = set_operation(settings, KEYSTORE_READ, optarg);
break;
case 'w':
result = set_operation(settings, KEYSTORE_WRITE, optarg);
break;
case 't':
settings->raw_data_in_device = TRUE;
break;
case 'c':
settings->interface_with_pem = TRUE;
settings->raw_data_in_device = TRUE;
break;
case 'd':
result = set_operation(settings, KEYSTORE_DELETE, optarg);
break;
case 's':
result =
set_operation(settings, KEYSTORE_CREATE_SIGNATURE, optarg);
break;
case 'v':
result =
set_operation(settings, KEYSTORE_VERIFY_SIGNATURE, optarg);
break;
case 'S':
result = set_operation(settings, KEYSTORE_SIGN_CSR, optarg);
break;
case 'V':
result =
set_operation(settings, KEYSTORE_VERIFY_CERTIFICATE, optarg);
break;
case 'a':
result = set_operation(settings, KEYSTORE_DERIVE_SYMM_KEY, optarg);
break;
case 'p':
settings->private_key_id = (uint32_t)strtoul(optarg, NULL, 0);
break;
case 'n':
settings->input_filename = optarg;
break;
case 'N':
settings->signature_filename = optarg;
break;
case 'm':
settings->show_stack_used = TRUE;
break;
case 'h':
print_usage(stdout, prg);
settings->skip_process = TRUE;
break;
default:
print_usage(stderr, prg);
result = -EINVAL;
break;
}
}
if ((result == 0) && (!settings->skip_process))
{
if (settings->operation == KEYSTORE_NO_ACTION)
{
print_usage(stderr, prg);
result = -EINVAL;
}
/* if device is specified as positional argument */
if (optind != argc)
{
settings->se05x_dev_filename = argv[optind];
}
}
return result;
}
static int convert_array_hex_to_bin(FAR char *hex_buffer,
size_t hex_buffer_size,
FAR uint8_t *bin_buffer,
size_t bin_buffer_size,
FAR size_t *bin_buffer_content_size)
{
hex_buffer_size = strcspn(hex_buffer, " \r\n");
if (hex_buffer_size & 1)
{
return -1;
}
*bin_buffer_content_size = 0;
size_t hex_buffer_pos;
for (hex_buffer_pos = 0; (hex_buffer_pos < hex_buffer_size) &&
(*bin_buffer_content_size < bin_buffer_size);
hex_buffer_pos += 2)
{
sscanf(&hex_buffer[hex_buffer_pos], "%2hhx",
&bin_buffer[*bin_buffer_content_size]);
(*bin_buffer_content_size)++;
}
return hex_buffer_pos == hex_buffer_size ? 0 : -1;
}
static int read_from_file(FAR const char *prompt, FAR char *filename,
FAR char *buffer, size_t buffer_size)
{
FAR FILE *f = stdin;
if (filename != NULL)
{
f = fopen(filename, "r");
}
else
{
puts(prompt);
}
size_t buffer_content_size = 0;
if (f != NULL)
{
FAR char *c = buffer;
int result = fgetc(f);
while ((result != EOF) && (result != EOT) &&
(buffer_content_size < buffer_size))
{
*c = result & 0xff;
c++;
buffer_content_size++;
result = fgetc(f);
}
}
if (filename != NULL)
{
fclose(f);
}
return buffer_content_size;
}
static int read_from_file_and_convert(FAR const char *prompt,
FAR char *filename,
FAR uint8_t *buffer,
size_t buffer_size,
FAR size_t *buffer_content_size)
{
char file_buffer[DEFAULT_BUFFER_SIZE];
size_t file_buffer_content_size;
int result;
file_buffer_content_size = read_from_file(
prompt, filename, (FAR char *)file_buffer, sizeof(file_buffer));
result = convert_array_hex_to_bin(file_buffer, file_buffer_content_size,
buffer, buffer_size, buffer_content_size
);
return result;
}
static size_t add_zero_termination_character(char *data, size_t content_size,
size_t buf_size)
{
size_t zero_termination_char_position = content_size - 1;
if ((content_size + 1) <= buf_size)
{
zero_termination_char_position = content_size;
content_size++;
}
data[zero_termination_char_position] = 0;
return content_size;
}
static int process(int se05x_fd, FAR struct settings_t *settings)
{
int result = 0;
if (settings->operation == KEYSTORE_GET_INFO)
{
struct se05x_info_s info;
result = ioctl(se05x_fd, SEIOC_GET_INFO, &info);
if (result == 0)
{
printf("OEF ID: %04x\n", info.oef_id);
}
}
else if (settings->operation == KEYSTORE_GET_UID)
{
struct se05x_uid_s uid;
result = ioctl(se05x_fd, SEIOC_GET_UID, &uid);
if (result == 0)
{
printf("UID: ");
for (size_t i = 0; i < SE05X_MODULE_UNIQUE_ID_LEN; i++)
{
printf("%02x", uid.uid[i]);
}
printf("\n");
}
}
else if (settings->operation == KEYSTORE_GENERATE)
{
struct se05x_generate_keypair_s args = {
.id = settings->key_id, .cipher = SE05X_ASYM_CIPHER_EC_NIST_P_256
};
result = ioctl(se05x_fd, SEIOC_GENERATE_KEYPAIR, &args);
if (result == 0)
{
printf("Keypair generated successfully\n");
}
}
else if (settings->operation == KEYSTORE_WRITE)
{
char pem_buf[DEFAULT_BUFFER_SIZE];
FAR const char *prompt = enter_key_hex;
if (settings->raw_data_in_device)
{
prompt =
settings->interface_with_pem ? enter_data_pem : enter_data_raw;
}
size_t pem_content_size = read_from_file(
prompt, settings->input_filename, pem_buf, sizeof(pem_buf));
uint8_t rawkey[RAW_KEY_BUFFER_SIZE];
size_t rawkey_size = sizeof(rawkey);
FAR uint8_t *data = (FAR uint8_t *)pem_buf;
size_t data_size = pem_content_size;
if (!settings->raw_data_in_device)
{
result = convert_public_key_pem_to_raw(rawkey, rawkey_size,
&rawkey_size, pem_buf);
if (result == 0)
{
data = rawkey;
data_size = rawkey_size;
}
}
if (settings->interface_with_pem)
{
pem_content_size = add_zero_termination_character(
pem_buf, pem_content_size, sizeof(pem_buf));
result = convert_pem_certificate_or_csr_to_der(
rawkey, rawkey_size, &rawkey_size, pem_buf, pem_content_size);
if (result == 0)
{
data = rawkey;
data_size = rawkey_size;
}
}
if (result == 0)
{
struct se05x_key_transmission_s args = {
.entry = {.id = settings->key_id,
.cipher = SE05X_ASYM_CIPHER_EC_NIST_P_256},
.content = {.buffer = data,
.buffer_size = data_size,
.buffer_content_size = data_size}
};
result = ioctl(se05x_fd,
settings->raw_data_in_device ? SEIOC_SET_DATA
: SEIOC_SET_KEY,
&args);
}
if (result == 0)
{
printf("Data stored successfully\n");
}
}
else if (settings->operation == KEYSTORE_READ)
{
uint8_t buffer[DEFAULT_BUFFER_SIZE];
struct se05x_key_transmission_s args = {
.entry = {.id = settings->key_id},
.content = {.buffer = buffer, .buffer_size = sizeof(buffer)}
};
result =
ioctl(se05x_fd,
settings->raw_data_in_device ? SEIOC_GET_DATA : SEIOC_GET_KEY,
&args);
FAR char *data = (FAR char *)args.content.buffer;
if ((result == 0)
&& settings->raw_data_in_device
&& !settings->interface_with_pem)
{
args.content.buffer_content_size = add_zero_termination_character(
data, args.content.buffer_content_size,
args.content.buffer_size);
}
char pem_buf[DEFAULT_BUFFER_SIZE];
if ((result == 0) && !settings->raw_data_in_device)
{
result = convert_public_key_raw_to_pem(
pem_buf, sizeof(pem_buf), args.content.buffer,
args.content.buffer_content_size);
data = pem_buf;
}
if ((result == 0) && settings->interface_with_pem)
{
size_t pem_content_size;
result = convert_der_certificate_or_csr_to_pem(
pem_buf, sizeof(pem_buf), &pem_content_size,
args.content.buffer, args.content.buffer_content_size);
data = pem_buf;
}
if (result == 0)
{
puts(data);
}
}
else if (settings->operation == KEYSTORE_DELETE)
{
result = ioctl(se05x_fd, SEIOC_DELETE_KEY, settings->key_id);
if (result == 0)
{
printf("Deleted key successfully\n");
}
}
else if (settings->operation == KEYSTORE_DERIVE_SYMM_KEY)
{
uint8_t buffer[SYMM_KEY_BUFFER_SIZE];
struct se05x_derive_key_s args = {
.private_key_id = settings->private_key_id,
.public_key_id = settings->key_id,
.content = {.buffer = buffer, .buffer_size = sizeof(buffer)},
};
result = ioctl(se05x_fd, SEIOC_DERIVE_SYMM_KEY, &args);
if (result == 0)
{
for (size_t i = 0; i < args.content.buffer_content_size; i++)
{
printf("%02x", args.content.buffer[i]);
}
printf("\n");
}
}
else if (settings->operation == KEYSTORE_CREATE_SIGNATURE)
{
uint8_t tbs_buffer[TBS_HASH_BUFFER_SIZE];
size_t tbs_content_size;
read_from_file_and_convert("Enter tbs(hex):", settings->input_filename,
tbs_buffer, sizeof(tbs_buffer),
&tbs_content_size);
uint8_t signature_buffer[SIGNATURE_BUFFER_SIZE];
size_t signature_content_len = 0;
if (result == 0)
{
struct se05x_signature_s args = {
.key_id = settings->key_id,
.algorithm = SE05X_ALGORITHM_SHA256,
.tbs = {.buffer = tbs_buffer,
.buffer_size = tbs_content_size,
.buffer_content_size = tbs_content_size},
.signature = {.buffer = signature_buffer,
.buffer_size = sizeof(signature_buffer)},
};
result = ioctl(se05x_fd, SEIOC_CREATE_SIGNATURE, &args);
signature_content_len = args.signature.buffer_content_size;
}
if (result == 0)
{
for (size_t i = 0; i < signature_content_len; i++)
{
printf("%02x", signature_buffer[i]);
}
printf("\n");
}
}
else if (settings->operation == KEYSTORE_VERIFY_SIGNATURE)
{
uint8_t tbs_buffer[TBS_HASH_BUFFER_SIZE];
size_t tbs_content_size;
result = read_from_file_and_convert(
"Enter tbs(hex):", settings->input_filename, tbs_buffer,
sizeof(tbs_buffer), &tbs_content_size);
uint8_t signature_buffer[SIGNATURE_BUFFER_SIZE];
size_t signature_content_size;
if (result == 0)
{
result = read_from_file_and_convert(
"Enter signature(hex):", settings->signature_filename,
signature_buffer, sizeof(signature_buffer),
&signature_content_size);
}
if (result == 0)
{
struct se05x_signature_s args = {
.key_id = settings->key_id,
.algorithm = SE05X_ALGORITHM_SHA256,
.tbs = {.buffer = tbs_buffer,
.buffer_size = tbs_content_size,
.buffer_content_size = tbs_content_size},
.signature = {.buffer = signature_buffer,
.buffer_size = signature_content_size,
.buffer_content_size = signature_content_size},
};
result = ioctl(se05x_fd, SEIOC_VERIFY_SIGNATURE, &args);
}
if (result == 0)
{
printf("Signature verified successfully\n");
}
}
else if (settings->operation == KEYSTORE_SIGN_CSR)
{
char csr_pem_buf[DEFAULT_BUFFER_SIZE];
size_t csr_pem_buf_content_size =
read_from_file("enter csr(pem)", settings->input_filename,
csr_pem_buf, sizeof(csr_pem_buf));
csr_pem_buf_content_size = add_zero_termination_character(csr_pem_buf,
csr_pem_buf_content_size, sizeof(csr_pem_buf));
char crt_pem_buf[DEFAULT_BUFFER_SIZE];
result = sign_csr(se05x_fd, settings->key_id, crt_pem_buf,
sizeof(crt_pem_buf), csr_pem_buf,
csr_pem_buf_content_size);
if (result == 0)
{
puts(crt_pem_buf);
}
}
else if (settings->operation == KEYSTORE_VERIFY_CERTIFICATE)
{
char pem_buf[DEFAULT_BUFFER_SIZE];
size_t pem_content_size =
read_from_file("enter crt(pem)", settings->input_filename, pem_buf,
sizeof(pem_buf));
pem_content_size = add_zero_termination_character(pem_buf,
pem_content_size, sizeof(pem_buf));
mbedtls_x509_crt crt;
mbedtls_x509_crt_init(&crt);
result = mbedtls_x509_crt_parse(&crt, (FAR uint8_t *)pem_buf,
pem_content_size);
uint8_t tbs_buffer[TBS_HASH_BUFFER_SIZE];
if (result == 0)
{
result = mbedtls_sha256(crt.tbs.p,
crt.tbs.len, tbs_buffer, 0
);
}
if (result == 0)
{
struct se05x_signature_s args = {
.key_id = settings->key_id,
.algorithm = SE05X_ALGORITHM_SHA256,
.tbs = {.buffer = tbs_buffer,
.buffer_size = sizeof(tbs_buffer),
.buffer_content_size = sizeof(tbs_buffer)},
.signature = {.buffer = crt.sig.p,
.buffer_size = crt.sig.len,
.buffer_content_size =
crt.sig.len},
};
result = ioctl(se05x_fd, SEIOC_VERIFY_SIGNATURE, &args);
}
if (result == 0)
{
printf("Signature verified successfully\n");
}
mbedtls_x509_crt_free(&crt);
}
return result;
}
/****************************************************************************
* Public Functions
****************************************************************************/
int main(int argc, FAR char *argv[])
{
struct settings_t settings = DEFAULT_SETTINGS;
int result = parse_arguments(argc, argv, &settings);
if ((result == 0) && (!settings.skip_process))
{
int fd = open(settings.se05x_dev_filename, O_RDONLY);
if (fd == -1)
{
result = -ENODEV;
}
else
{
result = process(fd, &settings);
close(fd);
}
}
if (result != 0)
{
fprintf(stderr, "err %i: %s\n", -result, strerror(-result));
}
if (settings.show_stack_used)
{
#ifdef CONFIG_STACK_COLORATION
FAR struct tcb_s *tcb;
tcb = nxsched_get_tcb(getpid());
fprintf(stderr, "\nStack used: %zu / %zu\n",
up_check_tcbstack(tcb), tcb->adj_stack_size);
#else
fprintf(stderr, "\nStack used: unknown"
" (STACK_COLORATION must be enabled)\n");
#endif
}
return result == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
}