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,3 @@
set(COMPONENT_SRCS "main.cpp")
set(COMPONENT_ADD_INCLUDEDIRS "")
register_component()

View File

@@ -0,0 +1,82 @@
menu "Open Voting Client Configuration"
config OVC_WIFI_SSID
string "WiFi SSID"
help
SSID (network name) for the example to connect to.
config OVC_WIFI_PASSWORD
string "WiFi Password"
help
WiFi password (WPA or WPA2) for the example to use.
config OVC_WEB_SERVER
string "Server address"
help
Server host (or ip) address
config OVC_WEB_PORT
int "Server port"
default 443
help
443 for TLS/SSL
config OVC_DDS_SNI
string "DDS Server SNI address"
help
SNI for authentication service
config OVC_CHOICES_SNI
string "DDS Server SNI address"
help
SNI for authentication service
config OVC_VOTING_SNI
string "DDS Server SNI address"
help
SNI for authentication service
config OVC_PHONE_NUMBER
string "Voter's Phone number"
help
User phone
config OVC_ID_CODE
string "Voter's ID number"
help
ID Code
config OVC_PIN_SDA
int "SDA Pin"
default 5
help
I2C SDA pin number connected to screen
config OVC_PIN_SCL
int "SCL Pin"
default 4
help
I2C SCL pin number connected to screen
config OVC_PIN_RESET
int "RESET Pin"
default -1
help
I2C RESET pin number connected to screen
config OVC_PIN_ROT_ENC_CLK
int "Rotary Encoder CLK Pin"
default 16
help
I2C RESET pin number connected to screen
config OVC_PIN_ROT_ENC_DT
int "Rotary Encoder DT Pin"
default 13
help
I2C RESET pin number connected to screen
config OVC_PIN_ROT_ENC_SW
int "Rotary Encoder SW Pin"
default 14
help
I2C RESET pin number connected to screen
endmenu

View File

@@ -0,0 +1,7 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
COMPONENT_EMBED_TXTFILES := server_root_cert.pem server_public_key.pem
COMPONENT_SRCDIRS = . impl/modules impl/models impl/views impl/controllers

View File

@@ -0,0 +1,234 @@
/**
* @file converter.cpp
*
* @brief namespace converter implementation
* */
#include <algorithm>
#include <vector>
#include "cJSON.h"
#include "esp_log.h"
#include "unity.h"
#include "mbedtls/base64.h"
#include "mbedtls/asn1.h"
#include "mbedtls/asn1write.h"
#include "mbedtls/sha256.h"
#include "converter.h"
unsigned char * converter::base64hash(unsigned char* input, size_t len){
unsigned char *hashBase64 = (unsigned char *) malloc(64);
memset(hashBase64, 0, 64);
static unsigned char hashResult[32];
memset(hashResult, 0, 32);
int error = mbedtls_sha256_ret(input, len, hashResult, 0);
if(error) throw "Unable to hash";
error = mbedtls_base64_encode(hashBase64, 64, &len, hashResult, 32);
if(error) throw "Unable to base64 encode";
return hashBase64;
}
// source: https://github.com/ARMmbed/mbedtls/blob/master/programs/util/pem2der.c
int converter::convert_pem_to_der( const unsigned char *input, size_t ilen,
unsigned char *output, size_t *olen ){
int ret;
const unsigned char *s1, *s2, *end = input + ilen;
size_t len = 0;
s1 = (unsigned char *) strstr( (const char *) input, "-----BEGIN" );
if( s1 == NULL )
return( -1 );
s2 = (unsigned char *) strstr( (const char *) input, "-----END" );
if( s2 == NULL )
return( -1 );
s1 += 10;
while( s1 < end && *s1 != '-' )
s1++;
while( s1 < end && *s1 == '-' )
s1++;
if( *s1 == '\r' ) s1++;
if( *s1 == '\n' ) s1++;
if( s2 <= s1 || s2 > end )
return( -1 );
ret = mbedtls_base64_decode( NULL, 0, &len, (const unsigned char *) s1, s2 - s1 );
if( ret == MBEDTLS_ERR_BASE64_INVALID_CHARACTER )
return( ret );
if( len > *olen )
return( -1 );
if( ( ret = mbedtls_base64_decode( output, len, &len, (const unsigned char *) s1,
s2 - s1 ) ) != 0 )
{
return( ret );
}
*olen = len;
return( 0 );
}
uint32_t* converter::hex2u32(unsigned char* hex, size_t len){
static uint32_t result[96];
int j = 0, i = len;
while(j< 96 && i >0){ // group every 4 byte and cast to uint32_t, then append to array
uint32_t hex3 = i > 2 ? hex[i-3] & 0xFF : 0;
uint32_t hex2 = i > 1 ? hex[i-2] & 0xFF : 0;
uint32_t hex1 = i > 0 ? hex[i-1] & 0xFF : 0;
uint32_t hex0 = hex[i] & 0xFF;
result[j] = (hex3 << 24 ) | (hex2 << 16 ) | ( hex1 << 8) | hex0; // because uint32_t is 4 bytes
i= i - 4;
j++;
}
return result;
}
unsigned char * converter::nb(uint32_t *x, int length, size_t *olen){
unsigned char * s = (unsigned char *) malloc(length * sizeof(uint32_t));
memset(s,0, length * 4);
*olen = 0;
for(int i=length-1;i>=0;i--){
unsigned char c1 = (unsigned char ) x[i] & 0xFF;
unsigned char c2 = (unsigned char ) (x[i] >> 8) & 0xFF;
unsigned char c3 = (unsigned char ) (x[i] >> 16) & 0xFF;
unsigned char c4 = (unsigned char ) (x[i] >> 24) & 0xFF;
memset(s+(length-1 - i)*4+0, c4, sizeof(c4));
memset(s+(length-1 - i)*4+1, c3, sizeof(c3));
memset(s+(length-1 - i)*4+2, c2, sizeof(c2));
memset(s+(length-1 - i)*4+3, c1, sizeof(c1));
*olen += 4;
}
if( (s[0] & 0xFF) >> 7 ){ // If first bit of first octet is 1, add zeros octet in-front
unsigned char * s2 = (unsigned char *) malloc(length * sizeof(uint32_t) + 1);
memset(s2,0,1);
memcpy(s2+1,s, *olen);
*olen +=1;
return s2;
}
return s;
}
void converter::insert_space(xml_data_t *user_data){
const char align_str[] = " ";
TEST_ASSERT(output_size >= user_data->output_off);
user_data->output[user_data->output_off++] = '\n';
for (int i = 0; i < user_data->depth; i++) {
for (int j = 0; j < strlen(align_str); ++j) {
TEST_ASSERT(output_size >= user_data->output_off);
user_data->output[user_data->output_off++] = align_str[j];
}
}
}
void XMLCALL converter::start_element(void *userData, const XML_Char *name, const XML_Char **atts){
xml_data_t *user_data = (xml_data_t *) userData;
if(user_data->depth)
insert_space(user_data);
user_data->last_name = name;
int ret;
if(atts[0] == NULL){
ret = snprintf(user_data->output + user_data->output_off,
output_size - user_data->output_off, "<%s>", name);
}
else{
int i=0;
static char atts_imploded[512];
bzero(atts_imploded, 512);
while(atts[i] != NULL){
strncat(atts_imploded , atts[i], strlen(atts[i]));
i++;
if(atts[i]!= NULL){
strncat(atts_imploded, "=\"", 4);
strncat(atts_imploded, atts[i], strlen(atts[i]));
strncat(atts_imploded, "\" ", 4);
i++;
}
}
atts_imploded[strlen(atts_imploded)-1] = '\0';
ret = snprintf(user_data->output + user_data->output_off,
output_size - user_data->output_off,
"<%s %s>", name, atts_imploded);
}
user_data->output_off += ret;
++user_data->depth;
}
void XMLCALL converter::end_element(void *userData, const XML_Char *name){
xml_data_t *user_data = (xml_data_t *) userData;
--user_data->depth;
if(strcmp(user_data->last_name, name)) // do not add extra space in between one line xml tags
insert_space(user_data);
int ret = snprintf(user_data->output + user_data->output_off, output_size - user_data->output_off,
"</%s>", name);
TEST_ASSERT_EQUAL(strlen(name) + 3, ret); // 3 are the tag characters: "</>"
user_data->output_off += ret;
}
void converter::data_handler(void *userData, const XML_Char *s, int len){
xml_data_t *user_data = (xml_data_t *) userData;
// s is not zero-terminated
char tmp_str[len+1];
memset(tmp_str, 0, len+1);
strncpy(tmp_str, s, len);
int ret = snprintf(user_data->output + user_data->output_off, output_size - user_data->output_off,
"%s", tmp_str);
TEST_ASSERT_EQUAL(strlen(tmp_str), ret);
user_data->output_off += ret;
}
void converter::xmlc14n11(char* str, size_t len){
xml_data_t *user_data = new xml_data_t();
user_data->depth = 0;
user_data->output = (char *) malloc(output_size);
user_data->output_off = 0;
user_data->last_name = {};
TEST_ASSERT_NOT_NULL(user_data->output);
memset(user_data->output, 0, output_size);
XML_Parser parser = XML_ParserCreate(NULL);
XML_SetUserData(parser, user_data);
XML_SetElementHandler(parser, start_element, end_element);
XML_SetCharacterDataHandler(parser, data_handler);
TEST_ASSERT_NOT_EQUAL(XML_STATUS_ERROR, XML_Parse(parser, str, strlen(str), 1));
XML_ParserFree(parser);
TEST_ASSERT_EQUAL(0, user_data->depth); // all closing tags have been found
memset(str, 0, len);
memcpy(str, user_data->output, user_data->output_off);
free(user_data->output);
delete(user_data);
}

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);
}

