nuttx-apps/crypto/controlse/controlse_main.cxx
hujun5 19dcc896d7 sched/tcb: add a reference count to the TCB to prevent it from being deleted.
To replace the large lock with smaller ones and reduce the large locks related to the TCB,
in many scenarios, we only need to ensure that the TCB won't be released
instead of locking, thus reducing the possibility of lock recursion.

Signed-off-by: hujun5 <hujun5@xiaomi.com>
2026-01-22 11:06:12 -03:00

717 lines
22 KiB
C++

//***************************************************************************
// apps/crypto/controlse/controlse_main.cxx
//
// SPDX-License-Identifier: Apache-2.0
//
// 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());
if (tcb != NULL)
{
fprintf(stderr, "\nStack used: %zu / %zu\n", up_check_tcbstack(tcb),
tcb->adj_stack_size);
nxsched_put_tcb(tcb);
}
#else
fprintf(stderr, "\nStack used: unknown"
" (STACK_COLORATION must be enabled)\n");
#endif
}
return result == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
}