Browse Source

initial commit

Simon 2 years ago
commit
74661cc556

+ 1 - 0
README.md

@@ -0,0 +1 @@
+# 2PPS

+ 1263 - 0
c/dpf.c

@@ -0,0 +1,1263 @@
+#include "dpf.h"
+#include <openssl/rand.h>
+#include <omp.h>
+#include <time.h>
+
+
+
+uint128_t dpf_reverse_lsb(uint128_t input){
+    uint128_t xor = 1;
+	return input ^ xor;
+}
+
+uint128_t dpf_lsb(uint128_t input){
+    return input & 1;
+}
+
+uint128_t dpf_set_lsb_zero(uint128_t input){
+    int lsb = input & 1;
+
+	if(lsb == 1){
+		return dpf_reverse_lsb(input);	
+	}else{
+		return input;
+	}
+}
+
+void _output_bit_to_bit(uint128_t input){
+    for(int i = 0; i < 64; i++)
+    {
+        if( (1ll << i) & input)
+            printf("1");
+	else
+	    printf("0");
+    }
+}
+
+void print_block(uint128_t input) {
+    uint64_t *val = (uint64_t *) &input;
+
+	//printf("%016lx%016lx\n", val[0], val[1]);
+	_output_bit_to_bit(val[0]);
+	_output_bit_to_bit(val[1]);
+	printf("\n");
+}
+
+uint128_t addModP(uint128_t in1, uint128_t in2){
+    uint128_t out = in1 + in2;
+    //if we wrapped around, add in the MODP
+    if(out + MODP < in1 || out > 0-MODP){
+        out += MODP;
+        //printf("addModP wrapped\n");
+    }
+    return out;
+}
+
+uint128_t subModP(uint128_t in1, uint128_t in2){
+    uint128_t out = in1 - in2;
+    //if we wrapped around, subtract the MODP
+    if(in2 > in1){
+        out -= MODP;
+        //printf("subModP wrapped\n");
+
+    }
+    //straight-line version?
+    //out -= (in2 > in1) * MODP;
+    return out;
+}
+
+//performance for this is really bad
+//but it gets _much_ better when compiled with -O2
+uint128_t multModP(uint128_t in1, uint128_t in2){
+    uint128_t out = 0;
+    uint128_t in1high = in1 >> 64;
+    uint128_t in2high = in2 >> 64;
+    uint128_t in1low = in1 & (uint128_t)0xffffffffffffffff;
+    uint128_t in2low = in2 & (uint128_t)0xffffffffffffffff;
+    
+    //printf("\n");
+    //print_block(in1high); printf("\n");
+    //print_block(in2high); printf("\n");
+    //print_block(in1low); printf("\n");
+    //print_block(in2low); printf("\n");
+    //printf("\n");
+
+    uint128_t outlow = in1low * in2low;
+    if(outlow + MODP < in1low || outlow + MODP < in2low|| outlow > 0-MODP) {
+        outlow += MODP; 
+        //printf("mult wrap\n");
+    }
+    
+    //print_block(outlow);
+        
+    uint128_t outhigh = in1high * in2high;
+    uint128_t outmid1 = in1high*in2low;
+    uint128_t outmid2 = in2high*in1low;
+    
+    //print_block(outhigh);
+    //print_block(outmid1);
+    //print_block(outmid2);
+        
+    //the low part gets the low order bits of the mids
+    uint128_t outlow1 = addModP(outmid1 << 64, outmid2 << 64);
+    
+    //uint128_t outlow1 = ((outmid1 & (uint128_t)0xffffffffffffffff) +  (outmid2 & (uint128_t)0xffffffffffffffff)) << 64;
+    //if(outlow1 + MODP < ((outmid1 & (uint128_t)0xffffffffffffffff) << 64)) outlow1 += MODP;
+        
+    //print_block(outlow1);
+    
+    out = addModP(outlow, outlow1);
+
+    //print_block(out);
+    
+    //multiply in the wrap for as many times as we wrapped
+    uint128_t lowWraps = ((outmid1 >> 64) + (outmid2 >> 64)) * MODP;
+    out = addModP(out, lowWraps);
+    
+    //print_block(lowWraps);
+    
+    //now we need to account for wraps caused by outhigh
+    uint128_t outhighlow = (outhigh & (uint128_t)0xffffffffffffffff) * MODP;
+    uint128_t outhighhigh = (outhigh >> 64) * MODP;
+    uint128_t outhighhighlow = outhighhigh << 64;
+    uint128_t outhighhighhigh = (outhighhigh >> 64) * MODP;
+    
+    //print_block(outhighlow);
+    //print_block(outhighhigh);
+    //print_block(outhighhighlow);
+    //print_block(outhighhighhigh);
+    
+    out = addModP(out, addModP(outhighlow, addModP(outhighhighlow, outhighhighhigh)));
+    
+    //print_block(out);
+
+    return out;
+}
+
+
+uint128_t getRandomBlock(){
+    static uint8_t* randKey = NULL;//(uint8_t*) malloc(16);
+    static EVP_CIPHER_CTX* randCtx;
+    static uint128_t counter = 0;
+    
+    int len = 0;
+    uint128_t output = 0;
+    if(!randKey){
+        randKey = (uint8_t*) malloc(16);
+        if(!(randCtx = EVP_CIPHER_CTX_new())) 
+            printf("errors occured in creating context\n");
+        if(!RAND_bytes(randKey, 16)){
+            printf("failed to seed randomness\n");
+        }
+        if(1 != EVP_EncryptInit_ex(randCtx, EVP_aes_128_ecb(), NULL, randKey, NULL))
+            printf("errors occured in randomness init\n");
+        EVP_CIPHER_CTX_set_padding(randCtx, 0);
+    }
+    
+    if(1 != EVP_EncryptUpdate(randCtx, (uint8_t*)&output, &len, (uint8_t*)&counter, 16))
+        printf("errors occured in generating randomness\n");
+    counter++;
+    return output;
+}
+
+//this is the PRG used for the DPF 
+void dpfPRG(EVP_CIPHER_CTX *ctx, uint128_t input, uint128_t* output1, uint128_t* output2, int* bit1, int* bit2){
+    
+	input = dpf_set_lsb_zero(input);
+
+    int len = 0;
+	uint128_t stashin[2];
+	stashin[0] = input;
+	stashin[1] = dpf_reverse_lsb(input);
+	uint128_t stash[2];
+
+    EVP_CIPHER_CTX_set_padding(ctx, 0);
+    if(1 != EVP_EncryptUpdate(ctx, (uint8_t*)stash, &len, (uint8_t*)stashin, 32))
+        printf("errors occured in encrypt\n");
+    //no need to do this since we're working with exact multiples of the block size
+    //if(1 != EVP_EncryptFinal_ex(ctx, stash + len, &len)) 
+    //    printf("errors occured in final\n");
+    
+	stash[0] = stash[0] ^ input;
+	stash[1] = stash[1] ^ input;
+	stash[1] = dpf_reverse_lsb(stash[1]);
+
+	*bit1 = dpf_lsb(stash[0]);
+	*bit2 = dpf_lsb(stash[1]);
+
+	*output1 = dpf_set_lsb_zero(stash[0]);
+	*output2 = dpf_set_lsb_zero(stash[1]);
+}
+
+static int getbit(uint128_t x, int n, int b){
+	return ((uint128_t)(x) >> (n - b)) & 1;
+}
+
+void genDPF(EVP_CIPHER_CTX *ctx, int domainSize, uint128_t index, int dataSize, uint8_t* data, unsigned char** k0, unsigned char **k1){
+    int maxLayer = domainSize;
+    
+    uint128_t s[maxLayer + 1][2];
+	int t[maxLayer + 1 ][2];
+	uint128_t sCW[maxLayer];
+	int tCW[maxLayer][2];
+    
+    s[0][0] = getRandomBlock();
+	s[0][1] = getRandomBlock();
+	t[0][0] = 0;
+	t[0][1] = 1;
+    
+    uint128_t s0[2], s1[2]; // 0=L,1=R
+    int t0[2], t1[2];
+	#define LEFT 0
+	#define RIGHT 1
+	for(int i = 1; i <= maxLayer; i++){
+        dpfPRG(ctx, s[i-1][0], &s0[LEFT], &s0[RIGHT], &t0[LEFT], &t0[RIGHT]);
+		dpfPRG(ctx, s[i-1][1], &s1[LEFT], &s1[RIGHT], &t1[LEFT], &t1[RIGHT]);
+        
+        int keep, lose;
+		int indexBit = getbit(index, domainSize, i);
+        if(indexBit == 0){
+			keep = LEFT;
+			lose = RIGHT;
+		}else{
+			keep = RIGHT;
+			lose = LEFT;
+		}
+            
+		
+        sCW[i-1] = s0[lose] ^ s1[lose];
+
+		tCW[i-1][LEFT] = t0[LEFT] ^ t1[LEFT] ^ indexBit ^ 1;
+		tCW[i-1][RIGHT] = t0[RIGHT] ^ t1[RIGHT] ^ indexBit;
+        
+		if(t[i-1][0] == 1){
+			s[i][0] = s0[keep] ^ sCW[i-1];
+			t[i][0] = t0[keep] ^ tCW[i-1][keep];
+		}else{
+			s[i][0] = s0[keep];
+			t[i][0] = t0[keep];
+		}
+
+		if(t[i-1][1] == 1){
+			s[i][1] = s1[keep] ^ sCW[i-1];
+			t[i][1] = t1[keep] ^ tCW[i-1][keep];
+		}else{
+			s[i][1] = s1[keep];
+			t[i][1] = t1[keep];
+		}
+		
+    }
+    
+	unsigned char *buff0;
+	unsigned char *buff1;
+	buff0 = (unsigned char*) malloc(1 + 16 + 1 + 18 * maxLayer + dataSize);
+	buff1 = (unsigned char*) malloc(1 + 16 + 1 + 18 * maxLayer + dataSize);
+    
+    //take data, xor it with dataSize bits generated from s_0^n and another dataSize bits generated from s_1^n
+    //use a counter mode encryption of 0 with each seed as key to get prg output
+    uint8_t *lastCW = (uint8_t*) malloc(dataSize);
+    uint8_t *convert0 = (uint8_t*) malloc(dataSize+16);
+    uint8_t *convert1 = (uint8_t*) malloc(dataSize+16);
+    uint8_t *zeros = (uint8_t*) malloc(dataSize+16);
+    memset(zeros, 0, dataSize+16);
+    
+    memcpy(lastCW, data, dataSize);
+    
+    int len = 0;
+    //generate dataSize length prg outputs from the seeds
+
+    EVP_CIPHER_CTX *seedCtx0;
+    EVP_CIPHER_CTX *seedCtx1;
+
+    if(!(seedCtx0 = EVP_CIPHER_CTX_new())) 
+        printf("errors occured in creating context\n");
+    if(!(seedCtx1 = EVP_CIPHER_CTX_new())) 
+        printf("errors occured in creating context\n");
+    if(1 != EVP_EncryptInit_ex(seedCtx0, EVP_aes_128_ctr(), NULL, (uint8_t*)&s[maxLayer][0], NULL))
+        printf("errors occured in init of dpf gen\n");
+    if(1 != EVP_EncryptInit_ex(seedCtx1, EVP_aes_128_ctr(), NULL, (uint8_t*)&s[maxLayer][1], NULL))
+        printf("errors occured in init of dpf gen\n");
+    if(1 != EVP_EncryptUpdate(seedCtx0, convert0, &len, zeros, dataSize))
+        printf("errors occured in encrypt\n"); 
+    if(1 != EVP_EncryptUpdate(seedCtx1, convert1, &len, zeros, dataSize))
+        printf("errors occured in encrypt\n"); 
+
+    for(int i = 0; i < dataSize; i++){
+        lastCW[i] = lastCW[i] ^ ((uint8_t*)convert0)[i] ^ ((uint8_t*)convert1)[i];
+    }
+
+	if(buff0 == NULL || buff1 == NULL){
+		printf("Memory allocation failed\n");
+		exit(1);
+	}
+
+	buff0[0] = domainSize;
+	memcpy(&buff0[1], &s[0][0], 16);
+	buff0[17] = t[0][0];
+	for(int i = 1; i <= maxLayer; i++){
+		memcpy(&buff0[18 * i], &sCW[i-1], 16);
+		buff0[18 * i + 16] = tCW[i-1][0];
+		buff0[18 * i + 17] = tCW[i-1][1]; 
+	}
+	memcpy(&buff0[18 * maxLayer + 18], lastCW, dataSize);
+
+	buff1[0] = domainSize;
+	memcpy(&buff1[18], &buff0[18], 18 * (maxLayer));
+	memcpy(&buff1[1], &s[0][1], 16);
+	buff1[17] = t[0][1];
+	memcpy(&buff1[18 * maxLayer + 18], lastCW, dataSize);
+
+	*k0 = buff0;
+	*k1 = buff1;
+    
+    free(lastCW);
+    free(convert0);
+    free(convert1);
+    free(zeros);
+    EVP_CIPHER_CTX_free(seedCtx0);
+    EVP_CIPHER_CTX_free(seedCtx1);
+}
+
+uint128_t evalDPF(EVP_CIPHER_CTX *ctx, unsigned char* k, uint128_t x, int dataSize, uint8_t* dataShare){
+   //NOTE: if dataSize is not a multiple of 16, the size of dataShare should be
+   //the next multiple of 16 after dataSize or else there is a memory bug.
+   //Thanks to Emma Dauterman for pointing this out.
+
+    //dataShare is of size dataSize
+    
+ 	int n = k[0];
+	int maxLayer = n;
+
+	uint128_t s[maxLayer + 1];
+	int t[maxLayer + 1];
+	uint128_t sCW[maxLayer];
+	int tCW[maxLayer][2];
+
+	memcpy(&s[0], &k[1], 16);
+	t[0] = k[17];
+
+	for(int i = 1; i <= maxLayer; i++){
+		memcpy(&sCW[i-1], &k[18 * i], 16);
+		tCW[i-1][0] = k[18 * i + 16];
+		tCW[i-1][1] = k[18 * i + 17];
+	}
+
+	uint128_t sL, sR;
+	int tL, tR;
+	for(int i = 1; i <= maxLayer; i++){
+		dpfPRG(ctx, s[i - 1], &sL, &sR, &tL, &tR); 
+
+		if(t[i-1] == 1){
+			sL = sL ^ sCW[i-1];
+			sR = sR ^ sCW[i-1];
+			tL = tL ^ tCW[i-1][0];
+			tR = tR ^ tCW[i-1][1];	
+		}
+
+		int xbit = getbit(x, n, i);
+		if(xbit == 0){
+			s[i] = sL;
+			t[i] = tL;
+		}else{
+			s[i] = sR;
+			t[i] = tR;
+		}
+	}
+    
+    //get the data share out
+    int len = 0;
+    uint8_t *zeros = (uint8_t*) malloc(dataSize+16);
+    memset(zeros, 0, dataSize+16);
+    //use a counter mode encryption of 0 with each seed as key to get prg output
+    //printf("here\n");
+    
+    EVP_CIPHER_CTX *seedCtx;
+    if(!(seedCtx = EVP_CIPHER_CTX_new())) 
+        printf("errors occured in creating context\n");
+    if(1 != EVP_EncryptInit_ex(seedCtx, EVP_aes_128_ctr(), NULL, (uint8_t*)&s[maxLayer], NULL))
+        printf("errors occured in init of dpf eval\n");
+    if(1 != EVP_EncryptUpdate(seedCtx, dataShare, &len, zeros, ((dataSize-1)|15)+1))
+        printf("errors occured in encrypt\n");
+    
+    for(int i = 0; i < dataSize; i++){
+        if(t[maxLayer] == 1){
+            //xor in correction word
+            dataShare[i] = dataShare[i] ^ k[18*n+18+i];
+            
+            //printf("xoring stuff in at index %d\n", i);
+        }
+                //printf("%x\n", (*dataShare)[i]);
+
+    }    
+    
+    free(zeros);
+    EVP_CIPHER_CTX_free(seedCtx);
+    
+    //print_block(s[maxLayer]);
+    //printf("%x\n", t[maxLayer]);
+
+    //use the last seed for dpf checking
+	return s[maxLayer];    
+}
+
+//use the correllated randomness so that servers and user pick same randomness
+//this is the PRF for dpf checking
+void PRF(EVP_CIPHER_CTX *ctx, uint128_t seed, int layer, int count, uint128_t* output){
+    int len = 0;
+    uint128_t input = seed;
+    int tries = 0;
+    
+    //xor count with 32 bits of input and layer with next 32 bits. 
+    //count = -1 is for determining whether to swap or not when shuffling halves
+    // if output mod 2 == 1, then user/servers will swap
+    int* temp; 
+    temp = (int*)&input;
+    temp[0] = temp[0] ^ count;
+    temp[1] = temp[1] ^ layer;
+    uint128_t stash = 0;
+    
+    do{
+        temp[2] = temp[2] ^ tries;
+        uint128_t stashin = input;
+
+        if(1 != EVP_EncryptUpdate(ctx, (uint8_t*)&stash, &len, (uint8_t*)&stashin, 16))
+            printf("errors occured in encrypt\n");
+
+        stash = stash ^ input;
+        tries++;
+        
+        //drop blocks that are not in Z_p when count is not -1
+    } while(count != -1 && stash + MODP < stash);
+
+	*output = stash;
+}
+
+void clientGenProof(EVP_CIPHER_CTX *ctx, uint128_t seed, int index, uint128_t aShare, uint128_t bShare, uint8_t* outputsAIn, uint8_t* outputsBIn){
+
+    uint128_t *outputsA = (uint128_t*) outputsAIn;
+    uint128_t *outputsB = (uint128_t*) outputsBIn;
+
+	//outputs will hold the following items in the following order
+	//this comes out to 10 values
+	//outputs for server A of the first multiplication proof (cc)	
+	//f0amult1 (index) 0
+	//g0amult1 1
+	//h0amult1 2
+	//h1amult1 3
+	//h2amult1 4
+	//outputs for server A of the second multiplication proof (mC)
+	//f0amult2 5
+	//g0amult2 6
+	//h0amult2 7
+	//h1amult2 8
+	//h2amult2 9
+	//all the same things needed for server B as well
+
+	//client generates a bunch of randomness and assigns most of the outputs 
+	//whp these are all in the field we're using
+	for(int i = 0; i < 10; i++){
+		outputsB[i] = getRandomBlock();	
+	}
+	outputsA[0] = getRandomBlock(); 
+	outputsA[1] = getRandomBlock();
+	outputsA[5] = getRandomBlock();
+	outputsA[6] = getRandomBlock();
+
+	//find f0,g0 based on randomness generated above
+	uint128_t f0mult1 = subModP(outputsA[0], outputsB[0]);
+	uint128_t g0mult1 = subModP(outputsA[1], outputsB[1]);
+	uint128_t f0mult2 = subModP(outputsA[5], outputsB[5]);
+	uint128_t g0mult2 = subModP(outputsA[6], outputsB[6]);
+
+	//find f1,g1 (shortcut since client knows non-zero index)
+	uint128_t rvalue;
+	PRF(ctx, seed, 0, index, &rvalue);
+	uint128_t f1mult1 = multModP(subModP(aShare, bShare), rvalue);
+	uint128_t g1mult1 = f1mult1; 
+	uint128_t f1mult2 = subModP(aShare, bShare); 
+	uint128_t g1mult2 = f1mult1;
+
+	//printf("value for m on client side"); print_block(f1mult2);
+	//printf("value for c on client side");print_block(f1mult1);
+
+	//calculate h0,h1 values by multiplying
+	uint128_t h0mult1 = multModP(f0mult1,g0mult1);
+	uint128_t h0mult2 = multModP(f0mult2,g0mult2);
+	uint128_t h1mult1 = multModP(f1mult1,g1mult1);
+	uint128_t h1mult2 = multModP(f1mult2,g1mult2);
+
+	//put shares of h0,h1 in output
+	outputsA[2] = addModP(h0mult1, outputsB[2]);
+	outputsA[7] = addModP(h0mult2, outputsB[7]);
+	outputsA[3] = addModP(h1mult1, outputsB[3]);
+	outputsA[8] = addModP(h1mult2, outputsB[8]);
+
+	//find f2,g2
+	uint128_t two = 2;
+	uint128_t f2mult1 = evalLinearR(two, f0mult1, f1mult1);
+	uint128_t g2mult1 = evalLinearR(two, g0mult1, g1mult1);
+	uint128_t f2mult2 = evalLinearR(two, f0mult2, f1mult2);
+	uint128_t g2mult2 = evalLinearR(two, g0mult2, g1mult2);
+
+	//calculate h2 values by multiplying
+	uint128_t h2mult1 = multModP(f2mult1,g2mult1);
+	uint128_t h2mult2 = multModP(f2mult2,g2mult2);
+
+	//put shares of h2 in output
+	outputsA[4] = addModP(h2mult1, outputsB[4]);
+	outputsA[9] = addModP(h2mult2, outputsB[9]);
+
+	/*
+	//for testing, trying to produce output of proof
+
+	//compute f(r), g(r)
+	uint128_t frmult1 = evalLinearR(10, f0mult1, f1mult1);
+	uint128_t grmult1 = evalLinearR(10, g0mult1, g1mult1);
+	uint128_t frmult2 = evalLinearR(10, f0mult2, f1mult2);
+	uint128_t grmult2 = evalLinearR(10, g0mult2, g1mult2);
+
+	//compute h(r)
+	uint128_t hrmult1 = evalQuadraticR(10, h0mult1, h1mult1, h2mult1);
+	uint128_t hrmult2 = evalQuadraticR(10, h0mult2, h1mult2, h2mult2);
+	
+	printf("values: ");
+	printf("\nf1: ");print_block(frmult1);
+	printf("\ng1: ");print_block(grmult1);
+	printf("\nh1: ");print_block(hrmult1);
+	printf("\nf2: ");print_block(frmult2);
+	printf("\ng2: ");print_block(grmult2);
+	printf("\nh2: ");print_block(hrmult2);
+	*/
+}
+
+void serverSetupProof(EVP_CIPHER_CTX *ctx, uint8_t *seedIn, int dbSize, uint8_t* vectorsIn, uint8_t* mIn, uint8_t* cIn){
+
+	uint128_t* m = (uint128_t*) mIn;
+	uint128_t* c = (uint128_t*) cIn;
+	uint128_t* vectors = (uint128_t*) vectorsIn;
+	uint128_t seed;
+	memcpy(&seed, seedIn, 16);
+
+	uint128_t prfOutput;
+	uint128_t workingM = 0;
+	uint128_t workingC = 0;
+
+	for(int i = 0; i < dbSize; i++){
+       		PRF(ctx, seed, 0, i, &prfOutput);    
+		workingM = addModP(workingM, vectors[i]);
+		workingC = addModP(workingC, multModP(vectors[i], prfOutput));
+	}
+
+	memcpy(m, &workingM, 16);
+	memcpy(c, &workingC, 16);
+}
+
+void serverComputeQuery(EVP_CIPHER_CTX *ctx, uint8_t *seedIn, uint8_t* mIn, uint8_t* cIn, uint8_t* proofIn, uint8_t* ansIn){
+	uint128_t* m = (uint128_t*) mIn;
+	uint128_t* c = (uint128_t*) cIn;
+	uint128_t* proof = (uint128_t*) proofIn;
+	uint128_t* ans = (uint128_t*) ansIn;
+	uint128_t seed;
+	memcpy(&seed, seedIn, 16);
+
+	//including this just for readability
+	uint128_t f0mult1 = proof[0];
+	uint128_t g0mult1 = proof[1];
+	uint128_t h0mult1 = proof[2];
+	uint128_t h1mult1 = proof[3];
+	uint128_t h2mult1 = proof[4];
+	uint128_t f0mult2 = proof[5];
+	uint128_t g0mult2 = proof[6];
+	uint128_t h0mult2 = proof[7];
+	uint128_t h1mult2 = proof[8];
+	uint128_t h2mult2 = proof[9];
+
+	//generate a random point r from seed
+	uint128_t r1, r2; 
+       	PRF(ctx, seed, 1, 0, &r1);
+       	PRF(ctx, seed, 2, 0, &r2);
+
+	//for testing
+	//r1 = 10;
+	//r2 = 10;
+	
+	//compute f(r), g(r)
+	uint128_t frmult1 = evalLinearR(r1, f0mult1, *c);
+	uint128_t grmult1 = evalLinearR(r1, g0mult1, *c);
+	uint128_t frmult2 = evalLinearR(r2, f0mult2, *m);
+	uint128_t grmult2 = evalLinearR(r2, g0mult2, *c);
+
+	//compute h(r)
+	uint128_t hrmult1 = evalQuadraticR(r1, h0mult1, h1mult1, h2mult1);
+	uint128_t hrmult2 = evalQuadraticR(r2, h0mult2, h1mult2, h2mult2);
+
+	//set the appropriate elements of ans (6 elements)
+	ans[0] = frmult1;
+	ans[1] = grmult1;
+	ans[2] = hrmult1;
+	ans[3] = frmult2;
+	ans[4] = grmult2;
+	ans[5] = hrmult2;
+}
+
+int serverVerifyProof(uint8_t* ans1In, uint8_t* ans2In){
+	uint128_t* ans1 = (uint128_t*) ans1In;
+	uint128_t* ans2 = (uint128_t*) ans2In;
+	
+	uint128_t f1 = subModP(ans1[0], ans2[0]);
+	uint128_t g1 = subModP(ans1[1], ans2[1]);
+	uint128_t h1 = subModP(ans1[2], ans2[2]);
+	uint128_t f2 = subModP(ans1[3], ans2[3]);
+	uint128_t g2 = subModP(ans1[4], ans2[4]);
+	uint128_t h2 = subModP(ans1[5], ans2[5]);
+
+	uint128_t prod1 = multModP(f1, g1);
+	uint128_t prod2 = multModP(f2, g2);
+	
+	/*
+	printf("recovered values: ");
+	printf("\nf1: ");print_block(f1);
+	printf("\ng1: ");print_block(g1);
+	printf("\nh1: ");print_block(h1);
+	printf("\nf2: ");print_block(f2);
+	printf("\ng2: ");print_block(g2);
+	printf("\nh2: ");print_block(h2);
+
+	printf("\n\nprod1: ");print_block(prod1);
+	printf("\nprod2: ");print_block(prod2);
+	*/
+
+	int pass = (memcmp(&prod1, &h1, 16) == 0) && (memcmp(&prod2, &h2, 16) == 0);
+	return pass;
+}
+
+uint128_t evalLinearR(uint128_t r, uint128_t p0, uint128_t p1){
+	uint128_t slope = subModP(p1, p0);
+	uint128_t pr = addModP(multModP(slope, r), p0);
+	return pr;
+}
+
+uint128_t evalQuadraticR(uint128_t r, uint128_t h0, uint128_t h1, uint128_t h2){
+	// h(r) = (1/2)(r-1)(r-2)h0 - r(r-2)h1 + (1/2)r(r-1)h2
+
+	//(1/2)(r-1)(r-2)h0
+	uint128_t t0 = 0;
+	if(r%2 == 0) {
+		t0 = multModP(h0, multModP(r-1, (r-2)/2));
+	} 
+	else{
+		t0 = multModP(h0, multModP((r-1)/2, r-2));
+	} 
+
+	//r(r-2)h1
+	uint128_t t1 = multModP(multModP(r, r-2), h1);
+	
+	//(1/2)r(r-1)h2
+	uint128_t t2 = 0;
+	if(r%2 == 0) {
+		t2 = multModP(h2, multModP(r-1, r/2));
+	} 
+	else{
+		t2 = multModP(h2, multModP((r-1)/2, r));
+	}
+	
+	//final sum
+	uint128_t ret = addModP(subModP(t0, t1), t2);
+}
+
+
+//client check inputs
+void clientVerify(EVP_CIPHER_CTX *ctx, uint128_t seed, int index, uint128_t aShare, uint128_t bShare, int dbLayers, uint8_t* bits, uint8_t* nonZeroVectorsIn){
+    
+    uint128_t *nonZeroVectors = (uint128_t*) nonZeroVectorsIn;
+    
+    //note that index is the actual index, not the virtual address, in our application to oblivious key value stores
+    //printf("clientVerify\n");
+    
+    //set bits vector to all zeros
+    memset(bits, 0, dbLayers);
+    
+    //#pragma omp parallel for
+    for(int i = 0; i < dbLayers; i++){
+        uint128_t whenToSwap;
+        int newIndex;
+        int oldIndex;
+
+        //set newAlphaIndex
+        oldIndex = index % (1<<(dbLayers - i));
+        newIndex = index % (1<<(dbLayers - i - 1));
+
+        //if the index has changed, then the nonzero value was in the second half
+        if(newIndex != oldIndex){
+            bits[i] = 1;
+        }
+        //printf("bits %d before potential flip: %d\n", i, bits[i]);
+
+        //check if the halves will be swapped and set the entry of bits
+        PRF(ctx, seed, i, -1, &whenToSwap);
+
+        bits[i] = bits[i] ^ ((uint128_t)whenToSwap % 2);
+        
+        uint128_t temp;
+        uint128_t temp2;
+        //check the mask value and set entry of nonZeroVectors
+        PRF(ctx, seed, i, oldIndex, &temp);
+        temp2 = multModP(subModP(aShare, bShare), temp);
+        memcpy(&nonZeroVectors[i], &temp2, 16);
+        
+    }
+    
+}
+
+//server check inputs
+void serverVerify(EVP_CIPHER_CTX *ctx, uint8_t *seedIn, int dbLayers, int dbSize, uint8_t* vectorsIn, uint8_t* outVectorsIn){
+    //outVectors should be of length 2*dbLayers since there are 2 sums per layer
+    //printf("serverVerify\n");
+    //don't modify vectors -- it should be treated as read-only, so make a copy
+    //uint128_t* vectorsWorkSpace = malloc(dbSize*sizeof(uint128_t));
+    //memcpy(vectorsWorkSpace, vectors, dbSize*sizeof(uint128_t));
+    
+    uint128_t *vectors = (uint128_t*) vectorsIn;
+    uint128_t *outVectors = (uint128_t*) outVectorsIn;
+    uint128_t seed;
+    memcpy(&seed, seedIn, 16);
+
+    
+    uint128_t* vectorsWorkSpace = vectors;
+    
+    uint128_t prfOutput;
+    uint128_t leftSum, rightSum;
+    int newDbSize = dbSize;
+    
+    
+    //#pragma omp declare reduction(ADDMODP: uint128_t : omp_out += omp_in + (omp_out + omp_in < omp_out || omp_out+omp_in > 0-MODP)*MODP)
+    
+
+    for(int i = 0; i < dbLayers; i++){
+        
+        leftSum = 0;
+        rightSum = 0;
+        
+        //multiply each element by a ``random'' value and add into the appropriate sum
+        //#pragma omp parallel for \
+        //  default(shared) private(prfOutput) \
+        //  reduction(ADDMODP:rightSum,leftSum)
+        for(int j = 0; j < newDbSize; j++){
+            PRF(ctx, seed, i, j, &prfOutput);         
+            if(j >= (1<<(dbLayers - i - 1))){ //if j is in right half
+                //rightSum = multModP(vectorsWorkSpace[j], prfOutput);
+                //printf("ladeeda%d\n", j);
+                //use line commented below when compiling without openmp
+                //looks like it actually works with openmp too!
+                rightSum = addModP(rightSum, multModP(vectorsWorkSpace[j], prfOutput));
+            }
+            else{ // j is in left half
+                //leftSum = multModP(vectorsWorkSpace[j], prfOutput);
+                        //printf("ladeedee%d\n", j);
+                //use line commented below when compiling without openmp
+                //looks like it actually works with openmp too!
+                leftSum = addModP(leftSum, multModP(vectorsWorkSpace[j], prfOutput));
+            }
+        }
+        //printf("\n");
+        
+        //add together left and right halves for next iteration
+        //#pragma omp parallel for
+        for(int j = 1<<(dbLayers - i - 1); j < newDbSize; j++){
+            vectorsWorkSpace[j - (1<<(dbLayers - i - 1))] =  addModP(vectorsWorkSpace[j - (1<<(dbLayers - i - 1))], vectorsWorkSpace[j]);
+        }
+        
+        //adjust newDbSize for next round
+        newDbSize = 1 << (dbLayers - (i+1));
+                
+        //check if the halves will be swapped and place the sums in the appropriate spots
+        PRF(ctx, seed, i, -1, &prfOutput);
+        if((uint128_t)prfOutput % 2 == 0){
+            memcpy(&outVectors[2*i], &leftSum, 16);
+            memcpy(&outVectors[2*i+1], &rightSum, 16);
+        }
+        else{
+            memcpy(&outVectors[2*i], &rightSum, 16);
+            memcpy(&outVectors[2*i+1], &leftSum, 16);
+        }
+        
+        //print_block(rightSum);
+        //print_block(leftSum);
+        
+    }
+    //free(vectorsWorkSpace);
+}
+
+//auditor functionality
+int auditorVerify(int dbLayers, uint8_t* bits, uint8_t* nonZeroVectorsIn, uint8_t* outVectorsAIn, uint8_t* outVectorsBIn){
+    
+    uint128_t *nonZeroVectors = (uint128_t*)nonZeroVectorsIn;
+    uint128_t *outVectorsA = (uint128_t*)outVectorsAIn;
+    uint128_t *outVectorsB = (uint128_t*)outVectorsBIn;
+    
+    //printf("auditorVerify\n");
+    int pass = 1; //set this to 0 if any check fails
+    uint128_t zero = 0;
+    
+    //#pragma omp parallel for
+    for(int i = 0; i < dbLayers; i++){
+        uint128_t mergeAB[2];
+        
+        //merge the output vectors to get the values
+        mergeAB[0] = subModP(outVectorsA[2*i], outVectorsB[2*i]);
+        mergeAB[1] = subModP(outVectorsA[2*i+1], outVectorsB[2*i+1]);
+        
+        //printf("%d %lu, %lu, %lu, %lu\n", i, outVectorsA[2*i], outVectorsA[2*i+1], outVectorsB[2*i], outVectorsB[2*i+1]);
+        
+        //first check that the appropriate side is 0
+        //only need to check AB since if it is 0 then so is BA
+        //then check that the other side is equal to the corresponding nonZeroVectors entry for one direction
+        if( memcmp(&mergeAB[1-bits[i]], &zero, 16) != 0 || (
+            memcmp(&mergeAB[bits[i]], &nonZeroVectors[i], 16) != 0
+        )){
+            printf("fail conditions in round %d: %d %d \n", i, memcmp(&mergeAB[1-bits[i]], &zero, 16), memcmp(&mergeAB[bits[i]], &nonZeroVectors[i], 16));
+            printf("auditor expected to see \n");print_block(nonZeroVectors[i]);
+            printf("but auditor saw \n");print_block(mergeAB[bits[i]]);
+            printf("difference \n"); print_block(nonZeroVectors[i] - mergeAB[bits[i]]);print_block(mergeAB[bits[i]] - nonZeroVectors[i]);
+
+            pass = 0;
+        }
+        
+    }
+    
+    return pass;
+}
+
+void digest_message(const unsigned char *message, size_t message_len, unsigned char **digest, unsigned int *digest_len)
+{
+	EVP_MD_CTX *mdctx;
+
+	if((mdctx = EVP_MD_CTX_create()) == NULL)
+		printf("digest error\n");
+
+	if(1 != EVP_DigestInit_ex(mdctx, EVP_sha256(), NULL))
+		printf("digest error\n");
+
+	if(1 != EVP_DigestUpdate(mdctx, message, message_len))
+		printf("digest error\n");
+
+	if((*digest = (unsigned char *)OPENSSL_malloc(EVP_MD_size(EVP_sha256()))) == NULL)
+		printf("digest error\n");
+
+	if(1 != EVP_DigestFinal_ex(mdctx, *digest, digest_len))
+		printf("digest error\n");
+
+	EVP_MD_CTX_destroy(mdctx);
+}
+
+void riposteClientVerify(EVP_CIPHER_CTX *ctx, uint128_t seed, int dbSize, uint128_t *va, uint128_t *vb, uint8_t **digestA, uint8_t **digestB){
+    uint128_t *mVectorA = (uint128_t*) malloc(dbSize*16);
+    uint128_t *mVectorB = (uint128_t*) malloc(dbSize*16);
+    uint128_t prfOutput;
+
+    //#pragma omp parallel for \
+    //default(shared) private(prfOutput)
+    for(int i = 0; i < dbSize; i++){
+        PRF(ctx, seed, 0, i, &prfOutput);
+        mVectorA[i] = va[i] ^ prfOutput;
+        mVectorB[i] = vb[i] ^ prfOutput;
+    }
+    
+    int len = 0;
+    digest_message((uint8_t*)mVectorA, dbSize*16, digestA, &len);
+    digest_message((uint8_t*)mVectorB, dbSize*16, digestB, &len);
+    free(mVectorA);
+    free(mVectorB);
+}
+
+void riposteServerVerify(EVP_CIPHER_CTX *ctx, uint128_t seed, int dbSize, uint128_t *vector, uint128_t *mVector, uint128_t *cValue){
+    
+    PRF(ctx, seed, 1, 0, cValue);
+    uint128_t prfOutput;
+    
+    //#pragma omp parallel for \
+    //default(shared) private(prfOutput)
+    for(int i = 0; i < dbSize; i++){
+        PRF(ctx, seed, 0, i, &prfOutput);
+        mVector[i] = vector[i] ^ prfOutput;
+    }
+}
+
+int riposteAuditorVerify(uint8_t *digestA, uint8_t *digestB, uint8_t *ma, uint8_t *mb, uint128_t ca, uint128_t cb, int dbSize){
+
+    int pass = 1;
+    
+    //check that the masked seeds are equal
+    if(ca != cb){
+        printf("failed audit, masked seeds unequal\n");
+        pass = 0;
+    }
+    
+    //check that m vectors differ in only 1 place
+    int differenceCount = 0;
+    
+    //#pragma omp parallel for
+    for(int i = 0; i < dbSize; i++) {
+        if(memcmp(&ma[i*16], &mb[i*16], 16) != 0){
+            //#pragma omp critical
+            differenceCount++;
+        }
+    }
+    if(differenceCount != 1){
+        printf("failed audit, difference count incorrect %d\n", differenceCount);
+        pass = 0;
+    }
+
+    //check that the digests match their expected values
+    int len = 0;
+    uint8_t *newDigestA = (uint8_t*)malloc(32);
+    uint8_t *newDigestB = (uint8_t*)malloc(32);
+    digest_message(ma, dbSize*16, &newDigestA, &len);
+    digest_message(mb, dbSize*16, &newDigestB, &len);
+    if(memcmp(digestA, newDigestA, 32) != 0 || memcmp(digestB, newDigestB, 32) != 0){
+        printf("failed audit, digest mismatch %d %d\n", memcmp(digestA, newDigestA, 32), memcmp(digestB, newDigestB, 32) != 0);
+        pass = 0;
+    }
+    
+    free(newDigestA);
+    free(newDigestB);
+    
+    return pass;
+}
+
+int dpf_tests(){
+//int main(){
+    //pick 2 64-bit values as a fixed aes key
+    //and use those values to key the aes we will be using as a PRG
+    EVP_CIPHER_CTX *ctx;
+    if(!(ctx = EVP_CIPHER_CTX_new())) 
+        printf("errors occured in creating context\n");
+    unsigned char *aeskey = (unsigned char*) "0123456789123456";
+    if(1 != EVP_EncryptInit_ex(ctx, EVP_aes_128_ecb(), NULL, aeskey, NULL))
+        printf("errors occured in init\n");
+    EVP_CIPHER_CTX_set_padding(ctx, 0);
+    
+    
+    //printf("mult test: %d", multModP((uint128_t)5<<125,((uint128_t)6)<<125)); return 0;
+    //generate DPF keys for a particular query
+	unsigned char *k0;
+	unsigned char *k1;
+    
+    
+    //functionality test for dpf
+    char* data = "this is the data!";
+    int dataSize = strlen(data)+1;
+
+    //can only test with smaller constants because
+    //gcc does not support 128 bit constants
+	genDPF(ctx, 128, 300, dataSize, data, &k0, &k1);
+    	
+    //evaluate dpf
+	uint128_t res1;
+	uint128_t res2;
+    uint8_t* dataShare0 = (uint8_t*) malloc(dataSize+16);
+    uint8_t* dataShare1 = (uint8_t*) malloc(dataSize+16);
+    
+	res1 = evalDPF(ctx, k0, 12, dataSize, dataShare0);
+	res2 = evalDPF(ctx, k1, 12, dataSize, dataShare1);
+    printf("\nresult evaluated at 12: ");
+	print_block(res1 ^ res2);
+
+	res1 = evalDPF(ctx, k0, 300, dataSize, dataShare0);
+	res2 = evalDPF(ctx, k1, 300, dataSize, dataShare1);
+    printf("\nresult evaluated at 300: ");
+	print_block(res1 ^ res2);
+    uint8_t* recoveredData = (uint8_t*) malloc(dataSize);
+    printf("data recovered: ");
+    for(int i = 0; i < dataSize; i++){
+        recoveredData[i] = dataShare0[i] ^ dataShare1[i];
+        printf("%c", recoveredData[i]);
+        //printf("%x\n", dataShare1[i]);
+
+    }
+    printf("\n");
+                //return 0;
+    
+
+    
+    //now we'll do a simple functionality test of the dpf checking.
+    //this will in no way be rigorous or even representative of the
+    //usual use case since all the evaluation points are small
+    //just a sanity check before moving on to the obliv key val stuff 
+    
+    uint128_t db[] = {4343423, 232132, 465437, 43, 300, 5346643};
+    int dbSize = 6;
+    int dbLayers = 3;
+    uint128_t* seed = malloc(sizeof(uint128_t));
+    *seed = getRandomBlock(); 
+    
+    //allocate the various arrays we will need
+    uint8_t* bits = malloc(dbLayers);
+    uint128_t* nonZeroVectors = malloc(sizeof(uint128_t)*dbLayers);
+    uint128_t* vectorsA = malloc(sizeof(uint128_t)*dbSize);
+    uint128_t* vectorsB = malloc(sizeof(uint128_t)*dbSize);
+    uint128_t* outVectorsA = malloc(sizeof(uint128_t)*2*dbLayers);
+    uint128_t* outVectorsB = malloc(sizeof(uint128_t)*2*dbLayers);
+    
+    //evaluate the db at each point for each server
+    //#pragma omp parallel for
+    for(int i = 0; i < dbSize; i++){
+        uint128_t res1, res2;
+        res1 = evalDPF(ctx, k0, db[i], dataSize, dataShare0);
+        res2 = evalDPF(ctx, k1, db[i], dataSize, dataShare1);
+        memcpy(&vectorsA[i], &res1, 16);
+        memcpy(&vectorsB[i], &res2, 16);
+    }
+
+    uint128_t* proofA = (uint128_t*)malloc(sizeof(uint128_t)*10);
+    uint128_t* proofB = (uint128_t*)malloc(sizeof(uint128_t)*10);
+    uint128_t* ansA = (uint128_t*)malloc(sizeof(uint128_t)*6);
+    uint128_t* ansB = (uint128_t*)malloc(sizeof(uint128_t)*6);
+    uint128_t mA, cA, mB, cB;
+    
+    //run the dpf verification functions
+
+    clientGenProof(ctx, *seed, 4, vectorsA[4], vectorsB[4], (uint8_t*)proofA, (uint8_t*)proofB);
+    serverSetupProof(ctx, (uint8_t*)seed, dbSize, (uint8_t*) vectorsA, (uint8_t*)&mA, (uint8_t*) &cA);
+    serverSetupProof(ctx, (uint8_t*)seed, dbSize, (uint8_t*) vectorsB, (uint8_t*)&mB, (uint8_t*) &cB);
+
+    //printf("m on server side"); print_block(subModP(mA,mB));
+    //printf("c on server side");print_block(subModP(cA,cB));
+
+    serverComputeQuery(ctx, (uint8_t*)seed, (uint8_t*)&mA, (uint8_t*) &cA, (uint8_t*)proofA, (uint8_t*) ansA);
+    serverComputeQuery(ctx, (uint8_t*)seed, (uint8_t*)&mB, (uint8_t*) &cB, (uint8_t*)proofB, (uint8_t*) ansB);
+    int pass = -1;
+    pass = serverVerifyProof((uint8_t*) ansA, (uint8_t*) ansB);
+    printf("dpf check verification: %d (should be 1)\n", pass);
+    
+    //now test the riposte auditing
+    free(outVectorsA);
+    free(outVectorsB);
+    outVectorsA = malloc(sizeof(uint128_t)*dbSize);
+    outVectorsB = malloc(sizeof(uint128_t)*dbSize);
+    uint128_t cValueA = 0;
+    uint128_t cValueB = 0;
+    uint8_t *digestA = (uint8_t*) malloc(32);
+    uint8_t *digestB = (uint8_t*) malloc(32);
+
+    riposteClientVerify(ctx, *seed, dbSize, vectorsA, vectorsB, &digestA, &digestB);
+
+    riposteServerVerify(ctx, *seed, dbSize, vectorsA, outVectorsA, &cValueA);
+    riposteServerVerify(ctx, *seed, dbSize, vectorsB, outVectorsB, &cValueB);
+    
+    pass = riposteAuditorVerify(digestA, digestB, (uint8_t*)outVectorsA, (uint8_t*)outVectorsB, cValueA, cValueB, dbSize);
+    printf("riposte dpf check verification: %d (should be 1)\n", pass);
+    
+    //tamper with dpf outputs to see if auditor catches it
+    //memcpy(&outVectorsB[2], &outVectorsA[1], 16);
+    //pass = riposteAuditorVerify(digestA, digestB, (uint8_t*)outVectorsA, (uint8_t*)outVectorsB, cValueA, cValueB, dbSize);
+    //printf("riposte dpf check verification: %d (should be 0)\n", pass);
+    
+    //return 0;
+    //performance test of dpf verification
+    
+    int dbSizes[4];
+    dbSizes[0] = 1000;
+    dbSizes[1] = 10000;
+    dbSizes[2] = 100000;
+    dbSizes[3] = 1000000;
+    int dbLayer[4];
+    dbLayer[0] = 10;
+    dbLayer[1] = 14;
+    dbLayer[2] = 17;
+    dbLayer[3] = 20;
+    clock_t begin, elapsed;
+
+     seed = malloc(sizeof(uint128_t));
+    *seed = getRandomBlock(); 
+    
+    vectorsA = malloc(sizeof(uint128_t)*dbSizes[3]);
+    vectorsB = malloc(sizeof(uint128_t)*dbSizes[3]);
+    
+    memset(vectorsA,'a',dbSizes[3]*16);
+    memset(vectorsB,'a',dbSizes[3]*16);
+    vectorsA[10] = 13;
+    vectorsB[10] = 12;
+        
+/*
+void clientGenProof(EVP_CIPHER_CTX *ctx, uint128_t seed, int index, uint128_t aShare, uint128_t bShare, uint8_t* outputsAIn, uint8_t* outputsBIn);
+void serverSetupProof(EVP_CIPHER_CTX *ctx, uint8_t *seedIn, int dbSize, uint8_t* vectorsIn, uint8_t* mIn, uint8_t* cIn);
+void serverComputeQuery(EVP_CIPHER_CTX *ctx, uint8_t *seedIn, uint8_t* mIn, uint8_t* cIn, uint8_t* proofIn, uint8_t* ansIn);
+int serverVerifyProof(uint8_t* ans1In, uint8_t* ans2In);
+*/
+
+    //us
+    for(int i = 0; i < 4; i++){
+        proofA = (uint128_t*)malloc(sizeof(uint128_t)*10);
+        proofB = (uint128_t*)malloc(sizeof(uint128_t)*10);
+        ansA = (uint128_t*)malloc(sizeof(uint128_t)*6);
+        ansB = (uint128_t*)malloc(sizeof(uint128_t)*6);
+        uint128_t mA, cA, mB, cB;
+        
+        begin = clock();
+        clientGenProof(ctx, *seed, 10, vectorsA[10], vectorsB[10], (uint8_t*)proofA, (uint8_t*)proofB);
+        elapsed = (clock() - begin) * 1000000 / CLOCKS_PER_SEC;
+        printf("client proof gen time for db size %d: %ld microseconds\n", dbSizes[i], elapsed);
+        
+        begin = clock();
+        serverSetupProof(ctx, (uint8_t*)seed, dbSizes[i], (uint8_t*)vectorsA, (uint8_t*)&mA, (uint8_t*)&cA);
+        elapsed = (clock() - begin) * 1000000 / CLOCKS_PER_SEC;
+        printf("server prep time for db size %d: %ld microseconds\n", dbSizes[i], elapsed);
+        serverSetupProof(ctx, (uint8_t*)seed, dbSizes[i], (uint8_t*)vectorsB, (uint8_t*)&mB, (uint8_t*)&cB);
+
+        begin = clock();
+        serverComputeQuery(ctx, (uint8_t*)seed, (uint8_t*)&mA, (uint8_t*)&cA, (uint8_t*)proofA, (uint8_t*)ansA);
+        elapsed = (clock() - begin) * 1000000 / CLOCKS_PER_SEC;
+        printf("server query time for db size %d: %ld microseconds\n", dbSizes[i], elapsed);
+        serverComputeQuery(ctx, (uint8_t*)seed, (uint8_t*)&mB, (uint8_t*)&cB, (uint8_t*)proofB, (uint8_t*)ansB);
+
+        pass = 0;
+        begin = clock();
+        pass = serverVerifyProof((uint8_t*)ansA,(uint8_t*)ansB);
+        elapsed = (clock() - begin) * 1000000 / CLOCKS_PER_SEC;
+        printf("server verification time for db size %d: %ld microseconds\n", dbSizes[i], elapsed);
+        if(pass == 0){
+            printf("dpf check verification failed %d\n", i);
+        }
+
+	free(proofA);
+	free(proofB);
+	free(ansA);
+	free(ansB);
+    }
+    
+    //riposte
+    for(int i = 0; i < 4; i++){ //something went wrong at 4
+        outVectorsA = malloc(sizeof(uint128_t)*dbSizes[i]);
+        outVectorsB = malloc(sizeof(uint128_t)*dbSizes[i]);
+        cValueA = 0;
+        cValueB = 0;
+        digestA = (uint8_t*) malloc(32);
+        digestB = (uint8_t*) malloc(32);
+        
+        
+        begin = clock();
+        riposteClientVerify(ctx, *seed, dbSizes[i], vectorsA, vectorsB, &digestA, &digestB);
+        elapsed = (clock() - begin) * 1000000 / CLOCKS_PER_SEC;
+        printf("riposte client verification time for db size %d: %ld microseconds\n", dbSizes[i], elapsed);
+        
+        begin = clock();
+        riposteServerVerify(ctx, *seed, dbSizes[i], vectorsA, outVectorsA, &cValueA);
+        elapsed = (clock() - begin) * 1000000 / CLOCKS_PER_SEC;
+        printf("riposte server verification time for db size %d: %ld microseconds\n", dbSizes[i], elapsed);
+        riposteServerVerify(ctx, *seed, dbSizes[i], vectorsB, outVectorsB, &cValueB);
+        
+        pass = 0;
+        begin = clock();
+        pass = riposteAuditorVerify(digestA, digestB, (uint8_t*)outVectorsA, (uint8_t*)outVectorsB, cValueA, cValueB, dbSizes[i]);
+        elapsed = (clock() - begin) * 1000000 / CLOCKS_PER_SEC;
+        printf("riposte auditor verification time for db size %d: %ld microseconds\n", dbSizes[i], elapsed);
+        if(pass == 0){
+            printf("riposte dpf check verification failed %d\n", i);
+        }
+        
+        free(outVectorsA);
+        free(outVectorsB);
+        free(digestA);
+        free(digestB);
+    }
+    
+    free(vectorsA);
+    free(vectorsB);
+    
+    return 0;
+    //performance test of the dpf
+    
+    char *s[10];
+    
+    s[0] = (char*) malloc(2);
+    s[1] = (char*) malloc(16);
+    s[2] = (char*) malloc(100);
+    s[3] = (char*) malloc(1000);
+    s[4] = (char*) malloc(10000);
+    s[5] = (char*) malloc(100000);
+    s[6] = (char*) malloc(1000000);
+    memset(s[0], 'a', 1);
+    memset(s[0]+1, '\0', 1);
+    memset(s[1], 'a', 15);
+    memset(s[1]+15, '\0', 1);
+    memset(s[2], 'a', 99);
+    memset(s[2]+99, '\0', 1);
+    memset(s[3], 'a', 999);
+    memset(s[3]+999, '\0', 1);
+    memset(s[4], 'a', 9999);
+    memset(s[4]+9999, '\0', 1);
+    memset(s[5], 'a', 99999);
+    memset(s[5]+99999, '\0', 1);    
+    memset(s[6], 'a', 999999);
+    memset(s[6]+999999, '\0', 1);
+       
+    free(dataShare0);
+    free(dataShare1);
+    free(recoveredData);
+    recoveredData = (uint8_t*) malloc(strlen(s[5])+1);
+    dataShare0 = (uint8_t*) malloc(strlen(s[5])+1);
+    dataShare1 = (uint8_t*) malloc(strlen(s[5])+1);
+    
+    for(int j = 2; j < 6; j++){//skip to the sizes we care about
+        dataSize = strlen(s[j])+1;
+        genDPF(ctx, 128, 300, dataSize, s[j], &k0, &k1);
+        res1 = evalDPF(ctx, k0, 12, dataSize, dataShare0);
+        res2 = evalDPF(ctx, k1, 12, dataSize, dataShare1);
+        for(int i = 0; i < dataSize; i++){
+            recoveredData[i] = dataShare0[i] ^ dataShare1[i];
+        }
+        if(strcmp(recoveredData, s[j]) == 0){
+            printf("string %d recovered data for wrong input", j);
+        }
+        res1 = evalDPF(ctx, k0, 300, dataSize, dataShare0);
+        res2 = evalDPF(ctx, k1, 300, dataSize, dataShare1);
+        for(int i = 0; i < dataSize; i++){
+            recoveredData[i] = dataShare0[i] ^ dataShare1[i];
+        }
+        if(strcmp(recoveredData, s[j]) != 0){
+            printf("string %d recovered incorrect data", j);
+        }
+
+        clock_t start = clock(), diff;
+        #pragma omp parallel for
+        for(int i = 0; i < 1000; i++){
+                res1 = evalDPF(ctx, k0, i*((uint128_t)2<<70), dataSize, dataShare0);
+        }
+        diff = clock() - start;
+        int msec = diff * 1000 / CLOCKS_PER_SEC;
+        printf("Time taken for string %d, db size 1,000: %d milliseconds\n", j, msec);
+        
+        start = clock();
+        #pragma omp parallel for
+        for(int i = 0; i < 10000; i++){
+                res1 = evalDPF(ctx, k0, i*((uint128_t)2<<70), dataSize, dataShare0);
+        }
+        diff = clock() - start;
+        msec = diff * 1000 / CLOCKS_PER_SEC;
+        printf("Time taken for string %d, db size 10,000: %d milliseconds\n", j, msec);
+        
+        start = clock();
+        #pragma omp parallel for
+        for(int i = 0; i < 100000; i++){
+                res1 = evalDPF(ctx, k0, i*((uint128_t)2<<70), dataSize, dataShare0);
+        }
+        diff = clock() - start;
+        msec = diff * 1000 / CLOCKS_PER_SEC;
+        printf("Time taken for string %d, db size 100,000: %d milliseconds\n", j, msec);
+        
+        /*
+        start = clock();
+        #pragma omp parallel for
+        for(int i = 0; i < 1000000; i++){
+                res1 = evalDPF(ctx, k0, i*((uint128_t)2<<70), dataSize, dataShare0);
+        }
+        diff = clock() - start;
+        msec = diff * 1000 / CLOCKS_PER_SEC;
+        printf("Time taken for string %d, db size 1,000,000: %d milliseconds\n", j, msec);
+        */
+
+    }
+    
+	return 0;
+}