View File

@@ -0,0 +1,508 @@
#ifndef MAIN_INCLUDE_CONTROLLER_H_
#define MAIN_INCLUDE_CONTROLLER_H_
/**
* @file controller.h
* @brief definition of controllers.
* */
#include <string>
#include <algorithm>
#include <vector>
#include "string.h"
#include "esp_log.h"
#include "mbedtls/base64.h"
#include "mbedtls/asn1.h"
#include "mbedtls/asn1write.h"
#include "mbedtls/sha256.h"
#include "expat.h"
#include "esp_gatts_api.h"
#include "module.h"
#include "model.h"
#include "view.h"
/**
* \class Controller
*
* \brief abstract class Controller. C in MVC,
*
* Controllers are bridge between views and models. They take data from model, modify it if required and pass it to the view to present to client.
* It has one main method - index(), which is called when controller is reached.
* However, depending on their complexity , controller may have other methods.
*
* */
class Controller {
protected:
View * vw; /**< associated view object*/
public:
virtual void index() = 0; /**< abstract method index */
virtual ~Controller(){}; /**< Destructor */
};
/**
* \class IndexController
* \brief sample inherited controller
* */
class IndexController : public Controller {
public:
IndexController(); /**< constructor */
void index(); /**< a method that only prints "Index ctrl"*/
};
/**
* \class AuthorizationController
* \brief responsible for logic behind mobilID authorization
*
*
* */
class AuthorizationController : public Controller{
private:
UserModel * model; /**< user model*/
const char * TAG = "authController";
const char* sni; /**< SNI for authorization */
public:
AuthorizationController(BaseModel *model, const char* sni); /**< constructor */
/**
* @brief JSON body generator to retrieve Authentication Certificate
* Example:
*
* {
* "id": 0.0,
* "method": "RPC.Authenticate",
* "params": [
* {
* "OS": "Operating System,2,0",
* "PhoneNo": "+37200000766",
* "IdCode": "60001019906"
* }
* ]
* }
*
* */
char* generateAuthenticateRequestJSON();
/**
* @brief JSON body generator to retrieve Authentication Status
*
* Example:
*
* {
* "id": 0.0,
* "method": "RPC.AuthenticateStatus",
* "params": [
* {
* "OS": "Operating System,2,0",
* "SessionCode": "2127729011",
* "SessionID": "057229fdfa2df7d3c7f4ced81b02760b"
* }
* ]
* }
*
* */
char* generateAuthenticateStatusRequestJSON();
/**
* @brief index method
*
* This method does nothing specific.
* */
void index();
/**
* @brief authorization request method.
*
* This method invokes authorization request to voting server.
* Server doesn't directly send authorization token. Instead, sends a challenge to user to verify (PIN code)
* Session ID is also returned which will be used in all upcoming requests.
* */
void auth();
/**
* @brief authorization status method
*
* After auth() called, server returns challenge code, session id and session code. Challenge code is displayed to user verify itself.
* To check whether user has been verified and get authorization token that is also going to be used in all upcoming requests, authorization status
* request has to be sent to server. This function sends that request until user verifies itself every 10 seconds.
*
* Upon successful verification, authorization token is obtained and stored in model.
* */
void authStatus();
};
/**
* \class ChoiceController
* \brief An authenticated user can request for list of candidates from voting server.
*
* Voting server returns list of candidates in special scheme in JSON format. ChoiceController parses that response and
* make candidate names be displayed on screen.
* */
class ChoiceController : public Controller{
private:
ChoiceModel *model; /**< choices model */
const char * TAG = "choicesCtrl";
const char* sni; /**< SNI for choices service */
public:
/**
* @brief JSON body generator to retrieve the List of Choices
*
* @param at Authentication token
* @param ssid SessionID
*
* Example:
*
* {
* "id": 0.0,
* "method": "RPC.VoterChoices",
* "params": [
* {
* "AuthMethod": "ticket",
* "AuthToken": "G1RTZqBSBKrzqReuKYrmFUFXWFPvaxhJjdiZi6zqAnaK3OvrT...",
* "OS": "Operating System,2,0",
* "SessionID": "057229fdfa2df7d3c7f4ced81b02760b"
* }
* ]
* }
*
* */
char* generateChoicesRequestJSON(ChoiceModel *cm);
ChoiceController(BaseModel *model, const char* sni); /**< constructor*/
/**
* @brief fetches data from server and waits user select a candidate
* */
void index();
/**
* Recursive function to parse JSON which holds the list of choices into choiceList,choiceListKeys and partyNameOf
* @param item pointer to cJSON structure
* @param par party (also, parent) name of current element, otherwise ""
*
* Example JSON body:
*
* {
* "Kuused":{
* "0000.101":"ARA SMIRNOVVVK",
* "0000.102":"MARGUS OTT KOVTP"
* },
* "Männid":{
* "0000.103":"ADINE SÄDEVVK"
* },
* "Üksikkandidaadid":{
* "0000.104":"LEILI KOVTP",
* "0000.107":"JUTA KOVVALAH",
* "0000.105":"KAJA KOVVALAG",
* "0000.106":"MAREK KOVVALAG"
* }
* }
*
* */
void parse_object(cJSON *item, char* par);
};
/**
* \class EncryptionController
* \brief A controller where selected candidate is turned into ballot and encrypted
*
* */
class EncryptionController : public Controller{
private:
EncryptionModel *model; /**< encryption model*/
const char * TAG = "encCtrl";
public:
/**
* @brief main controller method.
*
* Encrypt voters casted ballot using public election key
*
* */
void index();
EncryptionController(BaseModel *model); /**< constructor */
};
/**
* \class SignatureController
* \brief digitally sign encrypted ballot
* */
class SignatureController : Controller{
private:
SignatureModel *model; /**< signature model*/
const char* sni; /**< sni for signature service*/
const char* TAG = "signCtrl";
/**
* XML container for element SignedProperties
* */
const char * SignedProperties = ""
"<xades:SignedProperties xmlns:asic=\"http://uri.etsi.org/02918/v1.2.1#\" xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\" xmlns:xades=\"http://uri.etsi.org/01903/v1.3.2#\" Id=\"S0-SignedProperties\">"
"<xades:SignedSignatureProperties>"
"<xades:SigningTime>%s</xades:SigningTime>"
"<xades:SigningCertificate>"
"<xades:Cert>"
"<xades:CertDigest>"
"<ds:DigestMethod Algorithm=\"http://www.w3.org/2001/04/xmlenc#sha256\"></ds:DigestMethod>"
"<ds:DigestValue>%s</ds:DigestValue>"
"</xades:CertDigest>"
"<xades:IssuerSerial>"
"<ds:X509IssuerName>%s</ds:X509IssuerName>"
"<ds:X509SerialNumber>%s</ds:X509SerialNumber>"
"</xades:IssuerSerial>"
"</xades:Cert>"
"</xades:SigningCertificate>"
"</xades:SignedSignatureProperties>"
"<xades:SignedDataObjectProperties>"
"<xades:DataObjectFormat ObjectReference=\"#S0-RefId0\">"
"<xades:MimeType>application/octet-stream</xades:MimeType>"
"</xades:DataObjectFormat>"
"</xades:SignedDataObjectProperties>"
"</xades:SignedProperties>";
/**
* XML container for element SignedInfo
* */
const char * SignedInfo = ""
"<ds:SignedInfo xmlns:asic=\"http://uri.etsi.org/02918/v1.2.1#\" xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\" xmlns:xades=\"http://uri.etsi.org/01903/v1.3.2#\">"
"<ds:CanonicalizationMethod Algorithm=\"http://www.w3.org/2006/12/xml-c14n11\"></ds:CanonicalizationMethod>"
"<ds:SignatureMethod Algorithm=\"http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256\"></ds:SignatureMethod>"
"<ds:Reference Id=\"S0-RefId0\" URI=\"%s\">"
"<ds:DigestMethod Algorithm=\"http://www.w3.org/2001/04/xmlenc#sha256\"></ds:DigestMethod>"
"<ds:DigestValue>%s</ds:DigestValue>"
"</ds:Reference>"
"<ds:Reference Id=\"S0-RefId1\" Type=\"http://uri.etsi.org/01903#SignedProperties\" URI=\"#S0-SignedProperties\">"
"<ds:Transforms>"
"<ds:Transform Algorithm=\"http://www.w3.org/2006/12/xml-c14n11\"></ds:Transform>"
"</ds:Transforms>"
"<ds:DigestMethod Algorithm=\"http://www.w3.org/2001/04/xmlenc#sha256\"></ds:DigestMethod>"
"<ds:DigestValue>%s</ds:DigestValue>"
"</ds:Reference>"
"</ds:SignedInfo>";
/**
* XML container for element SignatureValue
* */
const char * SignatureValue = "<ds:SignatureValue xmlns:asic=\"http://uri.etsi.org/02918/v1.2.1#\" "
"xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\" xmlns:xades=\"http://uri.etsi.org/01903/v1.3.2#\" Id=\"S0-SIG\">%s</ds:SignatureValue>";
/**
* XML container for element XadESSignature
* */
const char * Signature = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"
"<asic:XAdESSignatures xmlns:asic=\"http://uri.etsi.org/02918/v1.2.1#\">"
"<ds:Signature xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\" Id=\"S0\">"
"%s"
"%s"
"<ds:KeyInfo>"
"<ds:X509Data>"
"<ds:X509Certificate>%s</ds:X509Certificate>"
"</ds:X509Data>"
"</ds:KeyInfo>"
"<ds:Object>"
"<xades:QualifyingProperties xmlns:xades=\"http://uri.etsi.org/01903/v1.3.2#\" Target=\"#S0\">"
"%s"
"</xades:QualifyingProperties>"
"</ds:Object>"
"</ds:Signature>"
"</asic:XAdESSignatures>";
public:
SignatureController(BaseModel *model, const char* sni);
/**
* @brief retrieve user public key
* */
void index();
/**
* @brief JSON body generator to get signing Certificate
*
* Example:
*
* {
* "id": 0.0,
* "method": "RPC.GetCertificate",
* "params": [
* {
* "AuthMethod": "ticket",
* "AuthToken": "G1RTZqBSBKrzqReuKYrmFUFXWFPvaxhJjdiZi6zqAnaK3OvrT...",
* "OS": "Operating System,2,0",
* "SessionID": "057229fdfa2df7d3c7f4ced81b02760b"
* "PhoneNo": "+37200000766",
* "IdCode": "60001019906"
* }
* ]
* }
*
* */
char* generateGetCertificateRequestJSON();
/*
* JSON body generator to initiate vote signing
* Example:
* {
* "id": 0.0,
* "method": "RPC.Sign",
* "params": [
* {
* "AuthMethod": "ticket",
* "AuthToken": "G1RTZqBSBKrzqReuKYrmFUFXWFPvaxhJjdiZi6zqAnaK3OvrT...",
* "Hash": "9IBrA05ylt2StdjxKkSTYMW/rQXY3Vub4upzShdfEzo=",
* "OS": "Operating System,2,0",
* "SessionID": "057229fdfa2df7d3c7f4ced81b02760b"
* "PhoneNo": "+37200000766",
* "IdCode": "60001019906"
* }
* ]
* }
*
* */
char* generateSignRequestJSON();
/**
* @brief send sign request for encrypted ballot hash
*
*
* */
void sign();
/*
* @brief JSON body generator to retrieve Signing Status
*
* Example:
*
* {
* "id": 0.0,
* "method": "RPC.SignStatus",
* "params": [
* {
* "OS": "Operating System,2,0",
* "SessionCode": "2127729011",
* "SessionID": "057229fdfa2df7d3c7f4ced81b02760b"
* }
* ]
* }
*
* */
char* generateSignStatusRequestJSON();
/**
* @brief check status of sign request
* */
void status();
/**
* @brief a helper method to put all xml elements into one single large elements.
* */
void combine();
};
/**
* \class ZipController
* \brief BDOC container creator for singature file
* */
class ZipController : public Controller{
private:
ZipModel *model;
const char* TAG = "zipCtrl";
/**
* XML container for manifest.xml
*/
const char * manifest = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>\n"
"<manifest:manifest xmlns:manifest=\"urn:oasis:names:tc:opendocument:xmlns:manifest:1.0\">\n"
" <manifest:file-entry manifest:full-path=\"/\" manifest:media-type=\"application/vnd.etsi.asic-e+zip\"/>\n"
" <manifest:file-entry manifest:full-path=\"%s\" manifest:media-type=\"application/octet-stream\"/>\n"
"</manifest:manifest>";
public:
ZipController(BaseModel *model); /**< constructor */
/**
* @brief create signature container according to BDOC 2.0 standards
*
* Using slightly modified miniz library, create .zip archieve with following file structure
*
* \META-INF
* singature0.xml
* manifest.xml
* EP2065.1.ballot
* mimetype
*
*/
void index();
};
/**
* \class VoteController
* \brief cast signed ballot file to voting service
*
* */
class VoteController : public Controller{
private:
VoteModel *model;
const char * TAG = "voteCtrl";
const char * sni;
public:
VoteController(BaseModel *model, const char* sni); /**< constructor */
/**
* @brief send bdoc container to voting service to cast your vote.
*
* A vote id is returned after bdoc container and vote data is validated in server side
* */
void index();
/**
* @brief JSON body generator to send signed vote
*
* Example
*
* {
* "id": 0.0,
* "method": "RPC.Vote",
* "params": [
* {
* "AuthMethod": "ticket",
* "AuthToken": "G1RTZqBSBKrzqReuKYrmFUFXWFPvaxhJjdiZi6zqAnaK3OvrT...",
* "Choices": "0140.1",
* "SessionID": "ec3a0cab353d552952289f2c7ad52e27",
* "OS": "Operating System,2,0",
* "Type": "bdoc",
* "Vote": "UEsDBAoABgAAAAIAAAAbWltZXR5cGVhcHBsaWNhdGlv\nbi92bmQuZX..."
* }
* ]
* }
* */
char* generateVoteRequestJSON();
};
/**
* \class BluetoothController
* \brief send vote data to verification app via BLE
*
* */
class BluetoothController : public Controller{
private:
BluetoothModel *model; /**< bluetooth model */
const char* TAG = "btCtrl";
public:
BluetoothController(BaseModel* model); /**< controller */
/**
* @brief turn on BLE and send session id, randomness and vote id over BLE channel
* */
void index();
};
#endif /* MAIN_INCLUDE_CONTROLLER_H_ */

