From e651ec16c547400cb8d3cfacdc8a05940369483c Mon Sep 17 00:00:00 2001 From: Tomas Rezucha Date: Thu, 17 Feb 2022 14:56:45 +0100 Subject: [PATCH] Format code --- esp_encrypted_img/include/esp_encrypted_img.h | 3 +- esp_encrypted_img/src/esp_encrypted_img.c | 895 +++++++++--------- expat/test/test_expat.c | 238 ++--- jsmn/include/jsmn.h | 212 ++--- jsmn/src/jsmn.c | 696 +++++++------- libsodium/test/test_sodium.c | 250 ++--- nghttp/port/private_include/config.h | 58 +- sh2lib/sh2lib.c | 740 +++++++-------- 8 files changed, 1555 insertions(+), 1537 deletions(-) diff --git a/esp_encrypted_img/include/esp_encrypted_img.h b/esp_encrypted_img/include/esp_encrypted_img.h index 6f43327..49418ba 100644 --- a/esp_encrypted_img/include/esp_encrypted_img.h +++ b/esp_encrypted_img/include/esp_encrypted_img.h @@ -9,6 +9,7 @@ #if 0 //High level layout for state machine +// *INDENT-OFF* @startuml [*] --> READ_MAGIC READ_MAGIC --> READ_MAGIC : READ LEN < 4 @@ -34,7 +35,7 @@ PROCESS_BINARY --> ESP_OK : READ LEN = BIN_SIZE ESP_OK --> [*] ESP_FAIL --> [*] @enduml - +// *INDENT-OFF* #endif #ifdef __cplusplus diff --git a/esp_encrypted_img/src/esp_encrypted_img.c b/esp_encrypted_img/src/esp_encrypted_img.c index 956dbbb..180ccb5 100644 --- a/esp_encrypted_img/src/esp_encrypted_img.c +++ b/esp_encrypted_img/src/esp_encrypted_img.c @@ -1,448 +1,447 @@ -/* - * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include "esp_encrypted_img.h" -#include -#include -#include - -#include "mbedtls/pk.h" -#include "mbedtls/entropy.h" -#include "mbedtls/ctr_drbg.h" -#include "mbedtls/gcm.h" -#include "sys/param.h" - -static const char *TAG = "esp_encrypted_img"; - -typedef enum { - ESP_PRE_ENC_IMG_READ_MAGIC, - ESP_PRE_ENC_IMG_READ_GCM, - ESP_PRE_ENC_IMG_READ_IV, - ESP_PRE_ENC_IMG_READ_BINSIZE, - ESP_PRE_ENC_IMG_READ_AUTH, - ESP_PRE_ENC_IMG_READ_EXTRA_HEADER, - ESP_PRE_ENC_DATA_DECODE_STATE, -} esp_encrypted_img_state; - -struct esp_encrypted_img_handle { - const char *rsa_pem; - size_t rsa_len; - uint32_t binary_file_len; - uint32_t binary_file_read; - char *gcm_key; - char *iv; - char auth_tag[16]; - esp_encrypted_img_state state; - mbedtls_gcm_context gcm_ctx; - size_t cache_buf_len; - char *cache_buf; -}; - -#define GCM_KEY_SIZE 32 -#define MAGIC_SIZE 4 -#define ENC_GCM_KEY_SIZE 384 -#define IV_SIZE 16 -#define BIN_SIZE_DATA 4 -#define AUTH_SIZE 16 -#define RESERVED_HEADER 88 - -typedef struct { - char magic[MAGIC_SIZE]; - char enc_gcm[ENC_GCM_KEY_SIZE]; - char iv[IV_SIZE]; - char bin_size[BIN_SIZE_DATA]; - char auth[AUTH_SIZE]; - char extra_header[RESERVED_HEADER]; -} pre_enc_bin_header; -#define HEADER_DATA_SIZE sizeof(pre_enc_bin_header) - -// Magic Byte is created using command: echo -n "esp_encrypted_img" | sha256sum -static uint32_t esp_enc_img_magic = 0x0788b6cf; - -typedef struct esp_encrypted_img_handle esp_encrypted_img_t; - -static int decipher_gcm_key(char *enc_gcm, esp_encrypted_img_t *handle) -{ - int ret = 1; - handle->gcm_key = calloc(1, GCM_KEY_SIZE); - if (!handle->gcm_key) { - return ESP_ERR_NO_MEM; - } - size_t olen = 0; - mbedtls_pk_context pk; - mbedtls_entropy_context entropy; - mbedtls_ctr_drbg_context ctr_drbg; - const char *pers = "mbedtls_pk_encrypt"; - - mbedtls_ctr_drbg_init( &ctr_drbg ); - mbedtls_entropy_init( &entropy ); - mbedtls_pk_init( &pk ); - - if ((ret = mbedtls_ctr_drbg_seed( &ctr_drbg, mbedtls_entropy_func, - &entropy, (const unsigned char *) pers, - strlen(pers))) != 0) { - ESP_LOGE(TAG, "failed\n ! mbedtls_ctr_drbg_seed returned -0x%04x\n", (unsigned int) - ret); - free(handle->gcm_key); - goto exit; - } - - ESP_LOGI(TAG, "Reading RSA private key"); - - if ( (ret = mbedtls_pk_parse_key(&pk, (const unsigned char *) handle->rsa_pem, handle->rsa_len, NULL, 0)) != 0) { - ESP_LOGE(TAG, "failed\n ! mbedtls_pk_parse_keyfile returned -0x%04x\n", (unsigned int) - ret ); - free(handle->gcm_key); - goto exit; - } - - if (( ret = mbedtls_pk_decrypt( &pk, (const unsigned char *)enc_gcm, ENC_GCM_KEY_SIZE, (unsigned char *)handle->gcm_key, &olen, GCM_KEY_SIZE, - mbedtls_ctr_drbg_random, &ctr_drbg ) ) != 0 ) { - ESP_LOGE(TAG, "failed\n ! mbedtls_pk_decrypt returned -0x%04x\n", (unsigned int) - ret ); - free(handle->gcm_key); - goto exit; - } - handle->cache_buf = realloc(handle->cache_buf, 16); - if (!handle->cache_buf) { - return ESP_ERR_NO_MEM; - } - handle->state = ESP_PRE_ENC_IMG_READ_IV; - handle->binary_file_read = 0; - handle->cache_buf_len = 0; - handle->iv = calloc(1, IV_SIZE); - if (!handle->iv) { - return ESP_ERR_NO_MEM; - } - -exit: - mbedtls_pk_free( &pk ); - mbedtls_entropy_free( &entropy ); - mbedtls_ctr_drbg_free( &ctr_drbg ); - free((void *)handle->rsa_pem); - - return (ret); -} - -esp_decrypt_handle_t esp_encrypted_img_decrypt_start(const esp_decrypt_cfg_t *cfg) -{ - if (cfg == NULL || cfg->rsa_pub_key == NULL) { - ESP_LOGE(TAG, "esp_encrypted_img_decrypt_start : Invalid argument"); - return NULL; - } - ESP_LOGI(TAG, "Starting Decryption Process"); - - esp_encrypted_img_t *handle = calloc(1, sizeof(esp_encrypted_img_t)); - if (!handle) { - ESP_LOGE(TAG, "Couldn't allocate memory to handle"); - goto failure; - } - - handle->rsa_pem = calloc(1, cfg->rsa_pub_key_len); - if (!handle->rsa_pem) { - ESP_LOGE(TAG, "Couldn't allocate memory to handle->rsa_pem"); - goto failure; - } - - handle->cache_buf = calloc(1, ENC_GCM_KEY_SIZE); - if (!handle->cache_buf) { - ESP_LOGE(TAG, "Couldn't allocate memory to handle->cache_buf"); - goto failure; - } - - memcpy((void *)handle->rsa_pem, cfg->rsa_pub_key, cfg->rsa_pub_key_len); - handle->rsa_len = cfg->rsa_pub_key_len; - handle->state = ESP_PRE_ENC_IMG_READ_MAGIC; - - esp_decrypt_handle_t *ctx = (esp_decrypt_handle_t *)handle; - return ctx; - -failure: - if (!handle) { - return NULL; - } - if (handle->rsa_pem) { - free((void *)handle->rsa_pem); - } - if (handle) { - free(handle); - } - return NULL; -} - -static esp_err_t process_bin(esp_encrypted_img_t *handle, pre_enc_decrypt_arg_t *args, int curr_index) -{ - size_t data_len = args->data_in_len; - - handle->binary_file_read += data_len - curr_index; - int dec_len = 0; - if (handle->binary_file_read != handle->binary_file_len) { - size_t copy_len = 0; - - if ((handle->cache_buf_len + (data_len - curr_index)) - (handle->cache_buf_len + (data_len - curr_index)) % 16 > 0) { - args->data_out = realloc(args->data_out, (handle->cache_buf_len + (data_len - curr_index)) - (handle->cache_buf_len + (data_len - curr_index)) % 16); - if (!args->data_out) { - return ESP_ERR_NO_MEM; - } - } - if (handle->cache_buf_len != 0) { - copy_len = MIN(16 - handle->cache_buf_len, data_len - curr_index); - memcpy(handle->cache_buf + handle->cache_buf_len, args->data_in + curr_index, copy_len); - handle->cache_buf_len += copy_len; - if (handle->cache_buf_len != 16) { - args->data_out_len = 0; - return ESP_ERR_NOT_FINISHED; - } - if (mbedtls_gcm_update(&handle->gcm_ctx, 16, (const unsigned char *)handle->cache_buf, (unsigned char *) args->data_out) != 0) { - return ESP_FAIL; - } - dec_len = 16; - } - handle->cache_buf_len = (data_len - curr_index - copy_len) % 16; - if (handle->cache_buf_len != 0) { - data_len -= handle->cache_buf_len; - memcpy(handle->cache_buf, args->data_in + (data_len), handle->cache_buf_len); - } - - if (data_len - copy_len - curr_index > 0) { - if (mbedtls_gcm_update(&handle->gcm_ctx, data_len - copy_len - curr_index, (const unsigned char *)args->data_in + curr_index + copy_len, (unsigned char *)args->data_out + dec_len) != 0) { - return ESP_FAIL; - } - } - args->data_out_len = dec_len + data_len - curr_index - copy_len; - return ESP_ERR_NOT_FINISHED; - } - - args->data_out = realloc(args->data_out, handle->cache_buf_len + data_len - curr_index); - if (!args->data_out) { - return ESP_ERR_NO_MEM; - } - size_t copy_len = 0; - - copy_len = MIN(16 - handle->cache_buf_len, data_len - curr_index); - memcpy(handle->cache_buf + handle->cache_buf_len, args->data_in + curr_index, copy_len); - handle->cache_buf_len += copy_len; - if (mbedtls_gcm_update(&handle->gcm_ctx, handle->cache_buf_len, (const unsigned char *)handle->cache_buf, (unsigned char *)args->data_out) != 0) { - return ESP_FAIL; - } - if (data_len - curr_index - copy_len > 0) { - if (mbedtls_gcm_update(&handle->gcm_ctx, data_len - curr_index - copy_len, (const unsigned char *)(args->data_in + curr_index + copy_len), (unsigned char *)(args->data_out + 16)) != 0) { - return ESP_FAIL; - } - } - - args->data_out_len = handle->cache_buf_len + data_len - copy_len - curr_index; - handle->cache_buf_len = 0; - - return ESP_OK; -} - -static void read_and_cache_data(esp_encrypted_img_t *handle, pre_enc_decrypt_arg_t *args, int *curr_index, int data_size) -{ - const int data_left = data_size - handle->binary_file_read; - const int data_recv = args->data_in_len - *curr_index; - if (handle->state == ESP_PRE_ENC_IMG_READ_IV && handle->iv) { - memcpy(handle->iv + handle->cache_buf_len, args->data_in + *curr_index, MIN(data_recv, data_left)); - } else if (handle->state == ESP_PRE_ENC_IMG_READ_AUTH) { - memcpy(handle->auth_tag + handle->cache_buf_len, args->data_in + *curr_index, MIN(data_recv, data_left)); - } else { - memcpy(handle->cache_buf + handle->cache_buf_len, args->data_in + *curr_index, MIN(data_recv, data_left)); - } - handle->cache_buf_len += MIN(data_recv, data_left); - int temp = *curr_index; - *curr_index += MIN(data_recv, data_left); - handle->binary_file_read += MIN(args->data_in_len - temp, data_left); -} - -esp_err_t esp_encrypted_img_decrypt_data(esp_decrypt_handle_t *ctx, pre_enc_decrypt_arg_t *args) -{ - if (ctx == NULL || args == NULL || args->data_in == NULL) { - return ESP_ERR_INVALID_ARG; - } - esp_encrypted_img_t *handle = (esp_encrypted_img_t *)ctx; - if (handle == NULL) { - ESP_LOGE(TAG, "esp_encrypted_img_decrypt_data: Invalid argument"); - return ESP_ERR_INVALID_ARG; - } - - esp_err_t err; - int curr_index = 0; - - switch (handle->state) { - case ESP_PRE_ENC_IMG_READ_MAGIC: - if (handle->cache_buf_len == 0 && (args->data_in_len - curr_index) >= MAGIC_SIZE) { - uint32_t recv_magic = *(uint32_t *)args->data_in; - - if (recv_magic != esp_enc_img_magic) { - ESP_LOGE(TAG, "Magic Verification failed"); - free((void *)handle->rsa_pem); - return ESP_FAIL; - } - curr_index += MAGIC_SIZE; - } else { - read_and_cache_data(handle, args, &curr_index, MAGIC_SIZE); - if (handle->binary_file_read == MAGIC_SIZE) { - uint32_t recv_magic = *(uint32_t *)handle->cache_buf; - - if (recv_magic != esp_enc_img_magic) { - ESP_LOGE(TAG, "Magic Verification failed"); - free((void *)handle->rsa_pem); - return ESP_FAIL; - } - handle->binary_file_read = 0; - handle->cache_buf_len = 0; - } else { - return ESP_ERR_NOT_FINISHED; - } - } - ESP_LOGI(TAG, "Magic Verified"); - handle->state = ESP_PRE_ENC_IMG_READ_GCM; - /* falls through */ - case ESP_PRE_ENC_IMG_READ_GCM: - if (handle->cache_buf_len == 0 && args->data_in_len - curr_index >= ENC_GCM_KEY_SIZE) { - if (decipher_gcm_key(args->data_in + curr_index, handle) != 0) { - ESP_LOGE(TAG, "Unable to decipher GCM key"); - return ESP_FAIL; - } - curr_index += ENC_GCM_KEY_SIZE; - } else { - read_and_cache_data(handle, args, &curr_index, ENC_GCM_KEY_SIZE); - if (handle->cache_buf_len == ENC_GCM_KEY_SIZE) { - if (decipher_gcm_key(handle->cache_buf, handle) != 0) { - ESP_LOGE(TAG, "Unable to decipher GCM key"); - return ESP_FAIL; - } - } else { - return ESP_ERR_NOT_FINISHED; - } - } - /* falls through */ - case ESP_PRE_ENC_IMG_READ_IV: - if (handle->cache_buf_len == 0 && args->data_in_len - curr_index >= IV_SIZE) { - memcpy(handle->iv, args->data_in + curr_index, IV_SIZE); - handle->binary_file_read = IV_SIZE; - curr_index += IV_SIZE; - } else { - read_and_cache_data(handle, args, &curr_index, IV_SIZE); - } - if (handle->binary_file_read == IV_SIZE) { - handle->state = ESP_PRE_ENC_IMG_READ_BINSIZE; - handle->binary_file_read = 0; - handle->cache_buf_len = 0; - mbedtls_gcm_init(&handle->gcm_ctx); - if ((err = mbedtls_gcm_setkey(&handle->gcm_ctx, MBEDTLS_CIPHER_ID_AES, (const unsigned char *)handle->gcm_key, GCM_KEY_SIZE * 8)) != 0) { - ESP_LOGE(TAG, "Error: mbedtls_gcm_set_key: -0x%04x\n", (unsigned int) - err); - return ESP_FAIL; - } - free(handle->gcm_key); - if (mbedtls_gcm_starts(&handle->gcm_ctx, MBEDTLS_GCM_DECRYPT, (const unsigned char *)handle->iv, IV_SIZE, NULL, 0) != 0) { - ESP_LOGE(TAG, "Error: mbedtls_gcm_starts: -0x%04x\n", (unsigned int) - err); - return ESP_FAIL; - } - free(handle->iv); - handle->iv = NULL; - } else { - return ESP_ERR_NOT_FINISHED; - } - /* falls through */ - case ESP_PRE_ENC_IMG_READ_BINSIZE: - if (handle->cache_buf_len == 0 && (args->data_in_len - curr_index) >= BIN_SIZE_DATA) { - handle->binary_file_len = *(uint32_t *)(args->data_in + curr_index); - curr_index += BIN_SIZE_DATA; - } else { - read_and_cache_data(handle, args, &curr_index, BIN_SIZE_DATA); - if (handle->binary_file_read == BIN_SIZE_DATA) { - handle->binary_file_len = *(uint32_t *)handle->cache_buf; - } else { - return ESP_ERR_NOT_FINISHED; - } - } - handle->state = ESP_PRE_ENC_IMG_READ_AUTH; - handle->binary_file_read = 0; - handle->cache_buf_len = 0; - /* falls through */ - case ESP_PRE_ENC_IMG_READ_AUTH: - if (handle->cache_buf_len == 0 && args->data_in_len - curr_index >= AUTH_SIZE) { - memcpy(handle->auth_tag, args->data_in + curr_index, AUTH_SIZE); - handle->binary_file_read = AUTH_SIZE; - curr_index += AUTH_SIZE; - } else { - read_and_cache_data(handle, args, &curr_index, AUTH_SIZE); - } - if (handle->binary_file_read == AUTH_SIZE) { - handle->state = ESP_PRE_ENC_IMG_READ_EXTRA_HEADER; - handle->binary_file_read = 0; - handle->cache_buf_len = 0; - } else { - return ESP_ERR_NOT_FINISHED; - } - /* falls through */ - case ESP_PRE_ENC_IMG_READ_EXTRA_HEADER: - { - int temp = curr_index; - curr_index += MIN(args->data_in_len - curr_index, RESERVED_HEADER - handle->binary_file_read); - handle->binary_file_read += MIN(args->data_in_len - temp, RESERVED_HEADER - handle->binary_file_read); - if (handle->binary_file_read == RESERVED_HEADER) { - handle->state = ESP_PRE_ENC_DATA_DECODE_STATE; - handle->binary_file_read = 0; - handle->cache_buf_len = 0; - } else { - return ESP_ERR_NOT_FINISHED; - } - } - /* falls through */ - case ESP_PRE_ENC_DATA_DECODE_STATE: - err = process_bin(handle, args, curr_index); - return err; - } - return ESP_OK; -} - -esp_err_t esp_encrypted_img_decrypt_end(esp_decrypt_handle_t *ctx) -{ - if (ctx == NULL) { - return ESP_ERR_INVALID_ARG; - } - esp_encrypted_img_t *handle = (esp_encrypted_img_t *)ctx; - esp_err_t err = ESP_OK; - if (handle == NULL) { - ESP_LOGE(TAG, "esp_encrypted_img_decrypt_data: Invalid argument"); - return ESP_ERR_INVALID_ARG; - } - if (handle->state == ESP_PRE_ENC_DATA_DECODE_STATE) { - if (handle->cache_buf_len != 0 || handle->binary_file_read != handle->binary_file_len) { - ESP_LOGE(TAG, "Invalid operation"); - err = ESP_FAIL; - goto exit; - } - - char *got_auth = calloc(1, AUTH_SIZE); - if (!got_auth) { - ESP_LOGE(TAG, "Unable to allocate memory"); - err = ESP_FAIL; - goto exit; - } - err = mbedtls_gcm_finish(&handle->gcm_ctx, (unsigned char *)got_auth, AUTH_SIZE); - if (err != 0) { - ESP_LOGE(TAG, "Error: %d", err); - free(got_auth); - err = ESP_FAIL; - goto exit; - } - if (memcmp(got_auth, handle->auth_tag, AUTH_SIZE) != 0) { - ESP_LOGE(TAG, "Invalid Auth"); - free(got_auth); - err = ESP_FAIL; - goto exit; - } - - free(got_auth); - } - err = ESP_OK; -exit: - mbedtls_gcm_free(&handle->gcm_ctx); - free(handle->cache_buf); - free(handle); - return err; -} +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "esp_encrypted_img.h" +#include +#include +#include + +#include "mbedtls/pk.h" +#include "mbedtls/entropy.h" +#include "mbedtls/ctr_drbg.h" +#include "mbedtls/gcm.h" +#include "sys/param.h" + +static const char *TAG = "esp_encrypted_img"; + +typedef enum { + ESP_PRE_ENC_IMG_READ_MAGIC, + ESP_PRE_ENC_IMG_READ_GCM, + ESP_PRE_ENC_IMG_READ_IV, + ESP_PRE_ENC_IMG_READ_BINSIZE, + ESP_PRE_ENC_IMG_READ_AUTH, + ESP_PRE_ENC_IMG_READ_EXTRA_HEADER, + ESP_PRE_ENC_DATA_DECODE_STATE, +} esp_encrypted_img_state; + +struct esp_encrypted_img_handle { + const char *rsa_pem; + size_t rsa_len; + uint32_t binary_file_len; + uint32_t binary_file_read; + char *gcm_key; + char *iv; + char auth_tag[16]; + esp_encrypted_img_state state; + mbedtls_gcm_context gcm_ctx; + size_t cache_buf_len; + char *cache_buf; +}; + +#define GCM_KEY_SIZE 32 +#define MAGIC_SIZE 4 +#define ENC_GCM_KEY_SIZE 384 +#define IV_SIZE 16 +#define BIN_SIZE_DATA 4 +#define AUTH_SIZE 16 +#define RESERVED_HEADER 88 + +typedef struct { + char magic[MAGIC_SIZE]; + char enc_gcm[ENC_GCM_KEY_SIZE]; + char iv[IV_SIZE]; + char bin_size[BIN_SIZE_DATA]; + char auth[AUTH_SIZE]; + char extra_header[RESERVED_HEADER]; +} pre_enc_bin_header; +#define HEADER_DATA_SIZE sizeof(pre_enc_bin_header) + +// Magic Byte is created using command: echo -n "esp_encrypted_img" | sha256sum +static uint32_t esp_enc_img_magic = 0x0788b6cf; + +typedef struct esp_encrypted_img_handle esp_encrypted_img_t; + +static int decipher_gcm_key(char *enc_gcm, esp_encrypted_img_t *handle) +{ + int ret = 1; + handle->gcm_key = calloc(1, GCM_KEY_SIZE); + if (!handle->gcm_key) { + return ESP_ERR_NO_MEM; + } + size_t olen = 0; + mbedtls_pk_context pk; + mbedtls_entropy_context entropy; + mbedtls_ctr_drbg_context ctr_drbg; + const char *pers = "mbedtls_pk_encrypt"; + + mbedtls_ctr_drbg_init( &ctr_drbg ); + mbedtls_entropy_init( &entropy ); + mbedtls_pk_init( &pk ); + + if ((ret = mbedtls_ctr_drbg_seed( &ctr_drbg, mbedtls_entropy_func, + &entropy, (const unsigned char *) pers, + strlen(pers))) != 0) { + ESP_LOGE(TAG, "failed\n ! mbedtls_ctr_drbg_seed returned -0x%04x\n", (unsigned int) - ret); + free(handle->gcm_key); + goto exit; + } + + ESP_LOGI(TAG, "Reading RSA private key"); + + if ( (ret = mbedtls_pk_parse_key(&pk, (const unsigned char *) handle->rsa_pem, handle->rsa_len, NULL, 0)) != 0) { + ESP_LOGE(TAG, "failed\n ! mbedtls_pk_parse_keyfile returned -0x%04x\n", (unsigned int) - ret ); + free(handle->gcm_key); + goto exit; + } + + if (( ret = mbedtls_pk_decrypt( &pk, (const unsigned char *)enc_gcm, ENC_GCM_KEY_SIZE, (unsigned char *)handle->gcm_key, &olen, GCM_KEY_SIZE, + mbedtls_ctr_drbg_random, &ctr_drbg ) ) != 0 ) { + ESP_LOGE(TAG, "failed\n ! mbedtls_pk_decrypt returned -0x%04x\n", (unsigned int) - ret ); + free(handle->gcm_key); + goto exit; + } + handle->cache_buf = realloc(handle->cache_buf, 16); + if (!handle->cache_buf) { + return ESP_ERR_NO_MEM; + } + handle->state = ESP_PRE_ENC_IMG_READ_IV; + handle->binary_file_read = 0; + handle->cache_buf_len = 0; + handle->iv = calloc(1, IV_SIZE); + if (!handle->iv) { + return ESP_ERR_NO_MEM; + } + +exit: + mbedtls_pk_free( &pk ); + mbedtls_entropy_free( &entropy ); + mbedtls_ctr_drbg_free( &ctr_drbg ); + free((void *)handle->rsa_pem); + + return (ret); +} + +esp_decrypt_handle_t esp_encrypted_img_decrypt_start(const esp_decrypt_cfg_t *cfg) +{ + if (cfg == NULL || cfg->rsa_pub_key == NULL) { + ESP_LOGE(TAG, "esp_encrypted_img_decrypt_start : Invalid argument"); + return NULL; + } + ESP_LOGI(TAG, "Starting Decryption Process"); + + esp_encrypted_img_t *handle = calloc(1, sizeof(esp_encrypted_img_t)); + if (!handle) { + ESP_LOGE(TAG, "Couldn't allocate memory to handle"); + goto failure; + } + + handle->rsa_pem = calloc(1, cfg->rsa_pub_key_len); + if (!handle->rsa_pem) { + ESP_LOGE(TAG, "Couldn't allocate memory to handle->rsa_pem"); + goto failure; + } + + handle->cache_buf = calloc(1, ENC_GCM_KEY_SIZE); + if (!handle->cache_buf) { + ESP_LOGE(TAG, "Couldn't allocate memory to handle->cache_buf"); + goto failure; + } + + memcpy((void *)handle->rsa_pem, cfg->rsa_pub_key, cfg->rsa_pub_key_len); + handle->rsa_len = cfg->rsa_pub_key_len; + handle->state = ESP_PRE_ENC_IMG_READ_MAGIC; + + esp_decrypt_handle_t *ctx = (esp_decrypt_handle_t *)handle; + return ctx; + +failure: + if (!handle) { + return NULL; + } + if (handle->rsa_pem) { + free((void *)handle->rsa_pem); + } + if (handle) { + free(handle); + } + return NULL; +} + +static esp_err_t process_bin(esp_encrypted_img_t *handle, pre_enc_decrypt_arg_t *args, int curr_index) +{ + size_t data_len = args->data_in_len; + + handle->binary_file_read += data_len - curr_index; + int dec_len = 0; + if (handle->binary_file_read != handle->binary_file_len) { + size_t copy_len = 0; + + if ((handle->cache_buf_len + (data_len - curr_index)) - (handle->cache_buf_len + (data_len - curr_index)) % 16 > 0) { + args->data_out = realloc(args->data_out, (handle->cache_buf_len + (data_len - curr_index)) - (handle->cache_buf_len + (data_len - curr_index)) % 16); + if (!args->data_out) { + return ESP_ERR_NO_MEM; + } + } + if (handle->cache_buf_len != 0) { + copy_len = MIN(16 - handle->cache_buf_len, data_len - curr_index); + memcpy(handle->cache_buf + handle->cache_buf_len, args->data_in + curr_index, copy_len); + handle->cache_buf_len += copy_len; + if (handle->cache_buf_len != 16) { + args->data_out_len = 0; + return ESP_ERR_NOT_FINISHED; + } + if (mbedtls_gcm_update(&handle->gcm_ctx, 16, (const unsigned char *)handle->cache_buf, (unsigned char *) args->data_out) != 0) { + return ESP_FAIL; + } + dec_len = 16; + } + handle->cache_buf_len = (data_len - curr_index - copy_len) % 16; + if (handle->cache_buf_len != 0) { + data_len -= handle->cache_buf_len; + memcpy(handle->cache_buf, args->data_in + (data_len), handle->cache_buf_len); + } + + if (data_len - copy_len - curr_index > 0) { + if (mbedtls_gcm_update(&handle->gcm_ctx, data_len - copy_len - curr_index, (const unsigned char *)args->data_in + curr_index + copy_len, (unsigned char *)args->data_out + dec_len) != 0) { + return ESP_FAIL; + } + } + args->data_out_len = dec_len + data_len - curr_index - copy_len; + return ESP_ERR_NOT_FINISHED; + } + + args->data_out = realloc(args->data_out, handle->cache_buf_len + data_len - curr_index); + if (!args->data_out) { + return ESP_ERR_NO_MEM; + } + size_t copy_len = 0; + + copy_len = MIN(16 - handle->cache_buf_len, data_len - curr_index); + memcpy(handle->cache_buf + handle->cache_buf_len, args->data_in + curr_index, copy_len); + handle->cache_buf_len += copy_len; + if (mbedtls_gcm_update(&handle->gcm_ctx, handle->cache_buf_len, (const unsigned char *)handle->cache_buf, (unsigned char *)args->data_out) != 0) { + return ESP_FAIL; + } + if (data_len - curr_index - copy_len > 0) { + if (mbedtls_gcm_update(&handle->gcm_ctx, data_len - curr_index - copy_len, (const unsigned char *)(args->data_in + curr_index + copy_len), (unsigned char *)(args->data_out + 16)) != 0) { + return ESP_FAIL; + } + } + + args->data_out_len = handle->cache_buf_len + data_len - copy_len - curr_index; + handle->cache_buf_len = 0; + + return ESP_OK; +} + +static void read_and_cache_data(esp_encrypted_img_t *handle, pre_enc_decrypt_arg_t *args, int *curr_index, int data_size) +{ + const int data_left = data_size - handle->binary_file_read; + const int data_recv = args->data_in_len - *curr_index; + if (handle->state == ESP_PRE_ENC_IMG_READ_IV && handle->iv) { + memcpy(handle->iv + handle->cache_buf_len, args->data_in + *curr_index, MIN(data_recv, data_left)); + } else if (handle->state == ESP_PRE_ENC_IMG_READ_AUTH) { + memcpy(handle->auth_tag + handle->cache_buf_len, args->data_in + *curr_index, MIN(data_recv, data_left)); + } else { + memcpy(handle->cache_buf + handle->cache_buf_len, args->data_in + *curr_index, MIN(data_recv, data_left)); + } + handle->cache_buf_len += MIN(data_recv, data_left); + int temp = *curr_index; + *curr_index += MIN(data_recv, data_left); + handle->binary_file_read += MIN(args->data_in_len - temp, data_left); +} + +esp_err_t esp_encrypted_img_decrypt_data(esp_decrypt_handle_t *ctx, pre_enc_decrypt_arg_t *args) +{ + if (ctx == NULL || args == NULL || args->data_in == NULL) { + return ESP_ERR_INVALID_ARG; + } + esp_encrypted_img_t *handle = (esp_encrypted_img_t *)ctx; + if (handle == NULL) { + ESP_LOGE(TAG, "esp_encrypted_img_decrypt_data: Invalid argument"); + return ESP_ERR_INVALID_ARG; + } + + esp_err_t err; + int curr_index = 0; + + switch (handle->state) { + case ESP_PRE_ENC_IMG_READ_MAGIC: + if (handle->cache_buf_len == 0 && (args->data_in_len - curr_index) >= MAGIC_SIZE) { + uint32_t recv_magic = *(uint32_t *)args->data_in; + + if (recv_magic != esp_enc_img_magic) { + ESP_LOGE(TAG, "Magic Verification failed"); + free((void *)handle->rsa_pem); + return ESP_FAIL; + } + curr_index += MAGIC_SIZE; + } else { + read_and_cache_data(handle, args, &curr_index, MAGIC_SIZE); + if (handle->binary_file_read == MAGIC_SIZE) { + uint32_t recv_magic = *(uint32_t *)handle->cache_buf; + + if (recv_magic != esp_enc_img_magic) { + ESP_LOGE(TAG, "Magic Verification failed"); + free((void *)handle->rsa_pem); + return ESP_FAIL; + } + handle->binary_file_read = 0; + handle->cache_buf_len = 0; + } else { + return ESP_ERR_NOT_FINISHED; + } + } + ESP_LOGI(TAG, "Magic Verified"); + handle->state = ESP_PRE_ENC_IMG_READ_GCM; + /* falls through */ + case ESP_PRE_ENC_IMG_READ_GCM: + if (handle->cache_buf_len == 0 && args->data_in_len - curr_index >= ENC_GCM_KEY_SIZE) { + if (decipher_gcm_key(args->data_in + curr_index, handle) != 0) { + ESP_LOGE(TAG, "Unable to decipher GCM key"); + return ESP_FAIL; + } + curr_index += ENC_GCM_KEY_SIZE; + } else { + read_and_cache_data(handle, args, &curr_index, ENC_GCM_KEY_SIZE); + if (handle->cache_buf_len == ENC_GCM_KEY_SIZE) { + if (decipher_gcm_key(handle->cache_buf, handle) != 0) { + ESP_LOGE(TAG, "Unable to decipher GCM key"); + return ESP_FAIL; + } + } else { + return ESP_ERR_NOT_FINISHED; + } + } + /* falls through */ + case ESP_PRE_ENC_IMG_READ_IV: + if (handle->cache_buf_len == 0 && args->data_in_len - curr_index >= IV_SIZE) { + memcpy(handle->iv, args->data_in + curr_index, IV_SIZE); + handle->binary_file_read = IV_SIZE; + curr_index += IV_SIZE; + } else { + read_and_cache_data(handle, args, &curr_index, IV_SIZE); + } + if (handle->binary_file_read == IV_SIZE) { + handle->state = ESP_PRE_ENC_IMG_READ_BINSIZE; + handle->binary_file_read = 0; + handle->cache_buf_len = 0; + mbedtls_gcm_init(&handle->gcm_ctx); + if ((err = mbedtls_gcm_setkey(&handle->gcm_ctx, MBEDTLS_CIPHER_ID_AES, (const unsigned char *)handle->gcm_key, GCM_KEY_SIZE * 8)) != 0) { + ESP_LOGE(TAG, "Error: mbedtls_gcm_set_key: -0x%04x\n", (unsigned int) - err); + return ESP_FAIL; + } + free(handle->gcm_key); + if (mbedtls_gcm_starts(&handle->gcm_ctx, MBEDTLS_GCM_DECRYPT, (const unsigned char *)handle->iv, IV_SIZE, NULL, 0) != 0) { + ESP_LOGE(TAG, "Error: mbedtls_gcm_starts: -0x%04x\n", (unsigned int) - err); + return ESP_FAIL; + } + free(handle->iv); + handle->iv = NULL; + } else { + return ESP_ERR_NOT_FINISHED; + } + /* falls through */ + case ESP_PRE_ENC_IMG_READ_BINSIZE: + if (handle->cache_buf_len == 0 && (args->data_in_len - curr_index) >= BIN_SIZE_DATA) { + handle->binary_file_len = *(uint32_t *)(args->data_in + curr_index); + curr_index += BIN_SIZE_DATA; + } else { + read_and_cache_data(handle, args, &curr_index, BIN_SIZE_DATA); + if (handle->binary_file_read == BIN_SIZE_DATA) { + handle->binary_file_len = *(uint32_t *)handle->cache_buf; + } else { + return ESP_ERR_NOT_FINISHED; + } + } + handle->state = ESP_PRE_ENC_IMG_READ_AUTH; + handle->binary_file_read = 0; + handle->cache_buf_len = 0; + /* falls through */ + case ESP_PRE_ENC_IMG_READ_AUTH: + if (handle->cache_buf_len == 0 && args->data_in_len - curr_index >= AUTH_SIZE) { + memcpy(handle->auth_tag, args->data_in + curr_index, AUTH_SIZE); + handle->binary_file_read = AUTH_SIZE; + curr_index += AUTH_SIZE; + } else { + read_and_cache_data(handle, args, &curr_index, AUTH_SIZE); + } + if (handle->binary_file_read == AUTH_SIZE) { + handle->state = ESP_PRE_ENC_IMG_READ_EXTRA_HEADER; + handle->binary_file_read = 0; + handle->cache_buf_len = 0; + } else { + return ESP_ERR_NOT_FINISHED; + } + /* falls through */ + case ESP_PRE_ENC_IMG_READ_EXTRA_HEADER: { + int temp = curr_index; + curr_index += MIN(args->data_in_len - curr_index, RESERVED_HEADER - handle->binary_file_read); + handle->binary_file_read += MIN(args->data_in_len - temp, RESERVED_HEADER - handle->binary_file_read); + if (handle->binary_file_read == RESERVED_HEADER) { + handle->state = ESP_PRE_ENC_DATA_DECODE_STATE; + handle->binary_file_read = 0; + handle->cache_buf_len = 0; + } else { + return ESP_ERR_NOT_FINISHED; + } + } + /* falls through */ + case ESP_PRE_ENC_DATA_DECODE_STATE: + err = process_bin(handle, args, curr_index); + return err; + } + return ESP_OK; +} + +esp_err_t esp_encrypted_img_decrypt_end(esp_decrypt_handle_t *ctx) +{ + if (ctx == NULL) { + return ESP_ERR_INVALID_ARG; + } + esp_encrypted_img_t *handle = (esp_encrypted_img_t *)ctx; + esp_err_t err = ESP_OK; + if (handle == NULL) { + ESP_LOGE(TAG, "esp_encrypted_img_decrypt_data: Invalid argument"); + return ESP_ERR_INVALID_ARG; + } + if (handle->state == ESP_PRE_ENC_DATA_DECODE_STATE) { + if (handle->cache_buf_len != 0 || handle->binary_file_read != handle->binary_file_len) { + ESP_LOGE(TAG, "Invalid operation"); + err = ESP_FAIL; + goto exit; + } + + char *got_auth = calloc(1, AUTH_SIZE); + if (!got_auth) { + ESP_LOGE(TAG, "Unable to allocate memory"); + err = ESP_FAIL; + goto exit; + } + err = mbedtls_gcm_finish(&handle->gcm_ctx, (unsigned char *)got_auth, AUTH_SIZE); + if (err != 0) { + ESP_LOGE(TAG, "Error: %d", err); + free(got_auth); + err = ESP_FAIL; + goto exit; + } + if (memcmp(got_auth, handle->auth_tag, AUTH_SIZE) != 0) { + ESP_LOGE(TAG, "Invalid Auth"); + free(got_auth); + err = ESP_FAIL; + goto exit; + } + + free(got_auth); + } + err = ESP_OK; +exit: + mbedtls_gcm_free(&handle->gcm_ctx); + free(handle->cache_buf); + free(handle); + return err; +} diff --git a/expat/test/test_expat.c b/expat/test/test_expat.c index e42fa5a..3734ebe 100644 --- a/expat/test/test_expat.c +++ b/expat/test/test_expat.c @@ -1,119 +1,119 @@ -/* - * SPDX-FileCopyrightText: 2018-2022 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include -#include "unity.h" - -typedef struct { - int depth; - char output[512]; - int output_off; -} user_data_t; - -static void insert_space(user_data_t *user_data) -{ - const char align_str[] = " "; - - TEST_ASSERT(sizeof(user_data->output) >= user_data->output_off); - user_data->output[user_data->output_off++] = '\n'; - - for (int i = 0; i < user_data->depth; i++) { - for (int j = 0; j < strlen(align_str); ++j) { - TEST_ASSERT(sizeof(user_data->output) >= user_data->output_off); - user_data->output[user_data->output_off++] = align_str[j]; - } - } -} - -static void XMLCALL start_element(void *userData, const XML_Char *name, const XML_Char **atts) -{ - user_data_t *user_data = (user_data_t *) userData; - - insert_space(user_data); - - const int ret = snprintf(user_data->output + user_data->output_off, - sizeof(user_data->output) - user_data->output_off, - "<%s>", name); - TEST_ASSERT_EQUAL(strlen(name) + 2, ret); // 2 are the tag characters: "<>" - user_data->output_off += ret; - ++user_data->depth; -} - -static void XMLCALL end_element(void *userData, const XML_Char *name) -{ - user_data_t *user_data = (user_data_t *) userData; - - --user_data->depth; - insert_space(user_data); - - int ret = snprintf(user_data->output + user_data->output_off, sizeof(user_data->output) - user_data->output_off, - "", name); - TEST_ASSERT_EQUAL(strlen(name) + 3, ret); // 3 are the tag characters: "" - user_data->output_off += ret; -} - -static void data_handler(void *userData, const XML_Char *s, int len) -{ - user_data_t *user_data = (user_data_t *) userData; - - insert_space(user_data); - - // s is not zero-terminated - char tmp_str[len+1]; - strlcpy(tmp_str, s, len+1); - - int ret = snprintf(user_data->output + user_data->output_off, sizeof(user_data->output) - user_data->output_off, - "%s", tmp_str); - TEST_ASSERT_EQUAL(strlen(tmp_str), ret); - user_data->output_off += ret; -} - -TEST_CASE("Expat parses XML", "[expat]") -{ - const char test_in[] = "Page titleheader
  1. A
  2. "\ - "
  3. B
  4. C
