X-Git-Url: https://git.siccegge.de//index.cgi?a=blobdiff_plain;f=src%2FDIPE.cxx;h=f278f5008857d195d565a1b0b299735b33f66daf;hb=d4e7cbce6db6c8a08ca82b12d9eeaa5a4f59fc3e;hp=e69de29bb2d1d6434b8b29ae775ad8c2e48c5391;hpb=5cb1a82c4ddb3fd578e6b99cce46406e1a6b357f;p=software%2FDIPE.git diff --git a/src/DIPE.cxx b/src/DIPE.cxx index e69de29..f278f50 100644 --- a/src/DIPE.cxx +++ b/src/DIPE.cxx @@ -0,0 +1,464 @@ +#include "DIPE.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using std::min; +using std::max; + +struct dipe_param { + pairing_t pairing; + element_t g1; + element_t g2; + element_t gt; +}; + +struct dipe_master_publickey { + size_t dimension; + element_t a; + element_t* k; +}; + +struct dipe_master_secretkey { + size_t dimension; + element_t a; + element_t* k; +}; + +struct dipe_secretkey { + size_t dimension; + element_t d; +}; + +struct dipe_ctxt { + size_t dimension; + element_t s; + element_t* cx; + element_t c; +}; + + +namespace { + size_t dipe_h_length(element_t elem, size_t dimension) { + return 16 /*cid*/ + dimension * element_length_in_bytes(elem); + } + + void dipe_generate_h(char* cid, element_t* y, size_t dimension, unsigned char* result) { + memcpy(result, cid, 16); + unsigned char* next_element = result+16; + for (size_t i = 0; i < dimension; ++i) { + element_to_bytes(next_element, y[i]); + next_element += element_length_in_bytes(y[0]); + } + } + + void dipe_generate_aeskey(element_t encapkey, uint8_t* aeskey) { + struct hmac_sha256_ctx ctx; + uint8_t salt[SHA256_DIGEST_SIZE]; + memset(salt, 0, SHA256_DIGEST_SIZE); + size_t elen = element_length_in_bytes(encapkey); + uint8_t coded[elen]; + element_to_bytes(coded, encapkey); + + hmac_sha256_set_key(&ctx, SHA256_DIGEST_SIZE, salt); + hkdf_extract(&ctx, + (nettle_hash_update_func *)hmac_sha256_update, + (nettle_hash_digest_func *)hmac_sha256_digest, + SHA256_DIGEST_SIZE, + elen, coded, + aeskey); + } + + /* Data format is iv | enc(4 byte len | ptxt | 0 padding) | tag + */ + int dipe_aes_encrypt(uint8_t* key, size_t ptxt_len, uint8_t* ptxt, size_t ctxt_len, uint8_t* ctxt) { + struct gcm_aes128_ctx ctx; + uint8_t iv[12]; + uint8_t block[16]; + uint32_t coded_ptxtlen; + + ctxt_len -= (12 + 16); /* IV + Tag */ + if (ctxt_len < ptxt_len) return -1; + + getrandom(iv, 12, 0); + memcpy(ctxt, iv, 12); + ctxt += 12; + memset(block, 0, 16); + gcm_aes128_set_key(&ctx, key); + gcm_aes128_set_iv(&ctx, 12, iv); + + /* First Block */ + coded_ptxtlen = htonl(ptxt_len); + memcpy(block, &coded_ptxtlen, 4); + memcpy(block+4, ptxt, min((size_t)12, ptxt_len)); + ptxt_len -= min((size_t)12, ptxt_len); + ptxt += 12; + + while (ctxt_len >= 16) { + gcm_aes128_encrypt(&ctx, 16, ctxt, block); + memset(block, 0, 16); + ctxt_len -= 16; + ctxt += 16; + + if (ptxt_len > 0) { + memcpy(block, ptxt, min((size_t)16, ptxt_len)); + ptxt += 16; + ptxt_len -= min((size_t)16, ptxt_len); + } + } + + if (ctxt_len > 0) { + gcm_aes128_encrypt(&ctx, ctxt_len, ctxt, block); + ctxt += ctxt_len; + } + + gcm_aes128_digest(&ctx, 16, ctxt); + return 0; + } + + int dipe_aes_decrypt(uint8_t* key, size_t len, uint8_t* ctxt, uint8_t* ptxt) { + struct gcm_aes128_ctx ctx; + uint8_t block[16]; + + gcm_aes128_set_key(&ctx, key); + gcm_aes128_set_iv(&ctx, 12, ctxt); + ctxt += 12; len -= 12; + len -= 16; /* GCM tag */ + + + gcm_aes128_decrypt(&ctx, min((size_t)16, len), block, ctxt); + uint32_t ptxtlen = 0; + memcpy(&ptxtlen, block, 4); + memcpy(ptxt, block+4, min((size_t)12, len-4)); + ptxtlen = ntohl(ptxtlen); + ctxt += min((size_t)16, len); len -= min((size_t)16, len); + ptxt += 12; + + if (len > 0) { + gcm_aes128_decrypt(&ctx, len, ptxt, ctxt); + ctxt += len; + } + gcm_aes128_digest(&ctx, 16, block); + + /* error case is with return code 0, see manual + * https://www.lysator.liu.se/~nisse/nettle/nettle.html#Miscellaneous-functions + */ + if (memeql_sec(ctxt, block, 16) == 0) + return 0; + else + return ptxtlen; + } +} + +/* Note: we need a curve where membership checking is hard for all + * group elements. See also https://ia.cr/2015/247 and followups + */ +void dipe_init(FILE* configfp, dipe_param_t* param) { + char buffer[2<<16]; + (*param) = (dipe_param_t)malloc(sizeof(dipe_param)); + fread(buffer, 1, 2<<16, configfp); + + pairing_init_set_buf((*param)->pairing, buffer, 2<<16); + + element_init_G1((*param)->g1, (*param)->pairing); + element_init_G2((*param)->g2, (*param)->pairing); + element_init_GT((*param)->gt, (*param)->pairing); + + element_random((*param)->g1); + element_random((*param)->g2); + pairing_apply((*param)->gt, (*param)->g1, (*param)->g2, (*param)->pairing); +} + +pairing_t* dipe_get_pairing(dipe_param_t param) { + return &(param->pairing); +} + +void dipe_master_keygen(dipe_param_t param, size_t dimension, dipe_master_publickey_t* pk, dipe_master_secretkey_t* sk) { + *pk = (dipe_master_publickey_t)malloc(sizeof(dipe_master_publickey)); + *sk = (dipe_master_secretkey_t)malloc(sizeof(dipe_master_secretkey)); + + (*sk)->dimension = (*pk)->dimension = dimension; + + element_init_Zr((*sk)->a, param->pairing); + element_init_GT((*pk)->a, param->pairing); + element_random((*sk)->a); + element_pow_zn((*pk)->a, param->gt, (*sk)->a); + + (*sk)->k = (element_t*)calloc(dimension, sizeof(element_t)); + (*pk)->k = (element_t*)calloc(dimension, sizeof(element_t)); + for (size_t i = 0; i < dimension; ++i) { + element_init_Zr((*sk)->k[i], param->pairing); + element_init_G1((*pk)->k[i], param->pairing); + element_random((*sk)->k[i]); + element_pow_zn((*pk)->k[i], param->g1, (*sk)->k[i]); + } +} + +void dipe_keygen(dipe_param_t param, dipe_master_secretkey_t msk, char* cid, element_t* y, dipe_secretkey_t* sk) { + *sk = (dipe_secretkey_t)malloc(sizeof(dipe_secretkey)); + (*sk)->dimension = msk->dimension; + element_init_G2((*sk)->d, param->pairing); + + size_t hash_input_length = dipe_h_length(y[0], msk->dimension); + unsigned char hash_input[hash_input_length]; + element_t h; + element_init_G2(h, param->pairing); + + dipe_generate_h(cid, y, msk->dimension, hash_input); + element_from_hash(h, hash_input, hash_input_length); + + element_t innerp; + element_t tmp; + + element_init_Zr(innerp, param->pairing); + element_init_Zr(tmp, param->pairing); + element_set0(innerp); + + element_from_hash(h, hash_input, hash_input_length); + + for (size_t i = 0; i < msk->dimension; ++i) { + element_mul(tmp, y[i], msk->k[i]); + element_add(innerp, innerp, tmp); + } + + element_t a2; + element_init_G2(a2, param->pairing); + element_pow_zn(h, h, innerp); + element_pow_zn(a2, param->g2, msk->a); + element_mul((*sk)->d, a2, h); + + element_clear(h); + element_clear(innerp); + element_clear(tmp); + element_clear(a2); +} + +void dipe_encap(dipe_param_t param, dipe_master_publickey_t mpk, element_t* x, element_t ptxt, dipe_ctxt_t* ctxt) { + *ctxt = (dipe_ctxt_t)malloc(sizeof(dipe_ctxt)); + (*ctxt)->dimension = mpk->dimension; + + element_t r; + element_t s; + element_init_Zr(r, param->pairing); + element_init_Zr(s, param->pairing); + + element_random(r); + element_random(s); + + element_init_G1((*ctxt)->s, param->pairing); + element_pow_zn((*ctxt)->s, param->g1, s); + + element_t k1; + element_t x1; + element_init_G1(k1, param->pairing); + element_init_G1(x1, param->pairing); + + (*ctxt)->cx = (element_t*)calloc(mpk->dimension, sizeof(element_t)); + for (size_t i = 0; i < mpk->dimension; ++i){ + element_pow_zn(x1, param->g1, x[i]); + element_pow_zn(x1, x1, r); + + element_pow_zn(k1, mpk->k[i], s); + + element_init_G1((*ctxt)->cx[i], param->pairing); + element_mul((*ctxt)->cx[i], k1, x1); + } + + element_t at; + element_init_GT(at, param->pairing); + element_pow_zn(at, mpk->a, s); + element_init_GT((*ctxt)->c, param->pairing); + element_mul((*ctxt)->c, at, ptxt); + + element_clear(r); + element_clear(s); + element_clear(k1); + element_clear(x1); + element_clear(at); +} + + +void dipe_decap(dipe_param_t param, dipe_secretkey_t sk, char* cid, element_t* y, dipe_ctxt_t ctxt, element_t ptxt) { + size_t hash_input_length = dipe_h_length(y[0], sk->dimension); + unsigned char hash_input[hash_input_length]; + element_t h; + element_init_G2(h, param->pairing); + + dipe_generate_h(cid, y, sk->dimension, hash_input); + element_from_hash(h, hash_input, hash_input_length); + + element_t tmp; + element_t innerp; + element_t hy; + element_init_GT(tmp, param->pairing); + element_init_GT(innerp, param->pairing); + element_init_G2(hy, param->pairing); + + element_set1(innerp); + for (size_t i = 0; i < sk->dimension; ++i) { + element_pow_zn(hy, h, y[i]); + pairing_apply(tmp, ctxt->cx[i], hy, param->pairing); + element_mul(innerp, innerp, tmp); + } + + pairing_apply(ptxt, ctxt->s, sk->d, param->pairing); + element_invert(ptxt, ptxt); + element_mul(ptxt, ptxt, ctxt->c); + element_mul(ptxt, ptxt, innerp); + + element_clear(h); + element_clear(tmp); + element_clear(innerp); + element_clear(hy); +} + +void dipe_encrypt(dipe_param_t param, dipe_master_publickey_t mpk, element_t* x, size_t ptxt_len, char* ptxt, size_t ctxt_len, char* ctxt) { + element_t key; + uint8_t aes[32]; + dipe_ctxt_t cap; + size_t cap_len; + + element_init_GT(key, param->pairing); + element_random(key); + dipe_generate_aeskey(key, aes); + dipe_encap(param, mpk, x, key, &cap); + cap_len = dipe_serialize_ctxt(param, cap, (uint8_t*)ctxt); + ctxt += cap_len; ctxt_len -= cap_len; + + dipe_aes_encrypt(aes, ptxt_len, (uint8_t*)ptxt, ctxt_len, (uint8_t*)ctxt); + + dipe_free_ctxt(cap); + element_clear(key); +} + +size_t dipe_decrypt(dipe_param_t param, dipe_secretkey_t sk, char* cid, element_t* y, size_t ctxt_len, char* ctxt, char* ptxt) { + dipe_ctxt_t cap; + uint8_t aes[32]; + element_t key; + size_t cap_len; + + element_init_GT(key, param->pairing); + cap_len = dipe_deserialize_ctxt(param, sk->dimension, &cap, (uint8_t*)ctxt); + ctxt += cap_len; + ctxt_len -= cap_len; + dipe_decap(param, sk, cid, y, cap, key); + dipe_generate_aeskey(key, aes); + + dipe_free_ctxt(cap); + element_clear(key); + + return dipe_aes_decrypt(aes, ctxt_len, (uint8_t*)ctxt, (uint8_t*)ptxt); +} + +/* Note: we're generating random-looking bytes here. Therefore we + * can't encode the dimension of the predicate vector (supposed to be + * set as system parameter) or information about the secret sharing + * (needs to be retrieved by some sort of trial decryption). + */ +size_t dipe_serialize_ctxt(__attribute__((unused)) dipe_param_t param, dipe_ctxt_t ctxt, uint8_t* buffer) { + size_t bytes_written = 0; + element_to_bytes_compressed(buffer, ctxt->s); + buffer += element_length_in_bytes_compressed(ctxt->s); + bytes_written += element_length_in_bytes_compressed(ctxt->s); + + for (size_t i = 0; i < ctxt->dimension; ++i) { + element_to_bytes_compressed(buffer, ctxt->cx[i]); + buffer += element_length_in_bytes_compressed(ctxt->cx[i]); + bytes_written += element_length_in_bytes_compressed(ctxt->cx[i]); + } + + element_to_bytes(buffer, ctxt->c); + buffer += element_length_in_bytes(ctxt->c); + bytes_written += element_length_in_bytes(ctxt->c); + + return bytes_written; +} + +size_t dipe_deserialize_ctxt(dipe_param_t param, size_t dimension, dipe_ctxt_t* ctxt, uint8_t* buffer) { + size_t bytes_read = 0; + *ctxt = (dipe_ctxt_t)malloc(sizeof(dipe_ctxt)); + (*ctxt)->dimension = dimension; + + element_init_G1((*ctxt)->s, param->pairing); + element_from_bytes_compressed((*ctxt)->s, buffer); + buffer += element_length_in_bytes_compressed((*ctxt)->s); + bytes_read += element_length_in_bytes_compressed((*ctxt)->s); + + (*ctxt)->cx = (element_t*)calloc(dimension, sizeof(element_t)); + for (size_t i = 0; i < dimension; ++i) { + element_init_G1((*ctxt)->cx[i], param->pairing); + element_from_bytes_compressed((*ctxt)->cx[i], buffer); + buffer += element_length_in_bytes_compressed((*ctxt)->cx[i]); + bytes_read += element_length_in_bytes_compressed((*ctxt)->cx[i]); + } + + element_init_GT((*ctxt)->c, param->pairing); + element_from_bytes((*ctxt)->c, buffer); + buffer += element_length_in_bytes((*ctxt)->c); + bytes_read += element_length_in_bytes((*ctxt)->c); + + return bytes_read; +} + +size_t dipe_ciphertext_overhead(dipe_param_t param, size_t dimension) { + size_t overhead = 12 + 16 + 4 /* IV + Tag + Size */; + element_t t; + + element_init_G1(t, param->pairing); + overhead += element_length_in_bytes_compressed(t); + overhead += dimension * element_length_in_bytes_compressed(t); + element_clear(t); + element_init_GT(t, param->pairing); + overhead += element_length_in_bytes(t); + element_clear(t); + + return overhead; +} + +void dipe_free_param(dipe_param_t param) { + element_clear(param->g1); + element_clear(param->g2); + element_clear(param->gt); + pairing_clear(param->pairing); + free(param); +} + +void dipe_free_master_secretkey(dipe_master_secretkey_t sk) { + for (size_t i = 0; i < sk->dimension; ++i) { + element_clear(sk->k[i]); + } + element_clear(sk->a); + free(sk->k); + free(sk); +} + +void dipe_free_master_publickey(dipe_master_publickey_t pk) { + for (size_t i = 0; i < pk->dimension; ++i) { + element_clear(pk->k[i]); + } + element_clear(pk->a); + free(pk->k); + free(pk); +} + +void dipe_free_secretkey(dipe_secretkey_t sk) { + element_clear(sk->d); + free(sk); +} + +void dipe_free_ctxt(dipe_ctxt_t ctxt) { + for (size_t i = 0; i < ctxt->dimension; ++i) { + element_clear(ctxt->cx[i]); + } + element_clear(ctxt->c); + element_clear(ctxt->s); + free(ctxt->cx); + free(ctxt); +}