first commit

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

View File

@@ -0,0 +1,153 @@
/**
* @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(){
ESP_LOGI(TAG, "%s", this->model->ID);
this->vw->render((void *) "Authorization");
this->vw->setLabel((char *) "Use mobile-ID");
}
void AuthorizationController::auth(){
this->vw->showLoader(true);
// Start mobilID Authentication
try{
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->setLabel(pin);
}
catch(const char* msg){
this->vw->setLabel((char *) msg);
this->vw->showLoader(false);
throw msg;
}
}
void AuthorizationController::authStatus(){
try{
char* authStatusReq = generateAuthenticateStatusRequestJSON();
cJSON * result, * authJson;
do{
ESP_LOGI(TAG, "waiting to confirm pin");
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->setLabel((char *) "Confirmed!");
this->vw->showLoader(false);
break;
}
cJSON_free(authJson);
}
while(1);
cJSON_Delete(authJson);
}
catch(const char* msg){
this->vw->setLabel((char *) msg);
this->vw->showLoader(false);
throw msg;
}
}

View File

@@ -0,0 +1,123 @@
/**
* @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{
this->vw->render(NULL);
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");
this->model->choices = (char *) malloc(21);
memset(this->model->choices, 0, 21);
strncpy(this->model->choices, cJSON_GetObjectItem(result, "Choices")->valuestring, 21);
unsigned char* list = (unsigned char *) cJSON_GetObjectItem(result, "List")->valuestring;
size_t bufflen = strlen((char*) list);
bufflen = (bufflen/4) *3 +1; // length before base64 encoding
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->showLoader(false);
this->vw->render((void *) this->model);
cJSON_Delete(parsedList);
free(decoded_list);
cJSON_Delete(choicesJson);
}
catch(const char* msg){
this->vw->setLabel((char *) msg);
this->vw->showLoader(false);
throw msg;
}
}
char* ChoiceController::generateChoicesRequestJSON(ChoiceModel *cm){
cJSON *root, *params, *param;
root = cJSON_CreateObject();
cJSON_AddNumberToObject(root, "id", 0.0);
cJSON_AddItemToObject(root, "method", cJSON_CreateString("RPC.VoterChoices"));
params = cJSON_CreateArray();
cJSON_AddItemToObject(root, "params", params);
cJSON_AddItemToArray(params, param=cJSON_CreateObject());
cJSON_AddStringToObject(param, "AuthMethod", "ticket");
cJSON_AddStringToObject(param, "AuthToken", cm->authToken);
cJSON_AddStringToObject(param, "OS", "Operating System,2,0");
cJSON_AddStringToObject(param, "SessionID", cm->ssid);
char* pretty = cJSON_Print(root);
cJSON_Delete(root);
return pretty;
}
void ChoiceController::parse_object(cJSON *item, char* par){
cJSON *subitem=item->child;
if ( !subitem ) { // leaf nodes are candidates
while(item){
choice_t *c = new choice_t(); // create new choice element
c->candidate = (char *) malloc(strlen(item->valuestring)+1); // allocate memory for candidate field
memset(c->candidate, 0, strlen(item->valuestring) +1); // set content 0
memcpy(c->candidate, item->valuestring, strlen(item->valuestring)); // copy from json to struct field
c->code = (char *) malloc(strlen(item->string)+1); // allocate memory for code field
memset(c->code, 0, strlen(item->string) +1); // set content 0
memcpy(c->code, item->string, strlen(item->string)); // copy from json to struct field
c->party = (char *) malloc(strlen(par)+1); // allocate memory for party field
memset(c->party, 0, strlen(par) +1); // set content 0
memcpy(c->party, par, strlen(par)); // copy from json to struct field
this->model->choice_list->push_back(*c); // push choice element to vector (list of choices)
item = item->next; // go to next candidate
}
}
while (subitem){
// handle subitem
if (subitem->child) {
parse_object(subitem->child, subitem->string);
}
subitem=subitem->next;
}
}

View File

@@ -0,0 +1,338 @@
/**
* @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(){
this->vw->render((void *)"Encryption");
this->vw->setLabel((char *) "Parsing encryption\ncertificate");
this->vw->showLoader(true);
try{
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->setLabel((char*) "Encrypted");
this->vw->showLoader(false);
}
catch(const char* msg){
this->vw->setLabel((char *) msg);
this->vw->showLoader(false);
throw msg;
}
}

View File

@@ -0,0 +1,23 @@
/**
* @file IndexController.cpp
* @brief IndexController implementation file
* */
#include "controller.h"
IndexController::IndexController(){
this->vw = new IndexView();
}
void IndexController::index(){
this->vw->render((void *) "Welcome\n");
this->vw->setLabel("starting");
}
void IndexController::end(){
this->vw->render((void *) "End\n");
this->vw->setLabel("Click boot to start again");
this->vw->showLoader(false);
}

