From daaf50ae4c5e7360c927accf87edfa53c62cf2c3 Mon Sep 17 00:00:00 2001 From: Matt Brown Date: Mon, 5 Sep 2022 16:12:00 +1200 Subject: [PATCH 1/8] Generate SSL key (and CSR) on device using mbedtls This is step 1 towards providing an improved "first boot with SSL" experience. --- include/mqtt_auth.h | 8 +++ src/mqtt.cpp | 7 +++ src/mqtt_auth.cpp | 122 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 137 insertions(+) create mode 100644 include/mqtt_auth.h create mode 100644 src/mqtt_auth.cpp diff --git a/include/mqtt_auth.h b/include/mqtt_auth.h new file mode 100644 index 0000000..47e96a4 --- /dev/null +++ b/include/mqtt_auth.h @@ -0,0 +1,8 @@ +#ifndef _MQTT_AUTH_H +#define _MQTT_AUTH_H + +namespace mqtt { + bool initKey(); +} + +#endif \ No newline at end of file diff --git a/src/mqtt.cpp b/src/mqtt.cpp index 3819fe6..e0336e3 100644 --- a/src/mqtt.cpp +++ b/src/mqtt.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -303,6 +304,12 @@ namespace mqtt { getSPS30StatusCallback = _getSPS30StatusCallback; if (config.mqttUseTls) { + // TODO: (Move elsewhere) + if (!LittleFS.exists(MQTT_CLIENT_KEY_FILENAME)) { + if (!initKey()) { + ESP_LOGE(TAG, "Failed to create MQTT client key"); + } + } wifiClient = new WiFiClientSecure(); if (config.mqttInsecure) { ((WiFiClientSecure*)wifiClient)->setInsecure(); diff --git a/src/mqtt_auth.cpp b/src/mqtt_auth.cpp new file mode 100644 index 0000000..f8bd474 --- /dev/null +++ b/src/mqtt_auth.cpp @@ -0,0 +1,122 @@ +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +// Local logging tag +static const char TAG[] = __FILE__; + +namespace mqtt { + + #define KEY_SIZE 2048 + #define EXPONENT 65537 + + // Clean-up after key initialization and report status + bool finishInitKey(mbedtls_pk_context *key, mbedtls_x509write_csr *req, + mbedtls_entropy_context *entropy, + mbedtls_ctr_drbg_context *ctr_drbg, const char *step, int ret) { + mbedtls_pk_free(key); + mbedtls_x509write_csr_free(req); + mbedtls_ctr_drbg_free(ctr_drbg); + mbedtls_entropy_free( entropy); + + char status[1024]; + bool rv; + if (ret != 0) { + char errMsg[1024]; + mbedtls_strerror( ret, errMsg, sizeof( errMsg ) ); + snprintf(&status[0], 1024, "Failed to initialize key at %s: %s", step, errMsg); + rv = false; + } else { + snprintf(&status[0], 1024, "Key initialized successfully"); + rv = true; + } + // waiting for https://github.com/oseiler2/CO2Monitor/pull/14/files + //sendStatus(status) + ESP_LOGD(TAG, "initKey finished: %s", status); + return rv; + } + + // Helper to write a file to disk + bool writeFile(const char *name, unsigned char *contents) { + File f; + if (!(f = LittleFS.open(name, FILE_WRITE))) { + return false; + } + + int len = strlen((char *)contents); + if (f.write(contents, len) != len) { + f.close(); + return false; + } + + f.close(); + return true; + } + + // Attempts to initialize an RSA key (and associated CSR) for the device + bool initKey(void) { + mbedtls_pk_context key; + mbedtls_x509write_csr req; + mbedtls_entropy_context entropy; + mbedtls_ctr_drbg_context ctr_drbg; + String mac = WifiManager::getMac(); + int ret = 1; + + mbedtls_ctr_drbg_init(&ctr_drbg); + mbedtls_entropy_init(&entropy); + mbedtls_pk_init(&key); + mbedtls_x509write_csr_init(&req); + mbedtls_x509write_csr_set_md_alg(&req, MBEDTLS_MD_SHA256); + + if ((ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, + (const unsigned char *)mac.c_str(), mac.length())) != 0) { + return finishInitKey(&key, &req, &entropy, &ctr_drbg, "seed", ret); + } + + if ((ret = mbedtls_pk_setup(&key, mbedtls_pk_info_from_type((mbedtls_pk_type_t)MBEDTLS_PK_RSA))) != 0) { + return finishInitKey(&key, &req, &entropy, &ctr_drbg, "setup", ret); + } + + if ((ret = mbedtls_rsa_gen_key(mbedtls_pk_rsa(key), mbedtls_ctr_drbg_random, &ctr_drbg, KEY_SIZE, EXPONENT)) != 0) { + return finishInitKey(&key, &req, &entropy, &ctr_drbg, "gen key", ret); + } + + unsigned char output_buf[16000]; + memset(output_buf, 0, 16000); + if( ( ret = mbedtls_pk_write_key_pem( &key, output_buf, 16000 ) ) != 0 ) { + return finishInitKey(&key, &req, &entropy, &ctr_drbg, "serialize key", ret); + } + if (!writeFile(MQTT_CLIENT_KEY_FILENAME, output_buf)) { + return finishInitKey(&key, &req, &entropy, &ctr_drbg, "write key", MBEDTLS_ERR_PK_FILE_IO_ERROR); + } + + char cn[50]; + snprintf(&cn[0], 50, "CN=%s,O=CO2Monitor", mac); + if ((ret = mbedtls_x509write_csr_set_subject_name( &req, cn)) != 0 ) { + return finishInitKey(&key, &req, &entropy, &ctr_drbg, "set subject name", ret); + } + mbedtls_x509write_csr_set_key(&req, &key); + + memset( output_buf, 0, 16000 ); + if ((ret = mbedtls_x509write_csr_pem(&req, output_buf, 16000, mbedtls_ctr_drbg_random, &ctr_drbg)) < 0) { + return finishInitKey(&key, &req, &entropy, &ctr_drbg, "serialize csr", ret); + } + if (!writeFile("/mqtt_client.csr", output_buf)) { + return finishInitKey(&key, &req, &entropy, &ctr_drbg, "write csr", MBEDTLS_ERR_PK_FILE_IO_ERROR); + } + + return finishInitKey(&key, &req, &entropy, &ctr_drbg, "", 0); + } + +} \ No newline at end of file From 7eaaae4cc0043e0a264792ecde2bc858303a0f38 Mon Sep 17 00:00:00 2001 From: Matt Brown Date: Tue, 6 Sep 2022 00:28:48 +1200 Subject: [PATCH 2/8] Hook up Cert/CSR request to MQTT messages Struggling with stack size here... --- include/config.h | 2 ++ include/mqtt_auth.h | 3 +++ src/main.cpp | 2 +- src/mqtt.cpp | 53 ++++++++++++++++++++++++++++++++++++++++----- src/mqtt_auth.cpp | 25 +++++++++++++++------ 5 files changed, 71 insertions(+), 14 deletions(-) diff --git a/include/config.h b/include/config.h index 61c3e53..7e2598f 100644 --- a/include/config.h +++ b/include/config.h @@ -16,7 +16,9 @@ static const char* CONFIG_FILENAME = "/config.json"; static const char* MQTT_ROOT_CA_FILENAME = "/mqtt_root_ca.pem"; static const char* MQTT_CLIENT_CERT_FILENAME = "/mqtt_client_cert.pem"; +static const char* MQTT_CLIENT_NEW_KEY_FILENAME = "/mqtt_client_key.pem.new"; static const char* MQTT_CLIENT_KEY_FILENAME = "/mqtt_client_key.pem"; +static const char* MQTT_CLIENT_CSR_FILENAME = "/mqtt_client_csr.pem"; #define PWM_CHANNEL_LEDS 0 diff --git a/include/mqtt_auth.h b/include/mqtt_auth.h index 47e96a4..300a1c3 100644 --- a/include/mqtt_auth.h +++ b/include/mqtt_auth.h @@ -1,6 +1,9 @@ #ifndef _MQTT_AUTH_H #define _MQTT_AUTH_H +// Haven't seen one bigger than ~1700bytes +#define PEM_BUFLEN 2048 + namespace mqtt { bool initKey(); } diff --git a/src/main.cpp b/src/main.cpp index 2b01113..92ade64 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -233,7 +233,7 @@ void setup() { xTaskCreatePinnedToCore(mqtt::mqttLoop, // task function "mqttLoop", // name of task - 8192, // stack size of task + 12288, // stack size of task (void*)1, // parameter of the task 2, // priority of the task &mqtt::mqttTask, // task handle diff --git a/src/mqtt.cpp b/src/mqtt.cpp index e0336e3..873e9d7 100644 --- a/src/mqtt.cpp +++ b/src/mqtt.cpp @@ -29,6 +29,7 @@ namespace mqtt { const uint8_t X_CMD_PUBLISH_SENSORS = bit(0); const uint8_t X_CMD_PUBLISH_CONFIGURATION = bit(1); + const uint8_t X_CMD_REQUEST_CERT = bit(2); TaskHandle_t mqttTask; QueueHandle_t mqttQueue; @@ -161,6 +162,48 @@ namespace mqtt { if (!mqtt_client->publish(buf, msg)) ESP_LOGE(TAG, "publish failed!"); } + // Attempts to register a CSR with the server to requst a certificate + void requestCert(void) { + MqttMessage msg; + msg.cmd = X_CMD_REQUEST_CERT; + if (mqttQueue) xQueueSendToBack(mqttQueue, (void*)&msg, pdMS_TO_TICKS(100)); + } + + void requestCertInternal() { + ESP_LOGD(TAG, "Requesting Certificate..."); + if (!LittleFS.exists(MQTT_CLIENT_CSR_FILENAME)) { + if (!initKey()) { + ESP_LOGE(TAG, "Failed to create MQTT client key"); + return; + } + } + + ESP_LOGD(TAG, "Submitting CSR..."); + File f; + if (!(f = LittleFS.open(MQTT_CLIENT_CSR_FILENAME, FILE_READ))) { + ESP_LOGE(TAG, "could not read CSR"); + return; + } + + char csr[PEM_BUFLEN]; + int len = f.read((uint8_t *)&csr[0], PEM_BUFLEN); + if (len <= 0) { + ESP_LOGE(TAG, "CSR was empty"); + return; + } + csr[len] = '\0'; + f.close(); + + if (mqtt_client->connected()) { + char topic[256]; + sprintf(topic, "%s/%u/up/csr", config.mqttTopic, config.deviceId); + ESP_LOGD(TAG, "Publishing certificate request: %s", topic); + if (!mqtt_client->publish(topic, csr)) ESP_LOGE(TAG, "failed to publish csr!"); + } else { + ESP_LOGE(TAG, "Generated CSR, but could not submit as MQTT not connected"); + } + } + void callback(char* topic, byte* payload, unsigned int length) { char buf[256]; char msg[length + 1]; @@ -199,6 +242,8 @@ namespace mqtt { setSPS30AutoCleanIntervalCallback(interval); } else if (strncmp(buf, "cleanSPS30", strlen(buf)) == 0) { cleanSPS30Callback(); + } else if (strncmp(buf, "requestCert", strlen(buf)) == 0) { + requestCert(); } else if (strncmp(buf, "getConfig", strlen(buf)) == 0) { publishConfiguration(); } else if (strncmp(buf, "setConfig", strlen(buf)) == 0) { @@ -304,12 +349,6 @@ namespace mqtt { getSPS30StatusCallback = _getSPS30StatusCallback; if (config.mqttUseTls) { - // TODO: (Move elsewhere) - if (!LittleFS.exists(MQTT_CLIENT_KEY_FILENAME)) { - if (!initKey()) { - ESP_LOGE(TAG, "Failed to create MQTT client key"); - } - } wifiClient = new WiFiClientSecure(); if (config.mqttInsecure) { ((WiFiClientSecure*)wifiClient)->setInsecure(); @@ -354,6 +393,8 @@ namespace mqtt { publishConfigurationInternal(); } else if (msg.cmd == X_CMD_PUBLISH_SENSORS) { publishSensorsInternal(msg.mask); + } else if (msg.cmd == X_CMD_REQUEST_CERT) { + requestCertInternal(); } } if (!mqtt_client->connected()) { diff --git a/src/mqtt_auth.cpp b/src/mqtt_auth.cpp index f8bd474..2f8d6f8 100644 --- a/src/mqtt_auth.cpp +++ b/src/mqtt_auth.cpp @@ -1,4 +1,6 @@ #include +#include +#include #include #include @@ -66,6 +68,8 @@ namespace mqtt { // Attempts to initialize an RSA key (and associated CSR) for the device bool initKey(void) { + ESP_LOGD(TAG, "Generating RSA private key... "); + mbedtls_pk_context key; mbedtls_x509write_csr req; mbedtls_entropy_context entropy; @@ -79,28 +83,33 @@ namespace mqtt { mbedtls_x509write_csr_init(&req); mbedtls_x509write_csr_set_md_alg(&req, MBEDTLS_MD_SHA256); + ESP_LOGD(TAG, "Initializing PRNG... "); if ((ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, (const unsigned char *)mac.c_str(), mac.length())) != 0) { return finishInitKey(&key, &req, &entropy, &ctr_drbg, "seed", ret); } + ESP_LOGD(TAG, "PK setup... "); if ((ret = mbedtls_pk_setup(&key, mbedtls_pk_info_from_type((mbedtls_pk_type_t)MBEDTLS_PK_RSA))) != 0) { return finishInitKey(&key, &req, &entropy, &ctr_drbg, "setup", ret); } + ESP_LOGD(TAG, "Actually generating... "); if ((ret = mbedtls_rsa_gen_key(mbedtls_pk_rsa(key), mbedtls_ctr_drbg_random, &ctr_drbg, KEY_SIZE, EXPONENT)) != 0) { return finishInitKey(&key, &req, &entropy, &ctr_drbg, "gen key", ret); } - unsigned char output_buf[16000]; - memset(output_buf, 0, 16000); - if( ( ret = mbedtls_pk_write_key_pem( &key, output_buf, 16000 ) ) != 0 ) { + unsigned char output_buf[PEM_BUFLEN]; + memset(output_buf, 0, PEM_BUFLEN); + ESP_LOGD(TAG, "Writing key PEM.. "); + if( ( ret = mbedtls_pk_write_key_pem( &key, output_buf, PEM_BUFLEN ) ) != 0 ) { return finishInitKey(&key, &req, &entropy, &ctr_drbg, "serialize key", ret); } - if (!writeFile(MQTT_CLIENT_KEY_FILENAME, output_buf)) { + if (!writeFile(MQTT_CLIENT_NEW_KEY_FILENAME, output_buf)) { return finishInitKey(&key, &req, &entropy, &ctr_drbg, "write key", MBEDTLS_ERR_PK_FILE_IO_ERROR); } + ESP_LOGD(TAG, "Generating CSR... "); char cn[50]; snprintf(&cn[0], 50, "CN=%s,O=CO2Monitor", mac); if ((ret = mbedtls_x509write_csr_set_subject_name( &req, cn)) != 0 ) { @@ -108,14 +117,16 @@ namespace mqtt { } mbedtls_x509write_csr_set_key(&req, &key); - memset( output_buf, 0, 16000 ); - if ((ret = mbedtls_x509write_csr_pem(&req, output_buf, 16000, mbedtls_ctr_drbg_random, &ctr_drbg)) < 0) { + ESP_LOGD(TAG, "Writing CSR PEM... "); + memset( output_buf, 0, PEM_BUFLEN ); + if ((ret = mbedtls_x509write_csr_pem(&req, output_buf, PEM_BUFLEN, mbedtls_ctr_drbg_random, &ctr_drbg)) < 0) { return finishInitKey(&key, &req, &entropy, &ctr_drbg, "serialize csr", ret); } - if (!writeFile("/mqtt_client.csr", output_buf)) { + if (!writeFile(MQTT_CLIENT_CSR_FILENAME, output_buf)) { return finishInitKey(&key, &req, &entropy, &ctr_drbg, "write csr", MBEDTLS_ERR_PK_FILE_IO_ERROR); } + ESP_LOGD(TAG, "Key generated."); return finishInitKey(&key, &req, &entropy, &ctr_drbg, "", 0); } From bce652a338ba3ad0de094645a76ff3c750742070 Mon Sep 17 00:00:00 2001 From: Matt Brown Date: Tue, 6 Sep 2022 14:25:01 +1200 Subject: [PATCH 3/8] Extend WD timer during key generation https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/wdts.html#id1 says this is OK to do... --- src/mqtt_auth.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/mqtt_auth.cpp b/src/mqtt_auth.cpp index 2f8d6f8..77fc876 100644 --- a/src/mqtt_auth.cpp +++ b/src/mqtt_auth.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -27,6 +28,10 @@ namespace mqtt { bool finishInitKey(mbedtls_pk_context *key, mbedtls_x509write_csr *req, mbedtls_entropy_context *entropy, mbedtls_ctr_drbg_context *ctr_drbg, const char *step, int ret) { + + // Reset watchdog timer to default + esp_task_wdt_init(CONFIG_ESP_TASK_WDT_TIMEOUT_S, true); + mbedtls_pk_free(key); mbedtls_x509write_csr_free(req); mbedtls_ctr_drbg_free(ctr_drbg); @@ -70,6 +75,10 @@ namespace mqtt { bool initKey(void) { ESP_LOGD(TAG, "Generating RSA private key... "); + // Getting the entropy for the key can take a few seconds, which doesn't play well with the default (5s) watchdog timer + // So bump it up for a bit while we generate the key - will be reset to the default value in finishInitKey above. + esp_task_wdt_init(15, true); + mbedtls_pk_context key; mbedtls_x509write_csr req; mbedtls_entropy_context entropy; From 8d85baaa0704c0dc8a1ab9c62f80bd40f51ca1ff Mon Sep 17 00:00:00 2001 From: Matt Brown Date: Tue, 6 Sep 2022 16:23:26 +1200 Subject: [PATCH 4/8] Add MQTT trigger to regenerate device key --- src/mqtt.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/mqtt.cpp b/src/mqtt.cpp index 873e9d7..1a2076d 100644 --- a/src/mqtt.cpp +++ b/src/mqtt.cpp @@ -163,15 +163,16 @@ namespace mqtt { } // Attempts to register a CSR with the server to requst a certificate - void requestCert(void) { + void requestCert(bool regenerateKey) { MqttMessage msg; msg.cmd = X_CMD_REQUEST_CERT; + msg.mask = regenerateKey; // re-use mask field as regen flag. if (mqttQueue) xQueueSendToBack(mqttQueue, (void*)&msg, pdMS_TO_TICKS(100)); } - void requestCertInternal() { + void requestCertInternal(bool regenerateKey) { ESP_LOGD(TAG, "Requesting Certificate..."); - if (!LittleFS.exists(MQTT_CLIENT_CSR_FILENAME)) { + if (!LittleFS.exists(MQTT_CLIENT_CSR_FILENAME) || regenerateKey) { if (!initKey()) { ESP_LOGE(TAG, "Failed to create MQTT client key"); return; @@ -243,7 +244,9 @@ namespace mqtt { } else if (strncmp(buf, "cleanSPS30", strlen(buf)) == 0) { cleanSPS30Callback(); } else if (strncmp(buf, "requestCert", strlen(buf)) == 0) { - requestCert(); + requestCert(false); + } else if (strncmp(buf, "regenerateKey", strlen(buf)) == 0) { + requestCert(true); } else if (strncmp(buf, "getConfig", strlen(buf)) == 0) { publishConfiguration(); } else if (strncmp(buf, "setConfig", strlen(buf)) == 0) { @@ -394,7 +397,7 @@ namespace mqtt { } else if (msg.cmd == X_CMD_PUBLISH_SENSORS) { publishSensorsInternal(msg.mask); } else if (msg.cmd == X_CMD_REQUEST_CERT) { - requestCertInternal(); + requestCertInternal(msg.mask == 1); } } if (!mqtt_client->connected()) { From d8fff9870bcec624763acd76e26d527654d04544 Mon Sep 17 00:00:00 2001 From: Matt Brown Date: Tue, 6 Sep 2022 16:24:08 +1200 Subject: [PATCH 5/8] Use heap not stack for the bulk of keygen memory Generating a key requires a reasonable number of multi-kb buffers. If we naively put those on the stack we have to enlarge the stack size for the MQTT task, which permanently denies other tasks use of that memory, even though it's going to be almost always unused in the MQTT stack given key generation is a very rare operation. Allocating these buffers explicitly on the heap means the memory is free for everything else to use the rest of the time. --- src/main.cpp | 2 +- src/mqtt.cpp | 16 ++++++-- src/mqtt_auth.cpp | 99 +++++++++++++++++++++++++++-------------------- 3 files changed, 71 insertions(+), 46 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 92ade64..2b01113 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -233,7 +233,7 @@ void setup() { xTaskCreatePinnedToCore(mqtt::mqttLoop, // task function "mqttLoop", // name of task - 12288, // stack size of task + 8192, // stack size of task (void*)1, // parameter of the task 2, // priority of the task &mqtt::mqttTask, // task handle diff --git a/src/mqtt.cpp b/src/mqtt.cpp index 1a2076d..73d1b17 100644 --- a/src/mqtt.cpp +++ b/src/mqtt.cpp @@ -186,10 +186,17 @@ namespace mqtt { return; } - char csr[PEM_BUFLEN]; - int len = f.read((uint8_t *)&csr[0], PEM_BUFLEN); - if (len <= 0) { - ESP_LOGE(TAG, "CSR was empty"); + int len = f.size(); + char *csr = (char *)malloc(len+1); + if (csr == NULL) { + ESP_LOGE(TAG, "Could not malloc CSR buffer"); + return; + } + memset(csr, 0, len+1); + int read = f.read((uint8_t *)csr, len); + if (read != len) { + ESP_LOGE(TAG, "CSR read failed"); + free(csr); return; } csr[len] = '\0'; @@ -203,6 +210,7 @@ namespace mqtt { } else { ESP_LOGE(TAG, "Generated CSR, but could not submit as MQTT not connected"); } + free(csr); } void callback(char* topic, byte* payload, unsigned int length) { diff --git a/src/mqtt_auth.cpp b/src/mqtt_auth.cpp index 77fc876..2c1b0e0 100644 --- a/src/mqtt_auth.cpp +++ b/src/mqtt_auth.cpp @@ -27,7 +27,8 @@ namespace mqtt { // Clean-up after key initialization and report status bool finishInitKey(mbedtls_pk_context *key, mbedtls_x509write_csr *req, mbedtls_entropy_context *entropy, - mbedtls_ctr_drbg_context *ctr_drbg, const char *step, int ret) { + mbedtls_ctr_drbg_context *ctr_drbg, unsigned char *output_buf, + const char *step, int ret) { // Reset watchdog timer to default esp_task_wdt_init(CONFIG_ESP_TASK_WDT_TIMEOUT_S, true); @@ -35,23 +36,35 @@ namespace mqtt { mbedtls_pk_free(key); mbedtls_x509write_csr_free(req); mbedtls_ctr_drbg_free(ctr_drbg); - mbedtls_entropy_free( entropy); + mbedtls_entropy_free(entropy); + delete key; + delete req; + delete ctr_drbg; + delete entropy; + if (output_buf != NULL) { + free(output_buf); + } - char status[1024]; - bool rv; if (ret != 0) { - char errMsg[1024]; - mbedtls_strerror( ret, errMsg, sizeof( errMsg ) ); - snprintf(&status[0], 1024, "Failed to initialize key at %s: %s", step, errMsg); - rv = false; - } else { - snprintf(&status[0], 1024, "Key initialized successfully"); - rv = true; + char *status = (char *)malloc(1024); + char *errMsg = (char *)malloc(1024); + if (status != NULL && errMsg != NULL) { + mbedtls_strerror( ret, errMsg, sizeof(errMsg)); + snprintf(status, 1024, "Failed to initialize key at %s: %s", step, errMsg); + free(errMsg); + } else { + ESP_LOGE(TAG, "(status not sent due to alloc failure) Failed to initialize key at %s", step); + } + // waiting for https://github.com/oseiler2/CO2Monitor/pull/14/files + //sendStatus(status) + ESP_LOGD(TAG, "initKey finished: %s", status); + free(status); + return false; } - // waiting for https://github.com/oseiler2/CO2Monitor/pull/14/files - //sendStatus(status) - ESP_LOGD(TAG, "initKey finished: %s", status); - return rv; + + //sendStatus("Key initialized successfully"); + ESP_LOGD(TAG, "Key initialized successfully"); + return true; } // Helper to write a file to disk @@ -79,64 +92,68 @@ namespace mqtt { // So bump it up for a bit while we generate the key - will be reset to the default value in finishInitKey above. esp_task_wdt_init(15, true); - mbedtls_pk_context key; - mbedtls_x509write_csr req; - mbedtls_entropy_context entropy; - mbedtls_ctr_drbg_context ctr_drbg; + mbedtls_pk_context *key = new mbedtls_pk_context; + mbedtls_x509write_csr *req = new mbedtls_x509write_csr; + mbedtls_entropy_context *entropy = new mbedtls_entropy_context; + mbedtls_ctr_drbg_context *ctr_drbg = new mbedtls_ctr_drbg_context; String mac = WifiManager::getMac(); int ret = 1; - mbedtls_ctr_drbg_init(&ctr_drbg); - mbedtls_entropy_init(&entropy); - mbedtls_pk_init(&key); - mbedtls_x509write_csr_init(&req); - mbedtls_x509write_csr_set_md_alg(&req, MBEDTLS_MD_SHA256); + mbedtls_ctr_drbg_init(ctr_drbg); + mbedtls_entropy_init(entropy); + mbedtls_pk_init(key); + mbedtls_x509write_csr_init(req); + mbedtls_x509write_csr_set_md_alg(req, MBEDTLS_MD_SHA256); ESP_LOGD(TAG, "Initializing PRNG... "); - if ((ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, + if ((ret = mbedtls_ctr_drbg_seed(ctr_drbg, mbedtls_entropy_func, entropy, (const unsigned char *)mac.c_str(), mac.length())) != 0) { - return finishInitKey(&key, &req, &entropy, &ctr_drbg, "seed", ret); + return finishInitKey(key, req, entropy, ctr_drbg, NULL, "seed", ret); } ESP_LOGD(TAG, "PK setup... "); - if ((ret = mbedtls_pk_setup(&key, mbedtls_pk_info_from_type((mbedtls_pk_type_t)MBEDTLS_PK_RSA))) != 0) { - return finishInitKey(&key, &req, &entropy, &ctr_drbg, "setup", ret); + if ((ret = mbedtls_pk_setup(key, mbedtls_pk_info_from_type((mbedtls_pk_type_t)MBEDTLS_PK_RSA))) != 0) { + return finishInitKey(key, req, entropy, ctr_drbg, NULL, "setup", ret); } ESP_LOGD(TAG, "Actually generating... "); - if ((ret = mbedtls_rsa_gen_key(mbedtls_pk_rsa(key), mbedtls_ctr_drbg_random, &ctr_drbg, KEY_SIZE, EXPONENT)) != 0) { - return finishInitKey(&key, &req, &entropy, &ctr_drbg, "gen key", ret); + if ((ret = mbedtls_rsa_gen_key(mbedtls_pk_rsa(*key), mbedtls_ctr_drbg_random, ctr_drbg, KEY_SIZE, EXPONENT)) != 0) { + return finishInitKey(key, req, entropy, ctr_drbg, NULL, "gen key", ret); + } + + unsigned char *output_buf = (unsigned char *)malloc(PEM_BUFLEN); + if (output_buf == NULL) { + return finishInitKey(key, req, entropy, ctr_drbg, NULL, "allocate buffer", MBEDTLS_ERR_PK_ALLOC_FAILED); } - unsigned char output_buf[PEM_BUFLEN]; memset(output_buf, 0, PEM_BUFLEN); ESP_LOGD(TAG, "Writing key PEM.. "); - if( ( ret = mbedtls_pk_write_key_pem( &key, output_buf, PEM_BUFLEN ) ) != 0 ) { - return finishInitKey(&key, &req, &entropy, &ctr_drbg, "serialize key", ret); + if( ( ret = mbedtls_pk_write_key_pem( key, output_buf, PEM_BUFLEN ) ) != 0 ) { + return finishInitKey(key, req, entropy, ctr_drbg, output_buf, "serialize key", ret); } if (!writeFile(MQTT_CLIENT_NEW_KEY_FILENAME, output_buf)) { - return finishInitKey(&key, &req, &entropy, &ctr_drbg, "write key", MBEDTLS_ERR_PK_FILE_IO_ERROR); + return finishInitKey(key, req, entropy, ctr_drbg, output_buf, "write key", MBEDTLS_ERR_PK_FILE_IO_ERROR); } ESP_LOGD(TAG, "Generating CSR... "); char cn[50]; snprintf(&cn[0], 50, "CN=%s,O=CO2Monitor", mac); - if ((ret = mbedtls_x509write_csr_set_subject_name( &req, cn)) != 0 ) { - return finishInitKey(&key, &req, &entropy, &ctr_drbg, "set subject name", ret); + if ((ret = mbedtls_x509write_csr_set_subject_name( req, cn)) != 0 ) { + return finishInitKey(key, req, entropy, ctr_drbg, output_buf, "set subject name", ret); } - mbedtls_x509write_csr_set_key(&req, &key); + mbedtls_x509write_csr_set_key(req, key); ESP_LOGD(TAG, "Writing CSR PEM... "); memset( output_buf, 0, PEM_BUFLEN ); - if ((ret = mbedtls_x509write_csr_pem(&req, output_buf, PEM_BUFLEN, mbedtls_ctr_drbg_random, &ctr_drbg)) < 0) { - return finishInitKey(&key, &req, &entropy, &ctr_drbg, "serialize csr", ret); + if ((ret = mbedtls_x509write_csr_pem(req, output_buf, PEM_BUFLEN, mbedtls_ctr_drbg_random, ctr_drbg)) < 0) { + return finishInitKey(key, req, entropy, ctr_drbg, output_buf, "serialize csr", ret); } if (!writeFile(MQTT_CLIENT_CSR_FILENAME, output_buf)) { - return finishInitKey(&key, &req, &entropy, &ctr_drbg, "write csr", MBEDTLS_ERR_PK_FILE_IO_ERROR); + return finishInitKey(key, req, entropy, ctr_drbg, output_buf, "write csr", MBEDTLS_ERR_PK_FILE_IO_ERROR); } ESP_LOGD(TAG, "Key generated."); - return finishInitKey(&key, &req, &entropy, &ctr_drbg, "", 0); + return finishInitKey(key, req, entropy, ctr_drbg, output_buf, "", 0); } } \ No newline at end of file From 99ea3e2315f55d4aeb82b3601de8cdbfbf1634bc Mon Sep 17 00:00:00 2001 From: Matt Brown Date: Wed, 7 Sep 2022 12:26:07 +1200 Subject: [PATCH 6/8] Add installCert MQTT message and supporting code This allows the server to pass back a signed certificate for the monitor, which will check it is signed by the trusted CA and matches the private key previously generated by a requestCert/regenerateKey command and then install it and reboot. This is fully functional for bootstrapping SSL on a device now, but not yet production ready - need further support for updating config, etc first. --- include/config.h | 6 ++ include/mqtt.h | 7 ++ include/mqtt_auth.h | 2 + src/mqtt.cpp | 9 ++- src/mqtt_auth.cpp | 170 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 193 insertions(+), 1 deletion(-) diff --git a/include/config.h b/include/config.h index 7e2598f..5f4320a 100644 --- a/include/config.h +++ b/include/config.h @@ -15,7 +15,13 @@ static const char* CONFIG_FILENAME = "/config.json"; static const char* MQTT_ROOT_CA_FILENAME = "/mqtt_root_ca.pem"; +// Certs and keys may have multiple versions on disk +// - no suffix: current active key/cert +// - old: previous key/cert from prior to last update +// - new: potential key that's generated, but not yet used - may not have an associated cert yet. +static const char* MQTT_CLIENT_OLD_CERT_FILENAME = "/mqtt_client_cert.pem.old"; static const char* MQTT_CLIENT_CERT_FILENAME = "/mqtt_client_cert.pem"; +static const char* MQTT_CLIENT_OLD_KEY_FILENAME = "/mqtt_client_key.pem.old"; static const char* MQTT_CLIENT_NEW_KEY_FILENAME = "/mqtt_client_key.pem.new"; static const char* MQTT_CLIENT_KEY_FILENAME = "/mqtt_client_key.pem"; static const char* MQTT_CLIENT_CSR_FILENAME = "/mqtt_client_csr.pem"; diff --git a/include/mqtt.h b/include/mqtt.h index c667e4f..8184b57 100644 --- a/include/mqtt.h +++ b/include/mqtt.h @@ -4,6 +4,13 @@ #include "globals.h" #include +// If you issue really large certs (e.g. long CN, extra options) this value may need to be +// increased, but 1600 is plenty for a typical CN and standard option openSSL issued cert. +#define MQTT_CERT_SIZE 1600 + +// Use larger of cert or config for MQTT buffer size. +#define MQTT_BUFFER_SIZE MQTT_CERT_SIZE > CONFIG_SIZE ? MQTT_CERT_SIZE : CONFIG_SIZE + namespace mqtt { typedef void (*calibrateCo2SensorCallback_t)(uint16_t); typedef void (*setTemperatureOffsetCallback_t)(float); diff --git a/include/mqtt_auth.h b/include/mqtt_auth.h index 300a1c3..d582d6f 100644 --- a/include/mqtt_auth.h +++ b/include/mqtt_auth.h @@ -6,6 +6,8 @@ namespace mqtt { bool initKey(); + bool installCert(char *, const unsigned int); + unsigned char *readPEM(const char *); } #endif \ No newline at end of file diff --git a/src/mqtt.cpp b/src/mqtt.cpp index 73d1b17..60f3513 100644 --- a/src/mqtt.cpp +++ b/src/mqtt.cpp @@ -255,6 +255,13 @@ namespace mqtt { requestCert(false); } else if (strncmp(buf, "regenerateKey", strlen(buf)) == 0) { requestCert(true); + } else if (strncmp(buf, "installCert", strlen(buf)) == 0) { + // TODO: Add config fallback support incase this doesn't work + // Use at your own risk for now. + if (installCert(&msg[0], length)) { + delay(1000); + esp_restart(); + } } else if (strncmp(buf, "getConfig", strlen(buf)) == 0) { publishConfiguration(); } else if (strncmp(buf, "setConfig", strlen(buf)) == 0) { @@ -389,7 +396,7 @@ namespace mqtt { mqtt_client = new PubSubClient(*wifiClient); mqtt_client->setServer(config.mqttHost, config.mqttServerPort); mqtt_client->setCallback(callback); - if (!mqtt_client->setBufferSize(CONFIG_SIZE)) ESP_LOGE(TAG, "mqtt_client->setBufferSize failed!"); + if (!mqtt_client->setBufferSize(MQTT_BUFFER_SIZE)) ESP_LOGE(TAG, "mqtt_client->setBufferSize failed!"); } void mqttLoop(void* pvParameters) { diff --git a/src/mqtt_auth.cpp b/src/mqtt_auth.cpp index 2c1b0e0..e4ddec3 100644 --- a/src/mqtt_auth.cpp +++ b/src/mqtt_auth.cpp @@ -8,6 +8,8 @@ #include #include #include +#include "mbedtls/x509.h" +#include "mbedtls/x509_crt.h" #include #include #include @@ -84,6 +86,43 @@ namespace mqtt { return true; } + // Trims any trailing space/newlines and ensures null terminated. + // Buf must be at least buflen+1 so there's space to add a null if required. + int trimAndNull(unsigned char *buf, int buflen) { + while (buf[buflen-1] == '\n' || buf[buflen-1] == ' ') { + buf[buflen-1] = '\0'; + buflen--; + } + if (buf[buflen-1] != '\0') { + buf[buflen] = '\0'; + buflen++; + } + return buflen; + } + + // Helper to read a PEM file from disk. + int readPEM(const char *filename, unsigned char *buf, int buflen) { + File f; + if (!(f = LittleFS.open(filename, FILE_READ))) { + ESP_LOGE(TAG, "could not read %s", filename); + return -1; + } + + if (buflen < f.size()+1) { + ESP_LOGE(TAG, "Insufficient buffer to read %s", filename); + return -1; + } + int read = f.read((uint8_t *)buf, buflen); + if (read != f.size()) { + ESP_LOGE(TAG, "PEM read from %s failed", filename); + return -1; + } + f.close(); + + // Strip any trailing newline, ensure null terminated. + return trimAndNull(buf, read); + } + // Attempts to initialize an RSA key (and associated CSR) for the device bool initKey(void) { ESP_LOGD(TAG, "Generating RSA private key... "); @@ -156,4 +195,135 @@ namespace mqtt { return finishInitKey(key, req, entropy, ctr_drbg, output_buf, "", 0); } + // Clean-up after certificate installation and report status + bool finishInstallCert(mbedtls_x509_crt *cacert, mbedtls_x509_crt *crt, mbedtls_x509_crl *cacrl, + mbedtls_pk_context *pk, mbedtls_ctr_drbg_context *ctr_drbg, const char *step, int ret) { + + mbedtls_x509_crt_free(cacert); + mbedtls_x509_crt_free(crt); + mbedtls_pk_free(pk); + mbedtls_ctr_drbg_free(ctr_drbg); + delete cacert; + delete crt; + delete pk; + delete ctr_drbg; + + if (ret != 0) { + char *status = (char *)malloc(1024); + char *errMsg = (char *)malloc(1024); + if (status != NULL && errMsg != NULL) { + mbedtls_strerror( ret, errMsg, 1024); + snprintf(status, 1024, "Failed to install certificate at %s: %s", step, errMsg); + free(errMsg); + } else { + ESP_LOGE(TAG, "(status not sent due to alloc failure) Failed to install certificate at %s", step); + } + // waiting for https://github.com/oseiler2/CO2Monitor/pull/14/files + //sendStatus(status) + ESP_LOGD(TAG, "installCert finished: %s", status); + free(status); + return false; + } + + //sendStatus("Key initialized successfully"); + ESP_LOGD(TAG, "Installed new certificate with Subject: %s", step); + return true; + } + + // Attempts to install the provided cert + bool installCert(char *cert_pem, const unsigned int len) { + ESP_LOGD(TAG, "Installing certificate... "); + + mbedtls_ctr_drbg_context *ctr_drbg = new mbedtls_ctr_drbg_context; + mbedtls_x509_crt *cacert = new mbedtls_x509_crt; + mbedtls_x509_crt *crt = new mbedtls_x509_crt; + mbedtls_x509_crl *cacrl = new mbedtls_x509_crl; + mbedtls_pk_context *pk = new mbedtls_pk_context; + + int ret = 1; + + mbedtls_ctr_drbg_init(ctr_drbg); + mbedtls_x509_crt_init(cacert); + mbedtls_x509_crt_init(crt); + mbedtls_pk_init(pk); + memset(cacrl, 0, sizeof(mbedtls_x509_crl)); + + // Load CA + unsigned char *pem_buf = (unsigned char *)malloc(PEM_BUFLEN); + if (pem_buf == NULL) { + finishInstallCert(cacert, crt, cacrl, pk, ctr_drbg, "ca buffer", MBEDTLS_ERR_X509_ALLOC_FAILED); + } + int ca_len = readPEM(MQTT_ROOT_CA_FILENAME, pem_buf, PEM_BUFLEN); + if (ca_len > 0) { + ret = mbedtls_x509_crt_parse(cacert, pem_buf, ca_len); + } else { + ret = MBEDTLS_ERR_X509_FILE_IO_ERROR; + } + free(pem_buf); + if (ret < 0) { + return finishInstallCert(cacert, crt, cacrl, pk, ctr_drbg, "loadca", ret); + } + + // Load cert + int pem_len = trimAndNull((unsigned char *)cert_pem, len); + if ((ret = mbedtls_x509_crt_parse(crt, (const unsigned char *)cert_pem, pem_len)) < 0) { + return finishInstallCert(cacert, crt, cacrl, pk, ctr_drbg, "parse cert", ret); + } + if (ret != 0) { + return finishInstallCert(cacert, crt, cacrl, pk, ctr_drbg, "bad cert", 0); + } + + // Validate against CA. + uint32_t flags; + if ((ret = mbedtls_x509_crt_verify(crt, cacert, cacrl, NULL, &flags, NULL, NULL)) != 0) { + return finishInstallCert(cacert, crt, cacrl, pk, ctr_drbg, "verify ca", ret); + } + + // Validate against key + pem_buf = (unsigned char *)malloc(PEM_BUFLEN); + if (pem_buf == NULL) { + finishInstallCert(cacert, crt, cacrl, pk, ctr_drbg, "key buffer", MBEDTLS_ERR_X509_ALLOC_FAILED); + } + int key_len = readPEM(MQTT_CLIENT_NEW_KEY_FILENAME, pem_buf, PEM_BUFLEN); + if (key_len > 0) { + ret = mbedtls_pk_parse_key(pk, pem_buf, key_len, NULL, 0); + } else { + ret = MBEDTLS_ERR_X509_FILE_IO_ERROR; + } + free(pem_buf); + if (ret != 0) { + return finishInstallCert(cacert, crt, cacrl, pk, ctr_drbg, "load key", ret); + } + if ((ret = mbedtls_pk_check_pair(&crt->pk, pk)) != 0) { + return finishInstallCert(cacert, crt, cacrl, pk, ctr_drbg, "verify key", ret); + } + + // Backup existing key/cert before installing + if (LittleFS.exists(MQTT_CLIENT_KEY_FILENAME)) { + if (!LittleFS.rename(MQTT_CLIENT_KEY_FILENAME, MQTT_CLIENT_OLD_KEY_FILENAME)) { + return finishInstallCert(cacert, crt, cacrl, pk, ctr_drbg, "backup key", MBEDTLS_ERR_X509_FILE_IO_ERROR); + } + } + if (LittleFS.exists(MQTT_CLIENT_CERT_FILENAME)) { + if (!LittleFS.rename(MQTT_CLIENT_CERT_FILENAME, MQTT_CLIENT_OLD_CERT_FILENAME)) { + return finishInstallCert(cacert, crt, cacrl, pk, ctr_drbg, "backup cert", MBEDTLS_ERR_X509_FILE_IO_ERROR); + } + } + + // Install new + if (!LittleFS.rename(MQTT_CLIENT_NEW_KEY_FILENAME, MQTT_CLIENT_KEY_FILENAME)) { + return finishInstallCert(cacert, crt, cacrl, pk, ctr_drbg, "write key", MBEDTLS_ERR_X509_FILE_IO_ERROR); + } + if (!writeFile(MQTT_CLIENT_CERT_FILENAME, (unsigned char *)cert_pem)) { + return finishInstallCert(cacert, crt, cacrl, pk, ctr_drbg, "write cert", MBEDTLS_ERR_X509_FILE_IO_ERROR); + } + + // Extract subject + char subject_name[256]; + if ((ret = mbedtls_x509_dn_gets(&subject_name[0], sizeof(subject_name), &crt->subject)) < 0) { + return finishInstallCert(cacert, crt, cacrl, pk, ctr_drbg, "get subject", ret); + } + return finishInstallCert(cacert, crt, cacrl, pk, ctr_drbg, subject_name, 0); + } + } \ No newline at end of file From c3f60551f4235538ee9b82ed20afa550f166d66a Mon Sep 17 00:00:00 2001 From: Oliver Seiler Date: Tue, 20 Sep 2022 18:04:07 +1200 Subject: [PATCH 7/8] rename files --- include/{mqtt_auth.h => certs.h} | 0 src/{mqtt_auth.cpp => certs.cpp} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename include/{mqtt_auth.h => certs.h} (100%) rename src/{mqtt_auth.cpp => certs.cpp} (100%) diff --git a/include/mqtt_auth.h b/include/certs.h similarity index 100% rename from include/mqtt_auth.h rename to include/certs.h diff --git a/src/mqtt_auth.cpp b/src/certs.cpp similarity index 100% rename from src/mqtt_auth.cpp rename to src/certs.cpp From 45c08a28962e02dcfea950e602397ed3f00efa1e Mon Sep 17 00:00:00 2001 From: Oliver Seiler Date: Tue, 20 Sep 2022 18:08:27 +1200 Subject: [PATCH 8/8] minor refactor and formatting --- include/certs.h | 9 +- src/certs.cpp | 614 ++++++++++++++++++++++++------------------------ src/mqtt.cpp | 101 ++++---- 3 files changed, 364 insertions(+), 360 deletions(-) diff --git a/include/certs.h b/include/certs.h index d582d6f..15b5b9c 100644 --- a/include/certs.h +++ b/include/certs.h @@ -1,13 +1,12 @@ -#ifndef _MQTT_AUTH_H -#define _MQTT_AUTH_H +#ifndef _CERTS_H +#define _CERTS_H // Haven't seen one bigger than ~1700bytes #define PEM_BUFLEN 2048 -namespace mqtt { +namespace certs { bool initKey(); - bool installCert(char *, const unsigned int); - unsigned char *readPEM(const char *); + bool installCert(char* cert_pem, const unsigned int len); } #endif \ No newline at end of file diff --git a/src/certs.cpp b/src/certs.cpp index e4ddec3..1d3fef4 100644 --- a/src/certs.cpp +++ b/src/certs.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include #include @@ -8,8 +8,8 @@ #include #include #include -#include "mbedtls/x509.h" -#include "mbedtls/x509_crt.h" +#include +#include #include #include #include @@ -21,309 +21,311 @@ // Local logging tag static const char TAG[] = __FILE__; -namespace mqtt { - - #define KEY_SIZE 2048 - #define EXPONENT 65537 - - // Clean-up after key initialization and report status - bool finishInitKey(mbedtls_pk_context *key, mbedtls_x509write_csr *req, - mbedtls_entropy_context *entropy, - mbedtls_ctr_drbg_context *ctr_drbg, unsigned char *output_buf, - const char *step, int ret) { - - // Reset watchdog timer to default - esp_task_wdt_init(CONFIG_ESP_TASK_WDT_TIMEOUT_S, true); - - mbedtls_pk_free(key); - mbedtls_x509write_csr_free(req); - mbedtls_ctr_drbg_free(ctr_drbg); - mbedtls_entropy_free(entropy); - delete key; - delete req; - delete ctr_drbg; - delete entropy; - if (output_buf != NULL) { - free(output_buf); - } - - if (ret != 0) { - char *status = (char *)malloc(1024); - char *errMsg = (char *)malloc(1024); - if (status != NULL && errMsg != NULL) { - mbedtls_strerror( ret, errMsg, sizeof(errMsg)); - snprintf(status, 1024, "Failed to initialize key at %s: %s", step, errMsg); - free(errMsg); - } else { - ESP_LOGE(TAG, "(status not sent due to alloc failure) Failed to initialize key at %s", step); - } - // waiting for https://github.com/oseiler2/CO2Monitor/pull/14/files - //sendStatus(status) - ESP_LOGD(TAG, "initKey finished: %s", status); - free(status); - return false; - } - - //sendStatus("Key initialized successfully"); - ESP_LOGD(TAG, "Key initialized successfully"); - return true; - } - - // Helper to write a file to disk - bool writeFile(const char *name, unsigned char *contents) { - File f; - if (!(f = LittleFS.open(name, FILE_WRITE))) { - return false; - } - - int len = strlen((char *)contents); - if (f.write(contents, len) != len) { - f.close(); - return false; - } - - f.close(); - return true; - } - - // Trims any trailing space/newlines and ensures null terminated. - // Buf must be at least buflen+1 so there's space to add a null if required. - int trimAndNull(unsigned char *buf, int buflen) { - while (buf[buflen-1] == '\n' || buf[buflen-1] == ' ') { - buf[buflen-1] = '\0'; - buflen--; - } - if (buf[buflen-1] != '\0') { - buf[buflen] = '\0'; - buflen++; - } - return buflen; - } - - // Helper to read a PEM file from disk. - int readPEM(const char *filename, unsigned char *buf, int buflen) { - File f; - if (!(f = LittleFS.open(filename, FILE_READ))) { - ESP_LOGE(TAG, "could not read %s", filename); - return -1; - } - - if (buflen < f.size()+1) { - ESP_LOGE(TAG, "Insufficient buffer to read %s", filename); - return -1; - } - int read = f.read((uint8_t *)buf, buflen); - if (read != f.size()) { - ESP_LOGE(TAG, "PEM read from %s failed", filename); - return -1; - } - f.close(); - - // Strip any trailing newline, ensure null terminated. - return trimAndNull(buf, read); - } - - // Attempts to initialize an RSA key (and associated CSR) for the device - bool initKey(void) { - ESP_LOGD(TAG, "Generating RSA private key... "); - - // Getting the entropy for the key can take a few seconds, which doesn't play well with the default (5s) watchdog timer - // So bump it up for a bit while we generate the key - will be reset to the default value in finishInitKey above. - esp_task_wdt_init(15, true); - - mbedtls_pk_context *key = new mbedtls_pk_context; - mbedtls_x509write_csr *req = new mbedtls_x509write_csr; - mbedtls_entropy_context *entropy = new mbedtls_entropy_context; - mbedtls_ctr_drbg_context *ctr_drbg = new mbedtls_ctr_drbg_context; - String mac = WifiManager::getMac(); - int ret = 1; - - mbedtls_ctr_drbg_init(ctr_drbg); - mbedtls_entropy_init(entropy); - mbedtls_pk_init(key); - mbedtls_x509write_csr_init(req); - mbedtls_x509write_csr_set_md_alg(req, MBEDTLS_MD_SHA256); - - ESP_LOGD(TAG, "Initializing PRNG... "); - if ((ret = mbedtls_ctr_drbg_seed(ctr_drbg, mbedtls_entropy_func, entropy, - (const unsigned char *)mac.c_str(), mac.length())) != 0) { - return finishInitKey(key, req, entropy, ctr_drbg, NULL, "seed", ret); - } - - ESP_LOGD(TAG, "PK setup... "); - if ((ret = mbedtls_pk_setup(key, mbedtls_pk_info_from_type((mbedtls_pk_type_t)MBEDTLS_PK_RSA))) != 0) { - return finishInitKey(key, req, entropy, ctr_drbg, NULL, "setup", ret); - } - - ESP_LOGD(TAG, "Actually generating... "); - if ((ret = mbedtls_rsa_gen_key(mbedtls_pk_rsa(*key), mbedtls_ctr_drbg_random, ctr_drbg, KEY_SIZE, EXPONENT)) != 0) { - return finishInitKey(key, req, entropy, ctr_drbg, NULL, "gen key", ret); - } - - unsigned char *output_buf = (unsigned char *)malloc(PEM_BUFLEN); - if (output_buf == NULL) { - return finishInitKey(key, req, entropy, ctr_drbg, NULL, "allocate buffer", MBEDTLS_ERR_PK_ALLOC_FAILED); - } - - memset(output_buf, 0, PEM_BUFLEN); - ESP_LOGD(TAG, "Writing key PEM.. "); - if( ( ret = mbedtls_pk_write_key_pem( key, output_buf, PEM_BUFLEN ) ) != 0 ) { - return finishInitKey(key, req, entropy, ctr_drbg, output_buf, "serialize key", ret); - } - if (!writeFile(MQTT_CLIENT_NEW_KEY_FILENAME, output_buf)) { - return finishInitKey(key, req, entropy, ctr_drbg, output_buf, "write key", MBEDTLS_ERR_PK_FILE_IO_ERROR); - } - - ESP_LOGD(TAG, "Generating CSR... "); - char cn[50]; - snprintf(&cn[0], 50, "CN=%s,O=CO2Monitor", mac); - if ((ret = mbedtls_x509write_csr_set_subject_name( req, cn)) != 0 ) { - return finishInitKey(key, req, entropy, ctr_drbg, output_buf, "set subject name", ret); - } - mbedtls_x509write_csr_set_key(req, key); - - ESP_LOGD(TAG, "Writing CSR PEM... "); - memset( output_buf, 0, PEM_BUFLEN ); - if ((ret = mbedtls_x509write_csr_pem(req, output_buf, PEM_BUFLEN, mbedtls_ctr_drbg_random, ctr_drbg)) < 0) { - return finishInitKey(key, req, entropy, ctr_drbg, output_buf, "serialize csr", ret); - } - if (!writeFile(MQTT_CLIENT_CSR_FILENAME, output_buf)) { - return finishInitKey(key, req, entropy, ctr_drbg, output_buf, "write csr", MBEDTLS_ERR_PK_FILE_IO_ERROR); - } - - ESP_LOGD(TAG, "Key generated."); - return finishInitKey(key, req, entropy, ctr_drbg, output_buf, "", 0); - } - - // Clean-up after certificate installation and report status - bool finishInstallCert(mbedtls_x509_crt *cacert, mbedtls_x509_crt *crt, mbedtls_x509_crl *cacrl, - mbedtls_pk_context *pk, mbedtls_ctr_drbg_context *ctr_drbg, const char *step, int ret) { - - mbedtls_x509_crt_free(cacert); - mbedtls_x509_crt_free(crt); - mbedtls_pk_free(pk); - mbedtls_ctr_drbg_free(ctr_drbg); - delete cacert; - delete crt; - delete pk; - delete ctr_drbg; - - if (ret != 0) { - char *status = (char *)malloc(1024); - char *errMsg = (char *)malloc(1024); - if (status != NULL && errMsg != NULL) { - mbedtls_strerror( ret, errMsg, 1024); - snprintf(status, 1024, "Failed to install certificate at %s: %s", step, errMsg); - free(errMsg); - } else { - ESP_LOGE(TAG, "(status not sent due to alloc failure) Failed to install certificate at %s", step); - } - // waiting for https://github.com/oseiler2/CO2Monitor/pull/14/files - //sendStatus(status) - ESP_LOGD(TAG, "installCert finished: %s", status); - free(status); - return false; - } - - //sendStatus("Key initialized successfully"); - ESP_LOGD(TAG, "Installed new certificate with Subject: %s", step); - return true; - } - - // Attempts to install the provided cert - bool installCert(char *cert_pem, const unsigned int len) { - ESP_LOGD(TAG, "Installing certificate... "); - - mbedtls_ctr_drbg_context *ctr_drbg = new mbedtls_ctr_drbg_context; - mbedtls_x509_crt *cacert = new mbedtls_x509_crt; - mbedtls_x509_crt *crt = new mbedtls_x509_crt; - mbedtls_x509_crl *cacrl = new mbedtls_x509_crl; - mbedtls_pk_context *pk = new mbedtls_pk_context; - - int ret = 1; - - mbedtls_ctr_drbg_init(ctr_drbg); - mbedtls_x509_crt_init(cacert); - mbedtls_x509_crt_init(crt); - mbedtls_pk_init(pk); - memset(cacrl, 0, sizeof(mbedtls_x509_crl)); - - // Load CA - unsigned char *pem_buf = (unsigned char *)malloc(PEM_BUFLEN); - if (pem_buf == NULL) { - finishInstallCert(cacert, crt, cacrl, pk, ctr_drbg, "ca buffer", MBEDTLS_ERR_X509_ALLOC_FAILED); - } - int ca_len = readPEM(MQTT_ROOT_CA_FILENAME, pem_buf, PEM_BUFLEN); - if (ca_len > 0) { - ret = mbedtls_x509_crt_parse(cacert, pem_buf, ca_len); - } else { - ret = MBEDTLS_ERR_X509_FILE_IO_ERROR; - } - free(pem_buf); - if (ret < 0) { - return finishInstallCert(cacert, crt, cacrl, pk, ctr_drbg, "loadca", ret); - } - - // Load cert - int pem_len = trimAndNull((unsigned char *)cert_pem, len); - if ((ret = mbedtls_x509_crt_parse(crt, (const unsigned char *)cert_pem, pem_len)) < 0) { - return finishInstallCert(cacert, crt, cacrl, pk, ctr_drbg, "parse cert", ret); - } - if (ret != 0) { - return finishInstallCert(cacert, crt, cacrl, pk, ctr_drbg, "bad cert", 0); - } - - // Validate against CA. - uint32_t flags; - if ((ret = mbedtls_x509_crt_verify(crt, cacert, cacrl, NULL, &flags, NULL, NULL)) != 0) { - return finishInstallCert(cacert, crt, cacrl, pk, ctr_drbg, "verify ca", ret); - } - - // Validate against key - pem_buf = (unsigned char *)malloc(PEM_BUFLEN); - if (pem_buf == NULL) { - finishInstallCert(cacert, crt, cacrl, pk, ctr_drbg, "key buffer", MBEDTLS_ERR_X509_ALLOC_FAILED); - } - int key_len = readPEM(MQTT_CLIENT_NEW_KEY_FILENAME, pem_buf, PEM_BUFLEN); - if (key_len > 0) { - ret = mbedtls_pk_parse_key(pk, pem_buf, key_len, NULL, 0); - } else { - ret = MBEDTLS_ERR_X509_FILE_IO_ERROR; - } - free(pem_buf); - if (ret != 0) { - return finishInstallCert(cacert, crt, cacrl, pk, ctr_drbg, "load key", ret); - } - if ((ret = mbedtls_pk_check_pair(&crt->pk, pk)) != 0) { - return finishInstallCert(cacert, crt, cacrl, pk, ctr_drbg, "verify key", ret); - } - - // Backup existing key/cert before installing - if (LittleFS.exists(MQTT_CLIENT_KEY_FILENAME)) { - if (!LittleFS.rename(MQTT_CLIENT_KEY_FILENAME, MQTT_CLIENT_OLD_KEY_FILENAME)) { - return finishInstallCert(cacert, crt, cacrl, pk, ctr_drbg, "backup key", MBEDTLS_ERR_X509_FILE_IO_ERROR); - } - } - if (LittleFS.exists(MQTT_CLIENT_CERT_FILENAME)) { - if (!LittleFS.rename(MQTT_CLIENT_CERT_FILENAME, MQTT_CLIENT_OLD_CERT_FILENAME)) { - return finishInstallCert(cacert, crt, cacrl, pk, ctr_drbg, "backup cert", MBEDTLS_ERR_X509_FILE_IO_ERROR); - } - } - - // Install new - if (!LittleFS.rename(MQTT_CLIENT_NEW_KEY_FILENAME, MQTT_CLIENT_KEY_FILENAME)) { - return finishInstallCert(cacert, crt, cacrl, pk, ctr_drbg, "write key", MBEDTLS_ERR_X509_FILE_IO_ERROR); - } - if (!writeFile(MQTT_CLIENT_CERT_FILENAME, (unsigned char *)cert_pem)) { - return finishInstallCert(cacert, crt, cacrl, pk, ctr_drbg, "write cert", MBEDTLS_ERR_X509_FILE_IO_ERROR); - } - - // Extract subject - char subject_name[256]; - if ((ret = mbedtls_x509_dn_gets(&subject_name[0], sizeof(subject_name), &crt->subject)) < 0) { - return finishInstallCert(cacert, crt, cacrl, pk, ctr_drbg, "get subject", ret); - } - return finishInstallCert(cacert, crt, cacrl, pk, ctr_drbg, subject_name, 0); +namespace certs { + +#define KEY_SIZE 2048 +#define EXPONENT 65537 + + // Clean-up after key initialization and report status + bool finishInitKey(mbedtls_pk_context* key, mbedtls_x509write_csr* req, + mbedtls_entropy_context* entropy, + mbedtls_ctr_drbg_context* ctr_drbg, unsigned char* output_buf, + const char* step, int ret) { + + // Reset watchdog timer to default + esp_task_wdt_init(CONFIG_ESP_TASK_WDT_TIMEOUT_S, true); + + mbedtls_pk_free(key); + mbedtls_x509write_csr_free(req); + mbedtls_ctr_drbg_free(ctr_drbg); + mbedtls_entropy_free(entropy); + delete key; + delete req; + delete ctr_drbg; + delete entropy; + if (output_buf != NULL) { + free(output_buf); } + if (ret != 0) { + char* status = (char*)malloc(1024); + char* errMsg = (char*)malloc(1024); + if (status != NULL && errMsg != NULL) { + mbedtls_strerror(ret, errMsg, sizeof(errMsg)); + snprintf(status, 1024, "Failed to initialize key at %s: %s", step, errMsg); + free(errMsg); + } else { + ESP_LOGE(TAG, "(status not sent due to alloc failure) Failed to initialize key at %s", step); + } + // waiting for https://github.com/oseiler2/CO2Monitor/pull/14/files + //sendStatus(status) + ESP_LOGD(TAG, "initKey finished: %s", status); + free(status); + return false; + } + + //sendStatus("Key initialized successfully"); + ESP_LOGD(TAG, "Key initialized successfully"); + return true; + } + + // Helper to write a file to disk + bool writeFile(const char* name, unsigned char* contents) { + File f; + if (!(f = LittleFS.open(name, FILE_WRITE))) { + return false; + } + + int len = strlen((char*)contents); + if (f.write(contents, len) != len) { + f.close(); + return false; + } + + f.close(); + return true; + } + + // Trims any trailing space/newlines and ensures null terminated. + // Buf must be at least buflen+1 so there's space to add a null if required. + int trimAndNull(unsigned char* buf, int buflen) { + while (buf[buflen - 1] == '\n' || buf[buflen - 1] == ' ') { + buf[buflen - 1] = '\0'; + buflen--; + } + if (buf[buflen - 1] != '\0') { + buf[buflen] = '\0'; + buflen++; + } + return buflen; + } + + // Helper to read a PEM file from disk. + int readPEM(const char* filename, unsigned char* buf, int buflen) { + File f; + if (!(f = LittleFS.open(filename, FILE_READ))) { + ESP_LOGE(TAG, "could not read %s", filename); + return -1; + } + + if (buflen < f.size() + 1) { + ESP_LOGE(TAG, "Insufficient buffer to read %s", filename); + f.close(); + return -1; + } + int read = f.read((uint8_t*)buf, buflen); + if (read != f.size()) { + ESP_LOGE(TAG, "PEM read from %s failed", filename); + f.close(); + return -1; + } + f.close(); + + // Strip any trailing newline, ensure null terminated. + return trimAndNull(buf, read); + } + + // Attempts to initialize an RSA key (and associated CSR) for the device + bool initKey(void) { + ESP_LOGD(TAG, "Generating RSA private key... "); + + // Getting the entropy for the key can take a few seconds, which doesn't play well with the default (5s) watchdog timer + // So bump it up for a bit while we generate the key - will be reset to the default value in finishInitKey above. + esp_task_wdt_init(15, true); + + mbedtls_pk_context* key = new mbedtls_pk_context; + mbedtls_x509write_csr* req = new mbedtls_x509write_csr; + mbedtls_entropy_context* entropy = new mbedtls_entropy_context; + mbedtls_ctr_drbg_context* ctr_drbg = new mbedtls_ctr_drbg_context; + String mac = WifiManager::getMac(); + int ret = 1; + + mbedtls_ctr_drbg_init(ctr_drbg); + mbedtls_entropy_init(entropy); + mbedtls_pk_init(key); + mbedtls_x509write_csr_init(req); + mbedtls_x509write_csr_set_md_alg(req, MBEDTLS_MD_SHA256); + + ESP_LOGD(TAG, "Initializing PRNG... "); + if ((ret = mbedtls_ctr_drbg_seed(ctr_drbg, mbedtls_entropy_func, entropy, + (const unsigned char*)mac.c_str(), mac.length())) != 0) { + return finishInitKey(key, req, entropy, ctr_drbg, NULL, "seed", ret); + } + + ESP_LOGD(TAG, "PK setup... "); + if ((ret = mbedtls_pk_setup(key, mbedtls_pk_info_from_type((mbedtls_pk_type_t)MBEDTLS_PK_RSA))) != 0) { + return finishInitKey(key, req, entropy, ctr_drbg, NULL, "setup", ret); + } + + ESP_LOGD(TAG, "Actually generating... "); + if ((ret = mbedtls_rsa_gen_key(mbedtls_pk_rsa(*key), mbedtls_ctr_drbg_random, ctr_drbg, KEY_SIZE, EXPONENT)) != 0) { + return finishInitKey(key, req, entropy, ctr_drbg, NULL, "gen key", ret); + } + + unsigned char* output_buf = (unsigned char*)malloc(PEM_BUFLEN); + if (output_buf == NULL) { + return finishInitKey(key, req, entropy, ctr_drbg, NULL, "allocate buffer", MBEDTLS_ERR_PK_ALLOC_FAILED); + } + + memset(output_buf, 0, PEM_BUFLEN); + ESP_LOGD(TAG, "Writing key PEM.. "); + if ((ret = mbedtls_pk_write_key_pem(key, output_buf, PEM_BUFLEN)) != 0) { + return finishInitKey(key, req, entropy, ctr_drbg, output_buf, "serialize key", ret); + } + if (!writeFile(MQTT_CLIENT_NEW_KEY_FILENAME, output_buf)) { + return finishInitKey(key, req, entropy, ctr_drbg, output_buf, "write key", MBEDTLS_ERR_PK_FILE_IO_ERROR); + } + + ESP_LOGD(TAG, "Generating CSR... "); + char cn[50]; + snprintf(&cn[0], 50, "CN=%s,O=CO2Monitor", mac); + if ((ret = mbedtls_x509write_csr_set_subject_name(req, cn)) != 0) { + return finishInitKey(key, req, entropy, ctr_drbg, output_buf, "set subject name", ret); + } + mbedtls_x509write_csr_set_key(req, key); + + ESP_LOGD(TAG, "Writing CSR PEM... "); + memset(output_buf, 0, PEM_BUFLEN); + if ((ret = mbedtls_x509write_csr_pem(req, output_buf, PEM_BUFLEN, mbedtls_ctr_drbg_random, ctr_drbg)) < 0) { + return finishInitKey(key, req, entropy, ctr_drbg, output_buf, "serialize csr", ret); + } + if (!writeFile(MQTT_CLIENT_CSR_FILENAME, output_buf)) { + return finishInitKey(key, req, entropy, ctr_drbg, output_buf, "write csr", MBEDTLS_ERR_PK_FILE_IO_ERROR); + } + + ESP_LOGD(TAG, "Key generated."); + return finishInitKey(key, req, entropy, ctr_drbg, output_buf, "", 0); + } + + // Clean-up after certificate installation and report status + bool finishInstallCert(mbedtls_x509_crt* cacert, mbedtls_x509_crt* crt, mbedtls_x509_crl* cacrl, + mbedtls_pk_context* pk, mbedtls_ctr_drbg_context* ctr_drbg, const char* step, int ret) { + + mbedtls_x509_crt_free(cacert); + mbedtls_x509_crt_free(crt); + mbedtls_pk_free(pk); + mbedtls_ctr_drbg_free(ctr_drbg); + delete cacert; + delete crt; + delete pk; + delete ctr_drbg; + + if (ret != 0) { + char* status = (char*)malloc(1024); + char* errMsg = (char*)malloc(1024); + if (status != NULL && errMsg != NULL) { + mbedtls_strerror(ret, errMsg, 1024); + snprintf(status, 1024, "Failed to install certificate at %s: %s", step, errMsg); + free(errMsg); + } else { + ESP_LOGE(TAG, "(status not sent due to alloc failure) Failed to install certificate at %s", step); + } + // waiting for https://github.com/oseiler2/CO2Monitor/pull/14/files + //sendStatus(status) + ESP_LOGD(TAG, "installCert finished: %s", status); + free(status); + return false; + } + + //sendStatus("Key initialized successfully"); + ESP_LOGD(TAG, "Installed new certificate with Subject: %s", step); + return true; + } + + // Attempts to install the provided cert + bool installCert(char* cert_pem, const unsigned int len) { + ESP_LOGD(TAG, "Installing certificate... "); + + mbedtls_ctr_drbg_context* ctr_drbg = new mbedtls_ctr_drbg_context; + mbedtls_x509_crt* cacert = new mbedtls_x509_crt; + mbedtls_x509_crt* crt = new mbedtls_x509_crt; + mbedtls_x509_crl* cacrl = new mbedtls_x509_crl; + mbedtls_pk_context* pk = new mbedtls_pk_context; + + int ret = 1; + + mbedtls_ctr_drbg_init(ctr_drbg); + mbedtls_x509_crt_init(cacert); + mbedtls_x509_crt_init(crt); + mbedtls_pk_init(pk); + memset(cacrl, 0, sizeof(mbedtls_x509_crl)); + + // Load CA + unsigned char* pem_buf = (unsigned char*)malloc(PEM_BUFLEN); + if (pem_buf == NULL) { + finishInstallCert(cacert, crt, cacrl, pk, ctr_drbg, "ca buffer", MBEDTLS_ERR_X509_ALLOC_FAILED); + } + int ca_len = readPEM(MQTT_ROOT_CA_FILENAME, pem_buf, PEM_BUFLEN); + if (ca_len > 0) { + ret = mbedtls_x509_crt_parse(cacert, pem_buf, ca_len); + } else { + ret = MBEDTLS_ERR_X509_FILE_IO_ERROR; + } + free(pem_buf); + if (ret < 0) { + return finishInstallCert(cacert, crt, cacrl, pk, ctr_drbg, "loadca", ret); + } + + // Load cert + int pem_len = trimAndNull((unsigned char*)cert_pem, len); + if ((ret = mbedtls_x509_crt_parse(crt, (const unsigned char*)cert_pem, pem_len)) < 0) { + return finishInstallCert(cacert, crt, cacrl, pk, ctr_drbg, "parse cert", ret); + } + if (ret != 0) { + return finishInstallCert(cacert, crt, cacrl, pk, ctr_drbg, "bad cert", 0); + } + + // Validate against CA. + uint32_t flags; + if ((ret = mbedtls_x509_crt_verify(crt, cacert, cacrl, NULL, &flags, NULL, NULL)) != 0) { + return finishInstallCert(cacert, crt, cacrl, pk, ctr_drbg, "verify ca", ret); + } + + // Validate against key + pem_buf = (unsigned char*)malloc(PEM_BUFLEN); + if (pem_buf == NULL) { + finishInstallCert(cacert, crt, cacrl, pk, ctr_drbg, "key buffer", MBEDTLS_ERR_X509_ALLOC_FAILED); + } + int key_len = readPEM(MQTT_CLIENT_NEW_KEY_FILENAME, pem_buf, PEM_BUFLEN); + if (key_len > 0) { + ret = mbedtls_pk_parse_key(pk, pem_buf, key_len, NULL, 0); + } else { + ret = MBEDTLS_ERR_X509_FILE_IO_ERROR; + } + free(pem_buf); + if (ret != 0) { + return finishInstallCert(cacert, crt, cacrl, pk, ctr_drbg, "load key", ret); + } + if ((ret = mbedtls_pk_check_pair(&crt->pk, pk)) != 0) { + return finishInstallCert(cacert, crt, cacrl, pk, ctr_drbg, "verify key", ret); + } + + // Backup existing key/cert before installing + if (LittleFS.exists(MQTT_CLIENT_KEY_FILENAME)) { + if (!LittleFS.rename(MQTT_CLIENT_KEY_FILENAME, MQTT_CLIENT_OLD_KEY_FILENAME)) { + return finishInstallCert(cacert, crt, cacrl, pk, ctr_drbg, "backup key", MBEDTLS_ERR_X509_FILE_IO_ERROR); + } + } + if (LittleFS.exists(MQTT_CLIENT_CERT_FILENAME)) { + if (!LittleFS.rename(MQTT_CLIENT_CERT_FILENAME, MQTT_CLIENT_OLD_CERT_FILENAME)) { + return finishInstallCert(cacert, crt, cacrl, pk, ctr_drbg, "backup cert", MBEDTLS_ERR_X509_FILE_IO_ERROR); + } + } + + // Install new + if (!LittleFS.rename(MQTT_CLIENT_NEW_KEY_FILENAME, MQTT_CLIENT_KEY_FILENAME)) { + return finishInstallCert(cacert, crt, cacrl, pk, ctr_drbg, "write key", MBEDTLS_ERR_X509_FILE_IO_ERROR); + } + if (!writeFile(MQTT_CLIENT_CERT_FILENAME, (unsigned char*)cert_pem)) { + return finishInstallCert(cacert, crt, cacrl, pk, ctr_drbg, "write cert", MBEDTLS_ERR_X509_FILE_IO_ERROR); + } + + // Extract subject + char subject_name[256]; + if ((ret = mbedtls_x509_dn_gets(&subject_name[0], sizeof(subject_name), &crt->subject)) < 0) { + return finishInstallCert(cacert, crt, cacrl, pk, ctr_drbg, "get subject", ret); + } + return finishInstallCert(cacert, crt, cacrl, pk, ctr_drbg, subject_name, 0); + } + } \ No newline at end of file diff --git a/src/mqtt.cpp b/src/mqtt.cpp index 60f3513..1ae021b 100644 --- a/src/mqtt.cpp +++ b/src/mqtt.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include #include @@ -29,7 +29,7 @@ namespace mqtt { const uint8_t X_CMD_PUBLISH_SENSORS = bit(0); const uint8_t X_CMD_PUBLISH_CONFIGURATION = bit(1); - const uint8_t X_CMD_REQUEST_CERT = bit(2); + const uint8_t X_CMD_REQUEST_CERT = bit(2); // TODO change to 3 TaskHandle_t mqttTask; QueueHandle_t mqttQueue; @@ -162,55 +162,57 @@ namespace mqtt { if (!mqtt_client->publish(buf, msg)) ESP_LOGE(TAG, "publish failed!"); } - // Attempts to register a CSR with the server to requst a certificate - void requestCert(bool regenerateKey) { - MqttMessage msg; - msg.cmd = X_CMD_REQUEST_CERT; - msg.mask = regenerateKey; // re-use mask field as regen flag. - if (mqttQueue) xQueueSendToBack(mqttQueue, (void*)&msg, pdMS_TO_TICKS(100)); - } + // Attempts to register a CSR with the server to requst a certificate + void requestCert(bool regenerateKey) { + MqttMessage msg; + msg.cmd = X_CMD_REQUEST_CERT; + msg.mask = regenerateKey; // re-use mask field as regen flag. + if (mqttQueue) xQueueSendToBack(mqttQueue, (void*)&msg, pdMS_TO_TICKS(100)); + } - void requestCertInternal(bool regenerateKey) { - ESP_LOGD(TAG, "Requesting Certificate..."); - if (!LittleFS.exists(MQTT_CLIENT_CSR_FILENAME) || regenerateKey) { - if (!initKey()) { - ESP_LOGE(TAG, "Failed to create MQTT client key"); - return; - } - } + void requestCertInternal(bool regenerateKey) { + ESP_LOGD(TAG, "Requesting Certificate..."); + if (!LittleFS.exists(MQTT_CLIENT_CSR_FILENAME) || regenerateKey) { + if (!certs::initKey()) { + ESP_LOGE(TAG, "Failed to create MQTT client key"); + return; + } + } - ESP_LOGD(TAG, "Submitting CSR..."); - File f; - if (!(f = LittleFS.open(MQTT_CLIENT_CSR_FILENAME, FILE_READ))) { - ESP_LOGE(TAG, "could not read CSR"); - return; - } + ESP_LOGD(TAG, "Submitting CSR..."); + File f; + if (!(f = LittleFS.open(MQTT_CLIENT_CSR_FILENAME, FILE_READ))) { + ESP_LOGE(TAG, "could not read CSR"); + return; + } - int len = f.size(); - char *csr = (char *)malloc(len+1); - if (csr == NULL) { - ESP_LOGE(TAG, "Could not malloc CSR buffer"); - return; - } - memset(csr, 0, len+1); - int read = f.read((uint8_t *)csr, len); - if (read != len) { - ESP_LOGE(TAG, "CSR read failed"); - free(csr); - return; - } - csr[len] = '\0'; - f.close(); - - if (mqtt_client->connected()) { - char topic[256]; - sprintf(topic, "%s/%u/up/csr", config.mqttTopic, config.deviceId); - ESP_LOGD(TAG, "Publishing certificate request: %s", topic); - if (!mqtt_client->publish(topic, csr)) ESP_LOGE(TAG, "failed to publish csr!"); - } else { - ESP_LOGE(TAG, "Generated CSR, but could not submit as MQTT not connected"); - } - free(csr); + int len = f.size(); + char* csr = (char*)malloc(len + 1); + if (csr == NULL) { + ESP_LOGE(TAG, "Could not malloc CSR buffer"); + f.close(); + return; + } + memset(csr, 0, len + 1); + int read = f.read((uint8_t*)csr, len); + if (read != len) { + ESP_LOGE(TAG, "CSR read failed"); + free(csr); + f.close(); + return; + } + csr[len] = '\0'; + f.close(); + + if (mqtt_client->connected()) { + char topic[256]; + sprintf(topic, "%s/%u/up/csr", config.mqttTopic, config.deviceId); + ESP_LOGD(TAG, "Publishing certificate request: %s", topic); + if (!mqtt_client->publish(topic, csr)) ESP_LOGE(TAG, "failed to publish csr!"); + } else { + ESP_LOGE(TAG, "Generated CSR, but could not submit as MQTT not connected"); + } + free(csr); } void callback(char* topic, byte* payload, unsigned int length) { @@ -258,7 +260,8 @@ namespace mqtt { } else if (strncmp(buf, "installCert", strlen(buf)) == 0) { // TODO: Add config fallback support incase this doesn't work // Use at your own risk for now. - if (installCert(&msg[0], length)) { + if (certs::installCert(&msg[0], length)) { + // TODO: publishStatusMsgInternal("installed new cert, rebooting shortly"); delay(1000); esp_restart(); }