267 lines
6.0 KiB
C
267 lines
6.0 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 "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);
|
|
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;
|
|
}
|
|
|
|
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);
|
|
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;
|
|
} |