View File

@@ -0,0 +1,31 @@
/**
* @file QRController.cpp
* @brief QRController implementation file
* */
#include "qrcode.h"
#include "controller.h"
QRController::QRController(BaseModel *model){
this->model = static_cast<QRModel *>(model);
this->vw = new QRView();
}
void QRController::index(){
// The structure to manage the QR code
QRCode qrcode;
// Allocate a chunk of memory to store the QR code
uint8_t qrcodeBytes[qrcode_getBufferSize(23)];
size_t len = strlen(this->model->ssid) + strlen(this->model->voteID) + strlen((char *)this->model->rndBase64) + 3;
char* data = (char *) malloc(len);
snprintf(data, len, "%s\n%s\n%s",this->model->ssid,this->model->rndBase64,this->model->voteID); //! * concatenate fields
// create qr code
qrcode_initText(&qrcode, qrcodeBytes, 23, ECC_LOW, data);
this->vw->render((void *) &qrcode);
}

View File

@@ -0,0 +1,357 @@
/**
* @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 *) "Sign");
this->vw->setLabel((char *) "Getting certificate");
this->vw->showLoader(true);
// 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
this->vw->setLabel((char *) cJSON_GetObjectItem(certificateJson, "error")->valuestring);
this->vw->showLoader(false);
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");
printf("available memory: %d\n", (int) heap_caps_get_largest_free_block(MALLOC_CAP_8BIT));
this->vw->setLabel((char *) "Insufficient memory");
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");
printf("available memory: %d\n", (int) heap_caps_get_largest_free_block(MALLOC_CAP_8BIT));
this->vw->setLabel((char *) "Insufficient memory");
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) {
this->vw->setLabel((char *) "Unexpected 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
// encrypted-ballot-name = election-identifier '.' question-identifier '.ballot'
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
ESP_LOGI(TAG, "signedInfo hash: %s", (char * ) this->model->SignedInfoHash);
}
catch(const char *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(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->setLabel((char *) pin);
cJSON_Delete(signJson);
}
catch(const char *msg){
this->vw->setLabel((char *) msg);
this->vw->showLoader(false);
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->setLabel((char *) "PIN CONFIRMED!");
break;
}
cJSON_free(signJson);
}
while(1);
this->vw->setLabel((char *)"Successfully signed");
this->vw->showLoader(false);
ESP_LOGI(TAG, "succesfully signed");
cJSON_Delete(signJson);
}
catch(const char *msg){
this->vw->setLabel((char *) msg);
this->vw->showLoader(false);
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);
free(this->model->signature);
// merge above 3 and create signature0.xml
this->model->Signature = (char *) malloc(10240);
if(this->model->Signature == NULL){
ESP_LOGE("ivxv", "unable to allocate memory of 5KB");
printf("available memory: %d\n", (int) heap_caps_get_free_size(MALLOC_CAP_8BIT));
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->setLabel((char *) msg);
this->vw->showLoader(false);
throw msg;
}
}

View File

@@ -0,0 +1,74 @@
/**
* @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{
this->vw->render((void *) "Voting");
this->vw->setLabel((char *) "Casting vote");
this->vw->showLoader(true);
// 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->setLabel((char *) "Vote casted");
this->vw->showLoader(false);
}
catch(const char *msg){
this->vw->setLabel((char *) msg);
this->vw->showLoader(false);
throw msg;
}
}
char* VoteController::generateVoteRequestJSON(){
cJSON *root, *params, *param;
root = cJSON_CreateObject();
cJSON_AddNumberToObject(root, "id", 0.0);
cJSON_AddItemToObject(root, "method", cJSON_CreateString("RPC.Vote"));
params = cJSON_CreateArray();
cJSON_AddItemToObject(root, "params", params);
cJSON_AddItemToArray(params, param=cJSON_CreateObject());
cJSON_AddStringToObject(param, "AuthMethod", "ticket");
cJSON_AddStringToObject(param, "AuthToken", this->model->authToken);
cJSON_AddStringToObject(param, "Choices", this->model->choices);
cJSON_AddStringToObject(param, "SessionID", this->model->ssid);
cJSON_AddStringToObject(param, "OS", "FreeRTOS");
cJSON_AddStringToObject(param, "Type", "bdoc");
cJSON_AddStringToObject(param, "Vote", (char *) this->model->voteBase64);
char* pretty = cJSON_Print(root);
cJSON_Delete(root);
return pretty;
}

View File

@@ -0,0 +1,96 @@
/**
* @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{
this->vw->render((void *) "Sign" );
this->vw->setLabel((char *) "Creating BDOC");
this->vw->showLoader(true);
// 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";
}
mz_free(zipBuf);
free(this->model->Signature);
free(this->model->ballot);
this->vw->setLabel((char*) "Signature complete");
this->vw->showLoader(false);
}
catch(const char *msg){
this->vw->setLabel((char *) msg);
this->vw->showLoader(false);
throw msg;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,90 @@
/**
* @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));
ESP_LOGE(TLS_TAG, "Connection failed...");
throw "Connection failed";
}
size_t written_bytes = 0;
do {
ret = esp_tls_conn_write(tls, req + written_bytes, strlen(req) - written_bytes); //! send rpc data
if(ret >= 0){
written_bytes += ret;
} else if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
ESP_LOGE(TLS_TAG, "esp_tls_conn_write returned 0x%x", ret);
esp_tls_conn_delete(tls);
}
} while(written_bytes < strlen(req));
do{
len = sizeof(buf) - 1;
bzero(buf, sizeof(buf));
ret = esp_tls_conn_read(tls, (char *)buf, len); //! recieve response in chunks
if(ret == MBEDTLS_ERR_SSL_WANT_WRITE || ret == MBEDTLS_ERR_SSL_WANT_READ)
continue;
if(ret < 0){
ESP_LOGE(TLS_TAG, "esp_tls_conn_read returned -0x%x", -ret);
break;
}
if(ret == 0){
ESP_LOGI(TLS_TAG, "connection closed");
break;
}
len = ret;
ESP_LOGD(TLS_TAG, "%d bytes read", len);
if(strlen(res) + len + 1 >= length){ //! In case no enough memory, extend res buffer
length += len; //! * increase by amount required
res = (char*) realloc(res, length); //! * reallocate memory
memset(res+length-len,0,len); //! * set new memory blocks 0 to assure the result is null-terminate string
}
memcpy(res + strlen(res), buf, len); //! write chunked response to buffer
} while(1);
cJSON *tmp = cJSON_Parse(res); //! parse response JSON into cJSON struct
free(res);
free(req);
esp_tls_conn_delete(tls);
return tmp;
}
RPC& RPC::Instance(){
static RPC instance;
return instance;
}

View File

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

View File

@@ -0,0 +1,77 @@
/**
* @file ScreenModule.cpp
* @brief ScreenModule implementation file
* */
#include "module.h"
#include "freertos/timers.h"
#include "esp_sleep.h"
#include "lvgl/lvgl.h"
#include "drv/disp_spi.h"
#include "drv/ili9341.h"
static void lv_task_timercb(void *timer){
/* Periodically call this function.
* The timing is not critical but should be between 1..10 ms */
lv_task_handler();
}
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(){
lv_init();
disp_spi_init();
ili9341_init();
static lv_color_t buf1[DISP_BUF_SIZE];
static lv_color_t buf2[DISP_BUF_SIZE];
static lv_disp_buf_t disp_buf;
lv_disp_buf_init(&disp_buf, buf1, buf2, DISP_BUF_SIZE);
lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv);
disp_drv.flush_cb = ili9341_flush;
disp_drv.buffer = &disp_buf;
disp_drv.rotated = 1 ;
lv_disp_drv_register(&disp_drv);
esp_timer_create_args_t lv_task_timer_conf = {
.callback = lv_task_timercb,
.name = "lv_task_timer"
};
esp_timer_handle_t lv_task_timer = NULL;
esp_timer_create(&lv_task_timer_conf, &lv_task_timer);
esp_timer_start_periodic(lv_task_timer, 5 * 1000U);
return ESP_OK;
}
esp_err_t ScreenModule::deinit(){
return ESP_OK;
}
ScreenModule& ScreenModule::Instance(){
static ScreenModule instance;
return instance;
}