View File

@@ -0,0 +1,141 @@
#ifndef MAIN_INCLUDE_CONVERTER_H_
#define MAIN_INCLUDE_CONVERTER_H_
/**
* @file converter.h
* @brief converter namespace - collection of frequently used conversion/encoding/decoding functions
*
* */
#include <stdint.h>
#include <string.h>
#include "expat.h"
/** Buffer size for xml parsing */
#define output_size 10240
/**
* \namespace converter
* */
namespace converter{
/** @brief a helper function to convert pem files to der.
*
* source: https://github.com/ARMmbed/mbedtls/blob/master/programs/util/pem2der.c
*
* @param input pem file buffer
* @param ilen input buffer length
* @param output buffer to store converted der content
* @param olen length of data written in output buffer
*
* @returns 0 if OK, else -1
* */
int convert_pem_to_der( const unsigned char *input, size_t ilen,
unsigned char *output, size_t *olen );
/**
* @brief a helper function to convert long hex numbers to base b = 2^32
*
* In base 2^32, every digit is 32 bit length unsigned integers (uint32_t).
* Therefore, each consequent 4 bytes in hex converted into one digit in base b.
* Note: numbers may contain '0x00' in hex representation which is treated as end of string
* in string functions. Another parameter is required to indicate length of hex representation
*
* @param hex input number buffer
* @param len input buffer length
*
* @returns long base-b number
*/
uint32_t* hex2u32(unsigned char* hex, size_t len);
/**
* @brief a helper function to convert long base-b number to hex values for DER encoding
*
* Inverse of hex2u32 function
*
* @param x long base-b number
* @param length length of input number
* @param len output buffer length
*
* @returns buffer containing number in hex representation
* */
unsigned char * nb(uint32_t *x, int length, size_t *olen);
/**
* @brief a helper function convert to first hash, then base64 encode
*
* This function wraps to mbed_tls functions. Given input string buffer,
* it first finds SHA256 hash, then converts the resulting hash into base64.
*
* @param input string buffer
* @param len input buffer length
*
* @returns output buffer
* */
unsigned char * base64hash(unsigned char* input, size_t len);
/** @brief struct used in xml parsing functions
* */
typedef struct {
int depth; /**< depth of recursion*/
char* output; /**< output buffer (for parsed xml) */
int output_off; /**< output buffer offset (last written index, i.e length of written data)*/
const char *last_name; /**< last opened xml tag */
} xml_data_t;
/**
* @brief a helper function to insert space for indentation
*
* This function prints extra whitespaces to output buffer for indentation purposes.
*
* @param user_data parsed xml data
* */
static void insert_space(xml_data_t *user_data);
/**
* @brief a callback function called at start of new xml node
*
* XMLParser traverses through xml nodes and each time it reaches a new node, this callback function is called.
* Copy tag name and attributes to output buffer
*
* @param user_data parsed xml data
* @param name just opened current xml node tag name
* @param atts other attributes of current xml node
* */
static void XMLCALL start_element(void *userData, const XML_Char *name, const XML_Char **atts);
/**
* @brief a callback function called at the end of new xml node
*
* XMLParser traverses through xml nodes and each time it reaches a closing tags of current node, this callback function is called.
* If current node is leaf node, add closing tags immediately. Otherwise, add whitespace before closing tags.
*
* @param user_data parsed xml data
* @param name to be closed current xml node tag name
* */
static void XMLCALL end_element(void *userData, const XML_Char *name);
/**
* @brief a function to define how to parse xml elements
*
* In between starting and closing tags, this functions is called by XMLParser.
* Here, it just copies data buffer to output buffer without adding extra whitespace.
*
* @param user_data parsed xml data
* @param s data in current xml node
* @param len length of data
* */
static void data_handler(void *userData, const XML_Char *s, int len);
/**
* @brief a helper funciton to canonicalize xml data (XMLC14N11).
*
* Specifications can be found here: \see https://www.w3.org/TR/xml-c14n11/#Example-SETags
*
* @param str xml input buffer
* @param len input buffer length
* */
void xmlc14n11(char* str, size_t len);
}
#endif

