integrated to AsyncBBClient

mixer
tzlil.gon 2015-12-27 13:12:17 +02:00
parent be6449f27d
commit 75c411a5e7
7 changed files with 468 additions and 106 deletions

View File

@ -1,4 +1,4 @@
#Thu Dec 17 12:20:25 IST 2015 #Sun Dec 27 09:28:01 IST 2015
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME

View File

@ -10,6 +10,9 @@ message BoolMsg {
bool value = 1; bool value = 1;
} }
message IntMsg {
int32 value = 1;
}
message MessageID { message MessageID {
// The ID of a message for unique retrieval. // The ID of a message for unique retrieval.
@ -49,6 +52,10 @@ enum FilterType {
MAX_ENTRY = 2; // Find all entries in database up to specified entry number (chronological) MAX_ENTRY = 2; // Find all entries in database up to specified entry number (chronological)
SIGNER_ID = 3; // Find all entries in database that correspond to specific signature (signer) SIGNER_ID = 3; // Find all entries in database that correspond to specific signature (signer)
TAG = 4; // Find all entries in database that have a specific tag TAG = 4; // Find all entries in database that have a specific tag
// NOTE: The MAX_MESSAGES filter must remain the last filter type
// This is because the condition it specifies in an SQL statement must come last in the statement
// Keeping it last here allows for easily sorting the filters and keeping the code general
MAX_MESSAGES = 5; // Return at most some specified number of messages MAX_MESSAGES = 5; // Return at most some specified number of messages
} }
@ -71,3 +78,30 @@ message MessageFilterList {
repeated MessageFilter filter = 1; repeated MessageFilter filter = 1;
} }
// This message is used to start a batch transfer to the Bulletin Board Server
message BeginBatchMessage {
bytes signerId = 1; // Unique signer identifier
int32 batchId = 2; // Unique identifier for the batch (unique per signer)
repeated string tag = 3; // Tags for the batch message
}
// This message is used to finalize and sign a batch transfer to the Bulletin Board Server
message CloseBatchMessage {
int32 batchId = 1; // Unique identifier for the batch (unique per signer)
int32 batchLength = 2; // Number of messages in the batch
meerkat.Signature sig = 3; // Signature on the (ordered) batch messages
}
// Container for single batch message data
message BatchData {
bytes data = 1;
}
// These messages comprise a batch message
message BatchMessage {
bytes signerId = 1; // Unique signer identifier
int32 batchId = 2; // Unique identifier for the batch (unique per signer)
int32 serialNum = 3; // Location of the message in the batch: starting from 0
BatchData data = 4; // Actual data
}

View File