View File

@@ -0,0 +1,76 @@
/**
* @file TouchModule.cpp
* @brief TouchModule implementation file
* */
#include "lvgl/lvgl.h"
#include "Arduino.h"
#include "TouchScreen.h"
#include "module.h"
#define TS_MINX 200
#define TS_MINY 150
#define TS_MAXX 940
#define TS_MAXY 950
TouchModule::TouchModule(gpio_num_t a, gpio_num_t b, gpio_num_t c, gpio_num_t d){
this->YP = a;
this->XP = b;
this->XM = c;
this->YM = d;
};
int map(int x, int in_min, int in_max, int out_min, int out_max) {
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
TouchScreen* ts;
static bool touch_callback(lv_indev_drv_t * drv, lv_indev_data_t *data){
static int last_x = 0, last_y = 0;
analogReadResolution(10);
TSPoint p = ts->getPoint();
bool valid = true;
int x,y;
if(p.z > 0){
p.x = 240 - map(p.x, TS_MINX, TS_MAXX, 0, 240);
p.y = 320 - map(p.y, TS_MINY, TS_MAXY, 0, 320);
x = p.x;
y = p.y;
last_x = x;
last_y = y;
}else{
x = last_x;
y = last_y;
valid = false;
}
data->point.x = x;
data->point.y = y;
data->state = valid ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL;
return false;
}
esp_err_t TouchModule::init(){
ts = new TouchScreen(XP, YP, XM, YM, 0 );
lv_indev_drv_t indev_drv;
lv_indev_drv_init(&indev_drv); /*Basic initialization*/
indev_drv.type = LV_INDEV_TYPE_POINTER;
indev_drv.read_cb = touch_callback;
/*Register the driver in LittlevGL and save the created input device object*/
lv_indev_t * my_indev = lv_indev_drv_register(&indev_drv);
return ESP_OK;
};
esp_err_t TouchModule::deinit(){
return ESP_OK;
};

View File

@@ -0,0 +1,87 @@
/**
* @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");
}
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_ERROR_CHECK(esp_event_loop_create_default());
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL));
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;
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) );
ESP_ERROR_CHECK(esp_wifi_start() );
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(s_retry_num >= MAXIMUM_RETRY){
throw "WiFi unable to connect";
}
}
return ESP_OK;
}
esp_err_t WiFiModule::deinit(){
return esp_wifi_deinit();
}

View File

@@ -0,0 +1,186 @@
/**
* @file ChoiceView.cpp
* @brief ChoiceView class implementation
* */
#include "esp_log.h"
#include "string.h"
#include "lvgl/lvgl.h"
#include "module.h"
#include "view.h"
#include "model.h"
lv_obj_t * selected;
static bool nextstage = false;
static void btn_event_cb(lv_obj_t * btn, lv_event_t event){
if(event == LV_EVENT_PRESSED) {
if(selected == NULL){
selected = btn;
lv_btn_set_state(btn, LV_BTN_STATE_TGL_PR);
}
else if(selected != btn){
lv_btn_set_state(selected, LV_BTN_STATE_REL);
selected = btn;
lv_btn_set_state(btn, LV_BTN_STATE_TGL_PR);
}else if(selected == btn){
lv_btn_set_state(btn, LV_BTN_STATE_PR);
selected= NULL;
}
}
}
static void next_cb(lv_obj_t * btn, lv_event_t event){
if(event == LV_EVENT_PRESSED) {
lv_obj_t * scr = lv_scr_act();
lv_obj_t * list1 = lv_obj_get_child(scr, NULL);
lv_list_ext_t * ext = (lv_list_ext_t *) lv_obj_get_ext_attr(list1);;
if(selected != NULL) {
nextstage = true;
}
else{
/*Create a window*/
lv_obj_t * win = lv_win_create(lv_scr_act(), NULL);
lv_win_set_title(win, "Window title"); /*Set the title*/
lv_obj_set_size(win, 240,120);
lv_obj_align(win, NULL, LV_ALIGN_CENTER, 0, 0);
/*Add control button to the header*/
lv_obj_t * close_btn = lv_win_add_btn(win, LV_SYMBOL_CLOSE); /*Add close button and use built-in close action*/
lv_obj_set_event_cb(close_btn, lv_win_close_event_cb);
/*Add some dummy content*/
lv_obj_t * txt = lv_label_create(win, NULL);
lv_label_set_text(txt, "Select a candidate to continue\n");
}
}
}
ChoiceView::ChoiceView(){};
void ChoiceView::render(void * data){
if(data == NULL){
lv_obj_t * scr = lv_obj_create(NULL, NULL);
lv_obj_del(lv_scr_act());
static lv_style_t style_scr_bg;
lv_style_copy(&style_scr_bg, &lv_style_plain);
style_scr_bg.body.main_color = lv_color_hex(0x487fb7);
style_scr_bg.body.grad_color = lv_color_hex(0x487fb7);
lv_obj_set_style(scr, &style_scr_bg);
lv_obj_t * label1 = lv_label_create(scr, NULL);
lv_label_set_text(label1, "Candidates");
lv_label_set_align(label1, LV_LABEL_ALIGN_LEFT);
lv_obj_align(label1, NULL, LV_ALIGN_IN_TOP_MID, 0, 10);
lv_label_set_style(label1, LV_LABEL_STYLE_MAIN, &lv_style_plain_color);
lv_style_copy(&big_text_style, &lv_style_plain_color);
big_text_style.text.font = &lv_font_roboto_28;
lv_obj_t * label2 = lv_label_create(scr, NULL);
lv_label_set_text(label2, "");
lv_label_set_long_mode(label2, LV_LABEL_LONG_BREAK);
lv_obj_set_size(label2, 200, 200);
lv_label_set_align(label2, LV_LABEL_ALIGN_CENTER);
lv_obj_align(label2, NULL, LV_ALIGN_IN_LEFT_MID, 0, -20);
lv_label_set_style(label2, LV_LABEL_STYLE_MAIN, &big_text_style);
lv_obj_set_hidden(label2, true);
/*Create a style for the Preloader*/
lv_style_copy(&style, &lv_style_plain);
style.line.width = 4; /*10 px thick arc*/
style.line.color = lv_color_hex3(0xfff); /*Blueish arc color*/
style.body.border.color = lv_color_hex(0x487fb7); /*Gray background color*/
style.body.border.width = 4;
style.body.padding.left = 0;
/*Create a Preloader object*/
lv_obj_t * preload = lv_preload_create(scr, NULL);
lv_preload_set_type(preload, LV_PRELOAD_TYPE_FILLSPIN_ARC);
lv_obj_set_size(preload, 60, 60);
lv_preload_set_spin_time(preload, 1000);
lv_preload_set_arc_length(preload, 120);
lv_obj_align(preload, NULL, LV_ALIGN_IN_BOTTOM_MID, 0, -5);
lv_preload_set_style(preload, LV_PRELOAD_STYLE_MAIN, &style);
lv_obj_set_hidden(preload, false);
lv_scr_load(scr);
}else{
ChoiceModel * model = (ChoiceModel *)data;
lv_coord_t hres = lv_disp_get_hor_res(NULL);
lv_coord_t vres = lv_disp_get_ver_res(NULL);
lv_obj_t * scr = lv_scr_act();
lv_obj_t * list1 = lv_list_create(scr, NULL);
lv_obj_set_size(list1, hres, vres-80);
lv_obj_align(list1, NULL, LV_ALIGN_IN_TOP_MID, 0, 40);
static lv_style_t list_style;
lv_style_copy(&list_style, &lv_style_btn_pr);
list_style.body.main_color = LV_COLOR_RED;
list_style.body.grad_color = lv_color_hex(0xff0088);
lv_list_set_style(list1, LV_LIST_STYLE_BTN_TGL_PR, &list_style);
lv_list_set_style(list1, LV_LIST_STYLE_BTN_TGL_REL, &list_style);
/*Add buttons to the list*/
lv_obj_t * list_btn;
for(int i=0;i< model->choice_list->size(); i++){
list_btn = lv_list_add_btn(list1, NULL, model->choice_list->at(i).candidate);
lv_obj_set_height(list_btn, 20);
lv_obj_set_event_cb(list_btn, btn_event_cb);
}
lv_list_set_sb_mode(list1,LV_SB_MODE_AUTO);
lv_obj_t * btn1_label;
lv_obj_t * btn1 = lv_btn_create(scr, NULL);
lv_obj_set_event_cb(btn1, next_cb);
lv_obj_set_size(btn1, 200, 40);
lv_obj_align(btn1, NULL, LV_ALIGN_IN_BOTTOM_MID, 0, 0);
btn1_label = lv_label_create(btn1, NULL);
lv_label_set_style(btn1_label, LV_LABEL_STYLE_MAIN, &lv_style_plain_color);
lv_label_set_text(btn1_label, "NEXT");
while (!nextstage){
// Wait for incoming events on the event queue.
vTaskDelay(100);
}
int r = lv_list_get_btn_index(list1, selected);
// 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->at(r).code, model->choice_list->at(r).party, model->choice_list->at(r).candidate);
}
}
void ChoiceView::setLabel(char* data){
for(int i=0;i<strlen(data);i++){
if(data[i] == '_')
data[i] = '\n';
}
lv_obj_t * scr = lv_scr_act();
lv_obj_t * child = lv_obj_get_child_back(scr, NULL);
child = lv_obj_get_child_back(scr, child);
lv_label_set_text(child, data);
lv_obj_set_hidden(child, false);
}
void ChoiceView::showLoader(bool en){
lv_obj_t * scr = lv_scr_act();
lv_obj_t * child = lv_obj_get_child(scr, NULL);
lv_obj_set_hidden(child, !en);
}

View File

@@ -0,0 +1,78 @@
/**
* @file IndexView.cpp
* @brief IndexView class implementation
* */
#include "lvgl/lvgl.h"
#include "view.h"
IndexView::IndexView(){};
void IndexView::render(void * data){
lv_obj_t * scr = lv_obj_create(NULL, NULL);
lv_obj_del(lv_scr_act());
lv_style_copy(&style_scr_bg, &lv_style_plain);
style_scr_bg.body.main_color = lv_color_hex(0x487fb7);
style_scr_bg.body.grad_color = lv_color_hex(0x487fb7);
lv_obj_set_style(scr, &style_scr_bg);
lv_obj_t * label1 = lv_label_create(scr, NULL);
lv_label_set_text(label1, (char *) data);
lv_label_set_align(label1, LV_LABEL_ALIGN_CENTER);
lv_obj_align(label1, NULL, LV_ALIGN_IN_TOP_MID, 0, 10);
lv_label_set_style(label1, LV_LABEL_STYLE_MAIN, &lv_style_plain_color);
lv_style_copy(&big_text_style, &lv_style_plain_color);
big_text_style.text.font = &lv_font_roboto_22;
lv_obj_t * label2 = lv_label_create(scr, NULL);
lv_label_set_text(label2, "");
lv_label_set_long_mode(label2, LV_LABEL_LONG_BREAK);
lv_obj_set_size(label2, 200, 200);
lv_label_set_align(label2, LV_LABEL_ALIGN_CENTER);
lv_obj_align(label2, NULL, LV_ALIGN_IN_LEFT_MID, 0, -20);
lv_label_set_style(label2, LV_LABEL_STYLE_MAIN, &big_text_style);
/*Create a style for the Preloader*/
lv_style_copy(&style, &lv_style_plain);
style.line.width = 4; /*10 px thick arc*/
style.line.color = lv_color_hex3(0xfff); /*Blueish arc color*/
style.body.border.color = lv_color_hex(0x487fb7); /*Gray background color*/
style.body.border.width = 4;
style.body.padding.left = 0;
/*Create a Preloader object*/
lv_obj_t * preload = lv_preload_create(scr, NULL);
lv_preload_set_type(preload, LV_PRELOAD_TYPE_FILLSPIN_ARC);
lv_obj_set_size(preload, 60, 60);
lv_preload_set_spin_time(preload, 1000);
lv_preload_set_arc_length(preload, 120);
lv_obj_align(preload, NULL, LV_ALIGN_IN_BOTTOM_MID, 0, -5);
lv_preload_set_style(preload, LV_PRELOAD_STYLE_MAIN, &style);
lv_obj_set_hidden(preload, true);
lv_scr_load(scr);
}
void IndexView::setLabel(char* data){
for(int i=0;i<strlen(data);i++){
if(data[i] == '_'){
data[i] = '\n';
}
}
lv_obj_t * scr = lv_scr_act();
lv_obj_t * child = lv_obj_get_child(scr, NULL);
child = lv_obj_get_child(scr, child);
lv_label_set_text(child, data);
}
void IndexView::showLoader(bool en){
lv_obj_t * scr = lv_scr_act();
lv_obj_t * child = lv_obj_get_child(scr, NULL);
lv_obj_set_hidden(child, !en);
}

View File

@@ -0,0 +1,98 @@
/**
* @file IndexView.cpp
* @brief IndexView class implementation
* */
#include "lvgl/lvgl.h"
#include "view.h"
#include "qrcode.h"
static bool clicked = false;
static void finish_cb(lv_obj_t * btn, lv_event_t event){
if(event == LV_EVENT_CLICKED){
clicked = true;
}
}
QRView::QRView(){};
void QRView::render(void * data){
QRCode * qrcode = (QRCode *) data;
lv_obj_t * scr = lv_obj_create(NULL, NULL);
lv_style_copy(&style_scr_bg, &lv_style_plain);
style_scr_bg.body.main_color = lv_color_hex(0x00ff00);
style_scr_bg.body.grad_color = lv_color_hex(0x00ff00);
lv_obj_set_style(scr, &style_scr_bg);
lv_obj_t * label1 = lv_label_create(scr, NULL);
lv_label_set_text(label1, (char *) "QR code");
lv_label_set_align(label1, LV_LABEL_ALIGN_CENTER);
lv_obj_align(label1, NULL, LV_ALIGN_IN_TOP_MID, 0, 10);
uint8_t CH = 2*qrcode->size;
static lv_color_t cbuf[LV_CANVAS_BUF_SIZE_INDEXED_1BIT(240, 240)];
lv_obj_t * canvas = lv_canvas_create(scr, NULL);
lv_canvas_set_buffer(canvas, cbuf, 240,240, LV_IMG_CF_INDEXED_1BIT);
lv_canvas_set_palette(canvas, 0, LV_COLOR_WHITE);
lv_canvas_set_palette(canvas, 1, LV_COLOR_BLACK);
lv_obj_align(canvas, NULL, LV_ALIGN_CENTER, 0, 0);
/*Create colors with the indices of the palette*/
lv_color_t c0;
lv_color_t c1;
c0.full = 0;
c1.full = 1;
lv_canvas_fill_bg(canvas, c0);
static lv_style_t small_text_style;
lv_style_copy(&small_text_style, &lv_style_plain);
small_text_style.text.font = &lv_font_roboto_12;
small_text_style.text.color = c1;
uint8_t x2=10,y2=10;
for (uint8_t y = 0; y < qrcode->size; y++) {
for (uint8_t x = 0; x < qrcode->size; x++) {
if (qrcode_getModule(qrcode, x, y)) {
lv_canvas_set_px(canvas, 2*x+5, 2*y+5, c1);
lv_canvas_set_px(canvas, 2*x+1+5, 2*y+5, c1);
lv_canvas_set_px(canvas, 2*x+5, 2*y+1+5, c1);
lv_canvas_set_px(canvas, 2*x+1+5, 2*y+1+5, c1);
}
x2+=2;
if(x2 == 2*qrcode->size){
y2+=2;
x2=10;
}
}
}
lv_obj_t * label2;
lv_obj_t * btn1 = lv_btn_create(scr, NULL);
lv_obj_set_event_cb(btn1, finish_cb);
lv_obj_set_size(btn1, 100, 40);
lv_obj_align(btn1, NULL, LV_ALIGN_IN_BOTTOM_MID, 0, 0);
label2 = lv_label_create(btn1, NULL);
lv_label_set_style(label2, LV_LABEL_STYLE_MAIN, &lv_style_plain_color);
lv_label_set_text(label2, "Finish");
lv_obj_del(lv_scr_act());
lv_scr_load(scr);
while(!clicked){
vTaskDelay(100);
}
}
void QRView::setLabel(char* data){
}
void QRView::showLoader(bool en){
}