Skip to content

Commit 22923c5

Browse files
committed
lib: tls_credentials: add command generate_key
Add command to generate a key on-device. Currently, only p256 keys are supported. Signed-off-by: Maximilian Deubel <[email protected]>
1 parent 9c53822 commit 22923c5

File tree

1 file changed

+185
-0
lines changed

1 file changed

+185
-0
lines changed

subsys/net/lib/tls_credentials/tls_credentials_shell.c

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,15 @@ LOG_MODULE_REGISTER(tls_credentials_shell, CONFIG_TLS_CREDENTIALS_LOG_LEVEL);
1616
#include <strings.h>
1717
#include <ctype.h>
1818

19+
#if defined(CONFIG_PSA_CRYPTO)
20+
#include <psa/crypto.h>
21+
#include <psa/crypto_extra.h>
22+
#endif /* defined(CONFIG_PSA_CRYPTO)*/
23+
24+
#if defined(MBEDTLS_PEM_WRITE_C)
25+
#include <mbedtls/pem.h>
26+
#endif /* defined(MBEDTLS_PEM_WRITE_C)*/
27+
1928
enum cred_storage_fmt {
2029
/* Credential is stored as a string and will be passed between the shell and storage
2130
* unmodified.
@@ -896,6 +905,179 @@ static int tls_cred_cmd_list(const struct shell *sh, size_t argc, char *argv[])
896905
return 0;
897906
}
898907

908+
#if defined(CONFIG_PSA_CRYPTO)
909+
/* Key type parameter for generate_ecdsa_keypair */
910+
enum ecdsa_key_type {
911+
ECDSA_P256 = 0, /* secp256r1 / P-256 */
912+
};
913+
914+
/* Generate ECDSA keypair with specified key type.
915+
* Returns 0 on success or negative error code.
916+
*/
917+
static int generate_ecdsa_keypair(psa_key_id_t *user_keypair_id,
918+
enum ecdsa_key_type key_type)
919+
{
920+
if (user_keypair_id != NULL) {
921+
psa_key_attributes_t key_attributes = PSA_KEY_ATTRIBUTES_INIT;
922+
psa_status_t status;
923+
psa_ecc_family_t ecc_family;
924+
size_t key_bits;
925+
926+
/* Initialize PSA Crypto (Can be done multiple times) */
927+
status = psa_crypto_init();
928+
if (status != PSA_SUCCESS) {
929+
LOG_ERR("psa_crypto_init() failed! (Error: %d)", status);
930+
return -EFAULT;
931+
}
932+
933+
/* Configure key parameters based on key type */
934+
switch (key_type) {
935+
case ECDSA_P256:
936+
ecc_family = PSA_ECC_FAMILY_SECP_R1;
937+
key_bits = 256;
938+
break;
939+
default:
940+
LOG_ERR("Unsupported key type: %d", key_type);
941+
return -EINVAL;
942+
}
943+
944+
/* Configure the key attributes */
945+
psa_set_key_usage_flags(&key_attributes, PSA_KEY_USAGE_EXPORT);
946+
psa_set_key_lifetime(&key_attributes, PSA_KEY_LIFETIME_VOLATILE);
947+
psa_set_key_algorithm(&key_attributes, PSA_ALG_ECDSA(PSA_ALG_SHA_256));
948+
psa_set_key_type(&key_attributes, PSA_KEY_TYPE_ECC_KEY_PAIR(ecc_family));
949+
psa_set_key_bits(&key_attributes, key_bits);
950+
951+
/* Generate the keypair */
952+
status = psa_generate_key(&key_attributes, user_keypair_id);
953+
if (status != PSA_SUCCESS) {
954+
LOG_ERR("psa_generate_key() failed! (Error: %d)", status);
955+
return -EFAULT;
956+
}
957+
958+
psa_reset_key_attributes(&key_attributes);
959+
960+
return 0;
961+
}
962+
return -EINVAL;
963+
}
964+
#endif /* defined(CONFIG_PSA_CRYPTO)*/
965+
966+
/* Generate a private key on device using ECDSA and store it as a credential
967+
* Returns 0 on success or negative error code.
968+
*/
969+
static int tls_cred_cmd_generate_key(const struct shell *sh, size_t argc, char *argv[])
970+
{
971+
#if defined(CONFIG_PSA_CRYPTO)
972+
int err = 0;
973+
psa_key_id_t keypair_id = 0;
974+
sec_tag_t sectag;
975+
enum ecdsa_key_type key_type;
976+
size_t key_der_len;
977+
size_t key_pem_len;
978+
uint8_t key_der[256]; /* Buffer for DER-formatted key */
979+
uint8_t key_pem[512]; /* Buffer for PEM-formatted key */
980+
981+
if (argc < 2) {
982+
shell_fprintf(sh, SHELL_ERROR, "Usage: generate_key <sectag> [key_type]\n");
983+
shell_fprintf(sh, SHELL_ERROR, "Supported key types: p256 (default)\n");
984+
return -EINVAL;
985+
}
986+
987+
/* Parse sectag */
988+
err = shell_parse_cred_sectag(sh, argv[1], &sectag, false);
989+
if (err) {
990+
return err;
991+
}
992+
993+
/* Parse key type if provided, default to P256 */
994+
key_type = ECDSA_P256;
995+
if (argc >= 3) {
996+
if (strcasecmp(argv[2], "p256") == 0) {
997+
key_type = ECDSA_P256;
998+
} else {
999+
shell_fprintf(sh, SHELL_ERROR, "Unknown key type: %s\n", argv[2]);
1000+
shell_fprintf(sh, SHELL_ERROR, "Supported types: p256\n");
1001+
return -EINVAL;
1002+
}
1003+
}
1004+
1005+
/* Lock credentials to prevent concurrent access */
1006+
credentials_lock();
1007+
1008+
/* Check if credential already exists */
1009+
if (credential_get(sectag, TLS_CREDENTIAL_PRIVATE_KEY)) {
1010+
shell_fprintf(sh, SHELL_ERROR,
1011+
"TLS credential with sectag %d and type PK already exists.\n",
1012+
sectag);
1013+
err = -EEXIST;
1014+
goto cleanup;
1015+
}
1016+
1017+
/* Generate the keypair */
1018+
err = generate_ecdsa_keypair(&keypair_id, key_type);
1019+
if (err) {
1020+
shell_fprintf(sh, SHELL_ERROR, "Failed to generate ECDSA keypair (Error: %d)\n",
1021+
err);
1022+
goto cleanup;
1023+
}
1024+
1025+
/* Export the private key in DER format */
1026+
psa_status_t status = psa_export_key(keypair_id, key_der, sizeof(key_der), &key_der_len);
1027+
1028+
if (status != PSA_SUCCESS) {
1029+
shell_fprintf(sh, SHELL_ERROR, "Failed to export key (Error: %d)\n", status);
1030+
err = (int)status;
1031+
goto cleanup;
1032+
}
1033+
1034+
#if defined(MBEDTLS_PEM_WRITE_C)
1035+
/* Convert DER to PEM format */
1036+
err = mbedtls_pem_write_buffer("-----BEGIN EC PRIVATE KEY-----\n",
1037+
"-----END EC PRIVATE KEY-----\n", key_der, key_der_len,
1038+
key_pem, sizeof(key_pem), &key_pem_len);
1039+
if (err != 0) {
1040+
shell_fprintf(sh, SHELL_ERROR, "Failed to convert key to PEM format (Error: %d)\n",
1041+
err);
1042+
goto cleanup;
1043+
}
1044+
1045+
/* Store the PEM-formatted key as a TLS credential */
1046+
err = tls_credential_add(sectag, TLS_CREDENTIAL_PRIVATE_KEY, key_pem, key_pem_len);
1047+
if (err) {
1048+
shell_fprintf(sh, SHELL_ERROR,
1049+
"Failed to store private key credential with sectag %d (Error: %d)\n",
1050+
sectag, err);
1051+
goto cleanup;
1052+
}
1053+
1054+
shell_fprintf(sh, SHELL_NORMAL,
1055+
"Successfully generated and stored ECDSA private key at sectag %d\n", sectag);
1056+
shell_fprintf(sh, SHELL_NORMAL, "Key type: %s, Format: PEM, Size: %d bytes\n",
1057+
(key_type == ECDSA_P256) ? "P-256" : "unknown", key_pem_len);
1058+
#else
1059+
shell_fprintf(sh, SHELL_ERROR,
1060+
"MBEDTLS_PEM_WRITE_C is not enabled. Cannot convert to PEM.\n");
1061+
err = -ENOTSUP;
1062+
goto cleanup;
1063+
#endif /* MBEDTLS_PEM_WRITE_C */
1064+
1065+
cleanup:
1066+
/* Destroy the volatile key handle */
1067+
if (keypair_id != 0) {
1068+
psa_destroy_key(keypair_id);
1069+
}
1070+
1071+
/* Unlock credentials */
1072+
credentials_unlock();
1073+
1074+
return err;
1075+
#else
1076+
shell_fprintf(sh, SHELL_ERROR, "PSA_CRYPTO is not enabled. Cannot generate keypair.\n");
1077+
return -ENOTSUP;
1078+
#endif /* defined(CONFIG_PSA_CRYPTO)*/
1079+
}
1080+
8991081
SHELL_STATIC_SUBCMD_SET_CREATE(tls_cred_buf_cmds,
9001082
SHELL_CMD(clear, NULL, "Clear the credential buffer", tls_cred_cmd_buf_clear),
9011083
SHELL_CMD(load, NULL, "Load credential directly to buffer so it can be added.",
@@ -915,6 +1097,9 @@ SHELL_STATIC_SUBCMD_SET_CREATE(tls_cred_cmds,
9151097
SHELL_CMD_ARG(list, NULL, "List stored TLS credentials, optionally filtering by type "
9161098
"or sectag.",
9171099
tls_cred_cmd_list, 0, 0),
1100+
SHELL_CMD_ARG(generate_key, NULL,
1101+
"Generate an ECDSA keypair on device and store as private key credential",
1102+
tls_cred_cmd_generate_key, 0, 0),
9181103
SHELL_SUBCMD_SET_END
9191104
);
9201105

0 commit comments

Comments
 (0)