/**
\file crypto.cpp
\author michael.zohner@ec-spride.de
\copyright ABY - A Framework for Efficient Mixed-protocol Secure Two-party Computation
Copyright (C) 2019 ENCRYPTO Group, TU Darmstadt
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ABY is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see .
\brief Implementation of crypto primitive class
*/
#include "crypto.h"
#include "../socket.h"
#include
#include
#include "ecc-pk-crypto.h"
#include "gmp-pk-crypto.h"
#include
#include
#include
#include
#include
crypto::crypto(uint32_t symsecbits, uint8_t* seed) {
init(symsecbits, seed);
}
crypto::crypto(uint32_t symsecbits) {
uint8_t* seed = (uint8_t*) malloc(sizeof(uint8_t) * AES_BYTES);
gen_secure_random(seed, AES_BYTES);
init(symsecbits, seed);
free(seed);
}
crypto::~crypto() {
free_prf_state(&global_prf_state);
free(aes_hash_in_buf);
free(aes_hash_out_buf);
free(sha_hash_buf);
free(aes_hash_buf_y1);
free(aes_hash_buf_y2);
#ifdef OPENSSL_OPAQUE_EVP_CIPHER_CTX
clean_aes_key(&aes_hash_key);
clean_aes_key(&aes_enc_key);
clean_aes_key(&aes_dec_key);
#endif
}
void crypto::init(uint32_t symsecbits, uint8_t* seed) {
secparam = get_sec_lvl(symsecbits);
#ifdef OPENSSL_OPAQUE_EVP_CIPHER_CTX
aes_hash_key = EVP_CIPHER_CTX_new();
aes_enc_key = EVP_CIPHER_CTX_new();
aes_dec_key = EVP_CIPHER_CTX_new();
#endif
init_prf_state(&global_prf_state, seed);
aes_hash_in_buf = (uint8_t*) malloc(AES_BYTES);
aes_hash_out_buf = (uint8_t*) malloc(AES_BYTES);
aes_hash_buf_y1 = (uint8_t*) malloc(AES_BYTES);
aes_hash_buf_y2 = (uint8_t*) malloc(AES_BYTES);
if (secparam.symbits == ST.symbits) {
hash_routine = &sha1_hash;
sha_hash_buf = (uint8_t*) malloc(SHA1_OUT_BYTES);
} else if (secparam.symbits == MT.symbits) {
hash_routine = &sha256_hash;
sha_hash_buf = (uint8_t*) malloc(SHA256_OUT_BYTES);
} else if (secparam.symbits == LT.symbits) {
hash_routine = &sha256_hash;
sha_hash_buf = (uint8_t*) malloc(SHA256_OUT_BYTES);
} else if (secparam.symbits == XLT.symbits) {
hash_routine = &sha512_hash;
sha_hash_buf = (uint8_t*) malloc(SHA512_OUT_BYTES);
} else if (secparam.symbits == XXLT.symbits) {
hash_routine = &sha512_hash;
sha_hash_buf = (uint8_t*) malloc(SHA512_OUT_BYTES);
} else {
hash_routine = &sha256_hash;
sha_hash_buf = (uint8_t*) malloc(SHA256_OUT_BYTES);
}
}
pk_crypto* crypto::gen_field(field_type ftype) {
uint8_t* pkseed = (uint8_t*) malloc(sizeof(uint8_t) * (secparam.symbits >> 3));
gen_rnd(pkseed, secparam.symbits >> 3);
pk_crypto* ret;
if (ftype == P_FIELD)
ret = new prime_field(secparam, pkseed);
else
ret = new ecc_field(secparam, pkseed);
free(pkseed);
return ret;
}
void gen_rnd_bytes(prf_state_ctx* prf_state, uint8_t* resbuf, uint32_t nbytes) {
AES_KEY_CTX* aes_key;
uint64_t* rndctr;
uint8_t* tmpbuf;
uint32_t i, size;
int32_t dummy;
aes_key = &(prf_state->aes_key);
rndctr = prf_state->ctr;
size = ceil_divide(nbytes, AES_BYTES);
tmpbuf = (uint8_t*) malloc(sizeof(uint8_t) * size * AES_BYTES);
//TODO it might be better to store the result directly in resbuf but this would require the invoking routine to pad it to a multiple of AES_BYTES
for (i = 0; i < size; i++, rndctr[0]++) {
#ifdef OPENSSL_OPAQUE_EVP_CIPHER_CTX
EVP_EncryptUpdate(*aes_key, tmpbuf + i * AES_BYTES, &dummy, (uint8_t*) rndctr, AES_BYTES);
#else
EVP_EncryptUpdate(aes_key, tmpbuf + i * AES_BYTES, &dummy, (uint8_t*) rndctr, AES_BYTES);
#endif
}
memcpy(resbuf, tmpbuf, nbytes);
free(tmpbuf);
}
void crypto::gen_rnd(uint8_t* resbuf, uint32_t nbytes) {
std::lock_guard lock(global_prf_state_mutex);
gen_rnd_bytes(&global_prf_state, resbuf, nbytes);
}
void crypto::gen_rnd_uniform(uint32_t* res, uint32_t mod) {
//pad to multiple of 4 bytes for uint32_t length
uint32_t nrndbytes = PadToMultiple(bits_in_bytes(secparam.symbits) + ceil_log2(mod), sizeof(uint32_t));
uint64_t bitsint = (8*sizeof(uint32_t));
uint32_t rnditers = ceil_divide(nrndbytes * 8, bitsint);
uint32_t* rndbuf = (uint32_t*) malloc(nrndbytes);
gen_rnd((uint8_t*) rndbuf, nrndbytes);
uint64_t tmpval = 0, tmpmod = mod;
for(uint32_t i = 0; i < rnditers; i++) {
tmpval = (((uint64_t) (tmpval << bitsint)) | ((uint64_t)rndbuf[i]));
tmpval %= tmpmod;
}
*res = (uint32_t) tmpval;
free(rndbuf);
}
void crypto::gen_rnd_from_seed(uint8_t* resbuf, uint32_t resbytes, uint8_t* seed) {
prf_state_ctx tmpstate;
init_prf_state(&tmpstate, seed);
gen_rnd_bytes(&tmpstate, resbuf, resbytes);
free_prf_state(&tmpstate);
}
void crypto::encrypt(AES_KEY_CTX* enc_key, uint8_t* resbuf, uint8_t* inbuf, uint32_t ninbytes) {
int32_t dummy;
#ifdef OPENSSL_OPAQUE_EVP_CIPHER_CTX
EVP_EncryptUpdate(*enc_key, resbuf, &dummy, inbuf, ninbytes);
#else
EVP_EncryptUpdate(enc_key, resbuf, &dummy, inbuf, ninbytes);
#endif
//EVP_EncryptFinal_ex(enc_key, resbuf, &dummy);
}
void crypto::decrypt(AES_KEY_CTX* dec_key, uint8_t* resbuf, uint8_t* inbuf, uint32_t ninbytes) {
int32_t dummy;
#ifdef OPENSSL_OPAQUE_EVP_CIPHER_CTX
EVP_DecryptUpdate(*dec_key, resbuf, &dummy, inbuf, ninbytes);
#else
EVP_DecryptUpdate(dec_key, resbuf, &dummy, inbuf, ninbytes);
#endif
//EVP_DecryptFinal_ex(dec_key, resbuf, &dummy);
}
void crypto::encrypt(uint8_t* resbuf, uint8_t* inbuf, uint32_t ninbytes) {
encrypt(&aes_enc_key, resbuf, inbuf, ninbytes);
}
void crypto::decrypt(uint8_t* resbuf, uint8_t* inbuf, uint32_t ninbytes) {
decrypt(&aes_dec_key, resbuf, inbuf, ninbytes);
}
void crypto::seed_aes_hash(uint8_t* seed, bc_mode mode, const uint8_t* iv) {
seed_aes_key(&aes_hash_key, seed, mode, iv);
}
void crypto::seed_aes_enc(uint8_t* seed, bc_mode mode, const uint8_t* iv) {
seed_aes_key(&aes_enc_key, seed, mode, iv, true);
seed_aes_key(&aes_dec_key, seed, mode, iv, false);
}
void crypto::init_aes_key(AES_KEY_CTX* aes_key, uint8_t* seed, bc_mode mode, const uint8_t* iv) {
seed_aes_key(aes_key, seed, mode, iv);
}
void crypto::init_aes_key(AES_KEY_CTX* aes_key, uint32_t symbits, uint8_t* seed, bc_mode mode, const uint8_t* iv, bool encrypt) {
seed_aes_key(aes_key, symbits, seed, mode, iv, encrypt);
}
void crypto::seed_aes_key(AES_KEY_CTX* aeskey, uint8_t* seed, bc_mode mode, const uint8_t* iv, bool encrypt) {
seed_aes_key(aeskey, secparam.symbits, seed, mode, iv, encrypt);
}
void crypto::clean_aes_key(AES_KEY_CTX* aeskey) {
#ifdef OPENSSL_OPAQUE_EVP_CIPHER_CTX
EVP_CIPHER_CTX_free(*aeskey);
#else
EVP_CIPHER_CTX_cleanup(aeskey);
#endif
}
void crypto::seed_aes_key(AES_KEY_CTX* aeskey, uint32_t symbits, uint8_t* seed, bc_mode mode, const uint8_t* iv, bool encrypt) {
#ifdef OPENSSL_OPAQUE_EVP_CIPHER_CTX
*aeskey = EVP_CIPHER_CTX_new();
AES_KEY_CTX aes_key_tmp = *aeskey;
#else
EVP_CIPHER_CTX_init(aeskey);
AES_KEY_CTX* aes_key_tmp = aeskey;
#endif
int (*initfct)(EVP_CIPHER_CTX*, const EVP_CIPHER*, ENGINE*, const unsigned char*, const unsigned char*);
if (encrypt)
initfct = EVP_EncryptInit_ex;
else
initfct = EVP_DecryptInit_ex;
switch (mode) {
case ECB:
if (symbits <= 128) {
initfct(aes_key_tmp, EVP_aes_128_ecb(), NULL, seed, iv);
} else if(symbits == 192) {
initfct(aes_key_tmp, EVP_aes_192_ecb(), NULL, seed, iv);
} else {
initfct(aes_key_tmp, EVP_aes_256_ecb(), NULL, seed, iv);
}
break;
case CBC:
if (symbits <= 128) {
initfct(aes_key_tmp, EVP_aes_128_cbc(), NULL, seed, iv);
} else if(symbits == 192) {
initfct(aes_key_tmp, EVP_aes_192_cbc(), NULL, seed, iv);
} else {
initfct(aes_key_tmp, EVP_aes_256_cbc(), NULL, seed, iv);
}
break;
default:
if (symbits <= 128) {
initfct(aes_key_tmp, EVP_aes_128_ecb(), NULL, seed, iv);
} else if(symbits == 192) {
initfct(aes_key_tmp, EVP_aes_192_ecb(), NULL, seed, iv);
} else {
initfct(aes_key_tmp, EVP_aes_256_ecb(), NULL, seed, iv);
}
break;
}
}
void crypto::hash_ctr(uint8_t* resbuf, uint32_t noutbytes, uint8_t* inbuf, uint32_t ninbytes, uint64_t ctr) {
uint8_t* tmpbuf = (uint8_t*) malloc(ninbytes + sizeof(uint64_t));
memcpy(tmpbuf, &ctr, sizeof(uint64_t));
memcpy(tmpbuf + sizeof(uint64_t), inbuf, ninbytes);
hash_routine(resbuf, noutbytes, tmpbuf, ninbytes+sizeof(uint64_t), sha_hash_buf);
free(tmpbuf);
}
void crypto::hash(uint8_t* resbuf, uint32_t noutbytes, uint8_t* inbuf, uint32_t ninbytes) {
uint8_t* hash_buf = (uint8_t*) malloc(get_hash_bytes());
hash_routine(resbuf, noutbytes, inbuf, ninbytes, hash_buf);
free(hash_buf);
}
void crypto::hash_buf(uint8_t* resbuf, uint32_t noutbytes, uint8_t* inbuf, uint32_t ninbytes, uint8_t* buf) {
hash_routine(resbuf, noutbytes, inbuf, ninbytes, buf);
}
void crypto::hash_non_threadsafe(uint8_t* resbuf, uint32_t noutbytes, uint8_t* inbuf, uint32_t ninbytes) {
hash_routine(resbuf, noutbytes, inbuf, ninbytes, sha_hash_buf);
}
//A fixed-key hashing scheme that uses AES, should not be used for real hashing, hashes to AES_BYTES bytes
//TODO not thread safe
void crypto::fixed_key_aes_hash(AES_KEY_CTX* aes_key, uint8_t* resbuf, uint32_t noutbytes, uint8_t* inbuf, uint32_t ninbytes) {
int32_t dummy;
memset(aes_hash_in_buf, 0, AES_BYTES);
memcpy(aes_hash_in_buf, inbuf, ninbytes);
//two encryption iterations TODO: not secure since both blocks are treated independently, implement DM or MMO
#ifdef OPENSSL_OPAQUE_EVP_CIPHER_CTX
EVP_EncryptUpdate(*aes_key, aes_hash_out_buf, &dummy, aes_hash_in_buf, AES_BYTES);
#else
EVP_EncryptUpdate(aes_key, aes_hash_out_buf, &dummy, aes_hash_in_buf, AES_BYTES);
#endif
((uint64_t*) aes_hash_out_buf)[0] ^= ((uint64_t*) aes_hash_in_buf)[0];
((uint64_t*) aes_hash_out_buf)[1] ^= ((uint64_t*) aes_hash_in_buf)[1];
memcpy(resbuf, aes_hash_out_buf, noutbytes);
}
//Generate a random permutation of neles elements using Knuths algorithm
void crypto::gen_rnd_perm(uint32_t* perm, uint32_t neles) {
uint32_t* rndbuf = (uint32_t*) malloc(sizeof(uint32_t) * neles);
uint32_t i, j;
//TODO Generate random numbers (CAREFUL: NOT UNIFORM)
gen_rnd((uint8_t*) rndbuf, sizeof(uint32_t) * neles);
for (i = 0; i < neles; i++) {
perm[i] = i;
}
for (i = 0; i < neles; i++) {
j = rndbuf[i] % neles; //NOT UNIFORM
std::swap(perm[i], perm[j]);
}
free(rndbuf);
}
uint32_t crypto::get_aes_key_bytes() {
if (secparam.symbits == ST.symbits)
return 16;
else if (secparam.symbits == MT.symbits)
return 16;
else if (secparam.symbits == LT.symbits)
return 16;
else if (secparam.symbits == XLT.symbits)
return 24;
else if (secparam.symbits == XXLT.symbits)
return 32;
else
return 64;
}
uint32_t crypto::get_hash_bytes() {
if (secparam.symbits == ST.symbits)
return 20;
else if (secparam.symbits == MT.symbits)
return 32;
else if (secparam.symbits == LT.symbits)
return 32;
else if (secparam.symbits == XLT.symbits)
return 64;
else if (secparam.symbits == XXLT.symbits)
return 64;
else
return 64;
}
//Generate a common seed, is only secure in the semi-honest model
void crypto::gen_common_seed(prf_state_ctx* prf_state, CSocket& sock) {
uint8_t *seed_buf, *seed_rcv_buf;
uint32_t seed_bytes, i;
seed_bytes = get_aes_key_bytes();
seed_buf = (uint8_t*) malloc(seed_bytes);
seed_rcv_buf = (uint8_t*) malloc(seed_bytes);
//randomly generate and exchange seed bytes:
gen_rnd(seed_buf, seed_bytes);
sock.Send(seed_buf, seed_bytes);
sock.Receive(seed_rcv_buf, seed_bytes);
//xor both seeds
for (i = 0; i < seed_bytes; i++) {
seed_buf[i] ^= seed_rcv_buf[i];
}
init_prf_state(prf_state, seed_buf);
free(seed_buf);
free(seed_rcv_buf);
}
void crypto::init_prf_state(prf_state_ctx* prf_state, uint8_t* seed) {
seed_aes_key(&(prf_state->aes_key), seed);
prf_state->ctr = (uint64_t*) calloc(ceil_divide(secparam.symbits, 8 * sizeof(uint64_t)), sizeof(uint64_t));
}
void crypto::free_prf_state(prf_state_ctx* prf_state) {
free(prf_state->ctr);
clean_aes_key(&(prf_state->aes_key));
}
void des_encrypt(uint8_t* resbuf, uint8_t* inbuf, uint8_t* key, bool encrypt) {
DES_cblock keyblock;
DES_cblock msgblock;
DES_cblock outblock;
DES_key_schedule schedule;
memcpy(msgblock, inbuf, 8);
memcpy( keyblock, key,8);
DES_set_key( &keyblock, &schedule );
/* Encryption occurs here */
DES_ecb_encrypt(&msgblock, &outblock, &schedule, (int) encrypt);
memcpy(resbuf, outblock, 8);
}
void des3_encrypt(uint8_t* resbuf, uint8_t* inbuf, uint8_t* key, bool encrypt) {
DES_cblock keyblock1, keyblock2, keyblock3;
DES_cblock msgblock;
DES_cblock outblock;
DES_key_schedule schedule1, schedule2, schedule3;
memcpy(msgblock, inbuf, 8);
memcpy( keyblock1, key,8);
memcpy( keyblock2, key+8,8);
memcpy( keyblock2, key+16,8);
DES_set_key( &keyblock1, &schedule1 );
DES_set_key( &keyblock2, &schedule2 );
DES_set_key( &keyblock3, &schedule3 );
/* Encryption occurs here */
DES_ecb3_encrypt(&msgblock, &outblock, &schedule1, &schedule2, &schedule2, (int) encrypt);
memcpy(resbuf, outblock, 8);
}
void sha1_hash(uint8_t* resbuf, uint32_t noutbytes, uint8_t* inbuf, uint32_t ninbytes, uint8_t* hash_buf) {
SHA_CTX sha;
SHA1_Init(&sha);
SHA1_Update(&sha, inbuf, ninbytes);
SHA1_Final(hash_buf, &sha);
memcpy(resbuf, hash_buf, noutbytes);
}
void sha256_hash(uint8_t* resbuf, uint32_t noutbytes, uint8_t* inbuf, uint32_t ninbytes, uint8_t* hash_buf) {
SHA256_CTX sha;
SHA256_Init(&sha);
SHA256_Update(&sha, inbuf, ninbytes);
SHA256_Final(hash_buf, &sha);
memcpy(resbuf, hash_buf, noutbytes);
}
void sha512_hash(uint8_t* resbuf, uint32_t noutbytes, uint8_t* inbuf, uint32_t ninbytes, uint8_t* hash_buf) {
SHA512_CTX sha;
SHA512_Init(&sha);
SHA512_Update(&sha, inbuf, ninbytes);
SHA512_Final(hash_buf, &sha);
memcpy(resbuf, hash_buf, noutbytes);
}
//Read random bytes from /dev/urandom
void gen_secure_random(uint8_t* dest, uint32_t nbytes) {
int fd = open("/dev/urandom", O_RDONLY);
if (fd < 0)
{
std::cerr << "Unable to open /dev/urandom, exiting" << std::endl;
exit(0);
}
size_t bytectr = 0;
while (bytectr < nbytes) {
ssize_t result = read(fd, dest + bytectr, nbytes - bytectr);
if (result < 0) {
std::cerr << "Unable to read from /dev/urandom, exiting" << std::endl;
exit(0);
}
bytectr += static_cast(result);
}
if (close(fd) < 0)
{
std::cerr << "Unable to close /dev/urandom" << std::endl;
}
}
seclvl get_sec_lvl(uint32_t symsecbits) {
if (symsecbits == ST.symbits)
return ST;
else if (symsecbits == MT.symbits)
return MT;
else if (symsecbits == LT.symbits)
return LT;
else if (symsecbits == XLT.symbits)
return XLT;
else if (symsecbits == XXLT.symbits)
return XXLT;
else
return LT;
}