first commit

This commit is contained in:
valeh
2020-12-22 14:30:09 +02:00
commit 26b0ba5954
1832 changed files with 17777948 additions and 0 deletions

View File

@@ -0,0 +1,150 @@
/**
* @file AuthorizationController.cpp
* @brief AuthorizationController implementation file
* */
#include "cJSON.h"
#include "controller.h"
#include "module.h"
AuthorizationController::AuthorizationController(BaseModel *model, const char* sni){
this->model = static_cast<UserModel *>(model);
this->sni = sni;
this->vw = new IndexView();
}
char* AuthorizationController::generateAuthenticateRequestJSON(){
cJSON *root, *params, *param;
root = cJSON_CreateObject();
cJSON_AddNumberToObject(root, "id", 0.0);
cJSON_AddItemToObject(root, "method", cJSON_CreateString("RPC.Authenticate"));
params = cJSON_CreateArray();
cJSON_AddItemToObject(root, "params", params);
cJSON_AddItemToArray(params, param = cJSON_CreateObject());
cJSON_AddStringToObject(param, "OS", "FreeRTOS");
cJSON_AddStringToObject(param, "PhoneNo", this->model->phone);
cJSON_AddStringToObject(param, "IDCode", this->model->ID);
char* pretty = cJSON_Print(root);
cJSON_Delete(root);
return pretty;
}
char* AuthorizationController::generateAuthenticateStatusRequestJSON(){
cJSON *root, *params, *param;
root = cJSON_CreateObject();
cJSON_AddNumberToObject(root, "id", 0.0);
cJSON_AddItemToObject(root, "method", cJSON_CreateString("RPC.AuthenticateStatus"));
params = cJSON_CreateArray();
cJSON_AddItemToObject(root, "params", params);
cJSON_AddItemToArray(params, param = cJSON_CreateObject());
cJSON_AddStringToObject(param, "OS", "FreeRTOS");
cJSON_AddStringToObject(param, "SessionID", this->model->ssid);
cJSON_AddStringToObject(param, "SessionCode", this->model->sscode);
char* pretty = cJSON_Print(root);
cJSON_Delete(root);
return pretty;
}
void AuthorizationController::index(){
this->vw->render((char *) "Authorization");
}
void AuthorizationController::auth(){
try{
// Start mobilID Authentication
ESP_LOGI(TAG, "authorization request sent");
cJSON *authJson = RPC::Instance().send_json_rpc(sni, generateAuthenticateRequestJSON());
if(authJson == NULL){
throw "Empty response";
}
if( cJSON_GetObjectItem(authJson, "error")->valuestring != NULL){
throw cJSON_GetObjectItem(authJson, "error")->valuestring;
}
cJSON* params = cJSON_GetObjectItem(authJson, "result");
int len = strlen(cJSON_GetObjectItem(params, "SessionID")->valuestring);
this->model->ssid = (char *) malloc(len +1);
memset(this->model->ssid, 0, len + 1);
memcpy(this->model->ssid, cJSON_GetObjectItem(params, "SessionID")->valuestring, len);
len = strlen(cJSON_GetObjectItem(params, "SessionCode")->valuestring);
this->model->sscode = (char *) malloc(len +1);
memset(this->model->sscode, 0, len + 1);
memcpy(this->model->sscode, cJSON_GetObjectItem(params, "SessionCode")->valuestring, len);
char pin[25];
bzero(pin, 25);
strncat(pin, "Verification code: ", 20);
strncat(pin, cJSON_GetObjectItem(params, "ChallengeID")->valuestring, 5);
cJSON_Delete(authJson);
this->vw->render(pin);
}
catch (const char* msg){
this->vw->render((void *)msg);
throw msg;
}
}
void AuthorizationController::authStatus(){
try{
char* authStatusReq = generateAuthenticateStatusRequestJSON();
cJSON * result, * authJson;
do{
ESP_LOGI(TAG, "waiting to confirm verification code");
for(int i=10; i>0; i--){
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
// Check authentication status
ESP_LOGI(TAG, "authorization status request sent");
authJson = RPC::Instance().send_json_rpc(sni, authStatusReq );
if(authJson == NULL){
throw "Empty response";
}
result = cJSON_GetObjectItem(authJson, "result");
if( cJSON_GetObjectItem(authJson, "error")->valuestring != NULL){
throw cJSON_GetObjectItem(authJson, "error")->valuestring;
}
if(strcmp(cJSON_GetObjectItem(result, "Status")->valuestring, "POLL") == 0){
continue;
}
if(strcmp(cJSON_GetObjectItem(result, "Status")->valuestring, "OK") == 0){
int len = strlen(cJSON_GetObjectItem(result, "AuthToken")->valuestring);
this->model->authToken = (char *)malloc(len + 1);
memset(this->model->authToken, 0, len + 1);
memcpy(this->model->authToken, cJSON_GetObjectItem(result, "AuthToken")->valuestring, len);
this->vw->render((char *) "PIN CONFIRMED!");
break;
}
cJSON_free(authJson);
}
while(1);
cJSON_Delete(authJson);
}
catch (const char* msg){
this->vw->render((void *)msg);
throw msg;
}
}

View File

@@ -0,0 +1,430 @@
#include "controller.h"
#include "view.h"
#include "module.h"
#include "esp_bt.h"
#include "esp_gap_ble_api.h"
#include "esp_gatts_api.h"
#include "esp_bt_defs.h"
#include "esp_bt_main.h"
#include "esp_gatt_common_api.h"
/**
* @file BluetoothController.cpp
* @brief BluetoothController implementation file
* */
#define GATTS_SERVICE_UUID_TEST_A 0x00FF
#define GATTS_CHAR_UUID_TEST_A 0xFF01
#define GATTS_DESCR_UUID_TEST_A 0x3333
#define GATTS_NUM_HANDLE_TEST_A 4
#define DEVICE_NAME "ESP_BOARD"
#define TEST_MANUFACTURER_DATA_LEN 17
#define GATTS_TAG "GATTS"
#define GATTS_DEMO_CHAR_VAL_LEN_MAX 0x40
#define PREPARE_BUF_MAX_SIZE 1024
#define MIN(a,b) (a <= b ? a : b)
static uint8_t char1_str[] = {0x11,0x22,0x33};
#ifdef CONFIG_SET_RAW_ADV_DATA
static uint8_t raw_adv_data[] = {
0x02, 0x01, 0x06,
0x02, 0x0a, 0xeb, 0x03, 0x03, 0xab, 0xcd
};
static uint8_t raw_scan_rsp_data[] = {
0x0f, 0x09, 0x45, 0x53, 0x50, 0x5f, 0x47, 0x41, 0x54, 0x54, 0x53, 0x5f, 0x44,
0x45, 0x4d, 0x4f
};
#else
static uint8_t adv_service_uuid128[16] = {
/* LSB <--------------------------------------------------------------------------------> MSB */
//first uuid, 16bit, [12],[13] is the value
0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00
};
// The length of adv data must be less than 31 bytes
//static uint8_t test_manufacturer[TEST_MANUFACTURER_DATA_LEN] = {0x12, 0x23, 0x45, 0x56};
//adv data
static esp_ble_adv_data_t adv_data = {
.set_scan_rsp = false,
.include_name = true,
.include_txpower = true,
.min_interval = 0x0006, //slave connection min interval, Time = min_interval * 1.25 msec
.max_interval = 0x0010, //slave connection max interval, Time = max_interval * 1.25 msec
.appearance = 0x00,
.manufacturer_len = 0, //TEST_MANUFACTURER_DATA_LEN,
.p_manufacturer_data = NULL, //&test_manufacturer[0],
.service_data_len = 0,
.p_service_data = NULL,
.service_uuid_len = sizeof(adv_service_uuid128),
.p_service_uuid = adv_service_uuid128,
.flag = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT),
};
// scan response data
static esp_ble_adv_data_t scan_rsp_data = {
.set_scan_rsp = true,
.include_name = true,
.include_txpower = true,
.min_interval = 0x0006,
.max_interval = 0x0010,
.appearance = 0x00,
.manufacturer_len = 0, //TEST_MANUFACTURER_DATA_LEN,
.p_manufacturer_data = NULL, //&test_manufacturer[0],
.service_data_len = 0,
.p_service_data = NULL,
.service_uuid_len = sizeof(adv_service_uuid128),
.p_service_uuid = adv_service_uuid128,
.flag = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT),
};
#endif /* CONFIG_SET_RAW_ADV_DATA */
#define adv_config_flag (1 << 0)
#define scan_rsp_config_flag (1 << 1)
#define PROFILE_A_APP_ID 0
struct gatts_profile_inst {
esp_gatts_cb_t gatts_cb;
uint16_t gatts_if;
uint16_t app_id;
uint16_t conn_id;
uint16_t service_handle;
esp_gatt_srvc_id_t service_id;
uint16_t char_handle;
esp_bt_uuid_t char_uuid;
esp_gatt_perm_t perm;
esp_gatt_char_prop_t property;
uint16_t descr_handle;
esp_bt_uuid_t descr_uuid;
char* data;
};
enum
{
OVC_IDX_SVC,
OVC_IDX_VVC_CHAR,
OVC_IDX_VVC_VAL,
OVC_IDX_VVC_USR_DESC,
OVC_IDX_VVC_NTF_CFG,
OVC_IDX_NB,
};
static const uint16_t ovc_svc = GATTS_SERVICE_UUID_TEST_A;
#define CHAR_DECLARATION_SIZE (sizeof(uint8_t))
static const uint16_t primary_service_uuid = ESP_GATT_UUID_PRI_SERVICE;
static const uint16_t character_declaration_uuid = ESP_GATT_UUID_CHAR_DECLARE;
static const uint16_t character_client_config_uuid = ESP_GATT_UUID_CHAR_CLIENT_CONFIG;
static const uint16_t character_client_description_uuid = ESP_GATT_UUID_CHAR_DESCRIPTION;
static const uint8_t char_prop_notify = ESP_GATT_CHAR_PROP_BIT_NOTIFY;
/// Heart Rate Sensor Service - Heart Rate Measurement Characteristic, notify
static const uint16_t vote_verification_svc_uuid = GATTS_CHAR_UUID_TEST_A;
static const uint8_t vote_verification_ccc[2] ={ 0x01, 0x01};
static uint16_t ovc_handle_table[OVC_IDX_NB];
static esp_gatts_attr_db_t attr_db[OVC_IDX_NB] = {
// Heart Rate Service Declaration
[OVC_IDX_SVC] =
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&primary_service_uuid, ESP_GATT_PERM_READ_ENC_MITM,
sizeof(uint16_t), sizeof(ovc_svc), (uint8_t *)&ovc_svc}},
// Vote Verification Characteristic Declaration
[OVC_IDX_VVC_CHAR] =
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_declaration_uuid, ESP_GATT_PERM_READ_ENC_MITM,
CHAR_DECLARATION_SIZE,CHAR_DECLARATION_SIZE, (uint8_t *)&char_prop_notify}},
// Vote Verification Characteristic Value
[OVC_IDX_VVC_VAL] =
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&vote_verification_svc_uuid, ESP_GATT_PERM_READ_ENC_MITM,
32,3, char1_str}},
// Vote Verification Characteristic User Descriptor
[OVC_IDX_VVC_USR_DESC] =
{{ESP_GATT_RSP_BY_APP}, {ESP_UUID_LEN_16, (uint8_t *)&character_client_description_uuid, ESP_GATT_PERM_READ_ENC_MITM,
600, 0, NULL}},
// Vote Verification Characteristic - Client Characteristic Configuration Descriptor
[OVC_IDX_VVC_NTF_CFG] =
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_client_config_uuid, ESP_GATT_PERM_READ_ENC_MITM|ESP_GATT_PERM_WRITE_ENC_MITM,
sizeof(uint16_t),sizeof(vote_verification_ccc), (uint8_t *)vote_verification_ccc}},
};
static BLEModule ble;
static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param);
static struct gatts_profile_inst PROFILE_A_APP;
static void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param)
{
/* If event is register event, store the gatts_if for each profile */
if (event == ESP_GATTS_REG_EVT) {
if (param->reg.status == ESP_GATT_OK && param->reg.app_id == PROFILE_A_APP_ID) {
PROFILE_A_APP.gatts_if = gatts_if;
} else {
ESP_LOGI(GATTS_TAG, "Reg app failed, app_id %04x, status %d\n", param->reg.app_id, param->reg.status);
return;
}
}
/* If the gatts_if equal to profile A, call profile A cb handler,
* so here call each profile's callback */
do {
if (gatts_if == ESP_GATT_IF_NONE || /* ESP_GATT_IF_NONE, not specify a certain gatt_if, need to call every profile cb function */
gatts_if == PROFILE_A_APP.gatts_if) {
if (PROFILE_A_APP.gatts_cb) {
PROFILE_A_APP.gatts_cb(event, gatts_if, param);
}
}
} while (0);
}
static uint8_t BT_ON = 0;
static uint8_t data_sent = 0;
uint16_t mtuVal = 23;
static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) {
switch (event) {
case ESP_GATTS_REG_EVT:{
ESP_LOGI(GATTS_TAG, "REGISTER_APP_EVT, status %d, app_id %d\n", param->reg.status, param->reg.app_id);
esp_err_t set_dev_name_ret;
set_dev_name_ret = esp_ble_gap_set_device_name(DEVICE_NAME);
if (set_dev_name_ret){
ESP_LOGE(GATTS_TAG, "set device name failed, error code = %x", set_dev_name_ret);
}
esp_ble_gap_config_local_privacy(true);
attr_db[OVC_IDX_VVC_USR_DESC].att_desc.length = (uint16_t) strlen( PROFILE_A_APP.data);
attr_db[OVC_IDX_VVC_USR_DESC].att_desc.value = (uint8_t *) PROFILE_A_APP.data;
esp_ble_gatts_create_attr_tab(attr_db, gatts_if, OVC_IDX_NB, PROFILE_A_APP_ID);
break;
}
case ESP_GATTS_READ_EVT:{
ESP_LOGI(GATTS_TAG, "GATT_READ_EVT, conn_id %d, trans_id %d, handle %d\n", param->read.conn_id, param->read.trans_id, param->read.handle);
esp_gatt_rsp_t rsp;
memset(&rsp, 0, sizeof(esp_gatt_rsp_t));
rsp.attr_value.handle = param->read.handle;
size_t len = MIN(mtuVal -1, strlen(PROFILE_A_APP.data) - param->read.offset);
rsp.attr_value.len = len ;
printf("%d %d %d %d %d\n", len, rsp.attr_value.len, rsp.attr_value.offset, param->read.offset, param->read.is_long);
memcpy(rsp.attr_value.value, PROFILE_A_APP.data + param->read.offset, len);
esp_ble_gatts_send_response(gatts_if, param->read.conn_id, param->read.trans_id, ESP_GATT_OK, &rsp);
break;
}
case ESP_GATTS_WRITE_EVT:{
ESP_LOGI(GATTS_TAG, "GATT_WRITE_EVT, conn_id %d, trans_id %d, handle %d", param->write.conn_id, param->write.trans_id, param->write.handle);
if (!param->write.is_prep){
ESP_LOGI(GATTS_TAG, "GATT_WRITE_EVT, value len %d, value :", param->write.len);
esp_log_buffer_hex(GATTS_TAG, param->write.value, param->write.len);
ESP_LOGI(GATTS_TAG, "%u %u", PROFILE_A_APP.descr_handle, param->write.handle);
if (param->write.len == 2){
uint16_t descr_value = param->write.value[1]<<8 | param->write.value[0];
printf("%d %04x \n", *(param->write.value), descr_value);
if(descr_value == 0x0101){
ESP_LOGI(GATTS_TAG, "data received");
esp_err_t ret;
data_sent = 1;
ret = esp_ble_gap_disconnect(param->write.bda);
if(ret != ESP_OK){
ESP_LOGE(GATTS_TAG, "unable to disconnect the device");
}
}else{
ESP_LOGE(GATTS_TAG, "unallowed operations");
esp_log_buffer_hex(GATTS_TAG, param->write.value, param->write.len);
}
}
}
break;
}
case ESP_GATTS_EXEC_WRITE_EVT:
break;
case ESP_GATTS_MTU_EVT:
mtuVal = param->mtu.mtu;
ESP_LOGI(GATTS_TAG, "ESP_GATTS_MTU_EVT, MTU %d", mtuVal);
break;
case ESP_GATTS_UNREG_EVT:
break;
case ESP_GATTS_CREATE_EVT:{
ESP_LOGI(GATTS_TAG, "CREATE_SERVICE_EVT, status %d, service_handle %d\n", param->create.status, param->create.service_handle);
break;
}
case ESP_GATTS_ADD_INCL_SRVC_EVT:
break;
case ESP_GATTS_ADD_CHAR_EVT: {
uint16_t length = 0;
const uint8_t *prf_char;
ESP_LOGI(GATTS_TAG, "ADD_CHAR_EVT, status %d, attr_handle %d, service_handle %d\n",
param->add_char.status, param->add_char.attr_handle, param->add_char.service_handle);
break;
}
case ESP_GATTS_ADD_CHAR_DESCR_EVT:
ESP_LOGI(GATTS_TAG, "ADD_DESCR_EVT, status %d, attr_handle %d, service_handle %d\n",
param->add_char_descr.status, param->add_char_descr.attr_handle, param->add_char_descr.service_handle);
break;
case ESP_GATTS_DELETE_EVT:
break;
case ESP_GATTS_START_EVT:
ESP_LOGI(GATTS_TAG, "SERVICE_START_EVT, status %d, service_handle %d\n",
param->start.status, param->start.service_handle);
break;
case ESP_GATTS_STOP_EVT:
break;
case ESP_GATTS_CONNECT_EVT: {
//start sent the update connection parameters to the peer device.
// esp_ble_gap_update_conn_params(&conn_params);
esp_ble_set_encryption(param->connect.remote_bda, ESP_BLE_SEC_ENCRYPT_MITM);
break;
}
case ESP_GATTS_DISCONNECT_EVT:
ESP_LOGI(GATTS_TAG, "ESP_GATTS_DISCONNECT_EVT, disconnect reason 0x%x", param->disconnect.reason);
if(!data_sent)
esp_ble_gap_start_advertising(ble.adv);
else
BT_ON=1;
break;
case ESP_GATTS_CONF_EVT:
ESP_LOGI(GATTS_TAG, "ESP_GATTS_CONF_EVT, status %d attr_handle %d", param->conf.status, param->conf.handle);
if (param->conf.status != ESP_GATT_OK){
esp_log_buffer_hex(GATTS_TAG, param->conf.value, param->conf.len);
}
break;
case ESP_GATTS_CREAT_ATTR_TAB_EVT: {
ESP_LOGI(GATTS_TAG, "The number handle = %x",param->add_attr_tab.num_handle);
if (param->create.status == ESP_GATT_OK){
if(param->add_attr_tab.num_handle == OVC_IDX_NB) {
memcpy(ovc_handle_table, param->add_attr_tab.handles, sizeof(ovc_handle_table));
esp_ble_gatts_start_service(ovc_handle_table[OVC_IDX_SVC]);
}else{
ESP_LOGE(GATTS_TAG, "Create attribute table abnormally, num_handle (%d) doesn't equal to OVC_IDX_NB(%d)",
param->add_attr_tab.num_handle, OVC_IDX_NB);
}
}else{
ESP_LOGE(GATTS_TAG, " Create attribute table failed, error code = %x", param->create.status);
}
}
break;
case ESP_GATTS_OPEN_EVT:
case ESP_GATTS_CANCEL_OPEN_EVT:
case ESP_GATTS_CLOSE_EVT:
case ESP_GATTS_LISTEN_EVT:
case ESP_GATTS_CONGEST_EVT:
case ESP_GATTS_SEND_SERVICE_CHANGE_EVT:
default:
break;
}
}
BluetoothController::BluetoothController(BaseModel *model){
this->vw = new IndexView();
this->model = static_cast<BluetoothModel *>(model);
}
void BluetoothController::index(){
try{
ble = BLEModule();
esp_err_t ret = ble.init(); //! * init bluetooth module, and start advertising
this->vw->render((void*) "bluetooth on");
BT_ON = 1;
PROFILE_A_APP.gatts_cb = gatts_profile_a_event_handler;
PROFILE_A_APP.gatts_if = ESP_GATT_IF_NONE; /* Not get the gatt_if, so initial is ESP_GATT_IF_NONE */
ret = esp_ble_gatts_register_callback(gatts_event_handler); //! * register callback function for bletooth events
if (ret){
ESP_LOGE(TAG, "gatts register error, error code = %x", ret);
throw "TAG error";
}
size_t len = strlen(this->model->ssid) + strlen(this->model->voteID) + strlen((char *)this->model->rndBase64) + 3;
PROFILE_A_APP.data = (char *) malloc(len);
memset(PROFILE_A_APP.data, 0 , len);
snprintf(PROFILE_A_APP.data, len, "%s\n%s\n%s",this->model->ssid,this->model->rndBase64,this->model->voteID); //! * prepare data to send
ret = esp_ble_gatts_app_register(PROFILE_A_APP_ID); //! * register app profile
if (ret){
ESP_LOGE(TAG, "gatts app register error, error code = %x", ret);
throw "BLE error";
}
esp_err_t local_mtu_ret = esp_ble_gatt_set_local_mtu(500);
if (local_mtu_ret){
ESP_LOGE(TAG, "set local MTU failed, error code = %x", local_mtu_ret);
throw "BLE error";
}
/* set the security iocap & auth_req & key size & init key response key parameters to the stack*/
esp_ble_auth_req_t auth_req = ESP_LE_AUTH_REQ_SC_MITM; //bonding with peer device after authentication
esp_ble_io_cap_t iocap = ESP_IO_CAP_OUT; //set the IO capability to No output No input
uint8_t key_size = 16; //the key size should be 7~16 bytes
uint8_t init_key = ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK;
uint8_t rsp_key = ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK;
//set static passkey
uint32_t passkey = esp_random() % 999999;
char bt_pin[8];
snprintf(bt_pin, 7, "%06u", passkey);
this->vw->render((void *) bt_pin);
uint8_t auth_option = ESP_BLE_ONLY_ACCEPT_SPECIFIED_AUTH_ENABLE;
uint8_t oob_support = ESP_BLE_OOB_DISABLE;
esp_ble_gap_set_security_param(ESP_BLE_SM_SET_STATIC_PASSKEY, &passkey, sizeof(uint32_t));
esp_ble_gap_set_security_param(ESP_BLE_SM_AUTHEN_REQ_MODE, &auth_req, sizeof(uint8_t));
esp_ble_gap_set_security_param(ESP_BLE_SM_IOCAP_MODE, &iocap, sizeof(uint8_t));
esp_ble_gap_set_security_param(ESP_BLE_SM_MAX_KEY_SIZE, &key_size, sizeof(uint8_t));
esp_ble_gap_set_security_param(ESP_BLE_SM_ONLY_ACCEPT_SPECIFIED_SEC_AUTH, &auth_option, sizeof(uint8_t));
esp_ble_gap_set_security_param(ESP_BLE_SM_OOB_SUPPORT, &oob_support, sizeof(uint8_t));
/* If your BLE device acts as a Slave, the init_key means you hope which types of key of the master should distribute to you,
and the response key means which key you can distribute to the master;
If your BLE device acts as a master, the response key means you hope which types of key of the slave should distribute to you,
and the init key means which key you can distribute to the slave. */
esp_ble_gap_set_security_param(ESP_BLE_SM_SET_INIT_KEY, &init_key, sizeof(uint8_t));
esp_ble_gap_set_security_param(ESP_BLE_SM_SET_RSP_KEY, &rsp_key, sizeof(uint8_t));
while(BT_ON && !data_sent){
vTaskDelay(500 / portTICK_PERIOD_MS); //! * wait while bluetooth task is running
}
ret = esp_ble_gatts_app_unregister(PROFILE_A_APP_ID);
if(ret != ESP_OK){
ESP_LOGE(GATTS_TAG, "unable to unregister the app profile");
}
ESP_LOGI(GATTS_TAG, "gatt server disconnected.");
ret = ble.deinit(); //! * turn off bluetooth
this->vw->render((void*) "bluetooth off");
}
catch (const char* msg){
this->vw->render((void *)msg);
throw msg;
}
}

