[Feature Request] Using Provisioning example, Storing Cert and PVT Key in storage NVS partition pcks11 module. How to retrieve so as to use demo_tasks.
I have managed to provision a device using fleet provisioning
Amazon Web Services IoT MQTT (Fleet Provisioning With CSR) Example
I'm using ESP-IDF 5.2
ESP32S3-F16-R8
I have for now stored the certificate and private key (obtained during fleet provisioning) in the
nvs storage partition: storage,data,nvs,,0x4000,,
The example disconnects, then reconnects, so all is fine.
Now I'm trying to implement MQTT for publish of data to the IoT Hub, using the
main/demo_tasks/temp_sub_pub_and_led_control_demo
the network context differs from the one used in fleet provisioning.
struct NetworkContext
{
MbedtlsPkcs11Context_t * pParams;
};
I wish to use the more refined approach used in the temp_sub_pub_and_led_control_demo for our MQTT telemetry (publish) framework.
To set up the NetworkContext, it uses:
/* Static function definitions ************************************************/
static BaseType_t prvInitializeNetworkContext( void )
{
/* This is returned by this function. */
BaseType_t xRet = pdPASS;
/* This is used to store the error return of ESP-IDF functions. */
esp_err_t xEspErrRet;
/* Verify that the MQTT endpoint and thing name have been configured by the
* user. */
if( strlen( CONFIG_GRI_MQTT_ENDPOINT ) == 0 )
{
ESP_LOGE( TAG, "Empty endpoint for MQTT broker. Set endpoint by "
"running idf.py menuconfig, then Golden Reference Integration -> "
"Endpoint for MQTT Broker to use." );
xRet = pdFAIL;
}
if( strlen( CONFIG_GRI_THING_NAME ) == 0 )
{
ESP_LOGE( TAG, "Empty thingname for MQTT broker. Set thing name by "
"running idf.py menuconfig, then Golden Reference Integration -> "
"Thing name." );
xRet = pdFAIL;
}
/* Initialize network context. */
xNetworkContext.pcHostname = CONFIG_GRI_MQTT_ENDPOINT;
xNetworkContext.xPort = CONFIG_GRI_MQTT_PORT;
/* Get the device certificate from esp_secure_crt_mgr and put into network
* context. */
xEspErrRet = esp_secure_cert_get_device_cert( &xNetworkContext.pcClientCert,
&xNetworkContext.pcClientCertSize );
if( xEspErrRet == ESP_OK )
{
#if CONFIG_GRI_OUTPUT_CERTS_KEYS
ESP_LOGI( TAG, "\nDevice Cert: \nLength: %" PRIu32 "\n%s",
xNetworkContext.pcClientCertSize,
xNetworkContext.pcClientCert );
#endif /* CONFIG_GRI_OUTPUT_CERTS_KEYS */
}
else
{
ESP_LOGE( TAG, "Error in getting device certificate. Error: %s",
esp_err_to_name( xEspErrRet ) );
xRet = pdFAIL;
}
/* Putting the Root CA certificate into the network context. */
xNetworkContext.pcServerRootCA = root_cert_auth_start;
xNetworkContext.pcServerRootCASize = root_cert_auth_end - root_cert_auth_start;
if( xEspErrRet == ESP_OK )
{
#if CONFIG_GRI_OUTPUT_CERTS_KEYS
ESP_LOGI( TAG, "\nCA Cert: \nLength: %" PRIu32 "\n%s",
xNetworkContext.pcServerRootCASize,
xNetworkContext.pcServerRootCA );
#endif /* CONFIG_GRI_OUTPUT_CERTS_KEYS */
}
else
{
ESP_LOGE( TAG, "Error in getting CA certificate. Error: %s",
esp_err_to_name( xEspErrRet ) );
xRet = pdFAIL;
}
#if CONFIG_ESP_SECURE_CERT_DS_PERIPHERAL
/* If the digital signature peripheral is being used, get the digital
* signature peripheral context from esp_secure_crt_mgr and put into
* network context. */
xNetworkContext.ds_data = esp_secure_cert_get_ds_ctx();
if( xNetworkContext.ds_data == NULL )
{
ESP_LOGE( TAG, "Error in getting digital signature peripheral data." );
xRet = pdFAIL;
}
#else /* if CONFIG_ESP_SECURE_CERT_DS_PERIPHERAL */
xEspErrRet = esp_secure_cert_get_priv_key( &xNetworkContext.pcClientKey,
&xNetworkContext.pcClientKeySize );
if( xEspErrRet == ESP_OK )
{
#if CONFIG_GRI_OUTPUT_CERTS_KEYS
ESP_LOGI( TAG, "\nPrivate Key: \nLength: %" PRIu32 "\n%s",
xNetworkContext.pcClientKeySize,
xNetworkContext.pcClientKey );
#endif /* CONFIG_GRI_OUTPUT_CERTS_KEYS */
}
else
{
ESP_LOGE( TAG, "Error in getting private key. Error: %s",
esp_err_to_name( xEspErrRet ) );
xRet = pdFAIL;
}
#endif /* CONFIG_ESP_SECURE_CERT_DS_PERIPHERAL */
xNetworkContext.pxTls = NULL;
xNetworkContext.xTlsContextSemaphore = xSemaphoreCreateMutex();
if( xNetworkContext.xTlsContextSemaphore == NULL )
{
ESP_LOGE( TAG, "Not enough memory to create TLS semaphore for global "
"network context." );
xRet = pdFAIL;
}
return xRet;
}
The examples use the certificate and private key stored in esp_secure_cert_manager partition, (thus a different approach to storing in storage nvs partition as keypair values written by pcks11 module in the fleet provisioning example).:
esp_secure_cert, 0x3F, , 0xD000, 0x2000, encrypted
I need to retrieve the client certificate from the pcks11 module, also the private key using their respective labels, and then I need to populate xNetworkContext with them, as in this example:
xEspErrRet = esp_secure_cert_get_device_cert( &xNetworkContext.pcClientCert,
&xNetworkContext.pcClientCertSize );
How can I read the certificate into xNetworkContext.pcClientCert from the nvs partition using pkcs11 module? I'm trying to get this to work, so we can migrate our application to AWS. I'm building out the framework as I go.
As an alternative later see below.
(I have a ATECC608B TRUST CUSTOM chip on the same custom PCB. It is not utilized at this moment. I could possibly write the Private Key and Client key obtained during fleet provisioning to the trust custom chip at runtime, and then use it for the examples, but have no idea how to do that. )
I haven't worked with the secure element on ESP32, but the functions referenced in this issue appear to be implemented by https://github.com/espressif/esp_secure_cert_mgr Perhaps check documentation there, first.
Otherwise, additional Espressif documentation for ESP-IDF 5.2 can be found here: https://docs.espressif.com/projects/esp-idf/en/v5.2.2/esp32s3/api-reference
@wreyford, were you able to try out @cookpate's suggestion?
Hi @wreyford , Did you successfully combine the two examples? I have the same problem now.
I am encountering this problem too now. Afaik the pkcs11 module will not let you read the private key. I'm working towards modifying the core MQTT Agent connection task to first check nvs for pkcs11-readable credentials, and establish a tls connection using the methods used in the original demo if found.
It will undoubtedly be hideous, but I will post my results if I get something that works alright.
Below code is how I was able to do it after fleet provisioning. But i haven't done any flash encryption / Secure boot so please keep that in mind.
// Initialize NVS partition "storage" xEspErrRet = nvs_flash_init_partition("storage"); if (xEspErrRet != ESP_OK) { ESP_LOGE(TAG, "Failed to initialize NVS partition 'storage': %s", esp_err_to_name(xEspErrRet)); if (xEspErrRet == ESP_ERR_NVS_NO_FREE_PAGES || xEspErrRet == ESP_ERR_NVS_NEW_VERSION_FOUND) { ESP_LOGI(TAG, " 'storage' partition retrying..."); xEspErrRet = nvs_flash_init_partition("storage"); if (xEspErrRet != ESP_OK) { ESP_LOGE(TAG, "Retry failed: %s", esp_err_to_name(xEspErrRet)); return pdFAIL; } } else { return pdFAIL; } }
nvs_handle_t nvs_handle; xEspErrRet = nvs_open_from_partition("storage", "creds", NVS_READONLY, &nvs_handle); if (xEspErrRet != ESP_OK) { ESP_LOGE(TAG, "Failed to open NVS 'creds' namespace: %s", esp_err_to_name(xEspErrRet)); return pdFAIL; }
// Read device certificate from NVS
size_t cert_size = 0;
xEspErrRet = nvs_get_blob(nvs_handle, "P11_Cert", NULL, &cert_size);
if (xEspErrRet != ESP_OK || cert_size == 0) {
ESP_LOGE(TAG, "P11_Cert not found or invalid: %s, size: %zu", esp_err_to_name(xEspErrRet), cert_size);
nvs_close(nvs_handle);
return pdFAIL;
}
xNetworkContext.pcClientCert = (char *)malloc(cert_size);
if (!xNetworkContext.pcClientCert) {
ESP_LOGE(TAG, "Failed to allocate memory for P11_Cert");
nvs_close(nvs_handle);
return pdFAIL;
}
xEspErrRet = nvs_get_blob(nvs_handle, "P11_Cert", (void *)xNetworkContext.pcClientCert, &cert_size);
if (xEspErrRet != ESP_OK) {
ESP_LOGE(TAG, "Failed to read P11_Cert: %s", esp_err_to_name(xEspErrRet));
free((void *)xNetworkContext.pcClientCert);
nvs_close(nvs_handle);
return pdFAIL;
}
xNetworkContext.pcClientCertSize = cert_size;
// Set Root CA certificate from embedded binary
xNetworkContext.pcServerRootCA = root_cert_auth_start;
xNetworkContext.pcServerRootCASize = root_cert_auth_end - root_cert_auth_start;
// Read device private key from NVS
size_t key_size = 0;
xEspErrRet = nvs_get_blob(nvs_handle, "P11_Key", NULL, &key_size);
if (xEspErrRet != ESP_OK || key_size == 0) {
ESP_LOGE(TAG, "P11_Key not found or invalid: %s, size: %zu", esp_err_to_name(xEspErrRet), key_size);
free((void *)xNetworkContext.pcClientCert);
nvs_close(nvs_handle);
return pdFAIL;
}
xNetworkContext.pcClientKey = (char *)malloc(key_size);
if (!xNetworkContext.pcClientKey) {
ESP_LOGE(TAG, "Failed to allocate memory for P11_Key");
free((void *)xNetworkContext.pcClientCert);
nvs_close(nvs_handle);
return pdFAIL;
}
xEspErrRet = nvs_get_blob(nvs_handle, "P11_Key", (void *)xNetworkContext.pcClientKey, &key_size);
if (xEspErrRet != ESP_OK) {
ESP_LOGE(TAG, "Failed to read P11_Key: %s", esp_err_to_name(xEspErrRet));
free((void *)xNetworkContext.pcClientCert);
free((void *)xNetworkContext.pcClientKey);
nvs_close(nvs_handle);
return pdFAIL;
}
xNetworkContext.pcClientKeySize = key_size;
@b-lenin Thank you for sharing your solution!