View File

@@ -0,0 +1,142 @@
#ifndef MAIN_INCLUDE_MODEL_H_
#define MAIN_INCLUDE_MODEL_H_
#include <vector>
#include "stdint.h"
/**
* @file model.h
* @brief header for model definitions
* */
/**
* \class BaseModel
* \brief base model class
* */
class BaseModel{
public:
char* ssid; /**< session ID*/
char* authToken; /**< authentication token */
};
/**
* \class UserModel
* \brief data model to use in AuthorizationController
* */
class UserModel : public BaseModel{
public:
char ID[12]; /**< ID code of user */
char phone[13]; /**< Phone number of user */
char* sscode; /**< session code for PIN confirmation */
UserModel(char* id, char* phone); /**< constructor */
};
/**
* @brief struct to store candidate details
* */
typedef struct{
char * party; /**< candidate party name */
char * code; /**< District code */
char * candidate; /**< candidate full name */
}choice_t;
/**
* \class ChoiceModel
* \brief data model to use in ChoiceController
* */
class ChoiceModel : public BaseModel{
public:
std::vector<choice_t> *choice_list; /**< list of candidates*/
char* choices; /**< The voters district identifier */
char* ballot; /**< ballot generated from selected candidate */
ChoiceModel(char *ssid, char* authToken); /**< constructor */
};
/**
* \class EncryptionModel
* \brief data model to use in EncryptionController
* */
class EncryptionModel : public BaseModel{
public:
char* ballot; /**< ballot generated from selected candidate */
unsigned char* ballotASN; /**< encoded encrypted ballot ballot data in ASN1 DER */
size_t ballotLength; /**< ballotASN file length */
unsigned char* rndBase64; /**< Base64 encoded randomness used in ecryption */
unsigned char* ballotHash; /**< ballotASN hash */
char* ballotFileName; /**< filename for encrypted ballot */
char* election_id; /**< election id from public election key*/
const unsigned char* keypem; /**< public election file in PEM */
int keypem_length; /**< public key buffer length */
EncryptionModel(char* ballot,char* ssid, char * authToken, const uint8_t keypem[], int keypem_length); /**< constructor */
};
/**
* \class SignatureModel
* \brief data model to use in SignatureController
* */
class SignatureModel : public BaseModel{
public:
char ID[12]; /**< ID code of user */
char phone[13]; /**< Phone number of user */
char* sscode; /**< session code for PIN confirmation */
unsigned char* ballotHash; /**< ballotASN hash */
size_t ballotLength; /**< ballotASN file length */
char* ballotFileName; /**< filename for encrypted ballot */
unsigned char* SignedInfoHash; /**< hash of SignedInfo element */
char* signature; /**< digital signature returned from mobilID*/
char* certificate; /**< signature certificate */
char* SI_XML; /**< SignedInfo XML element*/
char* SP_XML; /**< SignedProperties XML element*/
char* SV_XML; /**< SignatureValue XML element*/
char* Signature; /**< Signature XML element*/
SignatureModel(char* ssid, char * authToken, char* phone, char* id, unsigned char* ballotHash, size_t ballotLength, char* ballotFileName);
};
/**
* \class ZipModel
* \brief data model to use in ZipController
* */
class ZipModel : public BaseModel {
public:
unsigned char* ballot; /**< encrypted digital ballot */
char* ballotFileName; /**< ballot file name */
size_t ballotLength; /**< ballot length */
char* Signature; /**< Signature XML*/
unsigned char* voteBase64; /**< Base64 encoded bdoc container*/
ZipModel(unsigned char* ballot, char* ballotFileName, size_t ballotLength, char* Signature); /** constructor */
};
/**
* \class VoteModel
* \brief data model to use in VoteController
* */
class VoteModel : public BaseModel{
public:
unsigned char* voteBase64; /**< Base64 encoded bdoc container*/
char* voteID; /**< voteID */
char* choices; /**< voters district identifier */
VoteModel(char* ssid, char* authToken, unsigned char* voteBase64, char* choices); /**< constructor */
};
/**
* \class BluetoothModel
* \brief data model to use in BluetoothController
* */
class BluetoothModel : public BaseModel{
public:
char* ssid; /**< session ID */
char* voteID; /**< voteID */
unsigned char* rndBase64; /**< Base64 encoded randomness used in encryption */
BluetoothModel(char* ssid, char* voteID, unsigned char* rndBase64); /**< constructor */
};
#endif /* MAIN_INCLUDE_MODEL_H_ */

