Ver código fonte

works for low numbers of users, but beyond that begins to segfault

tristangurtler 3 anos atrás
pai
commit
4b5da7e2d0
3 arquivos alterados com 861 adições e 98 exclusões
  1. 3 3
      prsona/Makefile
  2. 188 0
      prsona/inc/server.hpp
  3. 670 95
      prsona/src/server.cpp

+ 3 - 3
prsona/Makefile

@@ -28,10 +28,10 @@ MG_INC_PATH = $(MG_PATH)/include
 MG_OBJ_PATH = $(MG_PATH)/out/src
 
 CPP = g++
-CPPFLAGS = -std=c++14 -Wall -I$(PRSONA_INC_PATH) -I$(BGN_INC_PATH) -I$(666_INC_PATH) -I$(MG_INC_PATH) -O2
+CPPFLAGS = -std=c++14 -Wall -I$(PRSONA_INC_PATH) -I$(BGN_INC_PATH) -I$(666_INC_PATH) -I$(MG_INC_PATH) -O1
 CPPTESTFLAGS = -std=c++14 -Wall -I$(PRSONA_INC_PATH) -I$(BGN_INC_PATH) -I$(666_INC_PATH) -I$(MG_INC_PATH) -g
-LDFLAGS = -lgmp -lgmpxx -lssl -lcrypto
-NETWORK_LDFLAGS = -ldl -lpthread
+LDFLAGS = -lgmp -lgmpxx -lssl -lcrypto -lpthread
+NETWORK_LDFLAGS = -ldl
 LDTESTFLAGS = -lgmp -lgmpxx -lssl -lcrypto -g
 
 CC = gcc

+ 188 - 0
prsona/inc/server.hpp

@@ -166,6 +166,194 @@ class PrsonaServer : public PrsonaBase {
             CurveBipoint& element,
             const Scalar& value);
 
+        // MULTI-THREADING
+        friend void generate_permutation_commitment_r(
+            const void *a,
+            void *b,
+            const void *c,
+            void *d);
+
+        friend void generate_pseudonym_commitment_r(
+            const void *a,
+            void *b,
+            const void *c,
+            const void *d,
+            void *e,
+            void *f);
+
+        friend void generate_server_tally_commitment_r(
+            const void *a,
+            void *b,
+            const void *c,
+            void *d);
+
+        friend void generate_matrix_commitment_r(
+            const void *a,
+            void *b,
+            void *c,
+            void *d,
+            const void *e,
+            const void *f,
+            void *g,
+            void *h);
+
+        friend void generate_user_tally_commitment_r(
+            const void *a,
+            const void *b,
+            const void *c,
+            const void *d,
+            const void *e,
+            void *f,
+            void *g,
+            void *h,
+            void *i,
+            void *j,
+            void *k);
+
+        friend void generate_permutation_proof_r(
+            const void *a,
+            void *b,
+            const void *c,
+            const void *d,
+            const void *e);
+
+        friend void generate_pseudonym_proof_r(
+            const void *a,
+            void *b,
+            const void *c,
+            const void *d,
+            const void *e,
+            const void *f,
+            const void *g,
+            const void *h,
+            const void *i,
+            const void *j);
+
+        friend void generate_server_tally_proof_r(
+            const void *a,
+            void *b,
+            const void *c,
+            const void *d,
+            const void *e,
+            const void *f,
+            const void *g,
+            const void *h,
+            const void *i,
+            const void *j);
+
+        friend void generate_first_half_matrix_proof_r(
+            const void *a,
+            void *b,
+            const void *c,
+            const void *d,
+            const void *e,
+            const void *f,
+            const void *g,
+            const void *h);
+
+        friend void generate_second_half_matrix_proof_r(
+            const void *a,
+            void *b,
+            const void *c,
+            const void *d,
+            const void *e,
+            const void *f,
+            const void *g,
+            const void *h);
+
+        friend void generate_user_tally_proof_r(
+            const void *a,
+            void *b,
+            const void *c,
+            const void *d,
+            const void *e,
+            const void *f,
+            const void *g,
+            const void *h,
+            const void *i,
+            const void *j,
+            const void *k,
+            const void *l,
+            const void *m,
+            const void *n);
+
+        friend void generate_tensor_r(
+            const void *a,
+            void *b,
+            const void *c,
+            const void *d,
+            const void *e,
+            const void *f,
+            const void *g,
+            const void *h,
+            const void *i,
+            const void *j);
+
+        friend void verify_permutation_r(
+            const void *a,
+            void *b,
+            const void *c,
+            const void *d);
+
+        friend void verify_pseudonym_r(
+            const void *a,
+            void *b,
+            const void *c,
+            const void *d,
+            const void *e,
+            const void *f,
+            const void *g);
+
+        friend void verify_server_tally_r(
+            const void *a,
+            void *b,
+            const void *c,
+            const void *d,
+            const void *e,
+            const void *f,
+            const void *g,
+            const void *h);
+
+        friend void verify_first_half_matrix_r(
+            const void *a,
+            void *b,
+            const void *c,
+            const void *d,
+            const void *e,
+            const void *f,
+            const void *g);
+
+        friend void verify_second_half_matrix_r(
+            const void *a,
+            void *b,
+            const void *c,
+            const void *d,
+            const void *e,
+            const void *f,
+            const void *g);
+
+        friend void verify_user_tally_r(
+            const void *a,
+            void *b,
+            const void *c,
+            const void *d,
+            const void *e,
+            const void *f,
+            const void *g,
+            const void *h,
+            const void *i,
+            const void *j);
+
+        friend void verify_tensor_r(
+            const void *a,
+            void *b,
+            const void *c,
+            const void *d,
+            const void *e,
+            const void *f,
+            const void *g,
+            const void *h);
+
     private:
         // constants for servers
         const size_t numServers;

+ 670 - 95
prsona/src/server.cpp

@@ -1,4 +1,5 @@
 #include <iostream>
+#include <thread>
 
 #include "server.hpp"
 