View File

@@ -0,0 +1,118 @@
/**
* @file ChoiceController.cpp
* @brief ChoiceController implementation file
* */
#include "cJSON.h"
#include "controller.h"
ChoiceController::ChoiceController(BaseModel *model, const char* sni){
this->vw = new ChoiceView();
this->model = static_cast<ChoiceModel*>(model);
this->sni = sni;
}
void ChoiceController::index(){
try{
char* choicesRequestJson = generateChoicesRequestJSON(this->model);
// Get choices from server
cJSON *choicesJson = RPC::Instance().send_json_rpc(sni, choicesRequestJson);
cJSON *result;
if(choicesJson == NULL){
throw "Empty response";
}
if( cJSON_GetObjectItem(choicesJson, "error")->valuestring != NULL){
throw cJSON_GetObjectItem(choicesJson, "error")->valuestring;
}
result = cJSON_GetObjectItem(choicesJson, "result");
unsigned char* list = (unsigned char *) cJSON_GetObjectItem(result, "List")->valuestring;
this->model->choices = (char *) malloc(21);
memset(this->model->choices, 0, 21);
strncpy(this->model->choices, cJSON_GetObjectItem(result, "Choices")->valuestring, 21);
size_t bufflen = strlen((char*) list);
bufflen = (bufflen/4) *3 +1; // base64 decoded length
unsigned char *decoded_list = (unsigned char *) malloc(bufflen);
if(decoded_list == NULL) throw "insufficient memory";
memset(decoded_list, 0, bufflen);
size_t len;
int error = mbedtls_base64_decode(decoded_list, bufflen, &len, list, strlen( (char*) list));
if(error) throw "Error decoding choice list";
cJSON *parsedList = cJSON_Parse((char *) decoded_list);
parse_object(parsedList, (char *)"" );
ESP_LOGI(TAG, "total candidates: %d", this->model->choice_list->size());
this->vw->render((void *) this->model);
cJSON_Delete(choicesJson);
cJSON_Delete(parsedList);
}
catch (const char* msg){
this->vw->render((void *)msg);
throw msg;
}
}
char* ChoiceController::generateChoicesRequestJSON(ChoiceModel *cm){
cJSON *root, *params, *param;
root = cJSON_CreateObject();
cJSON_AddNumberToObject(root, "id", 0.0);
cJSON_AddItemToObject(root, "method", cJSON_CreateString("RPC.VoterChoices"));
params = cJSON_CreateArray();
cJSON_AddItemToObject(root, "params", params);
cJSON_AddItemToArray(params, param=cJSON_CreateObject());
cJSON_AddStringToObject(param, "AuthMethod", "ticket");
cJSON_AddStringToObject(param, "AuthToken", cm->authToken);
cJSON_AddStringToObject(param, "OS", "Operating System,2,0");
cJSON_AddStringToObject(param, "SessionID", cm->ssid);
char* pretty = cJSON_Print(root);
cJSON_Delete(root);
return pretty;
}
void ChoiceController::parse_object(cJSON *item, char* par){
cJSON *subitem=item->child;
if ( !subitem ) { // leaf nodes are candidates
while(item){
choice_t *c = new choice_t(); // create new choice element
c->candidate = (char *) malloc(strlen(item->valuestring)+1); // allocate memory for candidate field
memset(c->candidate, 0, strlen(item->valuestring) +1); // set content 0
memcpy(c->candidate, item->valuestring, strlen(item->valuestring)); // copy from json to struct field
c->code = (char *) malloc(strlen(item->string)+1); // allocate memory for code field
memset(c->code, 0, strlen(item->string) +1); // set content 0
memcpy(c->code, item->string, strlen(item->string)); // copy from json to struct field
c->party = (char *) malloc(strlen(par)+1); // allocate memory for party field
memset(c->party, 0, strlen(par) +1); // set content 0
memcpy(c->party, par, strlen(par)); // copy from json to struct field
this->model->choice_list->push_back(*c); // push choice element to vector (list of choices)
item = item->next; // go to next candidate
}
}
while (subitem){
// handle subitem
if (subitem->child) {
parse_object(subitem->child, subitem->string);
}
subitem=subitem->next;
}
}