View File

@@ -0,0 +1,202 @@
#ifndef MAIN_INCLUDE_MODULE_H_
#define MAIN_INCLUDE_MODULE_H_
/**
* @file module.h
* @brief Header for module definitions
* */
#include "driver/gpio.h"
#include "esp_log.h"
#include "esp_tls.h"
#include "cJSON.h"
#include "esp_gap_ble_api.h"
#include "rotary_encoder.h"
/**
* \class Module
* \brief base abstract module class
* */
class Module{
public:
/**
* @brief module initialization abstract method
* */
virtual esp_err_t init()=0;
/**
* @brief module deinitialization abstract method
* */
virtual esp_err_t deinit()=0;
/**
* @brief default module destructor
* */
virtual ~Module(){};
};
/**
* \class RPC
* \brief remote procedure call module
*
* This module follows singleton pattern to prevent concurrent tls communications which may abort program
* */
class RPC{
private:
const char* TLS_TAG = "rpc";
RPC();
public:
esp_tls_cfg_t *cfg; /**< tls connection config file */
char* server; /**< host server address (full domain or ipaddress) */
int port; /**< host server process port, default 443 */
/**
* @brief RPC Communication method.
*
*
* @param const char* sni SNI of the service to make a request
* @param const char* req stringified JSON request body
*
* @returns pointer to cJSON structure variable that holds parsed JSON response
* */
cJSON * send_json_rpc(const char* sni, char* req);
public:
static RPC& Instance(); /**< singleton RPC instance*/
};
/**
* \class ScreenModule
* \brief screen device module
*
* This module follows singleton pattern to prevent unexpected behaviors when two process concurrently want to write data
* */
class ScreenModule : public Module{
private:
const char* TAG = "ssd_1306";
uint8_t screen_id = 0; /**< connected screen id */
uint8_t lineOff = 0; /**< last written line */
ScreenModule();
public:
gpio_num_t SCL_PIN; /**< SCL connection pin number */
gpio_num_t SDA_PIN; /**< SDA connection pin number */
gpio_num_t RESET_PIN; /**< RESET connection pin number */
void setSCL(gpio_num_t scl_pin); /**< set SCL pin */
void setSDA(gpio_num_t sda_pin); /**< set SCL pin */
void setRST(gpio_num_t rst_pin); /**< set RST pin */
esp_err_t init();
esp_err_t deinit();
/* UI method to write strings to screen */
void print_screen(char *buff); /**< print char buffer to screen */
void clear(); /**< clear screen */
void refresh(); /**< refresh screen to update content */
/** @brief write one line to screen
*
* @param str char buffer to write
* @param pl left padding
* */
void writeLine(char *str, uint8_t pl);
/** @brief draw white circle on screen
*
* @param x x coordinate of circle center
* @param y y coordinate of circle center
* @param r circle radius
* */
void FillCircle(uint8_t x, uint8_t y, uint8_t r);
public:
static ScreenModule& Instance(); /**< singleton ScreenModule instance*/
};
/**
* \class WiFiModule
* \brief WiFi connections module
* */
class WiFiModule : public Module{
private:
const char * TAG = "wi-fi";
char* WIFI_SSID; /**< WiFi SSID name */
char* WIFI_PASS; /**< WiFi password */
public:
WiFiModule(char *ssid, char* pass);
esp_err_t init();
esp_err_t deinit();
};
/**
* \class NVSModule
* \brief NVS module for board initialization
* */
class NVSModule : public Module{
private:
const char* TAG = "nvs";
public:
esp_err_t init();
esp_err_t deinit();
};
/**
* \class RotaryEncoderModule
* \brief RotaryEncoder module to control attached rotary encoder device. Listens to rotations and click events.
* */
class RotaryEncoderModule : public Module{
private:
const char* TAG = "RotEnc";
RotaryEncoderModule();
public:
gpio_num_t ROT_ENC_A_GPIO, ROT_ENC_B_GPIO, ROT_ENC_C_GPIO;
bool ENABLE_HALF_STEPS;
bool FLIP_DIRECTION;
rotary_encoder_info_t *info;
/** @brief set gpio pins connected to rotary encoder
*
* @param a pin connected to DT bus
* @param b pin connected to CLK bus
* @param c pin connected to SW bus
* */
void setPins(gpio_num_t a, gpio_num_t b, gpio_num_t c);
rotary_encoder_info_t* getInfo() const;
esp_err_t init();
esp_err_t deinit();
static RotaryEncoderModule& Instance(); /**< singleton RotaryEncoderModule instance */
RotaryEncoderModule(RotaryEncoderModule const&) = delete; /**< Delete public constructor */
void operator=(RotaryEncoderModule const&) = delete; /**< Disallow object copying */
};
/**
* \class SNTPModule
* \brief Time synchronization module
* */
class SNTPModule : public Module{
private:
const char* TAG = "sntp";
public:
static void initialize_sntp(void); /**< synchronize time over network*/
esp_err_t init();
esp_err_t deinit();
};
/**
* \class BLEModule
* \brief BLE communication module
* */
class BLEModule : public Module{
public:
const char* TAG = "ble";
esp_err_t init();
esp_err_t deinit();
BLEModule();
esp_ble_adv_params_t * adv; /**< bluetooth device advertisement params pointer*/
};
#endif /* MAIN_INCLUDE_MODULE_H_ */

