From d4e7cbce6db6c8a08ca82b12d9eeaa5a4f59fc3e Mon Sep 17 00:00:00 2001 From: Christoph Egger Date: Sun, 12 Jan 2020 21:24:49 +0100 Subject: [PATCH] implement hybrid encryption --- doc/source/index.rst | 1 + doc/source/notes.rst | 7 ++ include/DIPE.h | 7 ++ src/CMakeLists.txt | 2 +- src/DIPE.cxx | 214 +++++++++++++++++++++++++++++++++++++++++++ tests/testDIPE.cpp | 55 +++++++++++ 6 files changed, 285 insertions(+), 1 deletion(-) create mode 100644 doc/source/notes.rst diff --git a/doc/source/index.rst b/doc/source/index.rst index 6e799b7..a5ce351 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -10,6 +10,7 @@ Welcome to DIPE's documentation! :maxdepth: 2 :caption: Contents: + notes api diff --git a/doc/source/notes.rst b/doc/source/notes.rst new file mode 100644 index 0000000..7397dcb --- /dev/null +++ b/doc/source/notes.rst @@ -0,0 +1,7 @@ +Selection of Curves +=================== + +Note that the ciphertext contains elements in $G_1$ as well as $G_t$ +and therefore we need to be able to serialize them in a way that is +(computationally) indistinguishable from random. As per Shermans +comment and reference to https://ia.cr/2015/247 diff --git a/include/DIPE.h b/include/DIPE.h index 2e9c3a8..70d70b8 100644 --- a/include/DIPE.h +++ b/include/DIPE.h @@ -14,9 +14,16 @@ extern "C" { void dipe_master_keygen(dipe_param_t param, size_t dimension, dipe_master_publickey_t* pk, dipe_master_secretkey_t* sk); void dipe_keygen(dipe_param_t param, dipe_master_secretkey_t msk, char* cid, element_t* y, dipe_secretkey_t* sk); + 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); + 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); + size_t dipe_ciphertext_overhead(dipe_param_t param, size_t dimension); + void dipe_encap(dipe_param_t param, dipe_master_publickey_t mpk, element_t* x, element_t ptxt, dipe_ctxt_t* ctxt); 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 dipe_serialize_ctxt(dipe_param_t param, dipe_ctxt_t ctxt, uint8_t* buffer); + size_t dipe_deserialize_ctxt(dipe_param_t param, size_t dimension, dipe_ctxt_t* ctxt, uint8_t* buffer); + void dipe_free_param(dipe_param_t param); void dipe_free_master_secretkey(dipe_master_secretkey_t sk); void dipe_free_master_publickey(dipe_master_publickey_t pk); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ce0076e..7379e31 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -9,4 +9,4 @@ target_include_directories (DIPE PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../include/) target_link_libraries(DIPE pbc) target_link_libraries(DIPE gmp) -target_link_libraries(DIPE crypto) +target_link_libraries(DIPE nettle) diff --git a/src/DIPE.cxx b/src/DIPE.cxx index 53e4b1a..f278f50 100644 --- a/src/DIPE.cxx +++ b/src/DIPE.cxx @@ -1,5 +1,17 @@ #include "DIPE.h" + #include +#include +#include +#include +#include +#include +#include +#include +#include + +using std::min; +using std::max; struct dipe_param { pairing_t pairing; @@ -46,8 +58,108 @@ namespace { 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)); @@ -207,6 +319,108 @@ void dipe_decap(dipe_param_t param, dipe_secretkey_t sk, char* cid, element_t* y 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); diff --git a/tests/testDIPE.cpp b/tests/testDIPE.cpp index f1b6332..4219ba4 100644 --- a/tests/testDIPE.cpp +++ b/tests/testDIPE.cpp @@ -172,6 +172,61 @@ TEST(DipeTest, DipeDecapSuccess) { dipe_free_ctxt(ctxt); } +TEST(DipeTest, DipeDecryptSuccessSmall) { + char ctxt[768]; + char ptxt[768]; + size_t ptxt_len; + size_t overhead; + dipe_master_publickey_t pk; + dipe_master_secretkey_t msk; + dipe_secretkey_t sk; + + element_t y[6]; + element_t x[6]; + + for (size_t i = 0; i < 6; i+=2) { + element_init_Zr(y[i], *dipe_get_pairing(param)); + element_init_Zr(x[i], *dipe_get_pairing(param)); + element_init_Zr(y[i+1], *dipe_get_pairing(param)); + element_init_Zr(x[i+1], *dipe_get_pairing(param)); + + element_set1(y[i]); + element_set1(x[i+1]); + element_random(y[i+1]); + element_neg(x[i], y[i+1]); + } + + dipe_master_keygen(param, 6, &pk, &msk); + dipe_keygen(param, msk, "1234567890abcdef", y, &sk); + overhead = dipe_ciphertext_overhead(param, 6); + + for (size_t clen = overhead; clen < 768; ++clen) { + memset(ctxt, 0, 768); + memset(ptxt, 0, 768); + memcpy(ptxt, "test", 4); + + dipe_encrypt(param, pk, x, 4, ptxt, clen, ctxt); + ptxt_len = dipe_decrypt(param, sk, "1234567890abcdef", y, clen, ctxt, ptxt); + + if (clen >= overhead) { + ASSERT_EQ(ptxt_len, 4); + ASSERT_STREQ(ptxt, "test"); + } + else { + ASSERT_EQ(ptxt_len, 0); + } + } + + for (size_t i = 0; i < 6; ++i) { + element_clear(y[i]); + element_clear(x[i]); + } + + dipe_free_master_secretkey(msk); + dipe_free_master_publickey(pk); + dipe_free_secretkey(sk); +} + int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); -- 2.39.2