View File

@@ -0,0 +1,335 @@
/**
* @file EncryptionController.cpp
* @brief EncryptionController implementation file
* */
#include "esp_log.h"
#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
#include "esp_system.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
extern "C" {
#include "mbedtls/bignum.h"
}
#include "controller.h"
#include "converter.h"
#ifndef modlength
#define modlength 96 /**< 3072 bits = 384 bytes = 96 words */
#endif
/**
* Struct to store ElGamal encryption results.
* g is group generator and pk is public key
* In general, ElGamal encyption cipher is a pair (c1, c2). But, we'll need randomness later for verification,
* so we also keep random number
* */
typedef struct{
mbedtls_mpi c1; /**< c1 = g^r mod p */
mbedtls_mpi c2; /**< c2 = c1*g^pk mod p */
mbedtls_mpi random; /**< r */
}elgamal_cipher_t;
typedef struct{
mbedtls_mpi* base;
mbedtls_mpi* exponent;
mbedtls_mpi* buff;
mbedtls_mpi* mod;
uint8_t flag;
int error;
}task_param_t;
void taskMont(void* data){
task_param_t* params = (task_param_t*) data;
params->error = mbedtls_mpi_exp_mod(params->buff, params->base, params->exponent, params->mod, NULL);
params->flag = 1;
for(;;){
}
}
/**
* @brief secure big random generator
*
* Multi-precision integer (mpi) is an array of 64bit unsigned integers in big endian representation. This method
* creates grows input parameter to the length of modlength, then fills the array with random unsigned integers using
* esp_random()
*
* @param mpi The destination MPI. This must point to an initialized MPI.
*
*/
void mpi_RandomNumber(mbedtls_mpi * mpi ){
srand ( time(NULL) );
mbedtls_mpi_grow(mpi, modlength);
for(int i = 0;i < modlength; i++){
mpi->p[i] = (mbedtls_mpi_uint) esp_random();
}
}
/**
* @brief fast multi-core ElGamal implementation
* ElGamal is a homomorphic encryption method used in ivxv protocol.
* 3072 bit long keys provide 128 bit security.
*
* @param pk public key
* @param m encoded plaintext
* @param res pointer to cipher struct
* @param mod pointer to modulus variable
*
* @returns 0 if successful, -1 otherwise
* */
int ElGamalEncrypt(mbedtls_mpi * pk, mbedtls_mpi* m, elgamal_cipher_t * res, mbedtls_mpi * mod){
esp_err_t error;
mbedtls_mpi rnd, g, c1, c2;
mbedtls_mpi_init(&c1);
mbedtls_mpi_init(&c2);
mbedtls_mpi_init(&rnd);
mbedtls_mpi_init(&g);
mpi_RandomNumber(&rnd);
error = mbedtls_mpi_read_string(&g, 10, "2");
if(error) return error;
task_param_t * c1Params = new task_param_t();
c1Params->base = &g;
c1Params->exponent = &rnd;
c1Params->buff = &c1;
c1Params->mod = mod;
TaskHandle_t c1Task;
xTaskCreatePinnedToCore(taskMont, "C1_Mont", 4192, (void *) c1Params, 0 , &c1Task, 1);
mbedtls_mpi c3;
mbedtls_mpi_init(&c3);
error = mbedtls_mpi_exp_mod(&c3, pk, &rnd, mod, NULL);
if(error) return error;
error = esp_mpi_mul_mpi_mod(&c2, &c3, m, mod);
if(error) return error;
while(!c1Params->flag){
}
vTaskDelete(c1Task);
if(c1Params->error) return c1Params->error;
error = mbedtls_mpi_copy(&res->c1, &c1); if(error) return error;
error = mbedtls_mpi_copy(&res->c2, &c2); if(error) return error;
error = mbedtls_mpi_copy(&res->random, &rnd); if(error) return error;
mbedtls_mpi_free(&c1);
mbedtls_mpi_free(&c2);
mbedtls_mpi_free(&c3);
mbedtls_mpi_free(&rnd);
mbedtls_mpi_free(&g);
return 0;
}
EncryptionController::EncryptionController(BaseModel *model){
this->model = static_cast<EncryptionModel *>(model);
this->vw = new IndexView();
}
void EncryptionController::index(){
try{
this->vw->render((void *)"Encrypting ballot...");
unsigned char * PK_DER = (unsigned char *) malloc(1024);
memset(PK_DER, 0, 1024);
size_t lenPK = 1024;
memset(PK_DER, 0 , lenPK);
int error = converter::convert_pem_to_der(this->model->keypem, this->model->keypem_length, PK_DER, &lenPK); //! * First, convert election public key file to DER format*/
if(error) throw "unable to parse pem file";
unsigned char *p = PK_DER;
const unsigned char *end = p + lenPK;
size_t len; int i;
mbedtls_asn1_buf oid, oid_params;
memset( &oid, 0, sizeof( mbedtls_asn1_buf ) );
memset( &oid_params, 0, sizeof( mbedtls_asn1_buf ) );
mbedtls_mpi mod;
mbedtls_mpi_init(&mod);
//! * Start decoding ASN1 DER encoded key contents using mbedtls_asn1 library /
error = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); if(error) throw "ASN1 parse error";
error = mbedtls_asn1_get_alg(&p, end, &oid, &oid_params); if(error) throw "ASN1 parse error";
unsigned char * q = oid_params.p;
const unsigned char *q_end = q + oid_params.len;
error = mbedtls_asn1_get_mpi(&q, q_end, &mod); if(error) throw "ASN1 parse error"; //! * extract mod
error = mbedtls_asn1_get_tag(&q, q_end, &len, MBEDTLS_ASN1_INTEGER); if(error) throw "ASN1 parse error";
q += len;
error = mbedtls_asn1_get_tag(&q, q_end, &len, 0x1B ); if(error) throw "ASN1 parse error"; //! * extract electionID
this->model->election_id = (char *)malloc(len+1);
memset(this->model->election_id, 0, len+1);
memcpy(this->model->election_id, q, len);
mbedtls_asn1_bitstring bs = {0, 0 , NULL};
error = mbedtls_asn1_get_bitstring(&p, end, &bs); if(error) throw "ASN1 parse error";
p = bs.p;
error = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); if(error) throw "ASN1 parse error";
mbedtls_mpi key;
mbedtls_mpi_init(&key);
error = mbedtls_asn1_get_mpi(&p, end, &key); if(error) throw "ASN1 parse error"; //! * extract 3072bit public key
if(error) throw "key error";
if(p != end){
printf("Error. Didn't parse until the end\n");
throw "Incomplete ASN";
}
/**
* * Then, pad the ballot using following scheme
*
* padded-ballot = '\x00' '\x01' *'\xff' '\x00' ballot
*
*/
len = mbedtls_mpi_bitlen(&key);
len /= 8;
unsigned char *padded_ballot = (unsigned char *) malloc(len + 1);
memset(padded_ballot, 0xFF, len);
padded_ballot[0] = 0x00;
padded_ballot[1] = 0x01;
padded_ballot[len] = 0x00; // end of string (null-terminated)
padded_ballot[len - strlen(this->model->ballot) - 1] = 0x00;
for(i=1;i<=strlen(this->model->ballot);i++){
padded_ballot[len - i] = this->model->ballot[strlen(this->model->ballot) - i];
}
mbedtls_mpi plaintext;
mbedtls_mpi_init(&plaintext);
error = mbedtls_mpi_read_binary(&plaintext, padded_ballot, len); //! * convert padded ballot to group element
if (error) throw "ballot mpi conversion error";
free(padded_ballot);
mbedtls_mpi pq; //pq = (modulus-1)/2
mbedtls_mpi_init(&pq);
error = mbedtls_mpi_sub_int(&pq, &mod, 1); if(error) throw "error at mod - 1";
error = mbedtls_mpi_div_int(&pq, NULL, &pq, 2); if(error) throw "error at pq // 2";
//! * encode plaintext as a quadratic residue in the set
mbedtls_mpi encoded_plaintext, res;
mbedtls_mpi_init(&encoded_plaintext);
mbedtls_mpi_init(&res);
error = mbedtls_mpi_exp_mod(&res, &plaintext, &pq, &mod, NULL); if(error) throw "quadratic residue check error";
if( mbedtls_mpi_cmp_int(&res, 1) == 0){
error = mbedtls_mpi_copy(&encoded_plaintext, &plaintext);
if(error) throw "mpi copy error";
ESP_LOGI(TAG, "quadratic residue");
}else{
error = mbedtls_mpi_sub_mpi(&encoded_plaintext, &mod, &plaintext);
if(error) throw "mpi substract error";
}
elgamal_cipher_t *cipher = new elgamal_cipher_t();
mbedtls_mpi_init(&cipher->c1);
mbedtls_mpi_init(&cipher->c2);
mbedtls_mpi_init(&cipher->random);
time_t before_enc, after_enc;
time(&before_enc);
error = ElGamalEncrypt(&key, &encoded_plaintext, cipher, &mod); //! * encrypt and store results in cipher
time(&after_enc);
ESP_LOGI(TAG, "encryption took %ld seconds", (long) after_enc - (long) before_enc);
if(error) throw "Encryption error";
ESP_LOGI(TAG, "plaintext encrypted");
unsigned char * rndASN = (unsigned char*) malloc( len );
memset(rndASN, 0, len);
error = mbedtls_mpi_write_binary(&cipher->random, rndASN, len ); //! * convert used random number into hex and base64 encode it.
if(error) throw "error in rnd binary export";
this->model->rndBase64 = (unsigned char *)malloc(2*len);
memset(this->model->rndBase64,0, 2*len);
error = mbedtls_base64_encode(this->model->rndBase64, 2*len, &len, rndASN, len);
if(error) throw "Random number hash error";
//! * start DER encoding of cipher
puts((char *) this->model->rndBase64);
unsigned char * asnder = (unsigned char *) malloc(1220);
memset(asnder, 0, 1220);
unsigned char *asn_end = asnder + 1220;
int succ, total_succ = 0;
succ = mbedtls_asn1_write_mpi(&asn_end, asnder, &cipher->c2); //! * convert second component of cipher into hex and encode as ASN1 Integer
total_succ+=succ;
succ = mbedtls_asn1_write_mpi(&asn_end, asnder, &cipher->c1); //! * convert first component of cipher into hex and encode as ASN1 Integer
total_succ+=succ;
succ = mbedtls_asn1_write_len(&asn_end, asnder, total_succ);
total_succ+=succ;
succ = mbedtls_asn1_write_tag(&asn_end, asnder, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
total_succ+=succ;
int params_total = 0;
succ = mbedtls_asn1_write_raw_buffer(&asn_end, asnder, oid_params.p, oid_params.len);
params_total+=succ;
succ = mbedtls_asn1_write_len(&asn_end, asnder, oid_params.len);
params_total+=succ;
succ = mbedtls_asn1_write_tag(&asn_end, asnder, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
params_total+=succ;
succ = mbedtls_asn1_write_algorithm_identifier(&asn_end, asnder, (char *) oid.p, oid.len, params_total); //! * append encryption algorithm identifier to DER
total_succ+=succ;
succ = mbedtls_asn1_write_len(&asn_end, asnder, total_succ);
total_succ+=succ;
succ = mbedtls_asn1_write_tag(&asn_end, asnder, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
total_succ+=succ;
this->model->ballotASN = (unsigned char *) malloc(total_succ+1);
memset(this->model->ballotASN, 0, total_succ + 1);
memcpy(this->model->ballotASN, asnder+1220-total_succ, total_succ);
this->model->ballotLength = total_succ;
free(asnder);
free(PK_DER);
free(rndASN);
mbedtls_mpi_free(&mod);
mbedtls_mpi_free(&pq);
mbedtls_mpi_free(&key);
mbedtls_mpi_free(&plaintext);
mbedtls_mpi_free(&encoded_plaintext);
mbedtls_mpi_free(&res);
mbedtls_mpi_free(&cipher->c1);
mbedtls_mpi_free(&cipher->c2);
mbedtls_mpi_free(&cipher->random);
// SHA256 hash + Base64 asnder
this->model->ballotFileName = (char *)malloc(65);
memset(this->model->ballotFileName, 0, 65);
snprintf(this->model->ballotFileName, 64, "%s.%s.ballot", this->model->election_id, "1"); //! * set filename for DER encoded encrypted ballot
this->model->ballotHash = converter::base64hash(this->model->ballotASN, this->model->ballotLength); //! * hash and base64 encode it for future references
ESP_LOGI(TAG, "ballot hash: %s\n",(char *)this->model->ballotHash);
this->vw->render((void*) "Encrypted");
}
catch (const char* msg){
this->vw->render((void *)msg);
throw msg;
}
}

View File

@@ -0,0 +1,16 @@
/**
* @file IndexController.cpp
* @brief IndexController implementation file
* */
#include "controller.h"
IndexController::IndexController(){
this->vw = new IndexView();
}
void IndexController::index(){
this->vw->render((void *) "Index ctrl");
}

View File

@@ -0,0 +1,344 @@
/**
* @file SignatureController.cpp
* @brief SignatureController implementation file
* */
#include "cJSON.h"
#include "controller.h"
#include "converter.h"
SignatureController::SignatureController(BaseModel *model, const char * sni){
this->vw = new IndexView();
this->model = static_cast<SignatureModel *>(model);
this->sni = sni;
}
void SignatureController::index(){
try{
this->vw->render((void *) "Getting certificate");
// Get certificate
ESP_LOGI(TAG, "get certificate request sent");
cJSON *certificateJson = RPC::Instance().send_json_rpc(sni, generateGetCertificateRequestJSON()); //! * Get certificate from server
if(certificateJson == NULL){
throw "Empty response";
}
if( cJSON_GetObjectItem(certificateJson, "error")->valuestring != NULL){ //! * check for error message
throw cJSON_GetObjectItem(certificateJson, "error")->valuestring;
}
cJSON * result = cJSON_GetObjectItem(certificateJson, "result"); //! * if no error, parse JSON result
size_t len = strlen(cJSON_GetObjectItem(result, "Certificate")->valuestring);
char * signCertificate = (char *)malloc(len + 1); //! * allocate enough memory to store certificate
if(signCertificate == NULL){
ESP_LOGE("ivxv", "unable to allocate memory to store certificate");
throw "Insufficient memory";
}
memset(signCertificate, 0, len + 1);
memcpy(signCertificate, cJSON_GetObjectItem(result, "Certificate")->valuestring, len);
len = (len/64) *65 + len%64;
this->model->certificate = (char *)malloc(len+1);
memset(this->model->certificate,0, len+1); //! * base64 decode and add line breaks after 64th character
int i, j=0;
for(i=0;i<strlen(signCertificate);i++){
this->model->certificate[j++] = signCertificate[i];
if(i && (i+1)%64==0){
this->model->certificate[j++] = '\n';
}
}
cJSON_Delete(certificateJson);
// ASN1 Parse certificate
// decode certificate, parse it, hash it, base64 encode it.
unsigned char *certificateDER = (unsigned char *) malloc(1600);
if(certificateDER == NULL){
ESP_LOGE("ivxv", "unable to allocate memory to store certificate DER");
throw "Insufficient memory";
}
memset(certificateDER, 0, 1600);
int error = mbedtls_base64_decode(certificateDER, 1600, &len, (unsigned char *)signCertificate, strlen(signCertificate));
if(error) throw "Error";
int der_length = len;
unsigned char* p = certificateDER;
unsigned char* end = p + len;
int version;
mbedtls_mpi serial = {0,0,0};
char * serialBuf = (char *)malloc(128);
memset(serialBuf, 0 , 128);
error = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
if(error) throw "Error";
error = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
if(error) throw "Error";
error = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_CONTEXT_SPECIFIC);
if(error) throw "Error";
error = mbedtls_asn1_get_int(&p, end, &version);
if(error) throw "Error";
error = mbedtls_asn1_get_mpi(&p, end, &serial);
if(error) throw "Error";
error = mbedtls_mpi_write_string(&serial, 10, serialBuf, 128, &len); //! * parse certificate serial number
if(error) throw "Error";
mbedtls_asn1_buf oid, oid_params;
memset( &oid, 0, sizeof( mbedtls_asn1_buf ) );
memset( &oid_params, 0, sizeof( mbedtls_asn1_buf ) );
error = mbedtls_asn1_get_alg_null(&p, end, &oid);
if(error) throw "Error";
error = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
if(error) throw "Error";
char *DN = (char *)malloc(256);
memset(DN,0, 256);
char* CN = (char *) malloc(32);
memset(CN,0, 32);
char* C = (char *) malloc(32);
memset(C,0, 32);
char* O = (char *) malloc(32);
memset(O,0, 32);
char* org = (char *) malloc(32);
memset(org,0, 32);
for(i=0;i<4;i++){
memset( &oid, 0, sizeof( mbedtls_asn1_buf ) );
memset( &oid_params, 0, sizeof( mbedtls_asn1_buf ) );
error = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SET);
if(error) {
throw "Error";
}
error = mbedtls_asn1_get_alg(&p, end, &oid, &oid_params);
if(error) {
throw "Error";
}
if( *(oid.p + 2) == 0x03) strncat(CN, (char *) oid_params.p, oid_params.len);
if( *(oid.p + 2) == 0x06) strncat(C, (char *) oid_params.p, oid_params.len);
if( *(oid.p + 2) == 0x0A) strncat(O, (char *) oid_params.p, oid_params.len);
if( *(oid.p + 2) == 0x61) strncat(org, (char *) oid_params.p, oid_params.len);
}
if(error) throw "Error";
snprintf(DN, 256, "CN=%s,organizationIdentifier=%s,O=%s,C=%s", CN, org, O, C); // copy certificate DN field
unsigned char *certificateHashBase64 = converter::base64hash(certificateDER, der_length);
free(certificateDER);
free(signCertificate);
// Create signedProperties
char *signingTime = (char *) malloc(64);
memset(signingTime, 0, 64);
time_t now;
time(&now);
strftime(signingTime, 21,"%Y-%m-%dT%XZ", localtime(&now)); //! * get signing time
ESP_LOGI(TAG, "time: %s", signingTime);
this->model->SP_XML = (char *) malloc(2048);
memset(this->model->SP_XML, 0, 2048);
snprintf(this->model->SP_XML, 2048, SignedProperties, signingTime, (char *)certificateHashBase64, DN, serialBuf); //! * create SignedProperties XML element
converter::xmlc14n11(this->model->SP_XML, 2048); //! * canonicalize SignedProperties
unsigned char *spHashBase64 = converter::base64hash((unsigned char *)this->model->SP_XML, strlen(this->model->SP_XML)); //! * hash (sha256) + base64 encode canonicalize
// Create signedInfo
this->model->SI_XML = (char *) malloc(2048);
memset(this->model->SI_XML, 0, 2048);
snprintf(this->model->SI_XML, 2048, SignedInfo, this->model->ballotFileName ,(char *) this->model->ballotHash, spHashBase64); //! create DignedInfo XML element
converter::xmlc14n11(this->model->SI_XML, 2048);
this->model->SignedInfoHash = converter::base64hash((unsigned char *)this->model->SI_XML, strlen(this->model->SI_XML));//! * hash(sha268) andbase64 encode canonicalize
}
catch (const char* msg){
this->vw->render((void *)msg);
throw msg;
}
}
char* SignatureController::generateGetCertificateRequestJSON(){
cJSON *root, *params, *param;
root = cJSON_CreateObject();
cJSON_AddNumberToObject(root, "id", 0.0);
cJSON_AddItemToObject(root, "method", cJSON_CreateString("RPC.GetCertificate"));
params = cJSON_CreateArray();
cJSON_AddItemToObject(root, "params", params);
cJSON_AddItemToArray(params, param=cJSON_CreateObject());
cJSON_AddStringToObject(param, "AuthMethod", "ticket");
cJSON_AddStringToObject(param, "AuthToken", this->model->authToken);
cJSON_AddStringToObject(param, "OS", "FreeRTOS");
cJSON_AddStringToObject(param, "PhoneNo", this->model->phone);
cJSON_AddStringToObject(param, "IDCode", this->model->ID);
cJSON_AddStringToObject(param, "SessionID", this->model->ssid);
char* pretty = cJSON_Print(root);
cJSON_Delete(root);
return pretty;
}
char* SignatureController::generateSignRequestJSON(){
cJSON *root, *params, *param;
root = cJSON_CreateObject();
cJSON_AddNumberToObject(root, "id", 0.0);
cJSON_AddItemToObject(root, "method", cJSON_CreateString("RPC.Sign"));
params = cJSON_CreateArray();
cJSON_AddItemToObject(root, "params", params);
cJSON_AddItemToArray(params, param=cJSON_CreateObject());
cJSON_AddStringToObject(param, "AuthMethod", "ticket");
cJSON_AddStringToObject(param, "AuthToken", this->model->authToken);
cJSON_AddStringToObject(param, "Hash", (char *) this->model->SignedInfoHash);
cJSON_AddStringToObject(param, "OS", "FreeRTOS");
cJSON_AddStringToObject(param, "PhoneNo", this->model->phone);
cJSON_AddStringToObject(param, "IDCode", this->model->ID);
cJSON_AddStringToObject(param, "SessionID", this->model->ssid);
char* pretty = cJSON_Print(root);
cJSON_Delete(root);
return pretty;
}
void SignatureController::sign(){
try{
ESP_LOGI(TAG, "signing request sent");
cJSON * signJson = RPC::Instance().send_json_rpc(sni, generateSignRequestJSON());
if(signJson == NULL){
throw "Empty response";
}
if(cJSON_GetObjectItem(signJson, "error")->valuestring != NULL){
throw cJSON_GetObjectItem(signJson, "error")->valuestring;
}
cJSON * params = cJSON_GetObjectItem(signJson, "result");
size_t len = strlen(cJSON_GetObjectItem(params, "SessionCode")->valuestring);
this->model->sscode = (char *) malloc(len +1);
memset(this->model->sscode, 0, len + 1);
memcpy(this->model->sscode, cJSON_GetObjectItem(params, "SessionCode")->valuestring, len);
char pin[25];
bzero(pin, 25);
strncat(pin, "Verification code: ", 20);
strncat(pin, cJSON_GetObjectItem(params, "ChallengeID")->valuestring, 5);
this->vw->render((void *) pin);
cJSON_Delete(signJson);
}
catch (const char* msg){
this->vw->render((void *)msg);
throw msg;
}
}
char* SignatureController::generateSignStatusRequestJSON(){
cJSON *root, *params, *param;
root = cJSON_CreateObject();
cJSON_AddNumberToObject(root, "id", 0.0);
cJSON_AddItemToObject(root, "method", cJSON_CreateString("RPC.SignStatus"));
params = cJSON_CreateArray();
cJSON_AddItemToObject(root, "params", params);
cJSON_AddItemToArray(params, param = cJSON_CreateObject());
cJSON_AddStringToObject(param, "OS", "FreeRTOS");
cJSON_AddStringToObject(param, "SessionID", this->model->ssid);
cJSON_AddStringToObject(param, "SessionCode", this->model->sscode);
char* pretty = cJSON_Print(root);
cJSON_Delete(root);
return pretty;
}
void SignatureController::status(){
try{
// Poll RPC.SignStatus
cJSON *signJson, *result;
char* signStatusReq = generateSignStatusRequestJSON();
do{
ESP_LOGI(TAG, "waiting to confirm pin");
for(int i=10; i>0; i--){
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
// check status
ESP_LOGI(TAG, "signing status request sent");
signJson = RPC::Instance().send_json_rpc(sni, signStatusReq );
if(signJson == NULL){
throw "Empty response";
}
result = cJSON_GetObjectItem(signJson, "result");
if( cJSON_GetObjectItem(signJson, "error")->valuestring != NULL){
throw cJSON_GetObjectItem(signJson, "error")->valuestring;
}
if(strcmp(cJSON_GetObjectItem(result, "Status")->valuestring, "POLL") == 0){
continue;
}
if(strcmp(cJSON_GetObjectItem(result, "Status")->valuestring, "OK") == 0){
size_t len = strlen(cJSON_GetObjectItem(result, "Signature")->valuestring);
this->model->signature = (char *) malloc(len + 1);
memset(this->model->signature, 0, len + 1);
memcpy(this->model->signature, cJSON_GetObjectItem(result, "Signature")->valuestring, len);
this->vw->render((void *) "PIN CONFIRMED!");
break;
}
cJSON_free(signJson);
}
while(1);
this->vw->render((void *)"Successfully signed");
ESP_LOGI(TAG, "succesfully signed");
cJSON_Delete(signJson);
}
catch (const char* msg){
this->vw->render((void *)msg);
throw msg;
}
}
void SignatureController::combine(){
try{
// Create SignatureValue
this->model->SV_XML = (char *) malloc(512);
memset(this->model->SV_XML, 0, 512);
snprintf(this->model->SV_XML, 511, SignatureValue, this->model->signature);
// merge above 3 and create signature0.xml
this->model->Signature = (char *) malloc(10240);
if(this->model->Signature == NULL){
throw "Insufficient memory";
}
memset(this->model->Signature, 0, 10240);
snprintf(this->model->Signature, 10240, Signature, this->model->SI_XML, this->model->SV_XML, this->model->certificate, this->model->SP_XML);
free(this->model->SI_XML);
free(this->model->SP_XML);
free(this->model->SV_XML);
free(this->model->certificate);
}
catch (const char* msg){
this->vw->render((void *)msg);
throw msg;
}
}

View File

@@ -0,0 +1,69 @@
/**
* @file VoteController.cpp
* @brief VoteController implementation file
* */
#include "cJSON.h"
#include "controller.h"
VoteController::VoteController(BaseModel *model, const char * sni){
this->vw = new IndexView();
this->model = static_cast<VoteModel *>(model);
this->sni = sni;
}
void VoteController::index(){
try{
// send last hash to vote and get voteID
ESP_LOGI(TAG, "final voting request sent");
cJSON* voteJson = RPC::Instance().send_json_rpc(sni, generateVoteRequestJSON()); //! * send signed digital ballot to voting service
if(voteJson == NULL){
throw "Empty response";
}
if(cJSON_GetObjectItem(voteJson, "error")->valuestring != NULL){ //! * check error message
throw cJSON_GetObjectItem(voteJson, "error")->valuestring;
}
cJSON* result = cJSON_GetObjectItem(voteJson, "result"); //! * if no error, parse result
size_t len = strlen(cJSON_GetObjectItem(result, "VoteID")->valuestring);
this->model->voteID = (char *) malloc(len+1); //! * allocate memory and store voteID
memset(this->model->voteID, 0, len+1);
memcpy(this->model->voteID, cJSON_GetObjectItem(result, "VoteID")->valuestring,len);
cJSON_Delete(voteJson);
ESP_LOGI(TAG, "voteID: %s", this->model->voteID);
this->vw->render((void *) "Vote casted");
}
catch (const char* msg){
this->vw->render((void *)msg);
throw msg;
}
}
char* VoteController::generateVoteRequestJSON(){
cJSON *root, *params, *param;
root = cJSON_CreateObject();
cJSON_AddNumberToObject(root, "id", 0.0);
cJSON_AddItemToObject(root, "method", cJSON_CreateString("RPC.Vote"));
params = cJSON_CreateArray();
cJSON_AddItemToObject(root, "params", params);
cJSON_AddItemToArray(params, param=cJSON_CreateObject());
cJSON_AddStringToObject(param, "AuthMethod", "ticket");
cJSON_AddStringToObject(param, "AuthToken", this->model->authToken);
cJSON_AddStringToObject(param, "Choices", this->model->choices);
cJSON_AddStringToObject(param, "SessionID", this->model->ssid);
cJSON_AddStringToObject(param, "OS", "FreeRTOS");
cJSON_AddStringToObject(param, "Type", "bdoc");
cJSON_AddStringToObject(param, "Vote", (char *) this->model->voteBase64);
char* pretty = cJSON_Print(root);
cJSON_Delete(root);
return pretty;
}

View File

@@ -0,0 +1,89 @@
/**
* @file ZipController.cpp
* @brief ZipController implementation file
* */
#include "miniz.h"
#include "controller.h"
ZipController::ZipController(BaseModel *model){
this->vw = new IndexView();
this->model = static_cast<ZipModel *>(model);
}
void ZipController::index(){
try{
// create a container with .ballot and .xml files
mz_bool status;
mz_zip_archive bdoc;
mz_zip_zero_struct(&bdoc);
size_t len;
status = mz_zip_writer_init_heap(&bdoc, 0,10240); //! * init zip heap
if(!status){
throw mz_zip_get_error_string(bdoc.m_last_error);
}
// Add mimefile
char *mimetype = (char *) "application/vnd.etsi.asic-e+zip"; //! * add mimetype file to zip
status = mz_zip_writer_add_mem(&bdoc, "mimetype", (void *) mimetype, strlen(mimetype), MZ_NO_COMPRESSION);
if (!status){
throw mz_zip_get_error_string(bdoc.m_last_error);
}
// Add manifest.xml
char* manifestXML = (char *) malloc(512);
memset(manifestXML,0,512);
snprintf(manifestXML, 512, manifest, "EP2065.1.ballot");
status = mz_zip_writer_add_mem(&bdoc, "META-INF/manifest.xml", (void *)manifestXML, strlen(manifestXML), MZ_NO_COMPRESSION); //! * add manifest.xml file to zip
if (!status){
throw mz_zip_get_error_string(bdoc.m_last_error);
}
// Add ballot file
status = mz_zip_writer_add_mem(&bdoc, this->model->ballotFileName, (void *) this->model->ballot, this->model->ballotLength, MZ_NO_COMPRESSION); //! * add ballot file to zip
if (!status){
throw mz_zip_get_error_string(bdoc.m_last_error);
}
// Add signature0.xml
status = mz_zip_writer_add_mem(&bdoc, "META-INF/signatures0.xml", (void *) this->model->Signature, strlen(this->model->Signature), MZ_NO_COMPRESSION); //! * add signature file to zip
if (!status){
throw mz_zip_get_error_string(bdoc.m_last_error);
}
// Close zip file
void *zipBuf;
status = mz_zip_writer_finalize_heap_archive(&bdoc, &zipBuf, &len);
if (!status){
throw mz_zip_get_error_string(bdoc.m_last_error);
}
status = mz_zip_writer_end(&bdoc);
if (!status){
throw "zip end failed";
}
// hash container
this->model->voteBase64 = (unsigned char *)malloc(2*len);
if(this->model->voteBase64 == NULL){
throw "Insufficient memory";
}
memset(this->model->voteBase64, 0, 2*len);
int error = mbedtls_base64_encode(this->model->voteBase64, 2*len - 1, &len, (unsigned char *) zipBuf, len); //! * base64 encode zip file
if(error) {
throw "Base64 encode error";
}
this->vw->render((void*) "Signature complete");
mz_free(zipBuf);
free(this->model->Signature);
}
catch (const char* msg){
this->vw->render((void *)msg);
throw msg;
}
}

View File

@@ -0,0 +1,13 @@
/**
* @file BluetoothModel.cpp
* @brief BluetoothModel implementation file
* */
#include "model.h"
BluetoothModel::BluetoothModel(char *ssid, char* voteID, unsigned char * rndBase64){
this->ssid = ssid;
this->voteID = voteID;
this->rndBase64 = rndBase64;
}

View File

@@ -0,0 +1,16 @@
/**
* @file ChoiceModel.cpp
* @brief ChoiceModel implementation file
* */
#include <vector>
#include "model.h"
ChoiceModel::ChoiceModel(char* ssid, char * authToken){
this->ssid = ssid;
this->authToken = authToken;
this->choices = NULL;
this->choice_list = new std::vector<choice_t>();
this->ballot = NULL;
}

View File

@@ -0,0 +1,21 @@
/**
* @file EncryptionModel.cpp
* @brief EncryptionModel implementation file
* */
#include "model.h"
EncryptionModel::EncryptionModel(char* ballot, char* ssid, char * authToken, const uint8_t keypem[], int keypem_length){
this->ballot = ballot;
this->ballotASN = NULL;
this->ballotHash = NULL;
this->ballotFileName = NULL;
this->ballotLength = 0;
this->rndBase64 = NULL;
this->authToken = authToken;
this->ssid = ssid;
this->keypem = keypem;
this->keypem_length = keypem_length;
this->election_id = NULL;
}

View File

@@ -0,0 +1,30 @@
/**
* @file SignatureModel.cpp
* @brief SignatureModel implementation file
* */
#include "string.h"
#include "model.h"
SignatureModel::SignatureModel(char* ssid, char * authToken, char* phone, char* id, unsigned char* ballotHash, size_t ballotLength, char* ballotFileName){
this->ssid = ssid;
this->authToken = authToken;
this->sscode = NULL;
memset(this->ID, 0, 12);
memset(this->phone, 0, 13);
strncpy(this->ID, id, 11);
strncpy(this->phone, phone, 12);
this->ballotHash = ballotHash;
this->ballotLength = ballotLength;
this->ballotFileName = ballotFileName;
this->SignedInfoHash = NULL;
this->signature = NULL;
this->Signature = NULL;
this->certificate = NULL;
this->SI_XML = NULL;
this->SP_XML = NULL;
this->SV_XML = NULL;
}

View File

@@ -0,0 +1,19 @@
/**
* @file UserModel.cpp
* @brief UserModel implementation file
* */
#include "string.h"
#include "model.h"
UserModel::UserModel(char* id, char* phone){
memset(this->ID, 0, 12);
memset(this->phone, 0, 12);
strncpy(this->ID, id, 11);
strncpy(this->phone, phone, 12);
ssid = NULL;
authToken = NULL;
sscode = NULL;
}

View File

@@ -0,0 +1,20 @@
/**
* @file VoteModel.cpp
* @brief VoteModel implementation file
* */
#include "string.h";
#include "model.h"
VoteModel::VoteModel(char* ssid, char* authToken, unsigned char* voteBase64, char* choices){
this->ssid = ssid;
this->authToken = authToken;
this->choices = choices;
this->voteBase64 = voteBase64;
this->voteID = NULL;
}

View File

@@ -0,0 +1,19 @@
/**
* @file ZipModel.cpp
* @brief ZipModel implementation file
* */
#include "string.h"
#include "model.h"
ZipModel::ZipModel(unsigned char* ballot, char* ballotFileName, size_t ballotLength, char* Signature){
this->ballot = ballot;
this->ballotFileName = ballotFileName;
this->ballotLength = ballotLength;
this->Signature = Signature;
this->authToken = NULL;
this->ssid = NULL;
this->voteBase64 = NULL;
}

View File

@@ -0,0 +1,345 @@
/**
* @file BLEModule.cpp
* @brief BLEModule implementation file
* */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_bt.h"
#include "esp_gap_ble_api.h"
#include "esp_gatts_api.h"
#include "esp_bt_defs.h"
#include "esp_bt_main.h"
#include "esp_gatt_common_api.h"
#include "module.h"
static uint8_t adv_config_done = 0;
#define adv_config_flag (1 << 0)
#define scan_rsp_config_flag (1 << 1)
#define GATTS_TAG "GATTS"
static esp_ble_adv_params_t adv_params = {
.adv_int_min = 0x20,
.adv_int_max = 0x40,
.adv_type = ADV_TYPE_IND,
.own_addr_type = BLE_ADDR_TYPE_RANDOM,
.peer_addr = {},
.peer_addr_type = {},
.channel_map = ADV_CHNL_ALL,
.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY,
};
static uint8_t adv_service_uuid128[32] = {
/* LSB <--------------------------------------------------------------------------------> MSB */
//first uuid, 16bit, [12],[13] is the value
0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0xEE, 0x00, 0x00, 0x00,
//second uuid, 32bit, [12], [13], [14], [15] is the value
0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00,
};
// The length of adv data must be less than 31 bytes
//static uint8_t test_manufacturer[TEST_MANUFACTURER_DATA_LEN] = {0x12, 0x23, 0x45, 0x56};
//adv data
static esp_ble_adv_data_t adv_data = {
.set_scan_rsp = false,
.include_name = true,
.include_txpower = true,
.min_interval = 0x0006, //slave connection min interval, Time = min_interval * 1.25 msec
.max_interval = 0x0010, //slave connection max interval, Time = max_interval * 1.25 msec
.appearance = 0x00,
.manufacturer_len = 0, //TEST_MANUFACTURER_DATA_LEN,
.p_manufacturer_data = NULL, //&test_manufacturer[0],
.service_data_len = 0,
.p_service_data = NULL,
.service_uuid_len = sizeof(adv_service_uuid128),
.p_service_uuid = adv_service_uuid128,
.flag = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT),
};
// scan response data
static esp_ble_adv_data_t scan_rsp_data = {
.set_scan_rsp = true,
.include_name = true,
.include_txpower = true,
.min_interval = 0x0006,
.max_interval = 0x0010,
.appearance = 0x00,
.manufacturer_len = 0, //TEST_MANUFACTURER_DATA_LEN,
.p_manufacturer_data = NULL, //&test_manufacturer[0],
.service_data_len = 0,
.p_service_data = NULL,
.service_uuid_len = sizeof(adv_service_uuid128),
.p_service_uuid = adv_service_uuid128,
.flag = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT),
};
#define adv_config_flag (1 << 0)
#define scan_rsp_config_flag (1 << 1)
static char *esp_key_type_to_str(esp_ble_key_type_t key_type){
char *key_str = NULL;
switch(key_type) {
case ESP_LE_KEY_NONE:
key_str = "ESP_LE_KEY_NONE";
break;
case ESP_LE_KEY_PENC:
key_str = "ESP_LE_KEY_PENC";
break;
case ESP_LE_KEY_PID:
key_str = "ESP_LE_KEY_PID";
break;
case ESP_LE_KEY_PCSRK:
key_str = "ESP_LE_KEY_PCSRK";
break;
case ESP_LE_KEY_PLK:
key_str = "ESP_LE_KEY_PLK";
break;
case ESP_LE_KEY_LLK:
key_str = "ESP_LE_KEY_LLK";
break;
case ESP_LE_KEY_LENC:
key_str = "ESP_LE_KEY_LENC";
break;
case ESP_LE_KEY_LID:
key_str = "ESP_LE_KEY_LID";
break;
case ESP_LE_KEY_LCSRK:
key_str = "ESP_LE_KEY_LCSRK";
break;
default:
key_str = "INVALID BLE KEY TYPE";
break;
}
return key_str;
}
static char *esp_auth_req_to_str(esp_ble_auth_req_t auth_req){
char *auth_str = NULL;
switch(auth_req) {
case ESP_LE_AUTH_NO_BOND:
auth_str = "ESP_LE_AUTH_NO_BOND";
break;
case ESP_LE_AUTH_BOND:
auth_str = "ESP_LE_AUTH_BOND";
break;
case ESP_LE_AUTH_REQ_MITM:
auth_str = "ESP_LE_AUTH_REQ_MITM";
break;
case ESP_LE_AUTH_REQ_BOND_MITM:
auth_str = "ESP_LE_AUTH_REQ_BOND_MITM";
break;
case ESP_LE_AUTH_REQ_SC_ONLY:
auth_str = "ESP_LE_AUTH_REQ_SC_ONLY";
break;
case ESP_LE_AUTH_REQ_SC_BOND:
auth_str = "ESP_LE_AUTH_REQ_SC_BOND";
break;
case ESP_LE_AUTH_REQ_SC_MITM:
auth_str = "ESP_LE_AUTH_REQ_SC_MITM";
break;
case ESP_LE_AUTH_REQ_SC_MITM_BOND:
auth_str = "ESP_LE_AUTH_REQ_SC_MITM_BOND";
break;
default:
auth_str = "INVALID BLE AUTH REQ";
break;
}
return auth_str;
}
/**
* @brief BLE GAP server event handler
*
* Allows to connect/disconnect bluetooth devices
*
* @param event gap event fired when connection state changes
* @param param event parameters
* */
static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param){
switch (event) { //! * look for event type
#ifdef CONFIG_SET_RAW_ADV_DATA
case ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT:
adv_config_done &= (~adv_config_flag);
if (adv_config_done==0){
esp_ble_gap_start_advertising(BLEModule::adv_params);
}
break;
case ESP_GAP_BLE_SCAN_RSP_DATA_RAW_SET_COMPLETE_EVT:
adv_config_done &= (~scan_rsp_config_flag);
if (adv_config_done==0){
esp_ble_gap_start_advertising(BLEModule::adv_params);
}
break;
#else
case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT:
adv_config_done &= (~adv_config_flag);
if (adv_config_done == 0){
esp_ble_gap_start_advertising(&adv_params); //! * start advertising bluetooth profile to nearby devices after advertisement parameters set
}
break;
case ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT:
adv_config_done &= (~scan_rsp_config_flag);
if (adv_config_done == 0){
esp_ble_gap_start_advertising(&adv_params); //! * start advertising bluetooth profile to nearby devices after nearby devices scanned
}
break;
#endif
case ESP_GAP_BLE_ADV_START_COMPLETE_EVT:
//! - advertising start complete event to indicate advertising start successfully or failed
if (param->adv_start_cmpl.status != ESP_BT_STATUS_SUCCESS) {
ESP_LOGE(GATTS_TAG, "Advertising start failed\n");
}
break;
case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT:
//! - advertising stop complete event to indicate advertising stop successfully or failed
if (param->adv_stop_cmpl.status != ESP_BT_STATUS_SUCCESS) {
ESP_LOGE(GATTS_TAG, "Advertising stop failed\n");
} else {
ESP_LOGI(GATTS_TAG, "Stop adv successfully\n");
}
break;
case ESP_GAP_BLE_PASSKEY_REQ_EVT: /* passkey request event */
ESP_LOGI(GATTS_TAG, "ESP_GAP_BLE_PASSKEY_REQ_EVT");
/* Call the following function to input the passkey which is displayed on the remote device */
esp_ble_passkey_reply(param->ble_security.ble_req.bd_addr, true, 0x00);
break;
case ESP_GAP_BLE_NC_REQ_EVT:
/* The app will receive this evt when the IO has DisplayYesNO capability and the peer device IO also has DisplayYesNo capability.
show the passkey number to the user to confirm it with the number displayed by peer device. */
esp_ble_confirm_reply(param->ble_security.ble_req.bd_addr, true);
ESP_LOGI(GATTS_TAG, "ESP_GAP_BLE_NC_REQ_EVT, the passkey Notify number:%d", param->ble_security.key_notif.passkey);
break;
case ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT: //! * handle event fired when connection params change
ESP_LOGI(GATTS_TAG, "update connection params status = %d, min_int = %d, max_int = %d,conn_int = %d,latency = %d, timeout = %d",
param->update_conn_params.status,
param->update_conn_params.min_int,
param->update_conn_params.max_int,
param->update_conn_params.conn_int,
param->update_conn_params.latency,
param->update_conn_params.timeout);
break;
case ESP_GAP_BLE_PASSKEY_NOTIF_EVT: ///the app will receive this evt when the IO has Output capability and the peer device IO has Input capability.
///show the passkey number to the user to input it in the peer device.
ESP_LOGI(GATTS_TAG, "The passkey Notify number:%06d", param->ble_security.key_notif.passkey);
break;
case ESP_GAP_BLE_KEY_EVT:
//shows the ble key info share with peer device to the user.
ESP_LOGI(GATTS_TAG, "key type = %s", esp_key_type_to_str(param->ble_security.ble_key.key_type));
break;
case ESP_GAP_BLE_SEC_REQ_EVT:
/* send the positive (true) security response to the peer device to accept the security request.
If not accept the security request, should send the security response with negative(false) accept value*/
esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, true);
break;
case ESP_GAP_BLE_AUTH_CMPL_EVT: {
esp_bd_addr_t bd_addr;
memcpy(bd_addr, param->ble_security.auth_cmpl.bd_addr, sizeof(esp_bd_addr_t));
ESP_LOGI(GATTS_TAG, "remote BD_ADDR: %08x%04x",\
(bd_addr[0] << 24) + (bd_addr[1] << 16) + (bd_addr[2] << 8) + bd_addr[3],
(bd_addr[4] << 8) + bd_addr[5]);
ESP_LOGI(GATTS_TAG, "address type = %d", param->ble_security.auth_cmpl.addr_type);
ESP_LOGI(GATTS_TAG, "pair status = %s",param->ble_security.auth_cmpl.success ? "success" : "fail");
if(!param->ble_security.auth_cmpl.success) {
ESP_LOGI(GATTS_TAG, "fail reason = 0x%x",param->ble_security.auth_cmpl.fail_reason);
} else {
ESP_LOGI(GATTS_TAG, "auth mode = %s",esp_auth_req_to_str(param->ble_security.auth_cmpl.auth_mode));
}
// show_bonded_devices();
break;
}
case ESP_GAP_BLE_REMOVE_BOND_DEV_COMPLETE_EVT: {
ESP_LOGD(GATTS_TAG, "ESP_GAP_BLE_REMOVE_BOND_DEV_COMPLETE_EVT status = %d", param->remove_bond_dev_cmpl.status);
ESP_LOGI(GATTS_TAG, "ESP_GAP_BLE_REMOVE_BOND_DEV");
ESP_LOGI(GATTS_TAG, "-----ESP_GAP_BLE_REMOVE_BOND_DEV----");
esp_log_buffer_hex(GATTS_TAG, (void *)param->remove_bond_dev_cmpl.bd_addr, sizeof(esp_bd_addr_t));
ESP_LOGI(GATTS_TAG, "------------------------------------");
break;
}
case ESP_GAP_BLE_SET_LOCAL_PRIVACY_COMPLETE_EVT:
if (param->local_privacy_cmpl.status != ESP_BT_STATUS_SUCCESS){
ESP_LOGE(GATTS_TAG, "config local privacy failed, error status = %x", param->local_privacy_cmpl.status);
break;
}
//config adv data
esp_err_t ret;
ret = esp_ble_gap_config_adv_data(&adv_data);
if (ret){
ESP_LOGE(GATTS_TAG, "config adv data failed, error code = %x", ret);
}
adv_config_done |= adv_config_flag;
//config scan response data
ret = esp_ble_gap_config_adv_data(&scan_rsp_data);
if (ret){
ESP_LOGE(GATTS_TAG, "config scan response data failed, error code = %x", ret);
}
adv_config_done |= scan_rsp_config_flag;
break;
default:
break;
}
}
BLEModule::BLEModule(){
this->adv = &adv_params;
}
esp_err_t BLEModule::init(){
ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT)); //! * start Bluetooth Mode (turn on bluetooth)
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
esp_err_t ret = esp_bt_controller_init(&bt_cfg); //! * initialize low-level Bluetooth controller
if (ret) {
ESP_LOGE(TAG, "%s initialize controller failed: %s\n", __func__, esp_err_to_name(ret));
return ret;
}
ret = esp_bt_controller_enable(ESP_BT_MODE_BLE); //! * enable Bluetooth Low Energy
if (ret) {
ESP_LOGE(TAG, "%s enable controller failed: %s\n", __func__, esp_err_to_name(ret));
return ret;
}
ret = esp_bluedroid_init(); //! * initialize Bluedroid api
if (ret) {
ESP_LOGE(TAG, "%s init bluetooth failed: %s\n", __func__, esp_err_to_name(ret));
return ret;
}
ret = esp_bluedroid_enable(); //! * enable Bluedroid api
if (ret) {
ESP_LOGE(TAG, "%s enable bluetooth failed: %s\n", __func__, esp_err_to_name(ret));
return ret;
}
ret = esp_ble_gap_register_callback(gap_event_handler); //! * register GAP event handler
if (ret){
ESP_LOGE(TAG, "gap register error, error code = %x", ret);
return ret;
}
return ESP_OK;
}
esp_err_t BLEModule::deinit(){
esp_err_t ret;
ret = esp_bluedroid_disable(); //! * disable Bluedroid api
if(ret) return ret;
ret = esp_bluedroid_deinit(); //! * deinitialize Bluedroid api
if(ret) return ret;
ret = esp_bt_controller_disable(); //! * disable bluetooth controller
if(ret) return ret;
ret = esp_bt_controller_deinit(); //! * deinitialize bluetooth controller (turn off)
if(ret) return ret;
return ESP_OK;
}