+ 72 - 0
c/dpf.h

@@ -0,0 +1,72 @@
+#ifndef _DPF
+#define _DPF
+
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <openssl/conf.h>
+#include <openssl/evp.h>
+#include <openssl/err.h>
+#include <string.h>
+
+//defining these here
+//so they don't have to be repeated across okv and okvClient
+#define MAX_LAYERS 21
+#define MAX_DATA_SIZE 10000000
+#define MAX_THREADS 1024
+
+//use prime 2^128-159
+//so wrapping around involves a gap of size 159
+//from https://primes.utm.edu/lists/2small/100bit.html
+#define MODP (uint128_t) 159
+
+typedef __int128 int128_t;
+typedef unsigned __int128 uint128_t;
+
+void print_block(uint128_t input);
+
+//arithmetic mod P
+uint128_t addModP(uint128_t in1, uint128_t in2);
+uint128_t subModP(uint128_t in1, uint128_t in2);
+uint128_t multModP(uint128_t in1, uint128_t in2);
+
+uint128_t getRandomBlock(void);
+
+//DPF functions
+
+void dpfPRG(EVP_CIPHER_CTX *ctx, uint128_t input, uint128_t* output1, uint128_t* output2, int* bit1, int* bit2);
+
+void genDPF(EVP_CIPHER_CTX *ctx, int domainSize, uint128_t index, int dataSize, uint8_t* data, unsigned char** k0, unsigned char **k1);
+
+uint128_t evalDPF(EVP_CIPHER_CTX *ctx, unsigned char* k, uint128_t x, int dataSize, uint8_t* dataShare);
+
+//DPF checking functions
+
+void PRF(EVP_CIPHER_CTX *ctx, uint128_t seed, int layer, int count, uint128_t* output);
+void digest_message(const unsigned char *message, size_t message_len, unsigned char **digest, unsigned int *digest_len);
+
+//new auditing functions
+void clientGenProof(EVP_CIPHER_CTX *ctx, uint128_t seed, int index, uint128_t aShare, uint128_t bShare, uint8_t* outputsAIn, uint8_t* outputsBIn);
+void serverSetupProof(EVP_CIPHER_CTX *ctx, uint8_t *seedIn, int dbSize, uint8_t* vectorsIn, uint8_t* mIn, uint8_t* cIn);
+void serverComputeQuery(EVP_CIPHER_CTX *ctx, uint8_t *seedIn, uint8_t* mIn, uint8_t* cIn, uint8_t* proofIn, uint8_t* ansIn);
+int serverVerifyProof(uint8_t* ans1In, uint8_t* ans2In);
+uint128_t evalLinearR(uint128_t r, uint128_t p0, uint128_t p1);
+uint128_t evalQuadraticR(uint128_t r, uint128_t h0, uint128_t h1, uint128_t h2);
+
+
+//old auditing functions
+
+//client check inputs
+void clientVerify(EVP_CIPHER_CTX *ctx, uint128_t seed, int index, uint128_t aShare, uint128_t bShare, int dbLayers, uint8_t* bits, uint8_t* nonZeroVectorsIn);
+void riposteClientVerify(EVP_CIPHER_CTX *ctx, uint128_t seed, int dbSize, uint128_t *va, uint128_t *vb, uint8_t **digestA, uint8_t **digestB);
+
+//server check inputs
+void serverVerify(EVP_CIPHER_CTX *ctx, uint8_t *seedIn, int dbLayers, int dbSize, uint8_t* vectorsIn, uint8_t* outVectorsIn);
+void riposteServerVerify(EVP_CIPHER_CTX *ctx, uint128_t seed, int dbSize, uint128_t *vector, uint128_t *mVector, uint128_t *cValue);
+
+//auditor functionality
+int auditorVerify(int dbLayers, uint8_t* bits, uint8_t* nonZeroVectorsIn, uint8_t* outVectorsAIn, uint8_t* outVectorsBIn);
+int riposteAuditorVerify(uint8_t *digestA, uint8_t *digestB, uint8_t *ma, uint8_t *mb, uint128_t ca, uint128_t cb, int dbSize);
+
+#endif

