]> git.siccegge.de Git - software/DIPE.git/blobdiff - src/DIPE.cxx
implement hybrid encryption
[software/DIPE.git] / src / DIPE.cxx
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);