View File

@@ -0,0 +1,23 @@
/**
* @file NVSModule.cpp
* @brief NVSModule implementation file
* */
#include "nvs_flash.h"
#include "module.h"
esp_err_t NVSModule::init(){
ESP_LOGI(TAG, "initializing nvs");
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
return ret;
}
esp_err_t NVSModule::deinit(){
return nvs_flash_deinit();
}

View File

@@ -0,0 +1,92 @@
/**
* @file RPCModule.cpp
* @brief RPC implementation file
* */
#include "string.h"
#include "module.h"
RPC::RPC(){
this->cfg = new esp_tls_cfg_t();
this->server = NULL;
this->port = 443;
};
cJSON * RPC::send_json_rpc(const char* sni, char* req){
static char buf[512]; // tls connection buffer to read response
int length = 1024;
char *res; // buffer to store whole response body
res = (char *) malloc(length);
memset(res, 0, length);
int ret, len;
/* tls connection config. */
cfg->common_name = sni; //! set SNI extension
struct esp_tls *tls = esp_tls_conn_new(server, strlen(server),port, cfg); //! connect to defined address
if(tls != NULL) {
ESP_LOGI(TLS_TAG, "Connection established with %s", sni);
} else {
printf("available memory: %d\n", (int) heap_caps_get_free_size(MALLOC_CAP_32BIT | MALLOC_CAP_8BIT));
throw "Connection failed";
}
size_t written_bytes = 0;
do {
ret = esp_tls_conn_write(tls, req + written_bytes, strlen(req) - written_bytes); //! send rpc data
if (ret >= 0) {
written_bytes += ret;
} else if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
ESP_LOGE(TLS_TAG, "esp_tls_conn_write returned 0x%x", ret);
esp_tls_conn_delete(tls);
}
} while(written_bytes < strlen(req));
do
{
len = sizeof(buf) - 1;
bzero(buf, sizeof(buf));
ret = esp_tls_conn_read(tls, (char *)buf, len); //! recieve response in chunks
if(ret == MBEDTLS_ERR_SSL_WANT_WRITE || ret == MBEDTLS_ERR_SSL_WANT_READ)
continue;
if(ret < 0)
{
ESP_LOGE(TLS_TAG, "esp_tls_conn_read returned -0x%x", -ret);
break;
}
if(ret == 0)
{
ESP_LOGI(TLS_TAG, "connection closed");
break;
}
len = ret;
ESP_LOGD(TLS_TAG, "%d bytes read", len);
if(strlen(res) + len + 1 >= length){ //! In case no enough memory, extend res buffer
length += len; //! * increase by amount required
res = (char*) realloc(res, length); //! * reallocate memory
memset(res+length-len,0,len); //! * set new memory blocks 0 to assure the result is null-terminate string
}
memcpy(res + strlen(res), buf, len); //! write chunked response to buffer
} while(1);
cJSON *tmp = cJSON_Parse(res); //! parse response JSON into cJSON struct
free(res);
free(req);
esp_tls_conn_delete(tls);
return tmp;
}
RPC& RPC::Instance(){
static RPC instance;
return instance;
}

