@@ -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+
1928enum 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+
8991081SHELL_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