View File

@@ -0,0 +1,41 @@
/*
* u8g2_esp32_hal.h
*
* Created on: Feb 12, 2017
* Author: kolban
*/
#ifndef U8G2_ESP32_HAL_H_
#define U8G2_ESP32_HAL_H_
#include "u8g2.h"
#include "driver/gpio.h"
#include "driver/spi_master.h"
#include "driver/i2c.h"
#define U8G2_ESP32_HAL_UNDEFINED (GPIO_NUM_NC)
#define I2C_MASTER_NUM I2C_NUM_1 // I2C port number for master dev
#define I2C_MASTER_TX_BUF_DISABLE 0 // I2C master do not need buffer
#define I2C_MASTER_RX_BUF_DISABLE 0 // I2C master do not need buffer
#define I2C_MASTER_FREQ_HZ 50000 // I2C master clock frequency
#define ACK_CHECK_EN 0x1 // I2C master will check ack from slave
#define ACK_CHECK_DIS 0x0 // I2C master will not check ack from slave
typedef struct {
gpio_num_t clk;
gpio_num_t mosi;
gpio_num_t sda; // data for I²C
gpio_num_t scl; // clock for I²C
gpio_num_t cs;
gpio_num_t reset;
gpio_num_t dc;
} u8g2_esp32_hal_t ;
#define U8G2_ESP32_HAL_DEFAULT {U8G2_ESP32_HAL_UNDEFINED, U8G2_ESP32_HAL_UNDEFINED, U8G2_ESP32_HAL_UNDEFINED, U8G2_ESP32_HAL_UNDEFINED, U8G2_ESP32_HAL_UNDEFINED, U8G2_ESP32_HAL_UNDEFINED, U8G2_ESP32_HAL_UNDEFINED }
void u8g2_esp32_hal_init(u8g2_esp32_hal_t u8g2_esp32_hal_param);
uint8_t u8g2_esp32_spi_byte_cb(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr);
uint8_t u8g2_esp32_i2c_byte_cb(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr);
uint8_t u8g2_esp32_gpio_and_delay_cb(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr);
#endif /* U8G2_ESP32_HAL_H_ */

View File

@@ -0,0 +1,72 @@
#ifndef MAIN_INCLUDE_VIEW_H_
#define MAIN_INCLUDE_VIEW_H_
/**
* @file view.h
* @brief View class
* */
#include "module.h"
#include "model.h"
/**
* \class View
* \brief V in MVC pattern. Each controller is associated with a view.
* There is only one screen device attached to board. This view helps to display data and accept user input
* */
class View{
public:
ScreenModule screen = ScreenModule::Instance(); /**< singleton screen instance */
virtual void render(void* data) = 0; /**< abstract method to render data*/
virtual ~View(){}; // destructor
};
/**
* \class IndexView
* @brief default inherited class
* */
class IndexView : public View{
public:
IndexView();
/**
* @brief display data on screen
*
* @param data (void *) casted string buffer to display
* */
void render(void * data);
};
/**
* \class ChoiceView
* @brief more functional view class that ChoiceController uses.
* */
class ChoiceView : public View{
public:
ChoiceView();
/**
* @brief render ChoiceController data
*
* Here, users allowed to interact with device using RotaryEncoder. Every time encoder state is changed,
* screen is updated. A small circle in front of candidate names represent selected option index. This method
* will listen rotary encoder until click event is fired, and then create ballot based on selected candidate data.
*
* @param data (void *) casted ChoiceModel * model.
* */
void render(void * data);
/**
* @brief show all candidates
*
* Given list of candidates, this method print name of candidates on screen with small circle to highlight
* "cursor" location, in other words hovered element.
*
* @param v list of choices
* @param r position to draw circle
* */
void list(std::vector<choice_t>& v, int r);
};
#endif /* MAIN_INCLUDE_VIEW_H_ */

134
basic-setup/main/main.cpp Normal file
View File