View File

@@ -0,0 +1,52 @@
/**
* @file RotaryEncodeModule.cpp
* @brief RotaryEncoderModule implementation file
* */
#include "module.h"
RotaryEncoderModule::RotaryEncoderModule(){};
void RotaryEncoderModule::setPins(gpio_num_t a, gpio_num_t b, gpio_num_t c){
ROT_ENC_A_GPIO = a;
ROT_ENC_B_GPIO = b;
ROT_ENC_C_GPIO = c;
ENABLE_HALF_STEPS = false;
FLIP_DIRECTION = false;
this->info = new rotary_encoder_info_t();
}
rotary_encoder_info_t* RotaryEncoderModule::getInfo() const{
return info;
}
esp_err_t RotaryEncoderModule::init(){
// Initialise the rotary encoder device with the GPIOs for A and B signals
ESP_ERROR_CHECK(rotary_encoder_init(info, ROT_ENC_A_GPIO, ROT_ENC_B_GPIO, ROT_ENC_C_GPIO));
ESP_ERROR_CHECK(rotary_encoder_enable_half_steps(info, ENABLE_HALF_STEPS));
if( FLIP_DIRECTION )
ESP_ERROR_CHECK(rotary_encoder_flip_direction(info));
// Create a queue for events from the rotary encoder driver.
// Tasks can read from this queue to receive up to date position information.
QueueHandle_t event_queue = rotary_encoder_create_queue();
ESP_ERROR_CHECK(rotary_encoder_set_queue(info, event_queue));
ESP_LOGI(TAG, "rotary encoder inited");
return ESP_OK;
};
esp_err_t RotaryEncoderModule::deinit(){
ESP_ERROR_CHECK(rotary_encoder_uninit(info));
ESP_LOGI(TAG, "deinited");
return ESP_OK;
};
RotaryEncoderModule& RotaryEncoderModule::Instance(){
static RotaryEncoderModule instance;
return instance;
}

