#include #include #include #include "secure_api.h" #include "http_client.h" #include "constants.h" #include "crypto.h" #include "cJSON.h" #include "dev_name.h" #include "storage.h" #include "http_client.h" #include "esp_log.h" static const char *TAG = "secure_api"; static char *process_secure_request(const char *uri, const char *body) { char *url = calloc(1, 255); assert(url); size_t orig_len = storage_get_secure_origin(url); assert(orig_len > 0); strcat(url + strlen(url), uri); ESP_LOGI(TAG, "HTTP request on %s", url); char *root_cat = calloc(1, ROOT_CA_MAX_BYTES); assert(root_cat); assert(storage_get_root_ca(root_cat) > 0); http_request_opts opts = { .url = url, .root_ca = root_cat, .body = body, .method = body == NULL ? MethodGET : MethodPOST, .content_type = body == NULL ? NULL : "application/json"}; char *res = http_client_exec(&opts); free(url); free(root_cat); return res; } static char *dev_escaped_name() { unsigned char *name = (unsigned char *)dev_name(); assert(name); size_t escaped_name_len = http_client_escape_uri(NULL, name, strlen((char *)name)); unsigned char *escaped_name = calloc(1, escaped_name_len + 1); assert(escaped_name); http_client_escape_uri(escaped_name, name, strlen((char *)name)); free(name); return (char *)escaped_name; } enum DevEnrollmentStatus secure_api_get_device_enrollment_status() { ESP_LOGI(TAG, "Will check device enrollment status"); // Prepare URI char *escaped_name = dev_escaped_name(); char *uri = calloc(1, 255); assert(uri); sprintf(uri, "/devices_api/mgmt/enrollment_status?id=%s", escaped_name); free(escaped_name); char *res = process_secure_request(uri, NULL); free(uri); if (res == NULL) { ESP_LOGE(TAG, "Failed to query device enrollment status!"); return DevEnrollError; } enum DevEnrollmentStatus s = DevEnrollError; cJSON *root = cJSON_Parse(res); if (root == NULL) { ESP_LOGE(TAG, "Failed to decode JSON response from server!"); goto fail; } cJSON *status = cJSON_GetObjectItem(root, "status"); if (status == NULL) { ESP_LOGE(TAG, "Status missing in response from server!"); goto fail; } if (!strcmp(status->valuestring, "Unknown")) s = DevEnrollUnknown; else if (!strcmp(status->valuestring, "Pending")) s = DevEnrollPending; else if (!strcmp(status->valuestring, "Validated")) s = DevEnrollValidated; else { ESP_LOGE(TAG, "Unknown enrollment status: %s", status->valuestring); goto fail; } fail: cJSON_Delete(root); free(res); return s; } /** * Generate device information. Pointer to be released by caller */ static cJSON *genDevInfo() { cJSON *json = cJSON_CreateObject(); if (!json) return NULL; cJSON_AddStringToObject(json, "reference", DEV_REFERENCE); cJSON_AddStringToObject(json, "version", DEV_VERSION); cJSON_AddNumberToObject(json, "max_relays", DEV_MAX_RELAYS); return json; } int secure_api_enroll_device() { char *csr = crypto_get_csr(); if (!csr) { ESP_LOGE(TAG, "Failed to get CSR!"); return 1; } cJSON *obj = cJSON_CreateObject(); if (!obj) { ESP_LOGE(TAG, "Failed allocate memory to store JSON object!"); free(csr); return 1; } cJSON_AddItemToObject(obj, "info", genDevInfo()); cJSON_AddStringToObject(obj, "csr", csr); free(csr); char *body = cJSON_PrintUnformatted(obj); if (!body) { ESP_LOGE(TAG, "Failed to generate JSON body!"); cJSON_Delete(obj); return 1; } char *res = process_secure_request("/devices_api/mgmt/enroll", body); cJSON_Delete(obj); free(body); if (res == NULL) { ESP_LOGE(TAG, "Request failed!"); return 1; } free(res); return 0; } char *secure_api_get_dev_certificate() { ESP_LOGI(TAG, "Will request device certificate"); // Prepare URI char *escaped_name = dev_escaped_name(); char *uri = calloc(1, 255); assert(uri); sprintf(uri, "/devices_api/mgmt/get_certificate?id=%s", escaped_name); free(escaped_name); char *res = process_secure_request(uri, NULL); free(uri); if (res == NULL) { ESP_LOGE(TAG, "Failed to query device certificate!"); return NULL; } return res; }