@@ -357,6 +358,82 @@ bool PrsonaServer::set_EG_blind_generator(
  * SCORE TALLYING
  */
 
+void homomorphic_multiplication_r(
+    void *a,
+    void *b,
+    const void *c,
+    const void *d)
+{
+    BGN *bgnSystem = (BGN *) a;
+    Quadripoint *dst = (Quadripoint *) b;
+    const CurveBipoint *x = (const CurveBipoint *) c;
+    const TwistBipoint *y = (const TwistBipoint *) d;
+
+    *dst = bgnSystem->homomorphic_multiplication_no_rerandomize(*x, *y);
+}
+
+void homomorphic_addition_r(
+    void *a,
+    void *b,
+    size_t size)
+{
+    BGN *bgnSystem = (BGN *) a;
+    Quadripoint *arr = (Quadripoint *) b;
+
+    switch (size)
+    {
+        case 0:
+        case 1:
+            return;
+
+        case 2:
+            arr[0] = bgnSystem->homomorphic_addition_no_rerandomize(arr[0], arr[1]);
+            return;
+
+        default:
+            break;
+    }
+
+    size_t halfSize = size / 2;
+
+    std::thread threadA(homomorphic_addition_r, bgnSystem, arr, halfSize);
+    std::thread threadB(homomorphic_addition_r, bgnSystem, arr + halfSize, size - halfSize);
+    threadA.join();
+    threadB.join();
+
+    arr[0] = bgnSystem->homomorphic_addition_no_rerandomize(arr[0], arr[halfSize]);
+}
+
+void tally_r(
+    void *a,
+    void *b,
+    const void *c,
+    const void *d,
+    size_t i)
+{
+    BGN *bgnSystem = (BGN *) a;
+    Scalar *dst = (Scalar *) b;
+    const std::vector<CurveBipoint> *previousVoteTallies = (const std::vector<CurveBipoint> *) c;
+    const std::vector<std::vector<TwistBipoint>> *voteMatrix = (const std::vector<std::vector<TwistBipoint>> *) d;
+
+    Quadripoint *weightedVotes = new Quadripoint[previousVoteTallies->size()];
+    std::vector<std::thread> parallelizedMults;
+
+    // ZIP
+    for (size_t j = 0; j < previousVoteTallies->size(); j++)
+        parallelizedMults.push_back(std::thread(homomorphic_multiplication_r, bgnSystem, weightedVotes + j, &((*previousVoteTallies)[j]), &((*voteMatrix)[j][i])));
+    for (size_t j = 0; j < parallelizedMults.size(); j++)
+        parallelizedMults[j].join();
+
+    // FOLDL
+    homomorphic_addition_r(bgnSystem, weightedVotes, previousVoteTallies->size());
+
+    // DECRYPT
+    *dst = bgnSystem->decrypt(weightedVotes[0]);
+
+    delete [] weightedVotes;
+}
+
 /* Calculate scores homomorphically (as an individual server would normally)
  * and then decrypt them (which the servers would normally work together to do).
  *
@@ -365,29 +442,19 @@ bool PrsonaServer::set_EG_blind_generator(
 std::vector<Scalar> PrsonaServer::tally_scores()
 {
     std::vector<Quadripoint> BGNEncryptedTallies;
-    std::vector<Scalar> decryptedTallies;
-    for (size_t i = 0; i < voteMatrix.size(); i++)
-    {
-        std::vector<Quadripoint> weightedVotes;
-
-        // ZIP
-        for (size_t j = 0; j < previousVoteTallies.size(); j++)
-        {
-            Quadripoint curr = bgnSystem.homomorphic_multiplication_no_rerandomize(previousVoteTallies[j], voteMatrix[j][i]);
+    Scalar *decryptedTallies = new Scalar[voteMatrix.size()];
+    std::vector<std::thread> parallelizedTallies;
 
-            weightedVotes.push_back(curr);
-        }
+    for (size_t i = 0; i < voteMatrix.size(); i++)
+        parallelizedTallies.push_back(std::thread(tally_r, &bgnSystem, decryptedTallies + i, &previousVoteTallies, &voteMatrix, i));
+    for (size_t i = 0; i < parallelizedTallies.size(); i++)
+        parallelizedTallies[i].join();
 
-        // FOLDL
-        Quadripoint currEncryptedTally = weightedVotes[0];
-        for (size_t j = 1; j < weightedVotes.size(); j++)
-            currEncryptedTally = bgnSystem.homomorphic_addition_no_rerandomize(currEncryptedTally, weightedVotes[j]);
+    std::vector<Scalar> retval(decryptedTallies, decryptedTallies + voteMatrix.size());
 
-        // DECRYPT
-        decryptedTallies.push_back(bgnSystem.decrypt(currEncryptedTally));
-    }
+    delete [] decryptedTallies;
 
-    return decryptedTallies;
+    return retval;
 }
 
 /* Calculate what the maximum possible score this round was (that is,
@@ -516,6 +583,254 @@ void PrsonaServer::break_down_midway_pseudonyms(
  * EPOCH HELPERS
  */
 
+void generate_permutation_commitment_r(
+    const void *a,
+    void *b,
+    const void *c,
+    void *d)
+{
+    const PrsonaServer *server = (const PrsonaServer *) a;
+    std::vector<std::vector<Twistpoint>> *permutationCommits = (std::vector<std::vector<Twistpoint>> *) b;
+    const std::vector<std::vector<Scalar>> *permutations = (const std::vector<std::vector<Scalar>> *) c;
+    std::vector<std::vector<Scalar>> *permutationSeeds = (std::vector<std::vector<Scalar>> *) d;
+
+    *permutationCommits = server->generate_commitment_matrix(*permutations, *permutationSeeds);
+}
+
+void generate_pseudonym_commitment_r(
+    const void *a,
+    void *b,
+    const void *c,
+    const void *d,
+    void *e,
+    void *f)
+{
+    const PrsonaServer *server = (const PrsonaServer *) a;
+    std::vector<std::vector<Twistpoint>> *freshPseudonymCommits = (std::vector<std::vector<Twistpoint>> *) b;
+    const std::vector<std::vector<Scalar>> *permutations = (const std::vector<std::vector<Scalar>> *) c;
+    const Scalar *power = (const Scalar *) d;
+    std::vector<std::vector<Scalar>> *freshPseudonymSeeds = (std::vector<std::vector<Scalar>> *) e;
+    std::vector<std::vector<Twistpoint>> *freshPseudonymSeedCommits = (std::vector<std::vector<Twistpoint>> *) f;
+
+    freshPseudonymSeedCommits->clear();
+    *freshPseudonymCommits = server->generate_pseudonym_matrix(*permutations, *power, *freshPseudonymSeeds, *freshPseudonymSeedCommits);
+}
+
+void generate_server_tally_commitment_r(
+    const void *a,
+    void *b,
+    const void *c,
+    void *d)
+{
+    const PrsonaServer *server = (const PrsonaServer *) a;
+    std::vector<std::vector<CurveBipoint>> *serverTallyCommits = (std::vector<std::vector<CurveBipoint>> *) b;
+    const std::vector<std::vector<Scalar>> *permutations = (const std::vector<std::vector<Scalar>> *) c;
+    std::vector<std::vector<Scalar>> *serverTallySeeds = (std::vector<std::vector<Scalar>> *) d;
+
+    *serverTallyCommits = server->generate_server_tally_matrix(*permutations, *serverTallySeeds);
+}
+
+void generate_matrix_commitment_r(
+    const void *a,
+    void *b,
+    void *c,
+    void *d,
+    const void *e,
+    const void *f,
+    void *g,
+    void *h)
+{
+    const PrsonaServer *server = (const PrsonaServer *) a;
+    std::vector<std::vector<std::vector<TwistBipoint>>> *partwayVoteMatrixCommits = (std::vector<std::vector<std::vector<TwistBipoint>>> *) b;
+    std::vector<std::vector<TwistBipoint>> *partialVoteMatrix = (std::vector<std::vector<TwistBipoint>> *) c;
+    std::vector<std::vector<std::vector<TwistBipoint>>> *finalVoteMatrixCommits = (std::vector<std::vector<std::vector<TwistBipoint>>> *) d;
+    const std::vector<std::vector<Scalar>> *permutations = (const std::vector<std::vector<Scalar>> *) e;
+    const std::vector<std::vector<TwistBipoint>> *voteMatrix = (const std::vector<std::vector<TwistBipoint>> *) f;
+    std::vector<std::vector<std::vector<Scalar>>> *partwayVoteMatrixSeeds = (std::vector<std::vector<std::vector<Scalar>>> *) g;
+    std::vector<std::vector<std::vector<Scalar>>> *finalVoteMatrixSeeds = (std::vector<std::vector<std::vector<Scalar>>> *) h;
+
+    *partwayVoteMatrixCommits = server->generate_vote_tensor(*permutations, *voteMatrix, *partwayVoteMatrixSeeds, false);
+    *partialVoteMatrix = server->calculate_next_vote_matrix(*partwayVoteMatrixCommits);
+    *finalVoteMatrixCommits = server->generate_vote_tensor(*permutations, *partialVoteMatrix, *finalVoteMatrixSeeds, true);
+}
+
+void generate_user_tally_commitment_r(
+    const void *a,
+    const void *b,
+    const void *c,
+    const void *d,
+    const void *e,
+    void *f,
+    void *g,
+    void *h,
+    void *i,
+    void *j,
+    void *k)
+{
+    const PrsonaServer *server = (const PrsonaServer *) a;
+    const std::vector<std::vector<Scalar>> *permutations = (const std::vector<std::vector<Scalar>> *) b;
+    const Scalar *power = (const Scalar *) c;
+    const Twistpoint *nextGenerator = (const Twistpoint *) d;
+    const std::vector<Twistpoint> *currentPseudonyms = (const std::vector<Twistpoint> *) e;
+    std::vector<Twistpoint> *userTallyMasks = (std::vector<Twistpoint> *) f;
+    std::vector<std::vector<Twistpoint>> *userTallyMaskCommits = (std::vector<std::vector<Twistpoint>> *) g;
+    std::vector<Twistpoint> *userTallyMessages = (std::vector<Twistpoint> *) h;
+    std::vector<std::vector<Twistpoint>> *userTallyMessageCommits = (std::vector<std::vector<Twistpoint>> *) i;
+    std::vector<std::vector<Scalar>> *userTallySeeds = (std::vector<std::vector<Scalar>> *) j;
+    std::vector<std::vector<Twistpoint>> *userTallySeedCommits = (std::vector<std::vector<Twistpoint>> *) k;
+
+    userTallyMaskCommits->clear();
+    userTallyMessageCommits->clear();
+    userTallySeedCommits->clear();
+    server->generate_user_tally_matrix(*permutations, *power, *nextGenerator, *currentPseudonyms, *userTallyMasks, *userTallyMaskCommits, *userTallyMessages, *userTallyMessageCommits, *userTallySeeds, *userTallySeedCommits);
+}
+
+void generate_permutation_proof_r(
+    const void *a,
+    void *b,
+    const void *c,
+    const void *d,
+    const void *e)
+{
+    const PrsonaServer *server = (const PrsonaServer *) a;
+    std::vector<Proof> *dst = (std::vector<Proof> *) b;
+    const std::vector<std::vector<Scalar>> *permutations = (const std::vector<std::vector<Scalar>> *) c;
+    const std::vector<std::vector<Scalar>> *permutationSeeds = (const std::vector<std::vector<Scalar>> *) d;
+    const std::vector<std::vector<Twistpoint>> *permutationCommits = (const std::vector<std::vector<Twistpoint>> *) e;
+
+    *dst = server->generate_valid_permutation_proof(*permutations, *permutationSeeds, *permutationCommits);
+}
+
+void generate_pseudonym_proof_r(
+    const void *a,
+    void *b,
+    const void *c,
+    const void *d,
+    const void *e,
+    const void *f,
+    const void *g,
+    const void *h,
+    const void *i,
+    const void *j)
+{
+    const PrsonaServer *server = (const PrsonaServer *) a;
+    std::vector<Proof> *dst = (std::vector<Proof> *) b;
+    const std::vector<std::vector<Scalar>> *permutations = (const std::vector<std::vector<Scalar>> *) c;
+    const Scalar *power = (const Scalar *) d;
+    const std::vector<std::vector<Scalar>> *permutationSeeds = (const std::vector<std::vector<Scalar>> *) e;
+    const std::vector<std::vector<Scalar>> *freshPseudonymSeeds = (const std::vector<std::vector<Scalar>> *) f;
+    const std::vector<Twistpoint> *currentPseudonyms = (const std::vector<Twistpoint> *) g;
+    const std::vector<std::vector<Twistpoint>> *permutationCommits = (const std::vector<std::vector<Twistpoint>> *) h;
+    const std::vector<std::vector<Twistpoint>> *freshPseudonymCommits = (const std::vector<std::vector<Twistpoint>> *) i;
+    const std::vector<std::vector<Twistpoint>> *freshPseudonymSeedCommits = (const std::vector<std::vector<Twistpoint>> *) j;
+
+    *dst = server->generate_proof_of_reordering_plus_power(*permutations, *power, *permutationSeeds, *freshPseudonymSeeds, *currentPseudonyms, *permutationCommits, *freshPseudonymCommits, *freshPseudonymSeedCommits);
+}
+
+void generate_server_tally_proof_r(
+    const void *a,
+    void *b,
+    const void *c,
+    const void *d,
+    const void *e,
+    const void *f,
+    const void *g,
+    const void *h,
+    const void *i,
+    const void *j)
+{
+    const PrsonaServer *server = (const PrsonaServer *) a;
+    std::vector<Proof> *dst = (std::vector<Proof> *) b;
+    const std::vector<std::vector<Scalar>> *permutations = (const std::vector<std::vector<Scalar>> *) c;
+    const std::vector<std::vector<Scalar>> *permutationSeeds = (const std::vector<std::vector<Scalar>> *) d;
+    const std::vector<std::vector<Scalar>> *serverTallySeeds = (const std::vector<std::vector<Scalar>> *) e;
+    const std::vector<CurveBipoint> *previousVoteTallies = (const std::vector<CurveBipoint> *) f;
+    const std::vector<std::vector<Twistpoint>> *permutationCommits = (const std::vector<std::vector<Twistpoint>> *) g;
+    const std::vector<std::vector<CurveBipoint>> *serverTallyCommits = (const std::vector<std::vector<CurveBipoint>> *) h;
+    const CurveBipoint *curveG = (const CurveBipoint *) i;
+    const CurveBipoint *curveH = (const CurveBipoint *) j;
+
+    *dst = server->generate_proof_of_reordering<CurveBipoint>(*permutations, *permutationSeeds, *serverTallySeeds, *previousVoteTallies, *permutationCommits, *serverTallyCommits, *curveG, *curveH);
+}
+
+void generate_first_half_matrix_proof_r(
+    const void *a,
+    void *b,
+    const void *c,
+    const void *d,
+    const void *e,
+    const void *f,
+    const void *g,
+    const void *h)
+{
+    const PrsonaServer *server = (const PrsonaServer *) a;
+    std::vector<std::vector<Proof>> *dst = (std::vector<std::vector<Proof>> *) b;
+    const std::vector<std::vector<Scalar>> *permutations = (const std::vector<std::vector<Scalar>> *) c;
+    const std::vector<std::vector<Scalar>> *permutationSeeds = (const std::vector<std::vector<Scalar>> *) d;
+    const std::vector<std::vector<std::vector<Scalar>>> *partwayVoteMatrixSeeds = (const std::vector<std::vector<std::vector<Scalar>>> *) e;
+    const std::vector<std::vector<TwistBipoint>> *voteMatrix = (const std::vector<std::vector<TwistBipoint>> *) f;
+    const std::vector<std::vector<Twistpoint>> *permutationCommits = (const std::vector<std::vector<Twistpoint>> *) g;
+    const std::vector<std::vector<std::vector<TwistBipoint>>> *partwayVoteMatrixCommits = (const std::vector<std::vector<std::vector<TwistBipoint>>> *) h;
+
+    server->generate_vote_tensor_proofs(*dst, *permutations, *permutationSeeds, *partwayVoteMatrixSeeds, *voteMatrix, *permutationCommits, *partwayVoteMatrixCommits, false);
+}
+
+void generate_second_half_matrix_proof_r(
+    const void *a,
+    void *b,
+    const void *c,
+    const void *d,
+    const void *e,
+    const void *f,
+    const void *g,
+    const void *h)
+{
+    const PrsonaServer *server = (const PrsonaServer *) a;
+    std::vector<std::vector<Proof>> *dst = (std::vector<std::vector<Proof>> *) b;
+    const std::vector<std::vector<Scalar>> *permutations = (const std::vector<std::vector<Scalar>> *) c;
+    const std::vector<std::vector<Scalar>> *permutationSeeds = (const std::vector<std::vector<Scalar>> *) d;
+    const std::vector<std::vector<std::vector<Scalar>>> *finalVoteMatrixSeeds = (const std::vector<std::vector<std::vector<Scalar>>> *) e;
+    const std::vector<std::vector<TwistBipoint>> *partialVoteMatrix = (const std::vector<std::vector<TwistBipoint>> *) f;
+    const std::vector<std::vector<Twistpoint>> *permutationCommits = (const std::vector<std::vector<Twistpoint>> *) g;
+    const std::vector<std::vector<std::vector<TwistBipoint>>> *finalVoteMatrixCommits = (const std::vector<std::vector<std::vector<TwistBipoint>>> *) h;
+
+    server->generate_vote_tensor_proofs(*dst, *permutations, *permutationSeeds, *finalVoteMatrixSeeds, *partialVoteMatrix, *permutationCommits, *finalVoteMatrixCommits, true);
+}
+
+void generate_user_tally_proof_r(
+    const void *a,
+    void *b,
+    const void *c,
+    const void *d,
+    const void *e,
+    const void *f,
+    const void *g,
+    const void *h,
+    const void *i,
+    const void *j,
+    const void *k,
+    const void *l,
+    const void *m,
+    const void *n)
+{
+    const PrsonaServer *server = (const PrsonaServer *) a;
+    std::vector<Proof> *dst = (std::vector<Proof> *) b;
+    const std::vector<std::vector<Scalar>> *permutations = (const std::vector<std::vector<Scalar>> *) c;
+    const Scalar *power = (const Scalar *) d;
+    const Twistpoint *nextGenerator = (const Twistpoint *) e;
+    const std::vector<std::vector<Scalar>> *permutationSeeds = (const std::vector<std::vector<Scalar>> *) f;
+    const std::vector<std::vector<Scalar>> *userTallySeeds = (const std::vector<std::vector<Scalar>> *) g;
+    const std::vector<Twistpoint> *currentPseudonyms = (const std::vector<Twistpoint> *) h;
+    const std::vector<Twistpoint> *userTallyMasks = (const std::vector<Twistpoint> *) i;
+    const std::vector<Twistpoint> *userTallyMessages = (const std::vector<Twistpoint> *) j;
+    const std::vector<std::vector<Twistpoint>> *permutationCommits = (const std::vector<std::vector<Twistpoint>> *) k;
+    const std::vector<std::vector<Twistpoint>> *userTallyMaskCommits = (const std::vector<std::vector<Twistpoint>> *) l;
+    const std::vector<std::vector<Twistpoint>> *userTallyMessageCommits = (const std::vector<std::vector<Twistpoint>> *) m;
+    const std::vector<std::vector<Twistpoint>> *userTallySeedCommits = (const std::vector<std::vector<Twistpoint>> *) n;
+
+    *dst = server->generate_user_tally_proofs(*permutations, *power, *nextGenerator, *permutationSeeds, *userTallySeeds, *currentPseudonyms, *userTallyMasks, *userTallyMessages, *permutationCommits, *userTallyMaskCommits, *userTallyMessageCommits, *userTallySeedCommits);
+}
+
 std::vector<std::vector<Proof>> PrsonaServer::epoch_calculations(
     std::vector<std::vector<Twistpoint>>& permutationCommits,
     std::vector<std::vector<Twistpoint>>& freshPseudonymCommits,
@@ -530,62 +845,218 @@ std::vector<std::vector<Proof>> PrsonaServer::epoch_calculations(
     const Twistpoint& nextGenerator,
     bool doUserTallies)
 {
-    std::vector<std::vector<Proof>> retval;
-
     std::vector<std::vector<Scalar>> permutations = generate_permutation_matrix(power);
 
-    std::vector<std::vector<Scalar>> permutationSeeds;
-    permutationCommits.clear();
-    permutationCommits = generate_commitment_matrix(permutations, permutationSeeds);
+    std::vector<std::vector<Proof>> retval, firstHalfTensorProof, secondHalfTensorProof;
+    CurveBipoint curveG = bgnSystem.get_public_key().get_bipoint_curvegen();
+    CurveBipoint curveH = bgnSystem.get_public_key().get_bipoint_curve_subgroup_gen();
+    std::vector<Proof> *individualProofs = new std::vector<Proof>[doUserTallies ? 4 : 3];
 
-    retval.push_back(generate_valid_permutation_proof(permutations, permutationSeeds, permutationCommits));
+    std::vector<std::vector<Scalar>> permutationSeeds;
+    std::thread permutationCommitThread(generate_permutation_commitment_r, this, &permutationCommits, &permutations, &permutationSeeds);
 
     std::vector<std::vector<Scalar>> freshPseudonymSeeds;
-    freshPseudonymSeedCommits.clear();
-    freshPseudonymCommits.clear();
-    freshPseudonymCommits = generate_pseudonym_matrix(permutations, power, freshPseudonymSeeds, freshPseudonymSeedCommits);
-
-    retval.push_back(generate_proof_of_reordering_plus_power(permutations, power, permutationSeeds, freshPseudonymSeeds, currentPseudonyms, permutationCommits, freshPseudonymCommits, freshPseudonymSeedCommits));
+    std::thread pseudonymCommitThread(generate_pseudonym_commitment_r, this, &freshPseudonymCommits, &permutations, &power, &freshPseudonymSeeds, &freshPseudonymSeedCommits);
 
     std::vector<std::vector<Scalar>> serverTallySeeds;
-    serverTallyCommits.clear();
-    serverTallyCommits = generate_server_tally_matrix(permutations, serverTallySeeds);
-
-    retval.push_back(generate_proof_of_reordering<CurveBipoint>(permutations, permutationSeeds, serverTallySeeds, previousVoteTallies, permutationCommits, serverTallyCommits, bgnSystem.get_public_key().get_bipoint_curvegen(), bgnSystem.get_public_key().get_bipoint_curve_subgroup_gen()));
+    std::thread serverTallyCommitThread(generate_server_tally_commitment_r, this, &serverTallyCommits, &permutations, &serverTallySeeds);
 
     std::vector<std::vector<std::vector<Scalar>>> partwayVoteMatrixSeeds;
     std::vector<std::vector<std::vector<Scalar>>> finalVoteMatrixSeeds;
-    partwayVoteMatrixCommits.clear();
-    partwayVoteMatrixCommits = generate_vote_tensor(permutations, voteMatrix, partwayVoteMatrixSeeds, false);
+    std::vector<std::vector<TwistBipoint>> partialVoteMatrix;
+    std::thread voteMatrixCommitThread(generate_matrix_commitment_r, this, &partwayVoteMatrixCommits, &partialVoteMatrix, &finalVoteMatrixCommits, &permutations, &voteMatrix, &partwayVoteMatrixSeeds, &finalVoteMatrixSeeds);
+
+    std::vector<Twistpoint> userTallyMasks;
+    std::vector<Twistpoint> userTallyMessages;
+    std::vector<std::vector<Scalar>> userTallySeeds;
+    std::thread *userTallyCommitThread = NULL;
+    if (doUserTallies)
+        userTallyCommitThread = new std::thread(generate_user_tally_commitment_r, this, &permutations, &power, &nextGenerator, &currentPseudonyms, &userTallyMasks, &userTallyMaskCommits, &userTallyMessages, &userTallyMessageCommits, &userTallySeeds, &userTallySeedCommits);
 
-    std::vector<std::vector<TwistBipoint>> partialVoteMatrix = calculate_next_vote_matrix(partwayVoteMatrixCommits);
+    permutationCommitThread.join();
+    std::thread permutationProofThread(generate_permutation_proof_r, this, individualProofs, &permutations, &permutationSeeds, &permutationCommits);
 
-    finalVoteMatrixCommits.clear();
-    finalVoteMatrixCommits = generate_vote_tensor(permutations, partialVoteMatrix, finalVoteMatrixSeeds, true);
+    pseudonymCommitThread.join();
+    std::thread pseudonymProofThread(generate_pseudonym_proof_r, this, individualProofs + 1, &permutations, &power, &permutationSeeds, &freshPseudonymSeeds, &currentPseudonyms, &permutationCommits, &freshPseudonymCommits, &freshPseudonymSeedCommits);
 
-    generate_vote_tensor_proofs(retval, permutations, permutationSeeds, partwayVoteMatrixSeeds, voteMatrix, permutationCommits, partwayVoteMatrixCommits, false);
+    serverTallyCommitThread.join();
+    std::thread serverTallyProofThread(generate_server_tally_proof_r, this, individualProofs + 2, &permutations, &permutationSeeds, &serverTallySeeds, &previousVoteTallies, &permutationCommits, &serverTallyCommits, &curveG, &curveH);
 
-    generate_vote_tensor_proofs(retval, permutations, permutationSeeds, finalVoteMatrixSeeds, partialVoteMatrix, permutationCommits, finalVoteMatrixCommits, true);
+    voteMatrixCommitThread.join();
+    std::thread firstHalfProofThread(generate_first_half_matrix_proof_r, this, &firstHalfTensorProof, &permutations, &permutationSeeds, &partwayVoteMatrixSeeds, &voteMatrix, &permutationCommits, &partwayVoteMatrixCommits);
+    std::thread secondHalfProofThread(generate_second_half_matrix_proof_r, this, &secondHalfTensorProof, &permutations, &permutationSeeds, &finalVoteMatrixSeeds, &partialVoteMatrix, &permutationCommits, &finalVoteMatrixCommits);
 
+    if (userTallyCommitThread)
+    {
+        userTallyCommitThread->join();
+        delete userTallyCommitThread;
+        userTallyCommitThread = NULL;
+    }
+    std::thread *userTallyProofThread = NULL;
     if (doUserTallies)
+        userTallyProofThread = new std::thread(generate_user_tally_proof_r, this, individualProofs + 3, &permutations, &power, &nextGenerator, &permutationSeeds, &userTallySeeds, &currentPseudonyms, &userTallyMasks, &userTallyMessages, &permutationCommits, &userTallyMaskCommits, &userTallyMessageCommits, &userTallySeedCommits);
+
+    permutationProofThread.join();
+    pseudonymProofThread.join();
+    serverTallyProofThread.join();
+    firstHalfProofThread.join();
+    secondHalfProofThread.join();
+    if (userTallyProofThread)
     {
-        std::vector<Twistpoint> userTallyMasks;
-        std::vector<Twistpoint> userTallyMessages;
-        std::vector<std::vector<Scalar>> userTallySeeds;
-        userTallyMaskCommits.clear();
-        userTallyMessageCommits.clear();
-        userTallySeedCommits.clear();
-        generate_user_tally_matrix(permutations, power, nextGenerator, currentPseudonyms, userTallyMasks, userTallyMaskCommits, userTallyMessages, userTallyMessageCommits, userTallySeeds, userTallySeedCommits);
-
-        retval.push_back(generate_user_tally_proofs(permutations, power, nextGenerator, permutationSeeds, userTallySeeds, currentPseudonyms, userTallyMasks, userTallyMessages, permutationCommits, userTallyMaskCommits, userTallyMessageCommits, userTallySeedCommits));
+        userTallyProofThread->join();
+        delete userTallyProofThread;
+        userTallyProofThread = NULL;
     }
 
+    retval.push_back(individualProofs[0]);
+    retval.push_back(individualProofs[1]);
+    retval.push_back(individualProofs[2]);
+    for (size_t i = 0; i < firstHalfTensorProof.size(); i++)
+        retval.push_back(firstHalfTensorProof[i]);
+    for (size_t i = 0; i < secondHalfTensorProof.size(); i++)
+        retval.push_back(secondHalfTensorProof[i]);
+    if (doUserTallies)
+        retval.push_back(individualProofs[3]);
+
+    delete [] individualProofs;
+
     // Replace internal values
     update_data(freshPseudonymCommits, serverTallyCommits, finalVoteMatrixCommits, userTallyMaskCommits, userTallyMessageCommits);
 
     return retval;
 }
 
+void verify_permutation_r(
+    const void *a,
+    void *b,
+    const void *c,
+    const void *d)
+{
+    const PrsonaServer *server = (const PrsonaServer *) a;
+    bool *dst = (bool *) b;
+    const std::vector<Proof> *pi = (const std::vector<Proof> *) c;
+    const std::vector<std::vector<Twistpoint>> *permutationCommits = (const std::vector<std::vector<Twistpoint>> *) d;
+
+    *dst = server->verify_valid_permutation_proof(*pi, *permutationCommits);
+}
+
+void verify_pseudonym_r(
+    const void *a,
+    void *b,
+    const void *c,
+    const void *d,
+    const void *e,
+    const void *f,
+    const void *g)
+{
+    const PrsonaServer *server = (const PrsonaServer *) a;
+    bool *dst = (bool *) b;
+    const std::vector<Proof> *pi = (const std::vector<Proof> *) c;
+    const std::vector<Twistpoint> *currentPseudonyms = (const std::vector<Twistpoint> *) d;
+    const std::vector<std::vector<Twistpoint>> *permutationCommits = (const std::vector<std::vector<Twistpoint>> *) e;
+    const std::vector<std::vector<Twistpoint>> *freshPseudonymCommits = (const std::vector<std::vector<Twistpoint>> *) f;
+    const std::vector<std::vector<Twistpoint>> *freshPseudonymSeedCommits = (const std::vector<std::vector<Twistpoint>> *) g;
+
+    *dst = server->verify_proof_of_reordering_plus_power(*pi, *currentPseudonyms, *permutationCommits, *freshPseudonymCommits, *freshPseudonymSeedCommits);
+}
+
+void verify_server_tally_r(
+    const void *a,
+    void *b,
+    const void *c,
+    const void *d,
+    const void *e,
+    const void *f,
+    const void *g,
+    const void *h)
+{
+    const PrsonaServer *server = (const PrsonaServer *) a;
+    bool *dst = (bool *) b;
+    const std::vector<Proof> *pi = (const std::vector<Proof> *) c;
+    const std::vector<CurveBipoint> *previousVoteTallies = (const std::vector<CurveBipoint> *) d;
+    const std::vector<std::vector<Twistpoint>> *permutationCommits = (const std::vector<std::vector<Twistpoint>> *) e;
+    const std::vector<std::vector<CurveBipoint>> *serverTallyCommits = (const std::vector<std::vector<CurveBipoint>> *) f;
+    const CurveBipoint *curveG = (const CurveBipoint *) g;
+    const CurveBipoint *curveH = (const CurveBipoint *) h;
+
+    *dst = server->verify_proof_of_reordering<CurveBipoint>(*pi, *previousVoteTallies, *permutationCommits, *serverTallyCommits, *curveG, *curveH);
+}
+
+void verify_first_half_matrix_r(
+    const void *a,
+    void *b,
+    const void *c,
+    const void *d,
+    const void *e,
+    const void *f,
+    const void *g)
+{
+    const PrsonaServer *server = (const PrsonaServer *) a;
+    bool *dst = (bool *) b;
+    const std::vector<std::vector<Proof>> *pi = (const std::vector<std::vector<Proof>> *) c;
+    const size_t *currOffset = (const size_t *) d;
+    const std::vector<std::vector<TwistBipoint>> *voteMatrix = (const std::vector<std::vector<TwistBipoint>> *) e;
+    const std::vector<std::vector<Twistpoint>> *permutationCommits = (const std::vector<std::vector<Twistpoint>> *) f;
+    const std::vector<std::vector<std::vector<TwistBipoint>>> *partwayVoteMatrixCommits = (const std::vector<std::vector<std::vector<TwistBipoint>>> *) g;
+
+    *dst = server->verify_vote_tensor_proofs(*pi, *currOffset, *voteMatrix, *permutationCommits, *partwayVoteMatrixCommits, false);
+}
+
+void verify_second_half_matrix_r(
+    const void *a,
+    void *b,
+    const void *c,
+    const void *d,
+    const void *e,
+    const void *f,
+    const void *g)
+{
+    const PrsonaServer *server = (const PrsonaServer *) a;
+    bool *dst = (bool *) b;
+    const std::vector<std::vector<Proof>> *pi = (const std::vector<std::vector<Proof>> *) c;
+    const size_t *currOffset = (const size_t *) d;
+    const std::vector<std::vector<Twistpoint>> *permutationCommits = (const std::vector<std::vector<Twistpoint>> *) e;
+    const std::vector<std::vector<std::vector<TwistBipoint>>> *partwayVoteMatrixCommits = (const std::vector<std::vector<std::vector<TwistBipoint>>> *) f;
+    const std::vector<std::vector<std::vector<TwistBipoint>>> *finalVoteMatrixCommits = (const std::vector<std::vector<std::vector<TwistBipoint>>> *) g;
+
+    std::vector<std::vector<TwistBipoint>> partialVoteMatrix = server->calculate_next_vote_matrix(*partwayVoteMatrixCommits);
+    *dst = server->verify_vote_tensor_proofs(*pi, *currOffset, partialVoteMatrix, *permutationCommits, *finalVoteMatrixCommits, true);
+}
+
+void verify_user_tally_r(
+    const void *a,
+    void *b,
+    const void *c,
+    const void *d,
+    const void *e,
+    const void *f,
+    const void *g,
+    const void *h,
+    const void *i,
+    const void *j)
+{
+    const PrsonaServer *server = (const PrsonaServer *) a;
+    bool *dst = (bool *) b;
+    const std::vector<Proof> *pi = (const std::vector<Proof> *) c;
+    const Twistpoint *nextGenerator = (const Twistpoint *) d;
+    const std::vector<Twistpoint> *currentPseudonyms = (const std::vector<Twistpoint> *) e;
+    const std::vector<EGCiphertext> *currentUserEncryptedTallies = (const std::vector<EGCiphertext> *) f;
+    const std::vector<std::vector<Twistpoint>> *permutationCommits = (const std::vector<std::vector<Twistpoint>> *) g;
+    const std::vector<std::vector<Twistpoint>> *userTallyMaskCommits = (const std::vector<std::vector<Twistpoint>> *) h;
+    const std::vector<std::vector<Twistpoint>> *userTallyMessageCommits = (const std::vector<std::vector<Twistpoint>> *) i;
+    const std::vector<std::vector<Twistpoint>> *userTallySeedCommits = (const std::vector<std::vector<Twistpoint>> *) j;
+
+    std::vector<Twistpoint> userTallyMasks;
+    std::vector<Twistpoint> userTallyMessages;
+    for (size_t i = 0; i < currentUserEncryptedTallies->size(); i++)
+    {
+        userTallyMasks.push_back((*currentUserEncryptedTallies)[i].mask);
+        userTallyMessages.push_back((*currentUserEncryptedTallies)[i].encryptedMessage);
+    }
+
+    *dst = server->verify_user_tally_proofs(*pi, *nextGenerator, *currentPseudonyms, userTallyMasks, userTallyMessages, *permutationCommits, *userTallyMaskCommits, *userTallyMessageCommits, *userTallySeedCommits);
+}
+
 bool PrsonaServer::accept_epoch_updates(
     const std::vector<std::vector<Proof>>& pi,
     const std::vector<std::vector<Twistpoint>>& permutationCommits,
@@ -600,7 +1071,12 @@ bool PrsonaServer::accept_epoch_updates(
     const Twistpoint& nextGenerator,
     bool doUserTallies)
 {
-    bool verification;
+    bool permutationVerification = false;
+    bool pseudonymVerification = false;
+    bool serverTallyVerification = false;
+    bool firstHalfVerification = false;
+    bool secondHalfVerification = false;
+    bool userTallyVerification = true;
 
     if ((userTallyMaskCommits.empty() && doUserTallies) || (!userTallyMaskCommits.empty() && !doUserTallies))
     {
@@ -613,69 +1089,82 @@ bool PrsonaServer::accept_epoch_updates(
 
     size_t currOffset = 0;
 
-    verification = verify_valid_permutation_proof(pi[currOffset], permutationCommits);
+    std::thread permutationThread(verify_permutation_r, this, &permutationVerification, &(pi[currOffset]), &permutationCommits);
+    currOffset++;
+
+    std::thread pseudonymThread(verify_pseudonym_r, this, &pseudonymVerification, &(pi[currOffset]), &currentPseudonyms, &permutationCommits, &freshPseudonymCommits, &freshPseudonymSeedCommits);
+    currOffset++;
+
+    CurveBipoint curveG = bgnSystem.get_public_key().get_bipoint_curvegen();
+    CurveBipoint curveH = bgnSystem.get_public_key().get_bipoint_curve_subgroup_gen();
+    std::thread serverTallyThread(verify_server_tally_r, this, &serverTallyVerification, &(pi[currOffset]), &previousVoteTallies, &permutationCommits, &serverTallyCommits, &curveG, &curveH);
+    currOffset++;
+
+    size_t firstHalfOffset = currOffset;
+    std::thread firstHalfThread(verify_first_half_matrix_r, this, &firstHalfVerification, &pi, &firstHalfOffset, &voteMatrix, &permutationCommits, &partwayVoteMatrixCommits);
+    currOffset += voteMatrix.size();
+
+    size_t secondHalfOffset = currOffset;
+    std::thread secondHalfThread(verify_second_half_matrix_r, this, &secondHalfVerification, &pi, &secondHalfOffset, &permutationCommits, &partwayVoteMatrixCommits, &finalVoteMatrixCommits);
+    currOffset += voteMatrix.size();
+
+    std::thread *userTallyThread = NULL;
+    if (doUserTallies)
+        userTallyThread = new std::thread(verify_user_tally_r, this, &userTallyVerification, &(pi[currOffset]), &nextGenerator, &currentPseudonyms, &currentUserEncryptedTallies, &permutationCommits, &userTallyMaskCommits, &userTallyMessageCommits, &userTallySeedCommits);
     currOffset++;
-    if (!verification)
+
+    permutationThread.join();
+    pseudonymThread.join();
+    serverTallyThread.join();
+    firstHalfThread.join();
+    secondHalfThread.join();
+    if (userTallyThread)
+    {
+        userTallyThread->join();
+
+        delete userTallyThread;
+        userTallyThread = NULL;
+    }
+
+    if (!permutationVerification)
     {
         std::cerr << "Could not verify valid permutation matrix." << std::endl;
         return false;
     }
 
-    verification = verify_proof_of_reordering_plus_power(pi[currOffset], currentPseudonyms, permutationCommits, freshPseudonymCommits, freshPseudonymSeedCommits);
-    currOffset++;
-    if (!verification)
+    if (!pseudonymVerification)
     {
         std::cerr << "Could not verify valid pseudonym vector." << std::endl;
         return false;
     }
 
-    verification = verify_proof_of_reordering<CurveBipoint>( pi[currOffset], previousVoteTallies, permutationCommits, serverTallyCommits, bgnSystem.get_public_key().get_bipoint_curvegen(), bgnSystem.get_public_key().get_bipoint_curve_subgroup_gen());
-    currOffset++;
-    if (!verification)
+    if (!serverTallyVerification)
     {
         std::cerr << "Could not verify valid server tally vector." << std::endl;
         return false;
     }
 
-    verification = verify_vote_tensor_proofs(pi, currOffset, voteMatrix, permutationCommits, partwayVoteMatrixCommits, false);
-    currOffset += voteMatrix.size();
-    if (!verification)
+    if (!userTallyVerification)
     {
-        std::cerr << "Could not verify first half vote matrix." << std::endl;
+        std::cerr << "Could not verify user tallies." << std::endl;
         return false;
     }
 
-    std::vector<std::vector<TwistBipoint>> partialVoteMatrix = calculate_next_vote_matrix(partwayVoteMatrixCommits);
-    verification = verify_vote_tensor_proofs(pi, currOffset, partialVoteMatrix, permutationCommits, finalVoteMatrixCommits, true);
-    currOffset += voteMatrix.size();
-    if (!verification)
+    if (!firstHalfVerification)
     {
-        std::cerr << "Could not verify second half vote matrix." << std::endl;
+        std::cerr << "Could not verify first half vote matrix." << std::endl;
         return false;
     }
 
-    if (doUserTallies)
+    if (!secondHalfVerification)
     {
-        std::vector<Twistpoint> userTallyMasks;
-        std::vector<Twistpoint> userTallyMessages;
-        for (size_t i = 0; i < currentUserEncryptedTallies.size(); i++)
-        {
-            userTallyMasks.push_back(currentUserEncryptedTallies[i].mask);
-            userTallyMessages.push_back(currentUserEncryptedTallies[i].encryptedMessage);
-        }
-
-        verification = verify_user_tally_proofs(pi[currOffset], nextGenerator, currentPseudonyms, userTallyMasks, userTallyMessages, permutationCommits, userTallyMaskCommits, userTallyMessageCommits, userTallySeedCommits);
-        currOffset++;
-        if (!verification)
-        {
-            std::cerr << "Could not verify user tallies." << std::endl;
-            return false;
-        }
+        std::cerr << "Could not verify second half vote matrix." << std::endl;
+        return false;
     }
 
-    verification = update_data(freshPseudonymCommits, serverTallyCommits, finalVoteMatrixCommits, userTallyMaskCommits, userTallyMessageCommits);
+    bool finalVerification = update_data(freshPseudonymCommits, serverTallyCommits, finalVoteMatrixCommits, userTallyMaskCommits, userTallyMessageCommits);
 
-    return verification;
+    return finalVerification;
 }
 
 std::vector<std::vector<Scalar>> PrsonaServer::generate_permutation_matrix(
@@ -817,6 +1306,32 @@ std::vector<std::vector<TwistBipoint>> PrsonaServer::calculate_next_vote_matrix(
     return retval;
 }
 
+void generate_tensor_r(
+    const void *a,
+    void *b,
+    const void *c,
+    const void *d,
+    const void *e,
+    const void *f,
+    const void *g,
+    const void *h,
+    const void *i,
+    const void *j)
+{
+    const PrsonaServer *server = (const PrsonaServer *) a;
+    std::vector<Proof> *dst = (std::vector<Proof> *) b;
+    const std::vector<std::vector<Scalar>> *permutations = (const std::vector<std::vector<Scalar>> *) c;
+    const std::vector<std::vector<Scalar>> *permutationSeeds = (const std::vector<std::vector<Scalar>> *) d;
+    const std::vector<std::vector<Scalar>> *matrixSeeds = (const std::vector<std::vector<Scalar>> *) e;
+    const std::vector<TwistBipoint> *inputRow = (const std::vector<TwistBipoint> *) f;
+    const std::vector<std::vector<Twistpoint>> *permutationCommits = (const std::vector<std::vector<Twistpoint>> *) g;
+    const std::vector<std::vector<TwistBipoint>> *matrixCommits = (const std::vector<std::vector<TwistBipoint>> *) h;
+    const TwistBipoint *twistG = (const TwistBipoint *) i;
+    const TwistBipoint *twistH = (const TwistBipoint *) j;
+
+    *dst = server->generate_proof_of_reordering<TwistBipoint>(*permutations, *permutationSeeds, *matrixSeeds, *inputRow, *permutationCommits, *matrixCommits, *twistG, *twistH);
+}
+
 void PrsonaServer::generate_vote_tensor_proofs(
     std::vector<std::vector<Proof>>& pi,
     const std::vector<std::vector<Scalar>>& permutations,
@@ -827,21 +1342,62 @@ void PrsonaServer::generate_vote_tensor_proofs(
     const std::vector<std::vector<std::vector<TwistBipoint>>>& matrixCommits,
     bool inverted) const
 {
+    TwistBipoint twistG = bgnSystem.get_public_key().get_bipoint_twistgen();
+    TwistBipoint twistH = bgnSystem.get_public_key().get_bipoint_twist_subgroup_gen();
+
+    std::vector<Proof> *intermediaryProofs = new std::vector<Proof>[currMatrix.size()];
+    std::vector<TwistBipoint> *inputRows = new std::vector<TwistBipoint>[currMatrix.size()];
+    std::vector<std::thread> individualTensorProofThreads;
+
     for (size_t i = 0; i < currMatrix.size(); i++)
     {
-        std::vector<TwistBipoint> inputRow;
+        std::vector<TwistBipoint> currRow;
         if (inverted)
         {
             for (size_t j = 0; j < currMatrix.size(); j++)
-                inputRow.push_back(currMatrix[j][i]);
+                currRow.push_back(currMatrix[j][i]);
         }
         else
         {
-            inputRow = currMatrix[i];
+            currRow = currMatrix[i];
         }
+
+        inputRows[i] = currRow;
         
-        pi.push_back(generate_proof_of_reordering<TwistBipoint>(permutations, permutationSeeds, matrixSeeds[i], inputRow, permutationCommits, matrixCommits[i], bgnSystem.get_public_key().get_bipoint_twistgen(), bgnSystem.get_public_key().get_bipoint_twist_subgroup_gen()));
+        individualTensorProofThreads.push_back(std::thread(generate_tensor_r, this, intermediaryProofs + i, &permutations, &permutationSeeds, &(matrixSeeds[i]), inputRows + i, &permutationCommits, &(matrixCommits[i]), &twistG, &twistH));
     }
+
+    for (size_t i = 0; i < individualTensorProofThreads.size(); i++)
+    {
+        individualTensorProofThreads[i].join();
+
+        pi.push_back(intermediaryProofs[i]);
+    }
+
+    delete [] inputRows;
+    delete [] intermediaryProofs;
+}
+
+void verify_tensor_r(
+    const void *a,
+    void *b,
+    const void *c,
+    const void *d,
+    const void *e,
+    const void *f,
+    const void *g,
+    const void *h)
+{
+    const PrsonaServer *server = (const PrsonaServer *) a;
+    bool *dst = (bool *) b;
+    const std::vector<Proof> *pi = (const std::vector<Proof> *) c;
+    const std::vector<TwistBipoint> *inputRow = (const std::vector<TwistBipoint> *) d;
+    const std::vector<std::vector<Twistpoint>> *permutationCommits = (const std::vector<std::vector<Twistpoint>> *) e;
+    const std::vector<std::vector<TwistBipoint>> *matrixCommits = (const std::vector<std::vector<TwistBipoint>> *) f;
+    const TwistBipoint *twistG = (const TwistBipoint *) g;
+    const TwistBipoint *twistH = (const TwistBipoint *) h;
+
+    *dst = server->verify_proof_of_reordering<TwistBipoint>(*pi, *inputRow, *permutationCommits, *matrixCommits, *twistG, *twistH);
 }
 
 bool PrsonaServer::verify_vote_tensor_proofs(
@@ -852,25 +1408,44 @@ bool PrsonaServer::verify_vote_tensor_proofs(
     const std::vector<std::vector<std::vector<TwistBipoint>>>& matrixCommits,
     bool inverted) const
 {
-    bool retval = true;
+    TwistBipoint twistG = bgnSystem.get_public_key().get_bipoint_twistgen();
+    TwistBipoint twistH = bgnSystem.get_public_key().get_bipoint_twist_subgroup_gen();
+
+    bool *intermediaryValues = new bool[currMatrix.size()];
+    std::vector<TwistBipoint> *inputRows = new std::vector<TwistBipoint>[currMatrix.size()];
+    std::vector<std::thread> individualTensorProofThreads;
 
     for (size_t i = 0; i < currMatrix.size(); i++)
     {
-        std::vector<TwistBipoint> inputRow;
+        std::vector<TwistBipoint> currRow;
+
         if (inverted)
         {
             for (size_t j = 0; j < currMatrix.size(); j++)
-                inputRow.push_back(currMatrix[j][i]);
+                currRow.push_back(currMatrix[j][i]);
         }
         else
         {
-            inputRow = currMatrix[i];
+            currRow = currMatrix[i];
         }
 
+        inputRows[i] = currRow;
+
         size_t whichProof = i + start_offset;
-        retval = retval && verify_proof_of_reordering<TwistBipoint>(pi[whichProof], inputRow, permutationCommits, matrixCommits[i], bgnSystem.get_public_key().get_bipoint_twistgen(), bgnSystem.get_public_key().get_bipoint_twist_subgroup_gen());
+        individualTensorProofThreads.push_back(std::thread(verify_tensor_r, this, intermediaryValues + i, &(pi[whichProof]), inputRows + i, &permutationCommits, &(matrixCommits[i]), &twistG, &twistH));
+    }
+
+    bool retval = true;
+
+    for (size_t i = 0; i < individualTensorProofThreads.size(); i++)
+    {
+        individualTensorProofThreads[i].join();
+        retval = retval && intermediaryValues[i];
     }
 
+    delete [] inputRows;
+    delete [] intermediaryValues;
+
     return retval;
 }