View File

@@ -0,0 +1,59 @@
/**
* @file SNTPModule.cpp
* @brief SNTPModule implementation file
* */
#include "lwip/apps/sntp.h"
#include "sntp/sntp.h"
#include "module.h"
void time_sync_notification_cb(struct timeval *tv){
ESP_LOGI("Global","Notification of a time synchronization event");
}
/** Sample code to sync time with NTP server from ESP-IDF
* https://github.com/espressif/esp-idf/tree/master/examples/protocols/sntp
**/
void SNTPModule::initialize_sntp(void){
sntp_setoperatingmode(SNTP_OPMODE_POLL);
sntp_setservername(0, (char*) "pool.ntp.org");
sntp_set_time_sync_notification_cb(time_sync_notification_cb);
sntp_init();
}
esp_err_t SNTPModule::init(){
// Sync time
time_t now;
struct tm timeinfo;
time(&now);
localtime_r(&now, &timeinfo);
// Is time set? If not, tm_year will be (1970 - 1900).
if (timeinfo.tm_year < (2019 - 1900)) {
ESP_LOGI(TAG, "Time is not set yet. Connecting to WiFi and getting time over NTP.");
ESP_LOGI(TAG, "Initializing SNTP");
initialize_sntp();
int retry = 0;
const int retry_count = 10;
while (sntp_get_sync_status() == SNTP_SYNC_STATUS_RESET && ++retry < retry_count) {
ESP_LOGI(TAG, "Waiting for system time to be set... (%d/%d)", retry, retry_count);
vTaskDelay(2000 / portTICK_PERIOD_MS);
}
// update 'now' variable with current time
time(&now);
localtime_r(&now, &timeinfo);
char curr[20];
strftime(curr, 20,"%Y-%m-%dT%XZ", localtime(&now));
ESP_LOGI(TAG, "Time is set: %s", curr);
}
return ESP_OK;
}
esp_err_t SNTPModule::deinit(){
sntp_stop();
ESP_LOGI(TAG, "deinited");
return ESP_OK;
}