BIN
c/dpf.h.gch


+ 183 - 0
c/okv.c

@@ -0,0 +1,183 @@
+#include "okv.h"
+#include <openssl/rand.h>
+#include <omp.h>
+
+#include <math.h>
+
+//opti! adjust size
+#define MAX_DB_SIZE 1000010
+
+vatRow db[MAX_DB_SIZE];
+int dbSize = 0;
+
+uint128_t seed;
+
+EVP_CIPHER_CTX *ctx[MAX_THREADS];
+
+
+void initializeServer(int numThreads){
+    
+    for(int i = 0; i < numThreads; i++){
+        //set fixed key
+        if(!(ctx[i] = EVP_CIPHER_CTX_new())) 
+            printf("errors occured in creating context\n");
+        unsigned char *aeskey = (unsigned char*) "0123456789123456";
+        if(1 != EVP_EncryptInit_ex(ctx[i], EVP_aes_128_ecb(), NULL, aeskey, NULL))
+            printf("errors occured in init\n");
+        EVP_CIPHER_CTX_set_padding(ctx[i], 0);
+    }
+    
+    memset(&seed, 0, 16);
+}
+
+//creates a new DB
+void createDb(int isLeader, int dataSize){
+    vatRow entry;
+
+    if (isLeader == 1) {
+        entry.newKeyA = getRandomBlock();
+
+        if(!(entry.rowKeyA = EVP_CIPHER_CTX_new())) 
+            printf("errors occured in creating context\n");
+        if(1 != EVP_EncryptInit_ex(entry.rowKeyA, EVP_aes_128_ecb(), NULL, (uint8_t*)&entry.newKeyA, NULL))
+            printf("errors occured in init\n");
+        EVP_CIPHER_CTX_set_padding(entry.rowKeyA, 0);
+        
+    } else {
+        entry.newKeyB = getRandomBlock();
+
+        if(!(entry.rowKeyB = EVP_CIPHER_CTX_new())) 
+            printf("errors occured in creating context\n");
+        if(1 != EVP_EncryptInit_ex(entry.rowKeyB, EVP_aes_128_ecb(), NULL, (uint8_t*)&entry.newKeyB, NULL))
+            printf("errors occured in init\n");
+        EVP_CIPHER_CTX_set_padding(entry.rowKeyB, 0);
+    }
+    
+    entry.dataSize = dataSize;
+    entry.data = malloc(dataSize);
+    entry.mask = malloc(dataSize);
+    memset(entry.mask, 0 , dataSize);
+    memset(entry.data, 0 , dataSize);
+    db[dbSize] = entry;
+    int i = dbSize;//to make code below work without changing stuff
+    dbSize = dbSize + 1;
+    
+    
+    //now do the encryption/rerandomization for this entry so it can be retrieved normally
+    uint8_t* maskTemp = (uint8_t*) malloc(dataSize+16);
+    uint8_t* seedTemp = (uint8_t*) malloc(dataSize+16);
+    //get rerandomization mask
+    for(int j = 0; j < (db[i].dataSize+16)/16; j++){
+        memcpy(&seedTemp[16*j], &seed, 16);
+        seedTemp[16*j] = seedTemp[16*j] ^ j;
+    }
+    int len;
+    
+    if (isLeader == 1) {
+       if(1 != EVP_EncryptUpdate(db[i].rowKeyA, maskTemp, &len, seedTemp, ((dataSize-1)|15)+1))
+        printf("errors occured in rerandomization of entry %d\n", i); 
+    } else {
+        if(1 != EVP_EncryptUpdate(db[i].rowKeyB, maskTemp, &len, seedTemp, ((dataSize-1)|15)+1))
+        printf("errors occured in rerandomization of entry %d\n", i); 
+    }
+    
+
+    //xor data into db and rerandomize db entry
+    for(int j = 0; j < dataSize; j++){
+        db[i].data[j] = db[i].data[j] ^ maskTemp[j];
+        db[i].mask[j] = maskTemp[j];
+    }
+    free(maskTemp);
+    free(seedTemp);
+}
+
+void xorIn(int i, uint8_t *data){
+    for(int j = 0; j < db[i].dataSize; j++){
+        db[i].data[j] = db[i].data[j] ^ data[j];
+    }
+}
+
+void resetDb() {
+    memset(db, 0, MAX_DB_SIZE);
+    dbSize = 0;
+}
+
+//read an entry
+void readData(int index, uint8_t *data){
+    memcpy(data, db[index].data, db[index].dataSize);
+}
+
+//gets the seed
+void readSeed(uint8_t *seedIn){
+    memcpy(seedIn, &seed, 16);
+}
+
+//returns a uint128_t representation of the input
+uint128_t getUint128_t(int i){
+    return (uint128_t) i;
+}
+
+//decrypt and recover a row
+void decryptRow(int localIndex, uint8_t *out, uint8_t *dataA, uint8_t *dataB, uint8_t *seedA, uint8_t *seedB){
+    int len;
+    
+    uint8_t *maskA = (uint8_t*) malloc(db[localIndex].dataSize+16);
+    uint8_t *maskB = (uint8_t*) malloc(db[localIndex].dataSize+16);
+    uint8_t *seedTempA = (uint8_t*) malloc(db[localIndex].dataSize+16);
+    uint8_t *seedTempB = (uint8_t*) malloc(db[localIndex].dataSize+16);
+    
+    //get the masks
+    for(int j = 0; j < (db[localIndex].dataSize+16)/16; j++){
+            memcpy(&seedTempA[16*j], seedA, 16);
+            seedTempA[16*j] = seedTempA[16*j] ^ j;
+    }
+    if(1 != EVP_EncryptUpdate(db[localIndex].rowKeyA, maskA, &len, seedTempA, ((db[localIndex].dataSize-1)|15)+1))
+        printf("errors occured in rerandomization of entry %d\n", localIndex);
+    for(int j = 0; j < (db[localIndex].dataSize+16)/16; j++){
+            memcpy(&seedTempB[16*j], seedB, 16);
+            seedTempB[16*j] = seedTempB[16*j] ^ j;
+    }
+    if(1 != EVP_EncryptUpdate(db[localIndex].rowKeyB, maskB, &len, seedTempB, ((db[localIndex].dataSize-1)|15)+1))
+        printf("errors occured in rerandomization of entry %d\n", localIndex);
+    
+
+    for(int i = 0; i < db[localIndex].dataSize; i++){
+        out[i] = dataA[i] ^ dataB[i] ^ maskA[i] ^ maskB[i];
+    }
+    
+    free(maskA);
+    free(maskB);
+    free(seedTempA);
+    free(seedTempB);
+
+}
+
+//gets the block that was used to calculate the cipher
+void getCipher(int isLeader, int i, uint8_t *array) {
+    if (isLeader == 1) {
+        memcpy(array, &db[i].newKeyA, 16);
+    } else {
+        memcpy(array, &db[i].newKeyB, 16);
+    }
+}
+
+//puts in the block that was used to calculate the cipher on the other server and calculates the missing one
+void putCipher(int isLeader, int i, uint8_t *array) {
+    if (isLeader == 0) {
+        memcpy(&db[i].newKeyA, array, 16);
+
+        if(!(db[i].rowKeyA = EVP_CIPHER_CTX_new())) 
+            printf("errors occured in creating context\n");
+        if(1 != EVP_EncryptInit_ex(db[i].rowKeyA, EVP_aes_128_ecb(), NULL, (uint8_t*)&db[i].newKeyA, NULL))
+            printf("errors occured in init\n");
+        EVP_CIPHER_CTX_set_padding(db[i].rowKeyA, 0);
+    } else {
+        memcpy(&db[i].newKeyB, array, 16);
+        
+        if(!(db[i].rowKeyB = EVP_CIPHER_CTX_new())) 
+            printf("errors occured in creating context\n");
+        if(1 != EVP_EncryptInit_ex(db[i].rowKeyB, EVP_aes_128_ecb(), NULL, (uint8_t*)&db[i].newKeyB, NULL))
+            printf("errors occured in init\n");
+        EVP_CIPHER_CTX_set_padding(db[i].rowKeyB, 0);
+    }
+}

+ 34 - 0
c/okv.h

@@ -0,0 +1,34 @@
+#ifndef _OKV
+#define _OKV
+
+#include "dpf.h"
+
+typedef struct{
+    EVP_CIPHER_CTX *rowKeyA;
+    EVP_CIPHER_CTX *rowKeyB;
+    uint128_t newKeyA;
+    uint128_t newKeyB;
+    int dataSize; //size of the data stored here
+    uint8_t* mask; //current mask resulting from rerandomization
+    uint8_t* data; //the actual data
+} vatRow;
+
+void initializeServer(int numThreads);
+
+void createDb(int isLeader, int dataSize);
+
+void xorIn(int i, uint8_t *data);
+
+void resetDb();
+
+void readData(int index, uint8_t *data);
+
+void readSeed(uint8_t *seedIn);
+
+uint128_t getUint128_t(int i);
+
+void decryptRow(int localIndex, uint8_t *out, uint8_t *dataA, uint8_t *dataB, uint8_t *seedA, uint8_t *seedB);
+
+void getCipher(int isLeader, int i, uint8_t *array);
+
+#endif

+ 49 - 0
c/okvClient.c

@@ -0,0 +1,49 @@
+#include "okvClient.h"
+#include <openssl/rand.h>
+#include <omp.h>
+#include <math.h>
+
+//for rand() mb i should not use this
+//people on the internet say its not cryptographically secure
+#include <stdlib.h>
+
+//cipher for ciphering
+EVP_CIPHER_CTX *ctx;
+
+//position where client will write to
+uint128_t position;
+
+void initializeCipher() {
+
+    if(!(ctx = EVP_CIPHER_CTX_new())) 
+        printf("errors occured in creating context\n");
+    //todo! use random key, this is only for testing
+    unsigned char *aeskey = (unsigned char*) "0123456789123456";
+    if(1 != EVP_EncryptInit_ex(ctx, EVP_aes_128_ecb(), NULL, aeskey, NULL))
+        printf("errors occured in init\n");
+    EVP_CIPHER_CTX_set_padding(ctx, 0);
+}
+
+uint128_t prepQuery(int pos, int dbSize, uint8_t *dataToWrite, int dataSize, int *querySize, uint8_t **dpfQueryA, uint8_t **dpfQueryB){
+
+    position = pos; //rand() % dbSize; //todo! adjust this for different clients, rand gives 83
+    *querySize = 1 + 16 + 1 + 18 * 128 + dataSize;
+    
+    genDPF(ctx, 128, position, dataSize, dataToWrite, dpfQueryA, dpfQueryB);
+
+    return position;
+}
+
+void prepAudit(uint8_t *seed, uint8_t *outputsA, uint8_t *outputsB, uint8_t *dpfQueryA, uint8_t *dpfQueryB){
+    
+    uint128_t shareA;
+    uint128_t shareB;
+    
+    //eval the dpf query at A and B and put results in shareA, shareB
+    uint8_t temp[16];
+    shareA = evalDPF(ctx, dpfQueryA, position, 16, temp);
+    shareB = evalDPF(ctx, dpfQueryB, position, 16, temp);
+    
+    //call the auditing function
+    clientGenProof(ctx, *(uint128_t*)seed, 0, shareA, shareB, outputsA, outputsB);
+}

+ 19 - 0
c/okvClient.h

@@ -0,0 +1,19 @@
+#ifndef _OKVCLIENT
+#define _OKVCLIENT
+
+typedef __int128 int128_t;
+typedef unsigned __int128 uint128_t;
+
+void initializeCipher();
+
+//prepare a querys
+uint128_t prepQuery(int pos, int dbSize, uint8_t *dataToWrite, int dataSize, int *querySize, uint8_t **dpfQueryA, uint8_t **dpfQueryB);
+
+//prepare an audit response for most recent query
+void prepAudit(uint8_t *seed, uint8_t *outputsA, uint8_t *outputsB, uint8_t *dpfQueryA, uint8_t *dpfQueryB);
+
+void getCipher(int isLeader, int i, uint8_t *array);
+
+void putCipher(int isLeader, int i, uint8_t *array);
+
+#endif

BIN
client/client


+ 634 - 0
client/client.go