"; - const char test_expected[] = "\n"\ - "\n"\ - " \n"\ - " Page title\n"\ - " \n"\ - " \n"\ - " \n"\ - " header\n"\ - " \n"\ - "
    \n"\ - "
  1. \n"\ - " A\n"\ - "
  2. \n"\ - "
  3. \n"\ - " B\n"\ - "
  4. \n"\ - "
  5. \n"\ - " C\n"\ - "
  6. \n"\ - "
\n"\ - " \n"\ - ""; - user_data_t user_data = { - .depth = 0, - .output = { '\0' }, - .output_off = 0 - }; - - XML_Parser parser = XML_ParserCreate(NULL); - XML_SetUserData(parser, &user_data); - XML_SetElementHandler(parser, start_element, end_element); - XML_SetCharacterDataHandler(parser, data_handler); - - TEST_ASSERT_NOT_EQUAL(XML_STATUS_ERROR, XML_Parse(parser, test_in, strlen(test_in), 1)); - XML_ParserFree(parser); - - TEST_ASSERT_EQUAL(0, user_data.depth); // all closing tags have been found - - TEST_ASSERT_EQUAL(strlen(test_expected), strlen(user_data.output)); - TEST_ASSERT_EQUAL_STRING(test_expected, user_data.output); -} +/* + * SPDX-FileCopyrightText: 2018-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "unity.h" + +typedef struct { + int depth; + char output[512]; + int output_off; +} user_data_t; + +static void insert_space(user_data_t *user_data) +{ + const char align_str[] = " "; + + TEST_ASSERT(sizeof(user_data->output) >= user_data->output_off); + user_data->output[user_data->output_off++] = '\n'; + + for (int i = 0; i < user_data->depth; i++) { + for (int j = 0; j < strlen(align_str); ++j) { + TEST_ASSERT(sizeof(user_data->output) >= user_data->output_off); + user_data->output[user_data->output_off++] = align_str[j]; + } + } +} + +static void XMLCALL start_element(void *userData, const XML_Char *name, const XML_Char **atts) +{ + user_data_t *user_data = (user_data_t *) userData; + + insert_space(user_data); + + const int ret = snprintf(user_data->output + user_data->output_off, + sizeof(user_data->output) - user_data->output_off, + "<%s>", name); + TEST_ASSERT_EQUAL(strlen(name) + 2, ret); // 2 are the tag characters: "<>" + user_data->output_off += ret; + ++user_data->depth; +} + +static void XMLCALL end_element(void *userData, const XML_Char *name) +{ + user_data_t *user_data = (user_data_t *) userData; + + --user_data->depth; + insert_space(user_data); + + int ret = snprintf(user_data->output + user_data->output_off, sizeof(user_data->output) - user_data->output_off, + "", name); + TEST_ASSERT_EQUAL(strlen(name) + 3, ret); // 3 are the tag characters: "" + user_data->output_off += ret; +} + +static void data_handler(void *userData, const XML_Char *s, int len) +{ + user_data_t *user_data = (user_data_t *) userData; + + insert_space(user_data); + + // s is not zero-terminated + char tmp_str[len + 1]; + strlcpy(tmp_str, s, len + 1); + + int ret = snprintf(user_data->output + user_data->output_off, sizeof(user_data->output) - user_data->output_off, + "%s", tmp_str); + TEST_ASSERT_EQUAL(strlen(tmp_str), ret); + user_data->output_off += ret; +} + +TEST_CASE("Expat parses XML", "[expat]") +{ + const char test_in[] = "Page titleheader
  1. A
  2. "\ + "
  3. B
  4. C