@ -52,6 +52,16 @@ message BallotAnswerTranslationTable {
bytes data = 1; bytes data = 1;
} }
// Data required in order to access the Bulletin Board Servers
message BulletinBoardClientParams {
// Addresses of all Bulletin Board Servers
repeated string bulletinBoardAddress = 1;
// Threshold fraction of successful servers posts before a post task is considered complete
float minRedundancy = 2;
}
message ElectionParams { message ElectionParams {
// TODO: different sets of keys for different roles? // TODO: different sets of keys for different roles?
repeated SignatureVerificationKey trusteeVerificationKeys = 1; repeated SignatureVerificationKey trusteeVerificationKeys = 1;
@ -75,4 +85,6 @@ message ElectionParams {
// Translation table between answers and plaintext encoding // Translation table between answers and plaintext encoding
BallotAnswerTranslationTable answerTranslationTable = 7; BallotAnswerTranslationTable answerTranslationTable = 7;
// Data required in order to access the Bulletin Board Servers
BulletinBoardClientParams bulletinBoardClientParams = 8;
} }

View File

@ -1,11 +1,20 @@
package main; package main;
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import meerkat.crypto.mixnet.Mix2ZeroKnowledgeVerifier; import meerkat.crypto.mixnet.Mix2ZeroKnowledgeVerifier;
import meerkat.crypto.mixnet.Mixer; import meerkat.crypto.mixnet.Mixer;
import meerkat.protobuf.BulletinBoardAPI;
import meerkat.protobuf.Crypto; import meerkat.protobuf.Crypto;
import meerkat.protobuf.Mixing; import meerkat.protobuf.Mixing;
import necessary.AsyncBulletinBoardClient;
import necessary.BulletinBoardClient;
import necessary.SignedBatch;
import qilin.util.Pair; import qilin.util.Pair;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
@ -15,70 +24,189 @@ import java.util.List;
*/ */
public class MainMixing { public class MainMixing {
Mixer mixer; private Mixer mixer;
Mix2ZeroKnowledgeVerifier verifier; private Mix2ZeroKnowledgeVerifier verifier;
int n,layers; private int n, layers;
private AsyncBulletinBoardClient asyncBulletinBoardClient;
private final byte[] id;
private final int mixerOrder;
public MainMixing(Mixer mixer, Mix2ZeroKnowledgeVerifier verifier, int n) {
public MainMixing(Mixer mixer, int mixerOrder, Mix2ZeroKnowledgeVerifier verifier, int n
, AsyncBulletinBoardClient asyncBulletinBoardClient, byte[] id) {
this.mixer = mixer; this.mixer = mixer;
this.mixerOrder = mixerOrder;
this.verifier = verifier; this.verifier = verifier;
this.n = n; this.n = n;
this.layers = (int) (2 * Math.log(n) / Math.log(2)) - 1; // layers = 2logn -1 this.layers = (int) (2 * Math.log(n) / Math.log(2)) - 1; // layers = 2logn -1
this.asyncBulletinBoardClient = asyncBulletinBoardClient;
this.id = id;
} }
private void firstMixing( ) throws Exception public void main(List<Integer> prevBatchIds, int batchId, BulletinBoardClient.ClientCallback<?> callback) throws Exception {
{
updateBB(mixer.mix(getInputForFirstMixer())); List<Crypto.RerandomizableEncryptedMessage> mixerInput;
if (mixerOrder > 0) {
List<BatchHandler> batchHandlers = new ArrayList<BatchHandler>(prevBatchIds.size());
BatchHandler currentBatchHandler;
for (Integer prevBatchId : prevBatchIds) {
currentBatchHandler = new BatchHandler(n, layers);
asyncBulletinBoardClient.readBatch(id, prevBatchId, currentBatchHandler);
batchHandlers.add(currentBatchHandler);
} }
private void otherMixing() throws Exception boolean allDone = false;
{ while (!allDone) {
List<Mixing.ZeroKnowledgeProof[][]> zeroKnowledgeProofsLists = downloadProofs(); try {
List<Crypto.RerandomizableEncryptedMessage[][]> rerandomizableEncryptedMessageLists = downloadEncryptionTable(); Thread.sleep(30);
} catch (InterruptedException e) {
// do nothing
}
// check all handlers done
allDone = true;
for (BatchHandler batchHandler : batchHandlers) {
allDone &= batchHandler.done;
}
}
for (int i = 0; i < zeroKnowledgeProofsLists.size(); i++) // assert all handlers succeeded
{ for (BatchHandler batchHandler : batchHandlers) {
if(!verifyTable(n,layers,zeroKnowledgeProofsLists.get(i),rerandomizableEncryptedMessageLists.get(i))) if(batchHandler.e != null)
throw batchHandler.e;
}
BatchHandler lastBatchHandler = batchHandlers.get(batchHandlers.size() - 1);
mixerInput = lastBatchHandler.getInputForMixer();
} else {
// ToDo : handle first mixer case
mixerInput = null;
}
Pair<Mixing.ZeroKnowledgeProof[][], Crypto.RerandomizableEncryptedMessage[][]> mixerOutput
= mixer.mix(mixerInput);
updateBB(mixerOutput, batchId, callback);
}
private void updateBB(Pair<Mixing.ZeroKnowledgeProof[][], Crypto.RerandomizableEncryptedMessage[][]> mixerOutput
, int batchId, BulletinBoardClient.ClientCallback<?> callback) {
BatchConverter batchConverter = new BatchConverter();
List<BulletinBoardAPI.BatchData> batchDataList = batchConverter.mixerOutput2BatchData(mixerOutput);
asyncBulletinBoardClient.postBatch(id, batchId, batchDataList, callback);
}
private class BatchConverter {
ByteString IntegerToByteString(int a){
return ByteString.copyFrom(BigInteger.valueOf(a).toByteArray());
}
int ByteString2Integer(ByteString bs) {
return Integer.valueOf(bs.toString());
}
List<BulletinBoardAPI.BatchData> mixerOutput2BatchData
(Pair<Mixing.ZeroKnowledgeProof[][], Crypto.RerandomizableEncryptedMessage[][]> mixerOutput) {
List<BulletinBoardAPI.BatchData> result = new ArrayList<BulletinBoardAPI.BatchData>();
//
result.add(BulletinBoardAPI.BatchData.newBuilder()
.setData(IntegerToByteString(n))
.build());
for (Mixing.ZeroKnowledgeProof[] zkpLayer : mixerOutput.a) {
for (Mixing.ZeroKnowledgeProof zkp : zkpLayer) {
result.add(BulletinBoardAPI.BatchData.newBuilder()
.setData(zkp.toByteString())
.build());
}
}
for (Crypto.RerandomizableEncryptedMessage[] encryptionLayer : mixerOutput.b) {
for (Crypto.RerandomizableEncryptedMessage encryption : encryptionLayer) {
result.add(BulletinBoardAPI.BatchData.newBuilder()
.setData(encryption.toByteString())
.build());
}
}
return result;
}
Pair<Mixing.ZeroKnowledgeProof[][], Crypto.RerandomizableEncryptedMessage[][]> batchDataList2MixerOutput
(List<BulletinBoardAPI.BatchData> batchDataList) throws Exception {
if (n != ByteString2Integer(batchDataList.remove(0).getData())){
throw new Exception(); throw new Exception();
} }
Crypto.RerandomizableEncryptedMessage[] lastLayerInCurrent,firstLineInNext; int nDiv2 = n >>1;
for (int i = 0; i < rerandomizableEncryptedMessageLists.size() - 1 ; i++) Mixing.ZeroKnowledgeProof[][] proofs = new Mixing.ZeroKnowledgeProof[layers][nDiv2];
for (int layer = 0; layer <layers; layer++)
{ {
lastLayerInCurrent = rerandomizableEncryptedMessageLists.get(i)[layers - 1]; for (int proofIndex = 0 ; proofIndex < nDiv2 ; proofIndex ++)
firstLineInNext = rerandomizableEncryptedMessageLists.get(i + 1)[0];
if(!Arrays.equals(lastLayerInCurrent,firstLineInNext))
throw new Exception();
}
List<Crypto.RerandomizableEncryptedMessage> inputForMixer =
Arrays.asList(rerandomizableEncryptedMessageLists
.get(rerandomizableEncryptedMessageLists.size() - 1)[layers - 1]);
updateBB(mixer.mix(inputForMixer));
}
private List<Mixing.ZeroKnowledgeProof[][]> downloadProofs()
{ {
return null; proofs[layer][proofIndex] = Mixing.ZeroKnowledgeProof.parseFrom(batchDataList.remove(0).getData());
}
} }
private List<Crypto.RerandomizableEncryptedMessage[][]> downloadEncryptionTable() Crypto.RerandomizableEncryptedMessage[][] encryptions
= new Crypto.RerandomizableEncryptedMessage[layers + 1][n];
for (int layer = 0; layer <layers + 1; layer++)
{ {
return null; for (int encryptionIndex = 0 ; encryptionIndex < n ; encryptionIndex ++)
}
private List<Crypto.RerandomizableEncryptedMessage> getInputForFirstMixer()
{ {
return null; encryptions[layer][encryptionIndex] = Crypto.RerandomizableEncryptedMessage
.parseFrom(batchDataList.remove(0).getData());
}
} }
private void updateBB(Pair<Mixing.ZeroKnowledgeProof[][],Crypto.RerandomizableEncryptedMessage[][]> mixerOutput){ return new Pair<Mixing.ZeroKnowledgeProof[][], Crypto.RerandomizableEncryptedMessage[][]>(proofs,encryptions);
} }
private boolean verifyTable(int n,int layers, Mixing.ZeroKnowledgeProof[][] zeroKnowledgeProofs, }
Crypto.RerandomizableEncryptedMessage[][] rerandomizableEncryptedMessages)
private class BatchHandler implements BulletinBoardClient.ClientCallback<SignedBatch> {
public Pair<Mixing.ZeroKnowledgeProof[][],Crypto.RerandomizableEncryptedMessage[][]> mixerOutput;
public boolean done;
private int n,layers;
private Exception e;
public BatchHandler(int n, int layers) {
this.n = n;
this.layers = layers;
done = false;
e = null;
}
// convert batch message to MixerInput
// and verify it
@Override
public void handleCallback(SignedBatch msg) {
BatchConverter batchConverter = new BatchConverter();
try {
mixerOutput = batchConverter.batchDataList2MixerOutput(msg.getBatchDataList());
done = verifyTable();
} catch (Exception e) {
this.e = e;
done = true;
}
}
@Override
public void handleFailure(Throwable t) {
}
private boolean verifyTable()
{ {
int index1,index2,layer; int index1,index2,layer;
@ -89,6 +217,8 @@ public class MainMixing {
Arrays.fill(locationChecksumLayer,false); Arrays.fill(locationChecksumLayer,false);
} }
Mixing.ZeroKnowledgeProof[][] zeroKnowledgeProofs = mixerOutput.a;
Crypto.RerandomizableEncryptedMessage[][] rerandomizableEncryptedMessages = mixerOutput.b;
for (Mixing.ZeroKnowledgeProof[] zkpLayer: zeroKnowledgeProofs) { for (Mixing.ZeroKnowledgeProof[] zkpLayer: zeroKnowledgeProofs) {
for (Mixing.ZeroKnowledgeProof zkp: zkpLayer) { for (Mixing.ZeroKnowledgeProof zkp: zkpLayer) {
@ -130,4 +260,11 @@ public class MainMixing {
} }
return true; return true;
} }
public List<Crypto.RerandomizableEncryptedMessage> getInputForMixer()
{
return Arrays.asList(mixerOutput.b[mixerOutput.b.length - 1]);
}
}
} }

View File

@ -0,0 +1,62 @@
package necessary;
import meerkat.protobuf.BulletinBoardAPI.*;
import java.util.List;
/**
* Created by Arbel Deutsch Peled on 14-Dec-15.
*/
public interface AsyncBulletinBoardClient extends BulletinBoardClient {
/**
* Post a message to the bulletin board in an asynchronous manner
* @param msg is the message to be posted
* @param callback is a class containing methods to handle the result of the operation
* @return a unique message ID for the message, that can be later used to retrieve the batch
*/
MessageID postMessage(BulletinBoardMessage msg, ClientCallback<?> callback);
/**
* This method allows for sending large messages as a batch to the bulletin board
* @param signerId is the canonical form for the ID of the sender of this batch
* @param batchId is a unique (per signer) ID for this batch
* @param batchDataList is the (canonically ordered) list of data comprising the batch message
* @param startPosition is the location (in the batch) of the first entry in batchDataList (optionally used to continue interrupted post operations)
* @param callback is a callback function class for handling results of the operation
* @return a unique message ID for the entire message, that can be later used to retrieve the batch
*/
MessageID postBatch(byte[] signerId, int batchId, List<BatchData> batchDataList, int startPosition, ClientCallback<?> callback);
/**
* Overloading of the postBatch method in which startPosition is set to the default value 0
*/
MessageID postBatch(byte[] signerId, int batchId, List<BatchData> batchDataList, ClientCallback<?> callback);
/**
* Check how "safe" a given message is in an asynchronous manner
* The result of the computation is a rank between 0.0 and 1.0 indicating the fraction of servers containing the message
* @param id is the unique message identifier for retrieval
* @param callback is a callback function class for handling results of the operation
*/
void getRedundancy(MessageID id, ClientCallback<Float> callback);
/**
* Read all messages posted matching the given filter in an asynchronous manner
* Note that if messages haven't been "fully posted", this might return a different
* set of messages in different calls. However, messages that are fully posted
* are guaranteed to be included.
* @param filterList return only messages that match the filters (null means no filtering).
* @param callback is a callback function class for handling results of the operation
*/
void readMessages(MessageFilterList filterList, ClientCallback<List<BulletinBoardMessage>> callback);
/**
* Read a given batch message from the bulletin board
* @param signerId is the ID of the signer (sender) of the batch message
* @param batchId is the unique (per signer) ID of the batch
* @param callback is a callback class for handling the result of the operation
*/
void readBatch(byte[] signerId, int batchId, ClientCallback<SignedBatch> callback);
}

View File

@ -0,0 +1,63 @@
package necessary;
/**
* Created by Tzlil on 12/27/2015.
*/
import meerkat.comm.CommunicationException;
import meerkat.protobuf.Voting.*;
import static meerkat.protobuf.BulletinBoardAPI.*;
import java.util.List;
/**
* Created by talm on 24/10/15.
*/
public interface BulletinBoardClient {
interface ClientCallback<T> {
void handleCallback(T msg);
void handleFailure(Throwable t);
}
/**
* Initialize the client to use some specified servers
* @param clientParams contains the parameters required for the client setup
*/
void init(BulletinBoardClientParams clientParams);
/**
* Post a message to the bulletin board in a synchronous manner
* @param msg is the message to be posted
* @return a unique message ID for the message, that can be later used to retrieve the batch
* @throws CommunicationException
*/
MessageID postMessage(BulletinBoardMessage msg) throws CommunicationException;
/**
* Check how "safe" a given message is in a synchronous manner
* @param id is the unique message identifier for retrieval
* @return a normalized "redundancy score" from 0 (local only) to 1 (fully published)
*/
float getRedundancy(MessageID id);
/**
* Read all messages posted matching the given filter in a synchronous manner
* Note that if messages haven't been "fully posted", this might return a different
* set of messages in different calls. However, messages that are fully posted
* are guaranteed to be included.
* @param filterList return only messages that match the filters (null means no filtering).
* @return the list of messages
*/
List<BulletinBoardMessage> readMessages(MessageFilterList filterList);
/**
* Closes all connections, if any.
* This is done in a synchronous (blocking) way.
*/
void close();
}

View File

@ -0,0 +1,54 @@
package necessary;
/**
* Created by Tzlil on 12/27/2015.
*/
import meerkat.protobuf.BulletinBoardAPI.*;
import meerkat.protobuf.Crypto.*;
import java.util.LinkedList;
import java.util.List;
/**
* Created by Arbel Deutsch Peled on 14-Dec-15.
*
* A data structure for holding both a batch message and its signature
*/
public class SignedBatch {
private List<BatchData> batchDataList;
private Signature signature;
public SignedBatch() {
batchDataList = new LinkedList<BatchData>();
}
public SignedBatch(List<BatchData> newDataList) {
this();
appendBatchData(newDataList);
}
public SignedBatch(List<BatchData> newDataList, Signature newSignature) {
this(newDataList);
signature = newSignature;
}
public List<BatchData> getBatchDataList() {
return batchDataList;
}
public Signature getSignature() {
return signature;
}
public void appendBatchData(BatchData newBatchData) {
batchDataList.add(newBatchData);
}
public void appendBatchData(List<BatchData> newBatchDataList) {
batchDataList.addAll(newBatchDataList);
}
}