View File

@@ -0,0 +1,100 @@
/**
* @file ScreenModule.cpp
* @brief ScreenModule implementation file
* */
#include "u8g2.h"
#include "u8g2_esp32_hal.h"
#include "module.h"
u8g2_t u8g2; // a structure which will contain all the data for one display
ScreenModule::ScreenModule(){};
void ScreenModule::setSCL(gpio_num_t scl_pin){
this->SCL_PIN = scl_pin;
}
void ScreenModule::setSDA(gpio_num_t sda_pin){
this->SDA_PIN = sda_pin;
}
void ScreenModule::setRST(gpio_num_t rst_pin){
this->RESET_PIN = rst_pin;
}
esp_err_t ScreenModule::init(){
u8g2_esp32_hal_t u8g2_esp32_hal = U8G2_ESP32_HAL_DEFAULT;
u8g2_esp32_hal.sda = SDA_PIN;
u8g2_esp32_hal.scl = SCL_PIN;
if(RESET_PIN != GPIO_NUM_NC) u8g2_esp32_hal.reset = RESET_PIN;
u8g2_esp32_hal_init( u8g2_esp32_hal );
u8g2_Setup_ssd1306_i2c_128x64_noname_f(
&u8g2,
U8G2_R0,
//u8x8_byte_sw_i2c,
u8g2_esp32_i2c_byte_cb,
u8g2_esp32_gpio_and_delay_cb); // init u8g2 structure
try{
u8x8_SetI2CAddress(&u8g2.u8x8,0x78);
ESP_LOGI(TAG, "u8g2_InitDisplay");
u8g2_InitDisplay(&u8g2);
ESP_LOGI(TAG, "u8g2_SetPowerSave");
u8g2_SetPowerSave(&u8g2, 0); // wake up display
ESP_LOGI(TAG, "u8g2_ClearBuffer");
u8g2_ClearBuffer(&u8g2);
}catch(const char* msg){
throw msg;
}
return ESP_OK;
}
esp_err_t ScreenModule::deinit(){
u8g2_ClearBuffer(&u8g2);
return ESP_OK;
}
/* UI method to write strings to screen */
void ScreenModule::print_screen(char *buff){
u8g2_ClearBuffer(&u8g2);
u8g2_SetFont(&u8g2, u8g2_font_Georgia7px_tf);
u8g2_DrawStr(&u8g2, 0,10, buff);
u8g2_SendBuffer(&u8g2);
}
void ScreenModule::clear(){
u8g2_ClearBuffer(&u8g2);
lineOff = 0;
}
void ScreenModule::refresh(){
u8g2_SendBuffer(&u8g2);
}
void ScreenModule::writeLine(char *str, uint8_t pl){
u8g2_SetFont(&u8g2, u8g2_font_4x6_tf);
u8g2_DrawStr(&u8g2, pl, lineOff*10+10, str);
lineOff++;
}
void ScreenModule::FillCircle(uint8_t x, uint8_t y, uint8_t r){
u8g2_DrawDisc(&u8g2, x, y, r, U8G2_DRAW_ALL);
}
ScreenModule& ScreenModule::Instance(){
static ScreenModule instance;
return instance;
}

