Complete overhaul of batch system on server-side
Added batch methods to BulletinBoardUtils Related changes in BulletinBoardDigest and BulletinBoardSignature Merge remote-tracking branch 'origin/master' into Cached-Client Conflicts: bulletin-board-client/src/main/java/meerkat/bulletinboard/SimpleBulletinBoardClient.java bulletin-board-client/src/main/java/meerkat/bulletinboard/ThreadedBulletinBoardClient.java bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/BulletinBoardSQLServer.java bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/H2QueryProvider.java bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/MySQLQueryProvider.java bulletin-board-server/src/main/java/meerkat/bulletinboard/webapp/BulletinBoardWebApp.java bulletin-board-server/src/main/proto/meerkat/bulletin_board_server.proto meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardClient.java meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardServer.javaCached-Client
commit
b501992643
|
@ -13,4 +13,5 @@ out
|
||||||
*.prefs
|
*.prefs
|
||||||
*.project
|
*.project
|
||||||
*.classpath
|
*.classpath
|
||||||
bulletin-board-server/local-instances/meerkat.db
|
*.db
|
||||||
|
*.sql
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package meerkat.bulletinboard;
|
package meerkat.bulletinboard;
|
||||||
|
|
||||||
import com.google.protobuf.ByteString;
|
import meerkat.protobuf.BulletinBoardAPI.BatchChunk;
|
||||||
import meerkat.protobuf.BulletinBoardAPI.BatchData;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -13,13 +12,13 @@ public class BatchDataContainer {
|
||||||
|
|
||||||
public final byte[] signerId;
|
public final byte[] signerId;
|
||||||
public final int batchId;
|
public final int batchId;
|
||||||
public final List<BatchData> batchDataList;
|
public final List<BatchChunk> batchChunkList;
|
||||||
public final int startPosition;
|
public final int startPosition;
|
||||||
|
|
||||||
public BatchDataContainer(byte[] signerId, int batchId, List<BatchData> batchDataList, int startPosition) {
|
public BatchDataContainer(byte[] signerId, int batchId, List<BatchChunk> batchChunkList, int startPosition) {
|
||||||
this.signerId = signerId;
|
this.signerId = signerId;
|
||||||
this.batchId = batchId;
|
this.batchId = batchId;
|
||||||
this.batchDataList = batchDataList;
|
this.batchChunkList = batchChunkList;
|
||||||
this.startPosition = startPosition;
|
this.startPosition = startPosition;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -162,13 +162,13 @@ public class CachedBulletinBoardClient implements SubscriptionBulletinBoardClien
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void postBatchData(final byte[] signerId, final int batchId, final List<BatchData> batchDataList,
|
public void postBatchData(final byte[] signerId, final int batchId, final List<BatchChunk> batchChunkList,
|
||||||
final int startPosition, final FutureCallback<Boolean> callback) {
|
final int startPosition, final FutureCallback<Boolean> callback) {
|
||||||
|
|
||||||
localClient.postBatchData(signerId, batchId, batchDataList, startPosition, new FutureCallback<Boolean>() {
|
localClient.postBatchData(signerId, batchId, batchChunkList, startPosition, new FutureCallback<Boolean>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Boolean result) {
|
public void onSuccess(Boolean result) {
|
||||||
remoteClient.postBatchData(signerId, batchId, batchDataList, startPosition, callback);
|
remoteClient.postBatchData(signerId, batchId, batchChunkList, startPosition, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -181,12 +181,12 @@ public class CachedBulletinBoardClient implements SubscriptionBulletinBoardClien
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void postBatchData(final byte[] signerId, final int batchId, final List<BatchData> batchDataList, final FutureCallback<Boolean> callback) {
|
public void postBatchData(final byte[] signerId, final int batchId, final List<BatchChunk> batchChunkList, final FutureCallback<Boolean> callback) {
|
||||||
|
|
||||||
localClient.postBatchData(signerId, batchId, batchDataList, new FutureCallback<Boolean>() {
|
localClient.postBatchData(signerId, batchId, batchChunkList, new FutureCallback<Boolean>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Boolean result) {
|
public void onSuccess(Boolean result) {
|
||||||
remoteClient.postBatchData(signerId, batchId, batchDataList, callback);
|
remoteClient.postBatchData(signerId, batchId, batchChunkList, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -199,13 +199,13 @@ public class CachedBulletinBoardClient implements SubscriptionBulletinBoardClien
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void postBatchData(final ByteString signerId, final int batchId, final List<BatchData> batchDataList,
|
public void postBatchData(final ByteString signerId, final int batchId, final List<BatchChunk> batchChunkList,
|
||||||
final int startPosition, final FutureCallback<Boolean> callback) {
|
final int startPosition, final FutureCallback<Boolean> callback) {
|
||||||
|
|
||||||
localClient.postBatchData(signerId, batchId, batchDataList, startPosition, new FutureCallback<Boolean>() {
|
localClient.postBatchData(signerId, batchId, batchChunkList, startPosition, new FutureCallback<Boolean>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Boolean result) {
|
public void onSuccess(Boolean result) {
|
||||||
remoteClient.postBatchData(signerId, batchId, batchDataList, startPosition, callback);
|
remoteClient.postBatchData(signerId, batchId, batchChunkList, startPosition, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -218,12 +218,12 @@ public class CachedBulletinBoardClient implements SubscriptionBulletinBoardClien
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void postBatchData(final ByteString signerId, final int batchId, final List<BatchData> batchDataList, final FutureCallback<Boolean> callback) {
|
public void postBatchData(final ByteString signerId, final int batchId, final List<BatchChunk> batchChunkList, final FutureCallback<Boolean> callback) {
|
||||||
|
|
||||||
localClient.postBatchData(signerId, batchId, batchDataList, new FutureCallback<Boolean>() {
|
localClient.postBatchData(signerId, batchId, batchChunkList, new FutureCallback<Boolean>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Boolean result) {
|
public void onSuccess(Boolean result) {
|
||||||
remoteClient.postBatchData(signerId, batchId, batchDataList, callback);
|
remoteClient.postBatchData(signerId, batchId, batchChunkList, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -92,7 +92,7 @@ public class LocalBulletinBoardClient implements DeletableSubscriptionBulletinBo
|
||||||
.setBatchId(completeBatch.getBeginBatchMessage().getBatchId());
|
.setBatchId(completeBatch.getBeginBatchMessage().getBatchId());
|
||||||
|
|
||||||
int i=0;
|
int i=0;
|
||||||
for (BatchData data : completeBatch.getBatchDataList()){
|
for (BatchChunk data : completeBatch.getBatchDataList()){
|
||||||
|
|
||||||
server.postBatchMessage(builder.setSerialNum(i).setData(data).build());
|
server.postBatchMessage(builder.setSerialNum(i).setData(data).build());
|
||||||
|
|
||||||
|
@ -100,7 +100,7 @@ public class LocalBulletinBoardClient implements DeletableSubscriptionBulletinBo
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return server.closeBatchMessage(completeBatch.getCloseBatchMessage()).getValue();
|
return server.closeBatch(completeBatch.getCloseBatchMessage()).getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -140,13 +140,13 @@ public class LocalBulletinBoardClient implements DeletableSubscriptionBulletinBo
|
||||||
|
|
||||||
private final ByteString signerId;
|
private final ByteString signerId;
|
||||||
private final int batchId;
|
private final int batchId;
|
||||||
private final List<BatchData> batchDataList;
|
private final List<BatchChunk> batchChunkList;
|
||||||
private final int startPosition;
|
private final int startPosition;
|
||||||
|
|
||||||
public BatchDataPoster(ByteString signerId, int batchId, List<BatchData> batchDataList, int startPosition) {
|
public BatchDataPoster(ByteString signerId, int batchId, List<BatchChunk> batchChunkList, int startPosition) {
|
||||||
this.signerId = signerId;
|
this.signerId = signerId;
|
||||||
this.batchId = batchId;
|
this.batchId = batchId;
|
||||||
this.batchDataList = batchDataList;
|
this.batchChunkList = batchChunkList;
|
||||||
this.startPosition = startPosition;
|
this.startPosition = startPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,7 +159,7 @@ public class LocalBulletinBoardClient implements DeletableSubscriptionBulletinBo
|
||||||
.setBatchId(batchId);
|
.setBatchId(batchId);
|
||||||
|
|
||||||
int i = startPosition;
|
int i = startPosition;
|
||||||
for (BatchData data : batchDataList){
|
for (BatchChunk data : batchChunkList){
|
||||||
|
|
||||||
msgBuilder.setSerialNum(i)
|
msgBuilder.setSerialNum(i)
|
||||||
.setData(data);
|
.setData(data);
|
||||||
|
@ -178,23 +178,23 @@ public class LocalBulletinBoardClient implements DeletableSubscriptionBulletinBo
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void postBatchData(byte[] signerId, int batchId, List<BatchData> batchDataList, int startPosition, FutureCallback<Boolean> callback) {
|
public void postBatchData(byte[] signerId, int batchId, List<BatchChunk> batchChunkList, int startPosition, FutureCallback<Boolean> callback) {
|
||||||
postBatchData(ByteString.copyFrom(signerId), batchId, batchDataList, startPosition, callback);
|
postBatchData(ByteString.copyFrom(signerId), batchId, batchChunkList, startPosition, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void postBatchData(byte[] signerId, int batchId, List<BatchData> batchDataList, FutureCallback<Boolean> callback) {
|
public void postBatchData(byte[] signerId, int batchId, List<BatchChunk> batchChunkList, FutureCallback<Boolean> callback) {
|
||||||
postBatchData(signerId, batchId, batchDataList, 0, callback);
|
postBatchData(signerId, batchId, batchChunkList, 0, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void postBatchData(ByteString signerId, int batchId, List<BatchData> batchDataList, int startPosition, FutureCallback<Boolean> callback) {
|
public void postBatchData(ByteString signerId, int batchId, List<BatchChunk> batchChunkList, int startPosition, FutureCallback<Boolean> callback) {
|
||||||
Futures.addCallback(executorService.submit(new BatchDataPoster(signerId, batchId, batchDataList, startPosition)), callback);
|
Futures.addCallback(executorService.submit(new BatchDataPoster(signerId, batchId, batchChunkList, startPosition)), callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void postBatchData(ByteString signerId, int batchId, List<BatchData> batchDataList, FutureCallback<Boolean> callback) {
|
public void postBatchData(ByteString signerId, int batchId, List<BatchChunk> batchChunkList, FutureCallback<Boolean> callback) {
|
||||||
postBatchData(signerId, batchId, batchDataList, 0, callback);
|
postBatchData(signerId, batchId, batchChunkList, 0, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class BatchCloser implements Callable<Boolean> {
|
private class BatchCloser implements Callable<Boolean> {
|
||||||
|
@ -208,7 +208,7 @@ public class LocalBulletinBoardClient implements DeletableSubscriptionBulletinBo
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Boolean call() throws Exception {
|
public Boolean call() throws Exception {
|
||||||
return server.closeBatchMessage(msg).getValue();
|
return server.closeBatch(msg).getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -287,7 +287,7 @@ public class LocalBulletinBoardClient implements DeletableSubscriptionBulletinBo
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class BatchDataReader implements Callable<List<BatchData>> {
|
private class BatchDataReader implements Callable<List<BatchChunk>> {
|
||||||
|
|
||||||
private final BatchSpecificationMessage batchSpecificationMessage;
|
private final BatchSpecificationMessage batchSpecificationMessage;
|
||||||
|
|
||||||
|
@ -296,16 +296,16 @@ public class LocalBulletinBoardClient implements DeletableSubscriptionBulletinBo
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<BatchData> call() throws Exception {
|
public List<BatchChunk> call() throws Exception {
|
||||||
|
|
||||||
ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream();
|
ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream();
|
||||||
MessageOutputStream<BatchData> outputStream = new MessageOutputStream<>(byteOutputStream);
|
MessageOutputStream<BatchChunk> outputStream = new MessageOutputStream<>(byteOutputStream);
|
||||||
server.readBatch(batchSpecificationMessage, outputStream);
|
server.readBatch(batchSpecificationMessage, outputStream);
|
||||||
|
|
||||||
MessageInputStream<BatchData> inputStream =
|
MessageInputStream<BatchChunk> inputStream =
|
||||||
MessageInputStreamFactory.createMessageInputStream(
|
MessageInputStreamFactory.createMessageInputStream(
|
||||||
new ByteArrayInputStream(byteOutputStream.toByteArray()),
|
new ByteArrayInputStream(byteOutputStream.toByteArray()),
|
||||||
BatchData.class);
|
BatchChunk.class);
|
||||||
|
|
||||||
return inputStream.asList();
|
return inputStream.asList();
|
||||||
|
|
||||||
|
@ -387,7 +387,7 @@ public class LocalBulletinBoardClient implements DeletableSubscriptionBulletinBo
|
||||||
subscribe(filterList, 0, callback);
|
subscribe(filterList, 0, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class CompleteBatchReader implements Callable<CompleteBatch> {
|
private class CompleteBatchReader implements Callable<BulletinBoardMessage> {
|
||||||
|
|
||||||
private final BatchSpecificationMessage batchSpecificationMessage;
|
private final BatchSpecificationMessage batchSpecificationMessage;
|
||||||
|
|
||||||
|
@ -397,7 +397,7 @@ public class LocalBulletinBoardClient implements DeletableSubscriptionBulletinBo
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompleteBatch call() throws Exception {
|
public BulletinBoardMessage call() throws Exception {
|
||||||
|
|
||||||
final String[] TAGS_TO_REMOVE = {BulletinBoardConstants.BATCH_TAG, BulletinBoardConstants.BATCH_ID_TAG_PREFIX};
|
final String[] TAGS_TO_REMOVE = {BulletinBoardConstants.BATCH_TAG, BulletinBoardConstants.BATCH_ID_TAG_PREFIX};
|
||||||
|
|
||||||
|
@ -407,13 +407,13 @@ public class LocalBulletinBoardClient implements DeletableSubscriptionBulletinBo
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream();
|
ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream();
|
||||||
MessageOutputStream<BatchData> batchOutputStream = new MessageOutputStream<>(byteOutputStream);
|
MessageOutputStream<BatchChunk> batchOutputStream = new MessageOutputStream<>(byteOutputStream);
|
||||||
server.readBatch(batchSpecificationMessage,batchOutputStream);
|
server.readBatch(batchSpecificationMessage,batchOutputStream);
|
||||||
|
|
||||||
MessageInputStream<BatchData> batchInputStream =
|
MessageInputStream<BatchChunk> batchInputStream =
|
||||||
MessageInputStreamFactory.createMessageInputStream(
|
MessageInputStreamFactory.createMessageInputStream(
|
||||||
new ByteArrayInputStream(byteOutputStream.toByteArray()),
|
new ByteArrayInputStream(byteOutputStream.toByteArray()),
|
||||||
BatchData.class);
|
BatchChunk.class);
|
||||||
|
|
||||||
completeBatch.appendBatchData(batchInputStream.asList());
|
completeBatch.appendBatchData(batchInputStream.asList());
|
||||||
|
|
||||||
|
@ -462,7 +462,7 @@ public class LocalBulletinBoardClient implements DeletableSubscriptionBulletinBo
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void readBatch(BatchSpecificationMessage batchSpecificationMessage, FutureCallback<CompleteBatch> callback) {
|
public void readBatch(BatchSpecificationMessage batchSpecificationMessage, FutureCallback<BulletinBoardMessage> callback) {
|
||||||
Futures.addCallback(executorService.submit(new CompleteBatchReader(batchSpecificationMessage)), callback);
|
Futures.addCallback(executorService.submit(new CompleteBatchReader(batchSpecificationMessage)), callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -568,8 +568,8 @@ public class LocalBulletinBoardClient implements DeletableSubscriptionBulletinBo
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
List<BatchData> batchDataList = batchDataReader.call();
|
List<BatchChunk> batchChunkList = batchDataReader.call();
|
||||||
return new CompleteBatch(batchMessage, batchDataList);
|
return new CompleteBatch(batchMessage, batchChunkList);
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new CommunicationException("Error reading batch");
|
throw new CommunicationException("Error reading batch");
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package meerkat.bulletinboard;
|
package meerkat.bulletinboard;
|
||||||
|
|
||||||
|
import com.google.protobuf.BoolValue;
|
||||||
import com.google.protobuf.ByteString;
|
import com.google.protobuf.ByteString;
|
||||||
import meerkat.bulletinboard.workers.singleserver.*;
|
import meerkat.bulletinboard.workers.singleserver.*;
|
||||||
import meerkat.comm.CommunicationException;
|
import meerkat.comm.CommunicationException;
|
||||||
|
@ -70,18 +71,13 @@ public class SimpleBulletinBoardClient implements BulletinBoardClient{
|
||||||
// Only consider valid responses
|
// Only consider valid responses
|
||||||
if (response.getStatusInfo() == Response.Status.OK
|
if (response.getStatusInfo() == Response.Status.OK
|
||||||
|| response.getStatusInfo() == Response.Status.CREATED) {
|
|| response.getStatusInfo() == Response.Status.CREATED) {
|
||||||
response.readEntity(BoolMsg.class).getValue();
|
response.readEntity(BoolValue.class).getValue();
|
||||||
} else {
|
} else {
|
||||||
throw new CommunicationException("Server returned error. Status was: " + response.getStatus());
|
throw new CommunicationException("Server returned error. Status was: " + response.getStatus());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) { // Occurs only when server replies with valid status but invalid data
|
} catch (Exception e) { // Occurs only when server replies with valid status but invalid data
|
||||||
|
throw new CommunicationException("Error accessing database: " + e.getMessage());
|
||||||
throw new CommunicationException("Server returned invalid data type: " + e.getMessage());
|
|
||||||
|
|
||||||
} finally {
|
|
||||||
if (response != null)
|
|
||||||
response.close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate the correct message ID and return it
|
// Calculate the correct message ID and return it
|
||||||
|
@ -107,12 +103,12 @@ public class SimpleBulletinBoardClient implements BulletinBoardClient{
|
||||||
|
|
||||||
BatchMessage.Builder builder = BatchMessage.newBuilder().setSignerId(signerID).setBatchId(batchID);
|
BatchMessage.Builder builder = BatchMessage.newBuilder().setSignerId(signerID).setBatchId(batchID);
|
||||||
|
|
||||||
for (BatchData batchData : completeBatch.getBatchDataList()) {
|
for (BatchChunk batchChunk : completeBatch.getBatchDataList()) {
|
||||||
|
|
||||||
SingleServerPostBatchWorker postBatchWorker =
|
SingleServerPostBatchWorker postBatchWorker =
|
||||||
new SingleServerPostBatchWorker(
|
new SingleServerPostBatchWorker(
|
||||||
db,
|
db,
|
||||||
builder.setData(batchData).setSerialNum(pos).build(),
|
builder.setData(batchChunk).setSerialNum(pos).build(),
|
||||||
0);
|
0);
|
||||||
|
|
||||||
postBatchWorker.call();
|
postBatchWorker.call();
|
||||||
|
@ -187,6 +183,7 @@ public class SimpleBulletinBoardClient implements BulletinBoardClient{
|
||||||
for (String db : meerkatDBs) {
|
for (String db : meerkatDBs) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
webTarget = client.target(db).path(BULLETIN_BOARD_SERVER_PATH).path(READ_MESSAGES_PATH);
|
||||||
|
|
||||||
SingleServerReadMessagesWorker worker = new SingleServerReadMessagesWorker(db, filterList, 0);
|
SingleServerReadMessagesWorker worker = new SingleServerReadMessagesWorker(db, filterList, 0);
|
||||||
|
|
||||||
|
@ -235,9 +232,9 @@ public class SimpleBulletinBoardClient implements BulletinBoardClient{
|
||||||
|
|
||||||
SingleServerReadBatchWorker batchWorker = new SingleServerReadBatchWorker(db, batchSpecificationMessage, 0);
|
SingleServerReadBatchWorker batchWorker = new SingleServerReadBatchWorker(db, batchSpecificationMessage, 0);
|
||||||
|
|
||||||
List<BatchData> batchDataList = batchWorker.call();
|
List<BatchChunk> batchChunkList = batchWorker.call();
|
||||||
|
|
||||||
CompleteBatch result = new CompleteBatch(batchMessage, batchDataList);
|
CompleteBatch result = new CompleteBatch(batchMessage, batchChunkList);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
|
|
|
@ -6,12 +6,9 @@ import com.google.common.util.concurrent.ListeningScheduledExecutorService;
|
||||||
import com.google.common.util.concurrent.MoreExecutors;
|
import com.google.common.util.concurrent.MoreExecutors;
|
||||||
import com.google.protobuf.ByteString;
|
import com.google.protobuf.ByteString;
|
||||||
import meerkat.bulletinboard.workers.singleserver.*;
|
import meerkat.bulletinboard.workers.singleserver.*;
|
||||||
import meerkat.comm.CommunicationException;
|
|
||||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||||
import meerkat.protobuf.Voting.BulletinBoardClientParams;
|
import meerkat.protobuf.Voting.BulletinBoardClientParams;
|
||||||
import meerkat.util.BulletinBoardUtils;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -174,7 +171,7 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i
|
||||||
|
|
||||||
private final FutureCallback<CompleteBatch> callback;
|
private final FutureCallback<CompleteBatch> callback;
|
||||||
|
|
||||||
private List<BatchData> batchDataList;
|
private List<BatchChunk> batchChunkList;
|
||||||
private BulletinBoardMessage batchMessage;
|
private BulletinBoardMessage batchMessage;
|
||||||
|
|
||||||
private AtomicInteger remainingQueries;
|
private AtomicInteger remainingQueries;
|
||||||
|
@ -198,7 +195,7 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i
|
||||||
if (remainingQueries.decrementAndGet() == 0){
|
if (remainingQueries.decrementAndGet() == 0){
|
||||||
|
|
||||||
if (callback != null)
|
if (callback != null)
|
||||||
callback.onSuccess(new CompleteBatch(batchMessage, batchDataList));
|
callback.onSuccess(new CompleteBatch(batchMessage, batchChunkList));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -213,12 +210,12 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i
|
||||||
/**
|
/**
|
||||||
* @return a FutureCallback for the Batch Data List that ties to this object
|
* @return a FutureCallback for the Batch Data List that ties to this object
|
||||||
*/
|
*/
|
||||||
public FutureCallback<List<BatchData>> asBatchDataListFutureCallback() {
|
public FutureCallback<List<BatchChunk>> asBatchDataListFutureCallback() {
|
||||||
return new FutureCallback<List<BatchData>>() {
|
return new FutureCallback<List<BatchChunk>>() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(List<BatchData> result) {
|
public void onSuccess(List<BatchChunk> result) {
|
||||||
batchDataList = result;
|
batchChunkList = result;
|
||||||
|
|
||||||
combineAndReturn();
|
combineAndReturn();
|
||||||
}
|
}
|
||||||
|
@ -449,7 +446,7 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void postBatchData(ByteString signerId, int batchId, List<BatchData> batchDataList,
|
public void postBatchData(ByteString signerId, int batchId, List<BatchChunk> batchChunkList,
|
||||||
int startPosition, FutureCallback<Boolean> callback) {
|
int startPosition, FutureCallback<Boolean> callback) {
|
||||||
|
|
||||||
BatchMessage.Builder builder = BatchMessage.newBuilder()
|
BatchMessage.Builder builder = BatchMessage.newBuilder()
|
||||||
|
@ -458,11 +455,11 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i
|
||||||
|
|
||||||
// Create a unified callback to aggregate successful posts
|
// Create a unified callback to aggregate successful posts
|
||||||
|
|
||||||
PostBatchDataListCallback listCallback = new PostBatchDataListCallback(batchDataList.size(), callback);
|
PostBatchDataListCallback listCallback = new PostBatchDataListCallback(batchChunkList.size(), callback);
|
||||||
|
|
||||||
// Iterate through data list
|
// Iterate through data list
|
||||||
|
|
||||||
for (BatchData data : batchDataList) {
|
for (BatchChunk data : batchChunkList) {
|
||||||
builder.setSerialNum(startPosition).setData(data);
|
builder.setSerialNum(startPosition).setData(data);
|
||||||
|
|
||||||
// Create worker with redundancy 1 and MAX_RETRIES retries
|
// Create worker with redundancy 1 and MAX_RETRIES retries
|
||||||
|
@ -479,24 +476,24 @@ public class SingleServerBulletinBoardClient extends SimpleBulletinBoardClient i
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void postBatchData(ByteString signerId, int batchId, List<BatchData> batchDataList, FutureCallback<Boolean> callback) {
|
public void postBatchData(ByteString signerId, int batchId, List<BatchChunk> batchChunkList, FutureCallback<Boolean> callback) {
|
||||||
|
|
||||||
postBatchData(signerId, batchId, batchDataList, 0, callback);
|
postBatchData(signerId, batchId, batchChunkList, 0, callback);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void postBatchData(byte[] signerId, int batchId, List<BatchData> batchDataList,
|
public void postBatchData(byte[] signerId, int batchId, List<BatchChunk> batchChunkList,
|
||||||
int startPosition, FutureCallback<Boolean> callback) {
|
int startPosition, FutureCallback<Boolean> callback) {
|
||||||
|
|
||||||
postBatchData(ByteString.copyFrom(signerId), batchId, batchDataList, startPosition, callback);
|
postBatchData(ByteString.copyFrom(signerId), batchId, batchChunkList, startPosition, callback);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void postBatchData(byte[] signerId, int batchId, List<BatchData> batchDataList, FutureCallback<Boolean> callback) {
|
public void postBatchData(byte[] signerId, int batchId, List<BatchChunk> batchChunkList, FutureCallback<Boolean> callback) {
|
||||||
|
|
||||||
postBatchData(signerId, batchId, batchDataList, 0, callback);
|
postBatchData(signerId, batchId, batchChunkList, 0, callback);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -127,10 +127,10 @@ public class ThreadedBulletinBoardClient extends SimpleBulletinBoardClient imple
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void postBatchData(byte[] signerId, int batchId, List<BatchData> batchDataList,
|
public void postBatchData(byte[] signerId, int batchId, List<BatchChunk> batchChunkList,
|
||||||
int startPosition, FutureCallback<Boolean> callback) {
|
int startPosition, FutureCallback<Boolean> callback) {
|
||||||
|
|
||||||
BatchDataContainer batchDataContainer = new BatchDataContainer(signerId, batchId, batchDataList, startPosition);
|
BatchDataContainer batchDataContainer = new BatchDataContainer(signerId, batchId, batchChunkList, startPosition);
|
||||||
|
|
||||||
// Create job
|
// Create job
|
||||||
MultiServerPostBatchDataWorker worker =
|
MultiServerPostBatchDataWorker worker =
|
||||||
|
@ -142,24 +142,24 @@ public class ThreadedBulletinBoardClient extends SimpleBulletinBoardClient imple
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void postBatchData(byte[] signerId, int batchId, List<BatchData> batchDataList, FutureCallback<Boolean> callback) {
|
public void postBatchData(byte[] signerId, int batchId, List<BatchChunk> batchChunkList, FutureCallback<Boolean> callback) {
|
||||||
|
|
||||||
postBatchData(signerId, batchId, batchDataList, 0, callback);
|
postBatchData(signerId, batchId, batchChunkList, 0, callback);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void postBatchData(ByteString signerId, int batchId, List<BatchData> batchDataList,
|
public void postBatchData(ByteString signerId, int batchId, List<BatchChunk> batchChunkList,
|
||||||
int startPosition, FutureCallback<Boolean> callback) {
|
int startPosition, FutureCallback<Boolean> callback) {
|
||||||
|
|
||||||
postBatchData(signerId.toByteArray(), batchId, batchDataList, startPosition, callback);
|
postBatchData(signerId.toByteArray(), batchId, batchChunkList, startPosition, callback);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void postBatchData(ByteString signerId, int batchId, List<BatchData> batchDataList, FutureCallback<Boolean> callback) {
|
public void postBatchData(ByteString signerId, int batchId, List<BatchChunk> batchChunkList, FutureCallback<Boolean> callback) {
|
||||||
|
|
||||||
postBatchData(signerId, batchId, batchDataList, 0, callback);
|
postBatchData(signerId, batchId, batchChunkList, 0, callback);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -251,3 +251,4 @@ public class ThreadedBulletinBoardClient extends SimpleBulletinBoardClient imple
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
==== BASE ====
|
||||||
|
|
|
@ -21,7 +21,7 @@ public class MultiServerPostBatchDataWorker extends MultiServerGenericPostWorker
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doPost(SingleServerBulletinBoardClient client, BatchDataContainer payload) {
|
protected void doPost(SingleServerBulletinBoardClient client, BatchDataContainer payload) {
|
||||||
client.postBatchData(payload.signerId, payload.batchId, payload.batchDataList, payload.startPosition, this);
|
client.postBatchData(payload.signerId, payload.batchId, payload.batchChunkList, payload.startPosition, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package meerkat.bulletinboard.workers.multiserver;
|
package meerkat.bulletinboard.workers.multiserver;
|
||||||
|
|
||||||
import com.google.common.util.concurrent.FutureCallback;
|
import com.google.common.util.concurrent.FutureCallback;
|
||||||
import meerkat.bulletinboard.CompleteBatch;
|
|
||||||
import meerkat.bulletinboard.SingleServerBulletinBoardClient;
|
import meerkat.bulletinboard.SingleServerBulletinBoardClient;
|
||||||
import meerkat.protobuf.BulletinBoardAPI.BulletinBoardMessage;
|
import meerkat.protobuf.BulletinBoardAPI.BulletinBoardMessage;
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ import static meerkat.bulletinboard.BulletinBoardConstants.CLOSE_BATCH_PATH;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Arbel Deutsch Peled on 27-Dec-15.
|
* Created by Arbel Deutsch Peled on 27-Dec-15.
|
||||||
* Tries to contact server once and perform a close batch operation
|
* Tries to contact server once and perform a stop batch operation
|
||||||
*/
|
*/
|
||||||
public class SingleServerCloseBatchWorker extends SingleServerGenericPostWorker<CloseBatchMessage> {
|
public class SingleServerCloseBatchWorker extends SingleServerGenericPostWorker<CloseBatchMessage> {
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
package meerkat.bulletinboard.workers.singleserver;
|
package meerkat.bulletinboard.workers.singleserver;
|
||||||
|
|
||||||
|
import com.google.protobuf.BoolValue;
|
||||||
import meerkat.bulletinboard.SingleServerWorker;
|
import meerkat.bulletinboard.SingleServerWorker;
|
||||||
import meerkat.comm.CommunicationException;
|
import meerkat.comm.CommunicationException;
|
||||||
import meerkat.protobuf.BulletinBoardAPI.BoolMsg;
|
import meerkat.protobuf.Comm.*;
|
||||||
import meerkat.rest.Constants;
|
import meerkat.rest.Constants;
|
||||||
|
|
||||||
import javax.ws.rs.ProcessingException;
|
import javax.ws.rs.ProcessingException;
|
||||||
|
@ -43,8 +44,8 @@ public class SingleServerGenericPostWorker<T> extends SingleServerWorker<T, Bool
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
// If a BoolMsg entity is returned: the post was successful
|
// If a BoolValue entity is returned: the post was successful
|
||||||
response.readEntity(BoolMsg.class);
|
response.readEntity(BoolValue.class);
|
||||||
return Boolean.TRUE;
|
return Boolean.TRUE;
|
||||||
|
|
||||||
} catch (ProcessingException | IllegalStateException e) {
|
} catch (ProcessingException | IllegalStateException e) {
|
||||||
|
|
|
@ -1,25 +1,20 @@
|
||||||
package meerkat.bulletinboard.workers.singleserver;
|
package meerkat.bulletinboard.workers.singleserver;
|
||||||
|
|
||||||
import meerkat.bulletinboard.CompleteBatch;
|
|
||||||
import meerkat.bulletinboard.SingleServerWorker;
|
import meerkat.bulletinboard.SingleServerWorker;
|
||||||
import meerkat.comm.CommunicationException;
|
import meerkat.comm.CommunicationException;
|
||||||
import meerkat.comm.MessageInputStream;
|
import meerkat.comm.MessageInputStream;
|
||||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||||
import meerkat.rest.Constants;
|
import meerkat.rest.Constants;
|
||||||
|
|
||||||
import javax.ws.rs.ProcessingException;
|
|
||||||
import javax.ws.rs.client.Client;
|
import javax.ws.rs.client.Client;
|
||||||
import javax.ws.rs.client.Entity;
|
import javax.ws.rs.client.Entity;
|
||||||
import javax.ws.rs.client.WebTarget;
|
import javax.ws.rs.client.WebTarget;
|
||||||
import javax.ws.rs.core.GenericType;
|
|
||||||
import javax.ws.rs.core.Response;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static meerkat.bulletinboard.BulletinBoardConstants.BULLETIN_BOARD_SERVER_PATH;
|
import static meerkat.bulletinboard.BulletinBoardConstants.BULLETIN_BOARD_SERVER_PATH;
|
||||||
import static meerkat.bulletinboard.BulletinBoardConstants.READ_MESSAGES_PATH;
|
|
||||||
import static meerkat.bulletinboard.BulletinBoardConstants.READ_BATCH_PATH;
|
import static meerkat.bulletinboard.BulletinBoardConstants.READ_BATCH_PATH;
|
||||||
|
|
||||||
import static meerkat.bulletinboard.BulletinBoardConstants.BATCH_ID_TAG_PREFIX;
|
import static meerkat.bulletinboard.BulletinBoardConstants.BATCH_ID_TAG_PREFIX;
|
||||||
|
@ -27,7 +22,7 @@ import static meerkat.bulletinboard.BulletinBoardConstants.BATCH_ID_TAG_PREFIX;
|
||||||
/**
|
/**
|
||||||
* Created by Arbel Deutsch Peled on 27-Dec-15.
|
* Created by Arbel Deutsch Peled on 27-Dec-15.
|
||||||
*/
|
*/
|
||||||
public class SingleServerReadBatchWorker extends SingleServerWorker<BatchSpecificationMessage, List<BatchData>> {
|
public class SingleServerReadBatchWorker extends SingleServerWorker<BatchSpecificationMessage, List<BatchChunk>> {
|
||||||
|
|
||||||
public SingleServerReadBatchWorker(String serverAddress, BatchSpecificationMessage payload, int maxRetry) {
|
public SingleServerReadBatchWorker(String serverAddress, BatchSpecificationMessage payload, int maxRetry) {
|
||||||
super(serverAddress, payload, maxRetry);
|
super(serverAddress, payload, maxRetry);
|
||||||
|
@ -39,7 +34,7 @@ public class SingleServerReadBatchWorker extends SingleServerWorker<BatchSpecifi
|
||||||
* @return the complete batch as read from the server
|
* @return the complete batch as read from the server
|
||||||
* @throws CommunicationException if the server's response is invalid
|
* @throws CommunicationException if the server's response is invalid
|
||||||
*/
|
*/
|
||||||
public List<BatchData> call() throws CommunicationException{
|
public List<BatchChunk> call() throws CommunicationException{
|
||||||
|
|
||||||
Client client = clientLocal.get();
|
Client client = clientLocal.get();
|
||||||
|
|
||||||
|
@ -50,11 +45,11 @@ public class SingleServerReadBatchWorker extends SingleServerWorker<BatchSpecifi
|
||||||
webTarget = client.target(serverAddress).path(BULLETIN_BOARD_SERVER_PATH).path(READ_BATCH_PATH);
|
webTarget = client.target(serverAddress).path(BULLETIN_BOARD_SERVER_PATH).path(READ_BATCH_PATH);
|
||||||
InputStream in = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(payload, Constants.MEDIATYPE_PROTOBUF), InputStream.class);
|
InputStream in = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(payload, Constants.MEDIATYPE_PROTOBUF), InputStream.class);
|
||||||
|
|
||||||
MessageInputStream<BatchData> inputStream = null;
|
MessageInputStream<BatchChunk> inputStream = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
inputStream = MessageInputStream.MessageInputStreamFactory.createMessageInputStream(in, BatchData.class);
|
inputStream = MessageInputStream.MessageInputStreamFactory.createMessageInputStream(in, BatchChunk.class);
|
||||||
|
|
||||||
return inputStream.asList();
|
return inputStream.asList();
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,6 @@ import meerkat.crypto.concrete.ECDSASignature;
|
||||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||||
import meerkat.protobuf.Crypto;
|
import meerkat.protobuf.Crypto;
|
||||||
import meerkat.util.BulletinBoardMessageComparator;
|
import meerkat.util.BulletinBoardMessageComparator;
|
||||||
import meerkat.util.BulletinBoardMessageGenerator;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
@ -266,11 +265,11 @@ public class GenericBulletinBoardClientTester {
|
||||||
|
|
||||||
for (int i = 0 ; i < length ; i++){
|
for (int i = 0 ; i < length ; i++){
|
||||||
|
|
||||||
BatchData batchData = BatchData.newBuilder()
|
BatchChunk batchChunk = BatchChunk.newBuilder()
|
||||||
.setData(ByteString.copyFrom(randomByteArray(i)))
|
.setData(ByteString.copyFrom(randomByteArray(i)))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
completeBatch.appendBatchData(batchData);
|
completeBatch.appendBatchData(batchChunk);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -508,7 +507,7 @@ public class GenericBulletinBoardClientTester {
|
||||||
.build())
|
.build())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
// Try to close the (unopened) batch;
|
// Try to stop the (unopened) batch;
|
||||||
|
|
||||||
bulletinBoardClient.closeBatch(closeBatchMessage, failPostCallback);
|
bulletinBoardClient.closeBatch(closeBatchMessage, failPostCallback);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package meerkat.bulletinboard.sqlserver;
|
package meerkat.bulletinboard.sqlserver;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.sql.*;
|
import java.sql.*;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
@ -8,13 +10,12 @@ import com.google.protobuf.*;
|
||||||
import com.google.protobuf.Timestamp;
|
import com.google.protobuf.Timestamp;
|
||||||
import meerkat.bulletinboard.*;
|
import meerkat.bulletinboard.*;
|
||||||
import meerkat.bulletinboard.sqlserver.mappers.*;
|
import meerkat.bulletinboard.sqlserver.mappers.*;
|
||||||
import static meerkat.bulletinboard.BulletinBoardConstants.*;
|
|
||||||
|
|
||||||
import meerkat.comm.CommunicationException;
|
import meerkat.comm.CommunicationException;
|
||||||
|
|
||||||
|
import meerkat.comm.MessageInputStream;
|
||||||
import meerkat.comm.MessageOutputStream;
|
import meerkat.comm.MessageOutputStream;
|
||||||
import meerkat.crypto.DigitalSignature;
|
import meerkat.crypto.DigitalSignature;
|
||||||
import meerkat.crypto.concrete.ECDSASignature;
|
|
||||||
import meerkat.crypto.concrete.SHA256Digest;
|
import meerkat.crypto.concrete.SHA256Digest;
|
||||||
|
|
||||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||||
|
@ -127,7 +128,17 @@ public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{
|
||||||
new int[] {Types.BLOB, Types.INTEGER}
|
new int[] {Types.BLOB, Types.INTEGER}
|
||||||
),
|
),
|
||||||
|
|
||||||
|
CHECK_BATCH_OPEN(
|
||||||
|
new String[] {"SignerId", "BatchId"},
|
||||||
|
new int[] {Types.BLOB, Types.INTEGER}
|
||||||
|
),
|
||||||
|
|
||||||
GET_BATCH_MESSAGE_DATA(
|
GET_BATCH_MESSAGE_DATA(
|
||||||
|
new String[] {"MsgId", "StartPosition"},
|
||||||
|
new int[] {Types.BLOB, Types.INTEGER}
|
||||||
|
),
|
||||||
|
|
||||||
|
GET_BATCH_MESSAGE_DATA_BY_IDS(
|
||||||
new String[] {"SignerId", "BatchId", "StartPosition"},
|
new String[] {"SignerId", "BatchId", "StartPosition"},
|
||||||
new int[] {Types.BLOB, Types.INTEGER, Types.INTEGER}
|
new int[] {Types.BLOB, Types.INTEGER, Types.INTEGER}
|
||||||
),
|
),
|
||||||
|
@ -150,6 +161,16 @@ public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{
|
||||||
REMOVE_BATCH_TAGS(
|
REMOVE_BATCH_TAGS(
|
||||||
new String[] {"SignerId", "BatchId"},
|
new String[] {"SignerId", "BatchId"},
|
||||||
new int[] {Types.BLOB, Types.INTEGER}
|
new int[] {Types.BLOB, Types.INTEGER}
|
||||||
|
),
|
||||||
|
|
||||||
|
REMOVE_BATCH_IDS(
|
||||||
|
new String[] {"SignerId", "BatchId"},
|
||||||
|
new int[] {Types.BLOB, Types.INTEGER}
|
||||||
|
),
|
||||||
|
|
||||||
|
ADD_ENTRY_NUM_TO_BATCH(
|
||||||
|
new String[] {"SignerId", "BatchId", "EntryNum"},
|
||||||
|
new int[] {Types.BLOB, Types.INTEGER, Types.INTEGER}
|
||||||
);
|
);
|
||||||
|
|
||||||
private String[] paramNames;
|
private String[] paramNames;
|
||||||
|
@ -365,7 +386,6 @@ public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{
|
||||||
// TODO write signature reading part.
|
// TODO write signature reading part.
|
||||||
|
|
||||||
digest = new GenericBulletinBoardDigest(new SHA256Digest());
|
digest = new GenericBulletinBoardDigest(new SHA256Digest());
|
||||||
signer = new GenericBatchDigitalSignature(new ECDSASignature());
|
|
||||||
|
|
||||||
jdbcTemplate = new NamedParameterJdbcTemplate(sqlQueryProvider.getDataSource());
|
jdbcTemplate = new NamedParameterJdbcTemplate(sqlQueryProvider.getDataSource());
|
||||||
|
|
||||||
|
@ -411,12 +431,12 @@ public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This procedure is used to convert a boolean to a BoolMsg.
|
* This procedure is used to convert a boolean to a BoolValue.
|
||||||
* @param b is the boolean to convert.
|
* @param b is the boolean to convert.
|
||||||
* @return a ProtoBuf message with boolean payload.
|
* @return a ProtoBuf message with boolean payload.
|
||||||
*/
|
*/
|
||||||
private BoolMsg boolToBoolMsg(boolean b){
|
private BoolValue boolToBoolValue(boolean b){
|
||||||
return BoolMsg.newBuilder()
|
return BoolValue.newBuilder()
|
||||||
.setValue(b)
|
.setValue(b)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
@ -425,14 +445,17 @@ public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{
|
||||||
/**
|
/**
|
||||||
* This method posts a messages to the server
|
* This method posts a messages to the server
|
||||||
* @param msg is the message to post
|
* @param msg is the message to post
|
||||||
* @param checkSignature decides whether ot not the method should check the signature before it posts the message
|
* @param precalculatedMsgID is an optional precalculated message ID
|
||||||
* @return TRUE if the post is successful and FALSE otherwise
|
* It is used when the message is the stub of a batch message
|
||||||
|
* In this case the validity of the signature is not checked either
|
||||||
|
* @return -1 if the message is not verified
|
||||||
|
* The entry number of the message if the message is posted
|
||||||
* @throws CommunicationException
|
* @throws CommunicationException
|
||||||
*/
|
*/
|
||||||
public BoolMsg postMessage(BulletinBoardMessage msg, boolean checkSignature) throws CommunicationException{
|
private long postMessage(BulletinBoardMessage msg, byte[] precalculatedMsgID) throws CommunicationException{
|
||||||
|
|
||||||
if (checkSignature && !verifyMessage(msg)) {
|
if (precalculatedMsgID != null && !verifyMessage(msg)) {
|
||||||
return boolToBoolMsg(false);
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
String sql;
|
String sql;
|
||||||
|
@ -447,12 +470,16 @@ public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{
|
||||||
List<Signature> signatureList;
|
List<Signature> signatureList;
|
||||||
Signature[] signatures;
|
Signature[] signatures;
|
||||||
|
|
||||||
// Calculate message ID (depending only on the the unsigned message)
|
|
||||||
|
|
||||||
digest.reset();
|
if (precalculatedMsgID != null){
|
||||||
digest.update(msg.getMsg());
|
msgID = precalculatedMsgID;
|
||||||
|
} else{
|
||||||
|
// Calculate message ID (depending only on the the unsigned message)
|
||||||
|
digest.reset();
|
||||||
|
digest.update(msg.getMsg());
|
||||||
|
|
||||||
msgID = digest.digest();
|
msgID = digest.digest();
|
||||||
|
}
|
||||||
|
|
||||||
// Add message to table if needed and store entry number of message.
|
// Add message to table if needed and store entry number of message.
|
||||||
|
|
||||||
|
@ -530,17 +557,24 @@ public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{
|
||||||
|
|
||||||
jdbcTemplate.batchUpdate(sql,namedParameterArray);
|
jdbcTemplate.batchUpdate(sql,namedParameterArray);
|
||||||
|
|
||||||
return boolToBoolMsg(true);
|
return entryNum;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BoolMsg postMessage(BulletinBoardMessage msg) throws CommunicationException {
|
public BoolValue postMessage(BulletinBoardMessage msg) throws CommunicationException {
|
||||||
return postMessage(msg, true); // Perform a post and check the signature for authenticity
|
|
||||||
|
// Perform a post, calculate the message ID and check the signature for authenticity
|
||||||
|
if (postMessage(msg, null) != -1){
|
||||||
|
return BoolValue.newBuilder().setValue(true).build(); // Message was posted
|
||||||
|
}
|
||||||
|
|
||||||
|
return BoolValue.newBuilder().setValue(false).build(); // Message was not posted
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BoolMsg deleteMessage(MessageID msgID) throws CommunicationException {
|
public BoolValue deleteMessage(MessageID msgID) throws CommunicationException {
|
||||||
|
|
||||||
String sql = sqlQueryProvider.getSQLString(QueryType.DELETE_MSG_BY_ID);
|
String sql = sqlQueryProvider.getSQLString(QueryType.DELETE_MSG_BY_ID);
|
||||||
Map namedParameters = new HashMap();
|
Map namedParameters = new HashMap();
|
||||||
|
@ -551,12 +585,12 @@ public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{
|
||||||
|
|
||||||
//TODO: Log
|
//TODO: Log
|
||||||
|
|
||||||
return BoolMsg.newBuilder().setValue(affectedRows > 0).build();
|
return BoolValue.newBuilder().setValue(affectedRows > 0).build();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BoolMsg deleteMessage(long entryNum) throws CommunicationException {
|
public BoolValue deleteMessage(long entryNum) throws CommunicationException {
|
||||||
|
|
||||||
String sql = sqlQueryProvider.getSQLString(QueryType.DELETE_MSG_BY_ENTRY);
|
String sql = sqlQueryProvider.getSQLString(QueryType.DELETE_MSG_BY_ENTRY);
|
||||||
Map namedParameters = new HashMap();
|
Map namedParameters = new HashMap();
|
||||||
|
@ -567,7 +601,7 @@ public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{
|
||||||
|
|
||||||
//TODO: Log
|
//TODO: Log
|
||||||
|
|
||||||
return BoolMsg.newBuilder().setValue(affectedRows > 0).build();
|
return BoolValue.newBuilder().setValue(affectedRows > 0).build();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -653,6 +687,32 @@ public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private implementation of the message reader for returning result as a list
|
||||||
|
* @param filterList is a filter list that defines which messages the client is interested in
|
||||||
|
* @return the requested list of messages
|
||||||
|
*/
|
||||||
|
private List<BulletinBoardMessage> readMessages(MessageFilterList filterList) throws CommunicationException {
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
|
||||||
|
readMessages(filterList, new MessageOutputStream<BulletinBoardMessage>(outputStream));
|
||||||
|
|
||||||
|
MessageInputStream<BulletinBoardMessage> inputStream =
|
||||||
|
MessageInputStream.MessageInputStreamFactory.createMessageInputStream(new ByteArrayInputStream(
|
||||||
|
outputStream.toByteArray()),
|
||||||
|
BulletinBoardMessage.class);
|
||||||
|
|
||||||
|
return inputStream.asList();
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new CommunicationException(e.getCause() + " " + e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void readMessages(MessageFilterList filterList, MessageOutputStream<BulletinBoardMessage> out) throws CommunicationException {
|
public void readMessages(MessageFilterList filterList, MessageOutputStream<BulletinBoardMessage> out) throws CommunicationException {
|
||||||
|
@ -678,7 +738,7 @@ public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IntMsg getMessageCount(MessageFilterList filterList) throws CommunicationException {
|
public Int32Value getMessageCount(MessageFilterList filterList) throws CommunicationException {
|
||||||
|
|
||||||
BulletinBoardMessageList.Builder resultListBuilder = BulletinBoardMessageList.newBuilder();
|
BulletinBoardMessageList.Builder resultListBuilder = BulletinBoardMessageList.newBuilder();
|
||||||
|
|
||||||
|
@ -697,57 +757,62 @@ public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{
|
||||||
// Run query and stream the output using a MessageCallbackHandler
|
// Run query and stream the output using a MessageCallbackHandler
|
||||||
|
|
||||||
List<Long> count = jdbcTemplate.query(sqlBuilder.toString(), sqlAndParameters.parameters, new LongMapper());
|
List<Long> count = jdbcTemplate.query(sqlBuilder.toString(), sqlAndParameters.parameters, new LongMapper());
|
||||||
return IntMsg.newBuilder().setValue(count.get(0).intValue()).build();
|
return Int32Value.newBuilder().setValue(count.get(0).intValue()).build();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method returns a string representation of the tag associated with a batch ID
|
|
||||||
* @param batchId is the given batch ID
|
|
||||||
* @return the String representation of the tag
|
|
||||||
*/
|
|
||||||
private String batchIdToTag(int batchId) {
|
|
||||||
return BATCH_ID_TAG_PREFIX + Integer.toString(batchId);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method checks if a specified batch exists and is already closed
|
* This method checks if a specified batch exists and is already closed
|
||||||
* @param signerId is the ID of the publisher of the batch
|
* @param msgID is the unique ID of the batch message
|
||||||
* @param batchId is the unique (per signer) batch ID
|
|
||||||
* @return TRUE if the batch is closed and FALSE if it is still open or doesn't exist at all
|
* @return TRUE if the batch is closed and FALSE if it is still open or doesn't exist at all
|
||||||
*/
|
*/
|
||||||
private boolean isBatchClosed(ByteString signerId, int batchId) throws CommunicationException {
|
private boolean isBatchClosed(MessageID msgID) throws CommunicationException {
|
||||||
|
|
||||||
MessageFilterList filterList = MessageFilterList.newBuilder()
|
MessageFilterList filterList = MessageFilterList.newBuilder()
|
||||||
.addFilter(MessageFilter.newBuilder()
|
.addFilter(MessageFilter.newBuilder()
|
||||||
.setType(FilterType.SIGNER_ID)
|
.setType(FilterType.MSG_ID)
|
||||||
.setId(signerId)
|
.setId(msgID.getID())
|
||||||
.build())
|
|
||||||
.addFilter(MessageFilter.newBuilder()
|
|
||||||
.setType(FilterType.TAG)
|
|
||||||
.setTag(BATCH_TAG)
|
|
||||||
.build())
|
|
||||||
.addFilter(MessageFilter.newBuilder()
|
|
||||||
.setType(FilterType.TAG)
|
|
||||||
.setTag(batchIdToTag(batchId))
|
|
||||||
.build())
|
.build())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
int count = getMessageCount(filterList).getValue();
|
|
||||||
|
|
||||||
return count > 0;
|
List<BulletinBoardMessage> messages = readMessages(filterList);
|
||||||
|
|
||||||
|
if (messages.size() <= 0){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return messages.get(0).getMsg().getIsStub();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method checks if a specified batch exists and is still open
|
||||||
|
* @param signerId is the signer ID
|
||||||
|
* @param batchId is the batch ID
|
||||||
|
* @return TRUE if the batch is closed and FALSE if it is still open or doesn't exist at all
|
||||||
|
*/
|
||||||
|
private boolean isBatchOpen(ByteString signerId, int batchId) throws CommunicationException {
|
||||||
|
|
||||||
|
String sql = sqlQueryProvider.getSQLString(QueryType.CHECK_BATCH_OPEN);
|
||||||
|
MapSqlParameterSource namedParameters = new MapSqlParameterSource();
|
||||||
|
|
||||||
|
namedParameters.addValue(QueryType.CHECK_BATCH_OPEN.getParamName(0),signerId.toByteArray());
|
||||||
|
namedParameters.addValue(QueryType.CHECK_BATCH_OPEN.getParamName(1),batchId);
|
||||||
|
|
||||||
|
List<Long> result = jdbcTemplate.query(sql, namedParameters, new LongMapper());
|
||||||
|
|
||||||
|
return (result.size() > 0 && result.get(0) > 0);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BoolMsg beginBatch(BeginBatchMessage message) throws CommunicationException {
|
public BoolValue beginBatch(BeginBatchMessage message) throws CommunicationException {
|
||||||
|
|
||||||
// Check if batch is closed
|
// Check if batch is already open
|
||||||
if (isBatchClosed(message.getSignerId(), message.getBatchId())) {
|
if (isBatchOpen(message.getSignerId(), message.getBatchId())) {
|
||||||
return BoolMsg.newBuilder().setValue(false).build();
|
return BoolValue.newBuilder().setValue(false).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store tags
|
// Store tags
|
||||||
|
@ -760,17 +825,17 @@ public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{
|
||||||
|
|
||||||
jdbcTemplate.update(sql,namedParameters);
|
jdbcTemplate.update(sql,namedParameters);
|
||||||
|
|
||||||
return BoolMsg.newBuilder().setValue(true).build();
|
return BoolValue.newBuilder().setValue(true).build();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BoolMsg postBatchMessage(BatchMessage batchMessage) throws CommunicationException{
|
public BoolValue postBatchMessage(BatchMessage batchMessage) throws CommunicationException{
|
||||||
|
|
||||||
// Check if batch is closed
|
// Make sure batch is open
|
||||||
if (isBatchClosed(batchMessage.getSignerId(), batchMessage.getBatchId())) {
|
if (!isBatchOpen(batchMessage.getSignerId(), batchMessage.getBatchId())) {
|
||||||
return BoolMsg.newBuilder().setValue(false).build();
|
return BoolValue.newBuilder().setValue(false).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add data
|
// Add data
|
||||||
|
@ -784,19 +849,17 @@ public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{
|
||||||
|
|
||||||
jdbcTemplate.update(sql, namedParameters);
|
jdbcTemplate.update(sql, namedParameters);
|
||||||
|
|
||||||
return BoolMsg.newBuilder().setValue(true).build();
|
return BoolValue.newBuilder().setValue(true).build();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BoolMsg closeBatchMessage(CloseBatchMessage message) throws CommunicationException {
|
public BoolValue closeBatch(CloseBatchMessage message) throws CommunicationException {
|
||||||
|
|
||||||
ByteString signerId = message.getSig().getSignerId();
|
ByteString signerId = message.getSig(0).getSignerId();
|
||||||
int batchId = message.getBatchId();
|
int batchId = message.getBatchId();
|
||||||
|
|
||||||
KeyHolder keyHolder = new GeneratedKeyHolder();
|
|
||||||
|
|
||||||
// Check batch size
|
// Check batch size
|
||||||
|
|
||||||
String sql = sqlQueryProvider.getSQLString(QueryType.CHECK_BATCH_LENGTH);
|
String sql = sqlQueryProvider.getSQLString(QueryType.CHECK_BATCH_LENGTH);
|
||||||
|
@ -809,10 +872,10 @@ public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{
|
||||||
List<Long> lengthResult = jdbcTemplate.query(sql, namedParameters, new LongMapper());
|
List<Long> lengthResult = jdbcTemplate.query(sql, namedParameters, new LongMapper());
|
||||||
|
|
||||||
if (lengthResult.get(0) != message.getBatchLength()) {
|
if (lengthResult.get(0) != message.getBatchLength()) {
|
||||||
return BoolMsg.newBuilder().setValue(false).build();
|
return BoolValue.newBuilder().setValue(false).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get Tags and add them to CompleteBatch
|
// Get Tags and add them to BulletinBoardMessage
|
||||||
|
|
||||||
sql = sqlQueryProvider.getSQLString(QueryType.GET_BATCH_TAGS);
|
sql = sqlQueryProvider.getSQLString(QueryType.GET_BATCH_TAGS);
|
||||||
namedParameters = new MapSqlParameterSource();
|
namedParameters = new MapSqlParameterSource();
|
||||||
|
@ -823,73 +886,89 @@ public class BulletinBoardSQLServer implements DeletableBulletinBoardServer{
|
||||||
List<BeginBatchMessage> beginBatchMessages = jdbcTemplate.query(sql, namedParameters, new BeginBatchMessageMapper());
|
List<BeginBatchMessage> beginBatchMessages = jdbcTemplate.query(sql, namedParameters, new BeginBatchMessageMapper());
|
||||||
|
|
||||||
if (beginBatchMessages == null || beginBatchMessages.size() <= 0 || beginBatchMessages.get(0) == null) {
|
if (beginBatchMessages == null || beginBatchMessages.size() <= 0 || beginBatchMessages.get(0) == null) {
|
||||||
return BoolMsg.newBuilder().setValue(false).build();
|
return BoolValue.newBuilder().setValue(false).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
UnsignedBulletinBoardMessage unsignedMessage = UnsignedBulletinBoardMessage.newBuilder()
|
UnsignedBulletinBoardMessage unsignedMessage = UnsignedBulletinBoardMessage.newBuilder()
|
||||||
.addAllTag(beginBatchMessages.get(0).getTagList())
|
.addAllTag(beginBatchMessages.get(0).getTagList())
|
||||||
.addTag(BATCH_TAG)
|
|
||||||
.addTag(batchIdToTag(batchId))
|
|
||||||
.setTimestamp(message.getTimestamp())
|
.setTimestamp(message.getTimestamp())
|
||||||
|
.setIsStub(true)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
// Add actual batch data to CompleteBatch
|
// Digest the data
|
||||||
|
|
||||||
sql = sqlQueryProvider.getSQLString(QueryType.GET_BATCH_MESSAGE_DATA);
|
digest.reset();
|
||||||
|
digest.update(unsignedMessage);
|
||||||
|
|
||||||
|
sql = sqlQueryProvider.getSQLString(QueryType.GET_BATCH_MESSAGE_DATA_BY_IDS);
|
||||||
namedParameters = new MapSqlParameterSource();
|
namedParameters = new MapSqlParameterSource();
|
||||||
|
|
||||||
namedParameters.addValue(QueryType.GET_BATCH_MESSAGE_DATA.getParamName(0),signerId.toByteArray());
|
namedParameters.addValue(QueryType.GET_BATCH_MESSAGE_DATA_BY_IDS.getParamName(0),signerId.toByteArray());
|
||||||
namedParameters.addValue(QueryType.GET_BATCH_MESSAGE_DATA.getParamName(1),batchId);
|
namedParameters.addValue(QueryType.GET_BATCH_MESSAGE_DATA_BY_IDS.getParamName(1),batchId);
|
||||||
namedParameters.addValue(QueryType.GET_BATCH_MESSAGE_DATA.getParamName(2),0); // Read from the beginning
|
namedParameters.addValue(QueryType.GET_BATCH_MESSAGE_DATA_BY_IDS.getParamName(2),0); // Read from the beginning
|
||||||
|
jdbcTemplate.query(sql, namedParameters, new BatchDataDigestHandler(digest));
|
||||||
|
|
||||||
//TODO: Verification
|
byte[] msgID = digest.digest();
|
||||||
// Will need to use the following to carry out the signature calculation:
|
|
||||||
// jdbcTemplate.query(sql, namedParameters, new BatchDataMapper());
|
//TODO: Signature verification
|
||||||
|
|
||||||
// Create Bulletin Board message
|
// Create Bulletin Board message
|
||||||
BulletinBoardMessage bulletinBoardMessage = BulletinBoardMessage.newBuilder()
|
BulletinBoardMessage bulletinBoardMessage = BulletinBoardMessage.newBuilder()
|
||||||
.setMsg(unsignedMessage)
|
.setMsg(unsignedMessage)
|
||||||
.addSig(message.getSig())
|
.addAllSig(message.getSigList())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
// Post message without checking signature validity
|
// Post message with pre-calculated ID and without checking signature validity
|
||||||
postMessage(bulletinBoardMessage, false);
|
long entryNum = postMessage(bulletinBoardMessage, msgID);
|
||||||
|
|
||||||
|
// Remove signer ID and batch ID from batch data table
|
||||||
|
// This allows reuse of the batch ID in subsequent batches
|
||||||
|
|
||||||
|
sql = sqlQueryProvider.getSQLString(QueryType.REMOVE_BATCH_IDS);
|
||||||
|
|
||||||
|
namedParameters = new MapSqlParameterSource();
|
||||||
|
|
||||||
|
namedParameters.addValue(QueryType.REMOVE_BATCH_IDS.getParamName(0), signerId.toByteArray());
|
||||||
|
namedParameters.addValue(QueryType.REMOVE_BATCH_IDS.getParamName(1), batchId);
|
||||||
|
|
||||||
|
jdbcTemplate.update(sql, namedParameters);
|
||||||
|
|
||||||
// Remove tags from temporary table
|
// Remove tags from temporary table
|
||||||
sql = sqlQueryProvider.getSQLString(QueryType.REMOVE_BATCH_TAGS);
|
sql = sqlQueryProvider.getSQLString(QueryType.REMOVE_BATCH_TAGS);
|
||||||
namedParameters = new MapSqlParameterSource();
|
|
||||||
|
|
||||||
namedParameters.addValue(QueryType.REMOVE_BATCH_TAGS.getParamName(0), signerId.toByteArray());
|
jdbcTemplate.update(sql, namedParameters);
|
||||||
namedParameters.addValue(QueryType.REMOVE_BATCH_TAGS.getParamName(1), batchId);
|
|
||||||
|
// Add entry num to tag data table
|
||||||
|
sql = sqlQueryProvider.getSQLString(QueryType.ADD_ENTRY_NUM_TO_BATCH);
|
||||||
|
namedParameters.addValue(QueryType.ADD_ENTRY_NUM_TO_BATCH.getParamName(2), entryNum);
|
||||||
|
|
||||||
jdbcTemplate.update(sql, namedParameters);
|
jdbcTemplate.update(sql, namedParameters);
|
||||||
|
|
||||||
// Return TRUE
|
// Return TRUE
|
||||||
|
|
||||||
return BoolMsg.newBuilder().setValue(true).build();
|
return BoolValue.newBuilder().setValue(true).build();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void readBatch(BatchSpecificationMessage message, MessageOutputStream<BatchData> out) throws CommunicationException, IllegalArgumentException{
|
public void readBatch(BatchQuery batchQuery, MessageOutputStream<BatchChunk> out) throws CommunicationException, IllegalArgumentException{
|
||||||
|
|
||||||
// Check that batch is closed
|
// Check that batch is closed
|
||||||
if (!isBatchClosed(message.getSignerId(), message.getBatchId())) {
|
if (!isBatchClosed(batchQuery.getMsgID())) {
|
||||||
throw new IllegalArgumentException("No such batch");
|
throw new IllegalArgumentException("No such batch");
|
||||||
}
|
}
|
||||||
|
|
||||||
String sql = sqlQueryProvider.getSQLString(QueryType.GET_BATCH_MESSAGE_DATA);
|
String sql = sqlQueryProvider.getSQLString(QueryType.GET_BATCH_MESSAGE_DATA);
|
||||||
MapSqlParameterSource namedParameters = new MapSqlParameterSource();
|
MapSqlParameterSource namedParameters = new MapSqlParameterSource();
|
||||||
|
|
||||||
namedParameters.addValue(QueryType.GET_BATCH_MESSAGE_DATA.getParamName(0),message.getSignerId().toByteArray());
|
namedParameters.addValue(QueryType.GET_BATCH_MESSAGE_DATA.getParamName(0),batchQuery.getMsgID().getID().toByteArray());
|
||||||
namedParameters.addValue(QueryType.GET_BATCH_MESSAGE_DATA.getParamName(1),message.getBatchId());
|
namedParameters.addValue(QueryType.GET_BATCH_MESSAGE_DATA.getParamName(1),batchQuery.getStartPosition());
|
||||||
namedParameters.addValue(QueryType.GET_BATCH_MESSAGE_DATA.getParamName(2),message.getStartPosition());
|
|
||||||
|
|
||||||
jdbcTemplate.query(sql, namedParameters, new BatchDataCallbackHandler(out));
|
jdbcTemplate.query(sql, namedParameters, new BatchDataCallbackHandler(out));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds the entry number of the last entry in the database
|
* Finds the entry number of the last entry in the database
|
||||||
* @return the entry number, or -1 if no entries are found
|
* @return the entry number, or -1 if no entries are found
|
||||||
|
|
|
@ -2,11 +2,7 @@ package meerkat.bulletinboard.sqlserver;
|
||||||
|
|
||||||
import meerkat.protobuf.BulletinBoardAPI.FilterType;
|
import meerkat.protobuf.BulletinBoardAPI.FilterType;
|
||||||
import org.apache.commons.dbcp2.BasicDataSource;
|
import org.apache.commons.dbcp2.BasicDataSource;
|
||||||
import org.h2.jdbcx.JdbcDataSource;
|
|
||||||
import javax.naming.Context;
|
|
||||||
import javax.naming.InitialContext;
|
|
||||||
|
|
||||||
import javax.naming.NamingException;
|
|
||||||
import javax.sql.DataSource;
|
import javax.sql.DataSource;
|
||||||
import java.text.MessageFormat;
|
import java.text.MessageFormat;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
|
@ -112,11 +108,20 @@ public class H2QueryProvider implements BulletinBoardSQLServer.SQLQueryProvider
|
||||||
case GET_BATCH_MESSAGE_DATA:
|
case GET_BATCH_MESSAGE_DATA:
|
||||||
return MessageFormat.format(
|
return MessageFormat.format(
|
||||||
"SELECT Data FROM BatchTable"
|
"SELECT Data FROM BatchTable"
|
||||||
+ " WHERE SignerId = :{0} AND BatchId = :{1} AND SerialNum >= :{2}"
|
+ " INNER JOIN MsgTable ON MsgTable.EntryNum = BatchTable.EntryNum"
|
||||||
+ " ORDER BY SerialNum ASC",
|
+ " WHERE MsgTable.MsgId = :{0} AND BatchTable.SerialNum >= :{1}"
|
||||||
|
+ " ORDER BY BatchTable.SerialNum ASC",
|
||||||
QueryType.GET_BATCH_MESSAGE_DATA.getParamName(0),
|
QueryType.GET_BATCH_MESSAGE_DATA.getParamName(0),
|
||||||
QueryType.GET_BATCH_MESSAGE_DATA.getParamName(1),
|
QueryType.GET_BATCH_MESSAGE_DATA.getParamName(1));
|
||||||
QueryType.GET_BATCH_MESSAGE_DATA.getParamName(2));
|
|
||||||
|
case GET_BATCH_MESSAGE_DATA_BY_IDS:
|
||||||
|
return MessageFormat.format(
|
||||||
|
"SELECT Data FROM BatchTable"
|
||||||
|
+ " WHERE SignerId = :{0} AND BatchId = :{1} AND SerialNum >= :{2}"
|
||||||
|
+ " ORDER BY BatchTable.SerialNum ASC",
|
||||||
|
QueryType.GET_BATCH_MESSAGE_DATA_BY_IDS.getParamName(0),
|
||||||
|
QueryType.GET_BATCH_MESSAGE_DATA_BY_IDS.getParamName(1),
|
||||||
|
QueryType.GET_BATCH_MESSAGE_DATA_BY_IDS.getParamName(2));
|
||||||
|
|
||||||
case INSERT_BATCH_DATA:
|
case INSERT_BATCH_DATA:
|
||||||
return MessageFormat.format(
|
return MessageFormat.format(
|
||||||
|
@ -134,9 +139,16 @@ public class H2QueryProvider implements BulletinBoardSQLServer.SQLQueryProvider
|
||||||
QueryType.CHECK_BATCH_LENGTH.getParamName(0),
|
QueryType.CHECK_BATCH_LENGTH.getParamName(0),
|
||||||
QueryType.CHECK_BATCH_LENGTH.getParamName(1));
|
QueryType.CHECK_BATCH_LENGTH.getParamName(1));
|
||||||
|
|
||||||
|
case CHECK_BATCH_OPEN:
|
||||||
|
return MessageFormat.format(
|
||||||
|
"SELECT COUNT(SignerId) AS signCount FROM BatchTagTable"
|
||||||
|
+ " WHERE SignerId = :{0} AND BatchId = :{1}",
|
||||||
|
QueryType.CHECK_BATCH_OPEN.getParamName(0),
|
||||||
|
QueryType.CHECK_BATCH_OPEN.getParamName(1));
|
||||||
|
|
||||||
case STORE_BATCH_TAGS:
|
case STORE_BATCH_TAGS:
|
||||||
return MessageFormat.format(
|
return MessageFormat.format(
|
||||||
"INSERT INTO BatchTagTable (SignerId, BatchId, TagId) VALUES (:{0}, :{1}, :{2})",
|
"INSERT INTO BatchTagTable (SignerId, BatchId, Tags) VALUES (:{0}, :{1}, :{2})",
|
||||||
QueryType.STORE_BATCH_TAGS.getParamName(0),
|
QueryType.STORE_BATCH_TAGS.getParamName(0),
|
||||||
QueryType.STORE_BATCH_TAGS.getParamName(1),
|
QueryType.STORE_BATCH_TAGS.getParamName(1),
|
||||||
QueryType.STORE_BATCH_TAGS.getParamName(2));
|
QueryType.STORE_BATCH_TAGS.getParamName(2));
|
||||||
|
@ -153,6 +165,20 @@ public class H2QueryProvider implements BulletinBoardSQLServer.SQLQueryProvider
|
||||||
QueryType.REMOVE_BATCH_TAGS.getParamName(0),
|
QueryType.REMOVE_BATCH_TAGS.getParamName(0),
|
||||||
QueryType.REMOVE_BATCH_TAGS.getParamName(1));
|
QueryType.REMOVE_BATCH_TAGS.getParamName(1));
|
||||||
|
|
||||||
|
case REMOVE_BATCH_IDS:
|
||||||
|
return MessageFormat.format(
|
||||||
|
"UPDATE BatchTable Set (SignerId, BatchId) = (NULL, NULL)" +
|
||||||
|
" WHERE SignerId = :{0} AND BatchId = :{1}",
|
||||||
|
QueryType.REMOVE_BATCH_IDS.getParamName(0),
|
||||||
|
QueryType.REMOVE_BATCH_IDS.getParamName(1));
|
||||||
|
|
||||||
|
case ADD_ENTRY_NUM_TO_BATCH:
|
||||||
|
return MessageFormat.format(
|
||||||
|
"UPDATE BatchTable SET EntryNum = :{2} WHERE SignerId = :{0} AND BatchId = :{1}",
|
||||||
|
QueryType.ADD_ENTRY_NUM_TO_BATCH.getParamName(0),
|
||||||
|
QueryType.ADD_ENTRY_NUM_TO_BATCH.getParamName(1),
|
||||||
|
QueryType.ADD_ENTRY_NUM_TO_BATCH.getParamName(2));
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("Cannot serve a query of type " + queryType);
|
throw new IllegalArgumentException("Cannot serve a query of type " + queryType);
|
||||||
}
|
}
|
||||||
|
@ -255,12 +281,12 @@ public class H2QueryProvider implements BulletinBoardSQLServer.SQLQueryProvider
|
||||||
list.add("CREATE INDEX IF NOT EXISTS SignerIndex ON SignatureTable(SignerId)");
|
list.add("CREATE INDEX IF NOT EXISTS SignerIndex ON SignatureTable(SignerId)");
|
||||||
list.add("CREATE UNIQUE INDEX IF NOT EXISTS SignatureIndex ON SignatureTable(SignerId, EntryNum)");
|
list.add("CREATE UNIQUE INDEX IF NOT EXISTS SignatureIndex ON SignatureTable(SignerId, EntryNum)");
|
||||||
|
|
||||||
list.add("CREATE TABLE IF NOT EXISTS BatchTable (SignerId TINYBLOB, BatchId INT, SerialNum INT, Data BLOB,"
|
list.add("CREATE TABLE IF NOT EXISTS BatchTable (EntryNum INT, SignerId TINYBLOB, BatchId INT, SerialNum INT, Data BLOB,"
|
||||||
+ " UNIQUE(SignerId, BatchId, SerialNum))");
|
+ " UNIQUE(SignerId, BatchId, SerialNum))");
|
||||||
|
list.add("CREATE INDEX IF NOT EXISTS BatchDataIndex ON BatchTable(EntryNum, SerialNum)");
|
||||||
|
|
||||||
list.add("CREATE TABLE IF NOT EXISTS BatchTagTable (SignerId TINYBLOB, BatchId INT, Tags BLOB)");
|
list.add("CREATE TABLE IF NOT EXISTS BatchTagTable (SignerId TINYBLOB, BatchId INT, Tags BLOB)");
|
||||||
|
list.add("CREATE UNIQUE INDEX IF NOT EXISTS BatchTagIndex ON BatchTagTable(SignerId, BatchId)");
|
||||||
list.add("CREATE UNIQUE INDEX IF NOT EXISTS BatchIndex ON BatchTagTable(SignerId, BatchId)");
|
|
||||||
|
|
||||||
// This is used to create a simple table with one entry.
|
// This is used to create a simple table with one entry.
|
||||||
// It is used for implementing a workaround for the missing INSERT IGNORE syntax
|
// It is used for implementing a workaround for the missing INSERT IGNORE syntax
|
||||||
|
@ -275,13 +301,20 @@ public class H2QueryProvider implements BulletinBoardSQLServer.SQLQueryProvider
|
||||||
List<String> list = new LinkedList<String>();
|
List<String> list = new LinkedList<String>();
|
||||||
|
|
||||||
list.add("DROP TABLE IF EXISTS UtilityTable");
|
list.add("DROP TABLE IF EXISTS UtilityTable");
|
||||||
list.add("DROP INDEX IF EXISTS BatchIndex");
|
|
||||||
|
list.add("DROP INDEX IF EXISTS BatchTagIndex");
|
||||||
list.add("DROP TABLE IF EXISTS BatchTagTable");
|
list.add("DROP TABLE IF EXISTS BatchTagTable");
|
||||||
|
|
||||||
|
list.add("DROP INDEX IF EXISTS BatchDataIndex");
|
||||||
list.add("DROP TABLE IF EXISTS BatchTable");
|
list.add("DROP TABLE IF EXISTS BatchTable");
|
||||||
list.add("DROP INDEX IF EXISTS SignerIdIndex");
|
|
||||||
list.add("DROP TABLE IF EXISTS MsgTagTable");
|
list.add("DROP TABLE IF EXISTS MsgTagTable");
|
||||||
|
|
||||||
|
list.add("DROP INDEX IF EXISTS SignerIdIndex");
|
||||||
list.add("DROP TABLE IF EXISTS SignatureTable");
|
list.add("DROP TABLE IF EXISTS SignatureTable");
|
||||||
|
|
||||||
list.add("DROP TABLE IF EXISTS TagTable");
|
list.add("DROP TABLE IF EXISTS TagTable");
|
||||||
|
|
||||||
list.add("DROP TABLE IF EXISTS MsgTable");
|
list.add("DROP TABLE IF EXISTS MsgTable");
|
||||||
|
|
||||||
return list;
|
return list;
|
||||||
|
|
|
@ -111,11 +111,20 @@ public class MySQLQueryProvider implements SQLQueryProvider {
|
||||||
case GET_BATCH_MESSAGE_DATA:
|
case GET_BATCH_MESSAGE_DATA:
|
||||||
return MessageFormat.format(
|
return MessageFormat.format(
|
||||||
"SELECT Data FROM BatchTable"
|
"SELECT Data FROM BatchTable"
|
||||||
+ " WHERE SignerId = :{0} AND BatchId = :{1} AND SerialNum >= :{2}"
|
+ " INNER JOIN MsgTable ON MsgTable.EntryNum = BatchTable.EntryNum"
|
||||||
+ " ORDER BY SerialNum ASC",
|
+ " WHERE MsgTable.MsgId = :{0} AND BatchTable.SerialNum >= :{1}"
|
||||||
|
+ " ORDER BY BatchTable.SerialNum ASC",
|
||||||
QueryType.GET_BATCH_MESSAGE_DATA.getParamName(0),
|
QueryType.GET_BATCH_MESSAGE_DATA.getParamName(0),
|
||||||
QueryType.GET_BATCH_MESSAGE_DATA.getParamName(1),
|
QueryType.GET_BATCH_MESSAGE_DATA.getParamName(1));
|
||||||
QueryType.GET_BATCH_MESSAGE_DATA.getParamName(2));
|
|
||||||
|
case GET_BATCH_MESSAGE_DATA_BY_IDS:
|
||||||
|
return MessageFormat.format(
|
||||||
|
"SELECT Data FROM BatchTable"
|
||||||
|
+ " WHERE SignerId = :{0} AND BatchId = :{1} AND SerialNum >= :{2}"
|
||||||
|
+ " ORDER BY BatchTable.SerialNum ASC",
|
||||||
|
QueryType.GET_BATCH_MESSAGE_DATA_BY_IDS.getParamName(0),
|
||||||
|
QueryType.GET_BATCH_MESSAGE_DATA_BY_IDS.getParamName(1),
|
||||||
|
QueryType.GET_BATCH_MESSAGE_DATA_BY_IDS.getParamName(2));
|
||||||
|
|
||||||
case INSERT_BATCH_DATA:
|
case INSERT_BATCH_DATA:
|
||||||
return MessageFormat.format(
|
return MessageFormat.format(
|
||||||
|
@ -133,18 +142,23 @@ public class MySQLQueryProvider implements SQLQueryProvider {
|
||||||
QueryType.CHECK_BATCH_LENGTH.getParamName(0),
|
QueryType.CHECK_BATCH_LENGTH.getParamName(0),
|
||||||
QueryType.CHECK_BATCH_LENGTH.getParamName(1));
|
QueryType.CHECK_BATCH_LENGTH.getParamName(1));
|
||||||
|
|
||||||
case CONNECT_BATCH_TAG:
|
case CHECK_BATCH_OPEN:
|
||||||
return MessageFormat.format(
|
return MessageFormat.format(
|
||||||
"INSERT INTO BatchTagTable (SignerId, BatchId, TagId) SELECT :{0}, :{1}, TagId FROM TagTable"
|
"SELECT COUNT(Tags) AS signCount FROM BatchTagTable"
|
||||||
+ " WHERE Tag = :{2}",
|
+ " WHERE SignerId = :{0} AND BatchId = :{1}",
|
||||||
QueryType.CONNECT_BATCH_TAG.getParamName(0),
|
QueryType.CHECK_BATCH_OPEN.getParamName(0),
|
||||||
QueryType.CONNECT_BATCH_TAG.getParamName(1),
|
QueryType.CHECK_BATCH_OPEN.getParamName(1));
|
||||||
QueryType.CONNECT_BATCH_TAG.getParamName(2));
|
|
||||||
|
case STORE_BATCH_TAGS:
|
||||||
|
return MessageFormat.format(
|
||||||
|
"INSERT INTO BatchTagTable (SignerId, BatchId, Tags) VALUES (:{0}, :{1}, :{2})",
|
||||||
|
QueryType.STORE_BATCH_TAGS.getParamName(0),
|
||||||
|
QueryType.STORE_BATCH_TAGS.getParamName(1),
|
||||||
|
QueryType.STORE_BATCH_TAGS.getParamName(2));
|
||||||
|
|
||||||
case GET_BATCH_TAGS:
|
case GET_BATCH_TAGS:
|
||||||
return MessageFormat.format(
|
return MessageFormat.format(
|
||||||
"SELECT Tag FROM TagTable INNER JOIN BatchTagTable ON TagTable.TagId = BatchTagTable.TagId"
|
"SELECT Tags FROM BatchTagTable WHERE SignerId = :{0} AND BatchId = :{1}",
|
||||||
+ " WHERE SignerId = :{0} AND BatchId = :{1} ORDER BY Tag ASC",
|
|
||||||
QueryType.GET_BATCH_TAGS.getParamName(0),
|
QueryType.GET_BATCH_TAGS.getParamName(0),
|
||||||
QueryType.GET_BATCH_TAGS.getParamName(1));
|
QueryType.GET_BATCH_TAGS.getParamName(1));
|
||||||
|
|
||||||
|
@ -154,6 +168,20 @@ public class MySQLQueryProvider implements SQLQueryProvider {
|
||||||
QueryType.REMOVE_BATCH_TAGS.getParamName(0),
|
QueryType.REMOVE_BATCH_TAGS.getParamName(0),
|
||||||
QueryType.REMOVE_BATCH_TAGS.getParamName(1));
|
QueryType.REMOVE_BATCH_TAGS.getParamName(1));
|
||||||
|
|
||||||
|
case REMOVE_BATCH_IDS:
|
||||||
|
return MessageFormat.format(
|
||||||
|
"UPDATE BatchTable Set SignerId = NULL, BatchId = NULL" +
|
||||||
|
" WHERE SignerId = :{0} AND BatchId = :{1}",
|
||||||
|
QueryType.REMOVE_BATCH_IDS.getParamName(0),
|
||||||
|
QueryType.REMOVE_BATCH_IDS.getParamName(1));
|
||||||
|
|
||||||
|
case ADD_ENTRY_NUM_TO_BATCH:
|
||||||
|
return MessageFormat.format(
|
||||||
|
"UPDATE BatchTable SET EntryNum = :{2} WHERE SignerId = :{0} AND BatchId = :{1}",
|
||||||
|
QueryType.ADD_ENTRY_NUM_TO_BATCH.getParamName(0),
|
||||||
|
QueryType.ADD_ENTRY_NUM_TO_BATCH.getParamName(1),
|
||||||
|
QueryType.ADD_ENTRY_NUM_TO_BATCH.getParamName(2));
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("Cannot serve a query of type " + queryType);
|
throw new IllegalArgumentException("Cannot serve a query of type " + queryType);
|
||||||
}
|
}
|
||||||
|
@ -256,11 +284,11 @@ public class MySQLQueryProvider implements SQLQueryProvider {
|
||||||
+ " INDEX(SignerId(32)), CONSTRAINT Unique_Signature UNIQUE(SignerId(32), EntryNum),"
|
+ " INDEX(SignerId(32)), CONSTRAINT Unique_Signature UNIQUE(SignerId(32), EntryNum),"
|
||||||
+ " CONSTRAINT FOREIGN KEY (EntryNum) REFERENCES MsgTable(EntryNum))");
|
+ " CONSTRAINT FOREIGN KEY (EntryNum) REFERENCES MsgTable(EntryNum))");
|
||||||
|
|
||||||
list.add("CREATE TABLE IF NOT EXISTS BatchTable (SignerId TINYBLOB, BatchId INT, SerialNum INT, Data BLOB,"
|
list.add("CREATE TABLE IF NOT EXISTS BatchTable (EntryNum INT, SignerId TINYBLOB, BatchId INT, SerialNum INT, Data BLOB,"
|
||||||
+ " CONSTRAINT Unique_Batch UNIQUE(SignerId(32), BatchId, SerialNum))");
|
+ " CONSTRAINT Unique_Batch UNIQUE(SignerId(32), BatchId, SerialNum))");
|
||||||
|
|
||||||
list.add("CREATE TABLE IF NOT EXISTS BatchTagTable (SignerId TINYBLOB, BatchId INT, TagId INT,"
|
list.add("CREATE TABLE IF NOT EXISTS BatchTagTable (SignerId TINYBLOB, BatchId INT, Tags BLOB,"
|
||||||
+ " INDEX(SignerId(32), BatchId), CONSTRAINT FOREIGN KEY (TagId) REFERENCES TagTable(TagId))");
|
+ " INDEX(SignerId(32), BatchId))");
|
||||||
|
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
@ -278,4 +306,4 @@ public class MySQLQueryProvider implements SQLQueryProvider {
|
||||||
|
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -74,11 +74,20 @@ public class SQLiteQueryProvider implements BulletinBoardSQLServer.SQLQueryProvi
|
||||||
case GET_BATCH_MESSAGE_DATA:
|
case GET_BATCH_MESSAGE_DATA:
|
||||||
return MessageFormat.format(
|
return MessageFormat.format(
|
||||||
"SELECT Data FROM BatchTable"
|
"SELECT Data FROM BatchTable"
|
||||||
+ " WHERE SignerId = :{0} AND BatchId = :{1} AND SerialNum >= :{2}"
|
+ " INNER JOIN MsgTable ON MsgTable.EntryNum = BatchTable.EntryNum"
|
||||||
+ " ORDER BY SerialNum ASC",
|
+ " WHERE MsgTable.MsgId = :{0} AND BatchTable.SerialNum >= :{1}"
|
||||||
|
+ " ORDER BY BatchTable.SerialNum ASC",
|
||||||
QueryType.GET_BATCH_MESSAGE_DATA.getParamName(0),
|
QueryType.GET_BATCH_MESSAGE_DATA.getParamName(0),
|
||||||
QueryType.GET_BATCH_MESSAGE_DATA.getParamName(1),
|
QueryType.GET_BATCH_MESSAGE_DATA.getParamName(1));
|
||||||
QueryType.GET_BATCH_MESSAGE_DATA.getParamName(2));
|
|
||||||
|
case GET_BATCH_MESSAGE_DATA_BY_IDS:
|
||||||
|
return MessageFormat.format(
|
||||||
|
"SELECT Data FROM BatchTable"
|
||||||
|
+ " WHERE SignerId = :{0} AND BatchId = :{1} AND SerialNum >= :{2}"
|
||||||
|
+ " ORDER BY BatchTable.SerialNum ASC",
|
||||||
|
QueryType.GET_BATCH_MESSAGE_DATA_BY_IDS.getParamName(0),
|
||||||
|
QueryType.GET_BATCH_MESSAGE_DATA_BY_IDS.getParamName(1),
|
||||||
|
QueryType.GET_BATCH_MESSAGE_DATA_BY_IDS.getParamName(2));
|
||||||
|
|
||||||
case INSERT_BATCH_DATA:
|
case INSERT_BATCH_DATA:
|
||||||
return MessageFormat.format(
|
return MessageFormat.format(
|
||||||
|
@ -96,13 +105,19 @@ public class SQLiteQueryProvider implements BulletinBoardSQLServer.SQLQueryProvi
|
||||||
QueryType.CHECK_BATCH_LENGTH.getParamName(0),
|
QueryType.CHECK_BATCH_LENGTH.getParamName(0),
|
||||||
QueryType.CHECK_BATCH_LENGTH.getParamName(1));
|
QueryType.CHECK_BATCH_LENGTH.getParamName(1));
|
||||||
|
|
||||||
case CONNECT_BATCH_TAG:
|
case CHECK_BATCH_OPEN:
|
||||||
return MessageFormat.format(
|
return MessageFormat.format(
|
||||||
"INSERT INTO BatchTagTable (SignerId, BatchId, TagId) SELECT :{0}, :{1}, TagId FROM TagTable"
|
"SELECT COUNT(Tags) AS signCount FROM BatchTagTable"
|
||||||
+ " WHERE Tag = :{2}",
|
+ " WHERE SignerId = :{0} AND BatchId = :{1}",
|
||||||
QueryType.CONNECT_BATCH_TAG.getParamName(0),
|
QueryType.CHECK_BATCH_OPEN.getParamName(0),
|
||||||
QueryType.CONNECT_BATCH_TAG.getParamName(1),
|
QueryType.CHECK_BATCH_OPEN.getParamName(1));
|
||||||
QueryType.CONNECT_BATCH_TAG.getParamName(2));
|
|
||||||
|
case STORE_BATCH_TAGS:
|
||||||
|
return MessageFormat.format(
|
||||||
|
"INSERT INTO BatchTagTable (SignerId, BatchId, Tags) VALUES (:{0}, :{1}, :{2})",
|
||||||
|
QueryType.STORE_BATCH_TAGS.getParamName(0),
|
||||||
|
QueryType.STORE_BATCH_TAGS.getParamName(1),
|
||||||
|
QueryType.STORE_BATCH_TAGS.getParamName(2));
|
||||||
|
|
||||||
case GET_BATCH_TAGS:
|
case GET_BATCH_TAGS:
|
||||||
return MessageFormat.format(
|
return MessageFormat.format(
|
||||||
|
@ -117,6 +132,20 @@ public class SQLiteQueryProvider implements BulletinBoardSQLServer.SQLQueryProvi
|
||||||
QueryType.REMOVE_BATCH_TAGS.getParamName(0),
|
QueryType.REMOVE_BATCH_TAGS.getParamName(0),
|
||||||
QueryType.REMOVE_BATCH_TAGS.getParamName(1));
|
QueryType.REMOVE_BATCH_TAGS.getParamName(1));
|
||||||
|
|
||||||
|
case REMOVE_BATCH_IDS:
|
||||||
|
return MessageFormat.format(
|
||||||
|
"UPDATE BatchTable Set (SignerId, BatchId) = (NULL, NULL)" +
|
||||||
|
" WHERE SignerId = :{0} AND BatchId = :{1}",
|
||||||
|
QueryType.REMOVE_BATCH_IDS.getParamName(0),
|
||||||
|
QueryType.REMOVE_BATCH_IDS.getParamName(1));
|
||||||
|
|
||||||
|
case ADD_ENTRY_NUM_TO_BATCH:
|
||||||
|
return MessageFormat.format(
|
||||||
|
"UPDATE BatchTable SET EntryNum = :{2} WHERE SignerId = :{0} AND BatchId = :{1}",
|
||||||
|
QueryType.ADD_ENTRY_NUM_TO_BATCH.getParamName(0),
|
||||||
|
QueryType.ADD_ENTRY_NUM_TO_BATCH.getParamName(1),
|
||||||
|
QueryType.ADD_ENTRY_NUM_TO_BATCH.getParamName(2));
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("Cannot serve a query of type " + queryType);
|
throw new IllegalArgumentException("Cannot serve a query of type " + queryType);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
package meerkat.bulletinboard.sqlserver.mappers;
|
package meerkat.bulletinboard.sqlserver.mappers;
|
||||||
|
|
||||||
import com.google.protobuf.InvalidProtocolBufferException;
|
|
||||||
import meerkat.comm.MessageOutputStream;
|
import meerkat.comm.MessageOutputStream;
|
||||||
import meerkat.protobuf.BulletinBoardAPI.BatchData;
|
import meerkat.protobuf.BulletinBoardAPI.BatchChunk;
|
||||||
import org.springframework.jdbc.core.RowCallbackHandler;
|
import org.springframework.jdbc.core.RowCallbackHandler;
|
||||||
import org.springframework.jdbc.core.RowMapper;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
|
@ -15,16 +13,16 @@ import java.sql.SQLException;
|
||||||
*/
|
*/
|
||||||
public class BatchDataCallbackHandler implements RowCallbackHandler {
|
public class BatchDataCallbackHandler implements RowCallbackHandler {
|
||||||
|
|
||||||
private final MessageOutputStream<BatchData> out;
|
private final MessageOutputStream<BatchChunk> out;
|
||||||
|
|
||||||
public BatchDataCallbackHandler(MessageOutputStream<BatchData> out) {
|
public BatchDataCallbackHandler(MessageOutputStream<BatchChunk> out) {
|
||||||
this.out = out;
|
this.out = out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void processRow(ResultSet rs) throws SQLException {
|
public void processRow(ResultSet rs) throws SQLException {
|
||||||
try {
|
try {
|
||||||
out.writeMessage(BatchData.parseFrom(rs.getBytes(1)));
|
out.writeMessage(BatchChunk.parseFrom(rs.getBytes(1)));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
//TODO: Log
|
//TODO: Log
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
package meerkat.bulletinboard.sqlserver.mappers;
|
||||||
|
|
||||||
|
import meerkat.bulletinboard.BulletinBoardDigest;
|
||||||
|
import meerkat.protobuf.BulletinBoardAPI.BatchChunk;
|
||||||
|
import org.springframework.jdbc.core.RowCallbackHandler;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Arbel Deutsch Peled on 19-Dec-15.
|
||||||
|
*/
|
||||||
|
public class BatchDataDigestHandler implements RowCallbackHandler {
|
||||||
|
|
||||||
|
private final BulletinBoardDigest digest;
|
||||||
|
|
||||||
|
public BatchDataDigestHandler(BulletinBoardDigest digest) {
|
||||||
|
this.digest = digest;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void processRow(ResultSet rs) throws SQLException {
|
||||||
|
try {
|
||||||
|
BatchChunk batchChunk = BatchChunk.parseFrom(rs.getBytes(1));
|
||||||
|
digest.update(batchChunk.getData().toByteArray());
|
||||||
|
} catch (IOException e) {
|
||||||
|
//TODO: Log
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,26 +0,0 @@
|
||||||
package meerkat.bulletinboard.sqlserver.mappers;
|
|
||||||
|
|
||||||
import com.google.protobuf.ByteString;
|
|
||||||
import com.google.protobuf.InvalidProtocolBufferException;
|
|
||||||
import meerkat.protobuf.BulletinBoardAPI.BatchData;
|
|
||||||
import org.springframework.jdbc.core.RowMapper;
|
|
||||||
|
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by Arbel Deutsch Peled on 19-Dec-15.
|
|
||||||
*/
|
|
||||||
public class BatchDataMapper implements RowMapper<BatchData> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BatchData mapRow(ResultSet rs, int rowNum) throws SQLException {
|
|
||||||
|
|
||||||
try {
|
|
||||||
return BatchData.parseFrom(rs.getBytes(1));
|
|
||||||
} catch (InvalidProtocolBufferException e) {
|
|
||||||
return BatchData.getDefaultInstance();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -8,6 +8,8 @@ import javax.ws.rs.core.Context;
|
||||||
import javax.ws.rs.core.MediaType;
|
import javax.ws.rs.core.MediaType;
|
||||||
import javax.ws.rs.core.StreamingOutput;
|
import javax.ws.rs.core.StreamingOutput;
|
||||||
|
|
||||||
|
import com.google.protobuf.BoolValue;
|
||||||
|
import com.google.protobuf.Int32Value;
|
||||||
import meerkat.bulletinboard.BulletinBoardServer;
|
import meerkat.bulletinboard.BulletinBoardServer;
|
||||||
import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer;
|
import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer;
|
||||||
import meerkat.bulletinboard.sqlserver.H2QueryProvider;
|
import meerkat.bulletinboard.sqlserver.H2QueryProvider;
|
||||||
|
@ -86,7 +88,7 @@ public class BulletinBoardWebApp implements BulletinBoardServer, ServletContextL
|
||||||
@Consumes(MEDIATYPE_PROTOBUF)
|
@Consumes(MEDIATYPE_PROTOBUF)
|
||||||
@Produces(MEDIATYPE_PROTOBUF)
|
@Produces(MEDIATYPE_PROTOBUF)
|
||||||
@Override
|
@Override
|
||||||
public BoolMsg postMessage(BulletinBoardMessage msg) throws CommunicationException {
|
public BoolValue postMessage(BulletinBoardMessage msg) throws CommunicationException {
|
||||||
init();
|
init();
|
||||||
return bulletinBoard.postMessage(msg);
|
return bulletinBoard.postMessage(msg);
|
||||||
}
|
}
|
||||||
|
@ -102,7 +104,7 @@ public class BulletinBoardWebApp implements BulletinBoardServer, ServletContextL
|
||||||
@Consumes(MEDIATYPE_PROTOBUF)
|
@Consumes(MEDIATYPE_PROTOBUF)
|
||||||
@Produces(MEDIATYPE_PROTOBUF)
|
@Produces(MEDIATYPE_PROTOBUF)
|
||||||
@Override
|
@Override
|
||||||
public IntMsg getMessageCount(MessageFilterList filterList) throws CommunicationException {
|
public Int32Value getMessageCount(MessageFilterList filterList) throws CommunicationException {
|
||||||
init();
|
init();
|
||||||
return bulletinBoard.getMessageCount(filterList);
|
return bulletinBoard.getMessageCount(filterList);
|
||||||
}
|
}
|
||||||
|
@ -140,7 +142,7 @@ public class BulletinBoardWebApp implements BulletinBoardServer, ServletContextL
|
||||||
@Consumes(MEDIATYPE_PROTOBUF)
|
@Consumes(MEDIATYPE_PROTOBUF)
|
||||||
@Produces(MEDIATYPE_PROTOBUF)
|
@Produces(MEDIATYPE_PROTOBUF)
|
||||||
@Override
|
@Override
|
||||||
public BoolMsg beginBatch(BeginBatchMessage message) {
|
public BoolValue beginBatch(BeginBatchMessage message) {
|
||||||
try {
|
try {
|
||||||
init();
|
init();
|
||||||
return bulletinBoard.beginBatch(message);
|
return bulletinBoard.beginBatch(message);
|
||||||
|
@ -155,7 +157,7 @@ public class BulletinBoardWebApp implements BulletinBoardServer, ServletContextL
|
||||||
@Consumes(MEDIATYPE_PROTOBUF)
|
@Consumes(MEDIATYPE_PROTOBUF)
|
||||||
@Produces(MEDIATYPE_PROTOBUF)
|
@Produces(MEDIATYPE_PROTOBUF)
|
||||||
@Override
|
@Override
|
||||||
public BoolMsg postBatchMessage(BatchMessage batchMessage) {
|
public BoolValue postBatchMessage(BatchMessage batchMessage) {
|
||||||
try {
|
try {
|
||||||
init();
|
init();
|
||||||
return bulletinBoard.postBatchMessage(batchMessage);
|
return bulletinBoard.postBatchMessage(batchMessage);
|
||||||
|
@ -170,10 +172,10 @@ public class BulletinBoardWebApp implements BulletinBoardServer, ServletContextL
|
||||||
@Consumes(MEDIATYPE_PROTOBUF)
|
@Consumes(MEDIATYPE_PROTOBUF)
|
||||||
@Produces(MEDIATYPE_PROTOBUF)
|
@Produces(MEDIATYPE_PROTOBUF)
|
||||||
@Override
|
@Override
|
||||||
public BoolMsg closeBatchMessage(CloseBatchMessage message) {
|
public BoolValue closeBatch(CloseBatchMessage message) {
|
||||||
try {
|
try {
|
||||||
init();
|
init();
|
||||||
return bulletinBoard.closeBatchMessage(message);
|
return bulletinBoard.closeBatch(message);
|
||||||
} catch (CommunicationException e) {
|
} catch (CommunicationException e) {
|
||||||
System.err.println(e.getMessage());
|
System.err.println(e.getMessage());
|
||||||
return null;
|
return null;
|
||||||
|
@ -182,10 +184,10 @@ public class BulletinBoardWebApp implements BulletinBoardServer, ServletContextL
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void readBatch(BatchSpecificationMessage message, MessageOutputStream<BatchData> out) {
|
public void readBatch(BatchQuery batchQuery, MessageOutputStream<BatchChunk> out) throws CommunicationException, IllegalArgumentException {
|
||||||
try {
|
try {
|
||||||
init();
|
init();
|
||||||
bulletinBoard.readBatch(message, out);
|
bulletinBoard.readBatch(batchQuery, out);
|
||||||
} catch (CommunicationException | IllegalArgumentException e) {
|
} catch (CommunicationException | IllegalArgumentException e) {
|
||||||
System.err.println(e.getMessage());
|
System.err.println(e.getMessage());
|
||||||
}
|
}
|
||||||
|
@ -212,17 +214,17 @@ public class BulletinBoardWebApp implements BulletinBoardServer, ServletContextL
|
||||||
/**
|
/**
|
||||||
* Wrapper for the readBatch method which streams the output into the response
|
* Wrapper for the readBatch method which streams the output into the response
|
||||||
*/
|
*/
|
||||||
public StreamingOutput readBatch(final BatchSpecificationMessage message) {
|
public StreamingOutput readBatch(final BatchQuery batchQuery) {
|
||||||
|
|
||||||
return new StreamingOutput() {
|
return new StreamingOutput() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(OutputStream output) throws IOException, WebApplicationException {
|
public void write(OutputStream output) throws IOException, WebApplicationException {
|
||||||
MessageOutputStream<BatchData> out = new MessageOutputStream<>(output);
|
MessageOutputStream<BatchChunk> out = new MessageOutputStream<>(output);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
init();
|
init();
|
||||||
bulletinBoard.readBatch(message, out);
|
bulletinBoard.readBatch(batchQuery, out);
|
||||||
} catch (CommunicationException e) {
|
} catch (CommunicationException e) {
|
||||||
//TODO: Log
|
//TODO: Log
|
||||||
out.writeMessage(null);
|
out.writeMessage(null);
|
||||||
|
@ -270,4 +272,4 @@ public class BulletinBoardWebApp implements BulletinBoardServer, ServletContextL
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
package meerkat.bulletinboard;
|
package meerkat.bulletinboard;
|
||||||
|
|
||||||
|
|
||||||
|
import com.google.protobuf.BoolValue;
|
||||||
import com.google.protobuf.ByteString;
|
import com.google.protobuf.ByteString;
|
||||||
import com.google.protobuf.TextFormat;
|
import com.google.protobuf.TextFormat;
|
||||||
|
|
||||||
|
@ -8,6 +9,7 @@ import com.google.protobuf.Timestamp;
|
||||||
import meerkat.comm.MessageInputStream;
|
import meerkat.comm.MessageInputStream;
|
||||||
import meerkat.protobuf.Crypto.*;
|
import meerkat.protobuf.Crypto.*;
|
||||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||||
|
import meerkat.protobuf.Comm.*;
|
||||||
import static meerkat.bulletinboard.BulletinBoardConstants.*;
|
import static meerkat.bulletinboard.BulletinBoardConstants.*;
|
||||||
import meerkat.rest.Constants;
|
import meerkat.rest.Constants;
|
||||||
import meerkat.rest.ProtobufMessageBodyReader;
|
import meerkat.rest.ProtobufMessageBodyReader;
|
||||||
|
@ -20,7 +22,6 @@ import javax.ws.rs.client.Client;
|
||||||
import javax.ws.rs.client.ClientBuilder;
|
import javax.ws.rs.client.ClientBuilder;
|
||||||
import javax.ws.rs.client.Entity;
|
import javax.ws.rs.client.Entity;
|
||||||
import javax.ws.rs.client.WebTarget;
|
import javax.ws.rs.client.WebTarget;
|
||||||
import javax.ws.rs.core.MediaType;
|
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -61,7 +62,7 @@ public class BulletinBoardSQLServerIntegrationTest {
|
||||||
|
|
||||||
WebTarget webTarget;
|
WebTarget webTarget;
|
||||||
Response response;
|
Response response;
|
||||||
BoolMsg bool;
|
BoolValue bool;
|
||||||
|
|
||||||
BulletinBoardMessage msg;
|
BulletinBoardMessage msg;
|
||||||
|
|
||||||
|
@ -95,7 +96,7 @@ public class BulletinBoardSQLServerIntegrationTest {
|
||||||
|
|
||||||
response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(msg, Constants.MEDIATYPE_PROTOBUF));
|
response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(msg, Constants.MEDIATYPE_PROTOBUF));
|
||||||
System.err.println(response);
|
System.err.println(response);
|
||||||
bool = response.readEntity(BoolMsg.class);
|
bool = response.readEntity(BoolValue.class);
|
||||||
assert bool.getValue();
|
assert bool.getValue();
|
||||||
|
|
||||||
msg = BulletinBoardMessage.newBuilder()
|
msg = BulletinBoardMessage.newBuilder()
|
||||||
|
@ -114,7 +115,7 @@ public class BulletinBoardSQLServerIntegrationTest {
|
||||||
|
|
||||||
response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(msg, Constants.MEDIATYPE_PROTOBUF));
|
response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(msg, Constants.MEDIATYPE_PROTOBUF));
|
||||||
System.err.println(response);
|
System.err.println(response);
|
||||||
bool = response.readEntity(BoolMsg.class);
|
bool = response.readEntity(BoolValue.class);
|
||||||
assert bool.getValue();
|
assert bool.getValue();
|
||||||
|
|
||||||
// Test reading mechanism
|
// Test reading mechanism
|
||||||
|
|
|
@ -17,6 +17,7 @@ import java.security.UnrecoverableKeyException;
|
||||||
import java.security.cert.CertificateException;
|
import java.security.cert.CertificateException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
import com.google.protobuf.BoolValue;
|
||||||
import com.google.protobuf.ByteString;
|
import com.google.protobuf.ByteString;
|
||||||
|
|
||||||
import com.google.protobuf.Timestamp;
|
import com.google.protobuf.Timestamp;
|
||||||
|
@ -24,12 +25,12 @@ import meerkat.comm.CommunicationException;
|
||||||
import meerkat.comm.MessageInputStream;
|
import meerkat.comm.MessageInputStream;
|
||||||
import meerkat.comm.MessageOutputStream;
|
import meerkat.comm.MessageOutputStream;
|
||||||
import meerkat.comm.MessageInputStream.MessageInputStreamFactory;
|
import meerkat.comm.MessageInputStream.MessageInputStreamFactory;
|
||||||
import meerkat.crypto.Digest;
|
|
||||||
import meerkat.crypto.concrete.ECDSASignature;
|
import meerkat.crypto.concrete.ECDSASignature;
|
||||||
import meerkat.crypto.concrete.SHA256Digest;
|
import meerkat.crypto.concrete.SHA256Digest;
|
||||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||||
|
import meerkat.util.BulletinBoardMessageComparator;
|
||||||
import meerkat.util.BulletinBoardMessageGenerator;
|
import meerkat.util.BulletinBoardMessageGenerator;
|
||||||
import org.h2.util.DateTimeUtils;
|
import meerkat.util.BulletinBoardUtils;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
import static org.hamcrest.CoreMatchers.*;
|
import static org.hamcrest.CoreMatchers.*;
|
||||||
|
@ -38,7 +39,7 @@ import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
public class GenericBulletinBoardServerTest {
|
public class GenericBulletinBoardServerTest {
|
||||||
|
|
||||||
protected BulletinBoardServer bulletinBoardServer;
|
protected BulletinBoardServer bulletinBoardServer;
|
||||||
private GenericBatchDigitalSignature[] signers;
|
private GenericBulletinBoardSignature[] signers;
|
||||||
private ByteString[] signerIDs;
|
private ByteString[] signerIDs;
|
||||||
|
|
||||||
private Random random;
|
private Random random;
|
||||||
|
@ -58,24 +59,18 @@ public class GenericBulletinBoardServerTest {
|
||||||
private String[] tags;
|
private String[] tags;
|
||||||
private byte[][] data;
|
private byte[][] data;
|
||||||
|
|
||||||
private List<CompleteBatch> completeBatches;
|
private List<BulletinBoardMessage> batches;
|
||||||
|
|
||||||
private final ThreadMXBean threadBean = ManagementFactory.getThreadMXBean(); // Used to time the tests
|
private final ThreadMXBean threadBean = ManagementFactory.getThreadMXBean(); // Used to time the tests
|
||||||
|
|
||||||
private BulletinBoardMessageGenerator bulletinBoardMessageGenerator;
|
private BulletinBoardMessageGenerator bulletinBoardMessageGenerator;
|
||||||
|
|
||||||
private Digest digest;
|
private BulletinBoardDigest digest;
|
||||||
|
|
||||||
|
private BulletinBoardMessageComparator comparator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param bulletinBoardServer is an initialized server.
|
* @param bulletinBoardServer is an initialized server.
|
||||||
* @throws InstantiationException
|
|
||||||
* @throws IllegalAccessException
|
|
||||||
* @throws CertificateException
|
|
||||||
* @throws KeyStoreException
|
|
||||||
* @throws NoSuchAlgorithmException
|
|
||||||
* @throws IOException
|
|
||||||
* @throws UnrecoverableKeyException
|
|
||||||
* @throws CommunicationException
|
|
||||||
*/
|
*/
|
||||||
public void init(BulletinBoardServer bulletinBoardServer) {
|
public void init(BulletinBoardServer bulletinBoardServer) {
|
||||||
|
|
||||||
|
@ -84,10 +79,10 @@ public class GenericBulletinBoardServerTest {
|
||||||
|
|
||||||
this.bulletinBoardServer = bulletinBoardServer;
|
this.bulletinBoardServer = bulletinBoardServer;
|
||||||
|
|
||||||
signers = new GenericBatchDigitalSignature[2];
|
signers = new GenericBulletinBoardSignature[2];
|
||||||
signerIDs = new ByteString[signers.length];
|
signerIDs = new ByteString[signers.length];
|
||||||
signers[0] = new GenericBatchDigitalSignature(new ECDSASignature());
|
signers[0] = new GenericBulletinBoardSignature(new ECDSASignature());
|
||||||
signers[1] = new GenericBatchDigitalSignature(new ECDSASignature());
|
signers[1] = new GenericBulletinBoardSignature(new ECDSASignature());
|
||||||
|
|
||||||
InputStream keyStream = getClass().getResourceAsStream(KEYFILE_EXAMPLE);
|
InputStream keyStream = getClass().getResourceAsStream(KEYFILE_EXAMPLE);
|
||||||
char[] password = KEYFILE_PASSWORD1.toCharArray();
|
char[] password = KEYFILE_PASSWORD1.toCharArray();
|
||||||
|
@ -133,7 +128,9 @@ public class GenericBulletinBoardServerTest {
|
||||||
random = new Random(0);
|
random = new Random(0);
|
||||||
bulletinBoardMessageGenerator = new BulletinBoardMessageGenerator(random);
|
bulletinBoardMessageGenerator = new BulletinBoardMessageGenerator(random);
|
||||||
|
|
||||||
digest = new SHA256Digest();
|
digest = new GenericBulletinBoardDigest(new SHA256Digest());
|
||||||
|
|
||||||
|
comparator = new BulletinBoardMessageComparator();
|
||||||
|
|
||||||
long end = threadBean.getCurrentThreadCpuTime();
|
long end = threadBean.getCurrentThreadCpuTime();
|
||||||
System.err.println("Finished initializing GenericBulletinBoardServerTest");
|
System.err.println("Finished initializing GenericBulletinBoardServerTest");
|
||||||
|
@ -141,7 +138,7 @@ public class GenericBulletinBoardServerTest {
|
||||||
|
|
||||||
// Initialize Batch variables
|
// Initialize Batch variables
|
||||||
|
|
||||||
completeBatches = new ArrayList<CompleteBatch>(10);
|
batches = new ArrayList<>(10);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -445,73 +442,94 @@ public class GenericBulletinBoardServerTest {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests that posting a message before opening a batch does not work
|
|
||||||
* @throws CommunicationException
|
|
||||||
*/
|
|
||||||
public void testBatchPostAfterClose() throws CommunicationException, SignatureException {
|
|
||||||
|
|
||||||
final int BATCH_ID = 100;
|
private void postAsBatch(BulletinBoardMessage message, ByteString signerId, int batchId, int chunkSize, boolean close) throws CommunicationException {
|
||||||
|
|
||||||
CompleteBatch completeBatch = new CompleteBatch(Timestamp.newBuilder()
|
List<BatchChunk> batchChunks = BulletinBoardUtils.breakToBatch(message, chunkSize);
|
||||||
.setSeconds(978325)
|
BeginBatchMessage beginBatchMessage = BulletinBoardUtils.generateBeginBatchMessage(signerId, batchId, message);
|
||||||
.setNanos(8097234)
|
|
||||||
.build());
|
|
||||||
BoolMsg result;
|
|
||||||
|
|
||||||
// Create data
|
BoolValue result;
|
||||||
|
|
||||||
completeBatch.setBeginBatchMessage(BeginBatchMessage.newBuilder()
|
|
||||||
.setSignerId(signerIDs[1])
|
|
||||||
.setBatchId(BATCH_ID)
|
|
||||||
.addTag("Test")
|
|
||||||
.build());
|
|
||||||
|
|
||||||
BatchData batchData = BatchData.newBuilder()
|
|
||||||
.setData(ByteString.copyFrom((new byte[] {1,2,3,4})))
|
|
||||||
.build();
|
|
||||||
|
|
||||||
completeBatch.appendBatchData(batchData);
|
|
||||||
|
|
||||||
signers[1].updateContent(completeBatch);
|
|
||||||
|
|
||||||
completeBatch.setSignature(signers[1].sign());
|
|
||||||
|
|
||||||
// Begin batch
|
// Begin batch
|
||||||
|
|
||||||
result = bulletinBoardServer.beginBatch(completeBatch.getBeginBatchMessage());
|
result = bulletinBoardServer.beginBatch(beginBatchMessage);
|
||||||
|
|
||||||
assertThat("Was not able to open batch", result.getValue(), is(true));
|
assertThat("Was not able to open batch", result.getValue(), is(true));
|
||||||
|
|
||||||
// Post data
|
|
||||||
|
|
||||||
BatchMessage batchMessage = BatchMessage.newBuilder()
|
|
||||||
.setSignerId(signerIDs[1])
|
|
||||||
.setBatchId(BATCH_ID)
|
|
||||||
.setData(batchData)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
result = bulletinBoardServer.postBatchMessage(batchMessage);
|
|
||||||
|
|
||||||
assertThat("Was not able to post batch message", result.getValue(), is(true));
|
|
||||||
|
|
||||||
// Close batch
|
|
||||||
|
|
||||||
result = bulletinBoardServer.closeBatchMessage(completeBatch.getCloseBatchMessage());
|
|
||||||
|
|
||||||
assertThat("Was not able to close batch", result.getValue(), is(true));
|
|
||||||
|
|
||||||
// Attempt to open batch again
|
// Attempt to open batch again
|
||||||
|
|
||||||
result = bulletinBoardServer.beginBatch(completeBatch.getBeginBatchMessage());
|
result = bulletinBoardServer.beginBatch(beginBatchMessage);
|
||||||
|
|
||||||
assertThat("Was able to open a closed batch", result.getValue(), is(false));
|
assertThat("Was able to open a closed batch", result.getValue(), is(false));
|
||||||
|
|
||||||
// Attempt to add batch data
|
// Post data
|
||||||
|
|
||||||
result = bulletinBoardServer.postBatchMessage(batchMessage);
|
BatchMessage batchMessage = BatchMessage.getDefaultInstance();
|
||||||
|
|
||||||
assertThat("Was able to change a closed batch", result.getValue(), is(false));
|
for (int i = 0 ; i < batchChunks.size() ; i++){
|
||||||
|
|
||||||
|
batchMessage = BatchMessage.newBuilder()
|
||||||
|
.setSignerId(signerId)
|
||||||
|
.setBatchId(batchId)
|
||||||
|
.setSerialNum(i)
|
||||||
|
.setData(batchChunks.get(i))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
result = bulletinBoardServer.postBatchMessage(batchMessage);
|
||||||
|
|
||||||
|
assertThat("Was not able to post batch message", result.getValue(), is(true));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close batch
|
||||||
|
if (close) {
|
||||||
|
|
||||||
|
CloseBatchMessage closeBatchMessage = BulletinBoardUtils.generateCloseBatchMessage(batchId, batchChunks.size(), message);
|
||||||
|
|
||||||
|
result = bulletinBoardServer.closeBatch(closeBatchMessage);
|
||||||
|
|
||||||
|
assertThat("Was not able to close batch", result.getValue(), is(true));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that opening the same batch ID while one is open results in failure
|
||||||
|
* @throws CommunicationException
|
||||||
|
*/
|
||||||
|
public void testReopen() throws CommunicationException, SignatureException {
|
||||||
|
|
||||||
|
// Create data
|
||||||
|
final int BATCH_ID = 100;
|
||||||
|
final int DATA_SIZE = 1;
|
||||||
|
final int CHUNK_SIZE = 1;
|
||||||
|
final int TAG_NUMBER = 1;
|
||||||
|
|
||||||
|
Timestamp timestamp = Timestamp.newBuilder()
|
||||||
|
.setSeconds(141510)
|
||||||
|
.setNanos(48015)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
BulletinBoardMessage batch = bulletinBoardMessageGenerator.generateRandomMessage(signers, timestamp, DATA_SIZE, TAG_NUMBER);
|
||||||
|
|
||||||
|
// Post batch but do not close
|
||||||
|
|
||||||
|
postAsBatch(batch, signerIDs[0], BATCH_ID, CHUNK_SIZE, false);
|
||||||
|
|
||||||
|
// Attempt to open batch again
|
||||||
|
|
||||||
|
BoolValue result;
|
||||||
|
|
||||||
|
result = bulletinBoardServer.beginBatch(BulletinBoardUtils.generateBeginBatchMessage(signerIDs[0], BATCH_ID, batch));
|
||||||
|
|
||||||
|
assertThat("Was able to open a closed batch", result.getValue(), is(false));
|
||||||
|
|
||||||
|
// Close batch
|
||||||
|
|
||||||
|
result = bulletinBoardServer.closeBatch(BulletinBoardUtils.generateCloseBatchMessage(BATCH_ID, DATA_SIZE / CHUNK_SIZE, batch));
|
||||||
|
|
||||||
|
assertThat("Was not able to close batch", result.getValue(), is(true));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -521,110 +539,79 @@ public class GenericBulletinBoardServerTest {
|
||||||
*/
|
*/
|
||||||
public void testPostBatch() throws CommunicationException, SignatureException {
|
public void testPostBatch() throws CommunicationException, SignatureException {
|
||||||
|
|
||||||
CompleteBatch completeBatch = new CompleteBatch(Timestamp.newBuilder()
|
// Create data
|
||||||
.setSeconds(12345)
|
final int BATCH_ID = 200;
|
||||||
.setNanos(1111)
|
final int DATA_SIZE = 10000;
|
||||||
.build());
|
final int CHUNK_SIZE = 100;
|
||||||
int currentBatch = completeBatches.size();
|
final int TAG_NUMBER = 10;
|
||||||
|
|
||||||
BoolMsg result;
|
Timestamp timestamp = Timestamp.newBuilder()
|
||||||
|
.setSeconds(5235000)
|
||||||
|
.setNanos(32541)
|
||||||
|
.build();
|
||||||
|
|
||||||
// Define batch data
|
BulletinBoardMessage batch = bulletinBoardMessageGenerator.generateRandomMessage(signers, timestamp, DATA_SIZE, TAG_NUMBER);
|
||||||
|
|
||||||
String[] tempBatchTags = new String[]{randomString(),randomString(),randomString()};
|
// Post batch
|
||||||
byte[][] tempBatchData = new byte[Math.abs(randomByte())][];
|
|
||||||
|
|
||||||
for (int i = 0 ; i < tempBatchData.length ; i++) {
|
postAsBatch(batch, signerIDs[0], BATCH_ID, CHUNK_SIZE, true);
|
||||||
|
|
||||||
tempBatchData[i] = new byte[Math.abs(randomByte())];
|
|
||||||
|
|
||||||
for (int j = 0; j < tempBatchData[i].length; j++) {
|
|
||||||
tempBatchData[i][j] = randomByte();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Begin batch
|
|
||||||
|
|
||||||
completeBatch.setBeginBatchMessage(BeginBatchMessage.newBuilder()
|
|
||||||
.setSignerId(signerIDs[0])
|
|
||||||
.setBatchId(currentBatch)
|
|
||||||
.addAllTag(Arrays.asList(tempBatchTags))
|
|
||||||
.build());
|
|
||||||
|
|
||||||
result = bulletinBoardServer.beginBatch(completeBatch.getBeginBatchMessage());
|
|
||||||
|
|
||||||
assertThat("Could not begin batch " + currentBatch, result.getValue(), is(true));
|
|
||||||
|
|
||||||
// Add batch data and randomize data posting order
|
|
||||||
|
|
||||||
List<Integer> dataOrder = new ArrayList<Integer>(tempBatchData.length);
|
|
||||||
for (int i = 0 ; i < tempBatchData.length ; i++) {
|
|
||||||
dataOrder.add(i);
|
|
||||||
completeBatch.appendBatchData(BatchData.newBuilder()
|
|
||||||
.setData(ByteString.copyFrom(tempBatchData[i]))
|
|
||||||
.build());
|
|
||||||
}
|
|
||||||
Collections.shuffle(dataOrder);
|
|
||||||
|
|
||||||
// Post data
|
|
||||||
|
|
||||||
for (int i = 0 ; i < tempBatchData.length ; i++) {
|
|
||||||
|
|
||||||
int dataIndex = dataOrder.get(i);
|
|
||||||
|
|
||||||
result = bulletinBoardServer.postBatchMessage(BatchMessage.newBuilder()
|
|
||||||
.setSignerId(signerIDs[0])
|
|
||||||
.setBatchId(currentBatch)
|
|
||||||
.setSerialNum(dataIndex)
|
|
||||||
.setData(completeBatch.getBatchDataList().get(dataIndex))
|
|
||||||
.build());
|
|
||||||
|
|
||||||
assertThat("Could not post batch data for batch ID " + currentBatch + " serial number " + dataIndex,
|
|
||||||
result.getValue(), is(true));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close batch
|
|
||||||
|
|
||||||
signers[0].updateContent(completeBatch);
|
|
||||||
completeBatch.setSignature(signers[0].sign());
|
|
||||||
|
|
||||||
result = bulletinBoardServer.closeBatchMessage(completeBatch.getCloseBatchMessage());
|
|
||||||
|
|
||||||
assertThat("Could not close batch " + currentBatch, result.getValue(), is(true));
|
|
||||||
|
|
||||||
// Update locally stored batches
|
// Update locally stored batches
|
||||||
completeBatches.add(completeBatch);
|
batches.add(batch);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testReadBatch() throws CommunicationException {
|
public void testReadBatch() throws CommunicationException {
|
||||||
|
|
||||||
for (CompleteBatch completeBatch : completeBatches) {
|
for (BulletinBoardMessage message : batches) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
|
||||||
BatchSpecificationMessage batchSpecificationMessage =
|
digest.update(message);
|
||||||
BatchSpecificationMessage.newBuilder()
|
|
||||||
.setSignerId(completeBatch.getBeginBatchMessage().getSignerId())
|
MessageID msgId = digest.digestAsMessageID();
|
||||||
.setBatchId(completeBatch.getBeginBatchMessage().getBatchId())
|
|
||||||
|
MessageFilterList messageFilterList = MessageFilterList.newBuilder()
|
||||||
|
.addFilter(MessageFilter.newBuilder()
|
||||||
|
.setType(FilterType.MSG_ID)
|
||||||
|
.setId(msgId.getID())
|
||||||
|
.build())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
bulletinBoardServer.readMessages(messageFilterList, new MessageOutputStream<BulletinBoardMessage>(outputStream));
|
||||||
|
|
||||||
|
MessageInputStream<BulletinBoardMessage> messageInputStream =
|
||||||
|
MessageInputStreamFactory.createMessageInputStream(new ByteArrayInputStream(
|
||||||
|
outputStream.toByteArray()),
|
||||||
|
BulletinBoardMessage.class);
|
||||||
|
|
||||||
|
List<BulletinBoardMessage> messageList = messageInputStream.asList();
|
||||||
|
|
||||||
|
assertThat("No stub found for message ID " + msgId.getID().toStringUtf8(), messageList.size() == 1);
|
||||||
|
|
||||||
|
BulletinBoardMessage stub = messageList.get(0);
|
||||||
|
|
||||||
|
BatchQuery batchQuery =
|
||||||
|
BatchQuery.newBuilder()
|
||||||
|
.setMsgID(msgId)
|
||||||
.setStartPosition(0)
|
.setStartPosition(0)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
bulletinBoardServer.readBatch(batchSpecificationMessage, new MessageOutputStream<BatchData>(outputStream));
|
bulletinBoardServer.readBatch(batchQuery, new MessageOutputStream<BatchChunk>(outputStream));
|
||||||
|
|
||||||
MessageInputStream<BatchData> inputStream =
|
MessageInputStream<BatchChunk> batchInputStream =
|
||||||
MessageInputStreamFactory.createMessageInputStream(new ByteArrayInputStream(
|
MessageInputStreamFactory.createMessageInputStream(new ByteArrayInputStream(
|
||||||
outputStream.toByteArray()),
|
outputStream.toByteArray()),
|
||||||
BatchData.class);
|
BatchChunk.class);
|
||||||
|
|
||||||
List<BatchData> batchDataList = inputStream.asList();
|
List<BatchChunk> batchChunkList = batchInputStream.asList();
|
||||||
|
|
||||||
assertThat("Non-matching batch data for batch " + completeBatch.getBeginBatchMessage().getBatchId(),
|
BulletinBoardMessage retrievedMessage = BulletinBoardUtils.gatherBatch(stub, batchChunkList);
|
||||||
completeBatch.getBatchDataList().equals(batchDataList), is(true));
|
|
||||||
|
assertThat("Non-matching batch data for batch " + msgId.getID().toStringUtf8(),
|
||||||
|
comparator.compare(message, retrievedMessage) == 0);
|
||||||
|
|
||||||
} catch (IOException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
|
} catch (IOException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
|
||||||
assertThat("Error reading batch data list from input stream", false);
|
assertThat("Error reading batch data list from input stream", false);
|
||||||
|
@ -646,7 +633,7 @@ public class GenericBulletinBoardServerTest {
|
||||||
|
|
||||||
BulletinBoardMessage newMessage = bulletinBoardMessageGenerator.generateRandomMessage(signers, timestamp, 10, 10);
|
BulletinBoardMessage newMessage = bulletinBoardMessageGenerator.generateRandomMessage(signers, timestamp, 10, 10);
|
||||||
|
|
||||||
BoolMsg result = bulletinBoardServer.postMessage(newMessage);
|
BoolValue result = bulletinBoardServer.postMessage(newMessage);
|
||||||
assertThat("Failed to post message to BB Server", result.getValue(), is(true));
|
assertThat("Failed to post message to BB Server", result.getValue(), is(true));
|
||||||
|
|
||||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
|
|
@ -7,7 +7,6 @@ import meerkat.comm.CommunicationException;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.Result;
|
|
||||||
|
|
||||||
import java.lang.management.ManagementFactory;
|
import java.lang.management.ManagementFactory;
|
||||||
import java.lang.management.ThreadMXBean;
|
import java.lang.management.ThreadMXBean;
|
||||||
|
@ -108,9 +107,9 @@ public class H2BulletinBoardServerTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testBatchPostAfterClose() {
|
public void testBatchReopen() {
|
||||||
try{
|
try{
|
||||||
serverTest.testBatchPostAfterClose();
|
serverTest.testReopen();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
System.err.println(e.getMessage());
|
System.err.println(e.getMessage());
|
||||||
fail(e.getMessage());
|
fail(e.getMessage());
|
||||||
|
|
|
@ -8,13 +8,9 @@ import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.lang.management.ManagementFactory;
|
import java.lang.management.ManagementFactory;
|
||||||
import java.lang.management.ThreadMXBean;
|
import java.lang.management.ThreadMXBean;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.security.SignatureException;
|
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.DriverManager;
|
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.sql.Statement;
|
import java.sql.Statement;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -115,9 +111,9 @@ public class MySQLBulletinBoardServerTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testBatchPostAfterClose() {
|
public void testBatchReopen() {
|
||||||
try{
|
try{
|
||||||
serverTest.testBatchPostAfterClose();
|
serverTest.testReopen();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
System.err.println(e.getMessage());
|
System.err.println(e.getMessage());
|
||||||
fail(e.getMessage());
|
fail(e.getMessage());
|
||||||
|
|
|
@ -0,0 +1,223 @@
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id "us.kirchmeier.capsule" version "1.0.1"
|
||||||
|
id 'com.google.protobuf' version '0.7.0'
|
||||||
|
}
|
||||||
|
|
||||||
|
apply plugin: 'java'
|
||||||
|
apply plugin: 'eclipse'
|
||||||
|
apply plugin: 'idea'
|
||||||
|
|
||||||
|
apply plugin: 'maven-publish'
|
||||||
|
|
||||||
|
// Uncomment the lines below to define an application
|
||||||
|
// (this will also allow you to build a "fatCapsule" which includes
|
||||||
|
// the entire application, including all dependencies in a single jar)
|
||||||
|
//apply plugin: 'application'
|
||||||
|
//mainClassName='your.main.ApplicationClass'
|
||||||
|
|
||||||
|
// Is this a snapshot version?
|
||||||
|
ext { isSnapshot = false }
|
||||||
|
|
||||||
|
ext {
|
||||||
|
groupId = 'org.factcenter.meerkat'
|
||||||
|
nexusRepository = "https://cs.idc.ac.il/nexus/content/groups/${isSnapshot ? 'unstable' : 'public'}/"
|
||||||
|
|
||||||
|
// Credentials for IDC nexus repositories (needed only for using unstable repositories and publishing)
|
||||||
|
// Should be set in ${HOME}/.gradle/gradle.properties
|
||||||
|
nexusUser = project.hasProperty('nexusUser') ? project.property('nexusUser') : ""
|
||||||
|
nexusPassword = project.hasProperty('nexusPassword') ? project.property('nexusPassword') : ""
|
||||||
|
}
|
||||||
|
|
||||||
|
description = "TODO: Add a description"
|
||||||
|
|
||||||
|
// Your project version
|
||||||
|
version = "0.0"
|
||||||
|
|
||||||
|
version += "${isSnapshot ? '-SNAPSHOT' : ''}"
|
||||||
|
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
// Meerkat common
|
||||||
|
compile project(':meerkat-common')
|
||||||
|
|
||||||
|
// Logging
|
||||||
|
compile 'org.slf4j:slf4j-api:1.7.7'
|
||||||
|
runtime 'ch.qos.logback:logback-classic:1.1.2'
|
||||||
|
runtime 'ch.qos.logback:logback-core:1.1.2'
|
||||||
|
|
||||||
|
// Google protobufs
|
||||||
|
compile 'com.google.protobuf:protobuf-java:3.+'
|
||||||
|
|
||||||
|
// Depend on test resources from meerkat-common
|
||||||
|
testCompile project(path: ':meerkat-common', configuration: 'testOutput')
|
||||||
|
|
||||||
|
testCompile 'junit:junit:4.+'
|
||||||
|
|
||||||
|
runtime 'org.codehaus.groovy:groovy:2.4.+'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*==== You probably don't have to edit below this line =======*/
|
||||||
|
|
||||||
|
// Setup test configuration that can appear as a dependency in
|
||||||
|
// other subprojects
|
||||||
|
configurations {
|
||||||
|
testOutput.extendsFrom (testCompile)
|
||||||
|
}
|
||||||
|
|
||||||
|
task testJar(type: Jar, dependsOn: testClasses) {
|
||||||
|
classifier = 'tests'
|
||||||
|
from sourceSets.test.output
|
||||||
|
}
|
||||||
|
|
||||||
|
artifacts {
|
||||||
|
testOutput testJar
|
||||||
|
}
|
||||||
|
|
||||||
|
// The run task added by the application plugin
|
||||||
|
// is also of type JavaExec.
|
||||||
|
tasks.withType(JavaExec) {
|
||||||
|
// Assign all Java system properties from
|
||||||
|
// the command line to the JavaExec task.
|
||||||
|
systemProperties System.properties
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protobuf {
|
||||||
|
// Configure the protoc executable
|
||||||
|
protoc {
|
||||||
|
// Download from repositories
|
||||||
|
artifact = 'com.google.protobuf:protoc:3.+'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
idea {
|
||||||
|
module {
|
||||||
|
project.sourceSets.each { sourceSet ->
|
||||||
|
|
||||||
|
def srcDir = "${protobuf.generatedFilesBaseDir}/$sourceSet.name/java"
|
||||||
|
|
||||||
|
println "Adding $srcDir"
|
||||||
|
// add protobuf generated sources to generated source dir.
|
||||||
|
if ("test".equals(sourceSet.name)) {
|
||||||
|
testSourceDirs += file(srcDir)
|
||||||
|
} else {
|
||||||
|
sourceDirs += file(srcDir)
|
||||||
|
}
|
||||||
|
generatedSourceDirs += file(srcDir)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't exclude build directory
|
||||||
|
excludeDirs -= file(buildDir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*===================================
|
||||||
|
* "Fat" Build targets
|
||||||
|
*===================================*/
|
||||||
|
|
||||||
|
|
||||||
|
if (project.hasProperty('mainClassName') && (mainClassName != null)) {
|
||||||
|
|
||||||
|
task mavenCapsule(type: MavenCapsule) {
|
||||||
|
description = "Generate a capsule jar that automatically downloads and caches dependencies when run."
|
||||||
|
applicationClass mainClassName
|
||||||
|
destinationDir = buildDir
|
||||||
|
}
|
||||||
|
|
||||||
|
task fatCapsule(type: FatCapsule) {
|
||||||
|
description = "Generate a single capsule jar containing everything. Use -Pfatmain=... to override main class"
|
||||||
|
|
||||||
|
destinationDir = buildDir
|
||||||
|
|
||||||
|
def fatMain = hasProperty('fatmain') ? fatmain : mainClassName
|
||||||
|
|
||||||
|
applicationClass fatMain
|
||||||
|
|
||||||
|
def testJar = hasProperty('test')
|
||||||
|
|
||||||
|
if (hasProperty('fatmain')) {
|
||||||
|
appendix = "fat-${fatMain}"
|
||||||
|
} else {
|
||||||
|
appendix = "fat"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (testJar) {
|
||||||
|
from sourceSets.test.output
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*===================================
|
||||||
|
* Repositories
|
||||||
|
*===================================*/
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
|
||||||
|
// Prefer the local nexus repository (it may have 3rd party artifacts not found in mavenCentral)
|
||||||
|
maven {
|
||||||
|
url nexusRepository
|
||||||
|
|
||||||
|
if (isSnapshot) {
|
||||||
|
credentials { username
|
||||||
|
password
|
||||||
|
|
||||||
|
username nexusUser
|
||||||
|
password nexusPassword
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use local maven repository
|
||||||
|
mavenLocal()
|
||||||
|
|
||||||
|
// Use 'maven central' for other dependencies.
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
task "info" << {
|
||||||
|
println "Project: ${project.name}"
|
||||||
|
println "Description: ${project.description}"
|
||||||
|
println "--------------------------"
|
||||||
|
println "GroupId: $groupId"
|
||||||
|
println "Version: $version (${isSnapshot ? 'snapshot' : 'release'})"
|
||||||
|
println ""
|
||||||
|
}
|
||||||
|
info.description 'Print some information about project parameters'
|
||||||
|
|
||||||
|
|
||||||
|
/*===================================
|
||||||
|
* Publishing
|
||||||
|
*===================================*/
|
||||||
|
|
||||||
|
publishing {
|
||||||
|
publications {
|
||||||
|
mavenJava(MavenPublication) {
|
||||||
|
groupId project.groupId
|
||||||
|
pom.withXml {
|
||||||
|
asNode().appendNode('description', project.description)
|
||||||
|
}
|
||||||
|
from project.components.java
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
repositories {
|
||||||
|
maven {
|
||||||
|
url "https://cs.idc.ac.il/nexus/content/repositories/${project.isSnapshot ? 'snapshots' : 'releases'}"
|
||||||
|
credentials { username
|
||||||
|
password
|
||||||
|
|
||||||
|
username nexusUser
|
||||||
|
password nexusPassword
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
package meerkat.crypto.dkg.comm;
|
||||||
|
|
||||||
|
import com.google.protobuf.InvalidProtocolBufferException;
|
||||||
|
import meerkat.comm.Channel;
|
||||||
|
import meerkat.protobuf.Comm;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Tzlil on 2/14/2016.
|
||||||
|
*
|
||||||
|
* an implementation of ReceiverCallback
|
||||||
|
*/
|
||||||
|
public abstract class MessageHandler implements Channel.ReceiverCallback {
|
||||||
|
final Logger logger = LoggerFactory.getLogger(getClass());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fixed value for broadcasting
|
||||||
|
*/
|
||||||
|
public static final int BROADCAST = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle a broadcast (or unicast) message.
|
||||||
|
* If the message is invalid, the handler can throw an {@link InvalidProtocolBufferException}, in which
|
||||||
|
* case the message will simply be ignored.
|
||||||
|
* @param envelope
|
||||||
|
*/
|
||||||
|
public abstract void handleMessage(Comm.BroadcastMessage envelope) throws InvalidProtocolBufferException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Was this broadcastMessage was received by broadcast channel
|
||||||
|
* @param broadcastMessage
|
||||||
|
* @return broadcastMessage user destination == BROADCAST
|
||||||
|
*/
|
||||||
|
public boolean isBroadcast(Comm.BroadcastMessage broadcastMessage){
|
||||||
|
return broadcastMessage.getDestination() == BROADCAST;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void receiveMessage(Comm.BroadcastMessage envelope) {
|
||||||
|
try {
|
||||||
|
handleMessage(envelope);
|
||||||
|
} catch (InvalidProtocolBufferException e) {
|
||||||
|
logger.warn("Received invalid protocol buffer from channel", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
package meerkat.crypto.dkg.comm;
|
||||||
|
|
||||||
|
import meerkat.protobuf.DKG;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by talm on 12/04/16.
|
||||||
|
*/
|
||||||
|
public class MessageUtils {
|
||||||
|
public static DKG.Payload createMessage(DKG.Payload.Type type) {
|
||||||
|
return DKG.Payload.newBuilder().setType(type).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DKG.Payload createMessage(DKG.Payload.Type type, DKG.ShareMessage share) {
|
||||||
|
return DKG.Payload.newBuilder().setType(type).setShare(share).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DKG.Payload createMessage(DKG.Payload.Type type, DKG.ShareMessage.Builder share) {
|
||||||
|
return DKG.Payload.newBuilder().setType(type).setShare(share).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DKG.Payload createMessage(DKG.Payload.Type type, DKG.IDMessage id) {
|
||||||
|
return DKG.Payload.newBuilder().setType(type).setId(id).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DKG.Payload createMessage(DKG.Payload.Type type, DKG.IDMessage.Builder id) {
|
||||||
|
return DKG.Payload.newBuilder().setType(type).setId(id).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DKG.Payload createMessage(DKG.Payload.Type type, DKG.CommitmentMessage commitment) {
|
||||||
|
return DKG.Payload.newBuilder().setType(type).setCommitment(commitment).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DKG.Payload createMessage(DKG.Payload.Type type, DKG.CommitmentMessage.Builder commitment) {
|
||||||
|
return DKG.Payload.newBuilder().setType(type).setCommitment(commitment).build();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
package meerkat.crypto.dkg.feldman;
|
||||||
|
|
||||||
|
import meerkat.crypto.secretsharing.shamir.Polynomial;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Tzlil on 3/14/2016.
|
||||||
|
*
|
||||||
|
* contains all relevant information on specific party during
|
||||||
|
* the run of Joint Feldamn protocol
|
||||||
|
*/
|
||||||
|
public class Party<T> {
|
||||||
|
public final int id;
|
||||||
|
public Polynomial.Point share;
|
||||||
|
public ArrayList<T> commitments;
|
||||||
|
public boolean doneFlag;
|
||||||
|
public Protocol.ComplaintState[] complaints;
|
||||||
|
public boolean aborted;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param id party identifier - 1 <= id <= n
|
||||||
|
* @param n number of parties in current run protocol
|
||||||
|
* @param t protocol's threshold
|
||||||
|
*/
|
||||||
|
public Party(int id, int n, int t) {
|
||||||
|
this.id = id;
|
||||||
|
this.share = null;
|
||||||
|
this.doneFlag = false;
|
||||||
|
this.complaints = new Protocol.ComplaintState[n];
|
||||||
|
Arrays.fill(this.complaints, Protocol.ComplaintState.OK);
|
||||||
|
this.commitments = new ArrayList<T>(t + 1);
|
||||||
|
for (int i = 0; i <= t ; i++){
|
||||||
|
commitments.add(null);
|
||||||
|
}
|
||||||
|
this.aborted = false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,368 @@
|
||||||
|
package meerkat.crypto.dkg.feldman;
|
||||||
|
|
||||||
|
import meerkat.comm.Channel;
|
||||||
|
import meerkat.crypto.secretsharing.feldman.VerifiableSecretSharing;
|
||||||
|
import meerkat.crypto.secretsharing.shamir.Polynomial;
|
||||||
|
import com.google.protobuf.ByteString;
|
||||||
|
import meerkat.protobuf.DKG;
|
||||||
|
import org.factcenter.qilin.primitives.Group;
|
||||||
|
import org.factcenter.qilin.util.ByteEncoder;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import static meerkat.crypto.dkg.comm.MessageUtils.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Tzlil on 3/14/2016.
|
||||||
|
*
|
||||||
|
* an implementation of JointFeldman distributed key generation protocol.
|
||||||
|
*
|
||||||
|
* allows set of n parties to generate random key with threshold t.
|
||||||
|
*/
|
||||||
|
public class Protocol<T> extends VerifiableSecretSharing<T> {
|
||||||
|
public enum ComplaintState {
|
||||||
|
/**
|
||||||
|
* No complaints, no response required at this point.
|
||||||
|
*/
|
||||||
|
OK,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Party received complaint, waiting for response from party
|
||||||
|
*/
|
||||||
|
Waiting,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Party gave invalid answer to conplaint.
|
||||||
|
*/
|
||||||
|
Disqualified,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Party received complaint, gave valid answer.
|
||||||
|
*/
|
||||||
|
NonDisqualified
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* My share id.
|
||||||
|
*/
|
||||||
|
protected final int id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All parties participating in key generation.
|
||||||
|
* parties[id-1] has my info.
|
||||||
|
*/
|
||||||
|
private Party<T>[] parties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* communication object
|
||||||
|
*/
|
||||||
|
protected Channel channel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode/Decode group elements
|
||||||
|
*/
|
||||||
|
protected final ByteEncoder<T> encoder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* constructor
|
||||||
|
* @param q a large prime.
|
||||||
|
* @param t threshold. Any t+1 share holders can recover the secret,
|
||||||
|
* but any set of at most t share holders cannot
|
||||||
|
* @param n number of share holders
|
||||||
|
* @param zi secret, chosen from Zq
|
||||||
|
* @param random use for generate random polynomial
|
||||||
|
* @param group
|
||||||
|
* @param q a large prime dividing group order.
|
||||||
|
* @param g a generator of cyclic group of order q.
|
||||||
|
* the generated group is a subgroup of the given group.
|
||||||
|
* it must be chosen such that computing discrete logarithms is hard in this group.
|
||||||
|
* @param encoder Encode/Decode group elements (of type T) to/from byte array
|
||||||
|
*/
|
||||||
|
public Protocol(int t, int n, BigInteger zi, Random random, BigInteger q, T g
|
||||||
|
, Group<T> group, int id, ByteEncoder<T> encoder) {
|
||||||
|
super(t, n, zi, random, q, g,group);
|
||||||
|
this.id = id;
|
||||||
|
this.parties = new Party[n];
|
||||||
|
for (int i = 1; i <= n ; i++){
|
||||||
|
this.parties[i - 1] = new Party(i,n,t);
|
||||||
|
}
|
||||||
|
this.parties[id - 1].share = getShare(id);
|
||||||
|
this.encoder = encoder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* setter
|
||||||
|
* @param channel
|
||||||
|
*/
|
||||||
|
public void setChannel(Channel channel){
|
||||||
|
this.channel = channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* setter
|
||||||
|
* @param parties
|
||||||
|
*/
|
||||||
|
protected void setParties(Party[] parties){
|
||||||
|
this.parties = parties;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getter
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
protected Party[] getParties(){
|
||||||
|
return parties;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* stage1.1 according to the protocol
|
||||||
|
* Pi broadcasts Aik for k = 0,...,t.
|
||||||
|
*/
|
||||||
|
public void broadcastCommitments(){
|
||||||
|
broadcastCommitments(commitmentsArrayList);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pack commitments as messages and broadcast them
|
||||||
|
* @param commitments
|
||||||
|
*/
|
||||||
|
public void broadcastCommitments(ArrayList<T> commitments){
|
||||||
|
DKG.CommitmentMessage commitmentMessage;
|
||||||
|
for (int k = 0; k <= t ; k++){
|
||||||
|
commitmentMessage = DKG.CommitmentMessage.newBuilder()
|
||||||
|
.setCommitment(ByteString.copyFrom(encoder.encode(commitments.get(k))))
|
||||||
|
.setK(k)
|
||||||
|
.build();
|
||||||
|
channel.broadcastMessage(createMessage(DKG.Payload.Type.COMMITMENT, commitmentMessage));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send channel j her secret share (of my polynomial)
|
||||||
|
* @param j
|
||||||
|
*/
|
||||||
|
public void sendSecret(int j){
|
||||||
|
ByteString secret = ByteString.copyFrom(getShare(j).y.toByteArray());
|
||||||
|
channel.sendMessage(j, createMessage(DKG.Payload.Type.SHARE,
|
||||||
|
DKG.ShareMessage.newBuilder()
|
||||||
|
.setI(id)
|
||||||
|
.setJ(j)
|
||||||
|
.setShare(secret)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* stage1.2 according to the protocol
|
||||||
|
* Pi computes the shares Sij for j = 1,...,n and sends Sij secretly to Pj.
|
||||||
|
*/
|
||||||
|
public void sendSecrets(){
|
||||||
|
for (int j = 1; j <= n ; j++){
|
||||||
|
if(j != id){
|
||||||
|
sendSecret(j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param i
|
||||||
|
* @return computeVerificationValue(j,parties[i - 1].commitments,group) == g ^ parties[i - 1].share mod q
|
||||||
|
*/
|
||||||
|
public boolean isValidShare(int i){
|
||||||
|
Party<T> party = parties[i - 1];
|
||||||
|
synchronized (parties[i - 1]) {
|
||||||
|
return isValidShare(party.share, party.commitments, id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param share
|
||||||
|
* @param commitments
|
||||||
|
* @param j
|
||||||
|
* @return computeVerificationValue(j,commitments,group) == g ^ secret.y mod q
|
||||||
|
*/
|
||||||
|
public boolean isValidShare(Polynomial.Point share, ArrayList<T> commitments, int j){
|
||||||
|
try{
|
||||||
|
T v = computeVerificationValue(j,commitments,group);
|
||||||
|
return group.multiply(g,share.y).equals(v);
|
||||||
|
}
|
||||||
|
catch (NullPointerException e){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* stage2 according to the protocol
|
||||||
|
* Pj verifies all the shares he received (using isValidShare)
|
||||||
|
* if check fails for an index i, Pj broadcasts a complaint against Pi.
|
||||||
|
*/
|
||||||
|
public void broadcastComplaints(){
|
||||||
|
for (int i = 1; i <= n ; i++ ){
|
||||||
|
if(i != id && !isValidShare(i)) {
|
||||||
|
broadcastComplaint(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create a complaint message against i and broadcast it
|
||||||
|
* @param i
|
||||||
|
*/
|
||||||
|
private void broadcastComplaint(int i){
|
||||||
|
//message = new Message(Type.Complaint, j)
|
||||||
|
DKG.IDMessage complaint = DKG.IDMessage.newBuilder()
|
||||||
|
.setId(i)
|
||||||
|
.build();
|
||||||
|
channel.broadcastMessage(createMessage(DKG.Payload.Type.COMPLAINT, complaint));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create an answer message for j and broadcast it
|
||||||
|
* @param j
|
||||||
|
*/
|
||||||
|
public void broadcastComplaintAnswer(int j){
|
||||||
|
channel.broadcastMessage(createMessage(DKG.Payload.Type.ANSWER, DKG.ShareMessage.newBuilder()
|
||||||
|
.setI(id)
|
||||||
|
.setJ(j)
|
||||||
|
.setShare(ByteString.copyFrom(getShare(j).y.toByteArray()))));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* stage3.1 according to the protocol
|
||||||
|
* if more than t players complain against a player Pi he is disqualified.
|
||||||
|
*/
|
||||||
|
public void answerAllComplainingPlayers(){
|
||||||
|
ComplaintState[] complaints = parties[id - 1].complaints;
|
||||||
|
for (int i = 1; i <= n; i++) {
|
||||||
|
switch (complaints[i - 1]) {
|
||||||
|
case Waiting:
|
||||||
|
broadcastComplaintAnswer(i);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* stage3.2 according to the protocol
|
||||||
|
* if any of the revealed shares fails the verification test, player Pi is disqualified.
|
||||||
|
* set QUAL to be the set of non-disqualified players.
|
||||||
|
*/
|
||||||
|
public Set<Integer> calcQUAL(){
|
||||||
|
Set<Integer> QUAL = new HashSet<Integer>();
|
||||||
|
boolean nonDisqualified;
|
||||||
|
int counter;
|
||||||
|
for (int i = 1; i <= n; i++) {
|
||||||
|
synchronized (parties[i - 1]) {
|
||||||
|
ComplaintState[] complaints = parties[i - 1].complaints;
|
||||||
|
nonDisqualified = true;
|
||||||
|
counter = 0;
|
||||||
|
for (int j = 1; j <= n; j++) {
|
||||||
|
switch (complaints[j - 1]) {
|
||||||
|
case OK:
|
||||||
|
break;
|
||||||
|
case NonDisqualified:
|
||||||
|
counter++;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
nonDisqualified = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!nonDisqualified)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (nonDisqualified && counter <= t) {
|
||||||
|
QUAL.add(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return QUAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* compute Y, the commitment to the final public key (includes only qualifying set)
|
||||||
|
* stage4.1 according to the protocol
|
||||||
|
* public value y is computed as y = multiplication of yi mod p for i in QUAL
|
||||||
|
*/
|
||||||
|
public T calcY(Set<Integer> QUAL){
|
||||||
|
T y = group.zero();
|
||||||
|
for (int i : QUAL) {
|
||||||
|
synchronized (parties[i - 1]) {
|
||||||
|
y = group.add(y, parties[i - 1].commitments.get(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* stage4.2 according to the protocol
|
||||||
|
* public verification values are computed as Ak = multiplication
|
||||||
|
* of Aik mod p for i in QUAL for k = 0,...,t
|
||||||
|
*/
|
||||||
|
public ArrayList<T> calcCommitments(Set<Integer> QUAL){
|
||||||
|
ArrayList<T> commitments = new ArrayList<T>(t+1);
|
||||||
|
T value;
|
||||||
|
for (int k = 0; k <= t; k++){
|
||||||
|
value = group.zero();
|
||||||
|
for (int i : QUAL) {
|
||||||
|
synchronized (parties[i - 1]) {
|
||||||
|
value = group.add(value, parties[i - 1].commitments.get(k));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
commitments.add(k,value);
|
||||||
|
}
|
||||||
|
return commitments;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* stage4.3 according to the protocol
|
||||||
|
* Pj sets is share of the share as xj = sum of Sij mod q for i in QUAL
|
||||||
|
*/
|
||||||
|
public Polynomial.Point calcShare(Set<Integer> QUAL){
|
||||||
|
BigInteger xj = BigInteger.ZERO;
|
||||||
|
for (int i : QUAL) {
|
||||||
|
synchronized (parties[i - 1]) {
|
||||||
|
xj = xj.add(parties[i - 1].share.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new Polynomial.Point(BigInteger.valueOf(id) , xj.mod(q));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* decode commitment from arr
|
||||||
|
* @param arr
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public T decodeCommitment(byte[] arr){
|
||||||
|
return encoder.decode(arr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getter
|
||||||
|
* @return id
|
||||||
|
*/
|
||||||
|
public int getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getter
|
||||||
|
* @return channel
|
||||||
|
*/
|
||||||
|
public Channel getChannel() {
|
||||||
|
return channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getter
|
||||||
|
* @return encoder
|
||||||
|
*/
|
||||||
|
public ByteEncoder<T> getEncoder() {
|
||||||
|
return encoder;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,588 @@
|
||||||
|
package meerkat.crypto.dkg.feldman;
|
||||||
|
|
||||||
|
import com.google.protobuf.InvalidProtocolBufferException;
|
||||||
|
import com.google.protobuf.TextFormat;
|
||||||
|
import meerkat.comm.Channel;
|
||||||
|
import meerkat.crypto.dkg.comm.MessageHandler;
|
||||||
|
import meerkat.crypto.secretsharing.shamir.Polynomial;
|
||||||
|
import com.google.protobuf.ByteString;
|
||||||
|
import meerkat.protobuf.Comm;
|
||||||
|
import meerkat.protobuf.DKG;
|
||||||
|
import org.factcenter.qilin.primitives.Group;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ArrayBlockingQueue;
|
||||||
|
import java.util.concurrent.BlockingQueue;
|
||||||
|
import java.util.concurrent.LinkedBlockingDeque;
|
||||||
|
|
||||||
|
import static meerkat.crypto.dkg.comm.MessageUtils.createMessage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Tzlil on 3/14/2016.
|
||||||
|
*
|
||||||
|
* implementation of joint feldman protocol user.
|
||||||
|
*
|
||||||
|
* according to the protocol, each user run feldman verifiable secret sharing
|
||||||
|
* as a dealer.
|
||||||
|
*
|
||||||
|
* by the end of run(), each party in QUAL has his own share of the generated random key.
|
||||||
|
* this key can be recover by any subset of QUAL of size at least t + 1.
|
||||||
|
*/
|
||||||
|
public class User<T> implements Runnable {
|
||||||
|
final Logger logger = LoggerFactory.getLogger(getClass());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* joint feldman protocol object
|
||||||
|
*/
|
||||||
|
protected final Protocol<T> dkg;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* a generator of cyclic group of order q.
|
||||||
|
* the generated group is a subgroup of the given group.
|
||||||
|
* it must be chosen such that computing discrete logarithms is hard in this group.
|
||||||
|
*/
|
||||||
|
protected final T g;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* cyclic group contains g.
|
||||||
|
*/
|
||||||
|
protected final Group<T> group;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* user id
|
||||||
|
*/
|
||||||
|
protected final int id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* threshold
|
||||||
|
*/
|
||||||
|
protected final int t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* number of shares
|
||||||
|
*/
|
||||||
|
protected final int n;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* channel object
|
||||||
|
*/
|
||||||
|
protected final Channel channel; //
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All parties participating in key generation.
|
||||||
|
* parties[id-1] has my info.
|
||||||
|
*/
|
||||||
|
protected final Party[] parties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set of all non-disqualified parties
|
||||||
|
*/
|
||||||
|
protected Set<Integer> QUAL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* my own share of the generated random key.
|
||||||
|
*/
|
||||||
|
protected Polynomial.Point share;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* public verification values
|
||||||
|
*/
|
||||||
|
protected ArrayList<T> commitments;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* public value,
|
||||||
|
* y = g ^ key
|
||||||
|
*/
|
||||||
|
protected T y;
|
||||||
|
|
||||||
|
protected BlockingQueue<Comm.BroadcastMessage> receiveQueue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* constructor
|
||||||
|
* @param dkg joint feldman protocol object
|
||||||
|
* @param channel channel object
|
||||||
|
*/
|
||||||
|
public User(Protocol<T> dkg, Channel channel) {
|
||||||
|
this.dkg = dkg;
|
||||||
|
|
||||||
|
this.g = dkg.getGenerator();
|
||||||
|
this.group = dkg.getGroup();
|
||||||
|
this.n = dkg.getN();
|
||||||
|
this.t = dkg.getT();
|
||||||
|
this.id = dkg.getId();
|
||||||
|
|
||||||
|
this.channel = channel;
|
||||||
|
dkg.setChannel(channel);
|
||||||
|
registerReceiverCallback();
|
||||||
|
|
||||||
|
this.parties = dkg.getParties();
|
||||||
|
this.QUAL = null;
|
||||||
|
this.commitments = null;
|
||||||
|
this.share = null;
|
||||||
|
this.y = null;
|
||||||
|
|
||||||
|
this.receiveQueue = new LinkedBlockingDeque<>();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create MessageHandler and register it as ReceiverCallback
|
||||||
|
*/
|
||||||
|
protected void registerReceiverCallback() {
|
||||||
|
channel.registerReceiverCallback(new meerkat.crypto.dkg.comm.MessageHandler() {
|
||||||
|
@Override
|
||||||
|
public void handleMessage(Comm.BroadcastMessage envelope) throws InvalidProtocolBufferException {
|
||||||
|
receiveQueue.add(envelope);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// this.messageHandler = new MessageHandler();
|
||||||
|
// channel.registerReceiverCallback(messageHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait for at least one message to arrive, then handle any messages currently in the queue
|
||||||
|
*/
|
||||||
|
protected void waitAndHandleReceivedMessages() {
|
||||||
|
Comm.BroadcastMessage msg = null;
|
||||||
|
while (!stop && msg == null) {
|
||||||
|
try {
|
||||||
|
msg = receiveQueue.take();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
// Possibly stop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (!stop && msg != null) {
|
||||||
|
try {
|
||||||
|
handleMessage(msg);
|
||||||
|
} catch (InvalidProtocolBufferException e) {
|
||||||
|
logger.warn("Received invalid message: {}", TextFormat.printToString(msg));
|
||||||
|
}
|
||||||
|
msg = receiveQueue.poll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* stage1 according to the protocol
|
||||||
|
* 1. Pi broadcasts Aik for k = 0,...,t.
|
||||||
|
* 2. Pi computes the shares Sij for j = 1,...,n and sends Sij secretly to Pj.
|
||||||
|
*/
|
||||||
|
protected void stage1() {
|
||||||
|
dkg.broadcastCommitments();
|
||||||
|
dkg.sendSecrets();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if all shares and commitments have arrived from other parties
|
||||||
|
*/
|
||||||
|
protected boolean isStageOneCompleted() {
|
||||||
|
for (int i = 0 ; i < n ; i++) {
|
||||||
|
if (!parties[i].aborted) {
|
||||||
|
if (parties[i].share == null)
|
||||||
|
return false;
|
||||||
|
for (int k = 0 ; k <= t ; k++) {
|
||||||
|
if (parties[i].commitments.get(k) == null)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void waitUntilStageOneCompleted() {
|
||||||
|
while (!stop && !isStageOneCompleted())
|
||||||
|
waitAndHandleReceivedMessages();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isStageTwoCompleted() {
|
||||||
|
for (int i = 0 ; i < n ; i++) {
|
||||||
|
if (!parties[i].aborted && !parties[i].doneFlag)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* stage2 according to the protocol
|
||||||
|
* Pj verifies all the shares he received
|
||||||
|
* if check fails for an index i, Pj broadcasts a complaint against Pi.
|
||||||
|
* Pj broadcasts done message at the end of this stage
|
||||||
|
*/
|
||||||
|
protected void stage2() {
|
||||||
|
dkg.broadcastComplaints();
|
||||||
|
//broadcast done message after all complaints
|
||||||
|
channel.broadcastMessage(createMessage(DKG.Payload.Type.DONE));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* wait until all other parties done complaining by receiving done message
|
||||||
|
*/
|
||||||
|
protected void waitUntilStageTwoCompleted(){
|
||||||
|
while (!stop && !isStageTwoCompleted())
|
||||||
|
waitAndHandleReceivedMessages();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected boolean haveReceivedAllStage3ComplaintAnswers() {
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
if (parties[i].aborted)
|
||||||
|
continue;
|
||||||
|
for (int j = 0; j < n; j++) {
|
||||||
|
if (parties[i].complaints[j].equals(Protocol.ComplaintState.Waiting))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* stage3 according to the protocol
|
||||||
|
* 1. if more than t players complain against a player Pi he is disqualified.
|
||||||
|
* otherwise Pi broadcasts the share Sij for each complaining player Pj.
|
||||||
|
* 2. if any of the revealed shares fails the verification test, player Pi is disqualified.
|
||||||
|
* set QUAL to be the set of non-disqualified players.
|
||||||
|
*/
|
||||||
|
protected void stage3(){
|
||||||
|
dkg.answerAllComplainingPlayers();
|
||||||
|
|
||||||
|
// wait until there is no complaint waiting for answer
|
||||||
|
while (!stop && !haveReceivedAllStage3ComplaintAnswers())
|
||||||
|
waitAndHandleReceivedMessages();
|
||||||
|
|
||||||
|
this.QUAL = dkg.calcQUAL();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* stage4 according to the protocol
|
||||||
|
* 1. public value y is computed as y = multiplication of yi mod p for i in QUAL
|
||||||
|
* 2. public verification values are computed as Ak = multiplication of Aik mod p for i in QUAL for k = 0,...,t
|
||||||
|
* 3. Pj sets is share of the secret as xj = sum of Sij mod q for i in QUAL
|
||||||
|
*/
|
||||||
|
protected void stage4(){
|
||||||
|
this.y = dkg.calcY(QUAL);
|
||||||
|
this.commitments = dkg.calcCommitments(QUAL);
|
||||||
|
this.share = dkg.calcShare(QUAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
this.runThread = Thread.currentThread();
|
||||||
|
// For debugging
|
||||||
|
String previousName = runThread.getName();
|
||||||
|
runThread.setName(getClass().getName() +":" + getID());
|
||||||
|
|
||||||
|
try {
|
||||||
|
stage1();
|
||||||
|
waitUntilStageOneCompleted();
|
||||||
|
if (stop) return;
|
||||||
|
stage2();
|
||||||
|
waitUntilStageTwoCompleted();
|
||||||
|
if (stop) return;
|
||||||
|
stage3();
|
||||||
|
if (stop) return;
|
||||||
|
stage4();
|
||||||
|
} finally {
|
||||||
|
runThread.setName(previousName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* current thread in the main loop
|
||||||
|
*/
|
||||||
|
protected Thread runThread;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* flag indicates if there was request to stop the current run of the protocol
|
||||||
|
*/
|
||||||
|
protected boolean stop = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request the current run loop to exit gracefully
|
||||||
|
*/
|
||||||
|
public void stop() {
|
||||||
|
try {
|
||||||
|
stop = true;
|
||||||
|
runThread.interrupt();
|
||||||
|
}catch (Exception e){
|
||||||
|
//do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getter
|
||||||
|
* @return commitments
|
||||||
|
*/
|
||||||
|
public ArrayList<T> getCommitments() {
|
||||||
|
return commitments;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getter
|
||||||
|
* @return g
|
||||||
|
*/
|
||||||
|
public T getGenerator() {
|
||||||
|
return g;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getter
|
||||||
|
* @return group
|
||||||
|
*/
|
||||||
|
public Group<T> getGroup() {
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getter
|
||||||
|
* @return share
|
||||||
|
*/
|
||||||
|
public Polynomial.Point getShare() {
|
||||||
|
return share;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getter
|
||||||
|
* @return id
|
||||||
|
*/
|
||||||
|
public int getID() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getter
|
||||||
|
* @return n
|
||||||
|
*/
|
||||||
|
public int getN() {
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getter
|
||||||
|
* @return t
|
||||||
|
*/
|
||||||
|
public int getT() {
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getter
|
||||||
|
* @return y
|
||||||
|
*/
|
||||||
|
public T getPublicValue() {
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getter
|
||||||
|
* @return QUAL
|
||||||
|
*/
|
||||||
|
public Set<Integer> getQUAL() {
|
||||||
|
return QUAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getter
|
||||||
|
* @return channel
|
||||||
|
*/
|
||||||
|
public Channel getChannel() {
|
||||||
|
return channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* commitment message is valid if:
|
||||||
|
* 1. it was received in broadcast chanel
|
||||||
|
* 2. the sender didn't sent this commitment before
|
||||||
|
*/
|
||||||
|
protected boolean isValidCommitmentMessage(int sender, boolean isBroadcast, DKG.CommitmentMessage commitmentMessage){
|
||||||
|
int i = sender - 1;
|
||||||
|
int k = commitmentMessage.getK();
|
||||||
|
return isBroadcast && parties[i].commitments.get(k) == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* secret message is valid if:
|
||||||
|
* 1. it was received in private chanel
|
||||||
|
* 2. the sender didn't sent secret message before
|
||||||
|
* 3. secret.i == i
|
||||||
|
* 4. secret.j == id
|
||||||
|
*/
|
||||||
|
protected boolean isValidSecretMessage(int sender, boolean isBroadcast, DKG.ShareMessage secretMessage){
|
||||||
|
int i = secretMessage.getI();
|
||||||
|
int j = secretMessage.getJ();
|
||||||
|
if(sender != i || isBroadcast)
|
||||||
|
return false;
|
||||||
|
else
|
||||||
|
return parties[i - 1].share == null && j == id;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* done message is valid if:
|
||||||
|
* 1. it was received in broadcast chanel
|
||||||
|
* 2. the sender didn't sent done message before
|
||||||
|
*/
|
||||||
|
protected boolean isValidDoneMessage(int sender, boolean isBroadcast){
|
||||||
|
return isBroadcast && !parties[sender - 1].doneFlag;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* complaint message is valid if:
|
||||||
|
* 1. it was received in broadcast chanel
|
||||||
|
* 2. the sender didn't complained against id before
|
||||||
|
*/
|
||||||
|
protected boolean isValidComplaintMessage(int sender, boolean isBroadcast, DKG.IDMessage complaintMessage){
|
||||||
|
int i = sender;
|
||||||
|
int j = complaintMessage.getId();
|
||||||
|
|
||||||
|
assert(i > 0);
|
||||||
|
assert(j > 0);
|
||||||
|
assert(i <= parties.length);
|
||||||
|
assert(j <= parties[i-1].complaints.length);
|
||||||
|
|
||||||
|
return isBroadcast && parties[i - 1].complaints[j - 1].equals( Protocol.ComplaintState.OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* answer message is valid if:
|
||||||
|
* 1. it was received in broadcast chanel
|
||||||
|
* 2. secret.i == i
|
||||||
|
* 3. 1 <= secret.j <= n
|
||||||
|
* 4. it is marked that j complained against i and i didn't received
|
||||||
|
*/
|
||||||
|
protected boolean isValidAnswerMessage(int sender, boolean isBroadcast, DKG.ShareMessage secretMessage){
|
||||||
|
int i = secretMessage.getI();
|
||||||
|
int j = secretMessage.getJ();
|
||||||
|
if(sender != i || !isBroadcast)
|
||||||
|
return false;
|
||||||
|
else
|
||||||
|
return j >= 1 && j <= n && parties[i - 1].complaints[j - 1].equals(Protocol.ComplaintState.Waiting);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void handleMessage(Comm.BroadcastMessage envelope) throws InvalidProtocolBufferException {
|
||||||
|
int sender = envelope.getSender();
|
||||||
|
boolean isBroadcast = !envelope.getIsPrivate();
|
||||||
|
DKG.Payload msg = DKG.Payload.parseFrom(envelope.getPayload());
|
||||||
|
|
||||||
|
logger.debug("handling Message: Dst={}, Src={}, [{}]",
|
||||||
|
envelope.getDestination(), envelope.getSender(), TextFormat.printToString(msg));
|
||||||
|
|
||||||
|
switch (msg.getType()) {
|
||||||
|
case COMMITMENT:
|
||||||
|
/**
|
||||||
|
* saves the commitment
|
||||||
|
*/
|
||||||
|
assert msg.getPayloadDataCase() == DKG.Payload.PayloadDataCase.COMMITMENT;
|
||||||
|
DKG.CommitmentMessage commitmentMessage = msg.getCommitment();
|
||||||
|
if (isValidCommitmentMessage(sender, isBroadcast, commitmentMessage)) {
|
||||||
|
int i = sender - 1;
|
||||||
|
int k = commitmentMessage.getK();
|
||||||
|
|
||||||
|
parties[i].commitments.set(k, extractCommitment(commitmentMessage));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
case SHARE:
|
||||||
|
/**
|
||||||
|
* saves the secret
|
||||||
|
*/
|
||||||
|
assert msg.getPayloadDataCase() == DKG.Payload.PayloadDataCase.SHARE;
|
||||||
|
DKG.ShareMessage secretMessage = msg.getShare();
|
||||||
|
if(isValidSecretMessage(sender,isBroadcast,secretMessage)) {
|
||||||
|
int i = secretMessage.getI();
|
||||||
|
Polynomial.Point secret = extractShare(id,secretMessage.getShare());
|
||||||
|
parties[i - 1].share = secret;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DONE:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* marks that the sender was finished sending all his complaints
|
||||||
|
*/
|
||||||
|
if(isValidDoneMessage(sender,isBroadcast)) {
|
||||||
|
parties[sender - 1].doneFlag = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case COMPLAINT:
|
||||||
|
/**
|
||||||
|
* marks that the sender was complained against id
|
||||||
|
*/
|
||||||
|
if (msg.getPayloadDataCase() != DKG.Payload.PayloadDataCase.ID) {
|
||||||
|
logger.error("User {} Expecting ID message, got from SRC={} msg {}", getID(), envelope.getSender(), TextFormat.printToString(msg));
|
||||||
|
assert (msg.getPayloadDataCase() == DKG.Payload.PayloadDataCase.ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
DKG.IDMessage complaintMessage = msg.getId();
|
||||||
|
if(isValidComplaintMessage(sender,isBroadcast,complaintMessage)){
|
||||||
|
int i = sender;
|
||||||
|
int j = complaintMessage.getId();
|
||||||
|
parties[j - 1].complaints[i - 1] = Protocol.ComplaintState.Waiting;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ANSWER:
|
||||||
|
/**
|
||||||
|
* if the secret is valid, marks the complaint as NonDisqualified
|
||||||
|
* else marks it as Disqualified
|
||||||
|
* in case that the complainer is id ( j == id ), saves the secret
|
||||||
|
*/
|
||||||
|
assert msg.getPayloadDataCase() == DKG.Payload.PayloadDataCase.SHARE;
|
||||||
|
secretMessage = msg.getShare();
|
||||||
|
if(isValidAnswerMessage(sender,isBroadcast,secretMessage)) {
|
||||||
|
int i = secretMessage.getI();
|
||||||
|
int j = secretMessage.getJ();
|
||||||
|
Polynomial.Point secret = extractShare(j,secretMessage.getShare());
|
||||||
|
if (dkg.isValidShare(secret, parties[i - 1].commitments, j)) {
|
||||||
|
parties[i - 1].complaints[j - 1] = Protocol.ComplaintState.NonDisqualified;
|
||||||
|
} else {
|
||||||
|
parties[i - 1].complaints[j - 1] = Protocol.ComplaintState.Disqualified;
|
||||||
|
}
|
||||||
|
if (j == id) {
|
||||||
|
parties[i - 1].share = secret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ABORT:
|
||||||
|
/**
|
||||||
|
* marks that the sender was aborted
|
||||||
|
*/
|
||||||
|
parties[sender - 1].aborted = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
logger.error("Bad message: SRC={}, DST={}, Payload={}", envelope.getSender(), envelope.getDestination(), TextFormat.printToString(msg));
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* extract share value from ByteString
|
||||||
|
* @param i
|
||||||
|
* @param share
|
||||||
|
* @return new Point (i,share)
|
||||||
|
*/
|
||||||
|
public Polynomial.Point extractShare(int i, ByteString share){
|
||||||
|
BigInteger x = BigInteger.valueOf(i);
|
||||||
|
BigInteger y = new BigInteger(share.toByteArray());
|
||||||
|
return new Polynomial.Point(x,y);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param commitmentMessage
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public T extractCommitment(DKG.CommitmentMessage commitmentMessage){
|
||||||
|
return dkg.decodeCommitment(commitmentMessage.getCommitment().toByteArray());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
package meerkat.crypto.dkg.gjkr;
|
||||||
|
|
||||||
|
import meerkat.crypto.secretsharing.shamir.Polynomial;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Tzlil on 3/16/2016.
|
||||||
|
*
|
||||||
|
* an extension of DistributedKeyGenerationParty
|
||||||
|
* contains all relevant information on specific party during
|
||||||
|
* the run of the safe protocol
|
||||||
|
*/
|
||||||
|
public class Party<T> extends meerkat.crypto.dkg.feldman.Party<T> {
|
||||||
|
public Polynomial.Point shareT;
|
||||||
|
public boolean ysDoneFlag;
|
||||||
|
public ArrayList<T> verifiableValues;
|
||||||
|
public Set<Polynomial.Point> recoverSharesSet;
|
||||||
|
public Party(int id, int n, int t) {
|
||||||
|
super(id, n, t);
|
||||||
|
this.shareT = null;
|
||||||
|
this.ysDoneFlag = false;
|
||||||
|
this.verifiableValues = new ArrayList<T>(this.commitments);
|
||||||
|
this.recoverSharesSet = new HashSet<Polynomial.Point>();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,165 @@
|
||||||
|
package meerkat.crypto.dkg.gjkr;
|
||||||
|
|
||||||
|
import meerkat.crypto.dkg.comm.MessageUtils;
|
||||||
|
import meerkat.crypto.secretsharing.feldman.VerifiableSecretSharing;
|
||||||
|
import meerkat.crypto.secretsharing.shamir.Polynomial;
|
||||||
|
import com.google.protobuf.ByteString;
|
||||||
|
import meerkat.protobuf.DKG;
|
||||||
|
import org.factcenter.qilin.primitives.Group;
|
||||||
|
import org.factcenter.qilin.util.ByteEncoder;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Tzlil on 3/16/2016.
|
||||||
|
* TODO: comments
|
||||||
|
* TODO: put Channel (ChannelImpl) in constructor
|
||||||
|
*/
|
||||||
|
public class Protocol<T> extends meerkat.crypto.dkg.feldman.Protocol<T> {
|
||||||
|
|
||||||
|
private VerifiableSecretSharing<T> maskingShares;
|
||||||
|
private final T h;
|
||||||
|
private Party<T>[] parties;
|
||||||
|
|
||||||
|
public Protocol(int t, int n, BigInteger zi, Random random, BigInteger q, T g
|
||||||
|
, T h, Group<T> group, int id, ByteEncoder<T> byteEncoder) {
|
||||||
|
super(t, n, zi, random, q, g, group, id,byteEncoder);
|
||||||
|
this.h = h;
|
||||||
|
BigInteger r = new BigInteger(q.bitLength(),random).mod(q);
|
||||||
|
this.maskingShares = new VerifiableSecretSharing(t,n,r,random,q,h,group);
|
||||||
|
this.parties = new Party[n];
|
||||||
|
for (int i = 1; i <= n ; i++){
|
||||||
|
this.parties[i - 1] = new Party(i,n,t);
|
||||||
|
}
|
||||||
|
this.parties[id - 1].share = getShare(id);
|
||||||
|
this.parties[id - 1].shareT = maskingShares.getShare(id);
|
||||||
|
super.setParties(parties);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Party[] getParties(){
|
||||||
|
return parties;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setParties(Party[] parties) {
|
||||||
|
super.setParties(parties);
|
||||||
|
this.parties = parties;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendSecret(int j) {
|
||||||
|
Polynomial.Point secret = getShare(j);
|
||||||
|
Polynomial.Point secretT = maskingShares.getShare(j);
|
||||||
|
DKG.ShareMessage doubleSecretMessage = createShareMessage(id,j,secret,secretT);
|
||||||
|
// TODO: Change SHARE to SHARE
|
||||||
|
channel.sendMessage(j, MessageUtils.createMessage(DKG.Payload.Type.SHARE, doubleSecretMessage));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isValidShare(int i){
|
||||||
|
Party party = parties[i - 1];
|
||||||
|
return isValidShare(party.share, party.shareT, party.verifiableValues, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* test if share, shareT are valid with respect to verificationValues
|
||||||
|
* @param share
|
||||||
|
* @param shareT
|
||||||
|
* @param verificationValues
|
||||||
|
* @param j
|
||||||
|
* @return computeVerificationValue(j,verificationValues,group) == (g ^ share.y) * (h ^ shareT.y) mod q
|
||||||
|
*/
|
||||||
|
public boolean isValidShare(Polynomial.Point share, Polynomial.Point shareT, ArrayList<T> verificationValues, int j){
|
||||||
|
try {
|
||||||
|
T v = computeVerificationValue(j, verificationValues, group);
|
||||||
|
T exp = group.add(group.multiply(g, share.y), group.multiply(h, shareT.y));
|
||||||
|
return exp.equals(v);
|
||||||
|
}
|
||||||
|
catch (NullPointerException e){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create complaint message against i and broadcast it
|
||||||
|
* @param share
|
||||||
|
* @param shareT
|
||||||
|
* @param i
|
||||||
|
*/
|
||||||
|
private void broadcastComplaint(Polynomial.Point share, Polynomial.Point shareT, int i){
|
||||||
|
DKG.ShareMessage complaint = createShareMessage(i,id,share,shareT);
|
||||||
|
channel.broadcastMessage(MessageUtils.createMessage(DKG.Payload.Type.COMPLAINT, complaint));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* stage4.3 according to the protocol
|
||||||
|
* if check fails for index i, Pj
|
||||||
|
*/
|
||||||
|
public void computeAndBroadcastComplaints(Set<Integer> QUAL){
|
||||||
|
Party party;
|
||||||
|
for (int i : QUAL) {
|
||||||
|
party = parties[i - 1];
|
||||||
|
if (i != id) {
|
||||||
|
if (!super.isValidShare(party.share, party.commitments, id)) {
|
||||||
|
broadcastComplaint(party.share, party.shareT, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* compute verification values and broadcast them
|
||||||
|
* verificationValues[k] = g ^ commitments [k] * h ^ maskingShares.commitments [k]
|
||||||
|
*/
|
||||||
|
public void computeAndBroadcastVerificationValues(){
|
||||||
|
ArrayList<T> verificationValues = new ArrayList<T>(t+1);
|
||||||
|
ArrayList<T> hBaseCommitments = maskingShares.getCommitmentsArrayList();
|
||||||
|
for (int k = 0 ; k <= t ; k++){
|
||||||
|
verificationValues.add(k,group.add(commitmentsArrayList.get(k),hBaseCommitments.get(k)));
|
||||||
|
}
|
||||||
|
broadcastCommitments(verificationValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pack share, shareT i,j to createShareMessage
|
||||||
|
* @param i
|
||||||
|
* @param j
|
||||||
|
* @param share
|
||||||
|
* @param shareT
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
|
||||||
|
private DKG.ShareMessage createShareMessage(int i, int j, Polynomial.Point share, Polynomial.Point shareT){
|
||||||
|
DKG.ShareMessage ShareMessage = DKG.ShareMessage.newBuilder()
|
||||||
|
.setI(i)
|
||||||
|
.setJ(j)
|
||||||
|
.setShare(ByteString.copyFrom(share.y.toByteArray()))
|
||||||
|
.setShareT(ByteString.copyFrom(shareT.y.toByteArray()))
|
||||||
|
.build();
|
||||||
|
return ShareMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void broadcastComplaintAnswer(int j) {
|
||||||
|
DKG.ShareMessage answer = createShareMessage(id,j,getShare(j)
|
||||||
|
, maskingShares.getShare(j));
|
||||||
|
channel.broadcastMessage(MessageUtils.createMessage(DKG.Payload.Type.ANSWER, answer));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void broadcastAnswer(Polynomial.Point secret, Polynomial.Point secretT, int i){
|
||||||
|
DKG.ShareMessage complaint = createShareMessage(i,id,secret,secretT);
|
||||||
|
channel.broadcastMessage(MessageUtils.createMessage(DKG.Payload.Type.ANSWER,complaint));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getter
|
||||||
|
* @return h
|
||||||
|
*/
|
||||||
|
public T getH() {
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,359 @@
|
||||||
|
package meerkat.crypto.dkg.gjkr;
|
||||||
|
|
||||||
|
import com.google.protobuf.InvalidProtocolBufferException;
|
||||||
|
import meerkat.crypto.utils.Arithmetic;
|
||||||
|
import meerkat.crypto.utils.concrete.Fp;
|
||||||
|
import meerkat.comm.Channel;
|
||||||
|
import meerkat.crypto.secretsharing.shamir.Polynomial;
|
||||||
|
import meerkat.crypto.secretsharing.shamir.SecretSharing;
|
||||||
|
import meerkat.protobuf.Comm;
|
||||||
|
import meerkat.protobuf.DKG;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import static meerkat.crypto.dkg.comm.MessageUtils.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Tzlil on 3/16/2016.
|
||||||
|
* <p/>
|
||||||
|
* implementation of gjkr protocol user.
|
||||||
|
* <p/>
|
||||||
|
* this protocol extends joint Feldman protocol by splitting the protocol to commitment stage (stages 1,2,3)
|
||||||
|
* and revealing stage (stage 4).
|
||||||
|
* <p/>
|
||||||
|
* as in joint Feldman, each party in QUAL has his own share of the generated random key.
|
||||||
|
* this key can be recover by any subset of QUAL of size at least t + 1.
|
||||||
|
*/
|
||||||
|
public class User<T> extends meerkat.crypto.dkg.feldman.User<T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All parties participating in key generation.
|
||||||
|
* parties[id-1] has my info.
|
||||||
|
*/
|
||||||
|
protected Party<T>[] parties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gjkr secure protocol object
|
||||||
|
*/
|
||||||
|
protected final Protocol<T> sdkg;
|
||||||
|
|
||||||
|
boolean isStage4;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* constructor
|
||||||
|
*
|
||||||
|
* @param sdkg gjkr protocol object
|
||||||
|
* @param channel channel object
|
||||||
|
*/
|
||||||
|
public User(Protocol<T> sdkg, Channel channel) {
|
||||||
|
super(sdkg, channel);
|
||||||
|
this.sdkg = sdkg;
|
||||||
|
this.parties = sdkg.getParties();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* stage1 according to the protocol
|
||||||
|
* 1. Pi broadcasts Cik=Aik*Bik for k = 0,...,t.
|
||||||
|
* 2. Pi computes the shares Sij,Sij' for j = 1,...,n and sends Sij,Sij' secretly to Pj.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void stage1() {
|
||||||
|
sdkg.computeAndBroadcastVerificationValues();
|
||||||
|
sdkg.sendSecrets();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void waitUntilStageOneCompleted() {
|
||||||
|
super.waitUntilStageOneCompleted();
|
||||||
|
// save the received commitments as verification values
|
||||||
|
ArrayList<T> temp;
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
temp = parties[i].verifiableValues;
|
||||||
|
parties[i].verifiableValues = parties[i].commitments;
|
||||||
|
parties[i].commitments = temp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* stage2 according to the protocol
|
||||||
|
* Pj verifies all the shares,sharesT he received
|
||||||
|
* if check fails for an index i, Pj broadcasts a complaint against Pi.
|
||||||
|
* Pj broadcasts done message at the end of this stage
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void stage2() {
|
||||||
|
sdkg.broadcastComplaints();
|
||||||
|
//broadcast done message after all complaints
|
||||||
|
channel.broadcastMessage(createMessage(DKG.Payload.Type.DONE));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if all non-aborting qualified parties have sent commitments.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
protected boolean haveAllQualPartiesCommitted() {
|
||||||
|
for (int i : QUAL) {
|
||||||
|
if (parties[i - 1].aborted)
|
||||||
|
continue;
|
||||||
|
for (int k = 0; k <= t; k++) {
|
||||||
|
if (parties[i - 1].commitments.get(k) == null)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if all non-aborting qualified parties sent a done message
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
protected boolean areAllQualPartiesDone() {
|
||||||
|
for (int i : QUAL) {
|
||||||
|
if (parties[i - 1].aborted)
|
||||||
|
continue;
|
||||||
|
for (int k = 0; k <= t; k++) {
|
||||||
|
if (!parties[i - 1].ysDoneFlag)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if at least t + 1 secrets were received foreach i in QUAL that aborted
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
protected boolean haveReceivedEnoughSecretShares() {
|
||||||
|
for (int i : QUAL) {
|
||||||
|
if (parties[i - 1].aborted && parties[i - 1].recoverSharesSet.size() <= t)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* broadcast commitments and recover parties information if necessary
|
||||||
|
*/
|
||||||
|
private void resolveQualifyingPublicKey() {
|
||||||
|
sdkg.broadcastCommitments();
|
||||||
|
// wait until all parties in QUAL broadcast their commitments or aborted
|
||||||
|
while (!stop && !haveAllQualPartiesCommitted())
|
||||||
|
waitAndHandleReceivedMessages();
|
||||||
|
|
||||||
|
if (stop)
|
||||||
|
return;
|
||||||
|
|
||||||
|
sdkg.computeAndBroadcastComplaints(QUAL);
|
||||||
|
|
||||||
|
//broadcast done message after all complaints
|
||||||
|
channel.broadcastMessage(createMessage(DKG.Payload.Type.DONE));
|
||||||
|
|
||||||
|
// wait until all parties in QUAL done or aborted
|
||||||
|
while (!stop && !areAllQualPartiesDone())
|
||||||
|
waitAndHandleReceivedMessages();
|
||||||
|
|
||||||
|
if (stop)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// broadcast i private secret foreach i in QUAL that aborted
|
||||||
|
for (int i : QUAL) {
|
||||||
|
if (parties[i - 1].aborted) {
|
||||||
|
sdkg.broadcastAnswer(parties[i - 1].share, parties[i - 1].shareT, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// wait until at least t + 1 secrets will received foreach i in QUAL that aborted
|
||||||
|
while (!stop && !haveReceivedEnoughSecretShares())
|
||||||
|
waitAndHandleReceivedMessages();
|
||||||
|
|
||||||
|
if (stop)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Arithmetic<BigInteger> arithmetic = new Fp(sdkg.getQ());
|
||||||
|
// restore necessary information
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
if (parties[i].recoverSharesSet.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Polynomial.Point[] shares = new Polynomial.Point[t + 1];
|
||||||
|
int j = 0;
|
||||||
|
for (Polynomial.Point share : parties[i].recoverSharesSet) {
|
||||||
|
shares[j++] = share;
|
||||||
|
if (j >= shares.length) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Polynomial polynomial = SecretSharing.recoverPolynomial(shares, arithmetic);
|
||||||
|
BigInteger[] coefficients = polynomial.getCoefficients();
|
||||||
|
for (int k = 0; k <= t; k++) {
|
||||||
|
parties[i].commitments.add(k, group.multiply(g, coefficients[k]));
|
||||||
|
}
|
||||||
|
parties[i].share = new Polynomial.Point(BigInteger.valueOf(id), polynomial);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* notifies message handler and message handler that stage 4 was started
|
||||||
|
*/
|
||||||
|
protected void setStage4() {
|
||||||
|
isStage4 = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void stage4() {
|
||||||
|
setStage4();
|
||||||
|
resolveQualifyingPublicKey();
|
||||||
|
if (stop) return;
|
||||||
|
super.stage4();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* if !isStage4 as super, with extension to double secret message
|
||||||
|
* else answer message is valid if:
|
||||||
|
* 1. it was received in broadcast chanel
|
||||||
|
* 2. secret.j == sender
|
||||||
|
* 3. QUAL contains i and j
|
||||||
|
*/
|
||||||
|
protected boolean isValidAnswerMessage(int sender, boolean isBroadcast, DKG.ShareMessage doubleSecretMessage) {
|
||||||
|
if (!isStage4) {
|
||||||
|
return super.isValidAnswerMessage(sender, isBroadcast, doubleSecretMessage);
|
||||||
|
} else {
|
||||||
|
int i = doubleSecretMessage.getI();
|
||||||
|
int j = doubleSecretMessage.getJ();
|
||||||
|
return isBroadcast && j == sender && parties[i - 1].aborted && !parties[j - 1].aborted
|
||||||
|
&& QUAL.contains(i) && QUAL.contains(j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* as in super with respect to protocol stage
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected boolean isValidDoneMessage(int sender, boolean isBroadcast) {
|
||||||
|
if (!isStage4) {
|
||||||
|
return super.isValidDoneMessage(sender, isBroadcast);
|
||||||
|
} else {
|
||||||
|
return isBroadcast && !parties[sender - 1].ysDoneFlag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* use only in stage4
|
||||||
|
* complaint message is valid if:
|
||||||
|
* 1. it was received in broadcast chanel
|
||||||
|
* 2. secret.j == sender
|
||||||
|
* 3. QUAL contains i and j
|
||||||
|
*/
|
||||||
|
protected boolean isValidComplaintMessage(int sender, boolean isBroadcast,
|
||||||
|
DKG.ShareMessage complaintMessage) {
|
||||||
|
int i = complaintMessage.getI();
|
||||||
|
int j = complaintMessage.getJ();
|
||||||
|
return isBroadcast && j == sender && QUAL.contains(i) && QUAL.contains(j);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleMessage(Comm.BroadcastMessage envelope) throws InvalidProtocolBufferException {
|
||||||
|
int sender = envelope.getSender();
|
||||||
|
boolean isBroadcast = !envelope.getIsPrivate();
|
||||||
|
DKG.Payload msg = DKG.Payload.parseFrom(envelope.getPayload());
|
||||||
|
switch (msg.getType()) {
|
||||||
|
case SHARE:
|
||||||
|
/**
|
||||||
|
* as in super, with extension to double secret message
|
||||||
|
*/
|
||||||
|
DKG.ShareMessage doubleSecretMessage = msg.getShare();
|
||||||
|
if (isValidSecretMessage(sender, isBroadcast, doubleSecretMessage)) {
|
||||||
|
int i = doubleSecretMessage.getI();
|
||||||
|
synchronized (parties[i - 1]) {
|
||||||
|
parties[i - 1].share = extractShare(id, doubleSecretMessage.getShare());
|
||||||
|
parties[i - 1].shareT = extractShare(id, doubleSecretMessage.getShareT());
|
||||||
|
parties[i - 1].notify();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ANSWER:
|
||||||
|
/**
|
||||||
|
* if !isStage4 as super, with extension to double secret message
|
||||||
|
* else saves secret
|
||||||
|
*/
|
||||||
|
assert msg.getPayloadDataCase() == DKG.Payload.PayloadDataCase.SHARE;
|
||||||
|
doubleSecretMessage = msg.getShare();
|
||||||
|
if (isValidAnswerMessage(sender, isBroadcast, doubleSecretMessage)) {
|
||||||
|
int i = doubleSecretMessage.getI();
|
||||||
|
int j = doubleSecretMessage.getJ();
|
||||||
|
Polynomial.Point secret = extractShare(j, doubleSecretMessage.getShare());
|
||||||
|
Polynomial.Point secretT = extractShare(j, doubleSecretMessage.getShareT());
|
||||||
|
synchronized (parties[i - 1]) {
|
||||||
|
if (!isStage4) {
|
||||||
|
if (sdkg.isValidShare(secret, secretT, parties[j - 1].verifiableValues, i)) {
|
||||||
|
parties[i - 1].complaints[j - 1] = meerkat.crypto.dkg.feldman.Protocol.ComplaintState.NonDisqualified;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
parties[i - 1].complaints[j - 1] = meerkat.crypto.dkg.feldman.Protocol.ComplaintState.Disqualified;
|
||||||
|
}
|
||||||
|
if (j == id) {
|
||||||
|
parties[i - 1].share = secret;
|
||||||
|
parties[i - 1].shareT = secretT;
|
||||||
|
}
|
||||||
|
} else if (sdkg.isValidShare(secret, secretT, parties[i - 1].verifiableValues, j)) {
|
||||||
|
parties[i - 1].recoverSharesSet.add(secret);
|
||||||
|
}
|
||||||
|
parties[i - 1].notify();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DONE:
|
||||||
|
/**
|
||||||
|
* as in super with respect to protocol state
|
||||||
|
*/
|
||||||
|
if (!isStage4)
|
||||||
|
super.handleMessage(envelope);
|
||||||
|
else {
|
||||||
|
if (isValidDoneMessage(sender, isBroadcast)) {
|
||||||
|
synchronized (parties[sender - 1]) {
|
||||||
|
parties[sender - 1].ysDoneFlag = true;
|
||||||
|
parties[sender - 1].notify();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case COMPLAINT:
|
||||||
|
/**
|
||||||
|
* if !isStage4 as in super
|
||||||
|
* else if secret,secretT are valid with respect to verifiableValues but
|
||||||
|
* secret is not valid with respect to commitments then
|
||||||
|
* marks i as aborted
|
||||||
|
*/
|
||||||
|
if (!isStage4) {
|
||||||
|
super.handleMessage(envelope);
|
||||||
|
} else {
|
||||||
|
assert (msg.getPayloadDataCase() == DKG.Payload.PayloadDataCase.SHARE);
|
||||||
|
DKG.ShareMessage ysComplaintMessage = msg.getShare();
|
||||||
|
if (isValidComplaintMessage(sender, isBroadcast, ysComplaintMessage)) {
|
||||||
|
int i = ysComplaintMessage.getI();
|
||||||
|
int j = ysComplaintMessage.getJ();
|
||||||
|
Polynomial.Point secret = extractShare(i, ysComplaintMessage.getShare());
|
||||||
|
Polynomial.Point secretT = extractShare(i, ysComplaintMessage.getShareT());
|
||||||
|
if (sdkg.isValidShare(secret, secretT, parties[i - 1].verifiableValues, j)
|
||||||
|
&& !dkg.isValidShare(secret, parties[i - 1].commitments, j)) {
|
||||||
|
synchronized (parties[i - 1]) {
|
||||||
|
parties[i - 1].aborted = true;
|
||||||
|
parties[i - 1].notify();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
super.handleMessage(envelope);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,117 @@
|
||||||
|
package meerkat.crypto.secretsharing.feldman;
|
||||||
|
|
||||||
|
import meerkat.crypto.secretsharing.shamir.Polynomial;
|
||||||
|
import meerkat.crypto.secretsharing.shamir.SecretSharing;
|
||||||
|
import org.factcenter.qilin.primitives.Group;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Tzlil on 1/27/2016.
|
||||||
|
*
|
||||||
|
* an implementation of Feldman's verifiable secret sharing scheme.
|
||||||
|
*
|
||||||
|
* allows trusted dealer to share a key x among n parties.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class VerifiableSecretSharing<T> extends SecretSharing {
|
||||||
|
/**
|
||||||
|
* cyclic group contains g.
|
||||||
|
*/
|
||||||
|
protected final Group<T> group;
|
||||||
|
/**
|
||||||
|
* a generator of cyclic group of order q.
|
||||||
|
* the generated group is a subgroup of the given group.
|
||||||
|
* it must be chosen such that computing discrete logarithms is hard in this group.
|
||||||
|
*/
|
||||||
|
protected final T g;
|
||||||
|
/**
|
||||||
|
* commitments to polynomial coefficients.
|
||||||
|
* commitments[k] = g ^ coefficients[k] (group operation)
|
||||||
|
*/
|
||||||
|
protected final ArrayList<T> commitmentsArrayList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* constructor
|
||||||
|
* @param q a large prime.
|
||||||
|
* @param t threshold. Any t+1 share holders can recover the secret,
|
||||||
|
* but any set of at most t share holders cannot
|
||||||
|
* @param n number of share holders
|
||||||
|
* @param zi secret, chosen from Zq
|
||||||
|
* @param random use for generate random polynomial
|
||||||
|
* @param group
|
||||||
|
* @param q a large prime dividing group order.
|
||||||
|
* @param g a generator of cyclic group of order q.
|
||||||
|
* the generated group is a subgroup of the given group.
|
||||||
|
* it must be chosen such that computing discrete logarithms is hard in this group.
|
||||||
|
*/
|
||||||
|
public VerifiableSecretSharing(int t, int n, BigInteger zi, Random random, BigInteger q, T g
|
||||||
|
, Group<T> group) {
|
||||||
|
super(t, n, zi, random,q);
|
||||||
|
this.g = g;
|
||||||
|
this.group = group;
|
||||||
|
assert (this.group.contains(g));
|
||||||
|
this.commitmentsArrayList = generateCommitments();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* commitments[i] = g ^ polynomial.coefficients[i]
|
||||||
|
* @return commitments
|
||||||
|
*/
|
||||||
|
private ArrayList<T> generateCommitments() {
|
||||||
|
|
||||||
|
Polynomial polynomial = getPolynomial();
|
||||||
|
BigInteger[] coefficients = polynomial.getCoefficients();
|
||||||
|
ArrayList<T> commitments = new ArrayList<T>(t + 1);
|
||||||
|
for (int i = 0 ; i <= t;i++){
|
||||||
|
commitments.add(i,group.multiply(g,coefficients[i]));
|
||||||
|
}
|
||||||
|
return commitments;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute verification value (g^{share value}) using coefficient commitments sent by dealer and my share id.
|
||||||
|
* @param j my share holder id
|
||||||
|
* @param commitments commitments to polynomial coefficients of share (received from dealer)
|
||||||
|
* @param group
|
||||||
|
*
|
||||||
|
* @return product of Aik ^ (j ^ k) == g ^ polynomial(i)
|
||||||
|
*/
|
||||||
|
public static <T> T computeVerificationValue(int j, ArrayList<T> commitments, Group<T> group) {
|
||||||
|
T v = group.zero();
|
||||||
|
BigInteger power = BigInteger.ONE;
|
||||||
|
BigInteger J = BigInteger.valueOf(j);
|
||||||
|
for (int k = 0 ; k < commitments.size() ; k ++){
|
||||||
|
v = group.add(v,group.multiply(commitments.get(k),power));
|
||||||
|
power = power.multiply(J);
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getter
|
||||||
|
* @return generator of group
|
||||||
|
*/
|
||||||
|
public T getGenerator() {
|
||||||
|
return g;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getter
|
||||||
|
* @return group
|
||||||
|
*/
|
||||||
|
public Group<T> getGroup(){
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getter
|
||||||
|
* @return commitmentsArrayList
|
||||||
|
*/
|
||||||
|
public ArrayList<T> getCommitmentsArrayList() {
|
||||||
|
return commitmentsArrayList;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
package meerkat.crypto.secretsharing.shamir;
|
||||||
|
|
||||||
|
import meerkat.crypto.utils.Arithmetic;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Tzlil on 1/28/2016.
|
||||||
|
*
|
||||||
|
* container of lagrange polynomial
|
||||||
|
*
|
||||||
|
* Constructor is private (use {@link #lagrangePolynomials(Polynomial.Point[], Arithmetic)} to construct)
|
||||||
|
*
|
||||||
|
* l = (evaluate/divisor)* polynomial
|
||||||
|
*
|
||||||
|
* Note : image and divisor stored separately for avoiding lose of information by division
|
||||||
|
*/
|
||||||
|
class LagrangePolynomial{
|
||||||
|
public final Polynomial polynomial;
|
||||||
|
public final BigInteger image;
|
||||||
|
public final BigInteger divisor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* inner constructor, stores all given parameters
|
||||||
|
* @param polynomial
|
||||||
|
* @param image
|
||||||
|
* @param divisor
|
||||||
|
*/
|
||||||
|
private LagrangePolynomial(Polynomial polynomial, BigInteger image, BigInteger divisor) {
|
||||||
|
this.polynomial = polynomial;
|
||||||
|
this.image = image;
|
||||||
|
this.divisor = divisor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* static method
|
||||||
|
* @param points array points s.t there are no couple of points that shares the same x value
|
||||||
|
*
|
||||||
|
* @return the lagrange polynomials that mach to given points.
|
||||||
|
* in case there exists i != j s.t points[i].x == points[j].x returns null.
|
||||||
|
*/
|
||||||
|
public static LagrangePolynomial[] lagrangePolynomials(Polynomial.Point[] points,Arithmetic<BigInteger> arithmetic) {
|
||||||
|
Polynomial one = new Polynomial(new BigInteger[]{BigInteger.ONE},arithmetic);
|
||||||
|
LagrangePolynomial[] lagrangePolynomials = new LagrangePolynomial[points.length];
|
||||||
|
Polynomial[] factors = new Polynomial[points.length];
|
||||||
|
for (int i = 0 ; i < factors.length ; i++){
|
||||||
|
factors[i] = new Polynomial(new BigInteger[]{points[i].x.negate(),BigInteger.ONE},arithmetic); // X - Xi
|
||||||
|
}
|
||||||
|
Polynomial product;
|
||||||
|
BigInteger divisor;
|
||||||
|
for(int i = 0; i < points.length; i ++) {
|
||||||
|
product = one;
|
||||||
|
divisor = BigInteger.ONE;
|
||||||
|
for (int j = 0; j < points.length; j++) {
|
||||||
|
if (i != j) {
|
||||||
|
divisor = arithmetic.mul(divisor,arithmetic.sub(points[i].x,points[j].x));
|
||||||
|
product = product.mul(factors[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(divisor.equals(BigInteger.ZERO))
|
||||||
|
return null;
|
||||||
|
lagrangePolynomials[i] = new LagrangePolynomial(product,points[i].y,divisor);
|
||||||
|
}
|
||||||
|
return lagrangePolynomials;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,208 @@
|
||||||
|
package meerkat.crypto.secretsharing.shamir;
|
||||||
|
|
||||||
|
import meerkat.crypto.utils.Arithmetic;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Tzlil on 1/27/2016.
|
||||||
|
*/
|
||||||
|
public class Polynomial implements Comparable<Polynomial> {
|
||||||
|
private final int degree;
|
||||||
|
private final BigInteger[] coefficients;
|
||||||
|
private final Arithmetic<BigInteger> arithmetic;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* constructor
|
||||||
|
* @param coefficients
|
||||||
|
* @param arithmetic
|
||||||
|
* degree set as max index such that coefficients[degree] not equals zero
|
||||||
|
*/
|
||||||
|
public Polynomial(BigInteger[] coefficients,Arithmetic<BigInteger> arithmetic) {
|
||||||
|
int d = coefficients.length - 1;
|
||||||
|
while (d > 0 && coefficients[d].equals(BigInteger.ZERO)){
|
||||||
|
d--;
|
||||||
|
}
|
||||||
|
this.degree = d;
|
||||||
|
this.coefficients = coefficients;
|
||||||
|
this.arithmetic = arithmetic;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compare to another polynomial (order by degree, then coefficients).
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public int compareTo(Polynomial other) {
|
||||||
|
if (this.degree != other.degree)
|
||||||
|
return this.degree - other.degree;
|
||||||
|
int compare;
|
||||||
|
for (int i = degree; i >= degree ; i--){
|
||||||
|
compare = this.coefficients[i].compareTo(other.coefficients[i]);
|
||||||
|
if (compare != 0){
|
||||||
|
return compare;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param x
|
||||||
|
* @return sum of coefficients[i] * (x ^ i)
|
||||||
|
*/
|
||||||
|
public BigInteger evaluate(BigInteger x){
|
||||||
|
BigInteger result = BigInteger.ZERO;
|
||||||
|
BigInteger power = BigInteger.ONE;
|
||||||
|
for(int i = 0 ; i <= degree ; i++){
|
||||||
|
result = arithmetic.add(result,arithmetic.mul(coefficients[i],power));
|
||||||
|
power = power.multiply(x);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param points
|
||||||
|
* @return polynomial of minimal degree which goes through all points.
|
||||||
|
* If there exists i != j s.t points[i].x == points[j].x, method returns null.
|
||||||
|
*/
|
||||||
|
public static Polynomial interpolation(Point[] points, Arithmetic<BigInteger> arithmetic) {
|
||||||
|
LagrangePolynomial[] l = LagrangePolynomial.lagrangePolynomials(points,arithmetic);
|
||||||
|
if (l == null){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// product = product of l[i].divisor
|
||||||
|
BigInteger product = BigInteger.ONE;
|
||||||
|
for (int i = 0; i < l.length;i++){
|
||||||
|
product = arithmetic.mul(product,l[i].divisor);
|
||||||
|
}
|
||||||
|
|
||||||
|
// factor[i] = product divided by l[i].divisor = product of l[j].divisor s.t j!=i
|
||||||
|
BigInteger[] factors = new BigInteger[l.length];
|
||||||
|
for (int i = 0; i < l.length;i++){
|
||||||
|
factors[i] = arithmetic.div(product,l[i].divisor);
|
||||||
|
}
|
||||||
|
int degree = l[0].polynomial.degree;
|
||||||
|
|
||||||
|
// coefficients[j] = (sum of l[i].evaluate * factor[i] * l[i].coefficients[j] s.t i!=j) divide by product =
|
||||||
|
// = sum of l[i].evaluate * l[i].coefficients[j] / l[i].divisor s.t i!=j
|
||||||
|
BigInteger[] coefficients = new BigInteger[degree + 1];
|
||||||
|
for (int j = 0; j < coefficients.length;j++){
|
||||||
|
coefficients[j] = BigInteger.ZERO;
|
||||||
|
for (int i = 0; i < l.length; i++){
|
||||||
|
BigInteger current = arithmetic.mul(l[i].image,factors[i]);
|
||||||
|
current = arithmetic.mul(current,l[i].polynomial.coefficients[j]);
|
||||||
|
coefficients[j] = arithmetic.add(coefficients[j],current);
|
||||||
|
}
|
||||||
|
coefficients[j] = arithmetic.div(coefficients[j],product);
|
||||||
|
}
|
||||||
|
return new Polynomial(coefficients,arithmetic);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param other
|
||||||
|
* @return new Polynomial of degree max(this degree,other degree) s.t for all x
|
||||||
|
* new.evaluate(x) = this.evaluate(x) + other.evaluate(x)
|
||||||
|
*/
|
||||||
|
public Polynomial add(Polynomial other){
|
||||||
|
Polynomial bigger,smaller;
|
||||||
|
if(this.degree < other.degree){
|
||||||
|
bigger = other;
|
||||||
|
smaller = this;
|
||||||
|
}else{
|
||||||
|
bigger = this;
|
||||||
|
smaller = other;
|
||||||
|
}
|
||||||
|
BigInteger[] coefficients = bigger.getCoefficients();
|
||||||
|
|
||||||
|
for (int i = 0; i <= smaller.degree ; i++){
|
||||||
|
coefficients[i] = arithmetic.add(smaller.coefficients[i],bigger.coefficients[i]);
|
||||||
|
}
|
||||||
|
return new Polynomial(coefficients,other.arithmetic);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param constant
|
||||||
|
* @return new Polynomial of degree this.degree s.t for all x
|
||||||
|
* new.evaluate(x) = constant * this.evaluate(x)
|
||||||
|
*/
|
||||||
|
public Polynomial mul(BigInteger constant){
|
||||||
|
|
||||||
|
BigInteger[] coefficients = this.getCoefficients();
|
||||||
|
|
||||||
|
for (int i = 0; i <= this.degree ; i++){
|
||||||
|
coefficients[i] = arithmetic.mul(constant,coefficients[i]);
|
||||||
|
}
|
||||||
|
return new Polynomial(coefficients,arithmetic);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param other
|
||||||
|
* @return new Polynomial of degree this degree + other degree + 1 s.t for all x
|
||||||
|
* new.evaluate(x) = this.evaluate(x) * other.evaluate(x)
|
||||||
|
*/
|
||||||
|
public Polynomial mul(Polynomial other){
|
||||||
|
|
||||||
|
BigInteger[] coefficients = new BigInteger[this.degree + other.degree + 1];
|
||||||
|
Arrays.fill(coefficients,BigInteger.ZERO);
|
||||||
|
|
||||||
|
for (int i = 0; i <= this.degree ; i++){
|
||||||
|
for (int j = 0; j <= other.degree; j++){
|
||||||
|
coefficients[i+j] = arithmetic.add(coefficients[i+j],arithmetic.mul(this.coefficients[i],other.coefficients[j]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new Polynomial(coefficients,arithmetic);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** getter
|
||||||
|
* @return copy of coefficients
|
||||||
|
*/
|
||||||
|
public BigInteger[] getCoefficients() {
|
||||||
|
return Arrays.copyOf(coefficients,coefficients.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** getter
|
||||||
|
* @return degree
|
||||||
|
*/
|
||||||
|
public int getDegree() {
|
||||||
|
return degree;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* inner class
|
||||||
|
* container for (x,y) x from range and y from evaluate of polynomial
|
||||||
|
*/
|
||||||
|
public static class Point implements java.io.Serializable {
|
||||||
|
public final BigInteger x;
|
||||||
|
public final BigInteger y;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* constructor
|
||||||
|
* @param x
|
||||||
|
* @param polynomial y = polynomial.evaluate(x)
|
||||||
|
*/
|
||||||
|
public Point(BigInteger x, Polynomial polynomial) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = polynomial.evaluate(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* constructor
|
||||||
|
* @param x
|
||||||
|
* @param y
|
||||||
|
*/
|
||||||
|
public Point(BigInteger x,BigInteger y) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if(!super.equals(obj))
|
||||||
|
return false;
|
||||||
|
Point other = (Point)obj;
|
||||||
|
return this.x.equals(other.x) && this.y.equals(other.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,123 @@
|
||||||
|
package meerkat.crypto.secretsharing.shamir;
|
||||||
|
|
||||||
|
import meerkat.crypto.utils.Arithmetic;
|
||||||
|
import meerkat.crypto.utils.concrete.Fp;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Tzlil on 1/27/2016.
|
||||||
|
* an implementation of Shamire's secret sharing scheme
|
||||||
|
*/
|
||||||
|
public class SecretSharing{
|
||||||
|
/**
|
||||||
|
* threshold
|
||||||
|
*/
|
||||||
|
protected final int t;
|
||||||
|
/**
|
||||||
|
* number of shares
|
||||||
|
*/
|
||||||
|
protected final int n;
|
||||||
|
/**
|
||||||
|
* a large prime
|
||||||
|
*/
|
||||||
|
protected final BigInteger q;
|
||||||
|
/**
|
||||||
|
* random polynomial of degree s.t polynomial.evaluate(0) = secret
|
||||||
|
*/
|
||||||
|
protected final Polynomial polynomial;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* constructor
|
||||||
|
* @param q a large prime.
|
||||||
|
* @param t threshold. Any t+1 share holders can recover the secret,
|
||||||
|
* but any set of at most t share holders cannot
|
||||||
|
* @param n number of share holders
|
||||||
|
* @param zi secret, chosen from Zq
|
||||||
|
* @param random use for generate random polynomial
|
||||||
|
*/
|
||||||
|
public SecretSharing(int t, int n, BigInteger zi, Random random, BigInteger q) {
|
||||||
|
this.q = q;
|
||||||
|
this.t = t;
|
||||||
|
this.n = n;
|
||||||
|
this.polynomial = generateRandomPolynomial(zi,random);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param x
|
||||||
|
* @param random
|
||||||
|
* @return new Polynomial polynomial of degree t ,such that
|
||||||
|
* 1. polynomial(0) = x
|
||||||
|
* 2. polynomial coefficients randomly chosen from Zq (except of coefficients[0] = x)
|
||||||
|
*/
|
||||||
|
private Polynomial generateRandomPolynomial(BigInteger x, Random random) {
|
||||||
|
BigInteger[] coefficients = new BigInteger[t + 1];
|
||||||
|
coefficients[0] = x.mod(q);
|
||||||
|
int bits = q.bitLength();
|
||||||
|
for (int i = 1 ; i <= t; i++ ){
|
||||||
|
coefficients[i] = new BigInteger(bits,random).mod(q);
|
||||||
|
}
|
||||||
|
return new Polynomial(coefficients,new Fp(q));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param i in range of [1,...n]
|
||||||
|
*
|
||||||
|
* @return polynomial.evaluate(i)
|
||||||
|
*/
|
||||||
|
public Polynomial.Point getShare(int i){
|
||||||
|
assert (i > 0 && i <= n);
|
||||||
|
return new Polynomial.Point(BigInteger.valueOf(i), polynomial);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param shares - subset of the original shares
|
||||||
|
*
|
||||||
|
* @return evaluate of interpolation(shares) at x = 0
|
||||||
|
*/
|
||||||
|
public static BigInteger recoverSecret(Polynomial.Point[] shares, Arithmetic<BigInteger> arithmetic) {
|
||||||
|
return recoverPolynomial(shares,arithmetic).evaluate(BigInteger.ZERO);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @param shares - subset of the original shares
|
||||||
|
*
|
||||||
|
* @return interpolation(shares)
|
||||||
|
*/
|
||||||
|
public static Polynomial recoverPolynomial(Polynomial.Point[] shares, Arithmetic<BigInteger> arithmetic) {
|
||||||
|
return Polynomial.interpolation(shares,arithmetic);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getter
|
||||||
|
* @return threshold
|
||||||
|
*/
|
||||||
|
public int getT() {
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getter
|
||||||
|
* @return number of share holders
|
||||||
|
*/
|
||||||
|
public int getN() {
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getter
|
||||||
|
* @return the prime was given in the constructor
|
||||||
|
*/
|
||||||
|
public BigInteger getQ() {
|
||||||
|
return q;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getter
|
||||||
|
* @return the polynomial was generated in constructor
|
||||||
|
*/
|
||||||
|
public Polynomial getPolynomial() {
|
||||||
|
return polynomial;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
package meerkat.crypto.utils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Tzlil on 3/17/2016.
|
||||||
|
* defines the properties of the traditional operations : add,sub,mul,div
|
||||||
|
* between two objects of type T
|
||||||
|
*/
|
||||||
|
public interface Arithmetic<T> {
|
||||||
|
/**
|
||||||
|
* addition
|
||||||
|
* @param a
|
||||||
|
* @param b
|
||||||
|
* @return a + b
|
||||||
|
*/
|
||||||
|
T add(T a, T b);
|
||||||
|
/**
|
||||||
|
* subtraction
|
||||||
|
* @param a
|
||||||
|
* @param b
|
||||||
|
* @return a - b
|
||||||
|
*/
|
||||||
|
T sub(T a, T b);
|
||||||
|
/**
|
||||||
|
* multiplication
|
||||||
|
* @param a
|
||||||
|
* @param b
|
||||||
|
* @return a * b
|
||||||
|
*/
|
||||||
|
T mul(T a, T b);
|
||||||
|
/**
|
||||||
|
* division
|
||||||
|
* @param a
|
||||||
|
* @param b
|
||||||
|
* @return a / b
|
||||||
|
*/
|
||||||
|
T div(T a, T b);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
package meerkat.crypto.utils.concrete;
|
||||||
|
|
||||||
|
import meerkat.crypto.utils.Arithmetic;
|
||||||
|
import org.factcenter.qilin.primitives.concrete.Zpstar;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Tzlil on 3/17/2016.
|
||||||
|
* an implementation of Arithmetic<BigInteger> over prime fields: integers modulo p
|
||||||
|
*/
|
||||||
|
public class Fp implements Arithmetic<BigInteger> {
|
||||||
|
public final BigInteger p;
|
||||||
|
private final Zpstar zp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* constructor
|
||||||
|
* @param p prime
|
||||||
|
*/
|
||||||
|
public Fp(BigInteger p) {
|
||||||
|
this.p = p;
|
||||||
|
this.zp = new Zpstar(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BigInteger add(BigInteger a, BigInteger b){
|
||||||
|
return a.add(b).mod(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BigInteger sub(BigInteger a, BigInteger b){
|
||||||
|
return a.add(p).subtract(b).mod(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BigInteger mul(BigInteger a, BigInteger b){
|
||||||
|
return zp.add(a,b);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BigInteger div(BigInteger a, BigInteger b){
|
||||||
|
return mul(a,zp.negate(b));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package meerkat;
|
||||||
|
|
||||||
|
option java_package = "meerkat.protobuf";
|
||||||
|
|
||||||
|
message Payload {
|
||||||
|
enum Type {
|
||||||
|
SHARE = 0;
|
||||||
|
COMMITMENT = 1;
|
||||||
|
COMPLAINT = 2;
|
||||||
|
DONE = 3;
|
||||||
|
ANSWER = 4;
|
||||||
|
YCOMMITMENT = 5;
|
||||||
|
YCOMPLAINT = 6;
|
||||||
|
YANSWER = 7;
|
||||||
|
ABORT = 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Type of message in protocol
|
||||||
|
Type type = 1;
|
||||||
|
|
||||||
|
oneof payload_data {
|
||||||
|
IDMessage id = 5;
|
||||||
|
ShareMessage share = 6;
|
||||||
|
CommitmentMessage commitment = 7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message IDMessage {
|
||||||
|
int32 id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ShareMessage {
|
||||||
|
int32 i = 1;
|
||||||
|
int32 j = 2;
|
||||||
|
bytes share = 3;
|
||||||
|
// For double shares (used in GJKR protocol)
|
||||||
|
bytes share_t = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message CommitmentMessage {
|
||||||
|
int32 k = 1;
|
||||||
|
bytes commitment = 2;
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
package meerkat.crypto.dkg.feldman;
|
||||||
|
|
||||||
|
import meerkat.comm.Channel;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Tzlil on 3/21/2016.
|
||||||
|
*/
|
||||||
|
public class DKGMaliciousUser<T> extends User<T> {
|
||||||
|
|
||||||
|
private final Protocol<T> maliciousDkg;
|
||||||
|
private final Set<Integer> falls;
|
||||||
|
public DKGMaliciousUser(Protocol<T> dkg, Protocol<T> maliciousDKG, Channel channel, Set<Integer> falls) {
|
||||||
|
super(dkg, channel);
|
||||||
|
this.falls = falls;
|
||||||
|
this.maliciousDkg = maliciousDKG;
|
||||||
|
maliciousDKG.setParties(parties);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Set<Integer> selectFallsRandomly(Set<Integer> ids, Random random){
|
||||||
|
Set<Integer> falls = new HashSet<Integer>();
|
||||||
|
ArrayList<Integer> idsList = new ArrayList<Integer>();
|
||||||
|
for (int id : ids){
|
||||||
|
idsList.add(id);
|
||||||
|
}
|
||||||
|
int fallsSize = random.nextInt(idsList.size()) + 1;// 1 - (n-1)
|
||||||
|
while (falls.size() < fallsSize){
|
||||||
|
falls.add(idsList.remove(random.nextInt(idsList.size())));
|
||||||
|
}
|
||||||
|
return falls;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Protocol<T> generateMaliciousDKG(Protocol<T> dkg,Channel channel,Random random){
|
||||||
|
BigInteger q = dkg.getQ();
|
||||||
|
BigInteger zi = new BigInteger(q.bitLength(), random).mod(q);
|
||||||
|
Protocol<T> malicious = new Protocol<T>(dkg.getT(),dkg.getN(),zi,random,dkg.getQ()
|
||||||
|
,dkg.getGenerator(),dkg.getGroup(),dkg.getId(),dkg.getEncoder());
|
||||||
|
malicious.setChannel(channel);
|
||||||
|
return malicious;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stage1() {
|
||||||
|
dkg.broadcastCommitments();
|
||||||
|
sendSecrets(); //insteadof crypto.sendSecrets(channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stage3() {
|
||||||
|
maliciousDkg.answerAllComplainingPlayers();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stage4(){
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendSecrets(){
|
||||||
|
for (int j = 1; j <= n ; j++){
|
||||||
|
if(j != id){
|
||||||
|
if(falls.contains(j)){
|
||||||
|
maliciousDkg.sendSecret(j);
|
||||||
|
}else {
|
||||||
|
dkg.sendSecret(j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,170 @@
|
||||||
|
package meerkat.crypto.dkg.feldman;
|
||||||
|
|
||||||
|
import meerkat.comm.ChannelImpl;
|
||||||
|
import meerkat.crypto.utils.Arithmetic;
|
||||||
|
import meerkat.crypto.utils.concrete.Fp;
|
||||||
|
import meerkat.comm.Channel;
|
||||||
|
import meerkat.crypto.secretsharing.feldman.VerifiableSecretSharing;
|
||||||
|
import meerkat.crypto.secretsharing.shamir.Polynomial;
|
||||||
|
import meerkat.crypto.secretsharing.shamir.SecretSharing;
|
||||||
|
import meerkat.crypto.utils.BigIntegerByteEncoder;
|
||||||
|
import meerkat.crypto.utils.GenerateRandomPrime;
|
||||||
|
import org.factcenter.qilin.primitives.Group;
|
||||||
|
import org.factcenter.qilin.primitives.concrete.Zpstar;
|
||||||
|
import org.factcenter.qilin.util.ByteEncoder;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Tzlil on 3/21/2016.
|
||||||
|
*/
|
||||||
|
public class DKGTest {
|
||||||
|
|
||||||
|
int tests = 10;
|
||||||
|
BigInteger p = GenerateRandomPrime.SafePrime100Bits;
|
||||||
|
BigInteger q = p.subtract(BigInteger.ONE).divide(BigInteger.valueOf(2));
|
||||||
|
Group<BigInteger> group = new Zpstar(p);
|
||||||
|
Arithmetic<BigInteger> arithmetic = new Fp(q);
|
||||||
|
int t = 9;
|
||||||
|
int n = 20;
|
||||||
|
|
||||||
|
public void oneTest(Testable testable) throws Exception {
|
||||||
|
for (int i = 0; i < testable.threads.length ; i++){
|
||||||
|
testable.threads[i].start();
|
||||||
|
}
|
||||||
|
for (int i = 0; i < testable.threads.length ; i++){
|
||||||
|
testable.threads[i].join();
|
||||||
|
}
|
||||||
|
|
||||||
|
// got the right public value
|
||||||
|
BigInteger publicValue = group.multiply(testable.g,testable.secret);
|
||||||
|
for (int i: testable.valids){
|
||||||
|
assert (testable.dkgs[i - 1].getPublicValue().equals(publicValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
// assert valid verification values
|
||||||
|
BigInteger expected,verification;
|
||||||
|
for (int i: testable.valids){
|
||||||
|
expected = group.multiply(testable.g, testable.dkgs[i - 1].getShare().y);
|
||||||
|
verification = VerifiableSecretSharing.computeVerificationValue(i, testable.dkgs[i - 1].getCommitments(), group);
|
||||||
|
assert (expected.equals(verification));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// restore the secret from shares
|
||||||
|
ArrayList<Polynomial.Point> sharesList = new ArrayList<Polynomial.Point>();
|
||||||
|
|
||||||
|
for (int i: testable.valids){
|
||||||
|
sharesList.add(testable.dkgs[i - 1].getShare());
|
||||||
|
}
|
||||||
|
Polynomial.Point[] shares = new Polynomial.Point[sharesList.size()];
|
||||||
|
for (int i = 0; i < shares.length; i ++){
|
||||||
|
shares[i] = sharesList.get(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
BigInteger calculatedSecret = SecretSharing.recoverSecret(shares,arithmetic);
|
||||||
|
assert (calculatedSecret.equals(testable.secret));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test() throws Exception {
|
||||||
|
Testable testable;
|
||||||
|
for (int i = 0; i < tests; i++){
|
||||||
|
testable = new Testable(new Random());
|
||||||
|
oneTest(testable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Testable{
|
||||||
|
Set<Integer> valids;
|
||||||
|
Set<Integer> QUAL;
|
||||||
|
Set<Integer> aborted;
|
||||||
|
Set<Integer> malicious;
|
||||||
|
User<BigInteger>[] dkgs;
|
||||||
|
Thread[] threads;
|
||||||
|
BigInteger g;
|
||||||
|
BigInteger secret;
|
||||||
|
|
||||||
|
public Testable(Random random) {
|
||||||
|
this.dkgs = new User[n];
|
||||||
|
this.valids = new HashSet<Integer>();
|
||||||
|
this.QUAL = new HashSet<Integer>();
|
||||||
|
this.aborted = new HashSet<Integer>();
|
||||||
|
this.malicious = new HashSet<Integer>();
|
||||||
|
this.threads = new Thread[n];
|
||||||
|
this.g = sampleGenerator(random);
|
||||||
|
ArrayList<Integer> ids = new ArrayList<Integer>();
|
||||||
|
for (int id = 1; id<= n ; id++){
|
||||||
|
ids.add(id);
|
||||||
|
}
|
||||||
|
int id;
|
||||||
|
BigInteger s;
|
||||||
|
Protocol<BigInteger> dkg;
|
||||||
|
this.secret = BigInteger.ZERO;
|
||||||
|
ChannelImpl channels = new ChannelImpl();
|
||||||
|
ByteEncoder<BigInteger> byteEncoder = new BigIntegerByteEncoder();
|
||||||
|
while (!ids.isEmpty()) {
|
||||||
|
id = ids.remove(random.nextInt(ids.size()));
|
||||||
|
Channel channel = channels.getChannel(id);
|
||||||
|
s = randomIntModQ(random);
|
||||||
|
dkg = new meerkat.crypto.dkg.feldman.Protocol<BigInteger>(t, n, s, random, q, g, group, id,byteEncoder);
|
||||||
|
dkgs[id - 1] = randomDKGUser(id,channel,dkg,random);
|
||||||
|
threads[id - 1] = new Thread(dkgs[id - 1]);
|
||||||
|
if(QUAL.contains(id)){
|
||||||
|
this.secret = this.secret.add(s).mod(q);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public User<BigInteger> randomDKGUser(int id, Channel channel, Protocol<BigInteger> dkg, Random random){
|
||||||
|
if (QUAL.size() <= t) {
|
||||||
|
valids.add(id);
|
||||||
|
QUAL.add(id);
|
||||||
|
return new User<BigInteger>(dkg,channel);
|
||||||
|
}else{
|
||||||
|
int type = random.nextInt(3);
|
||||||
|
switch (type){
|
||||||
|
case 0:// regular
|
||||||
|
valids.add(id);
|
||||||
|
QUAL.add(id);
|
||||||
|
return new User<BigInteger>(dkg,channel);
|
||||||
|
case 1:// abort
|
||||||
|
int abortStage = random.nextInt(2) + 1; // 1 or 2
|
||||||
|
aborted.add(id);
|
||||||
|
if (abortStage == 2){
|
||||||
|
QUAL.add(id);
|
||||||
|
}
|
||||||
|
return new DKGUserImplAbort(dkg,channel,abortStage);
|
||||||
|
case 2:// malicious
|
||||||
|
malicious.add(id);
|
||||||
|
Set<Integer> falls = DKGMaliciousUser.selectFallsRandomly(valids,random);
|
||||||
|
Protocol<BigInteger> maliciousDKG = DKGMaliciousUser.generateMaliciousDKG(dkg,channel,random);
|
||||||
|
return new DKGMaliciousUser(dkg,maliciousDKG,channel,falls);
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public BigInteger sampleGenerator(Random random){
|
||||||
|
BigInteger ZERO = group.zero();
|
||||||
|
BigInteger g;
|
||||||
|
do {
|
||||||
|
g = group.sample(random);
|
||||||
|
} while (!g.equals(ZERO) && !group.multiply(g, q).equals(ZERO));
|
||||||
|
return g;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BigInteger randomIntModQ(Random random){
|
||||||
|
return new BigInteger(q.bitLength(), random).mod(q);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
package meerkat.crypto.dkg.feldman;
|
||||||
|
|
||||||
|
import meerkat.comm.Channel;
|
||||||
|
import meerkat.protobuf.DKG;
|
||||||
|
|
||||||
|
import static meerkat.crypto.dkg.comm.MessageUtils.createMessage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Tzlil on 3/14/2016.
|
||||||
|
*/
|
||||||
|
public class DKGUserImplAbort<T> extends User<T> {
|
||||||
|
|
||||||
|
final int abortStage;
|
||||||
|
int stage;
|
||||||
|
public DKGUserImplAbort(Protocol<T> dkg, Channel channel, int abortStage) {
|
||||||
|
super(dkg, channel);
|
||||||
|
this.abortStage = abortStage;// 1 - 2
|
||||||
|
this.stage = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void sendAbort(){
|
||||||
|
channel.broadcastMessage(createMessage(DKG.Payload.Type.ABORT));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void stage1() {
|
||||||
|
if(stage < abortStage)
|
||||||
|
super.stage1();
|
||||||
|
else if(stage == abortStage){
|
||||||
|
sendAbort();
|
||||||
|
}
|
||||||
|
stage++;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void stage2() {
|
||||||
|
if(stage < abortStage)
|
||||||
|
super.stage2();
|
||||||
|
else if(stage == abortStage){
|
||||||
|
sendAbort();
|
||||||
|
}
|
||||||
|
stage++;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void stage3() {
|
||||||
|
if(stage < abortStage)
|
||||||
|
super.stage3();
|
||||||
|
else if(stage == abortStage){
|
||||||
|
sendAbort();
|
||||||
|
}
|
||||||
|
stage++;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void stage4() {
|
||||||
|
if(stage < abortStage)
|
||||||
|
super.stage4();
|
||||||
|
else if(stage == abortStage){
|
||||||
|
sendAbort();
|
||||||
|
}
|
||||||
|
stage++;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
package meerkat.crypto.dkg.gjkr;
|
||||||
|
|
||||||
|
import meerkat.comm.Channel;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Tzlil on 3/29/2016.
|
||||||
|
*/
|
||||||
|
public class SDKGMaliciousUserImpl<T> extends User<T> {
|
||||||
|
|
||||||
|
private final Protocol<T> maliciousSDKG;
|
||||||
|
private final Set<Integer> falls;
|
||||||
|
public SDKGMaliciousUserImpl(Protocol<T> sdkg, Protocol<T> maliciousSDKG
|
||||||
|
, Channel channel, Set<Integer> falls) {
|
||||||
|
super(sdkg, channel);
|
||||||
|
this.falls = falls;
|
||||||
|
this.maliciousSDKG = maliciousSDKG;
|
||||||
|
maliciousSDKG.setParties(parties);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static<T> Protocol<T> generateMaliciousSDKG(Protocol<T> sdkg,Channel channel,Random random){
|
||||||
|
BigInteger q = sdkg.getQ();
|
||||||
|
BigInteger zi = new BigInteger(q.bitLength(), random).mod(q);
|
||||||
|
Protocol<T> malicious = new Protocol<T>(sdkg.getT(),sdkg.getN(),zi,random,sdkg.getQ()
|
||||||
|
,sdkg.getGenerator(),sdkg.getH(),sdkg.getGroup(),sdkg.getId(),sdkg.getEncoder());
|
||||||
|
malicious.setChannel(channel);
|
||||||
|
return malicious;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stage1() {
|
||||||
|
sdkg.computeAndBroadcastVerificationValues();
|
||||||
|
sendSecrets(); //insteadof crypto.sendSecrets(channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stage3() {
|
||||||
|
maliciousSDKG.answerAllComplainingPlayers();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stage4(){
|
||||||
|
//do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendSecrets(){
|
||||||
|
for (int j = 1; j <= n ; j++){
|
||||||
|
if(j != id){
|
||||||
|
if(falls.contains(j)){
|
||||||
|
maliciousSDKG.sendSecret(j);
|
||||||
|
}else {
|
||||||
|
sdkg.sendSecret(j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,206 @@
|
||||||
|
package meerkat.crypto.dkg.gjkr;
|
||||||
|
|
||||||
|
import meerkat.comm.ChannelImpl;
|
||||||
|
import meerkat.crypto.utils.Arithmetic;
|
||||||
|
import meerkat.crypto.utils.concrete.Fp;
|
||||||
|
import meerkat.comm.Channel;
|
||||||
|
import meerkat.crypto.secretsharing.feldman.VerifiableSecretSharing;
|
||||||
|
import meerkat.crypto.dkg.feldman.DKGMaliciousUser;
|
||||||
|
import meerkat.crypto.secretsharing.shamir.Polynomial;
|
||||||
|
import meerkat.crypto.secretsharing.shamir.SecretSharing;
|
||||||
|
import meerkat.crypto.utils.BigIntegerByteEncoder;
|
||||||
|
import meerkat.crypto.utils.GenerateRandomPrime;
|
||||||
|
import org.factcenter.qilin.primitives.Group;
|
||||||
|
import org.factcenter.qilin.primitives.concrete.Zpstar;
|
||||||
|
import org.factcenter.qilin.util.ByteEncoder;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.internal.runners.statements.Fail;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Tzlil on 3/29/2016.
|
||||||
|
* TODO: Separate into multiple tests,
|
||||||
|
* TODO: Make tests deterministic (using constant seed for random generator)
|
||||||
|
*/
|
||||||
|
public class SDKGTest {
|
||||||
|
|
||||||
|
private ExecutorService executorService = Executors.newCachedThreadPool();
|
||||||
|
|
||||||
|
final static int NUM_TESTS = 10;
|
||||||
|
BigInteger p = GenerateRandomPrime.SafePrime100Bits;
|
||||||
|
BigInteger q = p.subtract(BigInteger.ONE).divide(BigInteger.valueOf(2));
|
||||||
|
Group<BigInteger> group = new Zpstar(p);
|
||||||
|
Arithmetic<BigInteger> arithmetic = new Fp(q);
|
||||||
|
int t = 1;
|
||||||
|
int n = 20;
|
||||||
|
Random rand = new Random(1);
|
||||||
|
|
||||||
|
public void oneTest(Testable testable) throws Exception {
|
||||||
|
for (int i = 0; i < testable.sdkgs.length ; i++){
|
||||||
|
testable.futures[i] = executorService.submit(testable.sdkgs[i]);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < testable.futures.length ; i++){
|
||||||
|
testable.futures[i].get();
|
||||||
|
}
|
||||||
|
|
||||||
|
// got the right public value
|
||||||
|
BigInteger publicValue = group.multiply(testable.g,testable.secret);
|
||||||
|
for (int i: testable.valids){
|
||||||
|
assert (testable.sdkgs[i - 1].getPublicValue().equals(publicValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
// assert valid verification values
|
||||||
|
BigInteger expected,verification;
|
||||||
|
for (int i: testable.valids){
|
||||||
|
expected = group.multiply(testable.g, testable.sdkgs[i - 1].getShare().y);
|
||||||
|
verification = VerifiableSecretSharing.computeVerificationValue(i, testable.sdkgs[i - 1].getCommitments(), group);
|
||||||
|
assert (expected.equals(verification));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// restore the secret from shares
|
||||||
|
ArrayList<Polynomial.Point> sharesList = new ArrayList<Polynomial.Point>();
|
||||||
|
|
||||||
|
for (int i: testable.valids){
|
||||||
|
sharesList.add(testable.sdkgs[i - 1].getShare());
|
||||||
|
}
|
||||||
|
Polynomial.Point[] shares = new Polynomial.Point[sharesList.size()];
|
||||||
|
for (int i = 0; i < shares.length; i ++){
|
||||||
|
shares[i] = sharesList.get(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
BigInteger calculatedSecret = SecretSharing.recoverSecret(shares,arithmetic);
|
||||||
|
assert (calculatedSecret.equals(testable.secret));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test() throws Exception {
|
||||||
|
Testable testable;
|
||||||
|
for (int i = 0; i < NUM_TESTS; i++) {
|
||||||
|
testable = new Testable(n, t, group, q, rand);
|
||||||
|
oneTest(testable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class Testable {
|
||||||
|
Set<Integer> valids;
|
||||||
|
Set<Integer> QUAL;
|
||||||
|
Set<Integer> aborted;
|
||||||
|
Set<Integer> malicious;
|
||||||
|
User<BigInteger>[] sdkgs;
|
||||||
|
Future<?>[] futures;
|
||||||
|
BigInteger g;
|
||||||
|
BigInteger h;
|
||||||
|
BigInteger secret;
|
||||||
|
Group<BigInteger> group;
|
||||||
|
int n;
|
||||||
|
int t;
|
||||||
|
BigInteger q;
|
||||||
|
Random random;
|
||||||
|
ChannelImpl channels = new ChannelImpl();
|
||||||
|
|
||||||
|
public Testable(int n, int t, Group<BigInteger> group, BigInteger q, Random random) {
|
||||||
|
this.n = n;
|
||||||
|
this.t = t;
|
||||||
|
this.group = group;
|
||||||
|
this.q = q;
|
||||||
|
this.random = random;
|
||||||
|
this.sdkgs = new User[n];
|
||||||
|
this.valids = new HashSet<Integer>();
|
||||||
|
this.QUAL = new HashSet<Integer>();
|
||||||
|
this.aborted = new HashSet<Integer>();
|
||||||
|
this.malicious = new HashSet<Integer>();
|
||||||
|
this.futures = new Future[n];
|
||||||
|
this.g = sampleGenerator(random);
|
||||||
|
this.h = group.multiply(g,randomIntModQ(random));
|
||||||
|
ArrayList<Integer> ids = new ArrayList<Integer>();
|
||||||
|
for (int id = 1; id<= n ; id++){
|
||||||
|
ids.add(id);
|
||||||
|
}
|
||||||
|
int id;
|
||||||
|
BigInteger s;
|
||||||
|
Channel channel;
|
||||||
|
Protocol<BigInteger> sdkg;
|
||||||
|
this.secret = BigInteger.ZERO;
|
||||||
|
ByteEncoder<BigInteger> encoder = new BigIntegerByteEncoder();
|
||||||
|
while (!ids.isEmpty()) {
|
||||||
|
id = ids.remove(random.nextInt(ids.size()));
|
||||||
|
s = randomIntModQ(random);
|
||||||
|
channel = channels.getChannel(id);
|
||||||
|
sdkg = new Protocol<BigInteger>(t, n, s, random, q, g , h, group, id,encoder);
|
||||||
|
sdkgs[id - 1] = randomSDKGUser(id,channel,sdkg);
|
||||||
|
if(QUAL.contains(id)){
|
||||||
|
this.secret = this.secret.add(s).mod(q);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
enum UserType {
|
||||||
|
HONEST,
|
||||||
|
FAILSTOP,
|
||||||
|
MALICIOUS,
|
||||||
|
}
|
||||||
|
|
||||||
|
public User<BigInteger> newSDKGUser(int id, Channel channel, Protocol<BigInteger> sdkg, UserType userType) {
|
||||||
|
switch(userType) {
|
||||||
|
case HONEST:
|
||||||
|
valids.add(id);
|
||||||
|
QUAL.add(id);
|
||||||
|
return new User<BigInteger>(sdkg,channel);
|
||||||
|
|
||||||
|
case FAILSTOP:
|
||||||
|
int abortStage = random.nextInt(3) + 1; // 1 or 2 or 3
|
||||||
|
aborted.add(id);
|
||||||
|
if (abortStage > 1){
|
||||||
|
QUAL.add(id);
|
||||||
|
}
|
||||||
|
return new SDKGUserImplAbort(sdkg,channel,abortStage);
|
||||||
|
|
||||||
|
case MALICIOUS:
|
||||||
|
malicious.add(id);
|
||||||
|
Set<Integer> falls = DKGMaliciousUser.selectFallsRandomly(valids,random);
|
||||||
|
Protocol<BigInteger> maliciousSDKG = SDKGMaliciousUserImpl.generateMaliciousSDKG(sdkg,channel,random);
|
||||||
|
return new SDKGMaliciousUserImpl(sdkg,maliciousSDKG,channel,falls);
|
||||||
|
|
||||||
|
}
|
||||||
|
fail("Unknown user type");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public User<BigInteger> randomSDKGUser(int id, Channel channel, Protocol<BigInteger> sdkg){
|
||||||
|
if (QUAL.size() <= t) {
|
||||||
|
return newSDKGUser(id, channel, sdkg, UserType.HONEST);
|
||||||
|
} else {
|
||||||
|
UserType type = UserType.values()[random.nextInt(UserType.values().length)];
|
||||||
|
return newSDKGUser(id, channel, sdkg, type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public BigInteger sampleGenerator(Random random){
|
||||||
|
BigInteger ZERO = group.zero();
|
||||||
|
BigInteger g;
|
||||||
|
do {
|
||||||
|
g = group.sample(random);
|
||||||
|
} while (!g.equals(ZERO) && !group.multiply(g, q).equals(ZERO));
|
||||||
|
return g;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BigInteger randomIntModQ(Random random){
|
||||||
|
return new BigInteger(q.bitLength(), random).mod(q);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,80 @@
|
||||||
|
package meerkat.crypto.dkg.gjkr;
|
||||||
|
|
||||||
|
import com.google.protobuf.InvalidProtocolBufferException;
|
||||||
|
import meerkat.crypto.dkg.comm.MessageUtils;
|
||||||
|
import meerkat.comm.Channel;
|
||||||
|
import meerkat.protobuf.Comm;
|
||||||
|
import meerkat.protobuf.DKG;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Tzlil on 3/14/2016.
|
||||||
|
*/
|
||||||
|
public class SDKGUserImplAbort<T> extends User<T> {
|
||||||
|
|
||||||
|
public static class AbortException extends RuntimeException {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
final int abortStage;
|
||||||
|
int stage;
|
||||||
|
public SDKGUserImplAbort(Protocol<T> sdkg, Channel channel, int abortStage) {
|
||||||
|
super(sdkg, channel);
|
||||||
|
this.abortStage = abortStage;// 1 - 4
|
||||||
|
this.stage = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void abort(){
|
||||||
|
//stopReceiver();
|
||||||
|
channel.broadcastMessage(MessageUtils.createMessage(DKG.Payload.Type.ABORT));
|
||||||
|
throw new AbortException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void stage1() {
|
||||||
|
if(stage < abortStage)
|
||||||
|
super.stage1();
|
||||||
|
else if(stage == abortStage){
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
stage++;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void stage2() {
|
||||||
|
if(stage < abortStage)
|
||||||
|
super.stage2();
|
||||||
|
else if(stage == abortStage){
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
stage++;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void stage3() {
|
||||||
|
if(stage < abortStage)
|
||||||
|
super.stage3();
|
||||||
|
else if(stage == abortStage){
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
stage++;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void stage4() {
|
||||||
|
if(stage < abortStage)
|
||||||
|
super.stage4();
|
||||||
|
else if(stage == abortStage){
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
stage++;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
super.run();
|
||||||
|
} catch (AbortException e) {
|
||||||
|
// Expected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
package meerkat.crypto.secretsharing.feldman;
|
||||||
|
|
||||||
|
import meerkat.crypto.secretsharing.shamir.Polynomial;
|
||||||
|
import org.factcenter.qilin.primitives.Group;
|
||||||
|
import org.factcenter.qilin.primitives.concrete.Zpstar;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Tzlil on 1/29/2016.
|
||||||
|
*/
|
||||||
|
public class VerifiableSecretSharingTest {
|
||||||
|
|
||||||
|
|
||||||
|
VerifiableSecretSharing[] verifiableSecretSharingArray;
|
||||||
|
int tests = 1 << 10;
|
||||||
|
Random random;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void settings(){
|
||||||
|
BigInteger p = BigInteger.valueOf(2903);
|
||||||
|
BigInteger q = p.subtract(BigInteger.ONE).divide(BigInteger.valueOf(2));
|
||||||
|
Zpstar zpstar = new Zpstar(p);
|
||||||
|
random = new Random();
|
||||||
|
BigInteger g;
|
||||||
|
BigInteger ZERO = zpstar.zero();
|
||||||
|
do{
|
||||||
|
g = zpstar.sample(random);
|
||||||
|
}while (!g.equals(ZERO) && !zpstar.multiply(g,q).equals(ZERO));// sample from QRZp*
|
||||||
|
int t = 8;
|
||||||
|
int n = 20;
|
||||||
|
verifiableSecretSharingArray = new VerifiableSecretSharing[tests];
|
||||||
|
for (int i = 0; i < verifiableSecretSharingArray.length; i++){
|
||||||
|
verifiableSecretSharingArray[i] = new VerifiableSecretSharing(t,n
|
||||||
|
,new BigInteger(q.bitLength(),random).mod(q),random,q,g,zpstar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void oneTest(VerifiableSecretSharing<BigInteger> verifiableSecretSharing) throws Exception {
|
||||||
|
int n = verifiableSecretSharing.getN();
|
||||||
|
Group<BigInteger> zpstar = verifiableSecretSharing.getGroup();
|
||||||
|
BigInteger g = verifiableSecretSharing.getGenerator();
|
||||||
|
Polynomial.Point[] shares = new Polynomial.Point[n];
|
||||||
|
ArrayList<BigInteger> commitments = verifiableSecretSharing.getCommitmentsArrayList();
|
||||||
|
BigInteger[] verifications = new BigInteger[n];
|
||||||
|
for (int i = 1 ; i <= shares.length; i ++){
|
||||||
|
shares[i - 1] = verifiableSecretSharing.getShare(i);
|
||||||
|
verifications[i - 1] = VerifiableSecretSharing.computeVerificationValue(i,commitments,zpstar);
|
||||||
|
}
|
||||||
|
BigInteger expected;
|
||||||
|
for (int i = 0 ; i < shares.length ; i++){
|
||||||
|
expected = zpstar.multiply(g,shares[i].y);
|
||||||
|
assert (expected.equals(verifications[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void secretSharingTest() throws Exception {
|
||||||
|
for (int i = 0 ; i < verifiableSecretSharingArray.length; i ++){
|
||||||
|
oneTest(verifiableSecretSharingArray[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
package meerkat.crypto.secretsharing.shamir.PolynomialTests;
|
||||||
|
import meerkat.crypto.utils.GenerateRandomPolynomial;
|
||||||
|
import meerkat.crypto.utils.Z;
|
||||||
|
import meerkat.crypto.secretsharing.shamir.Polynomial;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Tzlil on 1/27/2016.
|
||||||
|
*/
|
||||||
|
public class AddTest {
|
||||||
|
|
||||||
|
Polynomial[] arr1;
|
||||||
|
Polynomial[] arr2;
|
||||||
|
int tests = 1 << 12;
|
||||||
|
int maxDegree = 15;
|
||||||
|
int bits = 128;
|
||||||
|
Random random;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void settings(){
|
||||||
|
random = new Random();
|
||||||
|
arr1 = new Polynomial[tests];
|
||||||
|
arr2 = new Polynomial[tests];
|
||||||
|
for (int i = 0; i < arr1.length; i++){
|
||||||
|
arr1[i] = GenerateRandomPolynomial.generateRandomPolynomial(random.nextInt(maxDegree),bits,random,new Z());
|
||||||
|
arr2[i] = GenerateRandomPolynomial.generateRandomPolynomial(random.nextInt(maxDegree),bits,random,new Z());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void oneTest(Polynomial p1, Polynomial p2){
|
||||||
|
Polynomial sum = p1.add(p2);
|
||||||
|
BigInteger x = new BigInteger(bits,random);
|
||||||
|
assert(sum.evaluate(x).equals(p1.evaluate(x).add(p2.evaluate(x))));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void addTest(){
|
||||||
|
for (int i = 0 ; i < arr1.length; i ++){
|
||||||
|
oneTest(arr1[i],arr2[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
package meerkat.crypto.secretsharing.shamir.PolynomialTests;
|
||||||
|
|
||||||
|
import meerkat.crypto.secretsharing.shamir.Polynomial;
|
||||||
|
import meerkat.crypto.utils.Arithmetic;
|
||||||
|
import meerkat.crypto.utils.concrete.Fp;
|
||||||
|
import meerkat.crypto.utils.GenerateRandomPolynomial;
|
||||||
|
import meerkat.crypto.utils.GenerateRandomPrime;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Tzlil on 1/27/2016.
|
||||||
|
*/
|
||||||
|
public class InterpolationTest {
|
||||||
|
Polynomial[] polynomials;
|
||||||
|
int tests = 1 << 10;
|
||||||
|
int maxDegree = 15;
|
||||||
|
int bits = 128;
|
||||||
|
Random random;
|
||||||
|
Polynomial.Point[][] pointsArrays;
|
||||||
|
Arithmetic<BigInteger> arithmetic;
|
||||||
|
BigInteger p = GenerateRandomPrime.SafePrime100Bits;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void settings(){
|
||||||
|
random = new Random();
|
||||||
|
polynomials = new Polynomial[tests];
|
||||||
|
pointsArrays = new Polynomial.Point[tests][];
|
||||||
|
arithmetic = new Fp(p);
|
||||||
|
for (int i = 0; i < polynomials.length; i++){
|
||||||
|
polynomials[i] = GenerateRandomPolynomial.generateRandomPolynomial(random.nextInt(maxDegree),bits,random,p);
|
||||||
|
pointsArrays[i] = randomPoints(polynomials[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Polynomial.Point[] randomPoints(Polynomial polynomial){
|
||||||
|
Polynomial.Point[] points = new Polynomial.Point[polynomial.getDegree() + 1];
|
||||||
|
BigInteger x;
|
||||||
|
Set<BigInteger> set = new HashSet();
|
||||||
|
for (int i = 0; i < points.length; i++){
|
||||||
|
x = new BigInteger(bits,random).mod(p);
|
||||||
|
if(set.contains(x)){
|
||||||
|
i--;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
set.add(x);
|
||||||
|
points[i] = new Polynomial.Point(x,polynomial);
|
||||||
|
}
|
||||||
|
return points;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void oneTest(Polynomial p, Polynomial.Point[] points) throws Exception {
|
||||||
|
Polynomial interpolation = Polynomial.interpolation(points,arithmetic);
|
||||||
|
assert (p.compareTo(interpolation) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void interpolationTest() throws Exception {
|
||||||
|
for (int i = 0; i < polynomials.length; i ++){
|
||||||
|
oneTest(polynomials[i],pointsArrays[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
package meerkat.crypto.secretsharing.shamir.PolynomialTests;
|
||||||
|
|
||||||
|
import meerkat.crypto.utils.GenerateRandomPolynomial;
|
||||||
|
import meerkat.crypto.utils.Z;
|
||||||
|
import meerkat.crypto.secretsharing.shamir.Polynomial;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Tzlil on 1/27/2016.
|
||||||
|
*/
|
||||||
|
public class MulByConstTest {
|
||||||
|
|
||||||
|
|
||||||
|
Polynomial[] arr1;
|
||||||
|
BigInteger[] arr2;
|
||||||
|
int tests = 1 << 12;
|
||||||
|
int maxDegree = 15;
|
||||||
|
int bits = 128;
|
||||||
|
Random random;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void settings(){
|
||||||
|
random = new Random();
|
||||||
|
arr1 = new Polynomial[tests];
|
||||||
|
arr2 = new BigInteger[tests];
|
||||||
|
for (int i = 0; i < arr1.length; i++){
|
||||||
|
arr1[i] = GenerateRandomPolynomial.generateRandomPolynomial(random.nextInt(maxDegree),bits,random,new Z());
|
||||||
|
arr2[i] = new BigInteger(bits,random);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void oneTest(Polynomial p, BigInteger c){
|
||||||
|
Polynomial product = p.mul(c);
|
||||||
|
BigInteger x = new BigInteger(bits,random);
|
||||||
|
assert(product.evaluate(x).equals(p.evaluate(x).multiply(c)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void mulByConstTest(){
|
||||||
|
for (int i = 0 ; i < arr1.length; i ++){
|
||||||
|
oneTest(arr1[i],arr2[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
package meerkat.crypto.secretsharing.shamir.PolynomialTests;
|
||||||
|
|
||||||
|
import meerkat.crypto.utils.GenerateRandomPolynomial;
|
||||||
|
import meerkat.crypto.utils.Z;
|
||||||
|
import meerkat.crypto.secretsharing.shamir.Polynomial;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Tzlil on 1/27/2016.
|
||||||
|
*/
|
||||||
|
public class MulTest {
|
||||||
|
|
||||||
|
|
||||||
|
Polynomial[] arr1;
|
||||||
|
Polynomial[] arr2;
|
||||||
|
int tests = 1 << 12;
|
||||||
|
int maxDegree = 15;
|
||||||
|
int bits = 128;
|
||||||
|
Random random;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void settings(){
|
||||||
|
random = new Random();
|
||||||
|
arr1 = new Polynomial[tests];
|
||||||
|
arr2 = new Polynomial[tests];
|
||||||
|
for (int i = 0; i < arr1.length; i++){
|
||||||
|
arr1[i] = GenerateRandomPolynomial.generateRandomPolynomial(random.nextInt(maxDegree),bits,random,new Z());
|
||||||
|
arr2[i] = GenerateRandomPolynomial.generateRandomPolynomial(random.nextInt(maxDegree),bits,random,new Z());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void oneTest(Polynomial p1, Polynomial p2){
|
||||||
|
Polynomial product = p1.mul(p2);
|
||||||
|
BigInteger x = new BigInteger(bits,random);
|
||||||
|
assert(product.evaluate(x).equals(p1.evaluate(x).multiply(p2.evaluate(x))));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void mulTest(){
|
||||||
|
for (int i = 0 ; i < arr1.length; i ++){
|
||||||
|
oneTest(arr1[i],arr2[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
package meerkat.crypto.secretsharing.shamir;
|
||||||
|
|
||||||
|
import meerkat.crypto.utils.concrete.Fp;
|
||||||
|
import meerkat.crypto.utils.GenerateRandomPrime;
|
||||||
|
import org.factcenter.qilin.primitives.CyclicGroup;
|
||||||
|
import org.factcenter.qilin.primitives.concrete.Zn;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Tzlil on 1/29/2016.
|
||||||
|
*/
|
||||||
|
public class SecretSharingTest {
|
||||||
|
|
||||||
|
SecretSharing[] secretSharingArray;
|
||||||
|
BigInteger[] secrets;
|
||||||
|
CyclicGroup<BigInteger> group;
|
||||||
|
int tests = 1 << 10;
|
||||||
|
Random random;
|
||||||
|
BigInteger p = GenerateRandomPrime.SafePrime100Bits;
|
||||||
|
BigInteger q = p.subtract(BigInteger.ONE).divide(BigInteger.valueOf(2));
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void settings(){
|
||||||
|
group = new Zn(q);
|
||||||
|
int t = 9;
|
||||||
|
int n = 20;
|
||||||
|
random = new Random();
|
||||||
|
secretSharingArray = new SecretSharing[tests];
|
||||||
|
secrets = new BigInteger[tests];
|
||||||
|
|
||||||
|
for (int i = 0; i < secretSharingArray.length; i++){
|
||||||
|
secrets[i] = group.sample(random);
|
||||||
|
secretSharingArray[i] = new SecretSharing(t,n,secrets[i],random,q);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void oneTest(SecretSharing secretSharing, BigInteger secret) throws Exception {
|
||||||
|
int t = secretSharing.getT();
|
||||||
|
int n = secretSharing.getN();
|
||||||
|
Polynomial.Point[] shares = new Polynomial.Point[t + 1];
|
||||||
|
List<Integer> indexes = new ArrayList<Integer>(n);
|
||||||
|
for (int i = 1 ; i <= n; i ++){
|
||||||
|
indexes.add(i);
|
||||||
|
}
|
||||||
|
for (int i = 0 ; i < shares.length ; i++){
|
||||||
|
shares[i] = secretSharing.getShare(indexes.remove(random.nextInt(indexes.size())));
|
||||||
|
}
|
||||||
|
BigInteger calculated = SecretSharing.recoverSecret(shares,new Fp(q));
|
||||||
|
assert (secret.equals(calculated));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void secretSharingTest() throws Exception {
|
||||||
|
for (int i = 0 ; i < secretSharingArray.length; i ++){
|
||||||
|
oneTest(secretSharingArray[i],secrets[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
package meerkat.crypto.utils;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Tzlil on 4/7/2016.
|
||||||
|
*/
|
||||||
|
public class BigIntegerByteEncoder implements org.factcenter.qilin.util.ByteEncoder<BigInteger> {
|
||||||
|
@Override
|
||||||
|
public byte[] encode(BigInteger input) {
|
||||||
|
return input.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BigInteger decode(byte[] input) {
|
||||||
|
return new BigInteger(1,input);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMinLength() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BigInteger denseDecode(byte[] input) {
|
||||||
|
return decode(input);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
package meerkat.crypto.utils;
|
||||||
|
|
||||||
|
import meerkat.crypto.secretsharing.shamir.Polynomial;
|
||||||
|
import meerkat.crypto.utils.concrete.Fp;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Tzlil on 1/27/2016.
|
||||||
|
*/
|
||||||
|
public class GenerateRandomPolynomial {
|
||||||
|
|
||||||
|
public static Polynomial generateRandomPolynomial(int degree, int bits, Random random, Arithmetic<BigInteger> arithmetic) {
|
||||||
|
BigInteger[] coefficients = new BigInteger[degree + 1];
|
||||||
|
|
||||||
|
for (int i = 0 ; i <= degree; i++ ){
|
||||||
|
coefficients[i] = new BigInteger(bits,random); // sample from Zp [0,... q-1]
|
||||||
|
}
|
||||||
|
return new Polynomial(coefficients,arithmetic);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Polynomial generateRandomPolynomial(int degree,int bits,Random random,BigInteger p) {
|
||||||
|
BigInteger[] coefficients = generateRandomPolynomial(degree,bits,random,new Fp(p)).getCoefficients();
|
||||||
|
for (int i = 0; i<coefficients.length;i++){
|
||||||
|
coefficients[i] = coefficients[i].mod(p);
|
||||||
|
}
|
||||||
|
return new Polynomial(coefficients,new Fp(p));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
package meerkat.crypto.utils;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Tzlil on 3/28/2016.
|
||||||
|
*/
|
||||||
|
public class GenerateRandomPrime {
|
||||||
|
private final static int Certainty = 10000;
|
||||||
|
public final static BigInteger SafePrime100Bits = new BigInteger("146407324427772525685319783363");
|
||||||
|
|
||||||
|
public static BigInteger generateRandomPrime(int bits, Random random) {
|
||||||
|
BigInteger p;
|
||||||
|
do {
|
||||||
|
p = new BigInteger(bits, random);
|
||||||
|
} while (!p.isProbablePrime(Certainty));
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BigInteger generateRandomSafePrime(int bits, Random random) {
|
||||||
|
BigInteger p;
|
||||||
|
BigInteger q;
|
||||||
|
do {
|
||||||
|
p = generateRandomPrime(bits, random);
|
||||||
|
q = p.subtract(BigInteger.ONE).divide(BigInteger.valueOf(2));
|
||||||
|
} while (!q.isProbablePrime(Certainty));
|
||||||
|
return q;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
package meerkat.crypto.utils;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Tzlil on 4/8/2016.
|
||||||
|
*/
|
||||||
|
public class Z implements Arithmetic<BigInteger> {
|
||||||
|
@Override
|
||||||
|
public BigInteger add(BigInteger a, BigInteger b) {
|
||||||
|
return a.add(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BigInteger sub(BigInteger a, BigInteger b) {
|
||||||
|
return a.subtract(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BigInteger mul(BigInteger a, BigInteger b) {
|
||||||
|
return a.multiply(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BigInteger div(BigInteger a, BigInteger b) {
|
||||||
|
return a.divide(b);
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,6 +13,7 @@ public interface AsyncBulletinBoardClient extends BulletinBoardClient {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Post a message to the bulletin board in an asynchronous manner
|
* Post a message to the bulletin board in an asynchronous manner
|
||||||
|
* The message may be broken up by the client into a batch message, depending on implementation
|
||||||
* @param msg is the message to be posted
|
* @param msg is the message to be posted
|
||||||
* @param callback is a class containing methods to handle the result of the operation
|
* @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
|
* @return a unique message ID for the message, that can be later used to retrieve the batch
|
||||||
|
@ -51,30 +52,30 @@ public interface AsyncBulletinBoardClient extends BulletinBoardClient {
|
||||||
* It does not close the batch
|
* It does not close the batch
|
||||||
* @param signerId is the canonical form for the ID of the sender of this batch
|
* @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 batchId is a unique (per signer) ID for this batch
|
||||||
* @param batchDataList is the (canonically ordered) list of data comprising the portion of the batch to be posted
|
* @param batchChunkList is the (canonically ordered) list of data comprising the portion of the batch to be posted
|
||||||
* @param startPosition is the location (in the batch) of the first entry in batchDataList
|
* @param startPosition is the location (in the batch) of the first entry in batchDataList
|
||||||
* (optionally used to continue interrupted post operations)
|
* (optionally used to continue interrupted post operations)
|
||||||
* The first position in the batch is position 0
|
* The first position in the batch is position 0
|
||||||
* @param callback is a callback function class for handling results of the operation
|
* @param callback is a callback function class for handling results of the operation
|
||||||
*/
|
*/
|
||||||
public void postBatchData(byte[] signerId, int batchId, List<BatchData> batchDataList,
|
public void postBatchData(byte[] signerId, int batchId, List<BatchChunk> batchChunkList,
|
||||||
int startPosition, FutureCallback<Boolean> callback);
|
int startPosition, FutureCallback<Boolean> callback);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Overloading of the postBatchData method which starts at the first position in the batch
|
* Overloading of the postBatchData method which starts at the first position in the batch
|
||||||
*/
|
*/
|
||||||
public void postBatchData(byte[] signerId, int batchId, List<BatchData> batchDataList, FutureCallback<Boolean> callback);
|
public void postBatchData(byte[] signerId, int batchId, List<BatchChunk> batchChunkList, FutureCallback<Boolean> callback);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Overloading of the postBatchData method which uses ByteString
|
* Overloading of the postBatchData method which uses ByteString
|
||||||
*/
|
*/
|
||||||
public void postBatchData(ByteString signerId, int batchId, List<BatchData> batchDataList,
|
public void postBatchData(ByteString signerId, int batchId, List<BatchChunk> batchChunkList,
|
||||||
int startPosition, FutureCallback<Boolean> callback);
|
int startPosition, FutureCallback<Boolean> callback);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Overloading of the postBatchData method which uses ByteString and starts at the first position in the batch
|
* Overloading of the postBatchData method which uses ByteString and starts at the first position in the batch
|
||||||
*/
|
*/
|
||||||
public void postBatchData(ByteString signerId, int batchId, List<BatchData> batchDataList, FutureCallback<Boolean> callback);
|
public void postBatchData(ByteString signerId, int batchId, List<BatchChunk> batchChunkList, FutureCallback<Boolean> callback);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempts to close a batch message
|
* Attempts to close a batch message
|
||||||
|
@ -96,6 +97,7 @@ public interface AsyncBulletinBoardClient extends BulletinBoardClient {
|
||||||
* Note that if messages haven't been "fully posted", this might return a different
|
* 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
|
* set of messages in different calls. However, messages that are fully posted
|
||||||
* are guaranteed to be included.
|
* are guaranteed to be included.
|
||||||
|
* Also: batch messages are returned as stubs.
|
||||||
* @param filterList return only messages that match the filters (null means no filtering).
|
* @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
|
* @param callback is a callback function class for handling results of the operation
|
||||||
*/
|
*/
|
||||||
|
@ -103,10 +105,10 @@ public interface AsyncBulletinBoardClient extends BulletinBoardClient {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read a given batch message from the bulletin board
|
* Read a given batch message from the bulletin board
|
||||||
* @param batchSpecificationMessage contains the data required to specify a single batch instance
|
* @param msgID is the batch message ID to be read
|
||||||
* @param callback is a callback class for handling the result of the operation
|
* @param callback is a callback class for handling the result of the operation
|
||||||
*/
|
*/
|
||||||
public void readBatch(BatchSpecificationMessage batchSpecificationMessage, FutureCallback<BulletinBoardMessage> callback);
|
public void readBatch(MessageID msgID, FutureCallback<BulletinBoardMessage> callback);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,34 +0,0 @@
|
||||||
package meerkat.bulletinboard;
|
|
||||||
|
|
||||||
import meerkat.crypto.DigitalSignature;
|
|
||||||
import meerkat.protobuf.BulletinBoardAPI.BeginBatchMessage;
|
|
||||||
import meerkat.protobuf.BulletinBoardAPI.BatchData;
|
|
||||||
import meerkat.protobuf.Crypto.Signature;
|
|
||||||
|
|
||||||
import java.security.InvalidKeyException;
|
|
||||||
import java.security.SignatureException;
|
|
||||||
import java.security.cert.CertificateException;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by Arbel Deutsch Peled on 20-Dec-15.
|
|
||||||
* Extends the DigitalSignature interface with methods for signing and authenticating Batch messages
|
|
||||||
*/
|
|
||||||
public interface BatchDigitalSignature extends DigitalSignature {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Appends the batch data to the signed content (ignoring the signature)
|
|
||||||
* @param completeBatch contains all the data about the batch
|
|
||||||
* @throws SignatureException
|
|
||||||
*/
|
|
||||||
public void updateContent(CompleteBatch completeBatch) throws SignatureException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Performs a complete verification process on the given batch message
|
|
||||||
* @param completeBatch contains the batch data as well as the signature
|
|
||||||
* @return TRUE if the batch is verified and FALSE otherwise
|
|
||||||
* @throws SignatureException | SignatureException | InvalidKeyException when underlying methods do so
|
|
||||||
*/
|
|
||||||
public boolean verify(CompleteBatch completeBatch) throws SignatureException, CertificateException, InvalidKeyException;
|
|
||||||
|
|
||||||
}
|
|
|
@ -5,7 +5,6 @@ import meerkat.protobuf.Voting.*;
|
||||||
|
|
||||||
import static meerkat.protobuf.BulletinBoardAPI.*;
|
import static meerkat.protobuf.BulletinBoardAPI.*;
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -21,20 +20,13 @@ public interface BulletinBoardClient {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Post a message to the bulletin board in a synchronous manner
|
* Post a message to the bulletin board in a synchronous manner
|
||||||
|
* The message may be broken up by the client into a batch message depending on implementation
|
||||||
* @param msg is the message to be posted
|
* @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
|
* @return a unique message ID for the message, that can be later used to retrieve the batch
|
||||||
* @throws CommunicationException
|
* @throws CommunicationException
|
||||||
*/
|
*/
|
||||||
MessageID postMessage(BulletinBoardMessage msg) throws CommunicationException;
|
MessageID postMessage(BulletinBoardMessage msg) throws CommunicationException;
|
||||||
|
|
||||||
/**
|
|
||||||
* Perform an end-to-end post of a signed batch message
|
|
||||||
* @param completeBatch contains all the data of the batch including the meta-data and the signature
|
|
||||||
* @return a unique identifier for the batch message
|
|
||||||
* @throws CommunicationException
|
|
||||||
*/
|
|
||||||
public MessageID postBatch(CompleteBatch completeBatch) throws CommunicationException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check how "safe" a given message is in a synchronous manner
|
* Check how "safe" a given message is in a synchronous manner
|
||||||
* @param id is the unique message identifier for retrieval
|
* @param id is the unique message identifier for retrieval
|
||||||
|
@ -47,6 +39,7 @@ public interface BulletinBoardClient {
|
||||||
* Note that if messages haven't been "fully posted", this might return a different
|
* 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
|
* set of messages in different calls. However, messages that are fully posted
|
||||||
* are guaranteed to be included.
|
* are guaranteed to be included.
|
||||||
|
* Also: batch messages are returned as stubs.
|
||||||
* @param filterList return only messages that match the filters (null means no filtering)
|
* @param filterList return only messages that match the filters (null means no filtering)
|
||||||
* @return the list of messages
|
* @return the list of messages
|
||||||
*/
|
*/
|
||||||
|
@ -54,11 +47,11 @@ public interface BulletinBoardClient {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read a given batch message from the bulletin board
|
* Read a given batch message from the bulletin board
|
||||||
* @param batchSpecificationMessage contains the data required to specify a single batch instance
|
* @param msgID is the batch message ID to be read
|
||||||
* @return the complete batch
|
* @return the complete batch
|
||||||
* @throws CommunicationException
|
* @throws CommunicationException
|
||||||
*/
|
*/
|
||||||
CompleteBatch readBatch(BatchSpecificationMessage batchSpecificationMessage) throws CommunicationException;
|
BulletinBoardMessage readBatch(MessageID msgID) throws CommunicationException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a SyncQuery to test against that corresponds with the current server state for a specific filter list
|
* Create a SyncQuery to test against that corresponds with the current server state for a specific filter list
|
||||||
|
|
|
@ -18,9 +18,4 @@ public interface BulletinBoardConstants {
|
||||||
public static final String CLOSE_BATCH_PATH = "/closebatch";
|
public static final String CLOSE_BATCH_PATH = "/closebatch";
|
||||||
public static final String SYNC_QUERY_PATH = "/syncquery";
|
public static final String SYNC_QUERY_PATH = "/syncquery";
|
||||||
|
|
||||||
// Other Constants
|
|
||||||
|
|
||||||
public static final String BATCH_TAG = "@BATCH";
|
|
||||||
public static final String BATCH_ID_TAG_PREFIX = "BATCHID#";
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,36 +7,24 @@ import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Arbel Deutsch Peled on 18-Dec-15.
|
* Created by Arbel Deutsch Peled on 18-Dec-15.
|
||||||
* Extends the Digest interface with a method for digesting Batch messages
|
* Extends the Digest interface with methods for digesting Bulletin Board messages
|
||||||
*/
|
*/
|
||||||
public interface BulletinBoardDigest extends Digest {
|
public interface BulletinBoardDigest extends Digest {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the digest with the batch message data (ignore the signature)
|
* Update the digest with the message data (ignore the signature)
|
||||||
* The digest only uses the part the signatures are computed on for this operation
|
* The digest only uses the part the signatures are computed on for this operation
|
||||||
* @param completeBatch is the batch message that needs to be digested
|
* If the message is a stub: this should be called before digesting the raw data
|
||||||
|
* @param msg is the message that needs to be digested
|
||||||
*/
|
*/
|
||||||
public void updateCompleteBatch(BulletinBoardMessage completeBatch);
|
public void update(BulletinBoardMessage msg);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the digest with a BulletinBoardMessage that contains only the metadata of a batch message
|
* Update the digest with the message data (ignore the signature)
|
||||||
* The digest only uses the part the signatures are computed on for this operation
|
* The digest only uses the part the signatures are computed on for this operation
|
||||||
* This operation is necessary before beginning to digest the actual data
|
* If the message is a stub: this should be called before digesting the raw data
|
||||||
* @param batchStub contains the metadata
|
* @param msg is the message that needs to be digested
|
||||||
*/
|
*/
|
||||||
public void updateBatchStub(BulletinBoardMessage batchStub);
|
public void update(UnsignedBulletinBoardMessage msg);
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the digest with the batch message data (ignore the signature)
|
|
||||||
* @param completeBatch is the batch message that needs to be digested
|
|
||||||
*/
|
|
||||||
public void updateCompleteBatch(UnsignedBulletinBoardMessage completeBatch);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the digest with a BulletinBoardMessage that contains only the metadata of a batch message
|
|
||||||
* This operation is necessary before beginning to digest the actual data
|
|
||||||
* @param batchStub contains the metadata
|
|
||||||
*/
|
|
||||||
public void updateBatchStub(UnsignedBulletinBoardMessage batchStub);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
package meerkat.bulletinboard;
|
package meerkat.bulletinboard;
|
||||||
|
|
||||||
|
import com.google.protobuf.BoolValue;
|
||||||
|
import com.google.protobuf.Int32Value;
|
||||||
import meerkat.comm.CommunicationException;
|
import meerkat.comm.CommunicationException;
|
||||||
import meerkat.comm.MessageOutputStream;
|
import meerkat.comm.MessageOutputStream;
|
||||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Arbel on 07/11/15.
|
* Created by Arbel on 07/11/15.
|
||||||
|
@ -29,7 +29,7 @@ public interface BulletinBoardServer{
|
||||||
* @return TRUE if the message has been authenticated and FALSE otherwise (in ProtoBuf form)
|
* @return TRUE if the message has been authenticated and FALSE otherwise (in ProtoBuf form)
|
||||||
* @throws CommunicationException on DB connection error
|
* @throws CommunicationException on DB connection error
|
||||||
*/
|
*/
|
||||||
public BoolMsg postMessage(BulletinBoardMessage msg) throws CommunicationException;
|
public BoolValue postMessage(BulletinBoardMessage msg) throws CommunicationException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read all posted messages matching the given filters
|
* Read all posted messages matching the given filters
|
||||||
|
@ -45,7 +45,7 @@ public interface BulletinBoardServer{
|
||||||
* @return an IntMsg containing the number of messages that match the filter
|
* @return an IntMsg containing the number of messages that match the filter
|
||||||
* @throws CommunicationException on DB connection error
|
* @throws CommunicationException on DB connection error
|
||||||
*/
|
*/
|
||||||
public IntMsg getMessageCount(MessageFilterList filterList) throws CommunicationException;
|
public Int32Value getMessageCount(MessageFilterList filterList) throws CommunicationException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Informs server about a new batch message
|
* Informs server about a new batch message
|
||||||
|
@ -55,10 +55,10 @@ public interface BulletinBoardServer{
|
||||||
* However, if such a batch exists and is already closed: the value returned will be FALSE
|
* However, if such a batch exists and is already closed: the value returned will be FALSE
|
||||||
* @throws CommunicationException on DB connection error
|
* @throws CommunicationException on DB connection error
|
||||||
*/
|
*/
|
||||||
public BoolMsg beginBatch(BeginBatchMessage message) throws CommunicationException;
|
public BoolValue beginBatch(BeginBatchMessage message) throws CommunicationException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Posts a (part of a) batch message to the bulletin board
|
* Posts a chunk of a batch message to the bulletin board
|
||||||
* Note that the existence and contents of a batch message are not available for reading before the batch is finalized
|
* Note that the existence and contents of a batch message are not available for reading before the batch is finalized
|
||||||
* @param batchMessage contains the (partial) data this message carries as well as meta-data required in order to place the data
|
* @param batchMessage contains the (partial) data this message carries as well as meta-data required in order to place the data
|
||||||
* in the correct position inside the correct batch
|
* in the correct position inside the correct batch
|
||||||
|
@ -66,8 +66,8 @@ public interface BulletinBoardServer{
|
||||||
* Specifically, if the batch is already closed: the value returned will be FALSE
|
* Specifically, if the batch is already closed: the value returned will be FALSE
|
||||||
* However, requiring to open a batch before insertion of messages is implementation-dependent
|
* However, requiring to open a batch before insertion of messages is implementation-dependent
|
||||||
* @throws CommunicationException on DB connection error
|
* @throws CommunicationException on DB connection error
|
||||||
*/
|
*/
|
||||||
public BoolMsg postBatchMessage(BatchMessage batchMessage) throws CommunicationException;
|
public BoolValue postBatchMessage(BatchMessage batchMessage) throws CommunicationException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempts to close and finalize a batch message
|
* Attempts to close and finalize a batch message
|
||||||
|
@ -76,16 +76,16 @@ public interface BulletinBoardServer{
|
||||||
* Specifically, if the signature is invalid or if some of the batch parts have not yet been submitted: the value returned will be FALSE
|
* Specifically, if the signature is invalid or if some of the batch parts have not yet been submitted: the value returned will be FALSE
|
||||||
* @throws CommunicationException on DB connection error
|
* @throws CommunicationException on DB connection error
|
||||||
*/
|
*/
|
||||||
public BoolMsg closeBatchMessage(CloseBatchMessage message) throws CommunicationException;
|
public BoolValue closeBatch(CloseBatchMessage message) throws CommunicationException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads a batch message from the server (starting with the supplied position)
|
* Reads a batch message from the server (starting with the supplied position)
|
||||||
* @param message specifies the signer ID and the batch ID to read as well as an (optional) start position
|
* @param batchQuery specifies which batch and what parts of it to retrieve
|
||||||
* @param out is a stream of the ordered batch messages starting from the specified start position (if given) or from the beginning (if omitted)
|
* @param out is a stream of the ordered batch messages starting from the specified start position (if given) or from the beginning (if omitted)
|
||||||
* @throws CommunicationException on DB connection error
|
* @throws CommunicationException on DB connection error
|
||||||
* @throws IllegalArgumentException if message does not specify a batch
|
* @throws IllegalArgumentException if message ID does not specify a batch
|
||||||
*/
|
*/
|
||||||
public void readBatch(BatchSpecificationMessage message, MessageOutputStream<BatchData> out) throws CommunicationException, IllegalArgumentException;
|
public void readBatch(BatchQuery batchQuery, MessageOutputStream<BatchChunk> out) throws CommunicationException, IllegalArgumentException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a SyncQuery to test against that corresponds with the current server state for a specific filter list
|
* Create a SyncQuery to test against that corresponds with the current server state for a specific filter list
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
package meerkat.bulletinboard;
|
||||||
|
|
||||||
|
import meerkat.crypto.DigitalSignature;
|
||||||
|
import meerkat.protobuf.BulletinBoardAPI.BulletinBoardMessage;
|
||||||
|
import meerkat.protobuf.BulletinBoardAPI.UnsignedBulletinBoardMessage;
|
||||||
|
import meerkat.protobuf.Crypto;
|
||||||
|
|
||||||
|
import java.security.SignatureException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Arbel Deutsch Peled on 18-Dec-15.
|
||||||
|
* Extends the DigitalSignature interface with methods for signing Bulletin Board messages
|
||||||
|
*/
|
||||||
|
public interface BulletinBoardSignature extends DigitalSignature {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add msg to the content stream to be verified / signed
|
||||||
|
* The digest only uses the part the signatures are computed on for this operation
|
||||||
|
* If the message is a stub: this should be called before updating with the raw data
|
||||||
|
* @param msg is the message that needs to be digested
|
||||||
|
*/
|
||||||
|
public void updateContent(BulletinBoardMessage msg) throws SignatureException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add msg to the content stream to be verified / signed
|
||||||
|
* If the message is a stub: this should be called before updating with the raw data
|
||||||
|
* @param msg is the message that needs to be digested
|
||||||
|
*/
|
||||||
|
public void updateContent(UnsignedBulletinBoardMessage msg) throws SignatureException;
|
||||||
|
|
||||||
|
}
|
|
@ -1,181 +0,0 @@
|
||||||
package meerkat.bulletinboard;
|
|
||||||
|
|
||||||
import com.google.protobuf.Timestamp;
|
|
||||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
|
||||||
import meerkat.protobuf.Crypto.*;
|
|
||||||
import meerkat.util.BulletinBoardUtils;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by Arbel Deutsch Peled on 14-Dec-15.
|
|
||||||
*
|
|
||||||
* A data structure for holding a complete batch message along with its signature
|
|
||||||
*/
|
|
||||||
public class CompleteBatch {
|
|
||||||
|
|
||||||
private BeginBatchMessage beginBatchMessage;
|
|
||||||
private List<BatchData> batchDataList;
|
|
||||||
private Signature signature;
|
|
||||||
private Timestamp timestamp;
|
|
||||||
|
|
||||||
public CompleteBatch() {
|
|
||||||
batchDataList = new LinkedList<BatchData>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public CompleteBatch(BeginBatchMessage newBeginBatchMessage) {
|
|
||||||
this();
|
|
||||||
beginBatchMessage = newBeginBatchMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CompleteBatch(BeginBatchMessage newBeginBatchMessage, List<BatchData> newDataList) {
|
|
||||||
this(newBeginBatchMessage);
|
|
||||||
appendBatchData(newDataList);
|
|
||||||
}
|
|
||||||
|
|
||||||
public CompleteBatch(BeginBatchMessage newBeginBatchMessage, List<BatchData> newDataList, Signature newSignature) {
|
|
||||||
this(newBeginBatchMessage, newDataList);
|
|
||||||
signature = newSignature;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CompleteBatch(BeginBatchMessage newBeginBatchMessage, List<BatchData> newDataList, Signature newSignature, Timestamp timestamp) {
|
|
||||||
this(newBeginBatchMessage, newDataList, newSignature);
|
|
||||||
this.timestamp = timestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CompleteBatch(Timestamp timestamp) {
|
|
||||||
this();
|
|
||||||
this.timestamp = timestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Combines the actual Bulletin board representation of a batch into a CompleteBatch object
|
|
||||||
* @param batchMessage is the BulletinBoard message that specifies the batch
|
|
||||||
* @param batchDataList is the (ordered) list of batch data
|
|
||||||
*/
|
|
||||||
public CompleteBatch(BulletinBoardMessage batchMessage, List<BatchData> batchDataList) throws IllegalArgumentException{
|
|
||||||
|
|
||||||
final String[] PREFIXES = {
|
|
||||||
BulletinBoardConstants.BATCH_ID_TAG_PREFIX,
|
|
||||||
BulletinBoardConstants.BATCH_TAG};
|
|
||||||
|
|
||||||
String batchIdStr = BulletinBoardUtils.findTagWithPrefix(batchMessage, BulletinBoardConstants.BATCH_ID_TAG_PREFIX);
|
|
||||||
|
|
||||||
if (batchIdStr == null){
|
|
||||||
throw new IllegalArgumentException("");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.beginBatchMessage =
|
|
||||||
BeginBatchMessage.newBuilder()
|
|
||||||
.setSignerId(batchMessage.getSig(0).getSignerId())
|
|
||||||
.setBatchId(Integer.parseInt(batchIdStr))
|
|
||||||
.addAllTag(BulletinBoardUtils.removePrefixTags(batchMessage, Arrays.asList(PREFIXES)))
|
|
||||||
.build();
|
|
||||||
|
|
||||||
this.batchDataList = batchDataList;
|
|
||||||
this.signature = batchMessage.getSig(0);
|
|
||||||
this.timestamp = batchMessage.getMsg().getTimestamp();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public BeginBatchMessage getBeginBatchMessage() {
|
|
||||||
return beginBatchMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<BatchData> getBatchDataList() {
|
|
||||||
return batchDataList;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Signature getSignature() {
|
|
||||||
return signature;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Timestamp getTimestamp() {
|
|
||||||
return timestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CloseBatchMessage getCloseBatchMessage() {
|
|
||||||
return CloseBatchMessage.newBuilder()
|
|
||||||
.setBatchId(getBeginBatchMessage().getBatchId())
|
|
||||||
.setBatchLength(getBatchDataList().size())
|
|
||||||
.setSig(getSignature())
|
|
||||||
.setTimestamp(getTimestamp())
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setBeginBatchMessage(BeginBatchMessage beginBatchMessage) {
|
|
||||||
this.beginBatchMessage = beginBatchMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void appendBatchData(BatchData newBatchData) {
|
|
||||||
batchDataList.add(newBatchData);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void appendBatchData(List<BatchData> newBatchDataList) {
|
|
||||||
batchDataList.addAll(newBatchDataList);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSignature(Signature newSignature) {
|
|
||||||
signature = newSignature;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTimestamp(Timestamp timestamp) {
|
|
||||||
this.timestamp = timestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object other) {
|
|
||||||
|
|
||||||
if (!(other instanceof CompleteBatch)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
CompleteBatch otherBatch = (CompleteBatch) other;
|
|
||||||
|
|
||||||
boolean result = true;
|
|
||||||
|
|
||||||
if (beginBatchMessage == null) {
|
|
||||||
if (otherBatch.getBeginBatchMessage() != null)
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
result = result && beginBatchMessage.equals(otherBatch.getBeginBatchMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (batchDataList == null) {
|
|
||||||
if (otherBatch.getBatchDataList() != null)
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
result = result && batchDataList.equals(otherBatch.getBatchDataList());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (signature == null) {
|
|
||||||
if (otherBatch.getSignature() != null)
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
result = result && signature.equals(otherBatch.getSignature());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (timestamp == null) {
|
|
||||||
if (otherBatch.getTimestamp() != null)
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
result = result && timestamp.equals(otherBatch.getTimestamp());
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
|
|
||||||
if (beginBatchMessage == null || beginBatchMessage.getSignerId() == null)
|
|
||||||
return "Unspecified batch " + super.toString();
|
|
||||||
|
|
||||||
return "Batch " + beginBatchMessage.getSignerId().toString() + ":" + beginBatchMessage.getBatchId();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,5 +1,6 @@
|
||||||
package meerkat.bulletinboard;
|
package meerkat.bulletinboard;
|
||||||
|
|
||||||
|
import com.google.protobuf.BoolValue;
|
||||||
import meerkat.comm.CommunicationException;
|
import meerkat.comm.CommunicationException;
|
||||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||||
|
|
||||||
|
@ -10,20 +11,22 @@ public interface DeletableBulletinBoardServer extends BulletinBoardServer {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes a message from the Bulletin Board
|
* Deletes a message from the Bulletin Board
|
||||||
|
* If the message is a batch: the batch data is deleted as well
|
||||||
* Logs this action
|
* Logs this action
|
||||||
* @param msgID is the ID of the message to delete
|
* @param msgID is the ID of the message to delete
|
||||||
* @return a BoolMsg containing the value TRUE if a message was deleted, FALSE if the message does not exist
|
* @return a BoolMsg containing the value TRUE if a message was deleted, FALSE if the message does not exist
|
||||||
* @throws CommunicationException in case of an error
|
* @throws CommunicationException in case of an error
|
||||||
*/
|
*/
|
||||||
public BoolMsg deleteMessage(MessageID msgID) throws CommunicationException;
|
public BoolValue deleteMessage(MessageID msgID) throws CommunicationException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes a message from the Bulletin Board
|
* Deletes a message from the Bulletin Board
|
||||||
|
* If the message is a batch: the batch data is deleted as well
|
||||||
* Logs this action
|
* Logs this action
|
||||||
* @param entryNum is the serial entry number of the message to delete
|
* @param entryNum is the serial entry number of the message to delete
|
||||||
* @return a BoolMsg containing the value TRUE if a message was deleted, FALSE if the message does not exist
|
* @return a BoolMsg containing the value TRUE if a message was deleted, FALSE if the message does not exist
|
||||||
* @throws CommunicationException in case of an error
|
* @throws CommunicationException in case of an error
|
||||||
*/
|
*/
|
||||||
public BoolMsg deleteMessage(long entryNum) throws CommunicationException;
|
public BoolValue deleteMessage(long entryNum) throws CommunicationException;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,104 +0,0 @@
|
||||||
package meerkat.bulletinboard;
|
|
||||||
|
|
||||||
import com.google.protobuf.ByteString;
|
|
||||||
import com.google.protobuf.Message;
|
|
||||||
import meerkat.crypto.DigitalSignature;
|
|
||||||
import meerkat.protobuf.BulletinBoardAPI.BatchData;
|
|
||||||
import meerkat.protobuf.Crypto;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.security.InvalidKeyException;
|
|
||||||
import java.security.KeyStore;
|
|
||||||
import java.security.KeyStoreException;
|
|
||||||
import java.security.SignatureException;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.security.UnrecoverableKeyException;
|
|
||||||
import java.security.cert.CertificateException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by Arbel Deutsch Peled on 20-Dec-15.
|
|
||||||
* Wrapper class for signing and verifying Batch signatures in a standardized way
|
|
||||||
*/
|
|
||||||
public class GenericBatchDigitalSignature implements BatchDigitalSignature{
|
|
||||||
|
|
||||||
private DigitalSignature digitalSignature;
|
|
||||||
|
|
||||||
public GenericBatchDigitalSignature(DigitalSignature digitalSignature) {
|
|
||||||
this.digitalSignature = digitalSignature;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void updateContent(CompleteBatch completeBatch) throws SignatureException {
|
|
||||||
|
|
||||||
digitalSignature.updateContent(completeBatch.getBeginBatchMessage());
|
|
||||||
|
|
||||||
for (BatchData batchData : completeBatch.getBatchDataList()) {
|
|
||||||
digitalSignature.updateContent(batchData);
|
|
||||||
}
|
|
||||||
|
|
||||||
digitalSignature.updateContent(completeBatch.getTimestamp());
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean verify(CompleteBatch completeBatch) throws SignatureException, CertificateException, InvalidKeyException {
|
|
||||||
|
|
||||||
digitalSignature.initVerify(completeBatch.getSignature());
|
|
||||||
updateContent(completeBatch);
|
|
||||||
return digitalSignature.verify();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void loadVerificationCertificates(InputStream certStream) throws CertificateException {
|
|
||||||
digitalSignature.loadVerificationCertificates(certStream);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clearVerificationCertificates() {
|
|
||||||
digitalSignature.clearVerificationCertificates();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void updateContent(Message msg) throws SignatureException {
|
|
||||||
digitalSignature.updateContent(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Crypto.Signature sign() throws SignatureException {
|
|
||||||
return digitalSignature.sign();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void initVerify(Crypto.Signature sig) throws CertificateException, InvalidKeyException {
|
|
||||||
digitalSignature.initVerify(sig);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean verify() {
|
|
||||||
return digitalSignature.verify();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public KeyStore.Builder getPKCS12KeyStoreBuilder(InputStream keyStream, char[] password)
|
|
||||||
throws IOException, CertificateException, KeyStoreException, NoSuchAlgorithmException {
|
|
||||||
return digitalSignature.getPKCS12KeyStoreBuilder(keyStream, password);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void loadSigningCertificate(KeyStore.Builder keyStoreBuilder) throws IOException, CertificateException, UnrecoverableKeyException {
|
|
||||||
digitalSignature.loadSigningCertificate(keyStoreBuilder);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ByteString getSignerID() {
|
|
||||||
return digitalSignature.getSignerID();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clearSigningKey() {
|
|
||||||
digitalSignature.clearSigningKey();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,5 +1,6 @@
|
||||||
package meerkat.bulletinboard;
|
package meerkat.bulletinboard;
|
||||||
|
|
||||||
|
import com.google.protobuf.ByteString;
|
||||||
import com.google.protobuf.BytesValue;
|
import com.google.protobuf.BytesValue;
|
||||||
import com.google.protobuf.Message;
|
import com.google.protobuf.Message;
|
||||||
import meerkat.crypto.Digest;
|
import meerkat.crypto.Digest;
|
||||||
|
@ -51,29 +52,23 @@ public class GenericBulletinBoardDigest implements BulletinBoardDigest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateCompleteBatch(BulletinBoardMessage completeBatch) {
|
public void update(BulletinBoardMessage msg) {
|
||||||
updateCompleteBatch(completeBatch.getMsg());
|
update(msg.getMsg());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateBatchStub(BulletinBoardMessage batchStub) {
|
public void update(UnsignedBulletinBoardMessage msg) {
|
||||||
updateBatchStub(batchStub.getMsg());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
for (ByteString tag : msg.getTagList().asByteStringList()){
|
||||||
public void updateCompleteBatch(UnsignedBulletinBoardMessage completeBatch) {
|
update(tag.toByteArray());
|
||||||
|
}
|
||||||
|
|
||||||
// Digest just the signed part
|
update(msg.getTimestamp());
|
||||||
UnsignedBulletinBoardMessage batchStub = completeBatch.toBuilder().clearData().build();
|
|
||||||
updateBatchStub(batchStub);
|
|
||||||
|
|
||||||
// Digest the data
|
if (!msg.getIsStub()){
|
||||||
update(completeBatch.getData().toByteArray());
|
update(msg.getData().toByteArray());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void updateBatchStub(UnsignedBulletinBoardMessage batchStub) {
|
|
||||||
update(batchStub);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,106 @@
|
||||||
|
package meerkat.bulletinboard;
|
||||||
|
|
||||||
|
import com.google.protobuf.ByteString;
|
||||||
|
import com.google.protobuf.Message;
|
||||||
|
import meerkat.crypto.Digest;
|
||||||
|
import meerkat.crypto.DigitalSignature;
|
||||||
|
import meerkat.protobuf.BulletinBoardAPI;
|
||||||
|
import meerkat.protobuf.BulletinBoardAPI.BulletinBoardMessage;
|
||||||
|
import meerkat.protobuf.BulletinBoardAPI.MessageID;
|
||||||
|
import meerkat.protobuf.BulletinBoardAPI.UnsignedBulletinBoardMessage;
|
||||||
|
import meerkat.protobuf.Crypto;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.security.*;
|
||||||
|
import java.security.cert.CertificateException;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Arbel Deutsch Peled on 19-Dec-15.
|
||||||
|
* Wrapper class for digesting Batches in a standardized way
|
||||||
|
*/
|
||||||
|
public class GenericBulletinBoardSignature implements BulletinBoardSignature {
|
||||||
|
|
||||||
|
private DigitalSignature signer;
|
||||||
|
|
||||||
|
public GenericBulletinBoardSignature(DigitalSignature signer) {
|
||||||
|
this.signer = signer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateContent(BulletinBoardMessage msg) throws SignatureException{
|
||||||
|
signer.updateContent(msg.getMsg());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateContent(UnsignedBulletinBoardMessage msg) throws SignatureException{
|
||||||
|
|
||||||
|
for (ByteString tag : msg.getTagList().asByteStringList()){
|
||||||
|
updateContent(tag.toByteArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
updateContent(msg.getTimestamp());
|
||||||
|
|
||||||
|
if (!msg.getIsStub()){
|
||||||
|
updateContent(msg.getData().toByteArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void loadVerificationCertificates(InputStream certStream) throws CertificateException {
|
||||||
|
signer.loadVerificationCertificates(certStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clearVerificationCertificates() {
|
||||||
|
signer.clearVerificationCertificates();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateContent(byte[] data) throws SignatureException {
|
||||||
|
signer.updateContent(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateContent(Message msg) throws SignatureException {
|
||||||
|
signer.updateContent(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Crypto.Signature sign() throws SignatureException {
|
||||||
|
return signer.sign();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initVerify(Crypto.Signature sig) throws CertificateException, InvalidKeyException {
|
||||||
|
signer.initVerify(sig);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean verify() {
|
||||||
|
return signer.verify();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KeyStore.Builder getPKCS12KeyStoreBuilder(InputStream keyStream, char[] password) throws IOException, CertificateException, KeyStoreException, NoSuchAlgorithmException {
|
||||||
|
return signer.getPKCS12KeyStoreBuilder(keyStream, password);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void loadSigningCertificate(KeyStore.Builder keyStoreBuilder) throws IOException, CertificateException, UnrecoverableKeyException {
|
||||||
|
signer.loadSigningCertificate(keyStoreBuilder);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ByteString getSignerID() {
|
||||||
|
return signer.getSignerID();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clearSigningKey() {
|
||||||
|
signer.clearSigningKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
package meerkat.comm;
|
||||||
|
|
||||||
|
import com.google.protobuf.Message;
|
||||||
|
import meerkat.protobuf.Comm;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A generic communication channel that supports point-to-point and broadcast operation
|
||||||
|
*/
|
||||||
|
|
||||||
|
public interface Channel {
|
||||||
|
/**
|
||||||
|
* Return the id of the channel's endpoint (this will be used as the source of message sent from the channel).
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public int getSourceId();
|
||||||
|
|
||||||
|
public interface ReceiverCallback {
|
||||||
|
public void receiveMessage(Comm.BroadcastMessage envelope);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sends a private message
|
||||||
|
* @param destUser destination user's identifier
|
||||||
|
* @param msg message
|
||||||
|
*/
|
||||||
|
public void sendMessage(int destUser, Message msg);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* broadcasts a message to all parties (including the sender)
|
||||||
|
* @param msg message
|
||||||
|
*/
|
||||||
|
public void broadcastMessage(Message msg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a callback to handle received messages.
|
||||||
|
* The callback is called in the <b>Channel</b> thread, so no long processing should
|
||||||
|
* occur in the callback method.
|
||||||
|
* @param callback
|
||||||
|
*/
|
||||||
|
public void registerReceiverCallback(ReceiverCallback callback);
|
||||||
|
}
|
|
@ -39,6 +39,13 @@ public interface DigitalSignature {
|
||||||
*/
|
*/
|
||||||
public void clearVerificationCertificates();
|
public void clearVerificationCertificates();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add raw data to the content stream to be verified / signed.
|
||||||
|
*
|
||||||
|
* @param data
|
||||||
|
* @throws SignatureException
|
||||||
|
*/
|
||||||
|
public void updateContent(byte[] data) throws SignatureException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add msg to the content stream to be verified / signed. Each message is (automatically)
|
* Add msg to the content stream to be verified / signed. Each message is (automatically)
|
||||||
|
|
|
@ -140,6 +140,11 @@ public class ECDSASignature implements DigitalSignature {
|
||||||
signer.update(msg.toByteString().asReadOnlyByteBuffer());
|
signer.update(msg.toByteString().asReadOnlyByteBuffer());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateContent(byte[] data) throws SignatureException {
|
||||||
|
signer.update(data);
|
||||||
|
}
|
||||||
|
|
||||||
public void updateContent(InputStream in) throws IOException, SignatureException {
|
public void updateContent(InputStream in) throws IOException, SignatureException {
|
||||||
ByteString inStr = ByteString.readFrom(in);
|
ByteString inStr = ByteString.readFrom(in);
|
||||||
signer.update(inStr.asReadOnlyByteBuffer());
|
signer.update(inStr.asReadOnlyByteBuffer());
|
||||||
|
|
|
@ -113,7 +113,7 @@ public class ECElGamalEncryption implements Encryption {
|
||||||
Pair<ECPoint,ECPoint> randomizer = elGamalPK.encrypt(curve.getInfinity(), rndInt);
|
Pair<ECPoint,ECPoint> randomizer = elGamalPK.encrypt(curve.getInfinity(), rndInt);
|
||||||
ConcreteCrypto.ElGamalCiphertext originalEncodedCipher= ConcreteCrypto.ElGamalCiphertext.parseFrom(msg.getData());
|
ConcreteCrypto.ElGamalCiphertext originalEncodedCipher= ConcreteCrypto.ElGamalCiphertext.parseFrom(msg.getData());
|
||||||
|
|
||||||
Pair<ECPoint,ECPoint> originalCipher = new Pair<>(
|
Pair<ECPoint,ECPoint> originalCipher = new Pair<ECPoint, ECPoint>(
|
||||||
curve.decodePoint(originalEncodedCipher.getC1().toByteArray()),
|
curve.decodePoint(originalEncodedCipher.getC1().toByteArray()),
|
||||||
curve.decodePoint(originalEncodedCipher.getC2().toByteArray()));
|
curve.decodePoint(originalEncodedCipher.getC2().toByteArray()));
|
||||||
Pair<ECPoint,ECPoint> newCipher = elGamalPK.add(originalCipher, randomizer);
|
Pair<ECPoint,ECPoint> newCipher = elGamalPK.add(originalCipher, randomizer);
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
package meerkat.pollingstation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Arbel Deutsch Peled on 21-Dec-15.
|
||||||
|
*/
|
||||||
|
public interface PollingStationConstants {
|
||||||
|
|
||||||
|
// Relative addresses for Scanner operations
|
||||||
|
|
||||||
|
public static final String POLLING_STATION_WEB_SCANNER_SCAN_PATH = "/scan";
|
||||||
|
public static final String POLLING_STATION_WEB_SCANNER_ERROR_PATH = "/error";
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
package meerkat.pollingstation;
|
||||||
|
|
||||||
|
import com.google.common.util.concurrent.FutureCallback;
|
||||||
|
import com.google.protobuf.BoolValue;
|
||||||
|
import meerkat.protobuf.PollingStation.*;
|
||||||
|
/**
|
||||||
|
* Created by Arbel on 05/05/2016.
|
||||||
|
* An interface for the scanner used by the Polling Station Committee
|
||||||
|
* The scanner works as a producer, while the polling station is the consumer
|
||||||
|
* That is to say: scans are pushed from the scanner rather than requested by the polling station
|
||||||
|
*/
|
||||||
|
public interface PollingStationScanner {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An interface for processing scans (Polling Station side)
|
||||||
|
*/
|
||||||
|
public interface Consumer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets up the connection to the scanner and begins receiving scans
|
||||||
|
* @throws Exception when the operation fails
|
||||||
|
*/
|
||||||
|
public void start() throws Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the connection to the scanner
|
||||||
|
* @throws Exception when the operation fails
|
||||||
|
*/
|
||||||
|
public void stop() throws Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subscribes to new scans
|
||||||
|
*
|
||||||
|
* @param scanCallback is the handler for scanned data
|
||||||
|
*/
|
||||||
|
public void subscribe(FutureCallback<ScannedData> scanCallback);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An interface for submitting scanned data (scanner side)
|
||||||
|
*/
|
||||||
|
public interface Producer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a scan to all subscribers
|
||||||
|
* @param scannedData contains the scanned data
|
||||||
|
* @return a BoolValue containing TRUE iff the scanned data has been sent to at least one subscriber
|
||||||
|
*/
|
||||||
|
public BoolValue newScan(ScannedData scannedData);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies subscribers about an error that occurred during scan
|
||||||
|
* @param errorMsg is the error that occurred
|
||||||
|
* @return a BoolValue containing TRUE iff the error has been sent to at least one subscriber
|
||||||
|
*/
|
||||||
|
public BoolValue reportScanError(ErrorMsg errorMsg);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -5,6 +5,7 @@ import meerkat.protobuf.BulletinBoardAPI.*;
|
||||||
import meerkat.protobuf.Crypto.*;
|
import meerkat.protobuf.Crypto.*;
|
||||||
|
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -24,22 +25,51 @@ public class BulletinBoardMessageComparator implements Comparator<BulletinBoardM
|
||||||
@Override
|
@Override
|
||||||
public int compare(BulletinBoardMessage msg1, BulletinBoardMessage msg2) {
|
public int compare(BulletinBoardMessage msg1, BulletinBoardMessage msg2) {
|
||||||
|
|
||||||
List<Signature> msg1Sigs = msg1.getSigList();
|
|
||||||
List<Signature> msg2Sigs = msg2.getSigList();
|
|
||||||
|
|
||||||
// Compare unsigned message
|
|
||||||
if (!msg1.getMsg().equals(msg2.getMsg())){
|
// Compare Timestamps
|
||||||
|
|
||||||
|
if (!msg1.getMsg().getTimestamp().equals(msg2.getMsg().getTimestamp())){
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compare signatures
|
// Compare tags (enforce order)
|
||||||
|
|
||||||
if (msg1Sigs.size() != msg2Sigs.size()){
|
List<String> tags1 = msg1.getMsg().getTagList();
|
||||||
|
Iterator<String> tags2 = msg2.getMsg().getTagList().iterator();
|
||||||
|
|
||||||
|
for (String tag : tags1){
|
||||||
|
if (!tags2.hasNext()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (!tags2.next().equals(tag)){
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare data (only for non-stub messages)
|
||||||
|
|
||||||
|
if (msg1.getMsg().getIsStub() != msg2.getMsg().getIsStub()){
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Signature sig : msg1Sigs){
|
if (msg1.getMsg().getIsStub()){
|
||||||
if (!msg2Sigs.contains(sig)) {
|
if (!msg1.getMsg().getData().equals(msg2.getMsg().getData())){
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare signatures (do not enforce order)
|
||||||
|
|
||||||
|
List<Signature> sigs1 = msg1.getSigList();
|
||||||
|
List<Signature> sigs2 = msg2.getSigList();
|
||||||
|
|
||||||
|
if (sigs1.size() != sigs2.size()){
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Signature sig : sigs1){
|
||||||
|
if (!sigs2.contains(sig)) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
package meerkat.util;
|
package meerkat.util;
|
||||||
|
|
||||||
import com.google.protobuf.ByteString;
|
import com.google.protobuf.ByteString;
|
||||||
import meerkat.bulletinboard.BatchDigitalSignature;
|
|
||||||
import meerkat.bulletinboard.CompleteBatch;
|
|
||||||
import meerkat.crypto.DigitalSignature;
|
import meerkat.crypto.DigitalSignature;
|
||||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||||
import com.google.protobuf.Timestamp;
|
import com.google.protobuf.Timestamp;
|
||||||
|
@ -131,97 +129,4 @@ public class BulletinBoardMessageGenerator {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates a complete instance of a CompleteBatch
|
|
||||||
* @param signer contains the credentials required to sign the message
|
|
||||||
* @param batchId is the (per-signer) batch-ID
|
|
||||||
* @param timestamp is the time at which the message was generated
|
|
||||||
* @param dataCount is the number of Batch Data in the batch
|
|
||||||
* @param dataSize is the number of bytes per Batch Data
|
|
||||||
* @param tagCount is the number of tags
|
|
||||||
* @param tags contains a list of tags to be added (in addition to the random ones)
|
|
||||||
* @return a random, signed CompleteBatch containing random data and tags
|
|
||||||
* @throws SignatureException if an error occurs while signing the batch
|
|
||||||
*/
|
|
||||||
public CompleteBatch generateRandomBatch(BatchDigitalSignature signer, int batchId, Timestamp timestamp, int dataCount, int dataSize, int tagCount, List<String> tags) throws SignatureException {
|
|
||||||
|
|
||||||
CompleteBatch result = new CompleteBatch(BeginBatchMessage.newBuilder()
|
|
||||||
.setSignerId(signer.getSignerID())
|
|
||||||
.setBatchId(batchId)
|
|
||||||
.addAllTag(tags)
|
|
||||||
.addAllTag(randomStrings(tagCount))
|
|
||||||
.build());
|
|
||||||
|
|
||||||
List<BatchData> batchDataList = new LinkedList<>();
|
|
||||||
|
|
||||||
for (int i = 0 ; i < dataCount ; i++) {
|
|
||||||
batchDataList.add(BatchData.newBuilder()
|
|
||||||
.setData(ByteString.copyFrom(randomBytes(dataSize)))
|
|
||||||
.build());
|
|
||||||
}
|
|
||||||
|
|
||||||
result.appendBatchData(batchDataList);
|
|
||||||
|
|
||||||
result.setTimestamp(timestamp);
|
|
||||||
|
|
||||||
signer.updateContent(result);
|
|
||||||
|
|
||||||
result.setSignature(signer.sign());
|
|
||||||
|
|
||||||
return result;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates a complete instance of a CompleteBatch
|
|
||||||
* @param signer contains the credentials required to sign the message
|
|
||||||
* @param batchId is the (per-signer) batch-ID
|
|
||||||
* @param timestamp is the time at which the message was generated
|
|
||||||
* @param dataCount is the number of Batch Data in the batch
|
|
||||||
* @param dataSize is the number of bytes per Batch Data
|
|
||||||
* @param tagCount is the number of tags
|
|
||||||
* @return a random, signed CompleteBatch containing random data and tags
|
|
||||||
* @throws SignatureException if an error occurs while signing the batch
|
|
||||||
*/
|
|
||||||
public CompleteBatch generateRandomBatch(BatchDigitalSignature signer, int batchId, Timestamp timestamp, int dataCount, int dataSize, int tagCount) throws SignatureException {
|
|
||||||
return generateRandomBatch(signer, batchId, timestamp, dataCount, dataSize, tagCount, new LinkedList<String>());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates a complete instance of a CompleteBatch
|
|
||||||
* @param signer contains the credentials required to sign the message
|
|
||||||
* @param batchId is the (per-signer) batch-ID
|
|
||||||
* @param dataCount is the number of Batch Data in the batch
|
|
||||||
* @param dataSize is the number of bytes per Batch Data
|
|
||||||
* @param tagCount is the number of tags
|
|
||||||
* @param tags contains a list of tags to be added (in addition to the random ones)
|
|
||||||
* @return a random, signed CompleteBatch containing random data, tags and timestamp
|
|
||||||
* @throws SignatureException if an error occurs while signing the batch
|
|
||||||
*/
|
|
||||||
public CompleteBatch generateRandomBatch(BatchDigitalSignature signer, int batchId, int dataCount, int dataSize, int tagCount, List<String> tags) throws SignatureException {
|
|
||||||
|
|
||||||
Timestamp timestamp = Timestamp.newBuilder()
|
|
||||||
.setSeconds(random.nextLong())
|
|
||||||
.setNanos(random.nextInt())
|
|
||||||
.build();
|
|
||||||
|
|
||||||
return generateRandomBatch(signer, batchId, timestamp, dataCount, dataSize, tagCount, tags);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates a complete instance of a CompleteBatch
|
|
||||||
* @param signer contains the credentials required to sign the message
|
|
||||||
* @param batchId is the (per-signer) batch-ID
|
|
||||||
* @param dataCount is the number of Batch Data in the batch
|
|
||||||
* @param dataSize is the number of bytes per Batch Data
|
|
||||||
* @param tagCount is the number of tags
|
|
||||||
* @return a random, signed CompleteBatch containing random data, tags and timestamp
|
|
||||||
* @throws SignatureException if an error occurs while signing the batch
|
|
||||||
*/
|
|
||||||
public CompleteBatch generateRandomBatch(BatchDigitalSignature signer, int batchId, int dataCount, int dataSize, int tagCount) throws SignatureException {
|
|
||||||
return generateRandomBatch(signer, batchId, dataCount, dataSize, tagCount, new LinkedList<String>());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
package meerkat.util;
|
package meerkat.util;
|
||||||
|
|
||||||
|
import com.google.protobuf.ByteString;
|
||||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -104,4 +106,150 @@ public class BulletinBoardUtils {
|
||||||
return new java.sql.Timestamp(protoTimestamp.getSeconds() * 1000 + protoTimestamp.getNanos() / 1000000);
|
return new java.sql.Timestamp(protoTimestamp.getSeconds() * 1000 + protoTimestamp.getNanos() / 1000000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Breaks up a bulletin board message into chunks
|
||||||
|
* @param msg is the complete message
|
||||||
|
* @return a list of BatchChunks that contains the raw message data
|
||||||
|
*/
|
||||||
|
public static List<BatchChunk> breakToBatch(BulletinBoardMessage msg, int chunkSize) {
|
||||||
|
|
||||||
|
byte[] data = msg.getMsg().getData().toByteArray();
|
||||||
|
|
||||||
|
int chunkNum = data.length / chunkSize;
|
||||||
|
if (data.length % chunkSize != 0)
|
||||||
|
chunkNum++;
|
||||||
|
|
||||||
|
List<BatchChunk> chunkList = new ArrayList<>(chunkNum);
|
||||||
|
|
||||||
|
int location = 0;
|
||||||
|
|
||||||
|
for (int i=0 ; i < chunkNum ; i++) {
|
||||||
|
|
||||||
|
int chunkLength;
|
||||||
|
|
||||||
|
if (i == chunkNum - 1){
|
||||||
|
chunkLength = data.length % chunkSize;
|
||||||
|
if (chunkLength == 0){
|
||||||
|
chunkLength = chunkSize;
|
||||||
|
}
|
||||||
|
} else{
|
||||||
|
chunkLength = chunkSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
chunkList.add(BatchChunk.newBuilder()
|
||||||
|
.setData(ByteString.copyFrom(data, location, chunkLength))
|
||||||
|
.build());
|
||||||
|
|
||||||
|
location += chunkLength;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return chunkList;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes concrete data from the message and turns it into a stub
|
||||||
|
* @param msg is the original message
|
||||||
|
* @return the message stub
|
||||||
|
*/
|
||||||
|
public static BulletinBoardMessage makeStub(BulletinBoardMessage msg) {
|
||||||
|
|
||||||
|
return BulletinBoardMessage.newBuilder()
|
||||||
|
.mergeFrom(msg)
|
||||||
|
.setMsg(UnsignedBulletinBoardMessage.newBuilder().
|
||||||
|
mergeFrom(msg.getMsg())
|
||||||
|
.setIsStub(true)
|
||||||
|
.clearData()
|
||||||
|
.build())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merges a batch chunk list back into a message stub to create a complete Bulletin Board message
|
||||||
|
* @param msgStub is a message stub
|
||||||
|
* @param chunkList contains the (ordered) data of the batch message
|
||||||
|
* @return a complete message containing both data and metadata
|
||||||
|
*/
|
||||||
|
public static BulletinBoardMessage gatherBatch(BulletinBoardMessage msgStub, List<BatchChunk> chunkList) {
|
||||||
|
|
||||||
|
List<ByteString> dataList = new LinkedList<>();
|
||||||
|
|
||||||
|
for (BatchChunk chunk : chunkList){
|
||||||
|
dataList.add(chunk.getData());
|
||||||
|
}
|
||||||
|
|
||||||
|
return BulletinBoardMessage.newBuilder()
|
||||||
|
.mergeFrom(msgStub)
|
||||||
|
.setMsg(UnsignedBulletinBoardMessage.newBuilder().
|
||||||
|
mergeFrom(msgStub.getMsg())
|
||||||
|
.setIsStub(false)
|
||||||
|
.setData(ByteString.copyFrom(dataList))
|
||||||
|
.build())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gerenates a BeginBatchMessage Protobuf which is used to begin uploading a message as a batch
|
||||||
|
* @param signerId is the ID of the message generator
|
||||||
|
* @param batchId is the identifier for the message
|
||||||
|
* @param msg is the Bulletin Board message to be uploaded, which can be a stub or a complete message
|
||||||
|
* @return the required BeginBatchMessage
|
||||||
|
*/
|
||||||
|
public static BeginBatchMessage generateBeginBatchMessage(ByteString signerId, int batchId, BulletinBoardMessage msg) {
|
||||||
|
|
||||||
|
if (msg.getSigCount() <= 0){
|
||||||
|
throw new IllegalArgumentException("No signatures found");
|
||||||
|
}
|
||||||
|
|
||||||
|
return BeginBatchMessage.newBuilder()
|
||||||
|
.setSignerId(signerId)
|
||||||
|
.setBatchId(batchId)
|
||||||
|
.addAllTag(msg.getMsg().getTagList())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gerenates a BeginBatchMessage Protobuf which is used to begin uploading a message as a batch
|
||||||
|
* @param batchId is the identifier for the message
|
||||||
|
* @param msg is the Bulletin Board message to be uploaded, which can be a stub or a complete message
|
||||||
|
* The signer ID used will be the one from the first signature in the message
|
||||||
|
* @return the required BeginBatchMessage
|
||||||
|
* @throws IllegalArgumentException if the message contains no signatures
|
||||||
|
*/
|
||||||
|
public static BeginBatchMessage generateBeginBatchMessage(int batchId, BulletinBoardMessage msg) throws IllegalArgumentException {
|
||||||
|
|
||||||
|
if (msg.getSigCount() <= 0){
|
||||||
|
throw new IllegalArgumentException("No signatures found");
|
||||||
|
}
|
||||||
|
|
||||||
|
return generateBeginBatchMessage(msg.getSig(0).getSignerId(), batchId, msg);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gerenates a CloseBatchMessage Protobuf which is used to finalize a batch message
|
||||||
|
* @param batchId is the identifier for the message
|
||||||
|
* @param batchLength is the number of chunks in the batch
|
||||||
|
* @param msg is the Bulletin Board message that was uploaded (and can also be a stub of said message)
|
||||||
|
* @throws IllegalArgumentException if the message contains no signatures
|
||||||
|
*/
|
||||||
|
public static CloseBatchMessage generateCloseBatchMessage(int batchId, int batchLength, BulletinBoardMessage msg) {
|
||||||
|
|
||||||
|
if (msg.getSigCount() <= 0){
|
||||||
|
throw new IllegalArgumentException("No signatures found");
|
||||||
|
}
|
||||||
|
|
||||||
|
return CloseBatchMessage.newBuilder()
|
||||||
|
.setTimestamp(msg.getMsg().getTimestamp())
|
||||||
|
.setBatchLength(batchLength)
|
||||||
|
.setBatchId(batchId)
|
||||||
|
.addAllSig(msg.getSigList())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,14 +7,6 @@ option java_package = "meerkat.protobuf";
|
||||||
import 'meerkat/crypto.proto';
|
import 'meerkat/crypto.proto';
|
||||||
import 'google/protobuf/timestamp.proto';
|
import 'google/protobuf/timestamp.proto';
|
||||||
|
|
||||||
message BoolMsg {
|
|
||||||
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.
|
||||||
// Note that it is assumed that this ID is a function of the message itself.
|
// Note that it is assumed that this ID is a function of the message itself.
|
||||||
|
@ -28,15 +20,19 @@ message UnsignedBulletinBoardMessage {
|
||||||
// Timestamp of the message (as defined by client)
|
// Timestamp of the message (as defined by client)
|
||||||
google.protobuf.Timestamp timestamp = 2;
|
google.protobuf.Timestamp timestamp = 2;
|
||||||
|
|
||||||
|
// Defines whether or not the message is a stub
|
||||||
|
// If the message is a stub: the data is ignored
|
||||||
|
bool isStub = 3;
|
||||||
|
|
||||||
// The actual content of the message
|
// The actual content of the message
|
||||||
bytes data = 3;
|
bytes data = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
message BulletinBoardMessage {
|
message BulletinBoardMessage {
|
||||||
|
|
||||||
// Serial entry number of message in database
|
// Serial entry number of message in database
|
||||||
int64 entryNum = 1;
|
int64 entryNum = 1;
|
||||||
|
|
||||||
// Unsigned raw data of message
|
// Unsigned raw data of message
|
||||||
UnsignedBulletinBoardMessage msg = 2;
|
UnsignedBulletinBoardMessage msg = 2;
|
||||||
|
|
||||||
|
@ -90,7 +86,7 @@ message MessageFilterList {
|
||||||
|
|
||||||
// This message is used to start a batch transfer to the Bulletin Board Server
|
// This message is used to start a batch transfer to the Bulletin Board Server
|
||||||
message BeginBatchMessage {
|
message BeginBatchMessage {
|
||||||
bytes signerId = 1; // Unique signer identifier
|
bytes signerId = 1; // Unique identifier of the main signer of the message
|
||||||
int32 batchId = 2; // Unique identifier for the batch (unique per signer)
|
int32 batchId = 2; // Unique identifier for the batch (unique per signer)
|
||||||
repeated string tag = 3; // Tags for the batch message
|
repeated string tag = 3; // Tags for the batch message
|
||||||
}
|
}
|
||||||
|
@ -100,17 +96,17 @@ message CloseBatchMessage {
|
||||||
int32 batchId = 1; // Unique identifier for the batch (unique per signer)
|
int32 batchId = 1; // Unique identifier for the batch (unique per signer)
|
||||||
int32 batchLength = 2; // Number of messages in the batch
|
int32 batchLength = 2; // Number of messages in the batch
|
||||||
google.protobuf.Timestamp timestamp = 3; // Timestamp of the batch (as defined by client)
|
google.protobuf.Timestamp timestamp = 3; // Timestamp of the batch (as defined by client)
|
||||||
meerkat.Signature sig = 4; // Signature on the (ordered) batch messages
|
repeated meerkat.Signature sig = 4; // Signature on the (ordered) batch messages ; First signature belongs to main signer
|
||||||
}
|
}
|
||||||
|
|
||||||
// Container for single batch message data
|
// Container for single chunk of abatch message
|
||||||
message BatchData {
|
message BatchChunk {
|
||||||
bytes data = 1;
|
bytes data = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// List of BatchData; Only used for testing
|
// List of BatchChunk; Only used for testing
|
||||||
message BatchDataList {
|
message BatchChunkList {
|
||||||
repeated BatchData data = 1;
|
repeated BatchChunk data = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// These messages comprise a batch message
|
// These messages comprise a batch message
|
||||||
|
@ -118,14 +114,7 @@ message BatchMessage {
|
||||||
bytes signerId = 1; // Unique signer identifier
|
bytes signerId = 1; // Unique signer identifier
|
||||||
int32 batchId = 2; // Unique identifier for the batch (unique per signer)
|
int32 batchId = 2; // Unique identifier for the batch (unique per signer)
|
||||||
int32 serialNum = 3; // Location of the message in the batch: starting from 0
|
int32 serialNum = 3; // Location of the message in the batch: starting from 0
|
||||||
BatchData data = 4; // Actual data
|
BatchChunk data = 4; // Actual data
|
||||||
}
|
|
||||||
|
|
||||||
// This message defines which batch to read and from which location to start reading
|
|
||||||
message BatchSpecificationMessage {
|
|
||||||
bytes signerId = 1; // Unique signer identifier
|
|
||||||
int32 batchId = 2; // Unique identifier for the batch (unique per signer)
|
|
||||||
int32 startPosition = 3; // Position in batch to start reading from
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This message is used to define a single query to the server to ascertain whether or not the server is synched with the client
|
// This message is used to define a single query to the server to ascertain whether or not the server is synched with the client
|
||||||
|
@ -168,4 +157,15 @@ message SyncQueryResponse {
|
||||||
// Largest value of timestamp for which the checksums match
|
// Largest value of timestamp for which the checksums match
|
||||||
google.protobuf.Timestamp lastTimeOfSync = 2;
|
google.protobuf.Timestamp lastTimeOfSync = 2;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// This message defines a query for retrieval of batch data
|
||||||
|
message BatchQuery {
|
||||||
|
|
||||||
|
// The unique message ID if the batch
|
||||||
|
MessageID msgID = 1;
|
||||||
|
|
||||||
|
// The first chunk to retrieve (0 is the first chunk)
|
||||||
|
int32 startPosition = 2;
|
||||||
|
|
||||||
}
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package meerkat;
|
||||||
|
|
||||||
|
option java_package = "meerkat.protobuf";
|
||||||
|
|
||||||
|
// Container for scanned data
|
||||||
|
message ScannedData {
|
||||||
|
bytes data = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Container for error messages
|
||||||
|
message ErrorMsg {
|
||||||
|
string msg = 1;
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package meerkat;
|
||||||
|
|
||||||
|
option java_package = "meerkat.protobuf";
|
||||||
|
|
||||||
|
message BroadcastMessage {
|
||||||
|
int32 sender = 1;
|
||||||
|
int32 destination = 2;
|
||||||
|
bool is_private = 3;
|
||||||
|
|
||||||
|
bytes payload = 5;
|
||||||
|
}
|
|
@ -0,0 +1,112 @@
|
||||||
|
package meerkat.bulletinboard;
|
||||||
|
|
||||||
|
import com.google.protobuf.ByteString;
|
||||||
|
import meerkat.crypto.concrete.ECDSASignature;
|
||||||
|
import meerkat.crypto.concrete.SHA256Digest;
|
||||||
|
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||||
|
import meerkat.util.BulletinBoardMessageGenerator;
|
||||||
|
import meerkat.util.BulletinBoardUtils;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.security.*;
|
||||||
|
import java.security.cert.CertificateException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Arbel Deutsch Peled on 16-Jun-16.
|
||||||
|
*/
|
||||||
|
public class BulletinBoardDigestTest {
|
||||||
|
|
||||||
|
private static String KEYFILE_EXAMPLE = "/certs/enduser-certs/user1-key-with-password-secret.p12";
|
||||||
|
private static String KEYFILE_EXAMPLE3 = "/certs/enduser-certs/user3-key-with-password-shh.p12";
|
||||||
|
|
||||||
|
private static String KEYFILE_PASSWORD1 = "secret";
|
||||||
|
private static String KEYFILE_PASSWORD3 = "shh";
|
||||||
|
|
||||||
|
private GenericBulletinBoardSignature[] signers;
|
||||||
|
private ByteString[] signerIDs;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void init() {
|
||||||
|
|
||||||
|
signers = new GenericBulletinBoardSignature[2];
|
||||||
|
signerIDs = new ByteString[signers.length];
|
||||||
|
signers[0] = new GenericBulletinBoardSignature(new ECDSASignature());
|
||||||
|
signers[1] = new GenericBulletinBoardSignature(new ECDSASignature());
|
||||||
|
|
||||||
|
InputStream keyStream = getClass().getResourceAsStream(KEYFILE_EXAMPLE);
|
||||||
|
char[] password = KEYFILE_PASSWORD1.toCharArray();
|
||||||
|
|
||||||
|
KeyStore.Builder keyStoreBuilder = null;
|
||||||
|
try {
|
||||||
|
keyStoreBuilder = signers[0].getPKCS12KeyStoreBuilder(keyStream, password);
|
||||||
|
|
||||||
|
signers[0].loadSigningCertificate(keyStoreBuilder);
|
||||||
|
|
||||||
|
keyStream = getClass().getResourceAsStream(KEYFILE_EXAMPLE3);
|
||||||
|
password = KEYFILE_PASSWORD3.toCharArray();
|
||||||
|
|
||||||
|
keyStoreBuilder = signers[1].getPKCS12KeyStoreBuilder(keyStream, password);
|
||||||
|
signers[1].loadSigningCertificate(keyStoreBuilder);
|
||||||
|
|
||||||
|
for (int i = 0 ; i < signers.length ; i++) {
|
||||||
|
signerIDs[i] = signers[i].getSignerID();
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
System.err.println("Failed reading from signature file " + e.getMessage());
|
||||||
|
fail("Failed reading from signature file " + e.getMessage());
|
||||||
|
} catch (CertificateException e) {
|
||||||
|
System.err.println("Failed reading certificate " + e.getMessage());
|
||||||
|
fail("Failed reading certificate " + e.getMessage());
|
||||||
|
} catch (KeyStoreException e) {
|
||||||
|
System.err.println("Failed reading keystore " + e.getMessage());
|
||||||
|
fail("Failed reading keystore " + e.getMessage());
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
System.err.println("Couldn't find signing algorithm " + e.getMessage());
|
||||||
|
fail("Couldn't find signing algorithm " + e.getMessage());
|
||||||
|
} catch (UnrecoverableKeyException e) {
|
||||||
|
System.err.println("Couldn't find signing key " + e.getMessage());
|
||||||
|
fail("Couldn't find signing key " + e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBatchDigest() throws SignatureException {
|
||||||
|
|
||||||
|
final int MESSAGE_SIZE = 100;
|
||||||
|
final int CHUNK_SIZE = 10;
|
||||||
|
final int TAG_NUM = 10;
|
||||||
|
|
||||||
|
BulletinBoardMessageGenerator generator = new BulletinBoardMessageGenerator(new Random(0));
|
||||||
|
|
||||||
|
BulletinBoardMessage completeMessage = generator.generateRandomMessage(signers, MESSAGE_SIZE, TAG_NUM);
|
||||||
|
|
||||||
|
BulletinBoardMessage stub = BulletinBoardUtils.makeStub(completeMessage);
|
||||||
|
List<BatchChunk> batchChunks = BulletinBoardUtils.breakToBatch(completeMessage, CHUNK_SIZE);
|
||||||
|
|
||||||
|
BulletinBoardDigest digest = new GenericBulletinBoardDigest(new SHA256Digest());
|
||||||
|
|
||||||
|
digest.update(completeMessage);
|
||||||
|
MessageID id1 = digest.digestAsMessageID();
|
||||||
|
|
||||||
|
digest.update(stub);
|
||||||
|
for (BatchChunk batchChunk : batchChunks){
|
||||||
|
digest.update(batchChunk.getData().toByteArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageID id2 = digest.digestAsMessageID();
|
||||||
|
|
||||||
|
assertThat("Digests not equal!", id1.getID().equals(id2.getID()));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
package meerkat.comm;
|
||||||
|
|
||||||
|
import com.google.protobuf.Message;
|
||||||
|
import com.google.protobuf.TextFormat;
|
||||||
|
import meerkat.protobuf.Comm;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Queue;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
import java.util.concurrent.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Tzlil on 2/14/2016.
|
||||||
|
*/
|
||||||
|
// TODO: Change nane to network
|
||||||
|
|
||||||
|
public class ChannelImpl {
|
||||||
|
final Logger logger = LoggerFactory.getLogger(getClass());
|
||||||
|
|
||||||
|
//private ExecutorService executorService = Executors.newCachedThreadPool();
|
||||||
|
public static int BROADCAST = 0;
|
||||||
|
Map<Integer,SingleChannel> channels = new TreeMap<>();
|
||||||
|
|
||||||
|
|
||||||
|
public ChannelImpl() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public Channel getChannel(int id) {
|
||||||
|
return new SingleChannel(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SingleChannel implements Channel {
|
||||||
|
protected final int id;
|
||||||
|
|
||||||
|
ReceiverCallback callback;
|
||||||
|
|
||||||
|
SingleChannel(int id) {
|
||||||
|
this.id = id;
|
||||||
|
channels.put(id, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSourceId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendMessage(int destUser, Message msg) {
|
||||||
|
if (destUser < 1)
|
||||||
|
return;
|
||||||
|
SingleChannel channel = channels.get(destUser);
|
||||||
|
if (channel == null) {
|
||||||
|
logger.warn("Party {} attempting to send message to non-existing party {}", getSourceId(), destUser);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Comm.BroadcastMessage broadcastMessage = Comm.BroadcastMessage.newBuilder()
|
||||||
|
.setSender(id)
|
||||||
|
.setDestination(destUser)
|
||||||
|
.setIsPrivate(true)
|
||||||
|
.setPayload(msg.toByteString())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
logger.debug("sending Message: Dst={},Src={} [{}]", broadcastMessage.getDestination(),
|
||||||
|
broadcastMessage.getSender(), TextFormat.printToString(msg));
|
||||||
|
|
||||||
|
channel.callback.receiveMessage(broadcastMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void broadcastMessage(Message msg) {
|
||||||
|
Comm.BroadcastMessage broadcastMessage = Comm.BroadcastMessage.newBuilder()
|
||||||
|
.setSender(id)
|
||||||
|
.setDestination(BROADCAST)
|
||||||
|
.setIsPrivate(false)
|
||||||
|
.setPayload(msg.toByteString())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
logger.debug("broadcasting Message: Src={} [{}]",
|
||||||
|
broadcastMessage.getSender(), TextFormat.printToString(msg));
|
||||||
|
|
||||||
|
for (SingleChannel channel : channels.values()) {
|
||||||
|
channel.callback.receiveMessage(broadcastMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void registerReceiverCallback(final ReceiverCallback callback) {
|
||||||
|
this.callback = callback;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -3,7 +3,6 @@ package meerkat.crypto.concrete;
|
||||||
import com.google.protobuf.ByteString;
|
import com.google.protobuf.ByteString;
|
||||||
import com.google.protobuf.Message;
|
import com.google.protobuf.Message;
|
||||||
|
|
||||||
import meerkat.crypto.concrete.ECDSASignature;
|
|
||||||
import meerkat.protobuf.Crypto;
|
import meerkat.protobuf.Crypto;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,6 @@ package meerkat.crypto.concrete;
|
||||||
import com.google.protobuf.ByteString;
|
import com.google.protobuf.ByteString;
|
||||||
import com.google.protobuf.Message;
|
import com.google.protobuf.Message;
|
||||||
import meerkat.protobuf.Crypto;
|
import meerkat.protobuf.Crypto;
|
||||||
import meerkat.crypto.concrete.ECDSASignature;
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
|
|
@ -41,6 +41,14 @@ version += "${isSnapshot ? '-SNAPSHOT' : ''}"
|
||||||
dependencies {
|
dependencies {
|
||||||
// Meerkat common
|
// Meerkat common
|
||||||
compile project(':meerkat-common')
|
compile project(':meerkat-common')
|
||||||
|
compile project(':restful-api-common')
|
||||||
|
|
||||||
|
// Jersey for RESTful API
|
||||||
|
compile 'org.glassfish.jersey.containers:jersey-container-servlet:2.5.+'
|
||||||
|
|
||||||
|
// Servlets
|
||||||
|
compile 'org.eclipse.jetty:jetty-server:9.3.+'
|
||||||
|
compile 'org.eclipse.jetty:jetty-servlet:9.3.+'
|
||||||
|
|
||||||
// Logging
|
// Logging
|
||||||
compile 'org.slf4j:slf4j-api:1.7.7'
|
compile 'org.slf4j:slf4j-api:1.7.7'
|
||||||
|
|
|
@ -0,0 +1,88 @@
|
||||||
|
package meerkat.pollingstation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Arbel on 5/31/2016.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import com.google.common.util.concurrent.FutureCallback;
|
||||||
|
import com.google.protobuf.BoolValue;
|
||||||
|
import meerkat.protobuf.PollingStation;
|
||||||
|
|
||||||
|
import javax.annotation.PostConstruct;
|
||||||
|
import javax.servlet.ServletContext;
|
||||||
|
import javax.ws.rs.Consumes;
|
||||||
|
import javax.ws.rs.POST;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.Produces;
|
||||||
|
import javax.ws.rs.core.Context;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import static meerkat.pollingstation.PollingStationConstants.POLLING_STATION_WEB_SCANNER_ERROR_PATH;
|
||||||
|
import static meerkat.pollingstation.PollingStationConstants.POLLING_STATION_WEB_SCANNER_SCAN_PATH;
|
||||||
|
import static meerkat.rest.Constants.MEDIATYPE_PROTOBUF;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements a Web-App interface for {@link meerkat.pollingstation.PollingStationScanner.Producer}
|
||||||
|
* This class depends on {@link meerkat.pollingstation.PollingStationWebScanner} and works in conjunction with it
|
||||||
|
*/
|
||||||
|
@Path("/")
|
||||||
|
public class PollingStationScannerWebApp implements PollingStationScanner.Producer {
|
||||||
|
|
||||||
|
@Context
|
||||||
|
ServletContext servletContext;
|
||||||
|
|
||||||
|
Iterable<FutureCallback<PollingStation.ScannedData>> callbacks;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called by the Jetty engine when instantiating the servlet
|
||||||
|
*/
|
||||||
|
@PostConstruct
|
||||||
|
public void init() throws Exception{
|
||||||
|
callbacks = (Iterable<FutureCallback<PollingStation.ScannedData>>) servletContext.getAttribute(PollingStationWebScanner.CALLBACKS_ATTRIBUTE_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path(POLLING_STATION_WEB_SCANNER_SCAN_PATH)
|
||||||
|
@Consumes(MEDIATYPE_PROTOBUF)
|
||||||
|
@Produces(MEDIATYPE_PROTOBUF)
|
||||||
|
@Override
|
||||||
|
public BoolValue newScan(PollingStation.ScannedData scannedData) {
|
||||||
|
|
||||||
|
boolean handled = false;
|
||||||
|
|
||||||
|
for (FutureCallback<PollingStation.ScannedData> callback : callbacks){
|
||||||
|
|
||||||
|
callback.onSuccess(scannedData);
|
||||||
|
handled = true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return BoolValue.newBuilder()
|
||||||
|
.setValue(handled)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path(POLLING_STATION_WEB_SCANNER_ERROR_PATH)
|
||||||
|
@Consumes(MEDIATYPE_PROTOBUF)
|
||||||
|
@Produces(MEDIATYPE_PROTOBUF)
|
||||||
|
@Override
|
||||||
|
public BoolValue reportScanError(PollingStation.ErrorMsg errorMsg) {
|
||||||
|
|
||||||
|
boolean handled = false;
|
||||||
|
|
||||||
|
for (FutureCallback<PollingStation.ScannedData> callback : callbacks){
|
||||||
|
|
||||||
|
callback.onFailure(new IOException(errorMsg.getMsg()));
|
||||||
|
handled = true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return BoolValue.newBuilder()
|
||||||
|
.setValue(handled)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
package meerkat.pollingstation;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
|
||||||
|
import com.google.common.util.concurrent.FutureCallback;
|
||||||
|
import org.eclipse.jetty.server.Server;
|
||||||
|
import org.eclipse.jetty.servlet.*;
|
||||||
|
|
||||||
|
import org.glassfish.jersey.servlet.ServletContainer;
|
||||||
|
import org.glassfish.jersey.server.ResourceConfig;
|
||||||
|
|
||||||
|
import meerkat.protobuf.PollingStation.ScannedData;
|
||||||
|
import meerkat.rest.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Arbel on 05/05/2016.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class PollingStationWebScanner implements PollingStationScanner.Consumer{
|
||||||
|
|
||||||
|
public final static String CALLBACKS_ATTRIBUTE_NAME = "callbacks";
|
||||||
|
|
||||||
|
private final Server server;
|
||||||
|
private final List<FutureCallback<ScannedData>> callbacks;
|
||||||
|
|
||||||
|
public PollingStationWebScanner(int port, String subAddress) {
|
||||||
|
|
||||||
|
callbacks = new LinkedList<>();
|
||||||
|
|
||||||
|
server = new Server(port);
|
||||||
|
|
||||||
|
ServletContextHandler servletContextHandler = new ServletContextHandler(server, subAddress);
|
||||||
|
servletContextHandler.setAttribute(CALLBACKS_ATTRIBUTE_NAME, (Iterable<FutureCallback<ScannedData>>) callbacks);
|
||||||
|
|
||||||
|
ResourceConfig resourceConfig = new ResourceConfig(PollingStationScannerWebApp.class);
|
||||||
|
resourceConfig.register(ProtobufMessageBodyReader.class);
|
||||||
|
resourceConfig.register(ProtobufMessageBodyWriter.class);
|
||||||
|
|
||||||
|
ServletHolder servletHolder = new ServletHolder(new ServletContainer(resourceConfig));
|
||||||
|
|
||||||
|
servletContextHandler.addServlet(servletHolder, "/*");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start() throws Exception {
|
||||||
|
server.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stop() throws Exception {
|
||||||
|
server.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void subscribe(FutureCallback<ScannedData> scanCallback) {
|
||||||
|
callbacks.add(scanCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,162 @@
|
||||||
|
package meerkat.pollingstation;
|
||||||
|
|
||||||
|
import com.google.common.util.concurrent.FutureCallback;
|
||||||
|
import com.google.protobuf.ByteString;
|
||||||
|
import meerkat.protobuf.PollingStation.*;
|
||||||
|
import meerkat.rest.Constants;
|
||||||
|
|
||||||
|
import meerkat.rest.*;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import javax.ws.rs.client.Client;
|
||||||
|
import javax.ws.rs.client.ClientBuilder;
|
||||||
|
import javax.ws.rs.client.Entity;
|
||||||
|
import javax.ws.rs.client.WebTarget;
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
|
||||||
|
import java.util.concurrent.Semaphore;
|
||||||
|
|
||||||
|
import static meerkat.pollingstation.PollingStationConstants.*;
|
||||||
|
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Arbel on 25/05/2016.
|
||||||
|
*/
|
||||||
|
public class PollingStationWebScannerTest {
|
||||||
|
|
||||||
|
private PollingStationScanner.Consumer scanner;
|
||||||
|
private static final String ADDRESS = "http://localhost";
|
||||||
|
private static final String SUB_ADDRESS = "";
|
||||||
|
private static final int PORT = 8080;
|
||||||
|
|
||||||
|
private Semaphore semaphore;
|
||||||
|
private Throwable thrown;
|
||||||
|
private boolean dataIsAsExpected;
|
||||||
|
|
||||||
|
private class ScanHandler implements FutureCallback<ScannedData> {
|
||||||
|
|
||||||
|
private final ScannedData expectedData;
|
||||||
|
|
||||||
|
public ScanHandler(ScannedData expectedData) {
|
||||||
|
this.expectedData = expectedData;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSuccess(ScannedData result) {
|
||||||
|
dataIsAsExpected = result.getData().equals(expectedData.getData());
|
||||||
|
semaphore.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(Throwable t) {
|
||||||
|
dataIsAsExpected = false;
|
||||||
|
thrown = t;
|
||||||
|
semaphore.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ErrorHandler implements FutureCallback<ScannedData> {
|
||||||
|
|
||||||
|
private final String expectedErrorMessage;
|
||||||
|
|
||||||
|
public ErrorHandler(String expectedErrorMessage) {
|
||||||
|
this.expectedErrorMessage = expectedErrorMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSuccess(ScannedData result) {
|
||||||
|
dataIsAsExpected = false;
|
||||||
|
semaphore.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(Throwable t) {
|
||||||
|
dataIsAsExpected = t.getMessage().equals(expectedErrorMessage);
|
||||||
|
semaphore.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void init() {
|
||||||
|
|
||||||
|
System.err.println("Setting up Scanner WebApp!");
|
||||||
|
|
||||||
|
scanner = new PollingStationWebScanner(PORT, SUB_ADDRESS);
|
||||||
|
|
||||||
|
semaphore = new Semaphore(0);
|
||||||
|
thrown = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
scanner.start();
|
||||||
|
} catch (Exception e) {
|
||||||
|
assertThat("Could not start server: " + e.getMessage(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSuccessfulScan() throws InterruptedException {
|
||||||
|
|
||||||
|
Client client = ClientBuilder.newClient();
|
||||||
|
client.register(ProtobufMessageBodyReader.class);
|
||||||
|
client.register(ProtobufMessageBodyWriter.class);
|
||||||
|
WebTarget webTarget = client.target(ADDRESS + ":" + PORT)
|
||||||
|
.path(SUB_ADDRESS).path(POLLING_STATION_WEB_SCANNER_SCAN_PATH);
|
||||||
|
|
||||||
|
byte[] data = {(byte) 1, (byte) 2};
|
||||||
|
|
||||||
|
ScannedData scannedData = ScannedData.newBuilder()
|
||||||
|
.setData(ByteString.copyFrom(data))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
scanner.subscribe(new ScanHandler(scannedData));
|
||||||
|
|
||||||
|
Response response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(scannedData, Constants.MEDIATYPE_PROTOBUF));
|
||||||
|
response.close();
|
||||||
|
|
||||||
|
semaphore.acquire();
|
||||||
|
assertThat("Scanner has thrown an error", thrown == null);
|
||||||
|
assertThat("Scanned data received was incorrect", dataIsAsExpected);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testErroneousScan() throws InterruptedException {
|
||||||
|
|
||||||
|
Client client = ClientBuilder.newClient();
|
||||||
|
client.register(ProtobufMessageBodyReader.class);
|
||||||
|
client.register(ProtobufMessageBodyWriter.class);
|
||||||
|
WebTarget webTarget = client.target(ADDRESS + ":" + PORT)
|
||||||
|
.path(SUB_ADDRESS).path(POLLING_STATION_WEB_SCANNER_ERROR_PATH);
|
||||||
|
|
||||||
|
ErrorMsg errorMsg = ErrorMsg.newBuilder()
|
||||||
|
.setMsg("!Error Message!")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
scanner.subscribe(new ErrorHandler(errorMsg.getMsg()));
|
||||||
|
|
||||||
|
Response response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(errorMsg, Constants.MEDIATYPE_PROTOBUF));
|
||||||
|
response.close();
|
||||||
|
|
||||||
|
semaphore.acquire();
|
||||||
|
assertThat("Scanner error received was incorrect", dataIsAsExpected);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void close() {
|
||||||
|
|
||||||
|
System.err.println("Scanner WebApp shutting down...");
|
||||||
|
|
||||||
|
try {
|
||||||
|
scanner.stop();
|
||||||
|
} catch (Exception e) {
|
||||||
|
assertThat("Could not stop server: " + e.getMessage(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -4,4 +4,5 @@ include 'bulletin-board-server'
|
||||||
include 'polling-station'
|
include 'polling-station'
|
||||||
include 'restful-api-common'
|
include 'restful-api-common'
|
||||||
include 'bulletin-board-client'
|
include 'bulletin-board-client'
|
||||||
|
include 'distributed-key-generation'
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue