Format code

This commit is contained in:
Tomas Rezucha 2022-02-17 14:56:45 +01:00
parent 5362db237b
commit e651ec16c5
8 changed files with 1555 additions and 1537 deletions

View File

@ -9,6 +9,7 @@
#if 0 //High level layout for state machine #if 0 //High level layout for state machine
// *INDENT-OFF*
@startuml @startuml
[*] --> READ_MAGIC [*] --> READ_MAGIC
READ_MAGIC --> READ_MAGIC : READ LEN < 4 READ_MAGIC --> READ_MAGIC : READ LEN < 4
@ -34,7 +35,7 @@ PROCESS_BINARY --> ESP_OK : READ LEN = BIN_SIZE
ESP_OK --> [*] ESP_OK --> [*]
ESP_FAIL --> [*] ESP_FAIL --> [*]
@enduml @enduml
// *INDENT-OFF*
#endif #endif
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -1,448 +1,447 @@
/* /*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
#include <string.h> #include <string.h>
#include "esp_encrypted_img.h" #include "esp_encrypted_img.h"
#include <errno.h> #include <errno.h>
#include <esp_log.h> #include <esp_log.h>
#include <esp_err.h> #include <esp_err.h>
#include "mbedtls/pk.h" #include "mbedtls/pk.h"
#include "mbedtls/entropy.h" #include "mbedtls/entropy.h"
#include "mbedtls/ctr_drbg.h" #include "mbedtls/ctr_drbg.h"
#include "mbedtls/gcm.h" #include "mbedtls/gcm.h"
#include "sys/param.h" #include "sys/param.h"
static const char *TAG = "esp_encrypted_img"; static const char *TAG = "esp_encrypted_img";
typedef enum { typedef enum {
ESP_PRE_ENC_IMG_READ_MAGIC, ESP_PRE_ENC_IMG_READ_MAGIC,
ESP_PRE_ENC_IMG_READ_GCM, ESP_PRE_ENC_IMG_READ_GCM,
ESP_PRE_ENC_IMG_READ_IV, ESP_PRE_ENC_IMG_READ_IV,
ESP_PRE_ENC_IMG_READ_BINSIZE, ESP_PRE_ENC_IMG_READ_BINSIZE,
ESP_PRE_ENC_IMG_READ_AUTH, ESP_PRE_ENC_IMG_READ_AUTH,
ESP_PRE_ENC_IMG_READ_EXTRA_HEADER, ESP_PRE_ENC_IMG_READ_EXTRA_HEADER,
ESP_PRE_ENC_DATA_DECODE_STATE, ESP_PRE_ENC_DATA_DECODE_STATE,
} esp_encrypted_img_state; } esp_encrypted_img_state;
struct esp_encrypted_img_handle { struct esp_encrypted_img_handle {
const char *rsa_pem; const char *rsa_pem;
size_t rsa_len; size_t rsa_len;
uint32_t binary_file_len; uint32_t binary_file_len;
uint32_t binary_file_read; uint32_t binary_file_read;
char *gcm_key; char *gcm_key;
char *iv; char *iv;
char auth_tag[16]; char auth_tag[16];
esp_encrypted_img_state state; esp_encrypted_img_state state;
mbedtls_gcm_context gcm_ctx; mbedtls_gcm_context gcm_ctx;
size_t cache_buf_len; size_t cache_buf_len;
char *cache_buf; char *cache_buf;
}; };
#define GCM_KEY_SIZE 32 #define GCM_KEY_SIZE 32
#define MAGIC_SIZE 4 #define MAGIC_SIZE 4
#define ENC_GCM_KEY_SIZE 384 #define ENC_GCM_KEY_SIZE 384
#define IV_SIZE 16 #define IV_SIZE 16
#define BIN_SIZE_DATA 4 #define BIN_SIZE_DATA 4
#define AUTH_SIZE 16 #define AUTH_SIZE 16
#define RESERVED_HEADER 88 #define RESERVED_HEADER 88
typedef struct { typedef struct {
char magic[MAGIC_SIZE]; char magic[MAGIC_SIZE];
char enc_gcm[ENC_GCM_KEY_SIZE]; char enc_gcm[ENC_GCM_KEY_SIZE];
char iv[IV_SIZE]; char iv[IV_SIZE];
char bin_size[BIN_SIZE_DATA]; char bin_size[BIN_SIZE_DATA];
char auth[AUTH_SIZE]; char auth[AUTH_SIZE];
char extra_header[RESERVED_HEADER]; char extra_header[RESERVED_HEADER];
} pre_enc_bin_header; } pre_enc_bin_header;
#define HEADER_DATA_SIZE sizeof(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 // Magic Byte is created using command: echo -n "esp_encrypted_img" | sha256sum
static uint32_t esp_enc_img_magic = 0x0788b6cf; static uint32_t esp_enc_img_magic = 0x0788b6cf;
typedef struct esp_encrypted_img_handle esp_encrypted_img_t; typedef struct esp_encrypted_img_handle esp_encrypted_img_t;
static int decipher_gcm_key(char *enc_gcm, esp_encrypted_img_t *handle) static int decipher_gcm_key(char *enc_gcm, esp_encrypted_img_t *handle)
{ {
int ret = 1; int ret = 1;
handle->gcm_key = calloc(1, GCM_KEY_SIZE); handle->gcm_key = calloc(1, GCM_KEY_SIZE);
if (!handle->gcm_key) { if (!handle->gcm_key) {
return ESP_ERR_NO_MEM; return ESP_ERR_NO_MEM;
} }
size_t olen = 0; size_t olen = 0;
mbedtls_pk_context pk; mbedtls_pk_context pk;
mbedtls_entropy_context entropy; mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context ctr_drbg; mbedtls_ctr_drbg_context ctr_drbg;
const char *pers = "mbedtls_pk_encrypt"; const char *pers = "mbedtls_pk_encrypt";
mbedtls_ctr_drbg_init( &ctr_drbg ); mbedtls_ctr_drbg_init( &ctr_drbg );
mbedtls_entropy_init( &entropy ); mbedtls_entropy_init( &entropy );
mbedtls_pk_init( &pk ); mbedtls_pk_init( &pk );
if ((ret = mbedtls_ctr_drbg_seed( &ctr_drbg, mbedtls_entropy_func, if ((ret = mbedtls_ctr_drbg_seed( &ctr_drbg, mbedtls_entropy_func,
&entropy, (const unsigned char *) pers, &entropy, (const unsigned char *) pers,
strlen(pers))) != 0) { strlen(pers))) != 0) {
ESP_LOGE(TAG, "failed\n ! mbedtls_ctr_drbg_seed returned -0x%04x\n", (unsigned int) - ret); ESP_LOGE(TAG, "failed\n ! mbedtls_ctr_drbg_seed returned -0x%04x\n", (unsigned int) - ret);
free(handle->gcm_key); free(handle->gcm_key);
goto exit; goto exit;
} }
ESP_LOGI(TAG, "Reading RSA private key"); 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) { 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 ); ESP_LOGE(TAG, "failed\n ! mbedtls_pk_parse_keyfile returned -0x%04x\n", (unsigned int) - ret );
free(handle->gcm_key); free(handle->gcm_key);
goto exit; 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, 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 ) { mbedtls_ctr_drbg_random, &ctr_drbg ) ) != 0 ) {
ESP_LOGE(TAG, "failed\n ! mbedtls_pk_decrypt returned -0x%04x\n", (unsigned int) - ret ); ESP_LOGE(TAG, "failed\n ! mbedtls_pk_decrypt returned -0x%04x\n", (unsigned int) - ret );
free(handle->gcm_key); free(handle->gcm_key);
goto exit; goto exit;
} }
handle->cache_buf = realloc(handle->cache_buf, 16); handle->cache_buf = realloc(handle->cache_buf, 16);
if (!handle->cache_buf) { if (!handle->cache_buf) {
return ESP_ERR_NO_MEM; return ESP_ERR_NO_MEM;
} }
handle->state = ESP_PRE_ENC_IMG_READ_IV; handle->state = ESP_PRE_ENC_IMG_READ_IV;
handle->binary_file_read = 0; handle->binary_file_read = 0;
handle->cache_buf_len = 0; handle->cache_buf_len = 0;
handle->iv = calloc(1, IV_SIZE); handle->iv = calloc(1, IV_SIZE);
if (!handle->iv) { if (!handle->iv) {
return ESP_ERR_NO_MEM; return ESP_ERR_NO_MEM;
} }
exit: exit:
mbedtls_pk_free( &pk ); mbedtls_pk_free( &pk );
mbedtls_entropy_free( &entropy ); mbedtls_entropy_free( &entropy );
mbedtls_ctr_drbg_free( &ctr_drbg ); mbedtls_ctr_drbg_free( &ctr_drbg );
free((void *)handle->rsa_pem); free((void *)handle->rsa_pem);
return (ret); return (ret);
} }
esp_decrypt_handle_t esp_encrypted_img_decrypt_start(const esp_decrypt_cfg_t *cfg) esp_decrypt_handle_t esp_encrypted_img_decrypt_start(const esp_decrypt_cfg_t *cfg)
{ {
if (cfg == NULL || cfg->rsa_pub_key == NULL) { if (cfg == NULL || cfg->rsa_pub_key == NULL) {
ESP_LOGE(TAG, "esp_encrypted_img_decrypt_start : Invalid argument"); ESP_LOGE(TAG, "esp_encrypted_img_decrypt_start : Invalid argument");
return NULL; return NULL;
} }
ESP_LOGI(TAG, "Starting Decryption Process"); ESP_LOGI(TAG, "Starting Decryption Process");
esp_encrypted_img_t *handle = calloc(1, sizeof(esp_encrypted_img_t)); esp_encrypted_img_t *handle = calloc(1, sizeof(esp_encrypted_img_t));
if (!handle) { if (!handle) {
ESP_LOGE(TAG, "Couldn't allocate memory to handle"); ESP_LOGE(TAG, "Couldn't allocate memory to handle");
goto failure; goto failure;
} }
handle->rsa_pem = calloc(1, cfg->rsa_pub_key_len); handle->rsa_pem = calloc(1, cfg->rsa_pub_key_len);
if (!handle->rsa_pem) { if (!handle->rsa_pem) {
ESP_LOGE(TAG, "Couldn't allocate memory to handle->rsa_pem"); ESP_LOGE(TAG, "Couldn't allocate memory to handle->rsa_pem");
goto failure; goto failure;
} }
handle->cache_buf = calloc(1, ENC_GCM_KEY_SIZE); handle->cache_buf = calloc(1, ENC_GCM_KEY_SIZE);
if (!handle->cache_buf) { if (!handle->cache_buf) {
ESP_LOGE(TAG, "Couldn't allocate memory to handle->cache_buf"); ESP_LOGE(TAG, "Couldn't allocate memory to handle->cache_buf");
goto failure; goto failure;
} }
memcpy((void *)handle->rsa_pem, cfg->rsa_pub_key, cfg->rsa_pub_key_len); memcpy((void *)handle->rsa_pem, cfg->rsa_pub_key, cfg->rsa_pub_key_len);
handle->rsa_len = cfg->rsa_pub_key_len; handle->rsa_len = cfg->rsa_pub_key_len;
handle->state = ESP_PRE_ENC_IMG_READ_MAGIC; handle->state = ESP_PRE_ENC_IMG_READ_MAGIC;
esp_decrypt_handle_t *ctx = (esp_decrypt_handle_t *)handle; esp_decrypt_handle_t *ctx = (esp_decrypt_handle_t *)handle;
return ctx; return ctx;
failure: failure:
if (!handle) { if (!handle) {
return NULL; return NULL;
} }
if (handle->rsa_pem) { if (handle->rsa_pem) {
free((void *)handle->rsa_pem); free((void *)handle->rsa_pem);
} }
if (handle) { if (handle) {
free(handle); free(handle);
} }
return NULL; return NULL;
} }
static esp_err_t process_bin(esp_encrypted_img_t *handle, pre_enc_decrypt_arg_t *args, int curr_index) 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; size_t data_len = args->data_in_len;
handle->binary_file_read += data_len - curr_index; handle->binary_file_read += data_len - curr_index;
int dec_len = 0; int dec_len = 0;
if (handle->binary_file_read != handle->binary_file_len) { if (handle->binary_file_read != handle->binary_file_len) {
size_t copy_len = 0; size_t copy_len = 0;
if ((handle->cache_buf_len + (data_len - curr_index)) - (handle->cache_buf_len + (data_len - curr_index)) % 16 > 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); 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) { if (!args->data_out) {
return ESP_ERR_NO_MEM; return ESP_ERR_NO_MEM;
} }
} }
if (handle->cache_buf_len != 0) { if (handle->cache_buf_len != 0) {
copy_len = MIN(16 - handle->cache_buf_len, data_len - curr_index); 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); memcpy(handle->cache_buf + handle->cache_buf_len, args->data_in + curr_index, copy_len);
handle->cache_buf_len += copy_len; handle->cache_buf_len += copy_len;
if (handle->cache_buf_len != 16) { if (handle->cache_buf_len != 16) {
args->data_out_len = 0; args->data_out_len = 0;
return ESP_ERR_NOT_FINISHED; 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) { if (mbedtls_gcm_update(&handle->gcm_ctx, 16, (const unsigned char *)handle->cache_buf, (unsigned char *) args->data_out) != 0) {
return ESP_FAIL; return ESP_FAIL;
} }
dec_len = 16; dec_len = 16;
} }
handle->cache_buf_len = (data_len - curr_index - copy_len) % 16; handle->cache_buf_len = (data_len - curr_index - copy_len) % 16;
if (handle->cache_buf_len != 0) { if (handle->cache_buf_len != 0) {
data_len -= handle->cache_buf_len; data_len -= handle->cache_buf_len;
memcpy(handle->cache_buf, args->data_in + (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 (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) { 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; return ESP_FAIL;
} }
} }
args->data_out_len = dec_len + data_len - curr_index - copy_len; args->data_out_len = dec_len + data_len - curr_index - copy_len;
return ESP_ERR_NOT_FINISHED; return ESP_ERR_NOT_FINISHED;
} }
args->data_out = realloc(args->data_out, handle->cache_buf_len + data_len - curr_index); args->data_out = realloc(args->data_out, handle->cache_buf_len + data_len - curr_index);
if (!args->data_out) { if (!args->data_out) {
return ESP_ERR_NO_MEM; return ESP_ERR_NO_MEM;
} }
size_t copy_len = 0; size_t copy_len = 0;
copy_len = MIN(16 - handle->cache_buf_len, data_len - curr_index); 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); memcpy(handle->cache_buf + handle->cache_buf_len, args->data_in + curr_index, copy_len);
handle->cache_buf_len += 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) { 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; return ESP_FAIL;
} }
if (data_len - curr_index - copy_len > 0) { 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) { 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; return ESP_FAIL;
} }
} }
args->data_out_len = handle->cache_buf_len + data_len - copy_len - curr_index; args->data_out_len = handle->cache_buf_len + data_len - copy_len - curr_index;
handle->cache_buf_len = 0; handle->cache_buf_len = 0;
return ESP_OK; 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) 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_left = data_size - handle->binary_file_read;
const int data_recv = args->data_in_len - *curr_index; const int data_recv = args->data_in_len - *curr_index;
if (handle->state == ESP_PRE_ENC_IMG_READ_IV && handle->iv) { 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)); 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) { } 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)); memcpy(handle->auth_tag + handle->cache_buf_len, args->data_in + *curr_index, MIN(data_recv, data_left));
} else { } else {
memcpy(handle->cache_buf + handle->cache_buf_len, args->data_in + *curr_index, MIN(data_recv, data_left)); 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); handle->cache_buf_len += MIN(data_recv, data_left);
int temp = *curr_index; int temp = *curr_index;
*curr_index += MIN(data_recv, data_left); *curr_index += MIN(data_recv, data_left);
handle->binary_file_read += MIN(args->data_in_len - temp, 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) 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) { if (ctx == NULL || args == NULL || args->data_in == NULL) {
return ESP_ERR_INVALID_ARG; return ESP_ERR_INVALID_ARG;
} }
esp_encrypted_img_t *handle = (esp_encrypted_img_t *)ctx; esp_encrypted_img_t *handle = (esp_encrypted_img_t *)ctx;
if (handle == NULL) { if (handle == NULL) {
ESP_LOGE(TAG, "esp_encrypted_img_decrypt_data: Invalid argument"); ESP_LOGE(TAG, "esp_encrypted_img_decrypt_data: Invalid argument");
return ESP_ERR_INVALID_ARG; return ESP_ERR_INVALID_ARG;
} }
esp_err_t err; esp_err_t err;
int curr_index = 0; int curr_index = 0;
switch (handle->state) { switch (handle->state) {
case ESP_PRE_ENC_IMG_READ_MAGIC: case ESP_PRE_ENC_IMG_READ_MAGIC:
if (handle->cache_buf_len == 0 && (args->data_in_len - curr_index) >= MAGIC_SIZE) { if (handle->cache_buf_len == 0 && (args->data_in_len - curr_index) >= MAGIC_SIZE) {
uint32_t recv_magic = *(uint32_t *)args->data_in; uint32_t recv_magic = *(uint32_t *)args->data_in;
if (recv_magic != esp_enc_img_magic) { if (recv_magic != esp_enc_img_magic) {
ESP_LOGE(TAG, "Magic Verification failed"); ESP_LOGE(TAG, "Magic Verification failed");
free((void *)handle->rsa_pem); free((void *)handle->rsa_pem);
return ESP_FAIL; return ESP_FAIL;
} }
curr_index += MAGIC_SIZE; curr_index += MAGIC_SIZE;
} else { } else {
read_and_cache_data(handle, args, &curr_index, MAGIC_SIZE); read_and_cache_data(handle, args, &curr_index, MAGIC_SIZE);
if (handle->binary_file_read == MAGIC_SIZE) { if (handle->binary_file_read == MAGIC_SIZE) {
uint32_t recv_magic = *(uint32_t *)handle->cache_buf; uint32_t recv_magic = *(uint32_t *)handle->cache_buf;
if (recv_magic != esp_enc_img_magic) { if (recv_magic != esp_enc_img_magic) {
ESP_LOGE(TAG, "Magic Verification failed"); ESP_LOGE(TAG, "Magic Verification failed");
free((void *)handle->rsa_pem); free((void *)handle->rsa_pem);
return ESP_FAIL; return ESP_FAIL;
} }
handle->binary_file_read = 0; handle->binary_file_read = 0;
handle->cache_buf_len = 0; handle->cache_buf_len = 0;
} else { } else {
return ESP_ERR_NOT_FINISHED; return ESP_ERR_NOT_FINISHED;
} }
} }
ESP_LOGI(TAG, "Magic Verified"); ESP_LOGI(TAG, "Magic Verified");
handle->state = ESP_PRE_ENC_IMG_READ_GCM; handle->state = ESP_PRE_ENC_IMG_READ_GCM;
/* falls through */ /* falls through */
case ESP_PRE_ENC_IMG_READ_GCM: case ESP_PRE_ENC_IMG_READ_GCM:
if (handle->cache_buf_len == 0 && args->data_in_len - curr_index >= ENC_GCM_KEY_SIZE) { 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) { if (decipher_gcm_key(args->data_in + curr_index, handle) != 0) {
ESP_LOGE(TAG, "Unable to decipher GCM key"); ESP_LOGE(TAG, "Unable to decipher GCM key");
return ESP_FAIL; return ESP_FAIL;
} }
curr_index += ENC_GCM_KEY_SIZE; curr_index += ENC_GCM_KEY_SIZE;
} else { } else {
read_and_cache_data(handle, args, &curr_index, ENC_GCM_KEY_SIZE); read_and_cache_data(handle, args, &curr_index, ENC_GCM_KEY_SIZE);
if (handle->cache_buf_len == ENC_GCM_KEY_SIZE) { if (handle->cache_buf_len == ENC_GCM_KEY_SIZE) {
if (decipher_gcm_key(handle->cache_buf, handle) != 0) { if (decipher_gcm_key(handle->cache_buf, handle) != 0) {
ESP_LOGE(TAG, "Unable to decipher GCM key"); ESP_LOGE(TAG, "Unable to decipher GCM key");
return ESP_FAIL; return ESP_FAIL;
} }
} else { } else {
return ESP_ERR_NOT_FINISHED; return ESP_ERR_NOT_FINISHED;
} }
} }
/* falls through */ /* falls through */
case ESP_PRE_ENC_IMG_READ_IV: case ESP_PRE_ENC_IMG_READ_IV:
if (handle->cache_buf_len == 0 && args->data_in_len - curr_index >= IV_SIZE) { if (handle->cache_buf_len == 0 && args->data_in_len - curr_index >= IV_SIZE) {
memcpy(handle->iv, args->data_in + curr_index, IV_SIZE); memcpy(handle->iv, args->data_in + curr_index, IV_SIZE);
handle->binary_file_read = IV_SIZE; handle->binary_file_read = IV_SIZE;
curr_index += IV_SIZE; curr_index += IV_SIZE;
} else { } else {
read_and_cache_data(handle, args, &curr_index, IV_SIZE); read_and_cache_data(handle, args, &curr_index, IV_SIZE);
} }
if (handle->binary_file_read == IV_SIZE) { if (handle->binary_file_read == IV_SIZE) {
handle->state = ESP_PRE_ENC_IMG_READ_BINSIZE; handle->state = ESP_PRE_ENC_IMG_READ_BINSIZE;
handle->binary_file_read = 0; handle->binary_file_read = 0;
handle->cache_buf_len = 0; handle->cache_buf_len = 0;
mbedtls_gcm_init(&handle->gcm_ctx); 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) { 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); ESP_LOGE(TAG, "Error: mbedtls_gcm_set_key: -0x%04x\n", (unsigned int) - err);
return ESP_FAIL; return ESP_FAIL;
} }
free(handle->gcm_key); free(handle->gcm_key);
if (mbedtls_gcm_starts(&handle->gcm_ctx, MBEDTLS_GCM_DECRYPT, (const unsigned char *)handle->iv, IV_SIZE, NULL, 0) != 0) { 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); ESP_LOGE(TAG, "Error: mbedtls_gcm_starts: -0x%04x\n", (unsigned int) - err);
return ESP_FAIL; return ESP_FAIL;
} }
free(handle->iv); free(handle->iv);
handle->iv = NULL; handle->iv = NULL;
} else { } else {
return ESP_ERR_NOT_FINISHED; return ESP_ERR_NOT_FINISHED;
} }
/* falls through */ /* falls through */
case ESP_PRE_ENC_IMG_READ_BINSIZE: case ESP_PRE_ENC_IMG_READ_BINSIZE:
if (handle->cache_buf_len == 0 && (args->data_in_len - curr_index) >= BIN_SIZE_DATA) { 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); handle->binary_file_len = *(uint32_t *)(args->data_in + curr_index);
curr_index += BIN_SIZE_DATA; curr_index += BIN_SIZE_DATA;
} else { } else {
read_and_cache_data(handle, args, &curr_index, BIN_SIZE_DATA); read_and_cache_data(handle, args, &curr_index, BIN_SIZE_DATA);
if (handle->binary_file_read == BIN_SIZE_DATA) { if (handle->binary_file_read == BIN_SIZE_DATA) {
handle->binary_file_len = *(uint32_t *)handle->cache_buf; handle->binary_file_len = *(uint32_t *)handle->cache_buf;
} else { } else {
return ESP_ERR_NOT_FINISHED; return ESP_ERR_NOT_FINISHED;
} }
} }
handle->state = ESP_PRE_ENC_IMG_READ_AUTH; handle->state = ESP_PRE_ENC_IMG_READ_AUTH;
handle->binary_file_read = 0; handle->binary_file_read = 0;
handle->cache_buf_len = 0; handle->cache_buf_len = 0;
/* falls through */ /* falls through */
case ESP_PRE_ENC_IMG_READ_AUTH: case ESP_PRE_ENC_IMG_READ_AUTH:
if (handle->cache_buf_len == 0 && args->data_in_len - curr_index >= AUTH_SIZE) { 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); memcpy(handle->auth_tag, args->data_in + curr_index, AUTH_SIZE);
handle->binary_file_read = AUTH_SIZE; handle->binary_file_read = AUTH_SIZE;
curr_index += AUTH_SIZE; curr_index += AUTH_SIZE;
} else { } else {
read_and_cache_data(handle, args, &curr_index, AUTH_SIZE); read_and_cache_data(handle, args, &curr_index, AUTH_SIZE);
} }
if (handle->binary_file_read == AUTH_SIZE) { if (handle->binary_file_read == AUTH_SIZE) {
handle->state = ESP_PRE_ENC_IMG_READ_EXTRA_HEADER; handle->state = ESP_PRE_ENC_IMG_READ_EXTRA_HEADER;
handle->binary_file_read = 0; handle->binary_file_read = 0;
handle->cache_buf_len = 0; handle->cache_buf_len = 0;
} else { } else {
return ESP_ERR_NOT_FINISHED; return ESP_ERR_NOT_FINISHED;
} }
/* falls through */ /* falls through */
case ESP_PRE_ENC_IMG_READ_EXTRA_HEADER: case ESP_PRE_ENC_IMG_READ_EXTRA_HEADER: {
{ int temp = curr_index;
int temp = curr_index; curr_index += MIN(args->data_in_len - curr_index, RESERVED_HEADER - handle->binary_file_read);
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);
handle->binary_file_read += MIN(args->data_in_len - temp, RESERVED_HEADER - handle->binary_file_read); if (handle->binary_file_read == RESERVED_HEADER) {
if (handle->binary_file_read == RESERVED_HEADER) { handle->state = ESP_PRE_ENC_DATA_DECODE_STATE;
handle->state = ESP_PRE_ENC_DATA_DECODE_STATE; handle->binary_file_read = 0;
handle->binary_file_read = 0; handle->cache_buf_len = 0;
handle->cache_buf_len = 0; } else {
} else { return ESP_ERR_NOT_FINISHED;
return ESP_ERR_NOT_FINISHED; }
} }
} /* falls through */
/* falls through */ case ESP_PRE_ENC_DATA_DECODE_STATE:
case ESP_PRE_ENC_DATA_DECODE_STATE: err = process_bin(handle, args, curr_index);
err = process_bin(handle, args, curr_index); return err;
return err; }
} return ESP_OK;
return ESP_OK; }
}
esp_err_t esp_encrypted_img_decrypt_end(esp_decrypt_handle_t *ctx)
esp_err_t esp_encrypted_img_decrypt_end(esp_decrypt_handle_t *ctx) {
{ if (ctx == NULL) {
if (ctx == NULL) { return ESP_ERR_INVALID_ARG;
return ESP_ERR_INVALID_ARG; }
} esp_encrypted_img_t *handle = (esp_encrypted_img_t *)ctx;
esp_encrypted_img_t *handle = (esp_encrypted_img_t *)ctx; esp_err_t err = ESP_OK;
esp_err_t err = ESP_OK; if (handle == NULL) {
if (handle == NULL) { ESP_LOGE(TAG, "esp_encrypted_img_decrypt_data: Invalid argument");
ESP_LOGE(TAG, "esp_encrypted_img_decrypt_data: Invalid argument"); return ESP_ERR_INVALID_ARG;
return ESP_ERR_INVALID_ARG; }
} if (handle->state == ESP_PRE_ENC_DATA_DECODE_STATE) {
if (handle->state == ESP_PRE_ENC_DATA_DECODE_STATE) { if (handle->cache_buf_len != 0 || handle->binary_file_read != handle->binary_file_len) {
if (handle->cache_buf_len != 0 || handle->binary_file_read != handle->binary_file_len) { ESP_LOGE(TAG, "Invalid operation");
ESP_LOGE(TAG, "Invalid operation"); err = ESP_FAIL;
err = ESP_FAIL; goto exit;
goto exit; }
}
char *got_auth = calloc(1, AUTH_SIZE);
char *got_auth = calloc(1, AUTH_SIZE); if (!got_auth) {
if (!got_auth) { ESP_LOGE(TAG, "Unable to allocate memory");
ESP_LOGE(TAG, "Unable to allocate memory"); err = ESP_FAIL;
err = ESP_FAIL; goto exit;
goto exit; }
} err = mbedtls_gcm_finish(&handle->gcm_ctx, (unsigned char *)got_auth, AUTH_SIZE);
err = mbedtls_gcm_finish(&handle->gcm_ctx, (unsigned char *)got_auth, AUTH_SIZE); if (err != 0) {
if (err != 0) { ESP_LOGE(TAG, "Error: %d", err);
ESP_LOGE(TAG, "Error: %d", err); free(got_auth);
free(got_auth); err = ESP_FAIL;
err = ESP_FAIL; goto exit;
goto exit; }
} if (memcmp(got_auth, handle->auth_tag, AUTH_SIZE) != 0) {
if (memcmp(got_auth, handle->auth_tag, AUTH_SIZE) != 0) { ESP_LOGE(TAG, "Invalid Auth");
ESP_LOGE(TAG, "Invalid Auth"); free(got_auth);
free(got_auth); err = ESP_FAIL;
err = ESP_FAIL; goto exit;
goto exit; }
}
free(got_auth);
free(got_auth); }
} err = ESP_OK;
err = ESP_OK; exit:
exit: mbedtls_gcm_free(&handle->gcm_ctx);
mbedtls_gcm_free(&handle->gcm_ctx); free(handle->cache_buf);
free(handle->cache_buf); free(handle);
free(handle); return err;
return err; }
}

View File

@ -1,119 +1,119 @@
/* /*
* SPDX-FileCopyrightText: 2018-2022 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2018-2022 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
#include <expat.h> #include <expat.h>
#include <string.h> #include <string.h>
#include "unity.h" #include "unity.h"
typedef struct { typedef struct {
int depth; int depth;
char output[512]; char output[512];
int output_off; int output_off;
} user_data_t; } user_data_t;
static void insert_space(user_data_t *user_data) static void insert_space(user_data_t *user_data)
{ {
const char align_str[] = " "; const char align_str[] = " ";
TEST_ASSERT(sizeof(user_data->output) >= user_data->output_off); TEST_ASSERT(sizeof(user_data->output) >= user_data->output_off);
user_data->output[user_data->output_off++] = '\n'; user_data->output[user_data->output_off++] = '\n';
for (int i = 0; i < user_data->depth; i++) { for (int i = 0; i < user_data->depth; i++) {
for (int j = 0; j < strlen(align_str); ++j) { for (int j = 0; j < strlen(align_str); ++j) {
TEST_ASSERT(sizeof(user_data->output) >= user_data->output_off); TEST_ASSERT(sizeof(user_data->output) >= user_data->output_off);
user_data->output[user_data->output_off++] = align_str[j]; 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) static void XMLCALL start_element(void *userData, const XML_Char *name, const XML_Char **atts)
{ {
user_data_t *user_data = (user_data_t *) userData; user_data_t *user_data = (user_data_t *) userData;
insert_space(user_data); insert_space(user_data);
const int ret = snprintf(user_data->output + user_data->output_off, const int ret = snprintf(user_data->output + user_data->output_off,
sizeof(user_data->output) - user_data->output_off, sizeof(user_data->output) - user_data->output_off,
"<%s>", name); "<%s>", name);
TEST_ASSERT_EQUAL(strlen(name) + 2, ret); // 2 are the tag characters: "<>" TEST_ASSERT_EQUAL(strlen(name) + 2, ret); // 2 are the tag characters: "<>"
user_data->output_off += ret; user_data->output_off += ret;
++user_data->depth; ++user_data->depth;
} }
static void XMLCALL end_element(void *userData, const XML_Char *name) static void XMLCALL end_element(void *userData, const XML_Char *name)
{ {
user_data_t *user_data = (user_data_t *) userData; user_data_t *user_data = (user_data_t *) userData;
--user_data->depth; --user_data->depth;
insert_space(user_data); insert_space(user_data);
int ret = snprintf(user_data->output + user_data->output_off, sizeof(user_data->output) - user_data->output_off, int ret = snprintf(user_data->output + user_data->output_off, sizeof(user_data->output) - user_data->output_off,
"</%s>", name); "</%s>", name);
TEST_ASSERT_EQUAL(strlen(name) + 3, ret); // 3 are the tag characters: "</>" TEST_ASSERT_EQUAL(strlen(name) + 3, ret); // 3 are the tag characters: "</>"
user_data->output_off += ret; user_data->output_off += ret;
} }
static void data_handler(void *userData, const XML_Char *s, int len) static void data_handler(void *userData, const XML_Char *s, int len)
{ {
user_data_t *user_data = (user_data_t *) userData; user_data_t *user_data = (user_data_t *) userData;
insert_space(user_data); insert_space(user_data);
// s is not zero-terminated // s is not zero-terminated
char tmp_str[len+1]; char tmp_str[len + 1];
strlcpy(tmp_str, s, 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, int ret = snprintf(user_data->output + user_data->output_off, sizeof(user_data->output) - user_data->output_off,
"%s", tmp_str); "%s", tmp_str);
TEST_ASSERT_EQUAL(strlen(tmp_str), ret); TEST_ASSERT_EQUAL(strlen(tmp_str), ret);
user_data->output_off += ret; user_data->output_off += ret;
} }
TEST_CASE("Expat parses XML", "[expat]") TEST_CASE("Expat parses XML", "[expat]")
{ {
const char test_in[] = "<html><title>Page title</title><body><h>header</h><ol><li>A</li>"\ const char test_in[] = "<html><title>Page title</title><body><h>header</h><ol><li>A</li>"\
"<li>B</li><li>C</li></ol></body></html>"; "<li>B</li><li>C</li></ol></body></html>";
const char test_expected[] = "\n"\ const char test_expected[] = "\n"\
"<html>\n"\ "<html>\n"\
" <title>\n"\ " <title>\n"\
" Page title\n"\ " Page title\n"\
" </title>\n"\ " </title>\n"\
" <body>\n"\ " <body>\n"\
" <h>\n"\ " <h>\n"\
" header\n"\ " header\n"\
" </h>\n"\ " </h>\n"\
" <ol>\n"\ " <ol>\n"\
" <li>\n"\ " <li>\n"\
" A\n"\ " A\n"\
" </li>\n"\ " </li>\n"\
" <li>\n"\ " <li>\n"\
" B\n"\ " B\n"\
" </li>\n"\ " </li>\n"\
" <li>\n"\ " <li>\n"\
" C\n"\ " C\n"\
" </li>\n"\ " </li>\n"\
" </ol>\n"\ " </ol>\n"\
" </body>\n"\ " </body>\n"\
"</html>"; "</html>";
user_data_t user_data = { user_data_t user_data = {
.depth = 0, .depth = 0,
.output = { '\0' }, .output = { '\0' },
.output_off = 0 .output_off = 0
}; };
XML_Parser parser = XML_ParserCreate(NULL); XML_Parser parser = XML_ParserCreate(NULL);
XML_SetUserData(parser, &user_data); XML_SetUserData(parser, &user_data);
XML_SetElementHandler(parser, start_element, end_element); XML_SetElementHandler(parser, start_element, end_element);
XML_SetCharacterDataHandler(parser, data_handler); XML_SetCharacterDataHandler(parser, data_handler);
TEST_ASSERT_NOT_EQUAL(XML_STATUS_ERROR, XML_Parse(parser, test_in, strlen(test_in), 1)); TEST_ASSERT_NOT_EQUAL(XML_STATUS_ERROR, XML_Parse(parser, test_in, strlen(test_in), 1));
XML_ParserFree(parser); XML_ParserFree(parser);
TEST_ASSERT_EQUAL(0, user_data.depth); // all closing tags have been found 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(strlen(test_expected), strlen(user_data.output));
TEST_ASSERT_EQUAL_STRING(test_expected, user_data.output); TEST_ASSERT_EQUAL_STRING(test_expected, user_data.output);
} }

View File

@ -1,106 +1,106 @@
/* /*
* Copyright (c) 2010 Serge A. Zaitsev * Copyright (c) 2010 Serge A. Zaitsev
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights * in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is * copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions: * furnished to do so, subject to the following conditions:
* *
* The above copyright notice and this permission notice shall be included in * The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software. * all copies or substantial portions of the Software.
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * 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 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
/** /**
* @file jsmn.h * @file jsmn.h
* @brief Definition of the JSMN (Jasmine) JSON parser. * @brief Definition of the JSMN (Jasmine) JSON parser.
* *
* For more information on JSMN: * For more information on JSMN:
* @see http://zserge.com/jsmn.html * @see http://zserge.com/jsmn.html
*/ */
#ifndef __JSMN_H_ #ifndef __JSMN_H_
#define __JSMN_H_ #define __JSMN_H_
#include <stddef.h> #include <stddef.h>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
/** /**
* JSON type identifier. Basic types are: * JSON type identifier. Basic types are:
* o Object * o Object
* o Array * o Array
* o String * o String
* o Other primitive: number, boolean (true/false) or null * o Other primitive: number, boolean (true/false) or null
*/ */
typedef enum { typedef enum {
JSMN_UNDEFINED = 0, JSMN_UNDEFINED = 0,
JSMN_OBJECT = 1, JSMN_OBJECT = 1,
JSMN_ARRAY = 2, JSMN_ARRAY = 2,
JSMN_STRING = 3, JSMN_STRING = 3,
JSMN_PRIMITIVE = 4 JSMN_PRIMITIVE = 4
} jsmntype_t; } jsmntype_t;
enum jsmnerr { enum jsmnerr {
/* Not enough tokens were provided */ /* Not enough tokens were provided */
JSMN_ERROR_NOMEM = -1, JSMN_ERROR_NOMEM = -1,
/* Invalid character inside JSON string */ /* Invalid character inside JSON string */
JSMN_ERROR_INVAL = -2, JSMN_ERROR_INVAL = -2,
/* The string is not a full JSON packet, more bytes expected */ /* The string is not a full JSON packet, more bytes expected */
JSMN_ERROR_PART = -3 JSMN_ERROR_PART = -3
}; };
/** /**
* JSON token description. * JSON token description.
* @param type type (object, array, string etc.) * @param type type (object, array, string etc.)
* @param start start position in JSON data string * @param start start position in JSON data string
* @param end end position in JSON data string * @param end end position in JSON data string
*/ */
typedef struct { typedef struct {
jsmntype_t type; jsmntype_t type;
int start; int start;
int end; int end;
int size; int size;
#ifdef JSMN_PARENT_LINKS #ifdef JSMN_PARENT_LINKS
int parent; int parent;
#endif #endif
} jsmntok_t; } jsmntok_t;
/** /**
* JSON parser. Contains an array of token blocks available. Also stores * JSON parser. Contains an array of token blocks available. Also stores
* the string being parsed now and current position in that string * the string being parsed now and current position in that string
*/ */
typedef struct { typedef struct {
unsigned int pos; /* offset in the JSON string */ unsigned int pos; /* offset in the JSON string */
unsigned int toknext; /* next token to allocate */ unsigned int toknext; /* next token to allocate */
int toksuper; /* superior token node, e.g parent object or array */ int toksuper; /* superior token node, e.g parent object or array */
} jsmn_parser; } jsmn_parser;
/** /**
* Create JSON parser over an array of tokens * Create JSON parser over an array of tokens
*/ */
void jsmn_init(jsmn_parser *parser); void jsmn_init(jsmn_parser *parser);
/** /**
* Run JSON parser. It parses a JSON data string into and array of tokens, each describing * Run JSON parser. It parses a JSON data string into and array of tokens, each describing
* a single JSON object. * a single JSON object.
*/ */
int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, int jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
jsmntok_t *tokens, unsigned int num_tokens); jsmntok_t *tokens, unsigned int num_tokens);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif
#endif /* __JSMN_H_ */ #endif /* __JSMN_H_ */