View File

@@ -0,0 +1,95 @@
/**
* @file WiFiModule.cpp
* @brief WiFiModule implementation file
**/
#include "string.h"
#include "esp_event.h"
#include "freertos/event_groups.h"
#include "esp_wifi.h"
#include "module.h"
static const int WIFI_CONNECTED_BIT = BIT0;
static int MAXIMUM_RETRY = 5;
static int wifi_status = 0;
static int s_retry_num = 0;
/* FreeRTOS event group to signal when we are connected*/
static EventGroupHandle_t s_wifi_event_group;
/** Sample code to connect wifi from ESP-IDF*/
static void event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data){
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
esp_wifi_connect();
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
if (s_retry_num < MAXIMUM_RETRY) {
esp_wifi_connect();
xEventGroupClearBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
s_retry_num++;
ESP_LOGI("wi-fi", "retry to connect to the AP");
}
else{
wifi_status = -1;
}
ESP_LOGI("wi-fi","connect to the AP fail");
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
s_retry_num = 0;
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
wifi_status = 1;
ESP_LOGI("wi-fi","connected");
}
}
WiFiModule::WiFiModule(char *ssid, char* pass){
this->WIFI_SSID = ssid;
this->WIFI_PASS = pass;
}
esp_err_t WiFiModule::init(){
s_wifi_event_group = xEventGroupCreate();
tcpip_adapter_init();
esp_err_t err;
err = esp_event_loop_create_default();
if(err != ESP_OK) throw "Event loop error";
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
err = esp_wifi_init(&cfg);
if(err != ESP_OK) throw "Wifi init error";
err = esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL);
if(err != ESP_OK) throw "Event handler error";
err = esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL);
if(err != ESP_OK) throw "Event handler error";
wifi_config_t wifi_config = {};
memset(&wifi_config,0, sizeof(wifi_config));
strcpy((char*)(wifi_config.sta.ssid), WIFI_SSID );
strcpy((char*)(wifi_config.sta.password), WIFI_PASS);
wifi_config.sta.bssid_set = false;
err = esp_wifi_set_mode(WIFI_MODE_STA); if(err != ESP_OK) throw "Wifi mode error";
err = esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config); if(err != ESP_OK) throw "Wifi config error";
err = esp_wifi_start(); if(err != ESP_OK) throw "Can't start wifi";
ESP_LOGI(TAG, "wifi_init_sta finished.");
ESP_LOGI(TAG, "connect to ap SSID:%s password:%s",WIFI_SSID, WIFI_PASS);
while(!wifi_status){
vTaskDelay(500 / portTICK_PERIOD_MS);
}
if(wifi_status == -1){
throw "Couldn't connect to wifi";
}
return ESP_OK;
}
esp_err_t WiFiModule::deinit(){
return esp_wifi_deinit();
}

View File

@@ -0,0 +1,77 @@
#include "view.h"
#include "model.h"
#include "esp_log.h"
#include "module.h"
#include "string.h"
/**
* @file ChoiceView.cpp
* @brief ChoiceView class implementation
* */
static int SCROLL_MAX = 6;
static int linestart = 0;
static int lineend = SCROLL_MAX;
static int min(int a, int b){
if(a < b) return a;
return b;
}
ChoiceView::ChoiceView(){};
void ChoiceView::render(void * data){
ChoiceModel * model = (ChoiceModel *) data;
int r = 0;
this->list(*model->choice_list, r);
QueueHandle_t event_queue = rotary_encoder_create_queue();
ESP_ERROR_CHECK(rotary_encoder_set_queue(RotaryEncoderModule::Instance().getInfo(), event_queue));
int choiceSelected = 0;
while (!choiceSelected)
{
// Wait for incoming events on the event queue.
rotary_encoder_event_t *event = new rotary_encoder_event_t();
if (xQueueReceive(event_queue, event, 1000 / portTICK_PERIOD_MS) == pdTRUE)
{
if(event->state.clicked ){
ESP_LOGI("RotEnc", "Event %s\n", event->state.clicked ? "clicked":"not clicked");
choiceSelected = 1;
break;
}
else{
ESP_LOGI("RotEnc", "Event: position %d, direction %s", event->state.position,
event->state.direction ? (event->state.direction == ROTARY_ENCODER_DIRECTION_CLOCKWISE ? "CW" : "CCW") : "NOT_SET");
r += event->state.direction == ROTARY_ENCODER_DIRECTION_CLOCKWISE ? -1 : 1 ;
if(r <= 0) r = 0;
if(r >= model->choice_list->size()) r = model->choice_list->size()-1;
if(r >= lineend ){ lineend = r+1; linestart = lineend - SCROLL_MAX; }
if(r <= linestart ){ linestart = r; lineend = linestart + SCROLL_MAX; }
this->list(*model->choice_list, r);
}
}
}
// ballot = district-choice '\x1F' choicelist-name '\x1F' choice-name //
model->ballot = (char *) malloc(225);
memset(model->ballot,0,225);
snprintf(model->ballot, 224, "%s\x1F%s\x1F%s", (*model->choice_list)[r].code, (*model->choice_list)[r].party, (*model->choice_list)[r].candidate);
}
void ChoiceView::list(std::vector<choice_t> &v,int r){
this->screen.clear();
for(int i=linestart;i< min(lineend, (int) v.size()); i++){
this->screen.writeLine(v[i].candidate, i==r ? 10 : 0);
}
this->screen.FillCircle(120, (r - linestart)*10+5,5);
this->screen.refresh();
}

View File

@@ -0,0 +1,14 @@
#include "view.h"
#include "module.h"
/**
* @file IndexView.cpp
* @brief IndexView class implementation
* */
IndexView::IndexView(){};
void IndexView::render(void * data){
this->screen.print_screen((char *)data);
}