nuttx-apps/crypto/controlse/controlse_main.cxx
2024-04-21 11:20:12 +08:00

712 lines
22 KiB
C++

//***************************************************************************
// apps/crypto/controlse/controlse_main.cxx
//
// 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, 2024 NXP
//***************************************************************************
// Included Files
//***************************************************************************
#include "crypto/controlse/ccertificate.hxx"
#include "crypto/controlse/ccsr.hxx"
#include "crypto/controlse/chex_util.hxx"
#include "crypto/controlse/cpublic_key.hxx"
#include "crypto/controlse/csecure_element.hxx"
#include "crypto/controlse/cstring.hxx"
#include <errno.h>
#include <fcntl.h>
#include <libgen.h>
#include <nuttx/config.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
//***************************************************************************
// 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,
} EKeystoreOperation;
typedef enum
{
KEYSTORE_DATA_TYPE_KEY = 0,
KEYSTORE_DATA_TYPE_CERT_OR_CSR,
KEYSTORE_DATA_TYPE_STRING,
} EKeystoreDataType;
struct SSettings
{
FAR const char *se05x_dev_filename;
FAR char *input_filename;
FAR char *signature_filename;
bool skip_process;
EKeystoreOperation operation;
EKeystoreDataType data_type;
uint32_t key_id;
uint32_t private_key_id;
bool show_stack_used;
};
//***************************************************************************
// Private Function Prototypes
//**************************************************************************
extern "C" int main(int argc, FAR char *argv[]);
//***************************************************************************
// 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 printUsage(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 setOperation(FAR struct SSettings *settings,
EKeystoreOperation 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 parseArguments(int argc, FAR char *argv[],
FAR struct SSettings *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 = setOperation(settings, KEYSTORE_GET_INFO, optarg);
break;
case 'u':
result = setOperation(settings, KEYSTORE_GET_UID, optarg);
break;
case 'g':
result = setOperation(settings, KEYSTORE_GENERATE, optarg);
break;
case 'r':
result = setOperation(settings, KEYSTORE_READ, optarg);
break;
case 'w':
result = setOperation(settings, KEYSTORE_WRITE, optarg);
break;
case 't':
settings->data_type = KEYSTORE_DATA_TYPE_STRING;
break;
case 'c':
settings->data_type = KEYSTORE_DATA_TYPE_CERT_OR_CSR;
break;
case 'd':
result = setOperation(settings, KEYSTORE_DELETE, optarg);
break;
case 's':
result = setOperation(settings, KEYSTORE_CREATE_SIGNATURE, optarg);
break;
case 'v':
result = setOperation(settings, KEYSTORE_VERIFY_SIGNATURE, optarg);
break;
case 'S':
result = setOperation(settings, KEYSTORE_SIGN_CSR, optarg);
break;
case 'V':
result = setOperation(settings, KEYSTORE_VERIFY_CERTIFICATE, optarg);
break;
case 'a':
result = setOperation(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':
printUsage(stdout, prg);
settings->skip_process = TRUE;
break;
default:
printUsage(stderr, prg);
result = -EINVAL;
break;
}
}
if ((result == 0) && (!settings->skip_process))
{
if (settings->operation == KEYSTORE_NO_ACTION)
{
printUsage(stderr, prg);
result = -EINVAL;
}
// if device is specified as positional argument
if (optind != argc)
{
settings->se05x_dev_filename = argv[optind];
}
}
return result;
}
// TODO: use chexutil
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 readFromFile(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);
// keep looping until EOF or EOT are received or the buffer has only 1
// space left (needed for the zero termination character)
while ((result != EOF) && (result != EOT)
&& ((buffer_content_size + 1) < buffer_size))
{
*c = result & 0xff;
c++;
buffer_content_size++;
result = fgetc(f);
}
// Add zero termination character
*c = 0;
buffer_content_size++;
}
puts("\n");
if (filename != NULL)
{
fclose(f);
}
return buffer_content_size;
}
static int readFromFileAndConvert(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 = readFromFile(
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 addZeroTerminationCharacter(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 SSettings *settings)
{
int result = 0;
Controlse::CSecureElement se(se05x_fd);
if (settings->operation == KEYSTORE_GET_INFO)
{
struct se05x_info_s info;
result = se.GetInfo(info) ? 0 : -EPERM;
if (result == 0)
{
printf("OEF ID: %04x\n", info.oef_id);
}
}
else if (settings->operation == KEYSTORE_GET_UID)
{
struct se05x_uid_s uid;
result = se.GetUid(uid) ? 0 : -EPERM;
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 = se.GenerateKey(args) ? 0 : -EPERM;
if (result == 0)
{
printf("Keypair generated successfully\n");
}
}
else if (settings->operation == KEYSTORE_WRITE)
{
char data[DEFAULT_BUFFER_SIZE];
FAR const char *prompt = settings->data_type == KEYSTORE_DATA_TYPE_STRING
? enter_data_raw
: enter_data_pem;
size_t data_size
= readFromFile(prompt, settings->input_filename, data, sizeof(data));
result = -EINVAL;
if (data_size != 0)
{
Controlse::ISecureElementObject *object = nullptr;
if (settings->data_type == KEYSTORE_DATA_TYPE_STRING)
{
object = new Controlse::CString(
data,
data_size - 1); // -1 because no zero termination character
// required
}
else if (settings->data_type == KEYSTORE_DATA_TYPE_KEY)
{
object = new Controlse::CPublicKey(data);
}
else if (settings->data_type == KEYSTORE_DATA_TYPE_CERT_OR_CSR)
{
object = new Controlse::CCertificate(
reinterpret_cast<uint8_t *>(data), data_size);
if (object)
{
if (!object->IsLoaded())
{
delete object;
object = new Controlse::CCsr(
reinterpret_cast<uint8_t *>(data), data_size);
}
}
}
result = -EPERM;
if (object)
{
if (object->IsLoaded())
{
result = object->StoreOnSecureElement(se, settings->key_id)
? 0
: -EPERM;
}
delete object;
}
}
if (result == 0)
{
printf("Data stored successfully\n");
}
}
else if (settings->operation == KEYSTORE_READ)
{
char *data = nullptr;
if (settings->data_type == KEYSTORE_DATA_TYPE_STRING)
{
Controlse::CString string(se, settings->key_id);
if (string.IsLoaded())
{
data = string.c_str();
}
}
else if (settings->data_type == KEYSTORE_DATA_TYPE_KEY)
{
Controlse::CPublicKey key(se, settings->key_id);
if (key.IsLoaded())
{
data = key.GetPem();
}
}
else if (settings->data_type == KEYSTORE_DATA_TYPE_CERT_OR_CSR)
{
Controlse::CCertificate cert(se, settings->key_id);
if (cert.IsLoaded())
{
data = cert.GetPem();
}
else
{
Controlse::CCsr csr(se, settings->key_id);
if (csr.IsLoaded())
{
data = csr.GetPem();
}
}
}
if (data)
{
puts(data);
delete[] data;
}
else
{
result = -EPERM;
}
}
else if (settings->operation == KEYSTORE_DELETE)
{
result = se.DeleteKey(settings->key_id) ? 0 : -EPERM;
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 = se.DeriveSymmetricalKey(args) ? 0 : -EPERM;
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;
readFromFileAndConvert("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 = se.CreateSignature(args) ? 0 : -EPERM;
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 = readFromFileAndConvert(
"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 = readFromFileAndConvert(
"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 = se.Verify(args) ? 0 : -EPERM;
}
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
= readFromFile("enter csr(pem)", settings->input_filename,
csr_pem_buf, sizeof(csr_pem_buf));
csr_pem_buf_content_size = addZeroTerminationCharacter(
csr_pem_buf, csr_pem_buf_content_size, sizeof(csr_pem_buf));
auto certificate = Controlse::CCertificate(
se, reinterpret_cast<uint8_t *>(csr_pem_buf),
csr_pem_buf_content_size, settings->key_id);
auto crt_pem = certificate.GetPem();
if (crt_pem)
{
puts(crt_pem);
delete[] crt_pem;
}
else
{
result = -EPERM;
}
}
else if (settings->operation == KEYSTORE_VERIFY_CERTIFICATE)
{
char pem_buf[DEFAULT_BUFFER_SIZE];
size_t pem_content_size
= readFromFile("enter crt(pem)", settings->input_filename, pem_buf,
sizeof(pem_buf));
pem_content_size = addZeroTerminationCharacter(pem_buf, pem_content_size,
sizeof(pem_buf));
auto certificate = Controlse::CCertificate(
reinterpret_cast<uint8_t *>(pem_buf), pem_content_size);
result = certificate.VerifyAgainst(se, settings->key_id) ? 0 : -EPERM;
if (result == 0)
{
printf("Signature verified successfully\n");
}
}
return result;
}
//***************************************************************************
// Public Functions
//**************************************************************************
int main(int argc, FAR char *argv[])
{
struct SSettings settings = DEFAULT_SETTINGS;
int result = parseArguments(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;
}