@@ -0,0 +1,634 @@
+package main
+
+/*
+#cgo CFLAGS: -O2
+#cgo LDFLAGS: -lcrypto -lm
+#include "../c/dpf.h"
+#include "../c/okvClient.h"
+#include "../c/dpf.c"
+#include "../c/okvClient.c"
+*/
+import "C"
+
+//sssssssssss
+import (
+	lib "2PPS/lib"
+	"bytes"
+	"crypto/rand"
+	"crypto/sha256"
+	"crypto/tls"
+	"encoding/json"
+	"fmt"
+	"math/big"
+	"net"
+	"sync"
+	"time"
+	"unsafe"
+
+	"golang.org/x/crypto/nacl/box"
+)
+
+type tweet struct {
+	Topics []string
+	Text   string
+}
+
+const leader string = "127.0.0.1:4441"
+
+//needs to be changed at leader/follower/client at the same time
+const neededSubscriptions = 1
+const numClients = 2
+
+var topicList []string
+var archiveTopicList []string
+
+//todo! expand this for multiple clients
+var archiveInterests = make([]int, 1)
+var sharedSecret [numClients][2][32]byte = createSharedSecret()
+
+var wantsArchive = make([]byte, 1)
+
+var dataLen int = 32
+var numThreads int = 12
+var numRows int = 2
+
+var leaderPublicKey *[32]byte
+var followerPublicKey *[32]byte
+var clientPrivateKey *[32]byte
+var clientPublicKey *[32]byte
+
+func main() {
+
+	//creates test tweets
+	tweets := make([][]byte, numClients)
+	for i := range tweets {
+		tweets[i] = make([]byte, dataLen)
+	}
+
+	for i := 0; i < numClients; i++ {
+		var tweet []byte
+		if i == 0 {
+			topics := []byte("house; mouse")
+			text := []byte("I am a house in a mouse;")
+			tweet = append(tweet, topics...)
+			tweet = append(tweet, text...)
+		}
+
+		length := len(tweet)
+		for i := length; i < dataLen; i++ {
+			tweet = append(tweet, []byte(";")[0])
+		}
+		tweets[i] = tweet
+	}
+
+	wg := &sync.WaitGroup{}
+
+	for i := 0; i < numClients; i++ {
+		wg.Add(1)
+		go client(tweets[i], i)
+	}
+	wg.Wait()
+}
+
+func client(tweet []byte, clientNumber int) {
+
+	/*
+		if len(os.Args) != 4 {
+			fmt.Println("try again with: numThreads, dataLength, numRows")
+			return
+		}
+
+		//input when executing is follower amount
+		serverAmount, _ = strconv.Atoi(os.Args[1])
+		serverAmount++
+		dataLen, _ = strconv.Atoi(os.Args[2])
+		numThreads, _ = strconv.Atoi(os.Args[3])
+		dbSize, _ = strconv.Atoi(os.Args[4])
+	*/
+
+	generatedPublicKey, generatedPrivateKey, err := box.GenerateKey(rand.Reader)
+	if err != nil {
+		panic(err)
+	}
+	clientPrivateKey = generatedPrivateKey
+	clientPublicKey = generatedPublicKey
+
+	C.initializeCipher()
+
+	//initializes the connection to the leader
+	conf := &tls.Config{
+		InsecureSkipVerify: true,
+	}
+
+	leaderConn, err := tls.Dial("tcp", leader, conf)
+	if err != nil {
+		panic(err)
+	}
+	leaderConn.SetDeadline(time.Time{})
+
+	//receives topics first so client can participate asap
+	receiveTopicLists(leaderConn)
+
+	//gets the public keys of both servers
+	var tmpLeaderPubKey [32]byte
+	_, err = leaderConn.Read(tmpLeaderPubKey[:])
+	if err != nil {
+		panic(err)
+	}
+	leaderPublicKey = &tmpLeaderPubKey
+
+	var tmpFollowerPubKey [32]byte
+	_, err = leaderConn.Read(tmpFollowerPubKey[:])
+	if err != nil {
+		panic(err)
+	}
+	followerPublicKey = &tmpFollowerPubKey
+
+	//sends own public key
+	_, err = leaderConn.Write(clientPublicKey[:])
+	if err != nil {
+		panic(err)
+	}
+
+	//setup ends above
+	//while client is active he is always connected and has to participate
+
+	for {
+		//gets current phase
+		phase := make([]byte, 1)
+		_, err = leaderConn.Read(phase)
+		if err != nil {
+			panic(err)
+		}
+
+		fmt.Println("Phase: ", phase[0])
+
+		if phase[0] == 1 {
+
+			//todo! put into tweet creation
+			roundAsBytes := make([]byte, 4)
+			_, err = leaderConn.Read(roundAsBytes)
+			if err != nil {
+				panic(err)
+			}
+
+			//prep the query
+			dataSize := len(tweet)
+			querySize := make([]byte, 4)
+			cQuerySize := C.int(byteToInt(querySize))
+
+			var dpfQueryA *C.uchar
+			var dpfQueryB *C.uchar
+
+			//change to clientNumber
+			C.prepQuery(C.int(0), C.int(numRows), (*C.uchar)(&tweet[0]), C.int(dataSize), &cQuerySize, &dpfQueryA, &dpfQueryB)
+
+			intQuerySize := int(cQuerySize) //byteToInt(querySize)
+
+			//write the query
+			queryAPlaintext := C.GoBytes(unsafe.Pointer(dpfQueryA), C.int(intQuerySize))
+
+			//encrypts queryA and appends it to message
+			var nonce [24]byte
+			//fill nonce with randomness
+			_, err = rand.Read(nonce[:])
+			if err != nil {
+				panic("couldn't get randomness for nonce!")
+			}
+			dpfQueryAEncrypted := box.Seal(nonce[:], queryAPlaintext, &nonce, leaderPublicKey, clientPrivateKey)
+
+			//encrypts queryB and appends it to message
+			queryBPlaintext := C.GoBytes(unsafe.Pointer(dpfQueryB), C.int(intQuerySize))
+
+			//fill nonce with randomness
+			_, err = rand.Read(nonce[:])
+			if err != nil {
+				panic("couldn't get randomness for nonce!")
+			}
+			dpfQueryBEncrypted := box.Seal(nonce[:], queryBPlaintext, &nonce, followerPublicKey, clientPrivateKey)
+
+			//writes the dpfQuery to the leader
+			dpfLengthBytes := intToByte(len(dpfQueryAEncrypted))
+			_, err = leaderConn.Write(dpfLengthBytes)
+			if err != nil {
+				panic(err)
+			}
+
+			_, err = leaderConn.Write(dpfQueryAEncrypted)
+			if err != nil {
+				panic(err)
+			}
+
+			_, err = leaderConn.Write(dpfQueryBEncrypted)
+			if err != nil {
+				panic(err)
+			}
+
+			//auditing starts here
+
+			//read seed from leader(in preparation for auditing)
+			seed := make([]byte, 16)
+			_, err = leaderConn.Read(seed)
+			if err != nil {
+				panic(err)
+			}
+
+			//prepare message for auditor, box it, and send to server A
+			//prepare the auditor message
+			outputsA := (*C.uchar)(C.malloc(C.ulong(160)))
+			outputsB := (*C.uchar)(C.malloc(C.ulong(160)))
+
+			C.prepAudit((*C.uchar)(&seed[0]), outputsA, outputsB, dpfQueryA, dpfQueryB)
+
+			auditPlaintextLeader := C.GoBytes(unsafe.Pointer(outputsA), C.int(160))
+			auditPlaintextFollower := C.GoBytes(unsafe.Pointer(outputsB), C.int(160))
+
+			//encrypt messages for auditing
+			//fill nonce with randomness
+			_, err = rand.Read(nonce[:])
+			if err != nil {
+				panic("couldn't get randomness for nonce!")
+			}
+			auditCiphertextLeader := box.Seal(nonce[:], auditPlaintextLeader, &nonce, leaderPublicKey, clientPrivateKey)
+
+			//fill nonce with randomness
+			_, err = rand.Read(nonce[:])
+			if err != nil {
+				panic("couldn't get randomness for nonce!")
+			}
+			auditCiphertextFollower := box.Seal(nonce[:], auditPlaintextFollower, &nonce, followerPublicKey, clientPrivateKey)
+
+			//send boxed audits to leader
+			auditLengthBytes := intToByte(len(auditCiphertextLeader))
+
+			_, err = leaderConn.Write(auditLengthBytes)
+			if err != nil {
+				panic(err)
+			}
+
+			_, err = leaderConn.Write(auditCiphertextLeader)
+			if err != nil {
+				panic(err)
+			}
+
+			_, err = leaderConn.Write(auditCiphertextFollower)
+			if err != nil {
+				panic(err)
+			}
+
+			C.free(unsafe.Pointer(outputsA))
+			C.free(unsafe.Pointer(outputsB))
+			C.free(unsafe.Pointer(dpfQueryA))
+			C.free(unsafe.Pointer(dpfQueryB))
+		} else if phase[0] == 3 {
+			/*
+				possible Values
+				0 : new client
+					leader expects sharedSecrets, expects pirQuery
+				1 : update needed
+					leader sends topicList, performs local update of sharedSecret, expects pirQuery
+				2 : no update needed
+					nothing
+			*/
+			subPhase := make([]byte, 1)
+			_, err := leaderConn.Read(subPhase)
+			if err != nil {
+				panic(err)
+			}
+
+			var encryptedQueryLeader, encryptedQueryFollower []byte
+			//first time participating
+			if subPhase[0] == 0 {
+				receiveTopicLists(leaderConn)
+				encryptedQueryLeader, encryptedQueryFollower = createPIRQuery(int(subPhase[0]), clientNumber)
+				sendQuerys(encryptedQueryLeader, encryptedQueryFollower, leaderConn, false)
+
+			}
+			//updates the topic list and what client is interested in
+			if subPhase[0] == 1 {
+
+				receiveTopicLists(leaderConn)
+
+				//updates local secret
+				for index := 0; index < 2; index++ {
+					sharedSecret[clientNumber][index] = sha256.Sum256(sharedSecret[clientNumber][index][:])
+				}
+
+				encryptedQueryLeader, encryptedQueryFollower = createPIRQuery(int(subPhase[0]), clientNumber)
+				sendQuerys(encryptedQueryLeader, encryptedQueryFollower, leaderConn, false)
+
+			}
+
+			receiveTweets(sharedSecret[clientNumber], leaderConn, clientNumber, false)
+
+			if len(archiveTopicList) > 0 {
+				wantsArchive[0] = 0 //archive test
+			} else {
+				wantsArchive[0] = 0
+			}
+
+			_, err = leaderConn.Write(wantsArchive)
+			if err != nil {
+				panic(err)
+			}
+
+			if wantsArchive[0] == 1 && len(archiveTopicList) > 0 {
+				encryptedQueryLeader, encryptedQueryFollower = createPIRQuery(-1, clientNumber)
+				sendQuerys(encryptedQueryLeader, encryptedQueryFollower, leaderConn, true)
+				receiveTweets(sharedSecret[clientNumber], leaderConn, clientNumber, true)
+			}
+
+		} else {
+			panic("somethin went wrong")
+		}
+	}
+}
+
+//creates and sends the pirQuerys for each server
+func createPIRQuery(subPhase int, clientNumber int) ([]byte, []byte) {
+	//later this will be taken from gui, this is only for testing
+	topicsOfInterest := make([]int, 1)
+	topicsOfInterest[0] = 1
+
+	archiveInterests[0] = 1
+
+	tmptopicsOfInterest := make([]int, len(topicsOfInterest))
+	copy(tmptopicsOfInterest, topicsOfInterest)
+
+	tmpNeededSubscriptions := neededSubscriptions
+
+	tmpTopicList := make([]string, len(topicList))
+	copy(tmpTopicList, topicList)
+	if wantsArchive[0] == 1 && subPhase == -1 {
+		tmpNeededSubscriptions = len(archiveInterests)
+		copy(tmptopicsOfInterest, archiveInterests) //archiveInterests from gui
+		copy(tmpTopicList, archiveTopicList)
+	}
+
+	topicsOfInterestAsBytes := make([][]byte, tmpNeededSubscriptions)
+	for i := range topicsOfInterestAsBytes {
+		topicsOfInterestAsBytes[i] = make([]byte, len(tmpTopicList))
+	}
+
+	//creates fake topicsOfInterest if client is boooring
+	if len(tmptopicsOfInterest) < tmpNeededSubscriptions && subPhase != -1 {
+		//this should be done when client is idling
+		tmptopicsOfInterest = addFakeInterests(tmptopicsOfInterest)
+	}
+
+	for topic, position := range tmptopicsOfInterest {
+		topicsOfInterestAsBytes[topic][position-1] = 1
+	}
+
+	//this for N servers for one topic
+	/* interested in topic 3, 6 servers
+	this needs to be repeated for every topic client is interested in
+
+	wanted
+	[0, 0, 1]
+
+	random creation of 1 pirQuery
+	[0, 1, 0]
+
+	manual creation of 1 pirQuery
+	[0, 1, 1]
+
+	xor result
+	[0, 0, 1]
+
+	*/
+
+	//pirQuery [serverAmount][topicsofinterest][topicAmount]byte
+	pirQuerys := make([][][]byte, 2)
+	for i := range pirQuerys {
+		pirQuerys[i] = make([][]byte, len(tmptopicsOfInterest))
+		for j := range pirQuerys[i] {
+			pirQuerys[i][j] = make([]byte, len(tmpTopicList))
+		}
+	}
+
+	//for leader
+	//pirQuery will be filled with random bits
+	for topic := range tmptopicsOfInterest {
+		for index := range tmpTopicList {
+			bit, err := rand.Int(rand.Reader, big.NewInt(2))
+			if err != nil {
+				panic(err)
+			}
+			pirQuerys[0][topic][index] = byte(bit.Int64())
+		}
+	}
+
+	//creating last manually with result and wanted
+	//if position random result correct -> 0, not correct -> 1
+	for topic := range tmptopicsOfInterest {
+		for index := range tmpTopicList {
+			if pirQuerys[0][topic][index] == topicsOfInterestAsBytes[topic][index] {
+				pirQuerys[1][topic][index] = 0
+			} else {
+				pirQuerys[1][topic][index] = 1
+			}
+		}
+	}
+
+	//flattens the querys to be able to send them more efficently
+	messagesFlattened := make([][]byte, 2)
+	//adds the sharedSecret to the first pirQuery when first time participating
+	if subPhase == 0 {
+		for server := 0; server < 2; server++ {
+			messagesFlattened[server] = append(messagesFlattened[server], sharedSecret[clientNumber][server][:]...)
+		}
+	}
+
+	for server := 0; server < 2; server++ {
+		for topic := range pirQuerys[server] {
+			messagesFlattened[server] = append(messagesFlattened[server], pirQuerys[server][topic][:]...)
+		}
+	}
+
+	var nonce [24]byte
+	_, err := rand.Read(nonce[:])
+	if err != nil {
+		panic("couldn't get randomness for nonce!")
+	}
+	encryptedQueryLeader := box.Seal(nonce[:], messagesFlattened[0], &nonce, leaderPublicKey, clientPrivateKey)
+
+	_, err = rand.Read(nonce[:])
+	if err != nil {
+		panic("couldn't get randomness for nonce!")
+	}
+	encryptedQueryFollower := box.Seal(nonce[:], messagesFlattened[1], &nonce, followerPublicKey, clientPrivateKey)
+
+	return encryptedQueryLeader, encryptedQueryFollower
+}
+
+func sendQuerys(encryptedQueryLeader, encryptedQueryFollower []byte, leaderConn net.Conn, getArchive bool) {
+	encryptedLength := len(encryptedQueryLeader)
+
+	//sends the pirQuerysLength to the leader
+	_, err := leaderConn.Write(intToByte(encryptedLength))
+	if err != nil {
+		panic(err)
+	}
+
+	//sends the pirQuerys to the leader
+	_, err = leaderConn.Write(encryptedQueryLeader)
+	if err != nil {
+		panic(err)
+	}
+
+	_, err = leaderConn.Write(encryptedQueryFollower)
+	if err != nil {
+		panic(err)
+	}
+
+	if getArchive {
+		leaderConn.Write(intToByte(len(archiveInterests)))
+		if err != nil {
+			panic(err)
+		}
+	}
+}
+
+func receiveTweets(sharedSecret [2][32]byte, leaderConn net.Conn, clientNumber int, getArchive bool) {
+
+	tmpNeededSubscriptions := neededSubscriptions
+	if getArchive {
+		tmpNeededSubscriptions = len(archiveInterests)
+	}
+	for i := 0; i < tmpNeededSubscriptions; i++ {
+		//client receives tweets
+		tweetsLengthBytes := make([]byte, 4)
+
+		_, err := leaderConn.Read(tweetsLengthBytes)
+		if err != nil {
+			panic(err)
+		}
+		tweetsLength := byteToInt(tweetsLengthBytes)
+
+		tweets := make([]byte, tweetsLength)
+		_, err = leaderConn.Read(tweets)
+		if err != nil {
+			panic(err)
+		}
+
+		//expand sharedSecret so it is of right length
+		expandBy := len(tweets) / 32
+		expandedSharedSecrets := make([][]byte, 2)
+
+		for i := 0; i < 2; i++ {
+			for j := 0; j < expandBy; j++ {
+				expandedSharedSecrets[i] = append(expandedSharedSecrets[i], sharedSecret[i][:]...)
+			}
+			//fmt.Println(expandedSharedSecrets[i])
+		}
+
+		//xors the received messge into the message to display
+		for i := 0; i < 2; i++ {
+			lib.Xor(expandedSharedSecrets[i][:], tweets)
+		}
+
+		//tweets can be displayed
+		fmt.Println("final result: ", string(tweets))
+	}
+}
+
+//creates a shared secret for each server
+func createSharedSecret() [numClients][2][32]byte {
+	var tmpSharedSecret [numClients][2][32]byte
+	for i := 0; i < numClients; i++ {
+		for j := 0; j < 2; j++ {
+			_, err := rand.Read(tmpSharedSecret[i][j][:])
+			if err != nil {
+				panic("couldn't get randomness for sharedSecret!")
+			}
+		}
+	}
+
+	return tmpSharedSecret
+}
+
+//generates a topicOfInterest Array with random values
+//todo! fix
+func addFakeInterests(topicsOfInterest []int) []int {
+	length := neededSubscriptions
+	fakeTopicsOfInterest := make([]int, length)
+
+	//fills the array with unique random ascending values in range with len(topicList)
+	for index := 0; index < length; index++ {
+		min := (index * (len(topicList) / length)) + 1
+		max := ((index + 1) * (len(topicList) / length))
+		//fmt.Println("max", max, "list", len(topicList))
+		if max == len(topicList)-1 {
+			max++
+		}
+		//fmt.Println("i", index, min, max, len(topicList))
+		bigNumber, err := rand.Int(rand.Reader, big.NewInt(int64(max-min+1)))
+		if err != nil {
+			panic(err)
+		}
+		var number int = int(bigNumber.Int64()) + min
+
+		fakeTopicsOfInterest[index] = number
+	}
+	//adds unique and new random numbers to topicOfInterests until length is satisfied
+	for _, number := range fakeTopicsOfInterest {
+		if !inList(number, topicsOfInterest) {
+			topicsOfInterest = append(topicsOfInterest, number)
+		}
+		if len(topicsOfInterest) == length {
+			break
+		}
+	}
+
+	return topicsOfInterest
+}
+
+func inList(number int, list []int) bool {
+	for _, element := range list {
+		if element == number {
+			return true
+		}
+	}
+	return false
+}
+
+func receiveTopicLists(leaderConn net.Conn) {
+	for i := 0; i < 2; i++ {
+		topicListLength := make([]byte, 4)
+		_, err := leaderConn.Read(topicListLength)
+		if err != nil {
+			panic(err)
+		}
+
+		recTopicList := make([]byte, byteToInt(topicListLength))
+		_, err = leaderConn.Read(recTopicList[:])
+		if err != nil {
+			panic(err)
+		}
+
+		var tmpTopicList []string
+		arrayReader := bytes.NewReader(recTopicList[:])
+		json.NewDecoder(arrayReader).Decode(&tmpTopicList)
+		if i == 0 {
+			topicList = tmpTopicList
+		} else {
+			archiveTopicList = tmpTopicList
+		}
+	}
+
+}
+
+func intToByte(myInt int) (retBytes []byte) {
+	retBytes = make([]byte, 4)
+	retBytes[3] = byte((myInt >> 24) & 0xff)
+	retBytes[2] = byte((myInt >> 16) & 0xff)
+	retBytes[1] = byte((myInt >> 8) & 0xff)
+	retBytes[0] = byte(myInt & 0xff)
+	return
+}
+
+func byteToInt(myBytes []byte) (x int) {
+	x = int(myBytes[3])<<24 + int(myBytes[2])<<16 + int(myBytes[1])<<8 + int(myBytes[0])
+	return
+}

+ 756 - 0
follower/follower.go

@@ -0,0 +1,756 @@
+package main
+
+//#cgo CFLAGS: -fopenmp -O2
+//#cgo LDFLAGS: -lcrypto -lm -fopenmp
+//#include "../c/dpf.h"
+//#include "../c/okv.h"
+//#include "../c/dpf.c"
+//#include "../c/okv.c"
+import "C"
+
+//sssssssssssss
+
+import (
+	"2PPS/lib"
+	"crypto/rand"
+	"crypto/rsa"
+	"crypto/sha256"
+	"crypto/tls"
+	"crypto/x509"
+	"crypto/x509/pkix"
+	"encoding/pem"
+	"fmt"
+	"math/big"
+	"net"
+	"strconv"
+	"sync"
+	"time"
+	"unsafe"
+
+	"golang.org/x/crypto/nacl/box"
+)
+
+const leader string = "127.0.0.1:4442"
+
+//needs to be changed at leader/follower/client at the same time
+const neededSubscriptions = 1
+
+//this stores all neccessary information for each client
+type clientKeys struct {
+	SharedSecret [32]byte
+	PirQuery     [][]byte
+}
+
+//uses clients publicKey as key
+var clientData = make(map[[32]byte]clientKeys)
+var topicList []byte
+var topicAmount int
+
+var followerPrivateKey *[32]byte
+var followerPublicKey *[32]byte
+var leaderPublicKey *[32]byte
+
+var numThreads int = 12
+
+//has to be dividable by 32
+var dataLength int = 32
+var numRows int = 2
+
+var round int = 1
+var startTime time.Time
+
+var maxTimePerRound time.Duration = 5 * time.Second
+
+func main() {
+
+	generatedPublicKey, generatedPrivateKey, err := box.GenerateKey(rand.Reader)
+	if err != nil {
+		panic(err)
+	}
+
+	followerPrivateKey = generatedPrivateKey
+	followerPublicKey = generatedPublicKey
+
+	/*
+		if len(os.Args) != 4 {
+			fmt.Println("try again: numThreads, dataLength, numRows")
+			return
+		}
+		numThreads, _ = strconv.Atoi(os.Args[2])
+		dataLength, _ = strconv.Atoi(os.Args[3])
+		numRows, _ = strconv.Atoi(os.Args[4])
+	*/
+
+	C.initializeServer(C.int(numThreads))
+
+	followerConnectionPrivateKey, err := rsa.GenerateKey(rand.Reader, 2048)
+	if err != nil {
+		panic(err)
+	}
+
+	// Generate a pem block with the private key
+	keyPem := pem.EncodeToMemory(&pem.Block{
+		Type:  "RSA PRIVATE KEY",
+		Bytes: x509.MarshalPKCS1PrivateKey(followerConnectionPrivateKey),
+	})
+
+	tml := x509.Certificate{
+		// you can add any attr that you need
+		NotBefore: time.Now(),
+		NotAfter:  time.Now().AddDate(5, 0, 0),
+		// you have to generate a different serial number each execution
+		SerialNumber: big.NewInt(123123),
+		Subject: pkix.Name{
+			CommonName:   "New Name",
+			Organization: []string{"New Org."},
+		},
+		BasicConstraintsValid: true,
+	}
+	cert, err := x509.CreateCertificate(rand.Reader, &tml, &tml, &followerConnectionPrivateKey.PublicKey, followerConnectionPrivateKey)
+	if err != nil {
+		panic(err)
+	}
+
+	// Generate a pem block with the certificate
+	certPem := pem.EncodeToMemory(&pem.Block{
+		Type:  "CERTIFICATE",
+		Bytes: cert,
+	})
+
+	tlsCert, err := tls.X509KeyPair(certPem, keyPem)
+	if err != nil {
+		panic(err)
+	}
+
+	config := &tls.Config{Certificates: []tls.Certificate{tlsCert}}
+
+	fmt.Println("start leader")
+
+	//listens for leader
+	lnLeader, err := tls.Listen("tcp", ":4443", config)
+	if err != nil {
+		panic(err)
+	}
+	defer lnLeader.Close()
+
+	leaderConnection, err := lnLeader.Accept()
+	if err != nil {
+		panic(err)
+	}
+
+	//send publicKey to leader
+	_, err = leaderConnection.Write(followerPublicKey[:])
+	if err != nil {
+		panic(err)
+	}
+
+	//receives leader PublicKey
+	var tmpLeaderPubKey [32]byte
+	_, err = leaderConnection.Read(tmpLeaderPubKey[:])
+	if err != nil {
+		panic(err)
+	}
+
+	leaderPublicKey = &tmpLeaderPubKey
+
+	//setup ends here
+
+	//locks access to DB
+	var m sync.Mutex
+	wg := &sync.WaitGroup{}
+
+	for {
+
+		//phase1
+		fmt.Println("phase1")
+
+		//create write db for this round
+		for i := 0; i < numRows; i++ {
+			C.createDb(C.int(0), C.int(dataLength))
+		}
+
+		for i := 0; i < numThreads; i++ {
+			wg.Add(1)
+			leaderConnection, err := lnLeader.Accept()
+			if err != nil {
+				panic(err)
+			}
+			leaderConnection.SetDeadline(time.Time{})
+			startTime = time.Now()
+			go phase1(i, leaderConnection, m, wg)
+		}
+		wg.Wait()
+
+		//phase2
+		fmt.Println("phase2")
+		leaderConnection, err := lnLeader.Accept()
+		if err != nil {
+			panic(err)
+		}
+		leaderConnection.SetDeadline(time.Time{})
+
+		phase2(leaderConnection)
+
+		//phase3
+
+		fmt.Println("phase3")
+
+		if round == 1 {
+			//addTestTweets()
+		}
+
+		//no tweets -> continue to phase 1 and mb get tweets
+		topicList, topicAmount = lib.GetTopicList(0)
+		if len(topicList) == 0 {
+			continue
+		}
+
+		for i := 0; i < numThreads; i++ {
+			wg.Add(1)
+			leaderConnection, err := lnLeader.Accept()
+			if err != nil {
+				panic(err)
+			}
+			leaderConnection.SetDeadline(time.Time{})
+			startTime = time.Now()
+			go phase3(leaderConnection, wg)
+		}
+
+		wg.Wait()
+
+		lib.CleanUpdbR(round)
+
+		round++
+
+	}
+}
+
+func phase1(id int, leaderWorkerConnection net.Conn, m sync.Mutex, wg *sync.WaitGroup) {
+
+	gotClient := make([]byte, 1)
+
+	for {
+		_, err := leaderWorkerConnection.Read(gotClient)
+		if err != nil {
+			panic(err)
+		}
+
+		//this worker is done
+		if gotClient[0] == 0 {
+			wg.Done()
+			return
+		}
+
+		//setup the worker-specific db
+		dbSize := int(C.dbSize)
+		db := make([][]byte, dbSize)
+		for i := 0; i < dbSize; i++ {
+			db[i] = make([]byte, int(C.db[i].dataSize))
+		}
+
+		//gets clients publicKey
+		var clientPublicKey *[32]byte
+		var tmpClientPublicKey [32]byte
+		_, err = leaderWorkerConnection.Read(tmpClientPublicKey[:])
+		if err != nil {
+			panic(err)
+		}
+		clientPublicKey = &tmpClientPublicKey
+
+		//gets dpfQuery from leader
+		dpfLengthBytes := make([]byte, 4)
+		_, err = leaderWorkerConnection.Read(dpfLengthBytes)
+		if err != nil {
+			panic(err)
+		}
+		dpfLength := byteToInt(dpfLengthBytes)
+
+		dpfQueryBEncrypted := make([]byte, dpfLength)
+
+		_, err = leaderWorkerConnection.Read(dpfQueryBEncrypted)
+		if err != nil {
+			panic(err)
+		}
+
+		//auditing starts here
+
+		//gets seeed from leader
+		seed := make([]byte, 16)
+		_, err = leaderWorkerConnection.Read(seed)
+		if err != nil {
+			panic(err)
+		}
+
+		//receive client audit result
+		auditLengthBytes := make([]byte, 4)
+		_, err = leaderWorkerConnection.Read(auditLengthBytes)
+		if err != nil {
+			panic(err)
+		}
+
+		auditLength := 200 //byteToInt(auditLengthBytes)
+		clientAuditEncrypted := make([]byte, auditLength)
+
+		_, err = leaderWorkerConnection.Read(clientAuditEncrypted)
+		if err != nil {
+			panic(err)
+		}
+
+		//decrypts the clients audit result
+		var decryptNonce [24]byte
+		copy(decryptNonce[:], clientAuditEncrypted[:24])
+		clientAuditB, ok := box.Open(nil, clientAuditEncrypted[24:], &decryptNonce, clientPublicKey, followerPrivateKey)
+		if !ok {
+			panic("clientAuditB decryption not ok")
+		}
+
+		//decrypt dpfQueryB for sorting into db
+		copy(decryptNonce[:], dpfQueryBEncrypted[:24])
+		dpfQueryB, ok := box.Open(nil, dpfQueryBEncrypted[24:], &decryptNonce, clientPublicKey, followerPrivateKey)
+		if !ok {
+			panic("dpfQueryB decryption not ok")
+		}
+
+		vector := make([]byte, dbSize*16)
+		//run dpf, xor into local db
+		for i := 0; i < dbSize; i++ {
+			ds := int(C.db[i].dataSize)
+			dataShare := make([]byte, ds)
+			pos := C.getUint128_t(C.int(i))
+			//fmt.Println(i, pos)
+			v := C.evalDPF(C.ctx[id], (*C.uchar)(&dpfQueryB[0]), pos, C.int(ds), (*C.uchar)(&dataShare[0]))
+			fmt.Println("")
+			fmt.Println("v", i, v)
+			copy(vector[i*16:(i+1)*16], C.GoBytes(unsafe.Pointer(&v), 16))
+			for j := 0; j < ds; j++ {
+				db[i][j] = db[i][j] ^ dataShare[j]
+			}
+		}
+
+		//prepare for audit
+		mVal := make([]byte, 16)
+		cVal := make([]byte, 16)
+
+		C.serverSetupProof(C.ctx[id], (*C.uchar)(&seed[0]), C.dbSize, (*C.uchar)(&vector[0]), (*C.uchar)(&mVal[0]), (*C.uchar)(&cVal[0]))
+
+		//compute audit query
+		auditResultB := make([]byte, 96)
+		C.serverComputeQuery(C.ctx[id], (*C.uchar)(&seed[0]), (*C.uchar)(&mVal[0]), (*C.uchar)(&cVal[0]), (*C.uchar)(&clientAuditB[0]), (*C.uchar)(&auditResultB[0]))
+
+		//encrypt follower audit result
+		var nonce [24]byte
+		//fill nonce with randomness
+		_, err = rand.Read(nonce[:])
+		if err != nil {
+			panic("couldn't get randomness for nonce!")
+		}
+		auditResultBEncrypted := box.Seal(nonce[:], auditResultB, &nonce, leaderPublicKey, followerPrivateKey)
+
+		//gets audit result from leader
+		auditResultAEncryptedLengthBytes := make([]byte, 4)
+		_, err = leaderWorkerConnection.Read(auditResultAEncryptedLengthBytes)
+		if err != nil {
+			panic(err)
+		}
+
+		auditResultAEncryptedLength := byteToInt(auditResultAEncryptedLengthBytes)
+
+		auditResultAEncrypted := make([]byte, auditResultAEncryptedLength)
+		_, err = leaderWorkerConnection.Read(auditResultAEncrypted)
+		if err != nil {
+			panic(err)
+		}
+
+		//send follower audit result to leader
+		auditResultBEncryptedLengthBytes := intToByte(len(auditResultBEncrypted))
+		_, err = leaderWorkerConnection.Write(auditResultBEncryptedLengthBytes)
+		if err != nil {
+			panic(err)
+		}
+
+		_, err = leaderWorkerConnection.Write(auditResultBEncrypted)
+		if err != nil {
+			panic(err)
+		}
+
+		//decrypts the audit result from leader
+		copy(decryptNonce[:], auditResultAEncrypted[:24])
+		auditResultA, ok := box.Open(nil, auditResultAEncrypted[24:], &decryptNonce, leaderPublicKey, followerPrivateKey)
+		if !ok {
+			panic("auditResultA decryption not ok")
+		}
+
+		//compute audit result
+		auditResult := int(C.serverVerifyProof((*C.uchar)(&auditResultA[0]), (*C.uchar)(&auditResultB[0])))
+
+		if auditResult == 0 {
+			//fmt.Println("audit failed")
+		} /*else {
+			fmt.Println("audit passed")
+		}
+		*/
+
+		//xor the worker's DB into the main DB
+		for i := 0; i < dbSize; i++ {
+			m.Lock()
+			C.xorIn(C.int(i), (*C.uchar)(&db[i][0]))
+			m.Unlock()
+		}
+	}
+}
+
+func phase2(leaderWorkerConnection net.Conn) {
+
+	//gets current seed
+	seedFollower := make([]byte, 16)
+	C.readSeed((*C.uchar)(&seedFollower[0]))
+
+	//get data
+	dbSize := int(C.dbSize)
+	tmpdbFollower := make([][]byte, dbSize)
+	for i := range tmpdbFollower {
+		tmpdbFollower[i] = make([]byte, dataLength)
+	}
+	for i := 0; i < dbSize; i++ {
+		C.readData(C.int(i), (*C.uchar)(&tmpdbFollower[i][0]))
+	}
+
+	//receive seed from leader
+	seedLeader := make([]byte, 16)
+	_, err := leaderWorkerConnection.Read(seedLeader)
+	if err != nil {
+		panic(err)
+	}
+
+	//receive data from leader
+	tmpdbLeader := make([][]byte, dbSize)
+	for i := range tmpdbLeader {
+		tmpdbLeader[i] = make([]byte, dataLength)
+	}
+	for i := 0; i < dbSize; i++ {
+		_, err = leaderWorkerConnection.Read(tmpdbLeader[i])
+		if err != nil {
+			panic(err)
+		}
+	}
+
+	//writes seed to leader
+	_, err = leaderWorkerConnection.Write(seedFollower)
+	if err != nil {
+		panic(err)
+	}
+
+	//write data to leader
+	for i := 0; i < dbSize; i++ {
+		_, err = leaderWorkerConnection.Write(tmpdbFollower[i])
+		if err != nil {
+			panic(err)
+		}
+	}
+	//put together the db
+	tmpdb := make([][]byte, dbSize)
+	for i := range tmpdb {
+		tmpdb[i] = make([]byte, dataLength)
+	}
+
+	//get own Ciphers
+	ciphersFollowers := make([]*C.uchar, dbSize)
+	for i := 0; i < dbSize; i++ {
+		ciphersFollowers[i] = (*C.uchar)(C.malloc(16))
+	}
+	for i := 0; i < dbSize; i++ {
+		C.getCipher(0, C.int(i), ciphersFollowers[i])
+	}
+
+	//receive ciphers from leader
+	ciphersLeader := make([]byte, dbSize*16)
+	for i := 0; i < dbSize; i++ {
+		_, err = leaderWorkerConnection.Read(ciphersLeader[i*16:])
+		if err != nil {
+			panic(err)
+		}
+	}
+
+	//send own Ciphers to leader
+	for i := 0; i < dbSize; i++ {
+		_, err = leaderWorkerConnection.Write(C.GoBytes(unsafe.Pointer(ciphersFollowers[i]), 16))
+		if err != nil {
+			panic(err)
+		}
+	}
+
+	//put in ciphers from leader
+	for i := 0; i < dbSize; i++ {
+		C.putCipher(0, C.int(i), (*C.uchar)(&ciphersLeader[i*16]))
+	}
+
+	for i := 0; i < dbSize; i++ {
+		C.decryptRow(C.int(i), (*C.uchar)(&tmpdb[i][0]), (*C.uchar)(&tmpdbLeader[i][0]), (*C.uchar)(&tmpdbFollower[i][0]), (*C.uchar)(&seedLeader[0]), (*C.uchar)(&seedFollower[0]))
+	}
+
+	var tweets []lib.Tweet
+	for i := 0; i < dbSize; i++ {
+		//discard cover message
+		if tmpdb[i][0] == 0 {
+			continue
+		} else {
+			//reconstruct tweet
+			var position int = 0
+			var topics []string
+			var topic string
+			var text string
+			for _, letter := range tmpdb[i] {
+				if string(letter) == ";" {
+					if topic != "" {
+						topics = append(topics, topic)
+						topic = ""
+					}
+					position++
+				} else {
+					if position == 0 {
+						if string(letter) == "," {
+							topics = append(topics, topic)
+							topic = ""
+						} else {
+							topic = topic + string(letter)
+						}
+					} else if position == 1 {
+						text = text + string(letter)
+					}
+				}
+			}
+
+			tweet := lib.Tweet{"", -1, topics, text, round}
+
+			tweets = append(tweets, tweet)
+		}
+	}
+
+	//fmt.Println("tweets recovered: ", tweets)
+
+	//sort into read db
+	lib.NewEntries(tweets, 0)
+
+	//reset write db after the tweets were moved to read db
+	C.resetDb()
+}
+
+func addTestTweets() {
+	//creates test tweets
+	tweets := make([]lib.Tweet, 5)
+
+	for i := range tweets {
+		j := i
+		if i == 1 {
+			j = 0
+		}
+		text := "Text " + strconv.Itoa(i)
+		var topics []string
+		topics = append(topics, "Topic "+strconv.Itoa(j))
+		tweets[i] = lib.Tweet{"", -1, topics, text, i}
+	}
+
+	lib.NewEntries(tweets, 0)
+}
+
+func phase3(leaderWorkerConnection net.Conn, wg *sync.WaitGroup) {
+
+	gotClient := make([]byte, 1)
+
+	for {
+		_, err := leaderWorkerConnection.Read(gotClient)
+		if err != nil {
+			panic(err)
+		}
+
+		//this worker is done
+		if gotClient[0] == 0 {
+			wg.Done()
+			return
+		}
+
+		subPhase := make([]byte, 1)
+		_, err = leaderWorkerConnection.Read(subPhase)
+		if err != nil {
+			panic(err)
+		}
+
+		var clientPublicKey [32]byte
+		_, err = leaderWorkerConnection.Read(clientPublicKey[:])
+		if err != nil {
+			panic(err)
+		}
+
+		//gets the client data
+		clientKeys := clientData[clientPublicKey]
+
+		if subPhase[0] == 0 || subPhase[0] == 1 {
+			clientKeys, _ = handlePirQuery(clientKeys, leaderWorkerConnection, int(subPhase[0]), clientPublicKey)
+		}
+
+		getSendTweets(clientKeys, nil, leaderWorkerConnection)
+
+		wantsArchive := make([]byte, 1)
+		_, err = leaderWorkerConnection.Read(wantsArchive)
+		if err != nil {
+			panic(err)
+		}
+
+		if wantsArchive[0] == 1 {
+			_, archiveQuerys := handlePirQuery(clientKeys, leaderWorkerConnection, -1, clientPublicKey)
+			getSendTweets(clientKeys, archiveQuerys, leaderWorkerConnection)
+
+		}
+
+		//saves clientKeys
+		clientData[clientPublicKey] = clientKeys
+	}
+}
+
+func getSendTweets(clientKeys clientKeys, archiveQuerys [][]byte, leaderWorkerConnection net.Conn) {
+	tmpNeededSubscriptions := neededSubscriptions
+	if archiveQuerys != nil {
+		tmpNeededSubscriptions = len(archiveQuerys)
+	}
+	for i := 0; i < tmpNeededSubscriptions; i++ {
+		//gets all requested tweets
+		var tweets []byte
+		if archiveQuerys == nil {
+			tweets = lib.GetTweets(clientKeys.PirQuery[i], dataLength, 0)
+		} else {
+			tweets = lib.GetTweets(archiveQuerys[i], dataLength, 1)
+		}
+
+		//expand sharedSecret so it is of right length
+		expandBy := len(tweets) / 32
+		var expandedSharedSecret []byte
+		for i := 0; i < expandBy; i++ {
+			expandedSharedSecret = append(expandedSharedSecret, clientKeys.SharedSecret[:]...)
+		}
+
+		fmt.Println(expandedSharedSecret)
+
+		//Xor's sharedSecret with all tweets
+		lib.Xor(expandedSharedSecret[:], tweets)
+
+		lib.Xor(tweets, expandedSharedSecret[:])
+
+		//sends tweets to leader
+		tweetsLengthBytes := intToByte(len(tweets))
+
+		_, err := leaderWorkerConnection.Write(tweetsLengthBytes)
+		if err != nil {
+			panic(err)
+		}
+
+		_, err = leaderWorkerConnection.Write(tweets)
+		if err != nil {
+			panic(err)
+		}
+
+	}
+}
+
+func handlePirQuery(clientKeys clientKeys, leaderWorkerConnection net.Conn, subPhase int, clientPublicKey [32]byte) (clientKeys, [][]byte) {
+	archiveNeededSubscriptions := make([]byte, 4)
+	if subPhase == -1 {
+		_, err := leaderWorkerConnection.Read(archiveNeededSubscriptions)
+		if err != nil {
+			panic(err)
+		}
+	}
+
+	//gets the msg length
+	msgLengthBytes := make([]byte, 4)
+	_, err := leaderWorkerConnection.Read(msgLengthBytes)
+	if err != nil {
+		panic(err)
+	}
+	msgLength := byteToInt(msgLengthBytes)
+
+	message := make([]byte, msgLength)
+
+	//gets the message
+	_, err = leaderWorkerConnection.Read(message)
+	if err != nil {
+		panic(err)
+	}
+
+	var decryptNonce [24]byte
+	copy(decryptNonce[:], message[:24])
+	decrypted, ok := box.Open(nil, message[24:], &decryptNonce, &clientPublicKey, followerPrivateKey)
+	if !ok {
+		panic("pirQuery decryption not ok")
+	}
+	//gets sharedSecret
+	if subPhase == 0 {
+		//bs!
+		var newSharedSecret [32]byte
+		for index := 0; index < 32; index++ {
+			newSharedSecret[index] = decrypted[index]
+		}
+		clientKeys.SharedSecret = newSharedSecret
+		decrypted = decrypted[32:]
+
+		//follower updates sharedSecret
+	} else if subPhase == 1 {
+		sharedSecret := clientKeys.SharedSecret
+		sharedSecret = sha256.Sum256(sharedSecret[:])
+		clientKeys.SharedSecret = sharedSecret
+	}
+
+	//follower expects pirQuery
+
+	//transforms byteArray to ints of wanted topics
+	pirQueryFlattened := decrypted
+	tmpNeededSubscriptions := neededSubscriptions
+	tmpTopicAmount := topicAmount
+	if subPhase == -1 {
+		tmpNeededSubscriptions = byteToInt(archiveNeededSubscriptions)
+		_, tmpTopicAmount = lib.GetTopicList(1)
+	}
+	pirQuerys := make([][]byte, tmpNeededSubscriptions)
+	for i := range pirQuerys {
+		pirQuerys[i] = make([]byte, tmpTopicAmount)
+	}
+	for i := 0; i < tmpNeededSubscriptions; i++ {
+		pirQuerys[i] = pirQueryFlattened[i*tmpTopicAmount : (i+1)*tmpTopicAmount]
+	}
+
+	//sets the pirQuery for the client in case whe are not archiving
+	if subPhase != -1 {
+		clientKeys.PirQuery = pirQuerys
+	}
+
+	return clientKeys, pirQuerys
+}
+
+func transformBytesToStringArray(topicsAsBytes []byte) []string {
+	var topics []string
+	var topic string
+	var position int = 0
+	for _, letter := range topicsAsBytes {
+		if string(letter) == "," {
+			topics[position] = topic
+			topic = ""
+			position++
+		} else {
+			topic = topic + string(letter)
+		}
+	}
+	return topics
+}
+
+func byteToInt(myBytes []byte) (x int) {
+	x = int(myBytes[3])<<24 + int(myBytes[2])<<16 + int(myBytes[1])<<8 + int(myBytes[0])
+	return
+}
+
+func intToByte(myInt int) (retBytes []byte) {
+	retBytes = make([]byte, 4)
+	retBytes[3] = byte((myInt >> 24) & 0xff)
+	retBytes[2] = byte((myInt >> 16) & 0xff)
+	retBytes[1] = byte((myInt >> 8) & 0xff)
+	retBytes[0] = byte(myInt & 0xff)
+	return
+}