View File

@ -1,340 +1,356 @@
/* /*
* Copyright (c) 2010 Serge A. Zaitsev * Copyright (c) 2010 Serge A. Zaitsev
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights * in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is * copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions: * furnished to do so, subject to the following conditions:
* *
* The above copyright notice and this permission notice shall be included in * The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software. * all copies or substantial portions of the Software.
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * 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 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
/** /**
* @file jsmn.c * @file jsmn.c
* @brief Implementation of the JSMN (Jasmine) JSON parser. * @brief Implementation of the JSMN (Jasmine) JSON parser.
* *
* For more information on JSMN: * For more information on JSMN:
* @see http://zserge.com/jsmn.html * @see http://zserge.com/jsmn.html
*/ */
#include "jsmn.h" #include "jsmn.h"
/** /**
* Allocates a fresh unused token from the token pull. * Allocates a fresh unused token from the token pull.
*/ */
static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser,
jsmntok_t *tokens, size_t num_tokens) { jsmntok_t *tokens, size_t num_tokens)
jsmntok_t *tok; {
if (parser->toknext >= num_tokens) { jsmntok_t *tok;
return NULL; if (parser->toknext >= num_tokens) {
} return NULL;
tok = &tokens[parser->toknext++]; }
tok->start = tok->end = -1; tok = &tokens[parser->toknext++];
tok->size = 0; tok->start = tok->end = -1;
#ifdef JSMN_PARENT_LINKS tok->size = 0;
tok->parent = -1; #ifdef JSMN_PARENT_LINKS
#endif tok->parent = -1;
return tok; #endif
} return tok;
}
/**
* Fills token type and boundaries. /**
*/ * Fills token type and boundaries.
static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type, */
int start, int end) { static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type,
token->type = type; int start, int end)
token->start = start; {
token->end = end; token->type = type;
token->size = 0; 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, * Fills next available token with JSON primitive.
size_t len, jsmntok_t *tokens, size_t num_tokens) { */
jsmntok_t *token; static int jsmn_parse_primitive(jsmn_parser *parser, const char *js,
int start; size_t len, jsmntok_t *tokens, size_t num_tokens)
{
start = parser->pos; jsmntok_t *token;
int start;
for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
switch (js[parser->pos]) { start = parser->pos;
#ifndef JSMN_STRICT
/* In strict mode primitive must be followed by "," or "}" or "]" */ for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
case ':': switch (js[parser->pos]) {
#endif #ifndef JSMN_STRICT
case '\t' : case '\r' : case '\n' : case ' ' : /* In strict mode primitive must be followed by "," or "}" or "]" */
case ',' : case ']' : case '}' : case ':':
goto found; #endif
} case '\t' : case '\r' : case '\n' : case ' ' :
if (js[parser->pos] < 32 || js[parser->pos] >= 127) { case ',' : case ']' : case '}' :
parser->pos = start; goto found;
return JSMN_ERROR_INVAL; }
} if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
} parser->pos = start;
#ifdef JSMN_STRICT return JSMN_ERROR_INVAL;
/* In strict mode primitive must be followed by a comma/object/array */ }
parser->pos = start; }
return JSMN_ERROR_PART; #ifdef JSMN_STRICT
#endif /* In strict mode primitive must be followed by a comma/object/array */
parser->pos = start;
found: return JSMN_ERROR_PART;
if (tokens == NULL) { #endif
parser->pos--;
return 0; found:
} if (tokens == NULL) {
token = jsmn_alloc_token(parser, tokens, num_tokens); parser->pos--;
if (token == NULL) { return 0;
parser->pos = start; }
return JSMN_ERROR_NOMEM; token = jsmn_alloc_token(parser, tokens, num_tokens);
} if (token == NULL) {
jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); parser->pos = start;
#ifdef JSMN_PARENT_LINKS return JSMN_ERROR_NOMEM;
token->parent = parser->toksuper; }
#endif jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
parser->pos--; #ifdef JSMN_PARENT_LINKS
return 0; 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) { * Fills next token with JSON string.
jsmntok_t *token; */
static int jsmn_parse_string(jsmn_parser *parser, const char *js,
int start = parser->pos; size_t len, jsmntok_t *tokens, size_t num_tokens)
{
parser->pos++; jsmntok_t *token;
/* Skip starting quote */ int start = parser->pos;
for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
char c = js[parser->pos]; parser->pos++;
/* Quote: end of string */ /* Skip starting quote */
if (c == '\"') { for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
if (tokens == NULL) { char c = js[parser->pos];
return 0;
} /* Quote: end of string */
token = jsmn_alloc_token(parser, tokens, num_tokens); if (c == '\"') {
if (token == NULL) { if (tokens == NULL) {
parser->pos = start; return 0;
return JSMN_ERROR_NOMEM; }
} token = jsmn_alloc_token(parser, tokens, num_tokens);
jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos); if (token == NULL) {
#ifdef JSMN_PARENT_LINKS parser->pos = start;
token->parent = parser->toksuper; return JSMN_ERROR_NOMEM;
#endif }
return 0; jsmn_fill_token(token, JSMN_STRING, start + 1, parser->pos);
} #ifdef JSMN_PARENT_LINKS
token->parent = parser->toksuper;
/* Backslash: Quoted symbol expected */ #endif
if (c == '\\' && parser->pos + 1 < len) { return 0;
int i; }
parser->pos++;
switch (js[parser->pos]) { /* Backslash: Quoted symbol expected */
/* Allowed escaped symbols */ if (c == '\\' && parser->pos + 1 < len) {
case '\"': case '/' : case '\\' : case 'b' : int i;
case 'f' : case 'r' : case 'n' : case 't' : parser->pos++;
break; switch (js[parser->pos]) {
/* Allows escaped symbol \uXXXX */ /* Allowed escaped symbols */
case 'u': case '\"': case '/' : case '\\' : case 'b' :
parser->pos++; case 'f' : case 'r' : case 'n' : case 't' :
for(i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) { break;
/* If it isn't a hex character we have an error */ /* Allows escaped symbol \uXXXX */
if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */ case 'u':
(js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */ parser->pos++;
(js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */ for (i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) {
parser->pos = start; /* If it isn't a hex character we have an error */
return JSMN_ERROR_INVAL; if (!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */
} (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */
parser->pos++; (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */
} parser->pos = start;
parser->pos--; return JSMN_ERROR_INVAL;
break; }
/* Unexpected symbol */ parser->pos++;
default: }
parser->pos = start; parser->pos--;
return JSMN_ERROR_INVAL; break;
} /* Unexpected symbol */
} default:
} parser->pos = start;
parser->pos = start; return JSMN_ERROR_INVAL;
return JSMN_ERROR_PART; }
} }
}
/** parser->pos = start;
* Parse JSON string and fill tokens. return JSMN_ERROR_PART;
*/ }
int jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
jsmntok_t *tokens, unsigned int num_tokens) { /**
int r; * Parse JSON string and fill tokens.
int i; */
jsmntok_t *token; int jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
int count = parser->toknext; jsmntok_t *tokens, unsigned int num_tokens)
{
for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { int r;
char c; int i;
jsmntype_t type; jsmntok_t *token;
int count = parser->toknext;
c = js[parser->pos];
switch (c) { for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
case '{': case '[': char c;
count++; jsmntype_t type;
if (tokens == NULL) {
break; c = js[parser->pos];
} switch (c) {
token = jsmn_alloc_token(parser, tokens, num_tokens); case '{': case '[':
if (token == NULL) count++;
return JSMN_ERROR_NOMEM; if (tokens == NULL) {
if (parser->toksuper != -1) { break;
tokens[parser->toksuper].size++; }
#ifdef JSMN_PARENT_LINKS token = jsmn_alloc_token(parser, tokens, num_tokens);
token->parent = parser->toksuper; if (token == NULL) {
#endif return JSMN_ERROR_NOMEM;
} }
token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); if (parser->toksuper != -1) {
token->start = parser->pos; tokens[parser->toksuper].size++;
parser->toksuper = parser->toknext - 1; #ifdef JSMN_PARENT_LINKS
break; token->parent = parser->toksuper;
case '}': case ']': #endif
if (tokens == NULL) }
break; token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); token->start = parser->pos;
#ifdef JSMN_PARENT_LINKS parser->toksuper = parser->toknext - 1;
if (parser->toknext < 1) { break;
return JSMN_ERROR_INVAL; case '}': case ']':
} if (tokens == NULL) {
token = &tokens[parser->toknext - 1]; break;
for (;;) { }
if (token->start != -1 && token->end == -1) { type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
if (token->type != type) { #ifdef JSMN_PARENT_LINKS
return JSMN_ERROR_INVAL; if (parser->toknext < 1) {
} return JSMN_ERROR_INVAL;
token->end = parser->pos + 1; }
parser->toksuper = token->parent; token = &tokens[parser->toknext - 1];
break; for (;;) {
} if (token->start != -1 && token->end == -1) {
if (token->parent == -1) { if (token->type != type) {
break; return JSMN_ERROR_INVAL;
} }
token = &tokens[token->parent]; token->end = parser->pos + 1;
} parser->toksuper = token->parent;
#else break;
for (i = parser->toknext - 1; i >= 0; i--) { }
token = &tokens[i]; if (token->parent == -1) {
if (token->start != -1 && token->end == -1) { break;
if (token->type != type) { }
return JSMN_ERROR_INVAL; token = &tokens[token->parent];
} }
parser->toksuper = -1; #else
token->end = parser->pos + 1; for (i = parser->toknext - 1; i >= 0; i--) {
break; token = &tokens[i];
} if (token->start != -1 && token->end == -1) {
} if (token->type != type) {
/* Error if unmatched closing bracket */ return JSMN_ERROR_INVAL;
if (i == -1) return JSMN_ERROR_INVAL; }
for (; i >= 0; i--) { parser->toksuper = -1;
token = &tokens[i]; token->end = parser->pos + 1;
if (token->start != -1 && token->end == -1) { break;
parser->toksuper = i; }
break; }
} /* Error if unmatched closing bracket */
} if (i == -1) {
#endif return JSMN_ERROR_INVAL;
break; }
case '\"': for (; i >= 0; i--) {
r = jsmn_parse_string(parser, js, len, tokens, num_tokens); token = &tokens[i];
if (r < 0) return r; if (token->start != -1 && token->end == -1) {
count++; parser->toksuper = i;
if (parser->toksuper != -1 && tokens != NULL) break;
tokens[parser->toksuper].size++; }
break; }
case '\t' : case '\r' : case '\n' : case ' ': #endif
break; break;
case ':': case '\"':
parser->toksuper = parser->toknext - 1; r = jsmn_parse_string(parser, js, len, tokens, num_tokens);
break; if (r < 0) {
case ',': return r;
if (tokens != NULL && parser->toksuper != -1 && }
tokens[parser->toksuper].type != JSMN_ARRAY && count++;
tokens[parser->toksuper].type != JSMN_OBJECT) { if (parser->toksuper != -1 && tokens != NULL) {
#ifdef JSMN_PARENT_LINKS tokens[parser->toksuper].size++;
parser->toksuper = tokens[parser->toksuper].parent; }
#else break;
for (i = parser->toknext - 1; i >= 0; i--) { case '\t' : case '\r' : case '\n' : case ' ':
if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) { break;
if (tokens[i].start != -1 && tokens[i].end == -1) { case ':':
parser->toksuper = i; parser->toksuper = parser->toknext - 1;
break; break;
} case ',':
} if (tokens != NULL && parser->toksuper != -1 &&
} tokens[parser->toksuper].type != JSMN_ARRAY &&
#endif tokens[parser->toksuper].type != JSMN_OBJECT) {
} #ifdef JSMN_PARENT_LINKS
break; parser->toksuper = tokens[parser->toksuper].parent;
#ifdef JSMN_STRICT #else
/* In strict mode primitives are: numbers and booleans */ for (i = parser->toknext - 1; i >= 0; i--) {
case '-': case '0': case '1' : case '2': case '3' : case '4': if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) {
case '5': case '6': case '7' : case '8': case '9': if (tokens[i].start != -1 && tokens[i].end == -1) {
case 't': case 'f': case 'n' : parser->toksuper = i;
/* And they must not be keys of the object */ break;
if (tokens != NULL && parser->toksuper != -1) { }
jsmntok_t *t = &tokens[parser->toksuper]; }
if (t->type == JSMN_OBJECT || }
(t->type == JSMN_STRING && t->size != 0)) { #endif
return JSMN_ERROR_INVAL; }
} break;
} #ifdef JSMN_STRICT
#else /* In strict mode primitives are: numbers and booleans */
/* In non-strict mode every unquoted value is a primitive */ case '-': case '0': case '1' : case '2': case '3' : case '4':
default: case '5': case '6': case '7' : case '8': case '9':
#endif case 't': case 'f': case 'n' :
r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens); /* And they must not be keys of the object */
if (r < 0) return r; if (tokens != NULL && parser->toksuper != -1) {
count++; jsmntok_t *t = &tokens[parser->toksuper];
if (parser->toksuper != -1 && tokens != NULL) if (t->type == JSMN_OBJECT ||
tokens[parser->toksuper].size++; (t->type == JSMN_STRING && t->size != 0)) {
break; return JSMN_ERROR_INVAL;
}
#ifdef JSMN_STRICT }
/* Unexpected char in strict mode */ #else
default: /* In non-strict mode every unquoted value is a primitive */
return JSMN_ERROR_INVAL; default:
#endif #endif
} r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
} if (r < 0) {
return r;
if (tokens != NULL) { }
for (i = parser->toknext - 1; i >= 0; i--) { count++;
/* Unmatched opened object or array */ if (parser->toksuper != -1 && tokens != NULL) {
if (tokens[i].start != -1 && tokens[i].end == -1) { tokens[parser->toksuper].size++;
return JSMN_ERROR_PART; }
} break;
}
} #ifdef JSMN_STRICT
/* Unexpected char in strict mode */
return count; default:
} return JSMN_ERROR_INVAL;
#endif
/** }
* Creates a new parser based over a given buffer with an array of tokens }
* available.
*/ if (tokens != NULL) {
void jsmn_init(jsmn_parser *parser) { for (i = parser->toknext - 1; i >= 0; i--) {
parser->pos = 0; /* Unmatched opened object or array */
parser->toknext = 0; if (tokens[i].start != -1 && tokens[i].end == -1) {
parser->toksuper = -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;
}

View File

@ -1,124 +1,126 @@
/* /*
* SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
#include "unity.h" #include "unity.h"
#include "sodium/crypto_hash_sha256.h" #include "sodium/crypto_hash_sha256.h"
#include "sodium/crypto_hash_sha512.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 /* 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. output to be compared to the matching .exp file.
We don't do this automatically yet, maybe once we have more options for We don't do this automatically yet, maybe once we have more options for
internal filesystem storage. internal filesystem storage.
*/ */
extern int aead_chacha20poly1305_xmain(void); extern int aead_chacha20poly1305_xmain(void);
TEST_CASE("aead_chacha20poly1305 test vectors", "[libsodium]") TEST_CASE("aead_chacha20poly1305 test vectors", "[libsodium]")
{ {
printf("Running aead_chacha20poly1305\n"); printf("Running aead_chacha20poly1305\n");
TEST_ASSERT_EQUAL(0, aead_chacha20poly1305_xmain()); TEST_ASSERT_EQUAL(0, aead_chacha20poly1305_xmain());
} }
extern int chacha20_xmain(void); extern int chacha20_xmain(void);
TEST_CASE("chacha20 test vectors", "[libsodium]") TEST_CASE("chacha20 test vectors", "[libsodium]")
{ {
printf("Running chacha20\n"); printf("Running chacha20\n");
TEST_ASSERT_EQUAL(0, chacha20_xmain()); TEST_ASSERT_EQUAL(0, chacha20_xmain());
} }
extern int box_xmain(void); extern int box_xmain(void);
extern int box2_xmain(void); extern int box2_xmain(void);
TEST_CASE("box tests", "[libsodium]") TEST_CASE("box tests", "[libsodium]")
{ {
printf("Running box\n"); printf("Running box\n");
TEST_ASSERT_EQUAL(0, box_xmain()); TEST_ASSERT_EQUAL(0, box_xmain());
printf("Running box2\n"); printf("Running box2\n");
TEST_ASSERT_EQUAL(0, box2_xmain()); TEST_ASSERT_EQUAL(0, box2_xmain());
} }
extern int ed25519_convert_xmain(void); extern int ed25519_convert_xmain(void);
TEST_CASE("ed25519_convert tests", "[libsodium][timeout=60]") TEST_CASE("ed25519_convert tests", "[libsodium][timeout=60]")
{ {
printf("Running ed25519_convert\n"); printf("Running ed25519_convert\n");
TEST_ASSERT_EQUAL(0, ed25519_convert_xmain() ); TEST_ASSERT_EQUAL(0, ed25519_convert_xmain() );
} }
extern int sign_xmain(void); extern int sign_xmain(void);
TEST_CASE("sign tests", "[libsodium]") TEST_CASE("sign tests", "[libsodium]")
{ {
printf("Running sign\n"); printf("Running sign\n");
TEST_ASSERT_EQUAL(0, sign_xmain() ); TEST_ASSERT_EQUAL(0, sign_xmain() );
} }
extern int hash_xmain(void); extern int hash_xmain(void);
TEST_CASE("hash tests", "[libsodium]") TEST_CASE("hash tests", "[libsodium]")
{ {
printf("Running hash\n"); printf("Running hash\n");
TEST_ASSERT_EQUAL(0, hash_xmain() ); TEST_ASSERT_EQUAL(0, hash_xmain() );
} }
TEST_CASE("sha256 sanity check", "[libsodium]") TEST_CASE("sha256 sanity check", "[libsodium]")
{ {
const uint8_t expected[] = { 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, const uint8_t expected[] = { 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41,
0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23, 0xb0, 0x03, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23, 0xb0, 0x03,
0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, 0xb4, 0x10, 0xff, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, 0xb4, 0x10, 0xff,
0x61, 0xf2, 0x00, 0x15, 0xad, }; 0x61, 0xf2, 0x00, 0x15, 0xad,
uint8_t calculated[32]; };
crypto_hash_sha256_state state; uint8_t calculated[32];
crypto_hash_sha256_state state;
const uint8_t *in = (const uint8_t *)"abc";
const size_t inlen = 3; const uint8_t *in = (const uint8_t *)"abc";
const size_t inlen = 3;
// One-liner version
crypto_hash_sha256(calculated, in, inlen); // One-liner version
TEST_ASSERT_EQUAL(sizeof(calculated), sizeof(expected)); crypto_hash_sha256(calculated, in, inlen);
TEST_ASSERT_EQUAL(sizeof(calculated), crypto_hash_sha256_bytes()); TEST_ASSERT_EQUAL(sizeof(calculated), sizeof(expected));
TEST_ASSERT_EQUAL_MEMORY(expected, calculated, crypto_hash_sha256_bytes()); 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); // Multi-line version
crypto_hash_sha256_update(&state, in, inlen - 1); // split into two updates crypto_hash_sha256_init(&state);
crypto_hash_sha256_update(&state, in + (inlen -1), 1); crypto_hash_sha256_update(&state, in, inlen - 1); // split into two updates
crypto_hash_sha256_final(&state, calculated); crypto_hash_sha256_update(&state, in + (inlen - 1), 1);
TEST_ASSERT_EQUAL_MEMORY(expected, calculated, crypto_hash_sha256_bytes()); crypto_hash_sha256_final(&state, calculated);
} TEST_ASSERT_EQUAL_MEMORY(expected, calculated, crypto_hash_sha256_bytes());
}
TEST_CASE("sha512 sanity check", "[libsodium]")
{ 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, const uint8_t expected[] = { 0xdd, 0xaf, 0x35, 0xa1, 0x93, 0x61, 0x7a, 0xba, 0xcc,
0xfa, 0x4e, 0x89, 0xa9, 0x7e, 0xa2, 0x0a, 0x9e, 0xee, 0x41, 0x73, 0x49, 0xae, 0x20, 0x41, 0x31, 0x12, 0xe6,
0xe6, 0x4b, 0x55, 0xd3, 0x9a, 0x21, 0x92, 0x99, 0x2a, 0xfa, 0x4e, 0x89, 0xa9, 0x7e, 0xa2, 0x0a, 0x9e, 0xee,
0x27, 0x4f, 0xc1, 0xa8, 0x36, 0xba, 0x3c, 0x23, 0xa3, 0xe6, 0x4b, 0x55, 0xd3, 0x9a, 0x21, 0x92, 0x99, 0x2a,
0xfe, 0xeb, 0xbd, 0x45, 0x4d, 0x44, 0x23, 0x64, 0x3c, 0x27, 0x4f, 0xc1, 0xa8, 0x36, 0xba, 0x3c, 0x23, 0xa3,
0xe8, 0x0e, 0x2a, 0x9a, 0xc9, 0x4f, 0xa5, 0x4c, 0xa4, 0xfe, 0xeb, 0xbd, 0x45, 0x4d, 0x44, 0x23, 0x64, 0x3c,
0x9f }; 0xe8, 0x0e, 0x2a, 0x9a, 0xc9, 0x4f, 0xa5, 0x4c, 0xa4,
0x9f
uint8_t calculated[64]; };
crypto_hash_sha512_state state;
uint8_t calculated[64];
const uint8_t *in = (const uint8_t *)"abc"; crypto_hash_sha512_state state;
const size_t inlen = 3;
const uint8_t *in = (const uint8_t *)"abc";
// One-liner version const size_t inlen = 3;
crypto_hash_sha512(calculated, in, inlen);
TEST_ASSERT_EQUAL(sizeof(calculated), sizeof(expected)); // One-liner version
TEST_ASSERT_EQUAL(sizeof(calculated), crypto_hash_sha512_bytes()); crypto_hash_sha512(calculated, in, inlen);
TEST_ASSERT_EQUAL_MEMORY(expected, calculated, crypto_hash_sha512_bytes()); TEST_ASSERT_EQUAL(sizeof(calculated), sizeof(expected));
TEST_ASSERT_EQUAL(sizeof(calculated), crypto_hash_sha512_bytes());
// Multi-line version TEST_ASSERT_EQUAL_MEMORY(expected, calculated, crypto_hash_sha512_bytes());
crypto_hash_sha512_init(&state);
crypto_hash_sha512_update(&state, in, inlen - 1); // split into two updates // Multi-line version
crypto_hash_sha512_update(&state, in + (inlen -1), 1); crypto_hash_sha512_init(&state);
crypto_hash_sha512_final(&state, calculated); crypto_hash_sha512_update(&state, in, inlen - 1); // split into two updates
TEST_ASSERT_EQUAL_MEMORY(expected, calculated, crypto_hash_sha512_bytes()); 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());
}

