]> git.siccegge.de Git - software/DIPE.git/commitdiff
implement hybrid encryption
authorChristoph Egger <christoph@christoph-egger.org>
Sun, 12 Jan 2020 20:24:49 +0000 (21:24 +0100)
committerChristoph Egger <christoph@christoph-egger.org>
Sun, 12 Jan 2020 20:24:49 +0000 (21:24 +0100)
doc/source/index.rst
doc/source/notes.rst [new file with mode: 0644]
include/DIPE.h
src/CMakeLists.txt
src/DIPE.cxx
tests/testDIPE.cpp

index 6e799b7069f8892cef602915c53ef57c37cf7b30..a5ce35135c3ff0fe895e80dd89a73001e5f40430 100644 (file)
@@ -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 (file)
index 0000000..7397dcb
--- /dev/null
@@ -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
index 2e9c3a8f0d1d03fa6950a31f21dd9a99cb9d967e..70d70b82de42a0142b4412902d4851d03c23e99e 100644 (file)
@@ -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);
index ce0076e1f48d2b50e18067965b447b5695fa6940..7379e31741d5762936b5716736855e912d4b8265 100644 (file)
@@ -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)
index 53e4b1a7342845b0c187ba13cb3c29862ba67534..f278f5008857d195d565a1b0b299735b33f66daf 100644 (file)
@@ -1,5 +1,17 @@
 #include "DIPE.h"
+
 #include <string.h>
+#include <nettle/hkdf.h>
+#include <nettle/hmac.h>
+#include <sys/random.h>
+#include <nettle/gcm.h>
+#include <nettle/memops.h>
+#include <arpa/inet.h>
+#include <algorithm>
+#include <errno.h>
+
+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);
index f1b6332398d5f95b6ad7aed938424324b5959260..4219ba46ec3f182ef9c178b34478f97628b21424 100644 (file)
@@ -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);