#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;
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));
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);
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);