Skip to content

Commit bc10d51

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 06ce9da commit bc10d51

File tree

1 file changed

+186
-0
lines changed

1 file changed

+186
-0
lines changed

subsys/net/lib/tls_credentials/tls_credentials_shell.c

Lines changed: 186 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.
@@ -883,6 +892,180 @@ static int tls_cred_cmd_list(const struct shell *sh, size_t argc, char *argv[])
883892
return 0;
884893
}
885894

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

0 commit comments

Comments
 (0)