espressif / esp-idf

Espressif IoT Development Framework. Official development framework for Espressif SoCs.
Apache License 2.0
13.45k stars 7.25k forks source link

JITP using ESP32 for AWSIOT (IDFGH-13189) #14125

Closed Detective-Roy closed 2 months ago

Detective-Roy commented 3 months ago

Is your feature request related to a problem?

I am working on a IOT project where I need to upload the data using MQTT. Its a consumer electronic device and we need to secure it using provisioning provided by AWS JITP. Every device is associated with unique device ID which we are providing while manufacturing. I want to make it secure and want to provision the individual certificate for each and every device. From AWS account I have 100 certificates ready for 100 devices. I want to know how we can provision it inside while manufacturing the device. Do I need to compile the fw 100 times including individual certificate and then flash. Or can we get only one compiled bin file and then combine with individual and flash it. Or can we flash the bin file first and then provision it later like I am doing for assigning unique name and device ID for each and every device?

Describe the solution you'd like.

I want to know the process and any example which is available for this. I already read the article which is provided by AWS but this provisioning is not clear or may be I missed. Please share me some article, example and description for the same?

Describe alternatives you've considered.

No response

Additional context.

No response

Alvin1Zhang commented 3 months ago

Thanks for reporting.

euripedesrocha commented 3 months ago

Hi @Detective-Roy, from my reading of the AWS JITP documentation and the description of your problem, I would advise you to use nvs API to store the certificates in the devices. From the mqtt client aspect, you can take the ssl_muthual_auth example as a starting point. The change that you will need to make from that example is that you will need to load the client certificates to memory and pass it to the mqtt client.

Detective-Roy commented 2 months ago

Hi @euripedesrocha , I have tried following steps- 1- made a nvs partition csv and bin file and given the address from 0x9000 to store inside the device. Please check this bash. "try.sh" try.txt

2- In my firmware I am reading these certificate and key variable after device boot in app_main() by calling this func:-

void read_certificates_from_nvs() {
    size_t required_size;
    esp_err_t err=0;
    printf("LOADING CERT AND KEY FROM NVS\n");
    err = nvs_get_str(core_storage, "client_cert", NULL, &required_size);
    if (err == ESP_OK) {
        ESP_LOGI(TAG, "REQ_SIZE from cert %d\n", required_size);
        nvs_get_str(core_storage, "client_cert", client_cert_pem, &required_size);
        for (int i=0; i<required_size; i++)
            printf("%c\n", client_cert_pem[i]);
        printf("\n");
    }
    else{
        printf("RET from cert %d\n", err);
    }
    err = nvs_get_str(core_storage, "client_key", NULL, &required_size);
    if (err == ESP_OK) {
        ESP_LOGI(TAG, "REQ_SIZE from key %d\n", required_size);
        nvs_get_str(core_storage, "client_key", client_key_pem, &required_size);
        for (int i=0; i<required_size; i++)
            printf("%c\n", client_key_pem[i]);
        printf("\n");
    }
    else{
        printf("RET from key %d\n", err);
    }
    nvs_close(core_storage);
}
}

I am getting the RET value as 0x1102. It means it is not able to read following key from the NVS.

3- From the MQTT init-

void mqtt_app_start(void)
{
  const esp_mqtt_client_config_t mqtt_cfg = {
    .broker.address.uri = "abc.aws.com"
    .broker.verification.certificate = (const char *)server_cert_pem_start,
    .task.priority=5,
    .task.stack_size=5120,
    .outbox.limit=4608,
    .credentials = {
        .client_id = device_id1,
      .authentication = {
        .certificate = client_cert_pem,
        .key = client_key_pem,
      },
    }
  };

    ESP_LOGI(TAG, "[APP] Free memory: %" PRIu32 " bytes", esp_get_free_heap_size());
    client = esp_mqtt_client_init(&mqtt_cfg);
    /* The last argument may be used to pass data to the event handler, in this example mqtt_event_handler */
    esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt_event_handler, NULL);
    esp_mqtt_client_start(client);
}

4- I am first flashing the "application bin" files and then flashing the nvs.bin using shell script. My partition table for the project is -

st_partition_table.csv

5- After this we are writing some more NVS variable but that is after the flashing of certificates. So, I am expecting that it will be written after the certificate ending address.

6- SDK Config of my project - sdkconfig_debug.txt

Issue- 1- On reading back the client cert and keys from NVS (func mentioned in point 2) and storing is giving error which is mentioned above. Because of that mqtt is not able to make the TLS handshake connection. 2- After reflashing the application bin files the NVS variables which I have written and mentioned on point 5 is got overwritten.

Let me know if you need more details regarding this issue.

rrtandler commented 2 months ago

Hi @Detective-Roy ,

I think you are missing the namespace in your .csv file. The namespace row should appear just before lines with client_cert and client_key and should then be used as a parameter of the nvs initialisation function returning the core_storage value. Please see the example examples/storage/nvsgen/nvs_data.csv and look at the line 3 defining namespace "storage" for all subsequent lines in that .csv. Then you can see the matching value "storage" in the call to the nvs_open_from_partitionin `examples/storage/nvsgen/main/nvsgen_example_main.c:34

Detective-Roy commented 2 months ago

Hi @rrtandler , Thanks for the guidelines. I am able to flash the nvs_partition.bin which is created using csv file. I have made changes in my shell script as suggested. try.txt

Now I am able to read my certificate and key on device initialization. Steps followed:- 1- flashed the application bin file with above mentioned partition table st_partition_table.csv 2- then flashed nvs bin file generated by "try.sh". 3- On Initialisation- in my app_main() I am calling nvs_storage_init() function mentioned below-

int nvs_storage_init(void)
{
    esp_err_t ret;

    ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES) {
        ESP_LOGI(NVS_TAG, "nvs_flash_init error \n");
        nvs_flash_erase();
        ret = nvs_flash_init();
    }

    ret = nvs_open_from_partition("nvs", "storage", NVS_READWRITE, &core_storage);
    if (ret != ESP_OK) {
        ESP_LOGE(NVS_TAG, "Error (%s) opening NVS handle!\n", esp_err_to_name(ret));
        return ret;
    }

    return ESP_OK;
}

Where core_storage declared globally as nvs handle.

4- After this - I am calling read_certificates_from_nvs() which is mentioned above. 5- I am able to read the cert and key whatever I have written while flashing using try.sh.

Issues:- Now I am writing some more variable in NVS after initialisation in runtime. like-

nvs_set_str(core_storage, "curr_ssid", "");
nvs_set_str(core_storage, "currwifi_pwd", "");
nvs_set_u32(core_storage, "logs_enable", 0);
nvs_set_u8(core_storage, "mnfctrng_config", 1);

and etc ......

But, I think these variables are not written to the nvs because whenever I am trying to read back it is giving me same error that variable is not available.

is there any need to create a custom nvs for the extra variable which I am writing in runtime or am I doing anything wrong in this?

rrtandler commented 2 months ago

@Detective-Roy Some troubleshooting ideas:

  1. Evaluate and ideally log the result code of each call to the nvs_set_* and then nvs_get* to control your program flow apropriately.
  2. From the code fragments you have posted, the function void read_certificates_from_nvs()ends with the nvs_close(core_storage); This invalidates the handle core_storage and if you do not re-initialize it, it prevents subsequent calls like nvs_set_*(core_storage, ... to work at all. You would see it from the result code(s), when logged.
Detective-Roy commented 2 months ago

Hi, Thanks for pointing out the error. It worked.