"; + const char test_expected[] = "\n"\ + "\n"\ + " \n"\ + " Page title\n"\ + " \n"\ + " \n"\ + " \n"\ + " header\n"\ + " \n"\ + "
    \n"\ + "
  1. \n"\ + " A\n"\ + "
  2. \n"\ + "
  3. \n"\ + " B\n"\ + "
  4. \n"\ + "
  5. \n"\ + " C\n"\ + "
  6. \n"\ + "
\n"\ + " \n"\ + ""; + user_data_t user_data = { + .depth = 0, + .output = { '\0' }, + .output_off = 0 + }; + + XML_Parser parser = XML_ParserCreate(NULL); + XML_SetUserData(parser, &user_data); + XML_SetElementHandler(parser, start_element, end_element); + XML_SetCharacterDataHandler(parser, data_handler); + + TEST_ASSERT_NOT_EQUAL(XML_STATUS_ERROR, XML_Parse(parser, test_in, strlen(test_in), 1)); + XML_ParserFree(parser); + + TEST_ASSERT_EQUAL(0, user_data.depth); // all closing tags have been found + + TEST_ASSERT_EQUAL(strlen(test_expected), strlen(user_data.output)); + TEST_ASSERT_EQUAL_STRING(test_expected, user_data.output); +} diff --git a/jsmn/include/jsmn.h b/jsmn/include/jsmn.h index 1df808e..c819caa 100644 --- a/jsmn/include/jsmn.h +++ b/jsmn/include/jsmn.h @@ -1,106 +1,106 @@ -/* - * Copyright (c) 2010 Serge A. Zaitsev - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -/** - * @file jsmn.h - * @brief Definition of the JSMN (Jasmine) JSON parser. - * - * For more information on JSMN: - * @see http://zserge.com/jsmn.html - */ - -#ifndef __JSMN_H_ -#define __JSMN_H_ - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * JSON type identifier. Basic types are: - * o Object - * o Array - * o String - * o Other primitive: number, boolean (true/false) or null - */ -typedef enum { - JSMN_UNDEFINED = 0, - JSMN_OBJECT = 1, - JSMN_ARRAY = 2, - JSMN_STRING = 3, - JSMN_PRIMITIVE = 4 -} jsmntype_t; - -enum jsmnerr { - /* Not enough tokens were provided */ - JSMN_ERROR_NOMEM = -1, - /* Invalid character inside JSON string */ - JSMN_ERROR_INVAL = -2, - /* The string is not a full JSON packet, more bytes expected */ - JSMN_ERROR_PART = -3 -}; - -/** - * JSON token description. - * @param type type (object, array, string etc.) - * @param start start position in JSON data string - * @param end end position in JSON data string - */ -typedef struct { - jsmntype_t type; - int start; - int end; - int size; -#ifdef JSMN_PARENT_LINKS - int parent; -#endif -} jsmntok_t; - -/** - * JSON parser. Contains an array of token blocks available. Also stores - * the string being parsed now and current position in that string - */ -typedef struct { - unsigned int pos; /* offset in the JSON string */ - unsigned int toknext; /* next token to allocate */ - int toksuper; /* superior token node, e.g parent object or array */ -} jsmn_parser; - -/** - * Create JSON parser over an array of tokens - */ -void jsmn_init(jsmn_parser *parser); - -/** - * Run JSON parser. It parses a JSON data string into and array of tokens, each describing - * a single JSON object. - */ -int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, - jsmntok_t *tokens, unsigned int num_tokens); - -#ifdef __cplusplus -} -#endif - -#endif /* __JSMN_H_ */ +/* + * Copyright (c) 2010 Serge A. Zaitsev + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/** + * @file jsmn.h + * @brief Definition of the JSMN (Jasmine) JSON parser. + * + * For more information on JSMN: + * @see http://zserge.com/jsmn.html + */ + +#ifndef __JSMN_H_ +#define __JSMN_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * JSON type identifier. Basic types are: + * o Object + * o Array + * o String + * o Other primitive: number, boolean (true/false) or null + */ +typedef enum { + JSMN_UNDEFINED = 0, + JSMN_OBJECT = 1, + JSMN_ARRAY = 2, + JSMN_STRING = 3, + JSMN_PRIMITIVE = 4 +} jsmntype_t; + +enum jsmnerr { + /* Not enough tokens were provided */ + JSMN_ERROR_NOMEM = -1, + /* Invalid character inside JSON string */ + JSMN_ERROR_INVAL = -2, + /* The string is not a full JSON packet, more bytes expected */ + JSMN_ERROR_PART = -3 +}; + +/** + * JSON token description. + * @param type type (object, array, string etc.) + * @param start start position in JSON data string + * @param end end position in JSON data string + */ +typedef struct { + jsmntype_t type; + int start; + int end; + int size; +#ifdef JSMN_PARENT_LINKS + int parent; +#endif +} jsmntok_t; + +/** + * JSON parser. Contains an array of token blocks available. Also stores + * the string being parsed now and current position in that string + */ +typedef struct { + unsigned int pos; /* offset in the JSON string */ + unsigned int toknext; /* next token to allocate */ + int toksuper; /* superior token node, e.g parent object or array */ +} jsmn_parser; + +/** + * Create JSON parser over an array of tokens + */ +void jsmn_init(jsmn_parser *parser); + +/** + * Run JSON parser. It parses a JSON data string into and array of tokens, each describing + * a single JSON object. + */ +int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, + jsmntok_t *tokens, unsigned int num_tokens); + +#ifdef __cplusplus +} +#endif + +#endif /* __JSMN_H_ */ diff --git a/jsmn/src/jsmn.c b/jsmn/src/jsmn.c index 2769b1d..98c9055 100644 --- a/jsmn/src/jsmn.c +++ b/jsmn/src/jsmn.c @@ -1,340 +1,356 @@ -/* - * Copyright (c) 2010 Serge A. Zaitsev - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -/** - * @file jsmn.c - * @brief Implementation of the JSMN (Jasmine) JSON parser. - * - * For more information on JSMN: - * @see http://zserge.com/jsmn.html - */ - -#include "jsmn.h" - -/** - * Allocates a fresh unused token from the token pull. - */ -static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, - jsmntok_t *tokens, size_t num_tokens) { - jsmntok_t *tok; - if (parser->toknext >= num_tokens) { - return NULL; - } - tok = &tokens[parser->toknext++]; - tok->start = tok->end = -1; - tok->size = 0; -#ifdef JSMN_PARENT_LINKS - tok->parent = -1; -#endif - return tok; -} - -/** - * Fills token type and boundaries. - */ -static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type, - int start, int end) { - token->type = type; - token->start = start; - token->end = end; - token->size = 0; -} - -/** - * Fills next available token with JSON primitive. - */ -static int jsmn_parse_primitive(jsmn_parser *parser, const char *js, - size_t len, jsmntok_t *tokens, size_t num_tokens) { - jsmntok_t *token; - int start; - - start = parser->pos; - - for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { - switch (js[parser->pos]) { -#ifndef JSMN_STRICT - /* In strict mode primitive must be followed by "," or "}" or "]" */ - case ':': -#endif - case '\t' : case '\r' : case '\n' : case ' ' : - case ',' : case ']' : case '}' : - goto found; - } - if (js[parser->pos] < 32 || js[parser->pos] >= 127) { - parser->pos = start; - return JSMN_ERROR_INVAL; - } - } -#ifdef JSMN_STRICT - /* In strict mode primitive must be followed by a comma/object/array */ - parser->pos = start; - return JSMN_ERROR_PART; -#endif - -found: - if (tokens == NULL) { - parser->pos--; - return 0; - } - token = jsmn_alloc_token(parser, tokens, num_tokens); - if (token == NULL) { - parser->pos = start; - return JSMN_ERROR_NOMEM; - } - jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); -#ifdef JSMN_PARENT_LINKS - token->parent = parser->toksuper; -#endif - parser->pos--; - return 0; -} - -/** - * Fills next token with JSON string. - */ -static int jsmn_parse_string(jsmn_parser *parser, const char *js, - size_t len, jsmntok_t *tokens, size_t num_tokens) { - jsmntok_t *token; - - int start = parser->pos; - - parser->pos++; - - /* Skip starting quote */ - for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { - char c = js[parser->pos]; - - /* Quote: end of string */ - if (c == '\"') { - if (tokens == NULL) { - return 0; - } - token = jsmn_alloc_token(parser, tokens, num_tokens); - if (token == NULL) { - parser->pos = start; - return JSMN_ERROR_NOMEM; - } - jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos); -#ifdef JSMN_PARENT_LINKS - token->parent = parser->toksuper; -#endif - return 0; - } - - /* Backslash: Quoted symbol expected */ - if (c == '\\' && parser->pos + 1 < len) { - int i; - parser->pos++; - switch (js[parser->pos]) { - /* Allowed escaped symbols */ - case '\"': case '/' : case '\\' : case 'b' : - case 'f' : case 'r' : case 'n' : case 't' : - break; - /* Allows escaped symbol \uXXXX */ - case 'u': - parser->pos++; - for(i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) { - /* If it isn't a hex character we have an error */ - if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */ - (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */ - (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */ - parser->pos = start; - return JSMN_ERROR_INVAL; - } - parser->pos++; - } - parser->pos--; - break; - /* Unexpected symbol */ - default: - parser->pos = start; - return JSMN_ERROR_INVAL; - } - } - } - parser->pos = start; - return JSMN_ERROR_PART; -} - -/** - * Parse JSON string and fill tokens. - */ -int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, - jsmntok_t *tokens, unsigned int num_tokens) { - int r; - int i; - jsmntok_t *token; - int count = parser->toknext; - - for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { - char c; - jsmntype_t type; - - c = js[parser->pos]; - switch (c) { - case '{': case '[': - count++; - if (tokens == NULL) { - break; - } - token = jsmn_alloc_token(parser, tokens, num_tokens); - if (token == NULL) - return JSMN_ERROR_NOMEM; - if (parser->toksuper != -1) { - tokens[parser->toksuper].size++; -#ifdef JSMN_PARENT_LINKS - token->parent = parser->toksuper; -#endif - } - token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); - token->start = parser->pos; - parser->toksuper = parser->toknext - 1; - break; - case '}': case ']': - if (tokens == NULL) - break; - type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); -#ifdef JSMN_PARENT_LINKS - if (parser->toknext < 1) { - return JSMN_ERROR_INVAL; - } - token = &tokens[parser->toknext - 1]; - for (;;) { - if (token->start != -1 && token->end == -1) { - if (token->type != type) { - return JSMN_ERROR_INVAL; - } - token->end = parser->pos + 1; - parser->toksuper = token->parent; - break; - } - if (token->parent == -1) { - break; - } - token = &tokens[token->parent]; - } -#else - for (i = parser->toknext - 1; i >= 0; i--) { - token = &tokens[i]; - if (token->start != -1 && token->end == -1) { - if (token->type != type) { - return JSMN_ERROR_INVAL; - } - parser->toksuper = -1; - token->end = parser->pos + 1; - break; - } - } - /* Error if unmatched closing bracket */ - if (i == -1) return JSMN_ERROR_INVAL; - for (; i >= 0; i--) { - token = &tokens[i]; - if (token->start != -1 && token->end == -1) { - parser->toksuper = i; - break; - } - } -#endif - break; - case '\"': - r = jsmn_parse_string(parser, js, len, tokens, num_tokens); - if (r < 0) return r; - count++; - if (parser->toksuper != -1 && tokens != NULL) - tokens[parser->toksuper].size++; - break; - case '\t' : case '\r' : case '\n' : case ' ': - break; - case ':': - parser->toksuper = parser->toknext - 1; - break; - case ',': - if (tokens != NULL && parser->toksuper != -1 && - tokens[parser->toksuper].type != JSMN_ARRAY && - tokens[parser->toksuper].type != JSMN_OBJECT) { -#ifdef JSMN_PARENT_LINKS - parser->toksuper = tokens[parser->toksuper].parent; -#else - for (i = parser->toknext - 1; i >= 0; i--) { - if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) { - if (tokens[i].start != -1 && tokens[i].end == -1) { - parser->toksuper = i; - break; - } - } - } -#endif - } - break; -#ifdef JSMN_STRICT - /* In strict mode primitives are: numbers and booleans */ - case '-': case '0': case '1' : case '2': case '3' : case '4': - case '5': case '6': case '7' : case '8': case '9': - case 't': case 'f': case 'n' : - /* And they must not be keys of the object */ - if (tokens != NULL && parser->toksuper != -1) { - jsmntok_t *t = &tokens[parser->toksuper]; - if (t->type == JSMN_OBJECT || - (t->type == JSMN_STRING && t->size != 0)) { - return JSMN_ERROR_INVAL; - } - } -#else - /* In non-strict mode every unquoted value is a primitive */ - default: -#endif - r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens); - if (r < 0) return r; - count++; - if (parser->toksuper != -1 && tokens != NULL) - tokens[parser->toksuper].size++; - break; - -#ifdef JSMN_STRICT - /* Unexpected char in strict mode */ - default: - return JSMN_ERROR_INVAL; -#endif - } - } - - if (tokens != NULL) { - for (i = parser->toknext - 1; i >= 0; i--) { - /* Unmatched opened object or array */ - if (tokens[i].start != -1 && tokens[i].end == -1) { - return JSMN_ERROR_PART; - } - } - } - - return count; -} - -/** - * Creates a new parser based over a given buffer with an array of tokens - * available. - */ -void jsmn_init(jsmn_parser *parser) { - parser->pos = 0; - parser->toknext = 0; - parser->toksuper = -1; -} +/* + * Copyright (c) 2010 Serge A. Zaitsev + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/** + * @file jsmn.c + * @brief Implementation of the JSMN (Jasmine) JSON parser. + * + * For more information on JSMN: + * @see http://zserge.com/jsmn.html + */ + +#include "jsmn.h" + +/** + * Allocates a fresh unused token from the token pull. + */ +static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, + jsmntok_t *tokens, size_t num_tokens) +{ + jsmntok_t *tok; + if (parser->toknext >= num_tokens) { + return NULL; + } + tok = &tokens[parser->toknext++]; + tok->start = tok->end = -1; + tok->size = 0; +#ifdef JSMN_PARENT_LINKS + tok->parent = -1; +#endif + return tok; +} + +/** + * Fills token type and boundaries. + */ +static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type, + int start, int end) +{ + token->type = type; + token->start = start; + token->end = end; + token->size = 0; +} + +/** + * Fills next available token with JSON primitive. + */ +static int jsmn_parse_primitive(jsmn_parser *parser, const char *js, + size_t len, jsmntok_t *tokens, size_t num_tokens) +{ + jsmntok_t *token; + int start; + + start = parser->pos; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + switch (js[parser->pos]) { +#ifndef JSMN_STRICT + /* In strict mode primitive must be followed by "," or "}" or "]" */ + case ':': +#endif + case '\t' : case '\r' : case '\n' : case ' ' : + case ',' : case ']' : case '}' : + goto found; + } + if (js[parser->pos] < 32 || js[parser->pos] >= 127) { + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } +#ifdef JSMN_STRICT + /* In strict mode primitive must be followed by a comma/object/array */ + parser->pos = start; + return JSMN_ERROR_PART; +#endif + +found: + if (tokens == NULL) { + parser->pos--; + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + parser->pos--; + return 0; +} + +/** + * Fills next token with JSON string. + */ +static int jsmn_parse_string(jsmn_parser *parser, const char *js, + size_t len, jsmntok_t *tokens, size_t num_tokens) +{ + jsmntok_t *token; + + int start = parser->pos; + + parser->pos++; + + /* Skip starting quote */ + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c = js[parser->pos]; + + /* Quote: end of string */ + if (c == '\"') { + if (tokens == NULL) { + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_STRING, start + 1, parser->pos); +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + return 0; + } + + /* Backslash: Quoted symbol expected */ + if (c == '\\' && parser->pos + 1 < len) { + int i; + parser->pos++; + switch (js[parser->pos]) { + /* Allowed escaped symbols */ + case '\"': case '/' : case '\\' : case 'b' : + case 'f' : case 'r' : case 'n' : case 't' : + break; + /* Allows escaped symbol \uXXXX */ + case 'u': + parser->pos++; + for (i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) { + /* If it isn't a hex character we have an error */ + if (!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */ + (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */ + (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */ + parser->pos = start; + return JSMN_ERROR_INVAL; + } + parser->pos++; + } + parser->pos--; + break; + /* Unexpected symbol */ + default: + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } + } + parser->pos = start; + return JSMN_ERROR_PART; +} + +/** + * Parse JSON string and fill tokens. + */ +int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, + jsmntok_t *tokens, unsigned int num_tokens) +{ + int r; + int i; + jsmntok_t *token; + int count = parser->toknext; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c; + jsmntype_t type; + + c = js[parser->pos]; + switch (c) { + case '{': case '[': + count++; + if (tokens == NULL) { + break; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + return JSMN_ERROR_NOMEM; + } + if (parser->toksuper != -1) { + tokens[parser->toksuper].size++; +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + } + token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); + token->start = parser->pos; + parser->toksuper = parser->toknext - 1; + break; + case '}': case ']': + if (tokens == NULL) { + break; + } + type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); +#ifdef JSMN_PARENT_LINKS + if (parser->toknext < 1) { + return JSMN_ERROR_INVAL; + } + token = &tokens[parser->toknext - 1]; + for (;;) { + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + token->end = parser->pos + 1; + parser->toksuper = token->parent; + break; + } + if (token->parent == -1) { + break; + } + token = &tokens[token->parent]; + } +#else + for (i = parser->toknext - 1; i >= 0; i--) { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + parser->toksuper = -1; + token->end = parser->pos + 1; + break; + } + } + /* Error if unmatched closing bracket */ + if (i == -1) { + return JSMN_ERROR_INVAL; + } + for (; i >= 0; i--) { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) { + parser->toksuper = i; + break; + } + } +#endif + break; + case '\"': + r = jsmn_parse_string(parser, js, len, tokens, num_tokens); + if (r < 0) { + return r; + } + count++; + if (parser->toksuper != -1 && tokens != NULL) { + tokens[parser->toksuper].size++; + } + break; + case '\t' : case '\r' : case '\n' : case ' ': + break; + case ':': + parser->toksuper = parser->toknext - 1; + break; + case ',': + if (tokens != NULL && parser->toksuper != -1 && + tokens[parser->toksuper].type != JSMN_ARRAY && + tokens[parser->toksuper].type != JSMN_OBJECT) { +#ifdef JSMN_PARENT_LINKS + parser->toksuper = tokens[parser->toksuper].parent; +#else + for (i = parser->toknext - 1; i >= 0; i--) { + if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) { + if (tokens[i].start != -1 && tokens[i].end == -1) { + parser->toksuper = i; + break; + } + } + } +#endif + } + break; +#ifdef JSMN_STRICT + /* In strict mode primitives are: numbers and booleans */ + case '-': case '0': case '1' : case '2': case '3' : case '4': + case '5': case '6': case '7' : case '8': case '9': + case 't': case 'f': case 'n' : + /* And they must not be keys of the object */ + if (tokens != NULL && parser->toksuper != -1) { + jsmntok_t *t = &tokens[parser->toksuper]; + if (t->type == JSMN_OBJECT || + (t->type == JSMN_STRING && t->size != 0)) { + return JSMN_ERROR_INVAL; + } + } +#else + /* In non-strict mode every unquoted value is a primitive */ + default: +#endif + r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens); + if (r < 0) { + return r; + } + count++; + if (parser->toksuper != -1 && tokens != NULL) { + tokens[parser->toksuper].size++; + } + break; + +#ifdef JSMN_STRICT + /* Unexpected char in strict mode */ + default: + return JSMN_ERROR_INVAL; +#endif + } + } + + if (tokens != NULL) { + for (i = parser->toknext - 1; i >= 0; i--) { + /* Unmatched opened object or array */ + if (tokens[i].start != -1 && tokens[i].end == -1) { + return JSMN_ERROR_PART; + } + } + } + + return count; +} + +/** + * Creates a new parser based over a given buffer with an array of tokens + * available. + */ +void jsmn_init(jsmn_parser *parser) +{ + parser->pos = 0; + parser->toknext = 0; + parser->toksuper = -1; +} diff --git a/libsodium/test/test_sodium.c b/libsodium/test/test_sodium.c index 22455dc..7b8582b 100644 --- a/libsodium/test/test_sodium.c +++ b/libsodium/test/test_sodium.c @@ -1,124 +1,126 @@ -/* - * SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ -#include "unity.h" -#include "sodium/crypto_hash_sha256.h" -#include "sodium/crypto_hash_sha512.h" - -/* Note: a lot of these libsodium test programs assert() things, but they're not complete unit tests - most expect - output to be compared to the matching .exp file. - - We don't do this automatically yet, maybe once we have more options for - internal filesystem storage. -*/ - -extern int aead_chacha20poly1305_xmain(void); - -TEST_CASE("aead_chacha20poly1305 test vectors", "[libsodium]") -{ - printf("Running aead_chacha20poly1305\n"); - TEST_ASSERT_EQUAL(0, aead_chacha20poly1305_xmain()); -} - -extern int chacha20_xmain(void); - -TEST_CASE("chacha20 test vectors", "[libsodium]") -{ - printf("Running chacha20\n"); - TEST_ASSERT_EQUAL(0, chacha20_xmain()); -} - -extern int box_xmain(void); -extern int box2_xmain(void); - -TEST_CASE("box tests", "[libsodium]") -{ - printf("Running box\n"); - TEST_ASSERT_EQUAL(0, box_xmain()); - - printf("Running box2\n"); - TEST_ASSERT_EQUAL(0, box2_xmain()); -} - -extern int ed25519_convert_xmain(void); - -TEST_CASE("ed25519_convert tests", "[libsodium][timeout=60]") -{ - printf("Running ed25519_convert\n"); - TEST_ASSERT_EQUAL(0, ed25519_convert_xmain() ); -} - -extern int sign_xmain(void); - -TEST_CASE("sign tests", "[libsodium]") -{ - printf("Running sign\n"); - TEST_ASSERT_EQUAL(0, sign_xmain() ); -} - -extern int hash_xmain(void); - -TEST_CASE("hash tests", "[libsodium]") -{ - printf("Running hash\n"); - TEST_ASSERT_EQUAL(0, hash_xmain() ); -} - -TEST_CASE("sha256 sanity check", "[libsodium]") -{ - const uint8_t expected[] = { 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, - 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23, 0xb0, 0x03, - 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, 0xb4, 0x10, 0xff, - 0x61, 0xf2, 0x00, 0x15, 0xad, }; - uint8_t calculated[32]; - crypto_hash_sha256_state state; - - const uint8_t *in = (const uint8_t *)"abc"; - const size_t inlen = 3; - - // One-liner version - crypto_hash_sha256(calculated, in, inlen); - TEST_ASSERT_EQUAL(sizeof(calculated), sizeof(expected)); - TEST_ASSERT_EQUAL(sizeof(calculated), crypto_hash_sha256_bytes()); - TEST_ASSERT_EQUAL_MEMORY(expected, calculated, crypto_hash_sha256_bytes()); - - // Multi-line version - crypto_hash_sha256_init(&state); - crypto_hash_sha256_update(&state, in, inlen - 1); // split into two updates - crypto_hash_sha256_update(&state, in + (inlen -1), 1); - crypto_hash_sha256_final(&state, calculated); - TEST_ASSERT_EQUAL_MEMORY(expected, calculated, crypto_hash_sha256_bytes()); -} - -TEST_CASE("sha512 sanity check", "[libsodium]") -{ - const uint8_t expected[] = { 0xdd, 0xaf, 0x35, 0xa1, 0x93, 0x61, 0x7a, 0xba, 0xcc, - 0x41, 0x73, 0x49, 0xae, 0x20, 0x41, 0x31, 0x12, 0xe6, - 0xfa, 0x4e, 0x89, 0xa9, 0x7e, 0xa2, 0x0a, 0x9e, 0xee, - 0xe6, 0x4b, 0x55, 0xd3, 0x9a, 0x21, 0x92, 0x99, 0x2a, - 0x27, 0x4f, 0xc1, 0xa8, 0x36, 0xba, 0x3c, 0x23, 0xa3, - 0xfe, 0xeb, 0xbd, 0x45, 0x4d, 0x44, 0x23, 0x64, 0x3c, - 0xe8, 0x0e, 0x2a, 0x9a, 0xc9, 0x4f, 0xa5, 0x4c, 0xa4, - 0x9f }; - - uint8_t calculated[64]; - crypto_hash_sha512_state state; - - const uint8_t *in = (const uint8_t *)"abc"; - const size_t inlen = 3; - - // One-liner version - crypto_hash_sha512(calculated, in, inlen); - TEST_ASSERT_EQUAL(sizeof(calculated), sizeof(expected)); - TEST_ASSERT_EQUAL(sizeof(calculated), crypto_hash_sha512_bytes()); - TEST_ASSERT_EQUAL_MEMORY(expected, calculated, crypto_hash_sha512_bytes()); - - // Multi-line version - crypto_hash_sha512_init(&state); - crypto_hash_sha512_update(&state, in, inlen - 1); // split into two updates - crypto_hash_sha512_update(&state, in + (inlen -1), 1); - crypto_hash_sha512_final(&state, calculated); - TEST_ASSERT_EQUAL_MEMORY(expected, calculated, crypto_hash_sha512_bytes()); -} +/* + * SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "unity.h" +#include "sodium/crypto_hash_sha256.h" +#include "sodium/crypto_hash_sha512.h" + +/* Note: a lot of these libsodium test programs assert() things, but they're not complete unit tests - most expect + output to be compared to the matching .exp file. + + We don't do this automatically yet, maybe once we have more options for + internal filesystem storage. +*/ + +extern int aead_chacha20poly1305_xmain(void); + +TEST_CASE("aead_chacha20poly1305 test vectors", "[libsodium]") +{ + printf("Running aead_chacha20poly1305\n"); + TEST_ASSERT_EQUAL(0, aead_chacha20poly1305_xmain()); +} + +extern int chacha20_xmain(void); + +TEST_CASE("chacha20 test vectors", "[libsodium]") +{ + printf("Running chacha20\n"); + TEST_ASSERT_EQUAL(0, chacha20_xmain()); +} + +extern int box_xmain(void); +extern int box2_xmain(void); + +TEST_CASE("box tests", "[libsodium]") +{ + printf("Running box\n"); + TEST_ASSERT_EQUAL(0, box_xmain()); + + printf("Running box2\n"); + TEST_ASSERT_EQUAL(0, box2_xmain()); +} + +extern int ed25519_convert_xmain(void); + +TEST_CASE("ed25519_convert tests", "[libsodium][timeout=60]") +{ + printf("Running ed25519_convert\n"); + TEST_ASSERT_EQUAL(0, ed25519_convert_xmain() ); +} + +extern int sign_xmain(void); + +TEST_CASE("sign tests", "[libsodium]") +{ + printf("Running sign\n"); + TEST_ASSERT_EQUAL(0, sign_xmain() ); +} + +extern int hash_xmain(void); + +TEST_CASE("hash tests", "[libsodium]") +{ + printf("Running hash\n"); + TEST_ASSERT_EQUAL(0, hash_xmain() ); +} + +TEST_CASE("sha256 sanity check", "[libsodium]") +{ + const uint8_t expected[] = { 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, + 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23, 0xb0, 0x03, + 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, 0xb4, 0x10, 0xff, + 0x61, 0xf2, 0x00, 0x15, 0xad, + }; + uint8_t calculated[32]; + crypto_hash_sha256_state state; + + const uint8_t *in = (const uint8_t *)"abc"; + const size_t inlen = 3; + + // One-liner version + crypto_hash_sha256(calculated, in, inlen); + TEST_ASSERT_EQUAL(sizeof(calculated), sizeof(expected)); + TEST_ASSERT_EQUAL(sizeof(calculated), crypto_hash_sha256_bytes()); + TEST_ASSERT_EQUAL_MEMORY(expected, calculated, crypto_hash_sha256_bytes()); + + // Multi-line version + crypto_hash_sha256_init(&state); + crypto_hash_sha256_update(&state, in, inlen - 1); // split into two updates + crypto_hash_sha256_update(&state, in + (inlen - 1), 1); + crypto_hash_sha256_final(&state, calculated); + TEST_ASSERT_EQUAL_MEMORY(expected, calculated, crypto_hash_sha256_bytes()); +} + +TEST_CASE("sha512 sanity check", "[libsodium]") +{ + const uint8_t expected[] = { 0xdd, 0xaf, 0x35, 0xa1, 0x93, 0x61, 0x7a, 0xba, 0xcc, + 0x41, 0x73, 0x49, 0xae, 0x20, 0x41, 0x31, 0x12, 0xe6, + 0xfa, 0x4e, 0x89, 0xa9, 0x7e, 0xa2, 0x0a, 0x9e, 0xee, + 0xe6, 0x4b, 0x55, 0xd3, 0x9a, 0x21, 0x92, 0x99, 0x2a, + 0x27, 0x4f, 0xc1, 0xa8, 0x36, 0xba, 0x3c, 0x23, 0xa3, + 0xfe, 0xeb, 0xbd, 0x45, 0x4d, 0x44, 0x23, 0x64, 0x3c, + 0xe8, 0x0e, 0x2a, 0x9a, 0xc9, 0x4f, 0xa5, 0x4c, 0xa4, + 0x9f + }; + + uint8_t calculated[64]; + crypto_hash_sha512_state state; + + const uint8_t *in = (const uint8_t *)"abc"; + const size_t inlen = 3; + + // One-liner version + crypto_hash_sha512(calculated, in, inlen); + TEST_ASSERT_EQUAL(sizeof(calculated), sizeof(expected)); + TEST_ASSERT_EQUAL(sizeof(calculated), crypto_hash_sha512_bytes()); + TEST_ASSERT_EQUAL_MEMORY(expected, calculated, crypto_hash_sha512_bytes()); + + // Multi-line version + crypto_hash_sha512_init(&state); + crypto_hash_sha512_update(&state, in, inlen - 1); // split into two updates + crypto_hash_sha512_update(&state, in + (inlen - 1), 1); + crypto_hash_sha512_final(&state, calculated); + TEST_ASSERT_EQUAL_MEMORY(expected, calculated, crypto_hash_sha512_bytes()); +} diff --git a/nghttp/port/private_include/config.h b/nghttp/port/private_include/config.h index 0aeefdc..05c7a73 100644 --- a/nghttp/port/private_include/config.h +++ b/nghttp/port/private_include/config.h @@ -1,29 +1,29 @@ -#ifndef __HAVE_CONFIG_H_ -#define __HAVE_CONFIG_H_ - -#define _U_ - -#define SIZEOF_INT_P 2 - -#include "stdio.h" -#include "stdlib.h" -#include "string.h" - -#if (!defined(nghttp_unlikely)) -#define nghttp_unlikely(Expression) !!(Expression) -#endif - -#define nghttp_ASSERT(Expression) do{if (!(Expression)) printf("%d\n", __LINE__);}while(0) - -#define CU_ASSERT(a) nghttp_ASSERT(a) -#define CU_ASSERT_FATAL(a) nghttp_ASSERT(a) - -#define NGHTTP_PLATFORM_HTONS(_n) ((uint16_t)((((_n) & 0xff) << 8) | (((_n) >> 8) & 0xff))) -#define NGHTTP_PLATFORM_HTONL(_n) ((uint32_t)( (((_n) & 0xff) << 24) | (((_n) & 0xff00) << 8) | (((_n) >> 8) & 0xff00) | (((_n) >> 24) & 0xff) )) - -#define htons(x) NGHTTP_PLATFORM_HTONS(x) -#define ntohs(x) NGHTTP_PLATFORM_HTONS(x) -#define htonl(x) NGHTTP_PLATFORM_HTONL(x) -#define ntohl(x) NGHTTP_PLATFORM_HTONL(x) - -#endif // __HAVE_CONFIG_H_ +#ifndef __HAVE_CONFIG_H_ +#define __HAVE_CONFIG_H_ + +#define _U_ + +#define SIZEOF_INT_P 2 + +#include "stdio.h" +#include "stdlib.h" +#include "string.h" + +#if (!defined(nghttp_unlikely)) +#define nghttp_unlikely(Expression) !!(Expression) +#endif + +#define nghttp_ASSERT(Expression) do{if (!(Expression)) printf("%d\n", __LINE__);}while(0) + +#define CU_ASSERT(a) nghttp_ASSERT(a) +#define CU_ASSERT_FATAL(a) nghttp_ASSERT(a) + +#define NGHTTP_PLATFORM_HTONS(_n) ((uint16_t)((((_n) & 0xff) << 8) | (((_n) >> 8) & 0xff))) +#define NGHTTP_PLATFORM_HTONL(_n) ((uint32_t)( (((_n) & 0xff) << 24) | (((_n) & 0xff00) << 8) | (((_n) >> 8) & 0xff00) | (((_n) >> 24) & 0xff) )) + +#define htons(x) NGHTTP_PLATFORM_HTONS(x) +#define ntohs(x) NGHTTP_PLATFORM_HTONS(x) +#define htonl(x) NGHTTP_PLATFORM_HTONL(x) +#define ntohl(x) NGHTTP_PLATFORM_HTONL(x) + +#endif // __HAVE_CONFIG_H_ diff --git a/sh2lib/sh2lib.c b/sh2lib/sh2lib.c index 498739f..9e56f5b 100644 --- a/sh2lib/sh2lib.c +++ b/sh2lib/sh2lib.c @@ -1,370 +1,370 @@ -/* - * SPDX-FileCopyrightText: 2017-2021 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "sh2lib.h" - -static const char *TAG = "sh2lib"; - -#define DBG_FRAME_SEND 1 - -/* - * The implementation of nghttp2_send_callback type. Here we write - * |data| with size |length| to the network and return the number of - * bytes actually written. See the documentation of - * nghttp2_send_callback for the details. - */ -static ssize_t callback_send_inner(struct sh2lib_handle *hd, const uint8_t *data, - size_t length) -{ - int rv = esp_tls_conn_write(hd->http2_tls, data, length); - if (rv <= 0) { - if (rv == ESP_TLS_ERR_SSL_WANT_READ || rv == ESP_TLS_ERR_SSL_WANT_WRITE) { - rv = NGHTTP2_ERR_WOULDBLOCK; - } else { - rv = NGHTTP2_ERR_CALLBACK_FAILURE; - } - } - return rv; -} - -static ssize_t callback_send(nghttp2_session *session, const uint8_t *data, - size_t length, int flags, void *user_data) -{ - int rv = 0; - struct sh2lib_handle *hd = user_data; - - int copy_offset = 0; - int pending_data = length; - - /* Send data in 1000 byte chunks */ - while (copy_offset != length) { - int chunk_len = pending_data > 1000 ? 1000 : pending_data; - int subrv = callback_send_inner(hd, data + copy_offset, chunk_len); - if (subrv <= 0) { - if (copy_offset == 0) { - /* If no data is transferred, send the error code */ - rv = subrv; - } - break; - } - copy_offset += subrv; - pending_data -= subrv; - rv += subrv; - } - return rv; -} - -/* - * The implementation of nghttp2_recv_callback type. Here we read data - * from the network and write them in |buf|. The capacity of |buf| is - * |length| bytes. Returns the number of bytes stored in |buf|. See - * the documentation of nghttp2_recv_callback for the details. - */ -static ssize_t callback_recv(nghttp2_session *session, uint8_t *buf, - size_t length, int flags, void *user_data) -{ - struct sh2lib_handle *hd = user_data; - int rv; - rv = esp_tls_conn_read(hd->http2_tls, (char *)buf, (int)length); - if (rv < 0) { - if (rv == ESP_TLS_ERR_SSL_WANT_READ || rv == ESP_TLS_ERR_SSL_WANT_WRITE) { - rv = NGHTTP2_ERR_WOULDBLOCK; - } else { - rv = NGHTTP2_ERR_CALLBACK_FAILURE; - } - } else if (rv == 0) { - rv = NGHTTP2_ERR_EOF; - } - return rv; -} - -const char *sh2lib_frame_type_str(int type) -{ - switch (type) { - case NGHTTP2_HEADERS: - return "HEADERS"; - break; - case NGHTTP2_RST_STREAM: - return "RST_STREAM"; - break; - case NGHTTP2_GOAWAY: - return "GOAWAY"; - break; - case NGHTTP2_DATA: - return "DATA"; - break; - case NGHTTP2_SETTINGS: - return "SETTINGS"; - break; - case NGHTTP2_PUSH_PROMISE: - return "PUSH_PROMISE"; - break; - case NGHTTP2_PING: - return "PING"; - break; - default: - return "other"; - break; - } -} - -static int callback_on_frame_send(nghttp2_session *session, - const nghttp2_frame *frame, void *user_data) -{ - ESP_LOGD(TAG, "[frame-send] frame type %s", sh2lib_frame_type_str(frame->hd.type)); - switch (frame->hd.type) { - case NGHTTP2_HEADERS: - if (nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)) { - ESP_LOGD(TAG, "[frame-send] C ----------------------------> S (HEADERS)"); -#if DBG_FRAME_SEND - ESP_LOGD(TAG, "[frame-send] headers nv-len = %d", frame->headers.nvlen); - const nghttp2_nv *nva = frame->headers.nva; - size_t i; - for (i = 0; i < frame->headers.nvlen; ++i) { - ESP_LOGD(TAG, "[frame-send] %s : %s", nva[i].name, nva[i].value); - } -#endif - } - break; - } - return 0; -} - -static int callback_on_frame_recv(nghttp2_session *session, - const nghttp2_frame *frame, void *user_data) -{ - ESP_LOGD(TAG, "[frame-recv][sid: %d] frame type %s", frame->hd.stream_id, sh2lib_frame_type_str(frame->hd.type)); - if (frame->hd.type != NGHTTP2_DATA) { - return 0; - } - /* Subsequent processing only for data frame */ - sh2lib_frame_data_recv_cb_t data_recv_cb = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id); - if (data_recv_cb) { - struct sh2lib_handle *h2 = user_data; - (*data_recv_cb)(h2, NULL, 0, DATA_RECV_FRAME_COMPLETE); - } - return 0; -} - -static int callback_on_stream_close(nghttp2_session *session, int32_t stream_id, - uint32_t error_code, void *user_data) -{ - - ESP_LOGD(TAG, "[stream-close][sid %d]", stream_id); - sh2lib_frame_data_recv_cb_t data_recv_cb = nghttp2_session_get_stream_user_data(session, stream_id); - if (data_recv_cb) { - struct sh2lib_handle *h2 = user_data; - (*data_recv_cb)(h2, NULL, 0, DATA_RECV_RST_STREAM); - } - return 0; -} - -static int callback_on_data_chunk_recv(nghttp2_session *session, uint8_t flags, - int32_t stream_id, const uint8_t *data, - size_t len, void *user_data) -{ - sh2lib_frame_data_recv_cb_t data_recv_cb; - ESP_LOGD(TAG, "[data-chunk][sid:%d]", stream_id); - data_recv_cb = nghttp2_session_get_stream_user_data(session, stream_id); - if (data_recv_cb) { - ESP_LOGD(TAG, "[data-chunk] C <---------------------------- S (DATA chunk)" - "%lu bytes", - (unsigned long int)len); - struct sh2lib_handle *h2 = user_data; - (*data_recv_cb)(h2, (char *)data, len, 0); - /* TODO: What to do with the return value: look for pause/abort */ - } - return 0; -} - -static int callback_on_header(nghttp2_session *session, const nghttp2_frame *frame, - const uint8_t *name, size_t namelen, const uint8_t *value, - size_t valuelen, uint8_t flags, void *user_data) -{ - ESP_LOGD(TAG, "[hdr-recv][sid:%d] %s : %s", frame->hd.stream_id, name, value); - return 0; -} - -static int do_http2_connect(struct sh2lib_handle *hd) -{ - int ret; - nghttp2_session_callbacks *callbacks; - nghttp2_session_callbacks_new(&callbacks); - nghttp2_session_callbacks_set_send_callback(callbacks, callback_send); - nghttp2_session_callbacks_set_recv_callback(callbacks, callback_recv); - nghttp2_session_callbacks_set_on_frame_send_callback(callbacks, callback_on_frame_send); - nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, callback_on_frame_recv); - nghttp2_session_callbacks_set_on_stream_close_callback(callbacks, callback_on_stream_close); - nghttp2_session_callbacks_set_on_data_chunk_recv_callback(callbacks, callback_on_data_chunk_recv); - nghttp2_session_callbacks_set_on_header_callback(callbacks, callback_on_header); - ret = nghttp2_session_client_new(&hd->http2_sess, callbacks, hd); - if (ret != 0) { - ESP_LOGE(TAG, "[sh2-connect] New http2 session failed"); - nghttp2_session_callbacks_del(callbacks); - return -1; - } - nghttp2_session_callbacks_del(callbacks); - - /* Create the SETTINGS frame */ - ret = nghttp2_submit_settings(hd->http2_sess, NGHTTP2_FLAG_NONE, NULL, 0); - if (ret != 0) { - ESP_LOGE(TAG, "[sh2-connect] Submit settings failed"); - return -1; - } - return 0; -} - -int sh2lib_connect(struct sh2lib_config_t *cfg, struct sh2lib_handle *hd) -{ - memset(hd, 0, sizeof(*hd)); - - if (cfg == NULL) { - ESP_LOGE(TAG, "[sh2-connect] pointer to sh2lib configurations cannot be NULL"); - goto error; - } - - const char *proto[] = {"h2", NULL}; - esp_tls_cfg_t tls_cfg = { - .alpn_protos = proto, - .cacert_buf = cfg->cacert_buf, - .cacert_bytes = cfg->cacert_bytes, - .non_block = true, - .timeout_ms = 10 * 1000, - }; - if ((hd->http2_tls = esp_tls_conn_http_new(cfg->uri, &tls_cfg)) == NULL) { - ESP_LOGE(TAG, "[sh2-connect] esp-tls connection failed"); - goto error; - } - struct http_parser_url u; - http_parser_url_init(&u); - http_parser_parse_url(cfg->uri, strlen(cfg->uri), 0, &u); - hd->hostname = strndup(&cfg->uri[u.field_data[UF_HOST].off], u.field_data[UF_HOST].len); - - /* HTTP/2 Connection */ - if (do_http2_connect(hd) != 0) { - ESP_LOGE(TAG, "[sh2-connect] HTTP2 Connection failed with %s", cfg->uri); - goto error; - } - - return 0; -error: - sh2lib_free(hd); - return -1; -} - -void sh2lib_free(struct sh2lib_handle *hd) -{ - if (hd->http2_sess) { - nghttp2_session_del(hd->http2_sess); - hd->http2_sess = NULL; - } - if (hd->http2_tls) { - esp_tls_conn_destroy(hd->http2_tls); - hd->http2_tls = NULL; - } - if (hd->hostname) { - free(hd->hostname); - hd->hostname = NULL; - } -} - -int sh2lib_execute(struct sh2lib_handle *hd) -{ - int ret; - ret = nghttp2_session_send(hd->http2_sess); - if (ret != 0) { - ESP_LOGE(TAG, "[sh2-execute] HTTP2 session send failed %d", ret); - return -1; - } - - ret = nghttp2_session_recv(hd->http2_sess); - if (ret != 0) { - ESP_LOGE(TAG, "[sh2-execute] HTTP2 session recv failed %d", ret); - return -1; - } - - return 0; -} - -int sh2lib_do_get_with_nv(struct sh2lib_handle *hd, const nghttp2_nv *nva, size_t nvlen, sh2lib_frame_data_recv_cb_t recv_cb) -{ - int ret = nghttp2_submit_request(hd->http2_sess, NULL, nva, nvlen, NULL, recv_cb); - if (ret < 0) { - ESP_LOGE(TAG, "[sh2-do-get] HEADERS call failed"); - return -1; - } - return ret; -} - -int sh2lib_do_get(struct sh2lib_handle *hd, const char *path, sh2lib_frame_data_recv_cb_t recv_cb) -{ - const nghttp2_nv nva[] = { SH2LIB_MAKE_NV(":method", "GET"), - SH2LIB_MAKE_NV(":scheme", "https"), - SH2LIB_MAKE_NV(":authority", hd->hostname), - SH2LIB_MAKE_NV(":path", path), - }; - return sh2lib_do_get_with_nv(hd, nva, sizeof(nva) / sizeof(nva[0]), recv_cb); -} - -ssize_t sh2lib_data_provider_cb(nghttp2_session *session, int32_t stream_id, uint8_t *buf, - size_t length, uint32_t *data_flags, - nghttp2_data_source *source, void *user_data) -{ - struct sh2lib_handle *h2 = user_data; - sh2lib_putpost_data_cb_t data_cb = source->ptr; - return (*data_cb)(h2, (char *)buf, length, data_flags); -} - -int sh2lib_do_putpost_with_nv(struct sh2lib_handle *hd, const nghttp2_nv *nva, size_t nvlen, - sh2lib_putpost_data_cb_t send_cb, - sh2lib_frame_data_recv_cb_t recv_cb) -{ - - nghttp2_data_provider sh2lib_data_provider; - sh2lib_data_provider.read_callback = sh2lib_data_provider_cb; - sh2lib_data_provider.source.ptr = send_cb; - int ret = nghttp2_submit_request(hd->http2_sess, NULL, nva, nvlen, &sh2lib_data_provider, recv_cb); - if (ret < 0) { - ESP_LOGE(TAG, "[sh2-do-putpost] HEADERS call failed"); - return -1; - } - return ret; -} - -int sh2lib_do_post(struct sh2lib_handle *hd, const char *path, - sh2lib_putpost_data_cb_t send_cb, - sh2lib_frame_data_recv_cb_t recv_cb) -{ - const nghttp2_nv nva[] = { SH2LIB_MAKE_NV(":method", "POST"), - SH2LIB_MAKE_NV(":scheme", "https"), - SH2LIB_MAKE_NV(":authority", hd->hostname), - SH2LIB_MAKE_NV(":path", path), - }; - return sh2lib_do_putpost_with_nv(hd, nva, sizeof(nva) / sizeof(nva[0]), send_cb, recv_cb); -} - -int sh2lib_do_put(struct sh2lib_handle *hd, const char *path, - sh2lib_putpost_data_cb_t send_cb, - sh2lib_frame_data_recv_cb_t recv_cb) -{ - const nghttp2_nv nva[] = { SH2LIB_MAKE_NV(":method", "PUT"), - SH2LIB_MAKE_NV(":scheme", "https"), - SH2LIB_MAKE_NV(":authority", hd->hostname), - SH2LIB_MAKE_NV(":path", path), - }; - return sh2lib_do_putpost_with_nv(hd, nva, sizeof(nva) / sizeof(nva[0]), send_cb, recv_cb); -} +/* + * SPDX-FileCopyrightText: 2017-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sh2lib.h" + +static const char *TAG = "sh2lib"; + +#define DBG_FRAME_SEND 1 + +/* + * The implementation of nghttp2_send_callback type. Here we write + * |data| with size |length| to the network and return the number of + * bytes actually written. See the documentation of + * nghttp2_send_callback for the details. + */ +static ssize_t callback_send_inner(struct sh2lib_handle *hd, const uint8_t *data, + size_t length) +{ + int rv = esp_tls_conn_write(hd->http2_tls, data, length); + if (rv <= 0) { + if (rv == ESP_TLS_ERR_SSL_WANT_READ || rv == ESP_TLS_ERR_SSL_WANT_WRITE) { + rv = NGHTTP2_ERR_WOULDBLOCK; + } else { + rv = NGHTTP2_ERR_CALLBACK_FAILURE; + } + } + return rv; +} + +static ssize_t callback_send(nghttp2_session *session, const uint8_t *data, + size_t length, int flags, void *user_data) +{ + int rv = 0; + struct sh2lib_handle *hd = user_data; + + int copy_offset = 0; + int pending_data = length; + + /* Send data in 1000 byte chunks */ + while (copy_offset != length) { + int chunk_len = pending_data > 1000 ? 1000 : pending_data; + int subrv = callback_send_inner(hd, data + copy_offset, chunk_len); + if (subrv <= 0) { + if (copy_offset == 0) { + /* If no data is transferred, send the error code */ + rv = subrv; + } + break; + } + copy_offset += subrv; + pending_data -= subrv; + rv += subrv; + } + return rv; +} + +/* + * The implementation of nghttp2_recv_callback type. Here we read data + * from the network and write them in |buf|. The capacity of |buf| is + * |length| bytes. Returns the number of bytes stored in |buf|. See + * the documentation of nghttp2_recv_callback for the details. + */ +static ssize_t callback_recv(nghttp2_session *session, uint8_t *buf, + size_t length, int flags, void *user_data) +{ + struct sh2lib_handle *hd = user_data; + int rv; + rv = esp_tls_conn_read(hd->http2_tls, (char *)buf, (int)length); + if (rv < 0) { + if (rv == ESP_TLS_ERR_SSL_WANT_READ || rv == ESP_TLS_ERR_SSL_WANT_WRITE) { + rv = NGHTTP2_ERR_WOULDBLOCK; + } else { + rv = NGHTTP2_ERR_CALLBACK_FAILURE; + } + } else if (rv == 0) { + rv = NGHTTP2_ERR_EOF; + } + return rv; +} + +const char *sh2lib_frame_type_str(int type) +{ + switch (type) { + case NGHTTP2_HEADERS: + return "HEADERS"; + break; + case NGHTTP2_RST_STREAM: + return "RST_STREAM"; + break; + case NGHTTP2_GOAWAY: + return "GOAWAY"; + break; + case NGHTTP2_DATA: + return "DATA"; + break; + case NGHTTP2_SETTINGS: + return "SETTINGS"; + break; + case NGHTTP2_PUSH_PROMISE: + return "PUSH_PROMISE"; + break; + case NGHTTP2_PING: + return "PING"; + break; + default: + return "other"; + break; + } +} + +static int callback_on_frame_send(nghttp2_session *session, + const nghttp2_frame *frame, void *user_data) +{ + ESP_LOGD(TAG, "[frame-send] frame type %s", sh2lib_frame_type_str(frame->hd.type)); + switch (frame->hd.type) { + case NGHTTP2_HEADERS: + if (nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)) { + ESP_LOGD(TAG, "[frame-send] C ----------------------------> S (HEADERS)"); +#if DBG_FRAME_SEND + ESP_LOGD(TAG, "[frame-send] headers nv-len = %d", frame->headers.nvlen); + const nghttp2_nv *nva = frame->headers.nva; + size_t i; + for (i = 0; i < frame->headers.nvlen; ++i) { + ESP_LOGD(TAG, "[frame-send] %s : %s", nva[i].name, nva[i].value); + } +#endif + } + break; + } + return 0; +} + +static int callback_on_frame_recv(nghttp2_session *session, + const nghttp2_frame *frame, void *user_data) +{ + ESP_LOGD(TAG, "[frame-recv][sid: %d] frame type %s", frame->hd.stream_id, sh2lib_frame_type_str(frame->hd.type)); + if (frame->hd.type != NGHTTP2_DATA) { + return 0; + } + /* Subsequent processing only for data frame */ + sh2lib_frame_data_recv_cb_t data_recv_cb = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id); + if (data_recv_cb) { + struct sh2lib_handle *h2 = user_data; + (*data_recv_cb)(h2, NULL, 0, DATA_RECV_FRAME_COMPLETE); + } + return 0; +} + +static int callback_on_stream_close(nghttp2_session *session, int32_t stream_id, + uint32_t error_code, void *user_data) +{ + + ESP_LOGD(TAG, "[stream-close][sid %d]", stream_id); + sh2lib_frame_data_recv_cb_t data_recv_cb = nghttp2_session_get_stream_user_data(session, stream_id); + if (data_recv_cb) { + struct sh2lib_handle *h2 = user_data; + (*data_recv_cb)(h2, NULL, 0, DATA_RECV_RST_STREAM); + } + return 0; +} + +static int callback_on_data_chunk_recv(nghttp2_session *session, uint8_t flags, + int32_t stream_id, const uint8_t *data, + size_t len, void *user_data) +{ + sh2lib_frame_data_recv_cb_t data_recv_cb; + ESP_LOGD(TAG, "[data-chunk][sid:%d]", stream_id); + data_recv_cb = nghttp2_session_get_stream_user_data(session, stream_id); + if (data_recv_cb) { + ESP_LOGD(TAG, "[data-chunk] C <---------------------------- S (DATA chunk)" + "%lu bytes", + (unsigned long int)len); + struct sh2lib_handle *h2 = user_data; + (*data_recv_cb)(h2, (char *)data, len, 0); + /* TODO: What to do with the return value: look for pause/abort */ + } + return 0; +} + +static int callback_on_header(nghttp2_session *session, const nghttp2_frame *frame, + const uint8_t *name, size_t namelen, const uint8_t *value, + size_t valuelen, uint8_t flags, void *user_data) +{ + ESP_LOGD(TAG, "[hdr-recv][sid:%d] %s : %s", frame->hd.stream_id, name, value); + return 0; +} + +static int do_http2_connect(struct sh2lib_handle *hd) +{ + int ret; + nghttp2_session_callbacks *callbacks; + nghttp2_session_callbacks_new(&callbacks); + nghttp2_session_callbacks_set_send_callback(callbacks, callback_send); + nghttp2_session_callbacks_set_recv_callback(callbacks, callback_recv); + nghttp2_session_callbacks_set_on_frame_send_callback(callbacks, callback_on_frame_send); + nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, callback_on_frame_recv); + nghttp2_session_callbacks_set_on_stream_close_callback(callbacks, callback_on_stream_close); + nghttp2_session_callbacks_set_on_data_chunk_recv_callback(callbacks, callback_on_data_chunk_recv); + nghttp2_session_callbacks_set_on_header_callback(callbacks, callback_on_header); + ret = nghttp2_session_client_new(&hd->http2_sess, callbacks, hd); + if (ret != 0) { + ESP_LOGE(TAG, "[sh2-connect] New http2 session failed"); + nghttp2_session_callbacks_del(callbacks); + return -1; + } + nghttp2_session_callbacks_del(callbacks); + + /* Create the SETTINGS frame */ + ret = nghttp2_submit_settings(hd->http2_sess, NGHTTP2_FLAG_NONE, NULL, 0); + if (ret != 0) { + ESP_LOGE(TAG, "[sh2-connect] Submit settings failed"); + return -1; + } + return 0; +} + +int sh2lib_connect(struct sh2lib_config_t *cfg, struct sh2lib_handle *hd) +{ + memset(hd, 0, sizeof(*hd)); + + if (cfg == NULL) { + ESP_LOGE(TAG, "[sh2-connect] pointer to sh2lib configurations cannot be NULL"); + goto error; + } + + const char *proto[] = {"h2", NULL}; + esp_tls_cfg_t tls_cfg = { + .alpn_protos = proto, + .cacert_buf = cfg->cacert_buf, + .cacert_bytes = cfg->cacert_bytes, + .non_block = true, + .timeout_ms = 10 * 1000, + }; + if ((hd->http2_tls = esp_tls_conn_http_new(cfg->uri, &tls_cfg)) == NULL) { + ESP_LOGE(TAG, "[sh2-connect] esp-tls connection failed"); + goto error; + } + struct http_parser_url u; + http_parser_url_init(&u); + http_parser_parse_url(cfg->uri, strlen(cfg->uri), 0, &u); + hd->hostname = strndup(&cfg->uri[u.field_data[UF_HOST].off], u.field_data[UF_HOST].len); + + /* HTTP/2 Connection */ + if (do_http2_connect(hd) != 0) { + ESP_LOGE(TAG, "[sh2-connect] HTTP2 Connection failed with %s", cfg->uri); + goto error; + } + + return 0; +error: + sh2lib_free(hd); + return -1; +} + +void sh2lib_free(struct sh2lib_handle *hd) +{ + if (hd->http2_sess) { + nghttp2_session_del(hd->http2_sess); + hd->http2_sess = NULL; + } + if (hd->http2_tls) { + esp_tls_conn_destroy(hd->http2_tls); + hd->http2_tls = NULL; + } + if (hd->hostname) { + free(hd->hostname); + hd->hostname = NULL; + } +} + +int sh2lib_execute(struct sh2lib_handle *hd) +{ + int ret; + ret = nghttp2_session_send(hd->http2_sess); + if (ret != 0) { + ESP_LOGE(TAG, "[sh2-execute] HTTP2 session send failed %d", ret); + return -1; + } + + ret = nghttp2_session_recv(hd->http2_sess); + if (ret != 0) { + ESP_LOGE(TAG, "[sh2-execute] HTTP2 session recv failed %d", ret); + return -1; + } + + return 0; +} + +int sh2lib_do_get_with_nv(struct sh2lib_handle *hd, const nghttp2_nv *nva, size_t nvlen, sh2lib_frame_data_recv_cb_t recv_cb) +{ + int ret = nghttp2_submit_request(hd->http2_sess, NULL, nva, nvlen, NULL, recv_cb); + if (ret < 0) { + ESP_LOGE(TAG, "[sh2-do-get] HEADERS call failed"); + return -1; + } + return ret; +} + +int sh2lib_do_get(struct sh2lib_handle *hd, const char *path, sh2lib_frame_data_recv_cb_t recv_cb) +{ + const nghttp2_nv nva[] = { SH2LIB_MAKE_NV(":method", "GET"), + SH2LIB_MAKE_NV(":scheme", "https"), + SH2LIB_MAKE_NV(":authority", hd->hostname), + SH2LIB_MAKE_NV(":path", path), + }; + return sh2lib_do_get_with_nv(hd, nva, sizeof(nva) / sizeof(nva[0]), recv_cb); +} + +ssize_t sh2lib_data_provider_cb(nghttp2_session *session, int32_t stream_id, uint8_t *buf, + size_t length, uint32_t *data_flags, + nghttp2_data_source *source, void *user_data) +{ + struct sh2lib_handle *h2 = user_data; + sh2lib_putpost_data_cb_t data_cb = source->ptr; + return (*data_cb)(h2, (char *)buf, length, data_flags); +} + +int sh2lib_do_putpost_with_nv(struct sh2lib_handle *hd, const nghttp2_nv *nva, size_t nvlen, + sh2lib_putpost_data_cb_t send_cb, + sh2lib_frame_data_recv_cb_t recv_cb) +{ + + nghttp2_data_provider sh2lib_data_provider; + sh2lib_data_provider.read_callback = sh2lib_data_provider_cb; + sh2lib_data_provider.source.ptr = send_cb; + int ret = nghttp2_submit_request(hd->http2_sess, NULL, nva, nvlen, &sh2lib_data_provider, recv_cb); + if (ret < 0) { + ESP_LOGE(TAG, "[sh2-do-putpost] HEADERS call failed"); + return -1; + } + return ret; +} + +int sh2lib_do_post(struct sh2lib_handle *hd, const char *path, + sh2lib_putpost_data_cb_t send_cb, + sh2lib_frame_data_recv_cb_t recv_cb) +{ + const nghttp2_nv nva[] = { SH2LIB_MAKE_NV(":method", "POST"), + SH2LIB_MAKE_NV(":scheme", "https"), + SH2LIB_MAKE_NV(":authority", hd->hostname), + SH2LIB_MAKE_NV(":path", path), + }; + return sh2lib_do_putpost_with_nv(hd, nva, sizeof(nva) / sizeof(nva[0]), send_cb, recv_cb); +} + +int sh2lib_do_put(struct sh2lib_handle *hd, const char *path, + sh2lib_putpost_data_cb_t send_cb, + sh2lib_frame_data_recv_cb_t recv_cb) +{ + const nghttp2_nv nva[] = { SH2LIB_MAKE_NV(":method", "PUT"), + SH2LIB_MAKE_NV(":scheme", "https"), + SH2LIB_MAKE_NV(":authority", hd->hostname), + SH2LIB_MAKE_NV(":path", path), + }; + return sh2lib_do_putpost_with_nv(hd, nva, sizeof(nva) / sizeof(nva[0]), send_cb, recv_cb); +}