View File

@ -1,29 +1,29 @@
#ifndef __HAVE_CONFIG_H_ #ifndef __HAVE_CONFIG_H_
#define __HAVE_CONFIG_H_ #define __HAVE_CONFIG_H_
#define _U_ #define _U_
#define SIZEOF_INT_P 2 #define SIZEOF_INT_P 2
#include "stdio.h" #include "stdio.h"
#include "stdlib.h" #include "stdlib.h"
#include "string.h" #include "string.h"
#if (!defined(nghttp_unlikely)) #if (!defined(nghttp_unlikely))
#define nghttp_unlikely(Expression) !!(Expression) #define nghttp_unlikely(Expression) !!(Expression)
#endif #endif
#define nghttp_ASSERT(Expression) do{if (!(Expression)) printf("%d\n", __LINE__);}while(0) #define nghttp_ASSERT(Expression) do{if (!(Expression)) printf("%d\n", __LINE__);}while(0)
#define CU_ASSERT(a) nghttp_ASSERT(a) #define CU_ASSERT(a) nghttp_ASSERT(a)
#define CU_ASSERT_FATAL(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_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 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 htons(x) NGHTTP_PLATFORM_HTONS(x)
#define ntohs(x) NGHTTP_PLATFORM_HTONS(x) #define ntohs(x) NGHTTP_PLATFORM_HTONS(x)
#define htonl(x) NGHTTP_PLATFORM_HTONL(x) #define htonl(x) NGHTTP_PLATFORM_HTONL(x)
#define ntohl(x) NGHTTP_PLATFORM_HTONL(x) #define ntohl(x) NGHTTP_PLATFORM_HTONL(x)
#endif // __HAVE_CONFIG_H_ #endif // __HAVE_CONFIG_H_

