mirror of
https://github.com/Valeh2012/PersonalVotingMachine
synced 2024-11-26 02:40:59 +02:00
336 lines
11 KiB
C++
336 lines
11 KiB
C++
|
/**
|
||
|
* @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;
|
||
|
}
|
||
|
}
|