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
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME

View File

@ -10,6 +10,9 @@ message BoolMsg {
bool value = 1;
}
message IntMsg {
int32 value = 1;
}
message MessageID {
// The ID of a message for unique retrieval.
@ -26,10 +29,10 @@ message UnsignedBulletinBoardMessage {
}
message BulletinBoardMessage {
// Serial entry number of message in database
int64 entryNum = 1;
// Unsigned raw data of message
UnsignedBulletinBoardMessage msg = 2;
@ -38,9 +41,9 @@ message BulletinBoardMessage {
}
message BulletinBoardMessageList {
repeated BulletinBoardMessage message = 1;
}
enum FilterType {
@ -49,13 +52,17 @@ enum FilterType {
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)
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
}
message MessageFilter {
FilterType type = 1;
oneof filter{
bytes id = 2;
int64 entry = 3;
@ -65,9 +72,36 @@ message MessageFilter {
}
message MessageFilterList {
// Combination of filters.
// To be implemented using intersection ("AND") operations.
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;
}
// 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 {
// TODO: different sets of keys for different roles?
repeated SignatureVerificationKey trusteeVerificationKeys = 1;
@ -75,4 +85,6 @@ message ElectionParams {
// Translation table between answers and plaintext encoding
BallotAnswerTranslationTable answerTranslationTable = 7;
// Data required in order to access the Bulletin Board Servers
BulletinBoardClientParams bulletinBoardClientParams = 8;
}

View File

@ -1,11 +1,20 @@
package main;
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import meerkat.crypto.mixnet.Mix2ZeroKnowledgeVerifier;
import meerkat.crypto.mixnet.Mixer;
import meerkat.protobuf.BulletinBoardAPI;
import meerkat.protobuf.Crypto;
import meerkat.protobuf.Mixing;
import necessary.AsyncBulletinBoardClient;
import necessary.BulletinBoardClient;
import necessary.SignedBatch;
import qilin.util.Pair;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@ -15,119 +24,247 @@ import java.util.List;
*/
public class MainMixing {
Mixer mixer;
Mix2ZeroKnowledgeVerifier verifier;
int n,layers;
private Mixer mixer;
private Mix2ZeroKnowledgeVerifier verifier;
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.mixerOrder = mixerOrder;
this.verifier = verifier;
this.n = n;
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
{
updateBB(mixer.mix(getInputForFirstMixer()));
}
public void main(List<Integer> prevBatchIds, int batchId, BulletinBoardClient.ClientCallback<?> callback) throws Exception {
private void otherMixing() throws Exception
{
List<Mixing.ZeroKnowledgeProof[][]> zeroKnowledgeProofsLists = downloadProofs();
List<Crypto.RerandomizableEncryptedMessage[][]> rerandomizableEncryptedMessageLists = downloadEncryptionTable();
for (int i = 0; i < zeroKnowledgeProofsLists.size(); i++)
{
if(!verifyTable(n,layers,zeroKnowledgeProofsLists.get(i),rerandomizableEncryptedMessageLists.get(i)))
throw new Exception();
}
Crypto.RerandomizableEncryptedMessage[] lastLayerInCurrent,firstLineInNext;
for (int i = 0; i < rerandomizableEncryptedMessageLists.size() - 1 ; i++)
{
lastLayerInCurrent = rerandomizableEncryptedMessageLists.get(i)[layers - 1];
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;
}
private List<Crypto.RerandomizableEncryptedMessage[][]> downloadEncryptionTable()
{
return null;
}
private List<Crypto.RerandomizableEncryptedMessage> getInputForFirstMixer()
{
return null;
}
private void updateBB(Pair<Mixing.ZeroKnowledgeProof[][],Crypto.RerandomizableEncryptedMessage[][]> mixerOutput){
}
private boolean verifyTable(int n,int layers, Mixing.ZeroKnowledgeProof[][] zeroKnowledgeProofs,
Crypto.RerandomizableEncryptedMessage[][] rerandomizableEncryptedMessages)
{
int index1,index2,layer;
//initialize locationChecksum table
// use for check BeneshNet validity
boolean[][] locationChecksum = new boolean[layers][n];
for (boolean[] locationChecksumLayer: locationChecksum) {
Arrays.fill(locationChecksumLayer,false);
}
List<Crypto.RerandomizableEncryptedMessage> mixerInput;
for (Mixing.ZeroKnowledgeProof[] zkpLayer: zeroKnowledgeProofs) {
for (Mixing.ZeroKnowledgeProof zkp: zkpLayer) {
Mixing.ZeroKnowledgeProof.Location location = zkp.getLocation();
index1 = location.getI();
index2 = location.getJ();
layer = location.getLayer();
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);
}
// check location validity
if (layer > layers >> 1) {
if (index2 - index1 != n >> (layers - layer))
return false;
boolean allDone = false;
while (!allDone) {
try {
Thread.sleep(30);
} catch (InterruptedException e) {
// do nothing
}
else{
if (index2 - index1 != n >> (layer + 1))
return false;
// check all handlers done
allDone = true;
for (BatchHandler batchHandler : batchHandlers) {
allDone &= batchHandler.done;
}
// mark location in table
locationChecksum[layer][index1] = true;
locationChecksum[layer][index2] = true;
// verify proof
if(!verifier.verify(rerandomizableEncryptedMessages[index1][layer],
rerandomizableEncryptedMessages[index2][layer],
rerandomizableEncryptedMessages[index1][layer + 1],
rerandomizableEncryptedMessages[index2][layer + 1],
zkp))
return false;
}
// assert all handlers succeeded
for (BatchHandler batchHandler : batchHandlers) {
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();
}
int nDiv2 = n >>1;
Mixing.ZeroKnowledgeProof[][] proofs = new Mixing.ZeroKnowledgeProof[layers][nDiv2];
for (int layer = 0; layer <layers; layer++)
{
for (int proofIndex = 0 ; proofIndex < nDiv2 ; proofIndex ++)
{
proofs[layer][proofIndex] = Mixing.ZeroKnowledgeProof.parseFrom(batchDataList.remove(0).getData());
}
}
Crypto.RerandomizableEncryptedMessage[][] encryptions
= new Crypto.RerandomizableEncryptedMessage[layers + 1][n];
for (int layer = 0; layer <layers + 1; layer++)
{
for (int encryptionIndex = 0 ; encryptionIndex < n ; encryptionIndex ++)
{
encryptions[layer][encryptionIndex] = Crypto.RerandomizableEncryptedMessage
.parseFrom(batchDataList.remove(0).getData());
}
}
return new Pair<Mixing.ZeroKnowledgeProof[][], Crypto.RerandomizableEncryptedMessage[][]>(proofs,encryptions);
}
}
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;
}
}
// verify all necessary locations for BeneshNet were proved
for (boolean[] checksumLayer: locationChecksum) {
for (boolean locationBoolean: checksumLayer) {
if (!locationBoolean)
return false;
}
@Override
public void handleFailure(Throwable t) {
}
private boolean verifyTable()
{
int index1,index2,layer;
//initialize locationChecksum table
// use for check BeneshNet validity
boolean[][] locationChecksum = new boolean[layers][n];
for (boolean[] locationChecksumLayer: locationChecksum) {
Arrays.fill(locationChecksumLayer,false);
}
Mixing.ZeroKnowledgeProof[][] zeroKnowledgeProofs = mixerOutput.a;
Crypto.RerandomizableEncryptedMessage[][] rerandomizableEncryptedMessages = mixerOutput.b;
for (Mixing.ZeroKnowledgeProof[] zkpLayer: zeroKnowledgeProofs) {
for (Mixing.ZeroKnowledgeProof zkp: zkpLayer) {
Mixing.ZeroKnowledgeProof.Location location = zkp.getLocation();
index1 = location.getI();
index2 = location.getJ();
layer = location.getLayer();
// check location validity
if (layer > layers >> 1) {
if (index2 - index1 != n >> (layers - layer))
return false;
}
else{
if (index2 - index1 != n >> (layer + 1))
return false;
}
// mark location in table
locationChecksum[layer][index1] = true;
locationChecksum[layer][index2] = true;
// verify proof
if(!verifier.verify(rerandomizableEncryptedMessages[index1][layer],
rerandomizableEncryptedMessages[index2][layer],
rerandomizableEncryptedMessages[index1][layer + 1],
rerandomizableEncryptedMessages[index2][layer + 1],
zkp))
return false;
}
}
// verify all necessary locations for BeneshNet were proved
for (boolean[] checksumLayer: locationChecksum) {
for (boolean locationBoolean: checksumLayer) {
if (!locationBoolean)
return false;
}
}
return true;
}
public List<Crypto.RerandomizableEncryptedMessage> getInputForMixer()
{
return Arrays.asList(mixerOutput.b[mixerOutput.b.length - 1]);
}
return true;
}
}

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