@@ -0,0 +1,134 @@
/**
* OpenVotingClient project
*
* @file main.cpp
* @brief Main application workflof is defined here
*
*/
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include "driver/gpio.h"
#include "driver/i2c.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "freertos/queue.h"
#include "esp_log.h"
#include "esp_system.h"
#include "sdkconfig.h" // generated by "make menuconfig"
#include "module.h"
#include "controller.h"
// Define Wi-Fi parameters
#define WIFI_SSID CONFIG_OVC_WIFI_SSID
#define WIFI_PASS CONFIG_OVC_WIFI_PASSWORD
// Define server parameters
#define WEB_SERVER CONFIG_OVC_WEB_SERVER
#define DDS_SNI CONFIG_OVC_DDS_SNI
#define CHOICES_SNI CONFIG_OVC_CHOICES_SNI
#define VOTING_SNI CONFIG_OVC_VOTING_SNI
#define WEB_PORT CONFIG_OVC_WEB_PORT
#define TAG "ivxv"
// Define User Data
#define PHONE_NUMBER CONFIG_OVC_PHONE_NUMBER
#define ID_CODE CONFIG_OVC_ID_CODE
// Find your own device SDA and SCL pins (for OLED display)
#define SDA_PIN gpio_num_t( CONFIG_OVC_PIN_SDA )
#define SCL_PIN gpio_num_t( CONFIG_OVC_PIN_SCL )
#define RESET_PIN gpio_num_t( CONFIG_OVC_PIN_RESET )
// Reserve 3 pins for rotary encoder
gpio_num_t ROT_ENC_A_GPIO = gpio_num_t( CONFIG_OVC_PIN_ROT_ENC_DT ); // dt
gpio_num_t ROT_ENC_B_GPIO = gpio_num_t( CONFIG_OVC_PIN_ROT_ENC_CLK ); // clk
gpio_num_t ROT_ENC_C_GPIO = gpio_num_t( CONFIG_OVC_PIN_ROT_ENC_SW ); // sw
/* Include external files ported to assembly binary */
extern const uint8_t server_root_cert_pem_start[] asm("_binary_server_root_cert_pem_start");
extern const uint8_t server_root_cert_pem_end[] asm("_binary_server_root_cert_pem_end");
extern const uint8_t server_public_key_pem_start[] asm("_binary_server_public_key_pem_start");
extern const uint8_t server_public_key_pem_end[] asm("_binary_server_public_key_pem_end");
extern "C" int app_main(){
ESP_ERROR_CHECK(gpio_install_isr_service(0));
try{
ScreenModule screen = ScreenModule::Instance();
screen.setSCL(SCL_PIN);
screen.setSDA(SDA_PIN);
screen.setRST(RESET_PIN);
screen.init();
NVSModule nvs = NVSModule();
nvs.init();
WiFiModule wifi = WiFiModule(WIFI_SSID, WIFI_PASS);
wifi.init();
SNTPModule sntp = SNTPModule();
sntp.init();
RotaryEncoderModule::Instance().setPins(ROT_ENC_A_GPIO, ROT_ENC_B_GPIO, ROT_ENC_C_GPIO);
RotaryEncoderModule::Instance().init();
RPC::Instance().cfg->cacert_pem_buf = server_root_cert_pem_start,
RPC::Instance().cfg->cacert_pem_bytes = server_root_cert_pem_end - server_root_cert_pem_start;
RPC::Instance().server = (char *) WEB_SERVER;
RPC::Instance().port = WEB_PORT;
UserModel um = UserModel((char *)ID_CODE,(char *) PHONE_NUMBER);
AuthorizationController authCtrl = AuthorizationController(&um, DDS_SNI);
authCtrl.index();
authCtrl.auth();
authCtrl.authStatus();
ChoiceModel cm = ChoiceModel(um.ssid, um.authToken);
ChoiceController choiceCtrl = ChoiceController(&cm, CHOICES_SNI);
choiceCtrl.index();
EncryptionModel em = EncryptionModel(cm.ballot, cm.ssid, cm.authToken, server_public_key_pem_start, server_public_key_pem_end - server_public_key_pem_start);
EncryptionController encCtrl = EncryptionController(&em);
encCtrl.index();
SignatureModel sm = SignatureModel(em.ssid, em.authToken, um.phone, um.ID, em.ballotHash, em.ballotLength, em.ballotFileName);
SignatureController signCtrl = SignatureController(&sm, DDS_SNI);
signCtrl.index();
signCtrl.sign();
signCtrl.status();
signCtrl.combine();
ZipModel zm = ZipModel(em.ballotASN, em.ballotFileName, em.ballotLength, sm.Signature);
ZipController zipCtrl = ZipController(&zm);
zipCtrl.index();
VoteModel vm = VoteModel(cm.ssid, cm.authToken, zm.voteBase64, cm.choices);
VoteController voteCtrl = VoteController(&vm, VOTING_SNI);
voteCtrl.index();
BluetoothModel bm = BluetoothModel(vm.ssid, vm.voteID, em.rndBase64);
BluetoothController bleCtrl = BluetoothController(&bm);
bleCtrl.index();
}catch(const char* msg){
puts(msg);
}
fflush(stdout);
return 0;
}

View File

