@@ -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.
@@ -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+
8861069SHELL_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