343 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			343 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| #include <stdlib.h>
 | |
| #include <string.h>
 | |
| #include <stdio.h>
 | |
| 
 | |
| #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 "jwt.h"
 | |
| #include "relays.h"
 | |
| 
 | |
| #include "esp_log.h"
 | |
| #include "esp_app_desc.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()
 | |
| {
 | |
|     const esp_app_desc_t *desc = esp_app_get_description();
 | |
| 
 | |
|     cJSON *json = cJSON_CreateObject();
 | |
|     if (!json)
 | |
|         return NULL;
 | |
|     cJSON_AddStringToObject(json, "reference", DEV_REFERENCE);
 | |
|     cJSON_AddStringToObject(json, "version", desc->version);
 | |
|     cJSON_AddNumberToObject(json, "max_relays", relays_count());
 | |
|     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);
 | |
|     cJSON_Delete(obj);
 | |
| 
 | |
|     if (!body)
 | |
|     {
 | |
|         ESP_LOGE(TAG, "Failed to generate JSON body!");
 | |
|         return 1;
 | |
|     }
 | |
| 
 | |
|     char *res = process_secure_request("/devices_api/mgmt/enroll", body);
 | |
| 
 | |
|     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;
 | |
| }
 | |
| 
 | |
| void secure_api_report_log_message(enum LogMessageSeverity severity, const char *msg)
 | |
| {
 | |
|     // Prepare signed payload
 | |
|     cJSON *obj = cJSON_CreateObject();
 | |
|     if (!obj)
 | |
|     {
 | |
|         ESP_LOGE(TAG, "Failed allocate memory to store JSON object!");
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     char *severity_s;
 | |
|     switch (severity)
 | |
|     {
 | |
|     case Info:
 | |
|         severity_s = "Info";
 | |
|         break;
 | |
|     case Warn:
 | |
|         severity_s = "Warn";
 | |
|         break;
 | |
|     case Error:
 | |
|         severity_s = "Error";
 | |
|         break;
 | |
|     default:
 | |
|         severity_s = "Debug";
 | |
|         break;
 | |
|     }
 | |
| 
 | |
|     cJSON_AddStringToObject(obj, "severity", severity_s);
 | |
|     cJSON_AddStringToObject(obj, "message", msg);
 | |
|     char *payload = jwt_gen(obj);
 | |
|     cJSON_Delete(obj);
 | |
| 
 | |
|     if (!payload)
 | |
|     {
 | |
|         ESP_LOGE(TAG, "Failed to build log report request!");
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     // Prepare request body
 | |
|     cJSON *json_body = cJSON_CreateObject();
 | |
|     if (!json_body)
 | |
|     {
 | |
|         ESP_LOGE(TAG, "Failed to allocated memory to store log report request body!");
 | |
|         free(payload);
 | |
|         return;
 | |
|     }
 | |
|     cJSON_AddStringToObject(json_body, "payload", payload);
 | |
|     free(payload);
 | |
| 
 | |
|     char *body = cJSON_PrintUnformatted(json_body);
 | |
|     cJSON_Delete(json_body);
 | |
| 
 | |
|     if (!body)
 | |
|     {
 | |
|         ESP_LOGE(TAG, "Failed to allocated memory to store encoded log report request body!");
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     // Send request
 | |
|     char *res = process_secure_request("/devices_api/logging/record", body);
 | |
| 
 | |
|     free(body);
 | |
| 
 | |
|     if (!res)
 | |
|     {
 | |
|         ESP_LOGE(TAG, "Log reporting failed!");
 | |
|     }
 | |
| 
 | |
|     free(res);
 | |
| }
 | |
| 
 | |
| sync_response *secure_api_sync_device()
 | |
| {
 | |
|     cJSON *obj = cJSON_CreateObject();
 | |
|     if (!obj)
 | |
|     {
 | |
|         ESP_LOGE(TAG, "Failed allocate memory to store JSON object!");
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     cJSON_AddItemToObject(obj, "info", genDevInfo());
 | |
| 
 | |
|     char *encoded_req = jwt_gen(obj);
 | |
|     cJSON_Delete(obj);
 | |
| 
 | |
|     if (!encoded_req)
 | |
|     {
 | |
|         ESP_LOGE(TAG, "Failed to encode JWT!");
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     // Prepare request body
 | |
|     cJSON *json_body = cJSON_CreateObject();
 | |
|     if (!json_body)
 | |
|     {
 | |
|         ESP_LOGE(TAG, "Failed to allocated memory to store sync request body!");
 | |
|         free(encoded_req);
 | |
|         return NULL;
 | |
|     }
 | |
|     cJSON_AddStringToObject(json_body, "payload", encoded_req);
 | |
|     free(encoded_req);
 | |
| 
 | |
|     char *body = cJSON_PrintUnformatted(json_body);
 | |
|     cJSON_Delete(json_body);
 | |
| 
 | |
|     if (!body)
 | |
|     {
 | |
|         ESP_LOGE(TAG, "Failed to allocated memory to store encoded sync request body!");
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     // Send request
 | |
|     char *res = process_secure_request("/devices_api/mgmt/sync", body);
 | |
|     free(body);
 | |
|     if (res == NULL)
 | |
|     {
 | |
|         ESP_LOGE(TAG, "Sync request failed!");
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     // Parse response
 | |
|     cJSON *states = cJSON_Parse(res);
 | |
|     free(res);
 | |
| 
 | |
|     if (!states)
 | |
|     {
 | |
|         ESP_LOGE(TAG, "Failed to decode sync response from server!");
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     sync_response *sync_res = sync_response_parse(states);
 | |
| 
 | |
|     cJSON_Delete(states);
 | |
| 
 | |
|     if (!sync_res)
 | |
|     {
 | |
|         ESP_LOGE(TAG, "Failed to parse sync response from server!");
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     return sync_res;
 | |
| } |