mirror of
https://github.com/Valeh2012/PersonalVotingMachine
synced 2025-12-08 01:55:13 +02:00
first commit
This commit is contained in:
3
basic-setup/main/CMakeLists.txt
Normal file
3
basic-setup/main/CMakeLists.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
set(COMPONENT_SRCS "main.cpp")
|
||||
set(COMPONENT_ADD_INCLUDEDIRS "")
|
||||
register_component()
|
||||
82
basic-setup/main/Kconfig.projbuild
Normal file
82
basic-setup/main/Kconfig.projbuild
Normal 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
|
||||
7
basic-setup/main/component.mk
Normal file
7
basic-setup/main/component.mk
Normal 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
|
||||
234
basic-setup/main/converter.cpp
Normal file
234
basic-setup/main/converter.cpp
Normal 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);
|
||||
}
|
||||
150
basic-setup/main/impl/controllers/AuthorizationController.cpp
Normal file
150
basic-setup/main/impl/controllers/AuthorizationController.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
430
basic-setup/main/impl/controllers/BluetoothController.cpp
Normal file
430
basic-setup/main/impl/controllers/BluetoothController.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
118
basic-setup/main/impl/controllers/ChoiceController.cpp
Normal file
118
basic-setup/main/impl/controllers/ChoiceController.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
335
basic-setup/main/impl/controllers/EncryptionController.cpp
Normal file
335
basic-setup/main/impl/controllers/EncryptionController.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
16
basic-setup/main/impl/controllers/IndexController.cpp
Normal file
16
basic-setup/main/impl/controllers/IndexController.cpp
Normal 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");
|
||||
}
|
||||
344
basic-setup/main/impl/controllers/SignatureController.cpp
Normal file
344
basic-setup/main/impl/controllers/SignatureController.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
69
basic-setup/main/impl/controllers/VoteController.cpp
Normal file
69
basic-setup/main/impl/controllers/VoteController.cpp
Normal 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;
|
||||
}
|
||||
89
basic-setup/main/impl/controllers/ZipController.cpp
Normal file
89
basic-setup/main/impl/controllers/ZipController.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
13
basic-setup/main/impl/models/BluetoothModel.cpp
Normal file
13
basic-setup/main/impl/models/BluetoothModel.cpp
Normal 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;
|
||||
}
|
||||
16
basic-setup/main/impl/models/ChoiceModel.cpp
Normal file
16
basic-setup/main/impl/models/ChoiceModel.cpp
Normal 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;
|
||||
}
|
||||
21
basic-setup/main/impl/models/EncryptionModel.cpp
Normal file
21
basic-setup/main/impl/models/EncryptionModel.cpp
Normal 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;
|
||||
}
|
||||
30
basic-setup/main/impl/models/SignatureModel.cpp
Normal file
30
basic-setup/main/impl/models/SignatureModel.cpp
Normal 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;
|
||||
}
|
||||
19
basic-setup/main/impl/models/UserModel.cpp
Normal file
19
basic-setup/main/impl/models/UserModel.cpp
Normal 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;
|
||||
}
|
||||
20
basic-setup/main/impl/models/VoteModel.cpp
Normal file
20
basic-setup/main/impl/models/VoteModel.cpp
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
19
basic-setup/main/impl/models/ZipModel.cpp
Normal file
19
basic-setup/main/impl/models/ZipModel.cpp
Normal 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;
|
||||
}
|
||||
345
basic-setup/main/impl/modules/BLEModule.cpp
Normal file
345
basic-setup/main/impl/modules/BLEModule.cpp
Normal 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;
|
||||
}
|
||||
23
basic-setup/main/impl/modules/NVSModule.cpp
Normal file
23
basic-setup/main/impl/modules/NVSModule.cpp
Normal 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();
|
||||
}
|
||||
92
basic-setup/main/impl/modules/RPCModule.cpp
Normal file
92
basic-setup/main/impl/modules/RPCModule.cpp
Normal 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;
|
||||
}
|
||||
52
basic-setup/main/impl/modules/RotaryEncodeModule.cpp
Normal file
52
basic-setup/main/impl/modules/RotaryEncodeModule.cpp
Normal 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;
|
||||
}
|
||||
59
basic-setup/main/impl/modules/SNTPModule.cpp
Normal file
59
basic-setup/main/impl/modules/SNTPModule.cpp
Normal 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;
|
||||
}
|
||||
100
basic-setup/main/impl/modules/ScreenModule.cpp
Normal file
100
basic-setup/main/impl/modules/ScreenModule.cpp
Normal 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;
|
||||
}
|
||||
95
basic-setup/main/impl/modules/WiFiModule.cpp
Normal file
95
basic-setup/main/impl/modules/WiFiModule.cpp
Normal 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();
|
||||
}
|
||||
77
basic-setup/main/impl/views/ChoiceView.cpp
Normal file
77
basic-setup/main/impl/views/ChoiceView.cpp
Normal 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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
14
basic-setup/main/impl/views/IndexView.cpp
Normal file
14
basic-setup/main/impl/views/IndexView.cpp
Normal 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);
|
||||
}
|
||||
508
basic-setup/main/include/controller.h
Normal file
508
basic-setup/main/include/controller.h
Normal 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_ */
|
||||
141
basic-setup/main/include/converter.h
Normal file
141
basic-setup/main/include/converter.h
Normal 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
|
||||
142
basic-setup/main/include/model.h
Normal file
142
basic-setup/main/include/model.h
Normal 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 voter’s 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_ */
|
||||
202
basic-setup/main/include/module.h
Normal file
202
basic-setup/main/include/module.h
Normal 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_ */
|
||||
41
basic-setup/main/include/u8g2_esp32_hal.h
Normal file
41
basic-setup/main/include/u8g2_esp32_hal.h
Normal 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_ */
|
||||
72
basic-setup/main/include/view.h
Normal file
72
basic-setup/main/include/view.h
Normal 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
134
basic-setup/main/main.cpp
Normal 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;
|
||||
}
|
||||
234
basic-setup/main/u8g2_esp32_hal.cpp
Normal file
234
basic-setup/main/u8g2_esp32_hal.cpp
Normal 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
|
||||
Reference in New Issue
Block a user