@@ -0,0 +1,234 @@
#include <stdio.h>
#include <string.h>
#include "sdkconfig.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "u8g2_esp32_hal.h"
static const char *TAG = "u8g2_hal";
static const unsigned int I2C_TIMEOUT_MS = 1000;
static spi_device_handle_t handle_spi; // SPI handle.
static i2c_cmd_handle_t handle_i2c; // I2C handle.
static u8g2_esp32_hal_t u8g2_esp32_hal; // HAL state data.
#undef ESP_ERROR_CHECK
#define ESP_ERROR_CHECK(x) do { esp_err_t rc = (x); if (rc != ESP_OK) { ESP_LOGE("err", "esp_err_t = %d", rc); assert(0 && #x);} } while(0);
/*
* Initialze the ESP32 HAL.
*/
void u8g2_esp32_hal_init(u8g2_esp32_hal_t u8g2_esp32_hal_param) {
u8g2_esp32_hal = u8g2_esp32_hal_param;
} // u8g2_esp32_hal_init
/*
* HAL callback function as prescribed by the U8G2 library. This callback is invoked
* to handle SPI communications.
*/
uint8_t u8g2_esp32_spi_byte_cb(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) {
ESP_LOGD(TAG, "spi_byte_cb: Received a msg: %d, arg_int: %d, arg_ptr: %p", msg, arg_int, arg_ptr);
switch(msg) {
case U8X8_MSG_BYTE_SET_DC:
if (u8g2_esp32_hal.dc != U8G2_ESP32_HAL_UNDEFINED) {
gpio_set_level(u8g2_esp32_hal.dc, arg_int);
}
break;
case U8X8_MSG_BYTE_INIT: {
if (u8g2_esp32_hal.clk == U8G2_ESP32_HAL_UNDEFINED ||
u8g2_esp32_hal.mosi == U8G2_ESP32_HAL_UNDEFINED ||
u8g2_esp32_hal.cs == U8G2_ESP32_HAL_UNDEFINED) {
break;
}
spi_bus_config_t bus_config;
memset(&bus_config, 0, sizeof(spi_bus_config_t));
bus_config.sclk_io_num = u8g2_esp32_hal.clk; // CLK
bus_config.mosi_io_num = u8g2_esp32_hal.mosi; // MOSI
bus_config.miso_io_num = -1; // MISO
bus_config.quadwp_io_num = -1; // Not used
bus_config.quadhd_io_num = -1; // Not used
//ESP_LOGI(TAG, "... Initializing bus.");
ESP_ERROR_CHECK(spi_bus_initialize(HSPI_HOST, &bus_config, 1));
spi_device_interface_config_t dev_config;
dev_config.address_bits = 0;
dev_config.command_bits = 0;
dev_config.dummy_bits = 0;
dev_config.mode = 0;
dev_config.duty_cycle_pos = 0;
dev_config.cs_ena_posttrans = 0;
dev_config.cs_ena_pretrans = 0;
dev_config.clock_speed_hz = 10000;
dev_config.spics_io_num = u8g2_esp32_hal.cs;
dev_config.flags = 0;
dev_config.queue_size = 200;
dev_config.pre_cb = NULL;
dev_config.post_cb = NULL;
//ESP_LOGI(TAG, "... Adding device bus.");
ESP_ERROR_CHECK(spi_bus_add_device(HSPI_HOST, &dev_config, &handle_spi));
break;
}
case U8X8_MSG_BYTE_SEND: {
spi_transaction_t trans_desc;
trans_desc.addr = 0;
trans_desc.cmd = 0;
trans_desc.flags = 0;
trans_desc.length = 8 * arg_int; // Number of bits NOT number of bytes.
trans_desc.rxlength = 0;
trans_desc.tx_buffer = arg_ptr;
trans_desc.rx_buffer = NULL;
//ESP_LOGI(TAG, "... Transmitting %d bytes.", arg_int);
ESP_ERROR_CHECK(spi_device_transmit(handle_spi, &trans_desc));
break;
}
}
return 0;
} // u8g2_esp32_spi_byte_cb
/*
* HAL callback function as prescribed by the U8G2 library. This callback is invoked
* to handle I2C communications.
*/
uint8_t u8g2_esp32_i2c_byte_cb(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) {
ESP_LOGD(TAG, "i2c_cb: Received a msg: %d, arg_int: %d, arg_ptr: %p", msg, arg_int, arg_ptr);
esp_err_t err;
switch(msg) {
case U8X8_MSG_BYTE_SET_DC: {
if (u8g2_esp32_hal.dc != U8G2_ESP32_HAL_UNDEFINED) {
gpio_set_level(u8g2_esp32_hal.dc, arg_int);
}
break;
}
case U8X8_MSG_BYTE_INIT: {
if (u8g2_esp32_hal.sda == U8G2_ESP32_HAL_UNDEFINED ||
u8g2_esp32_hal.scl == U8G2_ESP32_HAL_UNDEFINED) {
break;
}
i2c_config_t conf;
conf.mode = I2C_MODE_MASTER;
ESP_LOGI(TAG, "sda_io_num %d", u8g2_esp32_hal.sda);
conf.sda_io_num = u8g2_esp32_hal.sda;
conf.sda_pullup_en = GPIO_PULLUP_ENABLE;
ESP_LOGI(TAG, "scl_io_num %d", u8g2_esp32_hal.scl);
conf.scl_io_num = u8g2_esp32_hal.scl;
conf.scl_pullup_en = GPIO_PULLUP_ENABLE;
ESP_LOGI(TAG, "clk_speed %d", I2C_MASTER_FREQ_HZ);
conf.master.clk_speed = I2C_MASTER_FREQ_HZ;
ESP_LOGI(TAG, "i2c_param_config %d", conf.mode);
err = i2c_param_config(I2C_MASTER_NUM, &conf); if(err) throw "OLED I2C setup failed";
ESP_LOGI(TAG, "i2c_driver_install %d", I2C_MASTER_NUM);
err = i2c_driver_install(I2C_MASTER_NUM, conf.mode, I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0);
if(err) throw "Oled I2C driver install failed";
break;
}
case U8X8_MSG_BYTE_SEND: {
uint8_t* data_ptr = (uint8_t*)arg_ptr;
ESP_LOG_BUFFER_HEXDUMP(TAG, data_ptr, arg_int, ESP_LOG_VERBOSE);
while( arg_int > 0 ) {
ESP_ERROR_CHECK(i2c_master_write_byte(handle_i2c, *data_ptr, ACK_CHECK_EN));
data_ptr++;
arg_int--;
}
break;
}
case U8X8_MSG_BYTE_START_TRANSFER: {
uint8_t i2c_address = u8x8_GetI2CAddress(u8x8);
handle_i2c = i2c_cmd_link_create();
ESP_LOGD(TAG, "Start I2C transfer to %02X.", i2c_address>>1);
ESP_ERROR_CHECK(i2c_master_start(handle_i2c));
ESP_ERROR_CHECK(i2c_master_write_byte(handle_i2c, i2c_address | I2C_MASTER_WRITE, ACK_CHECK_EN));
break;
}
case U8X8_MSG_BYTE_END_TRANSFER: {
ESP_LOGD(TAG, "End I2C transfer.");
err = i2c_master_stop(handle_i2c); if(err) throw "Oled init failed";
err = i2c_master_cmd_begin(I2C_MASTER_NUM, handle_i2c, I2C_TIMEOUT_MS / portTICK_RATE_MS); if(err) throw "Oled init failed";
i2c_cmd_link_delete(handle_i2c);
break;
}
}
return 0;
} // u8g2_esp32_i2c_byte_cb
/*
* HAL callback function as prescribed by the U8G2 library. This callback is invoked
* to handle callbacks for GPIO and delay functions.
*/
uint8_t u8g2_esp32_gpio_and_delay_cb(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) {
ESP_LOGD(TAG, "gpio_and_delay_cb: Received a msg: %d, arg_int: %d, arg_ptr: %p", msg, arg_int, arg_ptr);
switch(msg) {
// Initialize the GPIO and DELAY HAL functions. If the pins for DC and RESET have been
// specified then we define those pins as GPIO outputs.
case U8X8_MSG_GPIO_AND_DELAY_INIT: {
uint64_t bitmask = 0;
if (u8g2_esp32_hal.dc != U8G2_ESP32_HAL_UNDEFINED) {
bitmask = bitmask | (1ull<<u8g2_esp32_hal.dc);
}
if (u8g2_esp32_hal.reset != U8G2_ESP32_HAL_UNDEFINED) {
bitmask = bitmask | (1ull<<u8g2_esp32_hal.reset);
}
if (u8g2_esp32_hal.cs != U8G2_ESP32_HAL_UNDEFINED) {
bitmask = bitmask | (1ull<<u8g2_esp32_hal.cs);
}
if (bitmask==0) {
break;
}
gpio_config_t gpioConfig;
gpioConfig.pin_bit_mask = bitmask;
gpioConfig.mode = GPIO_MODE_OUTPUT;
gpioConfig.pull_up_en = GPIO_PULLUP_DISABLE;
gpioConfig.pull_down_en = GPIO_PULLDOWN_ENABLE;
gpioConfig.intr_type = GPIO_INTR_DISABLE;
gpio_config(&gpioConfig);
break;
}
// Set the GPIO reset pin to the value passed in through arg_int.
case U8X8_MSG_GPIO_RESET:
if (u8g2_esp32_hal.reset != U8G2_ESP32_HAL_UNDEFINED) {
gpio_set_level(u8g2_esp32_hal.reset, arg_int);
}
break;
// Set the GPIO client select pin to the value passed in through arg_int.
case U8X8_MSG_GPIO_CS:
if (u8g2_esp32_hal.cs != U8G2_ESP32_HAL_UNDEFINED) {
gpio_set_level(u8g2_esp32_hal.cs, arg_int);
}
break;
// Set the Software I²C pin to the value passed in through arg_int.
case U8X8_MSG_GPIO_I2C_CLOCK:
if (u8g2_esp32_hal.scl != U8G2_ESP32_HAL_UNDEFINED) {
gpio_set_level(u8g2_esp32_hal.scl, arg_int);
// printf("%c",(arg_int==1?'C':'c'));
}
break;
// Set the Software I²C pin to the value passed in through arg_int.
case U8X8_MSG_GPIO_I2C_DATA:
if (u8g2_esp32_hal.sda != U8G2_ESP32_HAL_UNDEFINED) {
gpio_set_level(u8g2_esp32_hal.sda, arg_int);
// printf("%c",(arg_int==1?'D':'d'));
}
break;
// Delay for the number of milliseconds passed in through arg_int.
case U8X8_MSG_DELAY_MILLI:
vTaskDelay(arg_int/portTICK_PERIOD_MS);
break;
}
return 0;
} // u8g2_esp32_gpio_and_delay_cb