View File

@ -1,370 +1,370 @@
/* /*
* SPDX-FileCopyrightText: 2017-2021 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2017-2021 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
#include <stdlib.h> #include <stdlib.h>
#include <stdint.h> #include <stdint.h>
#include <stddef.h> #include <stddef.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <ctype.h> #include <ctype.h>
#include <netdb.h> #include <netdb.h>
#include <esp_log.h> #include <esp_log.h>
#include <http_parser.h> #include <http_parser.h>
#include "sh2lib.h" #include "sh2lib.h"
static const char *TAG = "sh2lib"; static const char *TAG = "sh2lib";
#define DBG_FRAME_SEND 1 #define DBG_FRAME_SEND 1
/* /*
* The implementation of nghttp2_send_callback type. Here we write * The implementation of nghttp2_send_callback type. Here we write
* |data| with size |length| to the network and return the number of * |data| with size |length| to the network and return the number of
* bytes actually written. See the documentation of * bytes actually written. See the documentation of
* nghttp2_send_callback for the details. * nghttp2_send_callback for the details.
*/ */
static ssize_t callback_send_inner(struct sh2lib_handle *hd, const uint8_t *data, static ssize_t callback_send_inner(struct sh2lib_handle *hd, const uint8_t *data,
size_t length) size_t length)
{ {
int rv = esp_tls_conn_write(hd->http2_tls, data, length); int rv = esp_tls_conn_write(hd->http2_tls, data, length);
if (rv <= 0) { if (rv <= 0) {
if (rv == ESP_TLS_ERR_SSL_WANT_READ || rv == ESP_TLS_ERR_SSL_WANT_WRITE) { if (rv == ESP_TLS_ERR_SSL_WANT_READ || rv == ESP_TLS_ERR_SSL_WANT_WRITE) {
rv = NGHTTP2_ERR_WOULDBLOCK; rv = NGHTTP2_ERR_WOULDBLOCK;
} else { } else {
rv = NGHTTP2_ERR_CALLBACK_FAILURE; rv = NGHTTP2_ERR_CALLBACK_FAILURE;
} }
} }
return rv; return rv;
} }
static ssize_t callback_send(nghttp2_session *session, const uint8_t *data, static ssize_t callback_send(nghttp2_session *session, const uint8_t *data,
size_t length, int flags, void *user_data) size_t length, int flags, void *user_data)
{ {
int rv = 0; int rv = 0;
struct sh2lib_handle *hd = user_data; struct sh2lib_handle *hd = user_data;
int copy_offset = 0; int copy_offset = 0;
int pending_data = length; int pending_data = length;
/* Send data in 1000 byte chunks */ /* Send data in 1000 byte chunks */
while (copy_offset != length) { while (copy_offset != length) {
int chunk_len = pending_data > 1000 ? 1000 : pending_data; int chunk_len = pending_data > 1000 ? 1000 : pending_data;
int subrv = callback_send_inner(hd, data + copy_offset, chunk_len); int subrv = callback_send_inner(hd, data + copy_offset, chunk_len);
if (subrv <= 0) { if (subrv <= 0) {
if (copy_offset == 0) { if (copy_offset == 0) {
/* If no data is transferred, send the error code */ /* If no data is transferred, send the error code */
rv = subrv; rv = subrv;
} }
break; break;
} }
copy_offset += subrv; copy_offset += subrv;
pending_data -= subrv; pending_data -= subrv;
rv += subrv; rv += subrv;
} }
return rv; return rv;
} }
/* /*
* The implementation of nghttp2_recv_callback type. Here we read data * The implementation of nghttp2_recv_callback type. Here we read data
* from the network and write them in |buf|. The capacity of |buf| is * from the network and write them in |buf|. The capacity of |buf| is
* |length| bytes. Returns the number of bytes stored in |buf|. See * |length| bytes. Returns the number of bytes stored in |buf|. See
* the documentation of nghttp2_recv_callback for the details. * the documentation of nghttp2_recv_callback for the details.
*/ */
static ssize_t callback_recv(nghttp2_session *session, uint8_t *buf, static ssize_t callback_recv(nghttp2_session *session, uint8_t *buf,
size_t length, int flags, void *user_data) size_t length, int flags, void *user_data)
{ {
struct sh2lib_handle *hd = user_data; struct sh2lib_handle *hd = user_data;
int rv; int rv;
rv = esp_tls_conn_read(hd->http2_tls, (char *)buf, (int)length); rv = esp_tls_conn_read(hd->http2_tls, (char *)buf, (int)length);
if (rv < 0) { if (rv < 0) {
if (rv == ESP_TLS_ERR_SSL_WANT_READ || rv == ESP_TLS_ERR_SSL_WANT_WRITE) { if (rv == ESP_TLS_ERR_SSL_WANT_READ || rv == ESP_TLS_ERR_SSL_WANT_WRITE) {
rv = NGHTTP2_ERR_WOULDBLOCK; rv = NGHTTP2_ERR_WOULDBLOCK;
} else { } else {
rv = NGHTTP2_ERR_CALLBACK_FAILURE; rv = NGHTTP2_ERR_CALLBACK_FAILURE;
} }
} else if (rv == 0) { } else if (rv == 0) {
rv = NGHTTP2_ERR_EOF; rv = NGHTTP2_ERR_EOF;
} }
return rv; return rv;
} }
const char *sh2lib_frame_type_str(int type) const char *sh2lib_frame_type_str(int type)
{ {
switch (type) { switch (type) {
case NGHTTP2_HEADERS: case NGHTTP2_HEADERS:
return "HEADERS"; return "HEADERS";
break; break;
case NGHTTP2_RST_STREAM: case NGHTTP2_RST_STREAM:
return "RST_STREAM"; return "RST_STREAM";
break; break;
case NGHTTP2_GOAWAY: case NGHTTP2_GOAWAY:
return "GOAWAY"; return "GOAWAY";
break; break;
case NGHTTP2_DATA: case NGHTTP2_DATA:
return "DATA"; return "DATA";
break; break;
case NGHTTP2_SETTINGS: case NGHTTP2_SETTINGS:
return "SETTINGS"; return "SETTINGS";
break; break;
case NGHTTP2_PUSH_PROMISE: case NGHTTP2_PUSH_PROMISE:
return "PUSH_PROMISE"; return "PUSH_PROMISE";
break; break;
case NGHTTP2_PING: case NGHTTP2_PING:
return "PING"; return "PING";
break; break;
default: default:
return "other"; return "other";
break; break;
} }
} }
static int callback_on_frame_send(nghttp2_session *session, static int callback_on_frame_send(nghttp2_session *session,
const nghttp2_frame *frame, void *user_data) const nghttp2_frame *frame, void *user_data)
{ {
ESP_LOGD(TAG, "[frame-send] frame type %s", sh2lib_frame_type_str(frame->hd.type)); ESP_LOGD(TAG, "[frame-send] frame type %s", sh2lib_frame_type_str(frame->hd.type));
switch (frame->hd.type) { switch (frame->hd.type) {
case NGHTTP2_HEADERS: case NGHTTP2_HEADERS:
if (nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)) { if (nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)) {
ESP_LOGD(TAG, "[frame-send] C ----------------------------> S (HEADERS)"); ESP_LOGD(TAG, "[frame-send] C ----------------------------> S (HEADERS)");
#if DBG_FRAME_SEND #if DBG_FRAME_SEND
ESP_LOGD(TAG, "[frame-send] headers nv-len = %d", frame->headers.nvlen); ESP_LOGD(TAG, "[frame-send] headers nv-len = %d", frame->headers.nvlen);
const nghttp2_nv *nva = frame->headers.nva; const nghttp2_nv *nva = frame->headers.nva;
size_t i; size_t i;
for (i = 0; i < frame->headers.nvlen; ++i) { for (i = 0; i < frame->headers.nvlen; ++i) {
ESP_LOGD(TAG, "[frame-send] %s : %s", nva[i].name, nva[i].value); ESP_LOGD(TAG, "[frame-send] %s : %s", nva[i].name, nva[i].value);
} }
#endif #endif
} }
break; break;
} }
return 0; return 0;
} }
static int callback_on_frame_recv(nghttp2_session *session, static int callback_on_frame_recv(nghttp2_session *session,
const nghttp2_frame *frame, void *user_data) 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)); 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) { if (frame->hd.type != NGHTTP2_DATA) {
return 0; return 0;
} }
/* Subsequent processing only for data frame */ /* 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); sh2lib_frame_data_recv_cb_t data_recv_cb = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
if (data_recv_cb) { if (data_recv_cb) {
struct sh2lib_handle *h2 = user_data; struct sh2lib_handle *h2 = user_data;
(*data_recv_cb)(h2, NULL, 0, DATA_RECV_FRAME_COMPLETE); (*data_recv_cb)(h2, NULL, 0, DATA_RECV_FRAME_COMPLETE);
} }
return 0; return 0;
} }
static int callback_on_stream_close(nghttp2_session *session, int32_t stream_id, static int callback_on_stream_close(nghttp2_session *session, int32_t stream_id,
uint32_t error_code, void *user_data) uint32_t error_code, void *user_data)
{ {
ESP_LOGD(TAG, "[stream-close][sid %d]", stream_id); 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); sh2lib_frame_data_recv_cb_t data_recv_cb = nghttp2_session_get_stream_user_data(session, stream_id);
if (data_recv_cb) { if (data_recv_cb) {
struct sh2lib_handle *h2 = user_data; struct sh2lib_handle *h2 = user_data;
(*data_recv_cb)(h2, NULL, 0, DATA_RECV_RST_STREAM); (*data_recv_cb)(h2, NULL, 0, DATA_RECV_RST_STREAM);
} }
return 0; return 0;
} }
static int callback_on_data_chunk_recv(nghttp2_session *session, uint8_t flags, static int callback_on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
int32_t stream_id, const uint8_t *data, int32_t stream_id, const uint8_t *data,
size_t len, void *user_data) size_t len, void *user_data)
{ {
sh2lib_frame_data_recv_cb_t data_recv_cb; sh2lib_frame_data_recv_cb_t data_recv_cb;
ESP_LOGD(TAG, "[data-chunk][sid:%d]", stream_id); ESP_LOGD(TAG, "[data-chunk][sid:%d]", stream_id);
data_recv_cb = nghttp2_session_get_stream_user_data(session, stream_id); data_recv_cb = nghttp2_session_get_stream_user_data(session, stream_id);
if (data_recv_cb) { if (data_recv_cb) {
ESP_LOGD(TAG, "[data-chunk] C <---------------------------- S (DATA chunk)" ESP_LOGD(TAG, "[data-chunk] C <---------------------------- S (DATA chunk)"
"%lu bytes", "%lu bytes",
(unsigned long int)len); (unsigned long int)len);
struct sh2lib_handle *h2 = user_data; struct sh2lib_handle *h2 = user_data;
(*data_recv_cb)(h2, (char *)data, len, 0); (*data_recv_cb)(h2, (char *)data, len, 0);
/* TODO: What to do with the return value: look for pause/abort */ /* TODO: What to do with the return value: look for pause/abort */
} }
return 0; return 0;
} }
static int callback_on_header(nghttp2_session *session, const nghttp2_frame *frame, static int callback_on_header(nghttp2_session *session, const nghttp2_frame *frame,
const uint8_t *name, size_t namelen, const uint8_t *value, const uint8_t *name, size_t namelen, const uint8_t *value,
size_t valuelen, uint8_t flags, void *user_data) size_t valuelen, uint8_t flags, void *user_data)
{ {
ESP_LOGD(TAG, "[hdr-recv][sid:%d] %s : %s", frame->hd.stream_id, name, value); ESP_LOGD(TAG, "[hdr-recv][sid:%d] %s : %s", frame->hd.stream_id, name, value);
return 0; return 0;
} }
static int do_http2_connect(struct sh2lib_handle *hd) static int do_http2_connect(struct sh2lib_handle *hd)
{ {
int ret; int ret;
nghttp2_session_callbacks *callbacks; nghttp2_session_callbacks *callbacks;
nghttp2_session_callbacks_new(&callbacks); nghttp2_session_callbacks_new(&callbacks);
nghttp2_session_callbacks_set_send_callback(callbacks, callback_send); nghttp2_session_callbacks_set_send_callback(callbacks, callback_send);
nghttp2_session_callbacks_set_recv_callback(callbacks, callback_recv); 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_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_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_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_data_chunk_recv_callback(callbacks, callback_on_data_chunk_recv);
nghttp2_session_callbacks_set_on_header_callback(callbacks, callback_on_header); nghttp2_session_callbacks_set_on_header_callback(callbacks, callback_on_header);
ret = nghttp2_session_client_new(&hd->http2_sess, callbacks, hd); ret = nghttp2_session_client_new(&hd->http2_sess, callbacks, hd);
if (ret != 0) { if (ret != 0) {
ESP_LOGE(TAG, "[sh2-connect] New http2 session failed"); ESP_LOGE(TAG, "[sh2-connect] New http2 session failed");
nghttp2_session_callbacks_del(callbacks); nghttp2_session_callbacks_del(callbacks);
return -1; return -1;
} }
nghttp2_session_callbacks_del(callbacks); nghttp2_session_callbacks_del(callbacks);
/* Create the SETTINGS frame */ /* Create the SETTINGS frame */
ret = nghttp2_submit_settings(hd->http2_sess, NGHTTP2_FLAG_NONE, NULL, 0); ret = nghttp2_submit_settings(hd->http2_sess, NGHTTP2_FLAG_NONE, NULL, 0);
if (ret != 0) { if (ret != 0) {
ESP_LOGE(TAG, "[sh2-connect] Submit settings failed"); ESP_LOGE(TAG, "[sh2-connect] Submit settings failed");
return -1; return -1;
} }
return 0; return 0;
} }
int sh2lib_connect(struct sh2lib_config_t *cfg, struct sh2lib_handle *hd) int sh2lib_connect(struct sh2lib_config_t *cfg, struct sh2lib_handle *hd)
{ {
memset(hd, 0, sizeof(*hd)); memset(hd, 0, sizeof(*hd));
if (cfg == NULL) { if (cfg == NULL) {
ESP_LOGE(TAG, "[sh2-connect] pointer to sh2lib configurations cannot be NULL"); ESP_LOGE(TAG, "[sh2-connect] pointer to sh2lib configurations cannot be NULL");
goto error; goto error;
} }
const char *proto[] = {"h2", NULL}; const char *proto[] = {"h2", NULL};
esp_tls_cfg_t tls_cfg = { esp_tls_cfg_t tls_cfg = {
.alpn_protos = proto, .alpn_protos = proto,
.cacert_buf = cfg->cacert_buf, .cacert_buf = cfg->cacert_buf,
.cacert_bytes = cfg->cacert_bytes, .cacert_bytes = cfg->cacert_bytes,
.non_block = true, .non_block = true,
.timeout_ms = 10 * 1000, .timeout_ms = 10 * 1000,
}; };
if ((hd->http2_tls = esp_tls_conn_http_new(cfg->uri, &tls_cfg)) == NULL) { if ((hd->http2_tls = esp_tls_conn_http_new(cfg->uri, &tls_cfg)) == NULL) {
ESP_LOGE(TAG, "[sh2-connect] esp-tls connection failed"); ESP_LOGE(TAG, "[sh2-connect] esp-tls connection failed");
goto error; goto error;
} }
struct http_parser_url u; struct http_parser_url u;
http_parser_url_init(&u); http_parser_url_init(&u);
http_parser_parse_url(cfg->uri, strlen(cfg->uri), 0, &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); hd->hostname = strndup(&cfg->uri[u.field_data[UF_HOST].off], u.field_data[UF_HOST].len);
/* HTTP/2 Connection */ /* HTTP/2 Connection */
if (do_http2_connect(hd) != 0) { if (do_http2_connect(hd) != 0) {
ESP_LOGE(TAG, "[sh2-connect] HTTP2 Connection failed with %s", cfg->uri); ESP_LOGE(TAG, "[sh2-connect] HTTP2 Connection failed with %s", cfg->uri);
goto error; goto error;
} }
return 0; return 0;
error: error:
sh2lib_free(hd); sh2lib_free(hd);
return -1; return -1;
} }
void sh2lib_free(struct sh2lib_handle *hd) void sh2lib_free(struct sh2lib_handle *hd)
{ {
if (hd->http2_sess) { if (hd->http2_sess) {
nghttp2_session_del(hd->http2_sess); nghttp2_session_del(hd->http2_sess);
hd->http2_sess = NULL; hd->http2_sess = NULL;
} }
if (hd->http2_tls) { if (hd->http2_tls) {
esp_tls_conn_destroy(hd->http2_tls); esp_tls_conn_destroy(hd->http2_tls);
hd->http2_tls = NULL; hd->http2_tls = NULL;
} }
if (hd->hostname) { if (hd->hostname) {
free(hd->hostname); free(hd->hostname);
hd->hostname = NULL; hd->hostname = NULL;
} }
} }
int sh2lib_execute(struct sh2lib_handle *hd) int sh2lib_execute(struct sh2lib_handle *hd)
{ {
int ret; int ret;
ret = nghttp2_session_send(hd->http2_sess); ret = nghttp2_session_send(hd->http2_sess);
if (ret != 0) { if (ret != 0) {
ESP_LOGE(TAG, "[sh2-execute] HTTP2 session send failed %d", ret); ESP_LOGE(TAG, "[sh2-execute] HTTP2 session send failed %d", ret);
return -1; return -1;
} }
ret = nghttp2_session_recv(hd->http2_sess); ret = nghttp2_session_recv(hd->http2_sess);
if (ret != 0) { if (ret != 0) {
ESP_LOGE(TAG, "[sh2-execute] HTTP2 session recv failed %d", ret); ESP_LOGE(TAG, "[sh2-execute] HTTP2 session recv failed %d", ret);
return -1; return -1;
} }
return 0; 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 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); int ret = nghttp2_submit_request(hd->http2_sess, NULL, nva, nvlen, NULL, recv_cb);
if (ret < 0) { if (ret < 0) {
ESP_LOGE(TAG, "[sh2-do-get] HEADERS call failed"); ESP_LOGE(TAG, "[sh2-do-get] HEADERS call failed");
return -1; return -1;
} }
return ret; return ret;
} }
int sh2lib_do_get(struct sh2lib_handle *hd, const char *path, sh2lib_frame_data_recv_cb_t recv_cb) 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"), const nghttp2_nv nva[] = { SH2LIB_MAKE_NV(":method", "GET"),
SH2LIB_MAKE_NV(":scheme", "https"), SH2LIB_MAKE_NV(":scheme", "https"),
SH2LIB_MAKE_NV(":authority", hd->hostname), SH2LIB_MAKE_NV(":authority", hd->hostname),
SH2LIB_MAKE_NV(":path", path), SH2LIB_MAKE_NV(":path", path),
}; };
return sh2lib_do_get_with_nv(hd, nva, sizeof(nva) / sizeof(nva[0]), recv_cb); 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, ssize_t sh2lib_data_provider_cb(nghttp2_session *session, int32_t stream_id, uint8_t *buf,
size_t length, uint32_t *data_flags, size_t length, uint32_t *data_flags,
nghttp2_data_source *source, void *user_data) nghttp2_data_source *source, void *user_data)
{ {
struct sh2lib_handle *h2 = user_data; struct sh2lib_handle *h2 = user_data;
sh2lib_putpost_data_cb_t data_cb = source->ptr; sh2lib_putpost_data_cb_t data_cb = source->ptr;
return (*data_cb)(h2, (char *)buf, length, data_flags); 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, 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_putpost_data_cb_t send_cb,
sh2lib_frame_data_recv_cb_t recv_cb) sh2lib_frame_data_recv_cb_t recv_cb)
{ {
nghttp2_data_provider sh2lib_data_provider; nghttp2_data_provider sh2lib_data_provider;
sh2lib_data_provider.read_callback = sh2lib_data_provider_cb; sh2lib_data_provider.read_callback = sh2lib_data_provider_cb;
sh2lib_data_provider.source.ptr = send_cb; sh2lib_data_provider.source.ptr = send_cb;
int ret = nghttp2_submit_request(hd->http2_sess, NULL, nva, nvlen, &sh2lib_data_provider, recv_cb); int ret = nghttp2_submit_request(hd->http2_sess, NULL, nva, nvlen, &sh2lib_data_provider, recv_cb);
if (ret < 0) { if (ret < 0) {
ESP_LOGE(TAG, "[sh2-do-putpost] HEADERS call failed"); ESP_LOGE(TAG, "[sh2-do-putpost] HEADERS call failed");
return -1; return -1;
} }
return ret; return ret;
} }
int sh2lib_do_post(struct sh2lib_handle *hd, const char *path, int sh2lib_do_post(struct sh2lib_handle *hd, const char *path,
sh2lib_putpost_data_cb_t send_cb, sh2lib_putpost_data_cb_t send_cb,
sh2lib_frame_data_recv_cb_t recv_cb) sh2lib_frame_data_recv_cb_t recv_cb)
{ {
const nghttp2_nv nva[] = { SH2LIB_MAKE_NV(":method", "POST"), const nghttp2_nv nva[] = { SH2LIB_MAKE_NV(":method", "POST"),
SH2LIB_MAKE_NV(":scheme", "https"), SH2LIB_MAKE_NV(":scheme", "https"),
SH2LIB_MAKE_NV(":authority", hd->hostname), SH2LIB_MAKE_NV(":authority", hd->hostname),
SH2LIB_MAKE_NV(":path", path), SH2LIB_MAKE_NV(":path", path),
}; };
return sh2lib_do_putpost_with_nv(hd, nva, sizeof(nva) / sizeof(nva[0]), send_cb, recv_cb); 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, int sh2lib_do_put(struct sh2lib_handle *hd, const char *path,
sh2lib_putpost_data_cb_t send_cb, sh2lib_putpost_data_cb_t send_cb,
sh2lib_frame_data_recv_cb_t recv_cb) sh2lib_frame_data_recv_cb_t recv_cb)
{ {
const nghttp2_nv nva[] = { SH2LIB_MAKE_NV(":method", "PUT"), const nghttp2_nv nva[] = { SH2LIB_MAKE_NV(":method", "PUT"),
SH2LIB_MAKE_NV(":scheme", "https"), SH2LIB_MAKE_NV(":scheme", "https"),
SH2LIB_MAKE_NV(":authority", hd->hostname), SH2LIB_MAKE_NV(":authority", hd->hostname),
SH2LIB_MAKE_NV(":path", path), SH2LIB_MAKE_NV(":path", path),
}; };
return sh2lib_do_putpost_with_nv(hd, nva, sizeof(nva) / sizeof(nva[0]), send_cb, recv_cb); return sh2lib_do_putpost_with_nv(hd, nva, sizeof(nva) / sizeof(nva[0]), send_cb, recv_cb);
} }