+ 7 - 0
go.mod

@@ -0,0 +1,7 @@
+module 2PPS
+
+go 1.17
+
+require golang.org/x/crypto v0.0.0-20220128200615-198e4374d7ed
+
+require golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 // indirect

+ 4 - 0
go.sum

@@ -0,0 +1,4 @@
+golang.org/x/crypto v0.0.0-20220128200615-198e4374d7ed h1:YoWVYYAfvQ4ddHv3OKmIvX7NCAhFGTj62VP2l2kfBbA=
+golang.org/x/crypto v0.0.0-20220128200615-198e4374d7ed/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

+ 28 - 0
gui/.gitignore

@@ -0,0 +1,28 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+.DS_Store
+dist
+dist-ssr
+coverage
+*.local
+
+/cypress/videos/
+/cypress/screenshots/
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?

+ 3 - 0
gui/.vscode/extensions.json

@@ -0,0 +1,3 @@
+{
+  "recommendations": ["johnsoncodehk.volar", "johnsoncodehk.vscode-typescript-vue-plugin"]
+}

+ 29 - 0
gui/README.md

@@ -0,0 +1,29 @@
+# gui
+
+This template should help get you started developing with Vue 3 in Vite.
+
+## Recommended IDE Setup
+
+[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=johnsoncodehk.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=johnsoncodehk.vscode-typescript-vue-plugin).
+
+## Customize configuration
+
+See [Vite Configuration Reference](https://vitejs.dev/config/).
+
+## Project Setup
+
+```sh
+npm install
+```
+
+### Compile and Hot-Reload for Development
+
+```sh
+npm run dev
+```
+
+### Compile and Minify for Production
+
+```sh
+npm run build
+```

+ 13 - 0
gui/index.html

@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <link rel="icon" href="/favicon.ico" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <title>2PPS</title>
+  </head>
+  <body>
+    <div id="app"></div>
+    <script type="module" src="/src/main.js"></script>
+  </body>
+</html>

+ 1143 - 0
gui/package-lock.json

@@ -0,0 +1,1143 @@
+{
+  "name": "gui",
+  "version": "0.0.0",
+  "lockfileVersion": 2,
+  "requires": true,
+  "packages": {
+    "": {
+      "name": "gui",
+      "version": "0.0.0",
+      "dependencies": {
+        "vue": "^3.2.31"
+      },
+      "devDependencies": {
+        "@vitejs/plugin-vue": "^2.2.2",
+        "vite": "^2.8.4"
+      }
+    },
+    "node_modules/@babel/parser": {
+      "version": "7.17.7",
+      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.7.tgz",
+      "integrity": "sha512-bm3AQf45vR4gKggRfvJdYJ0gFLoCbsPxiFLSH6hTVYABptNHY6l9NrhnucVjQ/X+SPtLANT9lc0fFhikj+VBRA==",
+      "bin": {
+        "parser": "bin/babel-parser.js"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/@vitejs/plugin-vue": {
+      "version": "2.2.4",
+      "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-2.2.4.tgz",
+      "integrity": "sha512-ev9AOlp0ljCaDkFZF3JwC/pD2N4Hh+r5srl5JHM6BKg5+99jiiK0rE/XaRs3pVm1wzyKkjUy/StBSoXX5fFzcw==",
+      "dev": true,
+      "engines": {
+        "node": ">=12.0.0"
+      },
+      "peerDependencies": {
+        "vite": "^2.5.10",
+        "vue": "^3.2.25"
+      }
+    },
+    "node_modules/@vue/compiler-core": {
+      "version": "3.2.31",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.31.tgz",
+      "integrity": "sha512-aKno00qoA4o+V/kR6i/pE+aP+esng5siNAVQ422TkBNM6qA4veXiZbSe8OTXHXquEi/f6Akc+nLfB4JGfe4/WQ==",
+      "dependencies": {
+        "@babel/parser": "^7.16.4",
+        "@vue/shared": "3.2.31",
+        "estree-walker": "^2.0.2",
+        "source-map": "^0.6.1"
+      }
+    },
+    "node_modules/@vue/compiler-dom": {
+      "version": "3.2.31",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.31.tgz",
+      "integrity": "sha512-60zIlFfzIDf3u91cqfqy9KhCKIJgPeqxgveH2L+87RcGU/alT6BRrk5JtUso0OibH3O7NXuNOQ0cDc9beT0wrg==",
+      "dependencies": {
+        "@vue/compiler-core": "3.2.31",
+        "@vue/shared": "3.2.31"
+      }
+    },
+    "node_modules/@vue/compiler-sfc": {
+      "version": "3.2.31",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.31.tgz",
+      "integrity": "sha512-748adc9msSPGzXgibHiO6T7RWgfnDcVQD+VVwYgSsyyY8Ans64tALHZANrKtOzvkwznV/F4H7OAod/jIlp/dkQ==",
+      "dependencies": {
+        "@babel/parser": "^7.16.4",
+        "@vue/compiler-core": "3.2.31",
+        "@vue/compiler-dom": "3.2.31",
+        "@vue/compiler-ssr": "3.2.31",
+        "@vue/reactivity-transform": "3.2.31",
+        "@vue/shared": "3.2.31",
+        "estree-walker": "^2.0.2",
+        "magic-string": "^0.25.7",
+        "postcss": "^8.1.10",
+        "source-map": "^0.6.1"
+      }
+    },
+    "node_modules/@vue/compiler-ssr": {
+      "version": "3.2.31",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.31.tgz",
+      "integrity": "sha512-mjN0rqig+A8TVDnsGPYJM5dpbjlXeHUm2oZHZwGyMYiGT/F4fhJf/cXy8QpjnLQK4Y9Et4GWzHn9PS8AHUnSkw==",
+      "dependencies": {
+        "@vue/compiler-dom": "3.2.31",
+        "@vue/shared": "3.2.31"
+      }
+    },
+    "node_modules/@vue/reactivity": {
+      "version": "3.2.31",
+      "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.31.tgz",
+      "integrity": "sha512-HVr0l211gbhpEKYr2hYe7hRsV91uIVGFYNHj73njbARVGHQvIojkImKMaZNDdoDZOIkMsBc9a1sMqR+WZwfSCw==",
+      "dependencies": {
+        "@vue/shared": "3.2.31"
+      }
+    },
+    "node_modules/@vue/reactivity-transform": {
+      "version": "3.2.31",
+      "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.31.tgz",
+      "integrity": "sha512-uS4l4z/W7wXdI+Va5pgVxBJ345wyGFKvpPYtdSgvfJfX/x2Ymm6ophQlXXB6acqGHtXuBqNyyO3zVp9b1r0MOA==",
+      "dependencies": {
+        "@babel/parser": "^7.16.4",
+        "@vue/compiler-core": "3.2.31",
+        "@vue/shared": "3.2.31",
+        "estree-walker": "^2.0.2",
+        "magic-string": "^0.25.7"
+      }
+    },
+    "node_modules/@vue/runtime-core": {
+      "version": "3.2.31",
+      "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.31.tgz",
+      "integrity": "sha512-Kcog5XmSY7VHFEMuk4+Gap8gUssYMZ2+w+cmGI6OpZWYOEIcbE0TPzzPHi+8XTzAgx1w/ZxDFcXhZeXN5eKWsA==",
+      "dependencies": {
+        "@vue/reactivity": "3.2.31",
+        "@vue/shared": "3.2.31"
+      }
+    },
+    "node_modules/@vue/runtime-dom": {
+      "version": "3.2.31",
+      "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.31.tgz",
+      "integrity": "sha512-N+o0sICVLScUjfLG7u9u5XCjvmsexAiPt17GNnaWHJUfsKed5e85/A3SWgKxzlxx2SW/Hw7RQxzxbXez9PtY3g==",
+      "dependencies": {
+        "@vue/runtime-core": "3.2.31",
+        "@vue/shared": "3.2.31",
+        "csstype": "^2.6.8"
+      }
+    },
+    "node_modules/@vue/server-renderer": {
+      "version": "3.2.31",
+      "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.31.tgz",
+      "integrity": "sha512-8CN3Zj2HyR2LQQBHZ61HexF5NReqngLT3oahyiVRfSSvak+oAvVmu8iNLSu6XR77Ili2AOpnAt1y8ywjjqtmkg==",
+      "dependencies": {
+        "@vue/compiler-ssr": "3.2.31",
+        "@vue/shared": "3.2.31"
+      },
+      "peerDependencies": {
+        "vue": "3.2.31"
+      }
+    },
+    "node_modules/@vue/shared": {
+      "version": "3.2.31",
+      "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.31.tgz",
+      "integrity": "sha512-ymN2pj6zEjiKJZbrf98UM2pfDd6F2H7ksKw7NDt/ZZ1fh5Ei39X5tABugtT03ZRlWd9imccoK0hE8hpjpU7irQ=="
+    },
+    "node_modules/csstype": {
+      "version": "2.6.20",
+      "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.20.tgz",
+      "integrity": "sha512-/WwNkdXfckNgw6S5R125rrW8ez139lBHWouiBvX8dfMFtcn6V81REDqnH7+CRpRipfYlyU1CmOnOxrmGcFOjeA=="
+    },
+    "node_modules/esbuild": {
+      "version": "0.14.27",
+      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.27.tgz",
+      "integrity": "sha512-MZQt5SywZS3hA9fXnMhR22dv0oPGh6QtjJRIYbgL1AeqAoQZE+Qn5ppGYQAoHv/vq827flj4tIJ79Mrdiwk46Q==",
+      "dev": true,
+      "hasInstallScript": true,
+      "bin": {
+        "esbuild": "bin/esbuild"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "optionalDependencies": {
+        "esbuild-android-64": "0.14.27",
+        "esbuild-android-arm64": "0.14.27",
+        "esbuild-darwin-64": "0.14.27",
+        "esbuild-darwin-arm64": "0.14.27",
+        "esbuild-freebsd-64": "0.14.27",
+        "esbuild-freebsd-arm64": "0.14.27",
+        "esbuild-linux-32": "0.14.27",
+        "esbuild-linux-64": "0.14.27",
+        "esbuild-linux-arm": "0.14.27",
+        "esbuild-linux-arm64": "0.14.27",
+        "esbuild-linux-mips64le": "0.14.27",
+        "esbuild-linux-ppc64le": "0.14.27",
+        "esbuild-linux-riscv64": "0.14.27",
+        "esbuild-linux-s390x": "0.14.27",
+        "esbuild-netbsd-64": "0.14.27",
+        "esbuild-openbsd-64": "0.14.27",
+        "esbuild-sunos-64": "0.14.27",
+        "esbuild-windows-32": "0.14.27",
+        "esbuild-windows-64": "0.14.27",
+        "esbuild-windows-arm64": "0.14.27"
+      }
+    },
+    "node_modules/esbuild-android-64": {
+      "version": "0.14.27",
+      "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.27.tgz",
+      "integrity": "sha512-LuEd4uPuj/16Y8j6kqy3Z2E9vNY9logfq8Tq+oTE2PZVuNs3M1kj5Qd4O95ee66yDGb3isaOCV7sOLDwtMfGaQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-android-arm64": {
+      "version": "0.14.27",
+      "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.27.tgz",
+      "integrity": "sha512-E8Ktwwa6vX8q7QeJmg8yepBYXaee50OdQS3BFtEHKrzbV45H4foMOeEE7uqdjGQZFBap5VAqo7pvjlyA92wznQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-darwin-64": {
+      "version": "0.14.27",
+      "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.27.tgz",
+      "integrity": "sha512-czw/kXl/1ZdenPWfw9jDc5iuIYxqUxgQ/Q+hRd4/3udyGGVI31r29LCViN2bAJgGvQkqyLGVcG03PJPEXQ5i2g==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-darwin-arm64": {
+      "version": "0.14.27",
+      "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.27.tgz",
+      "integrity": "sha512-BEsv2U2U4o672oV8+xpXNxN9bgqRCtddQC6WBh4YhXKDcSZcdNh7+6nS+DM2vu7qWIWNA4JbRG24LUUYXysimQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-freebsd-64": {
+      "version": "0.14.27",
+      "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.27.tgz",
+      "integrity": "sha512-7FeiFPGBo+ga+kOkDxtPmdPZdayrSzsV9pmfHxcyLKxu+3oTcajeZlOO1y9HW+t5aFZPiv7czOHM4KNd0tNwCA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-freebsd-arm64": {
+      "version": "0.14.27",
+      "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.27.tgz",
+      "integrity": "sha512-8CK3++foRZJluOWXpllG5zwAVlxtv36NpHfsbWS7TYlD8S+QruXltKlXToc/5ZNzBK++l6rvRKELu/puCLc7jA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-linux-32": {
+      "version": "0.14.27",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.27.tgz",
+      "integrity": "sha512-qhNYIcT+EsYSBClZ5QhLzFzV5iVsP1YsITqblSaztr3+ZJUI+GoK8aXHyzKd7/CKKuK93cxEMJPpfi1dfsOfdw==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-linux-64": {
+      "version": "0.14.27",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.27.tgz",
+      "integrity": "sha512-ESjck9+EsHoTaKWlFKJpPZRN26uiav5gkI16RuI8WBxUdLrrAlYuYSndxxKgEn1csd968BX/8yQZATYf/9+/qg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-linux-arm": {
+      "version": "0.14.27",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.27.tgz",
+      "integrity": "sha512-JnnmgUBdqLQO9hoNZQqNHFWlNpSX82vzB3rYuCJMhtkuaWQEmQz6Lec1UIxJdC38ifEghNTBsF9bbe8dFilnCw==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-linux-arm64": {
+      "version": "0.14.27",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.27.tgz",
+      "integrity": "sha512-no6Mi17eV2tHlJnqBHRLekpZ2/VYx+NfGxKcBE/2xOMYwctsanCaXxw4zapvNrGE9X38vefVXLz6YCF8b1EHiQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-linux-mips64le": {
+      "version": "0.14.27",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.27.tgz",
+      "integrity": "sha512-NolWP2uOvIJpbwpsDbwfeExZOY1bZNlWE/kVfkzLMsSgqeVcl5YMen/cedRe9mKnpfLli+i0uSp7N+fkKNU27A==",
+      "cpu": [
+        "mips64el"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-linux-ppc64le": {
+      "version": "0.14.27",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.27.tgz",
+      "integrity": "sha512-/7dTjDvXMdRKmsSxKXeWyonuGgblnYDn0MI1xDC7J1VQXny8k1qgNp6VmrlsawwnsymSUUiThhkJsI+rx0taNA==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-linux-riscv64": {
+      "version": "0.14.27",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.27.tgz",
+      "integrity": "sha512-D+aFiUzOJG13RhrSmZgrcFaF4UUHpqj7XSKrIiCXIj1dkIkFqdrmqMSOtSs78dOtObWiOrFCDDzB24UyeEiNGg==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-linux-s390x": {
+      "version": "0.14.27",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.27.tgz",
+      "integrity": "sha512-CD/D4tj0U4UQjELkdNlZhQ8nDHU5rBn6NGp47Hiz0Y7/akAY5i0oGadhEIg0WCY/HYVXFb3CsSPPwaKcTOW3bg==",
+      "cpu": [
+        "s390x"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-netbsd-64": {
+      "version": "0.14.27",
+      "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.27.tgz",
+      "integrity": "sha512-h3mAld69SrO1VoaMpYl3a5FNdGRE/Nqc+E8VtHOag4tyBwhCQXxtvDDOAKOUQexBGca0IuR6UayQ4ntSX5ij1Q==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "netbsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-openbsd-64": {
+      "version": "0.14.27",
+      "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.27.tgz",
+      "integrity": "sha512-xwSje6qIZaDHXWoPpIgvL+7fC6WeubHHv18tusLYMwL+Z6bEa4Pbfs5IWDtQdHkArtfxEkIZz77944z8MgDxGw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "openbsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-sunos-64": {
+      "version": "0.14.27",
+      "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.27.tgz",
+      "integrity": "sha512-/nBVpWIDjYiyMhuqIqbXXsxBc58cBVH9uztAOIfWShStxq9BNBik92oPQPJ57nzWXRNKQUEFWr4Q98utDWz7jg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "sunos"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-windows-32": {
+      "version": "0.14.27",
+      "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.27.tgz",
+      "integrity": "sha512-Q9/zEjhZJ4trtWhFWIZvS/7RUzzi8rvkoaS9oiizkHTTKd8UxFwn/Mm2OywsAfYymgUYm8+y2b+BKTNEFxUekw==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-windows-64": {
+      "version": "0.14.27",
+      "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.27.tgz",
+      "integrity": "sha512-b3y3vTSl5aEhWHK66ngtiS/c6byLf6y/ZBvODH1YkBM+MGtVL6jN38FdHUsZasCz9gFwYs/lJMVY9u7GL6wfYg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-windows-arm64": {
+      "version": "0.14.27",
+      "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.27.tgz",
+      "integrity": "sha512-I/reTxr6TFMcR5qbIkwRGvldMIaiBu2+MP0LlD7sOlNXrfqIl9uNjsuxFPGEG4IRomjfQ5q8WT+xlF/ySVkqKg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/estree-walker": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
+      "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
+    },
+    "node_modules/fsevents": {
+      "version": "2.3.2",
+      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+      "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+      "dev": true,
+      "hasInstallScript": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+      }
+    },
+    "node_modules/function-bind": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+      "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
+      "dev": true
+    },
+    "node_modules/has": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+      "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+      "dev": true,
+      "dependencies": {
+        "function-bind": "^1.1.1"
+      },
+      "engines": {
+        "node": ">= 0.4.0"
+      }
+    },
+    "node_modules/is-core-module": {
+      "version": "2.8.1",
+      "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz",
+      "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==",
+      "dev": true,
+      "dependencies": {
+        "has": "^1.0.3"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/magic-string": {
+      "version": "0.25.9",
+      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz",
+      "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==",
+      "dependencies": {
+        "sourcemap-codec": "^1.4.8"
+      }
+    },
+    "node_modules/nanoid": {
+      "version": "3.3.1",
+      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz",
+      "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==",
+      "bin": {
+        "nanoid": "bin/nanoid.cjs"
+      },
+      "engines": {
+        "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+      }
+    },
+    "node_modules/path-parse": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+      "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+      "dev": true
+    },
+    "node_modules/picocolors": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
+      "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
+    },
+    "node_modules/postcss": {
+      "version": "8.4.12",
+      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.12.tgz",
+      "integrity": "sha512-lg6eITwYe9v6Hr5CncVbK70SoioNQIq81nsaG86ev5hAidQvmOeETBqs7jm43K2F5/Ley3ytDtriImV6TpNiSg==",
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/postcss/"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/postcss"
+        }
+      ],
+      "dependencies": {
+        "nanoid": "^3.3.1",
+        "picocolors": "^1.0.0",
+        "source-map-js": "^1.0.2"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >=14"
+      }
+    },
+    "node_modules/resolve": {
+      "version": "1.22.0",
+      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz",
+      "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==",
+      "dev": true,
+      "dependencies": {
+        "is-core-module": "^2.8.1",
+        "path-parse": "^1.0.7",
+        "supports-preserve-symlinks-flag": "^1.0.0"
+      },
+      "bin": {
+        "resolve": "bin/resolve"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/rollup": {
+      "version": "2.70.1",
+      "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.70.1.tgz",
+      "integrity": "sha512-CRYsI5EuzLbXdxC6RnYhOuRdtz4bhejPMSWjsFLfVM/7w/85n2szZv6yExqUXsBdz5KT8eoubeyDUDjhLHEslA==",
+      "dev": true,
+      "bin": {
+        "rollup": "dist/bin/rollup"
+      },
+      "engines": {
+        "node": ">=10.0.0"
+      },
+      "optionalDependencies": {
+        "fsevents": "~2.3.2"
+      }
+    },
+    "node_modules/source-map": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/source-map-js": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
+      "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/sourcemap-codec": {
+      "version": "1.4.8",
+      "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
+      "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA=="
+    },
+    "node_modules/supports-preserve-symlinks-flag": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+      "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/vite": {
+      "version": "2.8.6",
+      "resolved": "https://registry.npmjs.org/vite/-/vite-2.8.6.tgz",
+      "integrity": "sha512-e4H0QpludOVKkmOsRyqQ7LTcMUDF3mcgyNU4lmi0B5JUbe0ZxeBBl8VoZ8Y6Rfn9eFKYtdXNPcYK97ZwH+K2ug==",
+      "dev": true,
+      "dependencies": {
+        "esbuild": "^0.14.14",
+        "postcss": "^8.4.6",
+        "resolve": "^1.22.0",
+        "rollup": "^2.59.0"
+      },
+      "bin": {
+        "vite": "bin/vite.js"
+      },
+      "engines": {
+        "node": ">=12.2.0"
+      },
+      "optionalDependencies": {
+        "fsevents": "~2.3.2"
+      },
+      "peerDependencies": {
+        "less": "*",
+        "sass": "*",
+        "stylus": "*"
+      },
+      "peerDependenciesMeta": {
+        "less": {
+          "optional": true
+        },
+        "sass": {
+          "optional": true
+        },
+        "stylus": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/vue": {
+      "version": "3.2.31",
+      "resolved": "https://registry.npmjs.org/vue/-/vue-3.2.31.tgz",
+      "integrity": "sha512-odT3W2tcffTiQCy57nOT93INw1auq5lYLLYtWpPYQQYQOOdHiqFct9Xhna6GJ+pJQaF67yZABraH47oywkJgFw==",
+      "dependencies": {
+        "@vue/compiler-dom": "3.2.31",
+        "@vue/compiler-sfc": "3.2.31",
+        "@vue/runtime-dom": "3.2.31",
+        "@vue/server-renderer": "3.2.31",
+        "@vue/shared": "3.2.31"
+      }
+    }
+  },
+  "dependencies": {
+    "@babel/parser": {
+      "version": "7.17.7",
+      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.7.tgz",
+      "integrity": "sha512-bm3AQf45vR4gKggRfvJdYJ0gFLoCbsPxiFLSH6hTVYABptNHY6l9NrhnucVjQ/X+SPtLANT9lc0fFhikj+VBRA=="
+    },
+    "@vitejs/plugin-vue": {
+      "version": "2.2.4",
+      "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-2.2.4.tgz",
+      "integrity": "sha512-ev9AOlp0ljCaDkFZF3JwC/pD2N4Hh+r5srl5JHM6BKg5+99jiiK0rE/XaRs3pVm1wzyKkjUy/StBSoXX5fFzcw==",
+      "dev": true,
+      "requires": {}
+    },
+    "@vue/compiler-core": {
+      "version": "3.2.31",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.31.tgz",
+      "integrity": "sha512-aKno00qoA4o+V/kR6i/pE+aP+esng5siNAVQ422TkBNM6qA4veXiZbSe8OTXHXquEi/f6Akc+nLfB4JGfe4/WQ==",
+      "requires": {
+        "@babel/parser": "^7.16.4",
+        "@vue/shared": "3.2.31",
+        "estree-walker": "^2.0.2",
+        "source-map": "^0.6.1"
+      }
+    },
+    "@vue/compiler-dom": {
+      "version": "3.2.31",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.31.tgz",
+      "integrity": "sha512-60zIlFfzIDf3u91cqfqy9KhCKIJgPeqxgveH2L+87RcGU/alT6BRrk5JtUso0OibH3O7NXuNOQ0cDc9beT0wrg==",
+      "requires": {
+        "@vue/compiler-core": "3.2.31",
+        "@vue/shared": "3.2.31"
+      }
+    },
+    "@vue/compiler-sfc": {
+      "version": "3.2.31",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.31.tgz",
+      "integrity": "sha512-748adc9msSPGzXgibHiO6T7RWgfnDcVQD+VVwYgSsyyY8Ans64tALHZANrKtOzvkwznV/F4H7OAod/jIlp/dkQ==",
+      "requires": {
+        "@babel/parser": "^7.16.4",
+        "@vue/compiler-core": "3.2.31",
+        "@vue/compiler-dom": "3.2.31",
+        "@vue/compiler-ssr": "3.2.31",
+        "@vue/reactivity-transform": "3.2.31",
+        "@vue/shared": "3.2.31",
+        "estree-walker": "^2.0.2",
+        "magic-string": "^0.25.7",
+        "postcss": "^8.1.10",
+        "source-map": "^0.6.1"
+      }
+    },
+    "@vue/compiler-ssr": {
+      "version": "3.2.31",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.31.tgz",
+      "integrity": "sha512-mjN0rqig+A8TVDnsGPYJM5dpbjlXeHUm2oZHZwGyMYiGT/F4fhJf/cXy8QpjnLQK4Y9Et4GWzHn9PS8AHUnSkw==",
+      "requires": {
+        "@vue/compiler-dom": "3.2.31",
+        "@vue/shared": "3.2.31"
+      }
+    },
+    "@vue/reactivity": {
+      "version": "3.2.31",
+      "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.31.tgz",
+      "integrity": "sha512-HVr0l211gbhpEKYr2hYe7hRsV91uIVGFYNHj73njbARVGHQvIojkImKMaZNDdoDZOIkMsBc9a1sMqR+WZwfSCw==",
+      "requires": {
+        "@vue/shared": "3.2.31"
+      }
+    },
+    "@vue/reactivity-transform": {
+      "version": "3.2.31",
+      "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.31.tgz",
+      "integrity": "sha512-uS4l4z/W7wXdI+Va5pgVxBJ345wyGFKvpPYtdSgvfJfX/x2Ymm6ophQlXXB6acqGHtXuBqNyyO3zVp9b1r0MOA==",
+      "requires": {
+        "@babel/parser": "^7.16.4",
+        "@vue/compiler-core": "3.2.31",
+        "@vue/shared": "3.2.31",
+        "estree-walker": "^2.0.2",
+        "magic-string": "^0.25.7"
+      }
+    },
+    "@vue/runtime-core": {
+      "version": "3.2.31",
+      "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.31.tgz",
+      "integrity": "sha512-Kcog5XmSY7VHFEMuk4+Gap8gUssYMZ2+w+cmGI6OpZWYOEIcbE0TPzzPHi+8XTzAgx1w/ZxDFcXhZeXN5eKWsA==",
+      "requires": {
+        "@vue/reactivity": "3.2.31",
+        "@vue/shared": "3.2.31"
+      }
+    },
+    "@vue/runtime-dom": {
+      "version": "3.2.31",
+      "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.31.tgz",
+      "integrity": "sha512-N+o0sICVLScUjfLG7u9u5XCjvmsexAiPt17GNnaWHJUfsKed5e85/A3SWgKxzlxx2SW/Hw7RQxzxbXez9PtY3g==",
+      "requires": {
+        "@vue/runtime-core": "3.2.31",
+        "@vue/shared": "3.2.31",
+        "csstype": "^2.6.8"
+      }
+    },
+    "@vue/server-renderer": {
+      "version": "3.2.31",
+      "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.31.tgz",
+      "integrity": "sha512-8CN3Zj2HyR2LQQBHZ61HexF5NReqngLT3oahyiVRfSSvak+oAvVmu8iNLSu6XR77Ili2AOpnAt1y8ywjjqtmkg==",
+      "requires": {
+        "@vue/compiler-ssr": "3.2.31",
+        "@vue/shared": "3.2.31"
+      }
+    },
+    "@vue/shared": {
+      "version": "3.2.31",
+      "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.31.tgz",
+      "integrity": "sha512-ymN2pj6zEjiKJZbrf98UM2pfDd6F2H7ksKw7NDt/ZZ1fh5Ei39X5tABugtT03ZRlWd9imccoK0hE8hpjpU7irQ=="
+    },
+    "csstype": {
+      "version": "2.6.20",
+      "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.20.tgz",
+      "integrity": "sha512-/WwNkdXfckNgw6S5R125rrW8ez139lBHWouiBvX8dfMFtcn6V81REDqnH7+CRpRipfYlyU1CmOnOxrmGcFOjeA=="
+    },
+    "esbuild": {
+      "version": "0.14.27",
+      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.27.tgz",
+      "integrity": "sha512-MZQt5SywZS3hA9fXnMhR22dv0oPGh6QtjJRIYbgL1AeqAoQZE+Qn5ppGYQAoHv/vq827flj4tIJ79Mrdiwk46Q==",
+      "dev": true,
+      "requires": {
+        "esbuild-android-64": "0.14.27",
+        "esbuild-android-arm64": "0.14.27",
+        "esbuild-darwin-64": "0.14.27",
+        "esbuild-darwin-arm64": "0.14.27",
+        "esbuild-freebsd-64": "0.14.27",
+        "esbuild-freebsd-arm64": "0.14.27",
+        "esbuild-linux-32": "0.14.27",
+        "esbuild-linux-64": "0.14.27",
+        "esbuild-linux-arm": "0.14.27",
+        "esbuild-linux-arm64": "0.14.27",
+        "esbuild-linux-mips64le": "0.14.27",
+        "esbuild-linux-ppc64le": "0.14.27",
+        "esbuild-linux-riscv64": "0.14.27",
+        "esbuild-linux-s390x": "0.14.27",
+        "esbuild-netbsd-64": "0.14.27",
+        "esbuild-openbsd-64": "0.14.27",
+        "esbuild-sunos-64": "0.14.27",
+        "esbuild-windows-32": "0.14.27",
+        "esbuild-windows-64": "0.14.27",
+        "esbuild-windows-arm64": "0.14.27"
+      }
+    },
+    "esbuild-android-64": {
+      "version": "0.14.27",
+      "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.27.tgz",
+      "integrity": "sha512-LuEd4uPuj/16Y8j6kqy3Z2E9vNY9logfq8Tq+oTE2PZVuNs3M1kj5Qd4O95ee66yDGb3isaOCV7sOLDwtMfGaQ==",
+      "dev": true,
+      "optional": true
+    },
+    "esbuild-android-arm64": {
+      "version": "0.14.27",
+      "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.27.tgz",
+      "integrity": "sha512-E8Ktwwa6vX8q7QeJmg8yepBYXaee50OdQS3BFtEHKrzbV45H4foMOeEE7uqdjGQZFBap5VAqo7pvjlyA92wznQ==",
+      "dev": true,
+      "optional": true
+    },
+    "esbuild-darwin-64": {
+      "version": "0.14.27",
+      "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.27.tgz",
+      "integrity": "sha512-czw/kXl/1ZdenPWfw9jDc5iuIYxqUxgQ/Q+hRd4/3udyGGVI31r29LCViN2bAJgGvQkqyLGVcG03PJPEXQ5i2g==",
+      "dev": true,
+      "optional": true
+    },
+    "esbuild-darwin-arm64": {
+      "version": "0.14.27",
+      "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.27.tgz",
+      "integrity": "sha512-BEsv2U2U4o672oV8+xpXNxN9bgqRCtddQC6WBh4YhXKDcSZcdNh7+6nS+DM2vu7qWIWNA4JbRG24LUUYXysimQ==",
+      "dev": true,
+      "optional": true
+    },
+    "esbuild-freebsd-64": {
+      "version": "0.14.27",
+      "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.27.tgz",
+      "integrity": "sha512-7FeiFPGBo+ga+kOkDxtPmdPZdayrSzsV9pmfHxcyLKxu+3oTcajeZlOO1y9HW+t5aFZPiv7czOHM4KNd0tNwCA==",
+      "dev": true,
+      "optional": true
+    },
+    "esbuild-freebsd-arm64": {
+      "version": "0.14.27",
+      "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.27.tgz",
+      "integrity": "sha512-8CK3++foRZJluOWXpllG5zwAVlxtv36NpHfsbWS7TYlD8S+QruXltKlXToc/5ZNzBK++l6rvRKELu/puCLc7jA==",
+      "dev": true,
+      "optional": true
+    },
+    "esbuild-linux-32": {
+      "version": "0.14.27",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.27.tgz",
+      "integrity": "sha512-qhNYIcT+EsYSBClZ5QhLzFzV5iVsP1YsITqblSaztr3+ZJUI+GoK8aXHyzKd7/CKKuK93cxEMJPpfi1dfsOfdw==",
+      "dev": true,
+      "optional": true
+    },
+    "esbuild-linux-64": {
+      "version": "0.14.27",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.27.tgz",
+      "integrity": "sha512-ESjck9+EsHoTaKWlFKJpPZRN26uiav5gkI16RuI8WBxUdLrrAlYuYSndxxKgEn1csd968BX/8yQZATYf/9+/qg==",
+      "dev": true,
+      "optional": true
+    },
+    "esbuild-linux-arm": {
+      "version": "0.14.27",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.27.tgz",
+      "integrity": "sha512-JnnmgUBdqLQO9hoNZQqNHFWlNpSX82vzB3rYuCJMhtkuaWQEmQz6Lec1UIxJdC38ifEghNTBsF9bbe8dFilnCw==",
+      "dev": true,
+      "optional": true
+    },
+    "esbuild-linux-arm64": {
+      "version": "0.14.27",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.27.tgz",
+      "integrity": "sha512-no6Mi17eV2tHlJnqBHRLekpZ2/VYx+NfGxKcBE/2xOMYwctsanCaXxw4zapvNrGE9X38vefVXLz6YCF8b1EHiQ==",
+      "dev": true,
+      "optional": true
+    },
+    "esbuild-linux-mips64le": {
+      "version": "0.14.27",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.27.tgz",
+      "integrity": "sha512-NolWP2uOvIJpbwpsDbwfeExZOY1bZNlWE/kVfkzLMsSgqeVcl5YMen/cedRe9mKnpfLli+i0uSp7N+fkKNU27A==",
+      "dev": true,
+      "optional": true
+    },
+    "esbuild-linux-ppc64le": {
+      "version": "0.14.27",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.27.tgz",
+      "integrity": "sha512-/7dTjDvXMdRKmsSxKXeWyonuGgblnYDn0MI1xDC7J1VQXny8k1qgNp6VmrlsawwnsymSUUiThhkJsI+rx0taNA==",
+      "dev": true,
+      "optional": true
+    },
+    "esbuild-linux-riscv64": {
+      "version": "0.14.27",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.27.tgz",
+      "integrity": "sha512-D+aFiUzOJG13RhrSmZgrcFaF4UUHpqj7XSKrIiCXIj1dkIkFqdrmqMSOtSs78dOtObWiOrFCDDzB24UyeEiNGg==",
+      "dev": true,
+      "optional": true
+    },
+    "esbuild-linux-s390x": {
+      "version": "0.14.27",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.27.tgz",
+      "integrity": "sha512-CD/D4tj0U4UQjELkdNlZhQ8nDHU5rBn6NGp47Hiz0Y7/akAY5i0oGadhEIg0WCY/HYVXFb3CsSPPwaKcTOW3bg==",
+      "dev": true,
+      "optional": true
+    },
+    "esbuild-netbsd-64": {
+      "version": "0.14.27",
+      "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.27.tgz",
+      "integrity": "sha512-h3mAld69SrO1VoaMpYl3a5FNdGRE/Nqc+E8VtHOag4tyBwhCQXxtvDDOAKOUQexBGca0IuR6UayQ4ntSX5ij1Q==",
+      "dev": true,
+      "optional": true
+    },
+    "esbuild-openbsd-64": {
+      "version": "0.14.27",
+      "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.27.tgz",
+      "integrity": "sha512-xwSje6qIZaDHXWoPpIgvL+7fC6WeubHHv18tusLYMwL+Z6bEa4Pbfs5IWDtQdHkArtfxEkIZz77944z8MgDxGw==",
+      "dev": true,
+      "optional": true
+    },
+    "esbuild-sunos-64": {
+      "version": "0.14.27",
+      "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.27.tgz",
+      "integrity": "sha512-/nBVpWIDjYiyMhuqIqbXXsxBc58cBVH9uztAOIfWShStxq9BNBik92oPQPJ57nzWXRNKQUEFWr4Q98utDWz7jg==",
+      "dev": true,
+      "optional": true
+    },
+    "esbuild-windows-32": {
+      "version": "0.14.27",
+      "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.27.tgz",
+      "integrity": "sha512-Q9/zEjhZJ4trtWhFWIZvS/7RUzzi8rvkoaS9oiizkHTTKd8UxFwn/Mm2OywsAfYymgUYm8+y2b+BKTNEFxUekw==",
+      "dev": true,
+      "optional": true
+    },
+    "esbuild-windows-64": {
+      "version": "0.14.27",
+      "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.27.tgz",
+      "integrity": "sha512-b3y3vTSl5aEhWHK66ngtiS/c6byLf6y/ZBvODH1YkBM+MGtVL6jN38FdHUsZasCz9gFwYs/lJMVY9u7GL6wfYg==",
+      "dev": true,
+      "optional": true
+    },
+    "esbuild-windows-arm64": {
+      "version": "0.14.27",
+      "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.27.tgz",
+      "integrity": "sha512-I/reTxr6TFMcR5qbIkwRGvldMIaiBu2+MP0LlD7sOlNXrfqIl9uNjsuxFPGEG4IRomjfQ5q8WT+xlF/ySVkqKg==",
+      "dev": true,
+      "optional": true
+    },
+    "estree-walker": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
+      "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
+    },
+    "fsevents": {
+      "version": "2.3.2",
+      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+      "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+      "dev": true,
+      "optional": true
+    },
+    "function-bind": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+      "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
+      "dev": true
+    },
+    "has": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+      "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+      "dev": true,
+      "requires": {
+        "function-bind": "^1.1.1"
+      }
+    },
+    "is-core-module": {
+      "version": "2.8.1",
+      "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz",
+      "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==",
+      "dev": true,
+      "requires": {
+        "has": "^1.0.3"
+      }
+    },
+    "magic-string": {
+      "version": "0.25.9",
+      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz",
+      "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==",
+      "requires": {
+        "sourcemap-codec": "^1.4.8"
+      }
+    },
+    "nanoid": {
+      "version": "3.3.1",
+      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz",
+      "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw=="
+    },
+    "path-parse": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+      "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+      "dev": true
+    },
+    "picocolors": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
+      "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
+    },
+    "postcss": {
+      "version": "8.4.12",
+      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.12.tgz",
+      "integrity": "sha512-lg6eITwYe9v6Hr5CncVbK70SoioNQIq81nsaG86ev5hAidQvmOeETBqs7jm43K2F5/Ley3ytDtriImV6TpNiSg==",
+      "requires": {
+        "nanoid": "^3.3.1",
+        "picocolors": "^1.0.0",
+        "source-map-js": "^1.0.2"
+      }
+    },
+    "resolve": {
+      "version": "1.22.0",
+      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz",
+      "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==",
+      "dev": true,
+      "requires": {
+        "is-core-module": "^2.8.1",
+        "path-parse": "^1.0.7",
+        "supports-preserve-symlinks-flag": "^1.0.0"
+      }
+    },
+    "rollup": {
+      "version": "2.70.1",
+      "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.70.1.tgz",
+      "integrity": "sha512-CRYsI5EuzLbXdxC6RnYhOuRdtz4bhejPMSWjsFLfVM/7w/85n2szZv6yExqUXsBdz5KT8eoubeyDUDjhLHEslA==",
+      "dev": true,
+      "requires": {
+        "fsevents": "~2.3.2"
+      }
+    },
+    "source-map": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
+    },
+    "source-map-js": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
+      "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw=="
+    },
+    "sourcemap-codec": {
+      "version": "1.4.8",
+      "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
+      "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA=="
+    },
+    "supports-preserve-symlinks-flag": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+      "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+      "dev": true
+    },
+    "vite": {
+      "version": "2.8.6",
+      "resolved": "https://registry.npmjs.org/vite/-/vite-2.8.6.tgz",
+      "integrity": "sha512-e4H0QpludOVKkmOsRyqQ7LTcMUDF3mcgyNU4lmi0B5JUbe0ZxeBBl8VoZ8Y6Rfn9eFKYtdXNPcYK97ZwH+K2ug==",
+      "dev": true,
+      "requires": {
+        "esbuild": "^0.14.14",
+        "fsevents": "~2.3.2",
+        "postcss": "^8.4.6",
+        "resolve": "^1.22.0",
+        "rollup": "^2.59.0"
+      }
+    },
+    "vue": {
+      "version": "3.2.31",
+      "resolved": "https://registry.npmjs.org/vue/-/vue-3.2.31.tgz",
+      "integrity": "sha512-odT3W2tcffTiQCy57nOT93INw1auq5lYLLYtWpPYQQYQOOdHiqFct9Xhna6GJ+pJQaF67yZABraH47oywkJgFw==",
+      "requires": {
+        "@vue/compiler-dom": "3.2.31",
+        "@vue/compiler-sfc": "3.2.31",
+        "@vue/runtime-dom": "3.2.31",
+        "@vue/server-renderer": "3.2.31",
+        "@vue/shared": "3.2.31"
+      }
+    }
+  }
+}

+ 16 - 0
gui/package.json

@@ -0,0 +1,16 @@
+{
+  "name": "gui",
+  "version": "0.0.0",
+  "scripts": {
+    "dev": "vite",
+    "build": "vite build",
+    "preview": "vite preview --port 5050"
+  },
+  "dependencies": {
+    "vue": "^3.2.31"
+  },
+  "devDependencies": {
+    "@vitejs/plugin-vue": "^2.2.2",
+    "vite": "^2.8.4"
+  }
+}

BIN
gui/public/favicon.ico


+ 81 - 0
gui/src/App.vue

@@ -0,0 +1,81 @@
+<template>
+<div class="main-grid">
+<div class="head">2PPS
+  	<form>
+    <input type="text" ref="my_input">
+    <button @click.prevent="submitTweet()">Submit</button>
+  </form>
+</div>
+<div class="topicLists">
+  <topicLists id="topicLists"></topicLists>
+</div>
+<div class="tweets">
+  <ul>
+    <Tweet
+      v-for="(tweet, index) in tweets"
+      :key="tweet.id"
+      :topics="tweet.topics"
+      :text="tweet.text"
+    ></Tweet>
+  </ul>
+  </div>
+</div>
+<div class="ArchivedTweets">
+</div>
+ 
+</template>
+
+<script>
+import topicLists from './components/TopicLists.vue'
+import Tweet from './components/Tweet.vue'
+
+export default {
+  components: { Tweet },
+  data() {
+    return {
+      tweetToSubmit: '',
+      tweets : [
+        {
+          id: 1,
+          topics: "house",
+          text: "asd"
+        },
+        {
+          id: 2,
+          topics: "mouse",
+          text: "asdds"
+        }
+      ]
+    }
+  },
+  methods: {
+    submitTweet () {
+      this.tweetToSubmit = this.$refs.my_input.value
+    }
+  }
+}
+
+</script>
+
+<style>
+@import './assets/base.css';
+
+header {
+  line-height: 1.5;
+  text-align: center;
+  font-size: large;
+}
+
+.topicLists {
+  float : left;
+  margin: 0 1.5%;
+  width: 30%;
+}
+
+.tweets {
+  float : left;
+  margin: 0 1.5%;
+  width: 30%;
+}
+
+</style>

+ 74 - 0
gui/src/assets/base.css

@@ -0,0 +1,74 @@
+/* color palette from <https://github.com/vuejs/theme> */
+:root {
+  --vt-c-white: #ffffff;
+  --vt-c-white-soft: #f8f8f8;
+  --vt-c-white-mute: #f2f2f2;
+
+  --vt-c-black: #181818;
+  --vt-c-black-soft: #222222;
+  --vt-c-black-mute: #282828;
+
+  --vt-c-indigo: #2c3e50;
+
+  --vt-c-divider-light-1: rgba(60, 60, 60, 0.29);
+  --vt-c-divider-light-2: rgba(60, 60, 60, 0.12);
+  --vt-c-divider-dark-1: rgba(84, 84, 84, 0.65);
+  --vt-c-divider-dark-2: rgba(84, 84, 84, 0.48);
+
+  --vt-c-text-light-1: var(--vt-c-indigo);
+  --vt-c-text-light-2: rgba(60, 60, 60, 0.66);
+  --vt-c-text-dark-1: var(--vt-c-white);
+  --vt-c-text-dark-2: rgba(235, 235, 235, 0.64);
+}
+
+/* semantic color variables for this project */
+:root {
+  --color-background: var(--vt-c-white);
+  --color-background-soft: var(--vt-c-white-soft);
+  --color-background-mute: var(--vt-c-white-mute);
+
+  --color-border: var(--vt-c-divider-light-2);
+  --color-border-hover: var(--vt-c-divider-light-1);
+
+  --color-heading: var(--vt-c-text-light-1);
+  --color-text: var(--vt-c-text-light-1);
+
+  --section-gap: 160px;
+}
+
+@media (prefers-color-scheme: dark) {
+  :root {
+    --color-background: var(--vt-c-black);
+    --color-background-soft: var(--vt-c-black-soft);
+    --color-background-mute: var(--vt-c-black-mute);
+
+    --color-border: var(--vt-c-divider-dark-2);
+    --color-border-hover: var(--vt-c-divider-dark-1);
+
+    --color-heading: var(--vt-c-text-dark-1);
+    --color-text: var(--vt-c-text-dark-2);
+  }
+}
+
+*,
+*::before,
+*::after {
+  box-sizing: border-box;
+  margin: 0;
+  position: relative;
+  font-weight: normal;
+}
+
+body {
+  min-height: 100vh;
+  color: var(--color-text);
+  background: var(--color-background);
+  transition: color 0.5s, background-color 0.5s;
+  line-height: 1.6;
+  font-family: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu,
+    Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
+  font-size: 15px;
+  text-rendering: optimizeLegibility;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}

+ 1 - 0
gui/src/assets/logo.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69"  xmlns:v="https://vecta.io/nano"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg>

+ 43 - 0
gui/src/components/HelloWorld.vue

@@ -0,0 +1,43 @@
+<script setup>
+defineProps({
+  msg: {
+    type: String,
+    required: true
+  }
+})
+</script>
+
+<template>
+  <div class="greetings">
+    <h1 class="green">{{ msg }}</h1>
+    <h3>
+      You’ve successfully created a project with
+      <a target="_blank" href="https://vitejs.dev/">Vite</a> +
+      <a target="_blank" href="https://vuejs.org/">Vue 3</a>.
+    </h3>
+  </div>
+</template>
+
+<style scoped>
+h1 {
+  font-weight: 500;
+  font-size: 2.6rem;
+  top: -10px;
+}
+
+h3 {
+  font-size: 1.2rem;
+}
+
+.greetings h1,
+.greetings h3 {
+  text-align: center;
+}
+
+@media (min-width: 1024px) {
+  .greetings h1,
+  .greetings h3 {
+    text-align: left;
+  }
+}
+</style>

+ 12 - 0
gui/src/components/TheWelcome.vue

@@ -0,0 +1,12 @@
+<script setup>
+import WelcomeItem from './WelcomeItem.vue'
+</script>
+
+<template>
+  <WelcomeItem>
+    <template #heading>TOPICS</template>
+
+    Tweet Text
+  </WelcomeItem>
+
+</template>

+ 13 - 0
gui/src/components/TopicLists.vue

@@ -0,0 +1,13 @@
+<template>
+<div class="tabs"></div>
+<div class="topics">test</div>
+<div class="archivedTopics"></div>
+
+</template>
+
+<script></script>
+
+
+<style>
+
+</style>

+ 19 - 0
gui/src/components/Tweet.vue

@@ -0,0 +1,19 @@
+<template>
+    <li>
+        <span>{{ topics }}</span>
+        <br>
+        <span>{{ text }}</span>
+    </li>
+</template>
+
+<script>
+export default {
+	props: {
+        topics: String,
+        text: String
+    }
+}
+</script>
+
+
+<style></style>

+ 83 - 0
gui/src/components/WelcomeItem.vue

@@ -0,0 +1,83 @@
+<template>
+  <div class="item">
+    <div class="details">
+      <h3>
+        <slot name="heading"></slot>
+      </h3>
+      <slot></slot>
+    </div>
+  </div>
+</template>
+
+<style scoped>
+.item {
+  margin-top: 2rem;
+  display: flex;
+}
+
+.details {
+  flex: 1;
+  margin-left: 1rem;
+}
+
+i {
+  display: flex;
+  place-items: center;
+  place-content: center;
+  width: 32px;
+  height: 32px;
+
+  color: var(--color-text);
+}
+
+h3 {
+  font-size: 1.2rem;
+  font-weight: 500;
+  margin-bottom: 0.4rem;
+  color: var(--color-heading);
+}
+
+@media (min-width: 1024px) {
+  .item {
+    margin-top: 0;
+    padding: 0.4rem 0 1rem calc(var(--section-gap) / 2);
+  }
+
+  i {
+    top: calc(50% - 25px);
+    left: -26px;
+    position: absolute;
+    border: 1px solid var(--color-border);
+    background: var(--color-background);
+    border-radius: 8px;
+    width: 50px;
+    height: 50px;
+  }
+
+  .item:before {
+    content: ' ';
+    border-left: 1px solid var(--color-border);
+    position: absolute;
+    left: 0;
+    bottom: calc(50% + 25px);
+    height: calc(50% - 25px);
+  }
+
+  .item:after {
+    content: ' ';
+    border-left: 1px solid var(--color-border);
+    position: absolute;
+    left: 0;
+    top: calc(50% + 25px);
+    height: calc(50% - 25px);
+  }
+
+  .item:first-of-type:before {
+    display: none;
+  }
+
+  .item:last-of-type:after {
+    display: none;
+  }
+}
+</style>

+ 4 - 0
gui/src/main.js

@@ -0,0 +1,4 @@
+import { createApp } from 'vue'
+import App from './App.vue'
+
+createApp(App).mount('#app')

+ 14 - 0
gui/vite.config.js

@@ -0,0 +1,14 @@
+import { fileURLToPath, URL } from 'url'
+
+import { defineConfig } from 'vite'
+import vue from '@vitejs/plugin-vue'
+
+// https://vitejs.dev/config/
+export default defineConfig({
+  plugins: [vue()],
+  resolve: {
+    alias: {
+      '@': fileURLToPath(new URL('./src', import.meta.url))
+    }
+  }
+})

+ 1079 - 0
leader/leader.go

@@ -0,0 +1,1079 @@
+package main
+
+//#cgo CFLAGS: -fopenmp -O2
+//#cgo LDFLAGS: -lcrypto -lm -fopenmp
+//#include "../c/dpf.h"
+//#include "../c/okv.h"
+//#include "../c/dpf.c"
+//#include "../c/okv.c"
+import "C"
+
+//ssssssssssssss
+
+import (
+	"crypto/rand"
+	"crypto/rsa"
+	"crypto/sha256"
+	"crypto/tls"
+	"crypto/x509"
+	"crypto/x509/pkix"
+	"encoding/pem"
+	"fmt"
+	"math/big"
+	"net"
+	"strconv"
+	"sync"
+	"time"
+
+	lib "2PPS/lib"
+	"unsafe"
+
+	"golang.org/x/crypto/nacl/box"
+)
+
+//this stores all neccessary information for each client
+type clientKeys struct {
+	roundsParticipating int
+	PublicKey           *[32]byte
+	SharedSecret        [32]byte
+	PirQuery            [][]byte
+}
+
+var clientData = make(map[net.Addr]clientKeys)
+
+var leaderPrivateKey *[32]byte
+
+var leaderPublicKey *[32]byte
+
+var followerPublicKey *[32]byte
+
+// every roundsBeforeUpdate the client updates his pirQuery, the sharedSecrets are updated locally
+const roundsBeforeUpdate int = 1
+
+const follower string = "127.0.0.1:4443"
+
+const maxNumberOfClients = 1000
+
+//needs to be changed at leader/follower/client at the same time
+const neededSubscriptions = 1
+
+var topicList []byte
+var topicAmount int
+var archiveTopicAmount int
+
+//works on my machine
+var numThreads = 12
+
+//has to be dividable by 32
+var dataLength int = 32
+
+//this needs to be adjusted peridodically
+var numRows int = 2
+
+//counts the number of rounds
+var round int = 1
+
+var startTime time.Time
+
+//adjust this for follower aswell
+var maxTimePerRound time.Duration = 5 * time.Second
+
+//channel for goroutine communication with clients
+var phase1Channel = make(chan net.Conn, maxNumberOfClients)
+var phase3Channel = make(chan net.Conn, maxNumberOfClients)
+
+func main() {
+
+	generatedPublicKey, generatedPrivateKey, err := box.GenerateKey(rand.Reader)
+	if err != nil {
+		panic(err)
+	}
+	//why is this neccessary?
+	leaderPrivateKey = generatedPrivateKey
+	leaderPublicKey = generatedPublicKey
+
+	/*
+		if len(os.Args) != 4 {
+			fmt.Println("try again with: numThreads, dataLength, numRows")
+			return
+		}
+		numThreads, _ = strconv.Atoi(os.Args[2])
+		dataLength, _ = strconv.Atoi(os.Args[3])
+		numRows, _ = strconv.Atoi(os.Args[4])
+	*/
+
+	C.initializeServer(C.int(numThreads))
+
+	//calls follower for setup
+	conf := &tls.Config{
+		InsecureSkipVerify: true,
+	}
+	followerConnection, err := tls.Dial("tcp", follower, conf)
+	if err != nil {
+		panic(err)
+	}
+	followerConnection.SetDeadline(time.Time{})
+
+	//receives follower publicKey
+	var tmpFollowerPubKey [32]byte
+	_, err = followerConnection.Read(tmpFollowerPubKey[:])
+	if err != nil {
+		panic(err)
+	}
+	followerPublicKey = &tmpFollowerPubKey
+
+	//send publicKey to follower
+	_, err = followerConnection.Write(leaderPublicKey[:])
+	if err != nil {
+		panic(err)
+	}
+
+	//goroutine for accepting new clients
+	go func() {
+		leaderConnectionPrivateKey, err := rsa.GenerateKey(rand.Reader, 2048)
+		if err != nil {
+			panic(err)
+		}
+
+		// Generate a pem block with the private key
+		keyPem := pem.EncodeToMemory(&pem.Block{
+			Type:  "RSA PRIVATE KEY",
+			Bytes: x509.MarshalPKCS1PrivateKey(leaderConnectionPrivateKey),
+		})
+
+		tml := x509.Certificate{
+			// you can add any attr that you need
+			NotBefore: time.Now(),
+			NotAfter:  time.Now().AddDate(5, 0, 0),
+			// you have to generate a different serial number each execution
+			SerialNumber: big.NewInt(123123),
+			Subject: pkix.Name{
+				CommonName:   "New Name",
+				Organization: []string{"New Org."},
+			},
+			BasicConstraintsValid: true,
+		}
+		cert, err := x509.CreateCertificate(rand.Reader, &tml, &tml, &leaderConnectionPrivateKey.PublicKey, leaderConnectionPrivateKey)
+		if err != nil {
+			panic(err)
+		}
+
+		// Generate a pem block with the certificate
+		certPem := pem.EncodeToMemory(&pem.Block{
+			Type:  "CERTIFICATE",
+			Bytes: cert,
+		})
+
+		tlsCert, err := tls.X509KeyPair(certPem, keyPem)
+		if err != nil {
+			panic(err)
+		}
+
+		config := &tls.Config{Certificates: []tls.Certificate{tlsCert}}
+
+		//listens for clients
+		lnClients, err := tls.Listen("tcp", ":4441", config)
+		if err != nil {
+			panic(err)
+		}
+		defer lnClients.Close()
+		for {
+			clientConnection, err := lnClients.Accept()
+			if err != nil {
+				panic(err)
+			}
+			clientConnection.SetDeadline(time.Time{})
+
+			//sends topicList so client can participate in phase 3 asap
+			sendTopicLists(clientConnection)
+
+			//send leader publicKey
+			_, err = clientConnection.Write(leaderPublicKey[:])
+			if err != nil {
+				panic(err)
+			}
+
+			//send follower publicKey
+			_, err = clientConnection.Write(followerPublicKey[:])
+			if err != nil {
+				panic(err)
+			}
+
+			var clientPublicKey *[32]byte
+			var tmpClientPublicKey [32]byte
+			//gets publicKey from client
+			_, err = clientConnection.Read(tmpClientPublicKey[:])
+			if err != nil {
+				panic(err)
+			}
+
+			clientPublicKey = &tmpClientPublicKey
+
+			//this is the key for map(client data)
+			remoteAddress := clientConnection.RemoteAddr()
+			//pirQuery will be added in phase 3
+			//bs! only want to set roundsParticipating and answerAmount to 0, mb there is a better way
+			//will work for now
+			var emptyArray [32]byte
+			var emptyByteArray [][]byte
+			keys := clientKeys{0, clientPublicKey, emptyArray, emptyByteArray}
+			clientData[remoteAddress] = keys
+
+			phase1Channel <- clientConnection
+		}
+	}()
+
+	wg := &sync.WaitGroup{}
+
+	//the current phase
+	phase := make([]byte, 1)
+
+	//locks access to DB
+	var m sync.Mutex
+
+	for {
+
+		//phase1
+		startTime = time.Now()
+		phase[0] = 1
+
+		fmt.Println("phase1")
+
+		//creates a new write Db for this round
+		for i := 0; i < numRows; i++ {
+			C.createDb(C.int(1), C.int(dataLength))
+		}
+		for id := 0; id < numThreads; id++ {
+			wg.Add(1)
+
+			followerConnection, err := tls.Dial("tcp", follower, conf)
+			if err != nil {
+				panic(err)
+			}
+			followerConnection.SetDeadline(time.Time{})
+
+			go phase1(id, phase, followerConnection, wg, m, startTime)
+		}
+
+		wg.Wait()
+
+		fmt.Println("phase2")
+
+		//phase2
+
+		followerConnection, err := tls.Dial("tcp", follower, conf)
+		if err != nil {
+			panic(err)
+		}
+		followerConnection.SetDeadline(time.Time{})
+
+		phase2(followerConnection)
+
+		//phase3
+
+		fmt.Println("phase3")
+
+		if round == 1 {
+			//addTestTweets()
+		}
+
+		//no tweets -> continue to phase 1 and mb get tweets
+		topicList, topicAmount = lib.GetTopicList(0)
+		if len(topicList) == 0 {
+			continue
+		}
+
+		phase[0] = 3
+		startTime = time.Now()
+
+		for id := 0; id < numThreads; id++ {
+			wg.Add(1)
+			followerConnection, err := tls.Dial("tcp", follower, conf)
+			if err != nil {
+				panic(err)
+			}
+			followerConnection.SetDeadline(time.Time{})
+
+			go phase3(id, phase, followerConnection, wg, startTime)
+		}
+
+		wg.Wait()
+
+		lib.CleanUpdbR(round)
+
+		round++
+	}
+}
+
+func phase1(id int, phase []byte, followerConnection net.Conn, wg *sync.WaitGroup, m sync.Mutex, startTime time.Time) {
+
+	roundAsBytes := intToByte(round)
+	gotClient := make([]byte, 1)
+	gotClient[0] = 0
+
+	//wait until time is up
+	for len(phase1Channel) == 0 {
+		if time.Since(startTime) > maxTimePerRound {
+			//tells follower that this worker is done
+			_, err := followerConnection.Write(gotClient)
+			if err != nil {
+				panic(err)
+			}
+			wg.Done()
+			return
+		}
+		time.Sleep(1 * time.Second)
+	}
+
+	for clientConnection := range phase1Channel {
+
+		gotClient[0] = 1
+		//tells follower that this worker got a clientConnection
+		_, err := followerConnection.Write(gotClient)
+		if err != nil {
+			panic(err)
+		}
+
+		//sends clients publicKey to follower
+		clientPublicKey := clientData[clientConnection.RemoteAddr()].PublicKey
+		_, err = followerConnection.Write(clientPublicKey[:])
+		if err != nil {
+			panic(err)
+		}
+
+		//setup the worker-specific db
+		dbSize := int(C.dbSize)
+		db := make([][]byte, dbSize)
+		for i := 0; i < dbSize; i++ {
+			db[i] = make([]byte, int(C.db[i].dataSize))
+		}
+
+		//tells client that phase 1 has begun
+		_, err = clientConnection.Write(phase)
+		if err != nil {
+			panic(err)
+		}
+
+		//tells client current round
+		_, err = clientConnection.Write(roundAsBytes)
+		if err != nil {
+			panic(err)
+		}
+
+		//accept dpfQuery from client
+		dpfLengthBytes := make([]byte, 4)
+		_, err = clientConnection.Read(dpfLengthBytes)
+		if err != nil {
+			panic(err)
+		}
+		dpfLength := byteToInt(dpfLengthBytes)
+
+		dpfQueryAEncrypted := make([]byte, dpfLength)
+		dpfQueryBEncrypted := make([]byte, dpfLength)
+
+		_, err = clientConnection.Read(dpfQueryAEncrypted)
+		if err != nil {
+			panic(err)
+		}
+
+		_, err = clientConnection.Read(dpfQueryBEncrypted)
+		if err != nil {
+			panic(err)
+		}
+
+		_, err = followerConnection.Write(dpfLengthBytes)
+		if err != nil {
+			panic(err)
+		}
+
+		_, err = followerConnection.Write(dpfQueryBEncrypted)
+		if err != nil {
+			panic(err)
+		}
+
+		//auditing starts here
+
+		//generate seed
+		var seed [16]byte
+		_, err = rand.Read(seed[:])
+		if err != nil {
+			panic(err)
+		}
+
+		//send seed to client
+		_, err = clientConnection.Write(seed[:])
+		if err != nil {
+			panic(err)
+		}
+
+		//send seed to follower
+		_, err = followerConnection.Write(seed[:])
+		if err != nil {
+			panic(err)
+		}
+
+		//receive proofs
+		auditLengthBytes := make([]byte, 4)
+
+		_, err = clientConnection.Read(auditLengthBytes)
+		if err != nil {
+			panic(err)
+		}
+
+		auditLength := byteToInt(auditLengthBytes)
+
+		clientAuditA := make([]byte, auditLength)
+		clientAuditB := make([]byte, auditLength)
+
+		_, err = clientConnection.Read(clientAuditA)
+		if err != nil {
+			panic(err)
+		}
+
+		_, err = clientConnection.Read(clientAuditB)
+		if err != nil {
+			panic(err)
+		}
+
+		//send client audit to follower
+		_, err = followerConnection.Write(auditLengthBytes)
+		if err != nil {
+			panic(err)
+		}
+
+		_, err = followerConnection.Write(clientAuditB)
+		if err != nil {
+			panic(err)
+		}
+
+		//decrypts the client audit
+		var decryptNonce [24]byte
+		copy(decryptNonce[:], clientAuditA[:24])
+
+		clientAuditA, ok := box.Open(nil, clientAuditA[24:], &decryptNonce, clientPublicKey, leaderPrivateKey)
+		if !ok {
+			panic("clientAudit decryption not ok")
+		}
+
+		//decrypt dpfQueryA for sorting into db
+		copy(decryptNonce[:], dpfQueryAEncrypted[:24])
+		dpfQueryA, ok := box.Open(nil, dpfQueryAEncrypted[24:], &decryptNonce, clientPublicKey, leaderPrivateKey)
+		if !ok {
+			panic("dpfQueryA decryption not ok")
+		}
+
+		vector := make([]byte, dbSize*16)
+		//var str string
+		//run dpf, xor into local db
+		for i := 0; i < dbSize; i++ {
+			ds := int(C.db[i].dataSize)
+			dataShare := make([]byte, ds)
+			pos := C.getUint128_t(C.int(i))
+			v := C.evalDPF(C.ctx[id], (*C.uchar)(&dpfQueryA[0]), pos, C.int(ds), (*C.uchar)(&dataShare[0]))
+			fmt.Println("")
+			fmt.Println("v", i, v)
+			/*if i == 99 {
+				for i := 0; i < 16; i++ {
+					str = str + strconv.FormatInt(int64(v[i]), 2)
+				}
+				fmt.Println(strconv.ParseInt(str, 2, len(str)))
+			}*/
+
+			copy(vector[i*16:(i+1)*16], C.GoBytes(unsafe.Pointer(&v), 16))
+			for j := 0; j < ds; j++ {
+				db[i][j] = db[i][j] ^ dataShare[j]
+			}
+		}
+
+		//prepare for audit
+		mVal := make([]byte, 16)
+		cVal := make([]byte, 16)
+		C.serverSetupProof(C.ctx[id], (*C.uchar)(&seed[0]), C.dbSize, (*C.uchar)(&vector[0]), (*C.uchar)(&mVal[0]), (*C.uchar)(&cVal[0]))
+
+		//compute audit query
+		auditResultA := make([]byte, 96)
+		C.serverComputeQuery(C.ctx[id], (*C.uchar)(&seed[0]), (*C.uchar)(&mVal[0]), (*C.uchar)(&cVal[0]), (*C.uchar)(&clientAuditA[0]), (*C.uchar)(&auditResultA[0]))
+
+		//encrypt leader audit result
+		var nonce [24]byte
+		//fill nonce with randomness
+		_, err = rand.Read(nonce[:])
+		if err != nil {
+			panic("couldn't get randomness for nonce!")
+		}
+
+		auditResultAEncrypted := box.Seal(nonce[:], auditResultA, &nonce, followerPublicKey, leaderPrivateKey)
+
+		encryptedAuditResultALengthBytes := intToByte(len(auditResultAEncrypted))
+
+		//send audit result to follower
+		_, err = followerConnection.Write(encryptedAuditResultALengthBytes)
+		if err != nil {
+			panic(err)
+		}
+
+		_, err = followerConnection.Write(auditResultAEncrypted)
+		if err != nil {
+			panic(err)
+		}
+
+		//receive follower audit result
+		auditResultBEncryptedLengthBytes := make([]byte, 4)
+		_, err = followerConnection.Read(auditResultBEncryptedLengthBytes)
+		if err != nil {
+			panic(err)
+		}
+		auditResultBEncryptedLength := byteToInt(auditResultBEncryptedLengthBytes)
+
+		auditResultBEncrypted := make([]byte, auditResultBEncryptedLength)
+		_, err = followerConnection.Read(auditResultBEncrypted)
+		if err != nil {
+			panic(err)
+		}
+
+		//decrypts the audit result from follower
+		copy(decryptNonce[:], auditResultBEncrypted[:24])
+		auditResultB, ok := box.Open(nil, auditResultBEncrypted[24:], &decryptNonce, followerPublicKey, leaderPrivateKey)
+		if !ok {
+			panic("auditResultB decryption not ok")
+		}
+
+		//compute audit result
+		auditResult := int(C.serverVerifyProof((*C.uchar)(&auditResultA[0]), (*C.uchar)(&auditResultB[0])))
+
+		if byteToInt(auditResultB) == 0 || auditResult == 0 {
+			//fmt.Println("audit failed")
+		} /*else {
+			fmt.Println("audit passed")
+		}
+		*/
+
+		//xor the worker's DB into the main DB
+		for i := 0; i < dbSize; i++ {
+			m.Lock()
+			C.xorIn(C.int(i), (*C.uchar)(&db[i][0]))
+			m.Unlock()
+		}
+		phase3Channel <- clientConnection
+
+		//loop that waits for new client or leaves phase1 if time is up
+		for {
+			if time.Since(startTime) < maxTimePerRound {
+				//this worker handles the next client
+				if len(phase1Channel) > 0 {
+					break
+					//this worker waits for next client
+				} else {
+					time.Sleep(1 * time.Second)
+				}
+				//times up
+			} else {
+				//tells follower that this worker is done
+				gotClient[0] = 0
+				_, err := followerConnection.Write(gotClient)
+				if err != nil {
+					panic(err)
+				}
+				wg.Done()
+				return
+			}
+		}
+	}
+}
+
+func phase2(followerConnection net.Conn) {
+
+	//gets current seed
+	seedLeader := make([]byte, 16)
+	C.readSeed((*C.uchar)(&seedLeader[0]))
+
+	//get data
+	dbSize := int(C.dbSize)
+	tmpdbLeader := make([][]byte, dbSize)
+	for i := range tmpdbLeader {
+		tmpdbLeader[i] = make([]byte, dataLength)
+	}
+	for i := 0; i < dbSize; i++ {
+		C.readData(C.int(i), (*C.uchar)(&tmpdbLeader[i][0]))
+	}
+
+	//writes seed to follower
+	_, err := followerConnection.Write(seedLeader)
+	if err != nil {
+		panic(err)
+	}
+
+	//write data to follower
+	//this is surely inefficent
+	for i := 0; i < dbSize; i++ {
+		_, err = followerConnection.Write(tmpdbLeader[i])
+		if err != nil {
+			panic(err)
+		}
+	}
+
+	//receive seed from follower
+	seedFollower := make([]byte, 16)
+	_, err = followerConnection.Read(seedFollower)
+	if err != nil {
+		panic(err)
+	}
+
+	//receive data from follower
+	tmpdbFollower := make([][]byte, dbSize)
+	for i := range tmpdbFollower {
+		tmpdbFollower[i] = make([]byte, dataLength)
+	}
+	for i := 0; i < dbSize; i++ {
+		_, err = followerConnection.Read(tmpdbFollower[i])
+		if err != nil {
+			panic(err)
+		}
+	}
+
+	//put together the db
+	tmpdb := make([][]byte, dbSize)
+	for i := range tmpdb {
+		tmpdb[i] = make([]byte, dataLength)
+	}
+
+	//get own Ciphers
+	ciphersLeader := make([]*C.uchar, dbSize)
+	for i := 0; i < dbSize; i++ {
+		ciphersLeader[i] = (*C.uchar)(C.malloc(16))
+	}
+	for i := 0; i < dbSize; i++ {
+		C.getCipher(1, C.int(i), ciphersLeader[i])
+	}
+
+	//send own Ciphers to follower
+	for i := 0; i < dbSize; i++ {
+		_, err = followerConnection.Write(C.GoBytes(unsafe.Pointer(ciphersLeader[i]), 16))
+		if err != nil {
+			panic(err)
+		}
+	}
+
+	//receive ciphers from follower
+	ciphersFollower := make([]byte, dbSize*16)
+	for i := 0; i < dbSize; i++ {
+		_, err = followerConnection.Read(ciphersFollower[i*16:])
+		if err != nil {
+			panic(err)
+		}
+	}
+
+	//put in ciphers from follower
+	for i := 0; i < dbSize; i++ {
+		C.putCipher(1, C.int(i), (*C.uchar)(&ciphersFollower[i*16]))
+	}
+
+	//decrypt each row
+	for i := 0; i < dbSize; i++ {
+		C.decryptRow(C.int(i), (*C.uchar)(&tmpdb[i][0]), (*C.uchar)(&tmpdbLeader[i][0]), (*C.uchar)(&tmpdbFollower[i][0]), (*C.uchar)(&seedLeader[0]), (*C.uchar)(&seedFollower[0]))
+		fmt.Println(i, tmpdb[i])
+		fmt.Println("")
+	}
+
+	var tweets []lib.Tweet
+	for i := 0; i < dbSize; i++ {
+		//discard cover message
+		if tmpdb[i][0] == 0 {
+			continue
+		} else {
+			//reconstruct tweet
+			var position int = 0
+			var topics []string
+			var topic string
+			var text string
+			for _, letter := range tmpdb[i] {
+				if string(letter) == ";" {
+					if topic != "" {
+						topics = append(topics, topic)
+						topic = ""
+					}
+					position++
+				} else {
+					if position == 0 {
+						if string(letter) == "," {
+							topics = append(topics, topic)
+							topic = ""
+						} else {
+							topic = topic + string(letter)
+						}
+					} else if position == 1 {
+						text = text + string(letter)
+					}
+				}
+			}
+
+			tweet := lib.Tweet{"", -1, topics, text, round}
+
+			tweets = append(tweets, tweet)
+		}
+	}
+
+	//fmt.Println("tweets recovered: ", tweets)
+
+	//sort into read db
+	lib.NewEntries(tweets, 0)
+
+	C.resetDb()
+}
+
+func addTestTweets() {
+	//creates test tweets
+	tweets := make([]lib.Tweet, 5)
+
+	for i := range tweets {
+		j := i
+		if i == 1 {
+			j = 0
+		}
+		text := "Text " + strconv.Itoa(i)
+		var topics []string
+		topics = append(topics, "Topic "+strconv.Itoa(j))
+		tweets[i] = lib.Tweet{"", -1, topics, text, i}
+	}
+
+	lib.NewEntries(tweets, 0)
+}
+
+//opti! mb it is quicker to send updated topicLists to clients first so pirQuerys are ready
+func phase3(id int, phase []byte, followerConnection net.Conn, wg *sync.WaitGroup, startTime time.Time) {
+
+	gotClient := make([]byte, 1)
+	gotClient[0] = 0
+
+	//wait until time is up
+	for len(phase3Channel) == 0 {
+		if time.Since(startTime) > maxTimePerRound {
+			//tells follower that this worker is done
+			_, err := followerConnection.Write(gotClient)
+			if err != nil {
+				panic(err)
+			}
+			wg.Done()
+			return
+		}
+		time.Sleep(1 * time.Second)
+	}
+
+	for clientConnection := range phase3Channel {
+
+		gotClient[0] = 1
+		//tells follower that this worker got a clientConnection
+		_, err := followerConnection.Write(gotClient)
+		if err != nil {
+			panic(err)
+		}
+		_, err = clientConnection.Write(phase)
+		if err != nil {
+			panic(err)
+		}
+
+		/*
+			possible Values
+			0 : new client
+				leader expects sharedSecrets, expects pirQuery
+			1 : update needed
+				leader sends topicList, performs local update of sharedSecret, expects pirQuery
+			2 : no update needed
+				nothing
+		*/
+		subPhase := make([]byte, 1)
+
+		//gets the data for the current client
+		var clientKeys = clientData[clientConnection.RemoteAddr()]
+		var roundsParticipating = clientKeys.roundsParticipating
+		//client participates for the first time
+		if roundsParticipating == 0 {
+			subPhase[0] = 0
+		} else if roundsParticipating%roundsBeforeUpdate == 0 {
+			subPhase[0] = 1
+		} else {
+			subPhase[0] = 2
+		}
+
+		//tells client what leader expects
+		_, err = clientConnection.Write(subPhase)
+		if err != nil {
+			panic(err)
+		}
+
+		//tells follower what will happen
+		_, err = followerConnection.Write(subPhase)
+		if err != nil {
+			panic(err)
+		}
+
+		//sends clients publicKey so follower knows which client is being served
+		_, err = followerConnection.Write(clientKeys.PublicKey[:])
+		if err != nil {
+			panic(err)
+		}
+
+		//increases rounds participating for client
+		clientKeys.roundsParticipating = roundsParticipating + 1
+
+		//declaring variables here to prevent dupclicates later
+		var sharedSecret [32]byte = clientData[clientConnection.RemoteAddr()].SharedSecret
+
+		if subPhase[0] == 0 {
+			sendTopicLists(clientConnection)
+			clientKeys, _ = handlePirQuery(clientKeys, clientConnection, followerConnection, int(subPhase[0]))
+		} else if subPhase[0] == 1 {
+			sendTopicLists(clientConnection)
+
+			//updates sharedSecret
+			sharedSecret = sha256.Sum256(sharedSecret[:])
+			clientKeys.SharedSecret = sharedSecret
+
+			clientKeys, _ = handlePirQuery(clientKeys, clientConnection, followerConnection, int(subPhase[0]))
+		}
+
+		getSendTweets(clientKeys, nil, clientConnection, followerConnection)
+
+		wantsArchive := make([]byte, 1)
+		_, err = clientConnection.Read(wantsArchive)
+		if err != nil {
+			panic(err)
+		}
+
+		followerConnection.Write(wantsArchive)
+		if err != nil {
+			panic(err)
+		}
+
+		if wantsArchive[0] == 1 && archiveTopicAmount > 0 {
+			_, archiveQuerys := handlePirQuery(clientKeys, clientConnection, followerConnection, -1)
+			getSendTweets(clientKeys, archiveQuerys, clientConnection, followerConnection)
+		}
+
+		//saves all changes for client
+		clientData[clientConnection.RemoteAddr()] = clientKeys
+
+		phase1Channel <- clientConnection
+
+		for {
+			if time.Since(startTime) < maxTimePerRound {
+				//this worker handles the next client
+				if len(phase3Channel) > 0 {
+					break
+					//this worker waits for next client
+				} else {
+					time.Sleep(1 * time.Second)
+				}
+				//times up
+			} else {
+				//tells follower that this worker is done
+				gotClient[0] = 0
+				_, err := followerConnection.Write(gotClient)
+				if err != nil {
+					panic(err)
+				}
+				wg.Done()
+				return
+			}
+		}
+	}
+}
+
+func getSendTweets(clientKeys clientKeys, archiveQuerys [][]byte, clientConnection, followerConnection net.Conn) {
+	tmpNeededSubscriptions := neededSubscriptions
+	if archiveQuerys != nil {
+		tmpNeededSubscriptions = len(archiveQuerys)
+	}
+	for i := 0; i < tmpNeededSubscriptions; i++ {
+		//gets all requested tweets
+		var tweets []byte
+		if archiveQuerys == nil {
+			tweets = lib.GetTweets(clientKeys.PirQuery[i], dataLength, 0)
+		} else {
+			tweets = lib.GetTweets(archiveQuerys[i], dataLength, 1)
+		}
+
+		//expand sharedSecret so it is of right length
+		expandBy := len(tweets) / 32
+		var expandedSharedSecret []byte
+		for i := 0; i < expandBy; i++ {
+			expandedSharedSecret = append(expandedSharedSecret, clientKeys.SharedSecret[:]...)
+		}
+
+		fmt.Println(expandedSharedSecret)
+
+		//Xor's sharedSecret with all tweets
+		lib.Xor(expandedSharedSecret[:], tweets)
+
+		//receives tweets from follower and Xor's them in
+		tweetsLengthBytes := make([]byte, 4)
+
+		_, err := followerConnection.Read(tweetsLengthBytes)
+		if err != nil {
+			panic(err)
+		}
+		tweetsReceivedLength := byteToInt(tweetsLengthBytes)
+
+		receivedTweets := make([]byte, tweetsReceivedLength)
+		_, err = followerConnection.Read(receivedTweets)
+		if err != nil {
+			panic(err)
+		}
+
+		lib.Xor(receivedTweets, tweets)
+
+		//sends tweets to client
+		tweetsLengthBytes = intToByte(len(tweets))
+		_, err = clientConnection.Write(tweetsLengthBytes)
+		if err != nil {
+			panic(err)
+		}
+		_, err = clientConnection.Write(tweets)
+		if err != nil {
+			panic(err)
+		}
+	}
+}
+
+func handlePirQuery(clientKeys clientKeys, clientConnection net.Conn, followerConnection net.Conn, subPhase int) (clientKeys, [][]byte) {
+
+	clientPublicKey := clientKeys.PublicKey
+
+	//gets the msg length
+	msgLengthBytes := make([]byte, 4)
+	_, err := clientConnection.Read(msgLengthBytes)
+	if err != nil {
+		panic(err)
+	}
+	msgLength := byteToInt(msgLengthBytes)
+
+	leaderBox := make([]byte, msgLength)
+	followerBox := make([]byte, msgLength)
+
+	//gets the leader box
+	_, err = clientConnection.Read(leaderBox)
+	if err != nil {
+		panic(err)
+	}
+
+	//gets the follower box
+	_, err = clientConnection.Read(followerBox)
+	if err != nil {
+		panic(err)
+	}
+
+	tmpNeededSubscriptions := neededSubscriptions
+	tmpTopicAmount := topicAmount
+	if subPhase == -1 {
+		archiveNeededSubscriptions := make([]byte, 4)
+		_, err = clientConnection.Read(archiveNeededSubscriptions)
+		if err != nil {
+			panic(err)
+		}
+		_, err = followerConnection.Write(archiveNeededSubscriptions)
+		if err != nil {
+			panic(err)
+		}
+		tmpNeededSubscriptions = byteToInt(archiveNeededSubscriptions)
+		tmpTopicAmount = archiveTopicAmount
+	}
+
+	//send length to follower
+	_, err = followerConnection.Write(msgLengthBytes)
+	if err != nil {
+		panic(err)
+	}
+
+	//send box to follower
+	_, err = followerConnection.Write(followerBox)
+	if err != nil {
+		panic(err)
+	}
+
+	var decryptNonce [24]byte
+	copy(decryptNonce[:], leaderBox[:24])
+	decrypted, ok := box.Open(nil, leaderBox[24:], &decryptNonce, clientPublicKey, leaderPrivateKey)
+	if !ok {
+		panic("pirQuery decryption not ok")
+	}
+
+	//if sharedSecret is send
+	if subPhase == 0 {
+		//bs!
+		var tmpSharedSecret [32]byte
+		for index := 0; index < 32; index++ {
+			tmpSharedSecret[index] = decrypted[index]
+		}
+		clientKeys.SharedSecret = tmpSharedSecret
+		decrypted = decrypted[32:]
+	}
+
+	//transforms byteArray to ints of wanted topics
+	pirQueryFlattened := decrypted
+	pirQuerys := make([][]byte, tmpNeededSubscriptions)
+	for i := range pirQuerys {
+		pirQuerys[i] = make([]byte, tmpTopicAmount)
+	}
+	for i := 0; i < tmpNeededSubscriptions; i++ {
+		pirQuerys[i] = pirQueryFlattened[i*tmpTopicAmount : (i+1)*tmpTopicAmount]
+	}
+
+	//sets the pirQuery for the client in case whe are not archiving
+	if subPhase != -1 {
+		clientKeys.PirQuery = pirQuerys
+	}
+
+	return clientKeys, pirQuerys
+}
+
+func transformBytesToStringArray(topicsAsBytes []byte) []string {
+	var topics []string
+	var topic string
+	var position int = 0
+	for _, letter := range topicsAsBytes {
+		if string(letter) == "," {
+			topics[position] = topic
+			topic = ""
+			position++
+		} else {
+			topic = topic + string(letter)
+		}
+	}
+	return topics
+}
+
+func byteToInt(myBytes []byte) (x int) {
+	x = int(myBytes[3])<<24 + int(myBytes[2])<<16 + int(myBytes[1])<<8 + int(myBytes[0])
+	return
+}
+
+func sendTopicLists(clientConnection net.Conn) {
+	for i := 0; i < 2; i++ {
+		var topicList []byte
+		if i == 0 {
+			topicList, topicAmount = lib.GetTopicList(i)
+		} else {
+			topicList, archiveTopicAmount = lib.GetTopicList(i)
+		}
+		topicListLengthBytes := intToByte(len(topicList))
+
+		_, err := clientConnection.Write(topicListLengthBytes)
+		if err != nil {
+			panic(err)
+		}
+
+		_, err = clientConnection.Write(topicList)
+		if err != nil {
+			panic(err)
+		}
+	}
+}
+
+func intToByte(myInt int) (retBytes []byte) {
+	retBytes = make([]byte, 4)
+	retBytes[3] = byte((myInt >> 24) & 0xff)
+	retBytes[2] = byte((myInt >> 16) & 0xff)
+	retBytes[1] = byte((myInt >> 8) & 0xff)
+	retBytes[0] = byte(myInt & 0xff)
+	return
+}

+ 184 - 0
lib/databaseRead.go

@@ -0,0 +1,184 @@
+package lib
+
+import (
+	"bytes"
+	"encoding/json"
+)
+
+//topicPointer and textPointer should not be exported
+//mb move tweet reconstruction to dbRead from servers
+type Tweet struct {
+	TopicPointer string
+	TextPointer  int
+	Topics       []string
+	Text         string
+	RoundPosted  int
+}
+
+var dbR = make(map[string][]Tweet)
+
+var archive = make(map[string][]Tweet)
+
+//has to be dividable by 32
+var minimumBlockSize int
+
+const roundsBeforeArchiving = 3
+
+var topicList []string
+
+var archiveTopicList []string
+
+func NewEntries(inputTweets []Tweet, whereTo int) {
+	tmpdb := dbR
+	if whereTo == 1 {
+		tmpdb = archive
+	}
+	var position int = 0
+	for _, tweet := range inputTweets {
+		for index := range tweet.Topics {
+			//new topic
+			if _, ok := tmpdb[tweet.Topics[index]]; !ok {
+				if whereTo == 0 {
+					topicList = append(topicList, tweet.Topics[index])
+				} else {
+					archiveTopicList = append(archiveTopicList, tweet.Topics[index])
+				}
+			}
+			//new tweet
+			if index == 0 {
+				position = len(tmpdb[tweet.Topics[0]])
+				tmpdb[tweet.Topics[index]] = append(tmpdb[tweet.Topics[0]], tweet)
+			} else {
+				//known tweet
+				//setting pointer for all other Topics
+				topic := tweet.Topics[index]
+				tweet.TopicPointer = tweet.Topics[0]
+				tweet.TextPointer = position
+				tweet.Topics = nil
+				tweet.Text = ""
+				tmpdb[topic] = append(tmpdb[topic], tweet)
+			}
+		}
+	}
+	if whereTo == 0 {
+		dbR = tmpdb
+	} else {
+		archive = tmpdb
+	}
+}
+
+//todo! add round to pirquery only get tweets that have been posted from that round onward
+func GetTweets(pirQuery []byte, dataLength int, whereFrom int) []byte {
+	tmpdb := dbR
+	if whereFrom == 1 {
+		tmpdb = archive
+	}
+	minimumBlockSize = dataLength * maxTweetAmount(whereFrom)
+	var wantedTopics = getNamesForTopics(pirQuery, whereFrom)
+	tweetsToReturn := make([][]Tweet, len(wantedTopics))
+	for index, wantedTopic := range wantedTopics {
+		for _, tweet := range tmpdb[wantedTopic] {
+			//new Tweet
+			if tweet.Text != "" {
+				tweet.RoundPosted = 0
+				tweetsToReturn[index] = append(tweetsToReturn[index], tweet)
+			} else {
+				//"copied" tweet
+				//find tweet with pointers
+				tweet = tmpdb[tweet.TopicPointer][tweet.TextPointer]
+				tweet.RoundPosted = 0
+				tweetsToReturn[index] = append(tweetsToReturn[index], tweet)
+			}
+		}
+	}
+	return tweetsToByteArray(tweetsToReturn)
+}
+
+func maxTweetAmount(whereFrom int) int {
+	tmpdb := dbR
+	if whereFrom == 1 {
+		tmpdb = archive
+	}
+	var max int = 0
+	for i := range tmpdb {
+		nrOfTweets := len(dbR[i])
+		if nrOfTweets > max {
+			max = nrOfTweets
+		}
+	}
+	return max
+}
+
+func getNamesForTopics(wantedIndices []byte, whereFrom int) []string {
+	var topicNames []string
+	tmpTopicList := topicList
+	if whereFrom == 1 {
+		tmpTopicList = archiveTopicList
+	}
+	for index, element := range wantedIndices {
+		if element == 1 {
+			topicNames = append(topicNames, tmpTopicList[index])
+		}
+	}
+	return topicNames
+}
+
+//transform struct to byte array for sending
+func tweetsToByteArray(tweetsToReturn [][]Tweet) []byte {
+	tweetsAsBytes := make([]byte, minimumBlockSize)
+	for _, block := range tweetsToReturn {
+		var tweetToAppend []byte
+		for _, tweet := range block {
+			for _, topic := range tweet.Topics {
+				tweetToAppend = append(tweetToAppend, []byte(topic)...)
+				tweetToAppend = append(tweetToAppend, ","...)
+			}
+			//replaces last with ";", bc there is text following and not another topic
+			tweetToAppend = tweetToAppend[:len(tweetToAppend)-1]
+			tweetToAppend = append(tweetToAppend, []byte(";")[0])
+			tweetToAppend = append(tweetToAppend, []byte(tweet.Text)...)
+			tweetToAppend = append(tweetToAppend, []byte(";")[0])
+		}
+		//adds padding
+		for i := len(tweetToAppend); i < minimumBlockSize; i++ {
+			tweetToAppend = append(tweetToAppend, []byte(";")[0])
+		}
+		Xor(tweetToAppend, tweetsAsBytes)
+	}
+
+	return tweetsAsBytes
+}
+
+//see func name
+func GetTopicList(whereFrom int) ([]byte, int) {
+	tmpTopicList := topicList
+	if whereFrom == 1 {
+		tmpTopicList = archiveTopicList
+	}
+	if (len(tmpTopicList)) == 0 {
+		return nil, 0
+	}
+	topicByteArray := new(bytes.Buffer)
+	json.NewEncoder(topicByteArray).Encode(tmpTopicList)
+	return topicByteArray.Bytes(), len(tmpTopicList)
+}
+
+//iterates through full dbR and moves old tweets to archive
+func CleanUpdbR(round int) {
+	var tweetsToArchive []Tweet
+	for j := range dbR {
+		tweets := dbR[j]
+		for i := len(tweets) - 1; i >= 0; i-- {
+			if round-roundsBeforeArchiving == tweets[i].RoundPosted {
+				//only adds the tweet to the archive when there is text
+				if tweets[i].Text != "" {
+					tweetsToArchive = append(tweetsToArchive, tweets[i])
+				}
+				//delets the tweet from the array
+				tweets = append(tweets[:i], tweets[i+1:]...)
+			}
+		}
+		dbR[j] = tweets
+	}
+	NewEntries(tweetsToArchive, 1)
+}

+ 112 - 0
lib/xor.go

@@ -0,0 +1,112 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package lib
+
+import (
+	"runtime"
+	"unsafe"
+)
+
+const wordSize = int(unsafe.Sizeof(uintptr(0)))
+const supportsUnaligned = runtime.GOARCH == "386" || runtime.GOARCH == "amd64"
+
+// fastXORBytes xors in bulk. It only works on architectures that
+// support unaligned read/writes.
+func fastXORBytes(dst, a, b []byte) int {
+	n := len(a)
+	if len(b) < n {
+		n = len(b)
+	}
+
+	w := n / wordSize
+	if w > 0 {
+		dw := *(*[]uintptr)(unsafe.Pointer(&dst))
+		aw := *(*[]uintptr)(unsafe.Pointer(&a))
+		bw := *(*[]uintptr)(unsafe.Pointer(&b))
+		for i := 0; i < w; i++ {
+			dw[i] = aw[i] ^ bw[i]
+		}
+	}
+
+	for i := (n - n%wordSize); i < n; i++ {
+		dst[i] = a[i] ^ b[i]
+	}
+
+	return n
+}
+
+func safeXORBytes(dst, a, b []byte) int {
+	n := len(a)
+	if len(b) < n {
+		n = len(b)
+	}
+	for i := 0; i < n; i++ {
+		dst[i] = a[i] ^ b[i]
+	}
+	return n
+}
+
+// xorBytes xors the bytes in a and b. The destination is assumed to have enough
+// space. Returns the number of bytes xor'd.
+func xorBytes(dst, a, b []byte) int {
+	if supportsUnaligned {
+		return fastXORBytes(dst, a, b)
+	} else {
+		// TODO(hanwen): if (dst, a, b) have common alignment
+		// we could still try fastXORBytes. It is not clear
+		// how often this happens, and it's only worth it if
+		// the block encryption itself is hardware
+		// accelerated.
+		return safeXORBytes(dst, a, b)
+	}
+}
+
+// fastXORWords XORs multiples of 4 or 8 bytes (depending on architecture.)
+// The arguments are assumed to be of equal length.
+func fastXORWords(dst, a, b []byte) {
+	dw := *(*[]uintptr)(unsafe.Pointer(&dst))
+	aw := *(*[]uintptr)(unsafe.Pointer(&a))
+	bw := *(*[]uintptr)(unsafe.Pointer(&b))
+	n := len(b) / wordSize
+	for i := 0; i < n; i++ {
+		dw[i] = aw[i] ^ bw[i]
+	}
+}
+
+func XorWords(dst, a, b []byte) {
+	if supportsUnaligned {
+		fastXORWords(dst, a, b)
+	} else {
+		safeXORBytes(dst, a, b)
+	}
+}
+
+func Xor(a, dst []byte) {
+	XorWords(dst, a, dst)
+}
+
+//make sure dst is all 0's when you call this
+func Xors(as [][]byte) []byte{
+	dst := make([]byte, len(as[0]))
+	for i := range as {
+		XorWords(dst, dst, as[i])
+	}
+	return dst
+}
+
+func XorsDC(bsss [][][]byte) [][]byte {
+	n := len(bsss)
+	m := len(bsss[0])
+	x := make([][]byte, n)
+	for i, _ := range bsss {
+		y := make([][]byte, m)
+		x[i] = make([]byte, len(bsss[i][0]))
+		for j := 0; j < m; j++ {
+			y[j] = bsss[j][i]
+		}
+		x[i] = Xors(y)
+	}
+	return x
+}

+ 6 - 0
package-lock.json

@@ -0,0 +1,6 @@
+{
+  "name": "2PPS",
+  "lockfileVersion": 2,
+  "requires": true,
+  "packages": {}
+}

+ 57 - 0
testing/array.go

@@ -0,0 +1,57 @@
+package main
+
+import "fmt"
+
+func main() {
+	var arr1 [1]byte
+	arr1[0] = 1
+	arr11 := arr1
+	arr11[0] = 0
+
+	fmt.Println(arr1)
+
+	arr2 := make([]byte, 1)
+	arr2[0] = 1
+
+	arr22 := arr2
+	arr22[0] = 0
+
+	fmt.Println(arr2)
+
+	arr3 := make([]byte, 1)
+	arr3[0] = 1
+
+	var arr33 []byte
+	arr33 = arr3
+	arr33[0] = 0
+
+	fmt.Println(arr3)
+
+	arr4 := make([]byte, 1)
+	arr4[0] = 1
+
+	arr44 := make([]byte, 1)
+	arr44 = arr4
+	arr44[0] = 0
+
+	fmt.Println(arr4)
+
+	//these two below work
+	arr5 := make([]byte, 1)
+	arr5[0] = 1
+
+	arr55 := make([]byte, 1)
+	copy(arr55, arr5)
+	arr55[0] = 0
+
+	fmt.Println(arr5)
+
+	var arr6 []byte
+	arr6 = append(arr6, 1)
+
+	arr66 := make([]byte, 1)
+	copy(arr66, arr6)
+	arr66[0] = 0
+
+	fmt.Println(arr5)
+}

+ 46 - 0
testing/box.go

@@ -0,0 +1,46 @@
+package main
+
+import (
+	"crypto/rand"
+	"fmt"
+
+	"golang.org/x/crypto/nacl/box"
+)
+
+func maina() {
+	senderPublicKey, senderPrivateKey, err := box.GenerateKey(rand.Reader)
+	if err != nil {
+		panic(err)
+	}
+
+	recipientPublicKey, recipientPrivateKey, err := box.GenerateKey(rand.Reader)
+	if err != nil {
+		panic(err)
+	}
+
+	// You must use a different nonce for each message you encrypt with the
+	// same key. Since the nonce here is 192 bits long, a random value
+	// provides a sufficiently small probability of repeats.
+	var nonce [24]byte
+	_, err = rand.Read(nonce[:])
+	if err != nil {
+		panic(err)
+	}
+
+	msg := []byte("Fucking finally")
+	// This encrypts msg and appends the result to the nonce.
+	encrypted := box.Seal(nonce[:], msg, &nonce, recipientPublicKey, senderPrivateKey)
+
+	// The recipient can decrypt the message using their private key and the
+	// sender's public key. When you decrypt, you must use the same nonce you
+	// used to encrypt the message. One way to achieve this is to store the
+	// nonce alongside the encrypted message. Above, we stored the nonce in the
+	// first 24 bytes of the encrypted text.
+	var decryptNonce [24]byte
+	copy(decryptNonce[:], encrypted[:24])
+	decrypted, ok := box.Open(nil, encrypted[24:], &decryptNonce, senderPublicKey, recipientPrivateKey)
+	if !ok {
+		panic("decryption error")
+	}
+	fmt.Println(string(decrypted))
+}

+ 45 - 0
testing/json.go

@@ -0,0 +1,45 @@
+package main
+
+import (
+	"bytes"
+	"encoding/json"
+	"fmt"
+)
+
+type tweet struct {
+	topicPointer string
+	textPointer  int
+	Text         string
+	Topics       []string
+}
+
+var topicList []string
+
+func test() {
+	topicList = append(topicList, "Sport", "Ball", "Kick")
+	fmt.Println(topicList[0])
+	topicByteArray := new(bytes.Buffer)
+	json.NewEncoder(topicByteArray).Encode(topicList)
+	fmt.Println(topicByteArray.String())
+
+	var recBookmark []string
+	arrayReader := bytes.NewReader(topicByteArray.Bytes())
+	json.NewDecoder(arrayReader).Decode(&recBookmark)
+
+	fmt.Println(recBookmark)
+
+	var tweets []tweet
+	tweet1 := tweet{"", 0, "Let's go", topicList}
+	tweet2 := tweet{"", 0, "HeyHo", topicList}
+	tweets = append(tweets, tweet1, tweet2)
+
+	tweetByteArray := new(bytes.Buffer)
+	json.NewEncoder(tweetByteArray).Encode(tweets)
+	fmt.Println(tweetByteArray.String())
+
+	var recTweets []tweet
+	tweetReader := bytes.NewReader(tweetByteArray.Bytes())
+	json.NewDecoder(tweetReader).Decode(&recTweets)
+	fmt.Println(recTweets[0].Text)
+
+}

+ 83 - 0
testing/key.go

@@ -0,0 +1,83 @@
+package main
+
+import (
+	"bytes"
+	"crypto/rand"
+	"crypto/rsa"
+	"crypto/tls"
+	"crypto/x509"
+	"crypto/x509/pkix"
+	"encoding/gob"
+	"encoding/pem"
+	"fmt"
+	"log"
+	"math/big"
+	"time"
+)
+
+func mainb() {
+	var leaderPrivateKey *rsa.PrivateKey
+	leaderPrivateKey, err := rsa.GenerateKey(rand.Reader, 2048)
+	if err != nil {
+		log.Fatal("Private key cannot be created.", err.Error())
+	}
+
+	// Generate a pem block with the private key
+	keyPem := pem.EncodeToMemory(&pem.Block{
+		Type:  "RSA PRIVATE KEY",
+		Bytes: x509.MarshalPKCS1PrivateKey(leaderPrivateKey),
+	})
+
+	tml := x509.Certificate{
+		// you can add any attr that you need
+		NotBefore: time.Now(),
+		NotAfter:  time.Now().AddDate(5, 0, 0),
+		// you have to generate a different serial number each execution
+		SerialNumber: big.NewInt(123123),
+		Subject: pkix.Name{
+			CommonName:   "New Name",
+			Organization: []string{"New Org."},
+		},
+		BasicConstraintsValid: true,
+	}
+	cert, err := x509.CreateCertificate(rand.Reader, &tml, &tml, &leaderPrivateKey.PublicKey, leaderPrivateKey)
+	if err != nil {
+		log.Fatal("Certificate cannot be created.", err.Error())
+	}
+
+	// Generate a pem block with the certificate
+	certPem := pem.EncodeToMemory(&pem.Block{
+		Type:  "CERTIFICATE",
+		Bytes: cert,
+	})
+
+	tlsCert, err := tls.X509KeyPair(certPem, keyPem)
+	if err != nil {
+		log.Fatal("Cannot be loaded the certificate.", err.Error())
+	}
+
+	//listens for clients
+	config := &tls.Config{Certificates: []tls.Certificate{tlsCert}}
+	lnClients, err := tls.Listen("tcp", ":4441", config)
+	if err != nil {
+		panic(err)
+	}
+	defer lnClients.Close()
+
+	var pub rsa.PublicKey = leaderPrivateKey.PublicKey
+
+	var network bytes.Buffer
+
+	enc := gob.NewEncoder(&network)
+	dec := gob.NewDecoder(&network)
+
+	enc.Encode(pub)
+
+	var pubRec rsa.PublicKey
+
+	dec.Decode(&pubRec)
+
+	fmt.Println(pub)
+	fmt.Println(pubRec)
+
+}

+ 74 - 0
testing/randomArray.go

@@ -0,0 +1,74 @@
+package main
+
+import (
+	"crypto/rand"
+	"fmt"
+	"math/big"
+)
+
+func notmain() {
+
+	//understanding pointers...
+	var i int = 1
+	var iArray [1]int
+	p := &i
+	iArray[0] = *p
+	i = 2
+	fmt.Println(iArray[0])
+	fmt.Println(i)
+
+	//understanding sclices...
+	var arr [5]int
+	arr[0] = 0
+	arr[1] = 1
+	arr[2] = 2
+	arr[3] = 3
+	arr[4] = 4
+	fmt.Println(arr[:1])
+	fmt.Println(arr[1:3]) // = [lower:upper)
+
+	//later this will be taken from application, this is only for testing
+	var topicsOfInterest []int
+	var lenTopicList = 100
+	//creates fake topicOfInterest if client has no interests
+	//creates random length with max being len(topicList)
+	if len(topicsOfInterest) == 0 {
+		maxLength := lenTopicList / 2
+		if maxLength == 0 {
+			value, err := rand.Int(rand.Reader, big.NewInt(int64(lenTopicList+1)))
+			if err != nil {
+				panic(err)
+			}
+			maxLength = int(value.Int64())
+		}
+		fmt.Println("MaxLength: ", maxLength)
+		lengthBig, err := rand.Int(rand.Reader, big.NewInt(int64(maxLength+1)))
+		if err != nil {
+			panic(err)
+		}
+		length := int(lengthBig.Int64())
+		fmt.Println("Actual length: ", length)
+		if length == 0 {
+			length++
+		}
+		fakeTopicsOfInterest := make([]int, length)
+
+		//fills the array with unique random ascending values in range with len(topicList)
+		for index := 0; index < length; index++ {
+			min := (index * (lenTopicList / length)) + 1
+			max := ((index + 1) * (lenTopicList / length))
+			if max == lenTopicList-1 {
+				max++
+			}
+			bigNumber, err := rand.Int(rand.Reader, big.NewInt(int64(max-min+1)))
+			if err != nil {
+				panic(err)
+			}
+			var number int = int(bigNumber.Int64()) + min
+
+			fakeTopicsOfInterest[index] = number
+
+			fmt.Println("Min: ", min, " Max: ", max, " Value chosen: ", fakeTopicsOfInterest[index])
+		}
+	}
+}