//*************************************************************************** // 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 #include #include #include #include #include #include #include #include #ifdef CONFIG_STACK_COLORATION #include #include #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] \n", prg); fprintf(f, "Options:\n"); fprintf(f, " -r (read item from keystore at )\n"); fprintf(f, " -w (set item in keystore at )\n"); fprintf(f, " -g (generate keypair at )\n"); fprintf(f, " -d (delete key at )\n"); fprintf(f, " -s (create signature for data\n"); fprintf(f, " with key at )\n"); fprintf(f, " -S (Sign CSR with key at )\n"); fprintf(f, " -v (verify signature for data\n"); fprintf(f, " with key at )\n"); fprintf(f, " -V (verify CRT with key at \n"); fprintf(f, " -a (derive symm key\n"); fprintf(f, " from public key \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 (select private key\n"); fprintf(f, " use with -a)\n"); fprintf(f, " -n (Read input from file)\n"); fprintf(f, " -N (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(data), data_size); if (object) { if (!object->IsLoaded()) { delete object; object = new Controlse::CCsr( reinterpret_cast(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(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(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; }