Defined semi-final versions of the batch interfaces.

Implemented in part extended BB Server interface.
Added Digest support for Batch messages.
Made GlobalCryptoSetup a final singleton.
Bulletin-Board-Batch
Arbel Deutsch Peled 2015-12-19 19:54:50 +02:00
parent 37fdc0bb83
commit 37f962d520
24 changed files with 516 additions and 102 deletions

View File

@ -117,6 +117,11 @@ public class ThreadedBulletinBoardClient extends SimpleBulletinBoardClient imple
// TODO: Implement
}
@Override
public void subscribe(MessageFilterList filterList, MessageHandler messageHandler) {
// TODO: Implement
}
@Override
public void close() {
super.close();

View File

@ -1,14 +1,8 @@
package meerkat.bulletinboard.callbacks;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListeningExecutorService;
import meerkat.bulletinboard.BulletinClientJob;
import meerkat.bulletinboard.BulletinClientJobResult;
import meerkat.bulletinboard.BulletinClientWorker;
import meerkat.protobuf.BulletinBoardAPI;
import java.util.List;
/**
* This is a future callback used to listen to workers and run on job finish

View File

@ -1,7 +1,7 @@
package meerkat.bulletinboard.callbacks;
import com.google.common.util.concurrent.ListeningExecutorService;
import meerkat.bulletinboard.BulletinBoardClient;
import meerkat.bulletinboard.AsyncBulletinBoardClient.*;
import meerkat.bulletinboard.BulletinClientJobResult;
import meerkat.protobuf.BulletinBoardAPI.*;
@ -13,10 +13,10 @@ import java.util.List;
*/
public class GetRedundancyFutureCallback extends ClientFutureCallback {
private BulletinBoardClient.ClientCallback<Float> callback;
private ClientCallback<Float> callback;
public GetRedundancyFutureCallback(ListeningExecutorService listeningExecutor,
BulletinBoardClient.ClientCallback<Float> callback) {
ClientCallback<Float> callback) {
super(listeningExecutor);
this.callback = callback;
}

View File

@ -2,13 +2,11 @@ package meerkat.bulletinboard.callbacks;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListeningExecutorService;
import meerkat.bulletinboard.BulletinBoardClient;
import meerkat.bulletinboard.AsyncBulletinBoardClient.*;
import meerkat.bulletinboard.BulletinClientJob;
import meerkat.bulletinboard.BulletinClientJobResult;
import meerkat.bulletinboard.BulletinClientWorker;
import meerkat.protobuf.BulletinBoardAPI;
import java.util.List;
/**
* This is a future callback used to listen to workers and run on job finish
@ -16,10 +14,10 @@ import java.util.List;
*/
public class PostMessageFutureCallback extends ClientFutureCallback {
private BulletinBoardClient.ClientCallback<?> callback;
private ClientCallback<?> callback;
public PostMessageFutureCallback(ListeningExecutorService listeningExecutor,
BulletinBoardClient.ClientCallback<?> callback) {
ClientCallback<?> callback) {
super(listeningExecutor);
this.callback = callback;
}

View File

@ -1,11 +1,8 @@
package meerkat.bulletinboard.callbacks;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListeningExecutorService;
import meerkat.bulletinboard.BulletinBoardClient;
import meerkat.bulletinboard.BulletinClientJob;
import meerkat.bulletinboard.AsyncBulletinBoardClient.*;
import meerkat.bulletinboard.BulletinClientJobResult;
import meerkat.bulletinboard.BulletinClientWorker;
import meerkat.protobuf.BulletinBoardAPI;
import java.util.List;
@ -16,10 +13,10 @@ import java.util.List;
*/
public class ReadMessagesFutureCallback extends ClientFutureCallback {
private BulletinBoardClient.ClientCallback<List<BulletinBoardAPI.BulletinBoardMessage>> callback;
private ClientCallback<List<BulletinBoardAPI.BulletinBoardMessage>> callback;
public ReadMessagesFutureCallback(ListeningExecutorService listeningExecutor,
BulletinBoardClient.ClientCallback<List<BulletinBoardAPI.BulletinBoardMessage>> callback) {
ClientCallback<List<BulletinBoardAPI.BulletinBoardMessage>> callback) {
super(listeningExecutor);
this.callback = callback;
}

View File

@ -1,7 +1,6 @@
import com.google.protobuf.ByteString;
import meerkat.bulletinboard.AsyncBulletinBoardClient;
import meerkat.bulletinboard.BulletinBoardClient;
import meerkat.bulletinboard.BulletinBoardClient.ClientCallback;
import meerkat.bulletinboard.AsyncBulletinBoardClient.ClientCallback;
import meerkat.bulletinboard.ThreadedBulletinBoardClient;
import meerkat.protobuf.BulletinBoardAPI.*;
import meerkat.protobuf.Crypto;

View File

@ -1,20 +1,29 @@
package meerkat.bulletinboard.sqlserver;
import java.security.InvalidKeyException;
import java.security.cert.CertificateException;
import java.sql.*;
import java.util.*;
import com.google.protobuf.ByteString;
import com.google.protobuf.ProtocolStringList;
import meerkat.bulletinboard.BulletinBoardServer;
import meerkat.bulletinboard.sqlserver.mappers.EntryNumMapper;
import meerkat.bulletinboard.sqlserver.mappers.MessageMapper;
import meerkat.bulletinboard.sqlserver.mappers.SignatureMapper;
import meerkat.bulletinboard.sqlserver.mappers.*;
import meerkat.comm.CommunicationException;
import meerkat.crypto.BatchDigest;
import meerkat.crypto.DigitalSignature;
import meerkat.crypto.concrete.ECDSASignature;
import meerkat.crypto.concrete.SHA256Digest;
import meerkat.protobuf.BulletinBoardAPI.*;
import meerkat.protobuf.Crypto.Signature;
import meerkat.protobuf.Crypto.SignatureVerificationKey;
import meerkat.crypto.Digest;
import meerkat.crypto.concrete.SHA256Digest;
import static meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer.SQLQueryProvider.*;
import javax.sql.DataSource;
@ -23,6 +32,8 @@ import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
/**
* This is a generic SQL implementation of the BulletinBoardServer API.
*/
@ -46,7 +57,12 @@ public class BulletinBoardSQLServer implements BulletinBoardServer{
CONNECT_TAG(new String[] {"EntryNum","Tag"}),
ADD_SIGNATURE(new String[] {"EntryNum","SignerId","Signature"}),
GET_SIGNATURES(new String[] {"EntryNum"}),
GET_MESSAGES(new String[] {});
GET_MESSAGES(new String[] {}),
GET_BATCH_MESSAGE_ENTRY(new String[] {"SignerId", "BatchId"}),
CHECK_BATCH_LENGTH(new String[] {"SignerId", "BatchId"}),
GET_BATCH_MESSAGE_DATA(new String[] {"SignerId", "BatchId", "StartPosition"}),
INSERT_BATCH_DATA(new String[] {"SignerId", "BatchId", "SerialNum", "Data"}),
CONNECT_BATCH_TAG(new String[] {"SignerId", "BatchId", "Tag"});
private String[] paramNames;
@ -58,6 +74,10 @@ public class BulletinBoardSQLServer implements BulletinBoardServer{
return paramNames;
}
public String getParamName(int num) {
return paramNames[num];
}
}
/**
@ -152,6 +172,11 @@ public class BulletinBoardSQLServer implements BulletinBoardServer{
}
/**
* This method returns the value of the parameter specified in a message filter
* @param messageFilter is the filter
* @return the object parameter for the SQL query embedded in the filter (this depends on the filter type)
*/
private Object getParam(MessageFilter messageFilter) {
switch (messageFilter.getType()) {
@ -170,7 +195,7 @@ public class BulletinBoardSQLServer implements BulletinBoardServer{
case MAX_MESSAGES:
return messageFilter.getMaxMessages();
default:
default: // Unsupported filter type
return null;
}
@ -193,7 +218,8 @@ public class BulletinBoardSQLServer implements BulletinBoardServer{
protected NamedParameterJdbcTemplate jdbcTemplate;
protected Digest digest;
protected BatchDigest digest;
protected DigitalSignature signer;
protected List<SignatureVerificationKey> trusteeSignatureVerificationArray;
protected int minTrusteeSignatures;
@ -232,6 +258,7 @@ public class BulletinBoardSQLServer implements BulletinBoardServer{
// TODO write signature reading part.
digest = new SHA256Digest();
signer = new ECDSASignature();
jdbcTemplate = new NamedParameterJdbcTemplate(sqlQueryProvider.getDataSource());
@ -264,12 +291,12 @@ public class BulletinBoardSQLServer implements BulletinBoardServer{
String sql;
sql = sqlQueryProvider.getSQLString(SQLQueryProvider.QueryType.INSERT_NEW_TAG);
sql = sqlQueryProvider.getSQLString(QueryType.INSERT_NEW_TAG);
Map namedParameters[] = new HashMap[tags.length];
for (int i = 0 ; i < tags.length ; i++){
namedParameters[i] = new HashMap();
namedParameters[i].put("Tag", tags[i]);
namedParameters[i].put(QueryType.INSERT_NEW_TAG.getParamName(0), tags[i]);
}
jdbcTemplate.batchUpdate(sql, namedParameters);
@ -316,11 +343,11 @@ public class BulletinBoardSQLServer implements BulletinBoardServer{
// Add message to table if needed and store entry number of message.
sql = sqlQueryProvider.getSQLString(SQLQueryProvider.QueryType.FIND_MSG_ID);
sql = sqlQueryProvider.getSQLString(QueryType.FIND_MSG_ID);
Map namedParameters = new HashMap();
namedParameters.put("MsgId",msgID);
namedParameters.put(QueryType.FIND_MSG_ID.getParamName(0),msgID);
List<Long> entryNums = jdbcTemplate.query(sql, new MapSqlParameterSource(namedParameters), new EntryNumMapper());
List<Long> entryNums = jdbcTemplate.query(sql, new MapSqlParameterSource(namedParameters), new LongMapper());
if (entryNums.size() > 0){
@ -328,8 +355,8 @@ public class BulletinBoardSQLServer implements BulletinBoardServer{
} else{
sql = sqlQueryProvider.getSQLString(SQLQueryProvider.QueryType.INSERT_MSG);
namedParameters.put("Msg", msg.getMsg().toByteArray());
sql = sqlQueryProvider.getSQLString(QueryType.INSERT_MSG);
namedParameters.put(QueryType.INSERT_MSG.getParamName(0), msg.getMsg().toByteArray());
KeyHolder keyHolder = new GeneratedKeyHolder();
jdbcTemplate.update(sql,new MapSqlParameterSource(namedParameters),keyHolder);
@ -354,14 +381,14 @@ public class BulletinBoardSQLServer implements BulletinBoardServer{
// Connect message to tags.
sql = sqlQueryProvider.getSQLString(SQLQueryProvider.QueryType.CONNECT_TAG);
sql = sqlQueryProvider.getSQLString(QueryType.CONNECT_TAG);
namedParameterArray = new HashMap[tags.length];
for (int i = 0 ; i < tags.length ; i++) {
namedParameterArray[i] = new HashMap();
namedParameterArray[i].put("EntryNum", entryNum);
namedParameterArray[i].put("Tag", tags[i]);
namedParameterArray[i].put(QueryType.CONNECT_TAG.getParamName(0), entryNum);
namedParameterArray[i].put(QueryType.CONNECT_TAG.getParamName(1), tags[i]);
}
jdbcTemplate.batchUpdate(sql, namedParameterArray);
@ -374,15 +401,15 @@ public class BulletinBoardSQLServer implements BulletinBoardServer{
// Connect message to signatures.
sql = sqlQueryProvider.getSQLString(SQLQueryProvider.QueryType.ADD_SIGNATURE);
sql = sqlQueryProvider.getSQLString(QueryType.ADD_SIGNATURE);
namedParameterArray = new HashMap[signatures.length];
for (int i = 0 ; i < signatures.length ; i++) {
namedParameterArray[i] = new HashMap();
namedParameterArray[i].put("EntryNum", entryNum);
namedParameterArray[i].put("SignerId", signatures[i].getSignerId().toByteArray());
namedParameterArray[i].put("Signature", signatures[i].toByteArray());
namedParameterArray[i].put(QueryType.ADD_SIGNATURE.getParamName(0), entryNum);
namedParameterArray[i].put(QueryType.ADD_SIGNATURE.getParamName(1), signatures[i].getSignerId().toByteArray());
namedParameterArray[i].put(QueryType.ADD_SIGNATURE.getParamName(2), signatures[i].toByteArray());
}
jdbcTemplate.batchUpdate(sql,namedParameterArray);
@ -412,7 +439,7 @@ public class BulletinBoardSQLServer implements BulletinBoardServer{
// Check if Tag/Signature tables are required for filtering purposes
sqlBuilder.append(sqlQueryProvider.getSQLString(SQLQueryProvider.QueryType.GET_MESSAGES));
sqlBuilder.append(sqlQueryProvider.getSQLString(QueryType.GET_MESSAGES));
// Add conditions
namedParameters = new MapSqlParameterSource();
@ -434,7 +461,7 @@ public class BulletinBoardSQLServer implements BulletinBoardServer{
sqlBuilder.append(sqlQueryProvider.getCondition(filter.getType(), paramNum));
SQLQueryProvider.FilterTypeParam filterTypeParam = SQLQueryProvider.FilterTypeParam.getFilterTypeParamName(filter.getType());
FilterTypeParam filterTypeParam = FilterTypeParam.getFilterTypeParamName(filter.getType());
namedParameters.addValue(
filterTypeParam.getParamName() + Integer.toString(paramNum),
@ -457,10 +484,10 @@ public class BulletinBoardSQLServer implements BulletinBoardServer{
// Retrieve signatures
namedParameters = new MapSqlParameterSource();
namedParameters.addValue("EntryNum", msgBuilder.getEntryNum());
namedParameters.addValue(QueryType.GET_SIGNATURES.getParamName(0), msgBuilder.getEntryNum());
List<Signature> signatures = jdbcTemplate.query(
sqlQueryProvider.getSQLString(SQLQueryProvider.QueryType.GET_SIGNATURES),
sqlQueryProvider.getSQLString(QueryType.GET_SIGNATURES),
namedParameters,
signatureMapper);
@ -478,6 +505,130 @@ public class BulletinBoardSQLServer implements BulletinBoardServer{
}
/**
* This method checks if a specified batch exists and is already closed
* @param signerId is the ID of the publisher of the batch
* @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
*/
private boolean isBatchClosed(ByteString signerId, int batchId){
String sql = sqlQueryProvider.getSQLString(QueryType.GET_BATCH_MESSAGE_ENTRY);
MapSqlParameterSource namedParameters = new MapSqlParameterSource();
namedParameters.addValue(QueryType.GET_BATCH_MESSAGE_ENTRY.getParamName(0), signerId);
namedParameters.addValue(QueryType.GET_BATCH_MESSAGE_ENTRY.getParamName(1), batchId);
List<Long> result = jdbcTemplate.query(sql,namedParameters,new LongMapper());
return (result.size() > 0);
}
@Override
public BoolMsg beginBatch(BeginBatchMessage message) throws CommunicationException {
// Check if batch is closed
if (isBatchClosed(message.getSignerId(), message.getBatchId())) {
return BoolMsg.newBuilder().setValue(false).build();
}
// Add new tags to table
ProtocolStringList tagList = message.getTagList();
String[] tags = new String[tagList.size()];
tags = tagList.toArray(tags);
try {
insertNewTags(tags);
} catch (SQLException e) {
throw new CommunicationException(e.getMessage());
}
// Connect tags
String sql = sqlQueryProvider.getSQLString(QueryType.CONNECT_BATCH_TAG);
MapSqlParameterSource namedParameters[] = new MapSqlParameterSource[tags.length];
for (int i=0 ; i < tags.length ; i++) {
namedParameters[i] = new MapSqlParameterSource();
namedParameters[i].addValue(QueryType.CONNECT_BATCH_TAG.getParamName(0),message.getSignerId());
namedParameters[i].addValue(QueryType.CONNECT_BATCH_TAG.getParamName(1),message.getBatchId());
namedParameters[i].addValue(QueryType.CONNECT_BATCH_TAG.getParamName(2),tags[i]);
}
jdbcTemplate.batchUpdate(sql,namedParameters);
return BoolMsg.newBuilder().setValue(true).build();
}
@Override
public BoolMsg postBatchMessage(BatchMessage batchMessage) {
// Check if batch is closed
if (isBatchClosed(batchMessage.getSignerId(), batchMessage.getBatchId())) {
return BoolMsg.newBuilder().setValue(false).build();
}
// Add data
String sql = sqlQueryProvider.getSQLString(QueryType.INSERT_BATCH_DATA);
MapSqlParameterSource namedParameters = new MapSqlParameterSource();
namedParameters.addValue(QueryType.INSERT_BATCH_DATA.getParamName(0),batchMessage.getSignerId());
namedParameters.addValue(QueryType.INSERT_BATCH_DATA.getParamName(1),batchMessage.getBatchId());
namedParameters.addValue(QueryType.INSERT_BATCH_DATA.getParamName(2),batchMessage.getSerialNum());
namedParameters.addValue(QueryType.INSERT_BATCH_DATA.getParamName(3),batchMessage.getData());
jdbcTemplate.update(sql, namedParameters);
return BoolMsg.newBuilder().setValue(true).build();
}
@Override
public BoolMsg closeBatchMessage(CloseBatchMessage message) throws CommunicationException {
// Check batch size
String sql = sqlQueryProvider.getSQLString(QueryType.CHECK_BATCH_LENGTH);
MapSqlParameterSource namedParameters = new MapSqlParameterSource();
namedParameters.addValue(QueryType.CHECK_BATCH_LENGTH.getParamName(0),message.getSig().getSignerId());
namedParameters.addValue(QueryType.CHECK_BATCH_LENGTH.getParamName(1),message.getBatchId());
List<Long> lengthResult = jdbcTemplate.query(sql, namedParameters, new LongMapper());
if (lengthResult.get(0) != message.getBatchLength()) {
return BoolMsg.newBuilder().setValue(false).build();
}
// Check signature
sql = sqlQueryProvider.getSQLString(QueryType.GET_BATCH_MESSAGE_DATA);
namedParameters = new MapSqlParameterSource();
namedParameters.addValue(QueryType.GET_BATCH_MESSAGE_DATA.getParamName(0),message.getSig().getSignerId());
namedParameters.addValue(QueryType.GET_BATCH_MESSAGE_DATA.getParamName(1),message.getBatchId());
namedParameters.addValue(QueryType.GET_BATCH_MESSAGE_DATA.getParamName(2),0); // Read from the beginning
List<BatchData> batchDataList = jdbcTemplate.query(sql, namedParameters, new BatchDataMapper());
try {
signer.initVerify(message.getSig());
} catch (CertificateException | InvalidKeyException e) {
return BoolMsg.newBuilder().setValue(false).build();
}
//TODO: FInish this
//signer.updateContent(msgStream);
//assertTrue("Signature did not verify!", signer.verify());
return null;
}
@Override
public List<BatchData> readBatch(BatchSpecificationMessage message) {
return null; // TODO: Implement this
}
@Override
public void close() {}

View File

@ -5,6 +5,7 @@ import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer.SQLQueryProvider;
import meerkat.protobuf.BulletinBoardAPI.FilterType;
import javax.sql.DataSource;
import java.text.MessageFormat;
import java.util.LinkedList;
import java.util.List;
@ -32,21 +33,72 @@ public class MySQLQueryProvider implements SQLQueryProvider {
public String getSQLString(QueryType queryType) throws IllegalArgumentException{
switch(queryType) {
case ADD_SIGNATURE:
return "INSERT IGNORE INTO SignatureTable (EntryNum, SignerId, Signature) VALUES (:EntryNum, :SignerId, :Signature)";
return MessageFormat.format(
"INSERT IGNORE INTO SignatureTable (EntryNum, SignerId, Signature) VALUES ({0}, {1}, {2})",
QueryType.ADD_SIGNATURE.getParamName(0),
QueryType.ADD_SIGNATURE.getParamName(1),
QueryType.ADD_SIGNATURE.getParamName(2));
case CONNECT_TAG:
return "INSERT IGNORE INTO MsgTagTable (TagId, EntryNum)"
+ " SELECT TagTable.TagId, :EntryNum AS EntryNum FROM TagTable WHERE Tag = :Tag";
return MessageFormat.format(
"INSERT IGNORE INTO MsgTagTable (TagId, EntryNum)"
+ " SELECT TagTable.TagId, {0} AS EntryNum FROM TagTable WHERE Tag = {1}",
QueryType.CONNECT_TAG.getParamName(0),
QueryType.CONNECT_TAG.getParamName(1));
case FIND_MSG_ID:
return "SELECT EntryNum From MsgTable WHERE MsgId = :MsgId";
return MessageFormat.format(
"SELECT EntryNum From MsgTable WHERE MsgId = {0}",
QueryType.FIND_MSG_ID.getParamName(0));
case GET_MESSAGES:
return "SELECT MsgTable.EntryNum, MsgTable.Msg FROM MsgTable";
case GET_SIGNATURES:
return "SELECT Signature FROM SignatureTable WHERE EntryNum = :EntryNum";
return MessageFormat.format(
"SELECT Signature FROM SignatureTable WHERE EntryNum = {0}",
QueryType.GET_SIGNATURES.getParamName(0));
case INSERT_MSG:
return "INSERT INTO MsgTable (MsgId, Msg) VALUES(:MsgId, :Msg)";
return MessageFormat.format(
"INSERT INTO MsgTable (MsgId, Msg) VALUES({0}, {1})",
QueryType.INSERT_MSG.getParamName(0),
QueryType.INSERT_MSG.getParamName(1));
case INSERT_NEW_TAG:
return "INSERT IGNORE INTO TagTable(Tag) VALUES (:Tag)";
return MessageFormat.format(
"INSERT IGNORE INTO TagTable(Tag) VALUES ({0})",
QueryType.INSERT_NEW_TAG.getParamName(0));
case GET_BATCH_MESSAGE_ENTRY:
return MessageFormat.format(
"SELECT MsgTable.EntryNum, MsgTable.Msg FROM MsgTable"
+ "INNER JOIN SignatureTable ON MsgTable.EntryNum = SignatureTable.EntryNum"
+ "INNER JOIN MsgTagTable ON MsgTable.EntryNum = MsgTagTable.EntryNum"
+ "INNER JOIN TagTable ON MsgTagTable.TagId = TagTable.TagId"
+ "WHERE SignatureTable.SignerId = {0}"
+ "AND TagTable.Tag = {1}",
QueryType.GET_BATCH_MESSAGE_ENTRY.getParamName(0),
QueryType.GET_BATCH_MESSAGE_ENTRY.getParamName(1));
case GET_BATCH_MESSAGE_DATA:
return MessageFormat.format(
"SELECT Data FROM BatchTable"
+ " WHERE SignerId = {0} AND BatchId = {1} AND SerialNum >= {2}"
+ " ORDER BY SerialNum ASC",
QueryType.GET_BATCH_MESSAGE_DATA.getParamName(0),
QueryType.GET_BATCH_MESSAGE_DATA.getParamName(1),
QueryType.GET_BATCH_MESSAGE_DATA.getParamName(2));
case CHECK_BATCH_LENGTH:
return MessageFormat.format(
"SELECT COUNT(Data) AS BatchLength FROM BatchTable"
+ " WHERE SignerId = {0} AND BatchId = {1}",
QueryType.GET_BATCH_MESSAGE_DATA.getParamName(0),
QueryType.GET_BATCH_MESSAGE_DATA.getParamName(1));
default:
throw new IllegalArgumentException("Cannot serve a query of type " + queryType);
}
@ -119,7 +171,8 @@ public class MySQLQueryProvider implements SQLQueryProvider {
public List<String> getSchemaCreationCommands() {
List<String> list = new LinkedList<String>();
list.add("CREATE TABLE IF NOT EXISTS MsgTable (EntryNum INT NOT NULL AUTO_INCREMENT PRIMARY KEY, MsgId TINYBLOB, Msg BLOB, UNIQUE(MsgId(50)))");
list.add("CREATE TABLE IF NOT EXISTS MsgTable (EntryNum INT NOT NULL AUTO_INCREMENT PRIMARY KEY,"
+ " MsgId TINYBLOB, Msg BLOB, UNIQUE(MsgId(50)))");
list.add("CREATE TABLE IF NOT EXISTS TagTable (TagId INT NOT NULL AUTO_INCREMENT PRIMARY KEY, Tag VARCHAR(50), UNIQUE(Tag))");
@ -129,7 +182,14 @@ public class MySQLQueryProvider implements SQLQueryProvider {
+ " CONSTRAINT UNIQUE (EntryNum, TagID))");
list.add("CREATE TABLE IF NOT EXISTS SignatureTable (EntryNum INT, SignerId TINYBLOB, Signature TINYBLOB,"
+ " INDEX(SignerId(32)), CONSTRAINT Uni UNIQUE(SignerId(32), EntryNum), CONSTRAINT FOREIGN KEY (EntryNum) REFERENCES MsgTable(EntryNum))");
+ " INDEX(SignerId(32)), CONSTRAINT Unique_Signature UNIQUE(SignerId(32), EntryNum),"
+ " CONSTRAINT FOREIGN KEY (EntryNum) REFERENCES MsgTable(EntryNum))");
list.add("CREATE TABLE IF NOT EXISTS BatchTable (SignerId TINYBLOB, BatchId INT, SerialNum INT, Data BLOB,"
+ " CONSTRAINT Unique_Batch UNIQUE(SignerId(32), BatchId, SerialNum))");
list.add("CREATE TABLE IF NOT EXISTS BatchTagTable (SignerId TINYBLOB, BatchId INT, TagId INT,"
+ " INDEX(SignerId, BatchId), CONSTRAINT FOREIGN KEY (TagId) REFERENCES TagTable(TagId))");
return list;
}
@ -138,6 +198,8 @@ public class MySQLQueryProvider implements SQLQueryProvider {
public List<String> getSchemaDeletionCommands() {
List<String> list = new LinkedList<String>();
list.add("DROP TABLE IF EXISTS BatchTagTable");
list.add("DROP TABLE IF EXISTS BatchTable");
list.add("DROP TABLE IF EXISTS MsgTagTable");
list.add("DROP TABLE IF EXISTS SignatureTable");
list.add("DROP TABLE IF EXISTS TagTable");

View File

@ -0,0 +1,21 @@
package meerkat.bulletinboard.sqlserver.mappers;
import com.google.protobuf.ByteString;
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 {
return BatchData.newBuilder().setData(ByteString.copyFrom(rs.getBytes(rowNum))).build();
}
}

View File

@ -9,7 +9,7 @@ import java.sql.SQLException;
/**
* Created by Arbel Deutsch Peled on 11-Dec-15.
*/
public class EntryNumMapper implements RowMapper<Long> {
public class LongMapper implements RowMapper<Long> {
@Override
public Long mapRow(ResultSet rs, int rowNum) throws SQLException {

View File

@ -17,12 +17,11 @@ import meerkat.bulletinboard.sqlserver.H2QueryProvider;
import meerkat.bulletinboard.sqlserver.MySQLQueryProvider;
import meerkat.bulletinboard.sqlserver.SQLiteQueryProvider;
import meerkat.comm.CommunicationException;
import meerkat.protobuf.BulletinBoardAPI.BoolMsg;
import meerkat.protobuf.BulletinBoardAPI.BulletinBoardMessage;
import meerkat.protobuf.BulletinBoardAPI.BulletinBoardMessageList;
import meerkat.protobuf.BulletinBoardAPI.MessageFilterList;
import meerkat.protobuf.BulletinBoardAPI.*;
import meerkat.rest.Constants;
import java.util.List;
@Path(Constants.BULLETIN_BOARD_SERVER_PATH)
public class BulletinBoardWebApp implements BulletinBoardServer, ServletContextListener{
@ -99,6 +98,58 @@ public class BulletinBoardWebApp implements BulletinBoardServer, ServletContextL
return bulletinBoard.readMessages(filterList);
}
@Path(Constants.BEGIN_BATCH_PATH)
@POST
@Consumes(Constants.MEDIATYPE_PROTOBUF)
@Produces(Constants.MEDIATYPE_PROTOBUF)
@Override
public BoolMsg beginBatch(BeginBatchMessage message) {
try {
return bulletinBoard.beginBatch(message);
} catch (CommunicationException e) {
System.err.println(e.getMessage());
return null;
}
}
@Path(Constants.POST_BATCH_PATH)
@POST
@Consumes(Constants.MEDIATYPE_PROTOBUF)
@Produces(Constants.MEDIATYPE_PROTOBUF)
@Override
public BoolMsg postBatchMessage(BatchMessage batchMessage) {
try {
return bulletinBoard.postBatchMessage(batchMessage);
} catch (CommunicationException e) {
System.err.println(e.getMessage());
return null;
}
}
@Path(Constants.CLOSE_BATCH_PATH)
@POST
@Consumes(Constants.MEDIATYPE_PROTOBUF)
@Produces(Constants.MEDIATYPE_PROTOBUF)
@Override
public BoolMsg closeBatchMessage(CloseBatchMessage message) {
try {
return bulletinBoard.closeBatchMessage(message);
} catch (CommunicationException e) {
System.err.println(e.getMessage());
return null;
}
}
@Override
public List<BatchData> readBatch(BatchSpecificationMessage message) {
try {
return bulletinBoard.readBatch(message);
} catch (CommunicationException | IllegalArgumentException e) {
System.err.println(e.getMessage());
return null;
}
}
@Override
public void close(){
try {

View File

@ -9,13 +9,22 @@ import java.util.List;
*/
public interface AsyncBulletinBoardClient extends BulletinBoardClient {
public interface ClientCallback<T> {
void handleCallback(T msg);
void handleFailure(Throwable t);
}
public interface MessageHandler {
void handleNewMessages(List<BulletinBoardMessage> messageList);
}
/**
* Post a message to the bulletin board in an asynchronous manner
* @param msg is the message to be posted
* @param callback is a class containing methods to handle the result of the operation
* @return a unique message ID for the message, that can be later used to retrieve the batch
*/
MessageID postMessage(BulletinBoardMessage msg, ClientCallback<?> callback);
public MessageID postMessage(BulletinBoardMessage msg, ClientCallback<?> callback);
/**
* This method allows for sending large messages as a batch to the bulletin board
@ -26,12 +35,12 @@ public interface AsyncBulletinBoardClient extends BulletinBoardClient {
* @param callback is a callback function class for handling results of the operation
* @return a unique message ID for the entire message, that can be later used to retrieve the batch
*/
MessageID postBatch(byte[] signerId, int batchId, List<BatchData> batchDataList, int startPosition, ClientCallback<?> callback);
public MessageID postBatch(byte[] signerId, int batchId, List<BatchData> batchDataList, int startPosition, ClientCallback<?> callback);
/**
* Overloading of the postBatch method in which startPosition is set to the default value 0
*/
MessageID postBatch(byte[] signerId, int batchId, List<BatchData> batchDataList, ClientCallback<?> callback);
public MessageID postBatch(byte[] signerId, int batchId, List<BatchData> batchDataList, ClientCallback<?> callback);
/**
* Check how "safe" a given message is in an asynchronous manner
@ -39,7 +48,7 @@ public interface AsyncBulletinBoardClient extends BulletinBoardClient {
* @param id is the unique message identifier for retrieval
* @param callback is a callback function class for handling results of the operation
*/
void getRedundancy(MessageID id, ClientCallback<Float> callback);
public void getRedundancy(MessageID id, ClientCallback<Float> callback);
/**
* Read all messages posted matching the given filter in an asynchronous manner
@ -49,7 +58,7 @@ public interface AsyncBulletinBoardClient extends BulletinBoardClient {
* @param filterList return only messages that match the filters (null means no filtering).
* @param callback is a callback function class for handling results of the operation
*/
void readMessages(MessageFilterList filterList, ClientCallback<List<BulletinBoardMessage>> callback);
public void readMessages(MessageFilterList filterList, ClientCallback<List<BulletinBoardMessage>> callback);
/**
* Read a given batch message from the bulletin board
@ -57,6 +66,13 @@ public interface AsyncBulletinBoardClient extends BulletinBoardClient {
* @param batchId is the unique (per signer) ID of the batch
* @param callback is a callback class for handling the result of the operation
*/
void readBatch(byte[] signerId, int batchId, ClientCallback<SignedBatch> callback);
public void readBatch(byte[] signerId, int batchId, ClientCallback<SignedBatch> callback);
/**
* Subscribes to a notifier that will return any new messages on the server that match the given filters
* @param filterList defines the set of filters for message retrieval
* @param messageHandler defines the handler for new messages received
*/
public void subscribe(MessageFilterList filterList, MessageHandler messageHandler);
}

View File

@ -12,11 +12,6 @@ import java.util.List;
*/
public interface BulletinBoardClient {
interface ClientCallback<T> {
void handleCallback(T msg);
void handleFailure(Throwable t);
}
/**
* Initialize the client to use some specified servers
* @param clientParams contains the parameters required for the client setup

View File

@ -3,40 +3,83 @@ package meerkat.bulletinboard;
import meerkat.comm.CommunicationException;
import meerkat.protobuf.BulletinBoardAPI.*;
import java.util.List;
/**
* Created by Arbel on 07/11/15.
*
* This interface refers to a single instance of a Bulletin Board.
* An implementation of this interface may use any DB and be hosted on any machine.
* This interface refers to a single instance of a Bulletin Board
* An implementation of this interface may use any DB and be hosted on any machine.
*/
public interface BulletinBoardServer{
/**
* This method initializes the server by reading the signature data and storing it.
* It also establishes the connection to the DB.
* @throws CommunicationException on DB connection error.
* This method initializes the server by reading the signature data and storing it
* It also establishes the connection to the DB
* @throws CommunicationException on DB connection error
*/
public void init(String meerkatDB) throws CommunicationException;
/**
* Post a message to bulletin board.
* @param msg is the actual (signed) message
* @return TRUE if the message has been authenticated and FALSE otherwise (in ProtoBuf form).
* @throws CommunicationException on DB connection error.
* @return TRUE if the message has been authenticated and FALSE otherwise (in ProtoBuf form)
* @throws CommunicationException on DB connection error
*/
public BoolMsg postMessage(BulletinBoardMessage msg) throws CommunicationException;
public BoolMsg postMessage(BulletinBoardMessage msg) throws CommunicationException;
/**
* Read all messages posted matching the given filter.
* @param filter return only messages that match the filter (empty list means no filtering).
* Read all messages posted matching the given filter
* @param filterList return only messages that match the filters (empty list or null means no filtering)
* @return
* @throws CommunicationException on DB connection error
*/
BulletinBoardMessageList readMessages(MessageFilterList filterList) throws CommunicationException;
public BulletinBoardMessageList readMessages(MessageFilterList filterList) throws CommunicationException;
/**
* Informs server about a new batch message
* @param message contains the required data about the new batch
* @return TRUE if the batch request is accepted amd FALSE otherwise
* Specifically, if such a batch already exists and is not yet closed: the value returned will be TRUE
* However, if such a batch exists and is already closed: the value returned will be FALSE
* @throws CommunicationException on DB connection error
*/
public BoolMsg beginBatch(BeginBatchMessage message) throws CommunicationException;
/**
* Posts a (part 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
* @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
* @return TRUE if the message is accepted and successfully saved and FALSE otherwise
* 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
* @throws CommunicationException on DB connection error
*/
public BoolMsg postBatchMessage(BatchMessage batchMessage) throws CommunicationException;
/**
* Attempts to close and finalize a batch message
* @param message contains the data necessary to close the batch; in particular: the signature for the batch
* @return TRUE if the batch was successfully closed, FALSE otherwise
* 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
*/
public BoolMsg closeBatchMessage(CloseBatchMessage message) throws CommunicationException;
/**
* 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
* @return an ordered list of batch messages starting from the specified start position (if given) or from the beginning (if omitted)
* @throws CommunicationException on DB connection error
* @throws IllegalArgumentException if message does not specify a batch
*/
public List<BatchData> readBatch(BatchSpecificationMessage message) throws CommunicationException, IllegalArgumentException;
/**
* This method closes the connection to the DB.
* @throws CommunicationException on DB connection error.
* This method closes the connection to the DB
* @throws CommunicationException on DB connection error
*/
public void close() throws CommunicationException;
public void close() throws CommunicationException;
}

View File

@ -0,0 +1,20 @@
package meerkat.crypto;
import meerkat.protobuf.BulletinBoardAPI.*;
import java.util.List;
/**
* Created by Arbel Deutsch Peled on 18-Dec-15.
* Extends the Digest interface with a method for digesting Batch messages
*/
public interface BatchDigest extends Digest{
/**
* Update the digest with the
* @param beginBatchMessage is the message that starts the batch
* @param batchDataList is the (ordered) list of data in the batch
*/
public void update(BeginBatchMessage beginBatchMessage, List<BatchData> batchDataList);
}

View File

@ -13,7 +13,7 @@ public interface Digest {
* (copied from {@link MessageDigest#digest()})
* @return
*/
byte[] digest();
public byte[] digest();
/**
* Updates the digest using the specified message (in serialized wire form)
@ -22,12 +22,12 @@ public interface Digest {
* @param msg
* @return
*/
void update(Message msg);
public void update(Message msg);
/**
* Resets the digest for further use.
*/
void reset();
public void reset();
/**
* Clone the current digest state

View File

@ -30,7 +30,10 @@ import javax.security.auth.callback.UnsupportedCallbackException;
*
* This class is not thread-safe (each thread should have its own instance).
*/
public class ECDSASignature extends GlobalCryptoSetup implements DigitalSignature {
public class ECDSASignature implements DigitalSignature {
private static GlobalCryptoSetup globalCryptoSetup = GlobalCryptoSetup.getInstance();
final Logger logger = LoggerFactory.getLogger(getClass());
final public static String KEYSTORE_TYPE = "PKCS12";

View File

@ -31,7 +31,10 @@ import java.util.Random;
/**
* Created by talm on 17/11/15.
*/
public class ECElGamalEncryption extends GlobalCryptoSetup implements Encryption {
public class ECElGamalEncryption implements Encryption {
private static GlobalCryptoSetup globalCryptoSetup = GlobalCryptoSetup.getInstance();
final Logger logger = LoggerFactory.getLogger(getClass());
public final static String KEY_ALGORITHM = "ECDH";

View File

@ -0,0 +1,27 @@
package meerkat.crypto.concrete;
import com.google.protobuf.Message;
import meerkat.crypto.BatchDigest;
import meerkat.protobuf.BulletinBoardAPI.*;
import java.util.List;
/**
* Created by Arbel Deutsch Peled on 19-Dec-15.
*/
public abstract class GenericBatchDigest implements BatchDigest{
@Override
public void update(BeginBatchMessage beginBatchMessage, List<BatchData> batchDataList) {
update(beginBatchMessage);
for (BatchData batchData : batchDataList) {
update(batchData);
}
}
@Override
// Repeated here to circumvent compiler error
public abstract BatchDigest clone() throws CloneNotSupportedException;
}

View File

@ -10,13 +10,26 @@ import java.security.Security;
/**
* A class that performs required crypto setup
*/
public class GlobalCryptoSetup {
public final class GlobalCryptoSetup {
private static GlobalCryptoSetup globalCryptoSetup;
final static Logger logger = LoggerFactory.getLogger(GlobalCryptoSetup.class);
static boolean loadedBouncyCastle = false;
static Provider bouncyCastleProvider;
private boolean loadedBouncyCastle = false;
private Provider bouncyCastleProvider;
public static boolean hasSecp256k1Curve() {
private GlobalCryptoSetup() { doSetup(); }
public static GlobalCryptoSetup getInstance() {
if (globalCryptoSetup == null) {
globalCryptoSetup = new GlobalCryptoSetup();
}
return globalCryptoSetup;
}
public boolean hasSecp256k1Curve() {
// For now we just check if the java version is at least 8
String[] version = System.getProperty("java.version").split("\\.");
int major = Integer.parseInt(version[0]);
@ -24,9 +37,11 @@ public class GlobalCryptoSetup {
return ((major > 1) || ((major > 0) && (minor > 7)));
}
public static Provider getBouncyCastleProvider() { doSetup(); return bouncyCastleProvider; }
public Provider getBouncyCastleProvider() {
return bouncyCastleProvider;
}
public static synchronized void doSetup() {
public void doSetup() {
if (bouncyCastleProvider == null) {
bouncyCastleProvider = new BouncyCastleProvider();
// Make bouncycastle our default provider if we're running on a JVM version < 8
@ -39,5 +54,4 @@ public class GlobalCryptoSetup {
}
}
public GlobalCryptoSetup() { doSetup(); }
}

View File

@ -2,6 +2,7 @@ package meerkat.crypto.concrete;
import com.google.protobuf.ByteString;
import com.google.protobuf.Message;
import meerkat.crypto.BatchDigest;
import meerkat.crypto.Digest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -13,7 +14,7 @@ import java.security.NoSuchAlgorithmException;
/**
* Created by talm on 11/9/15.
*/
public class SHA256Digest extends GlobalCryptoSetup implements Digest {
public class SHA256Digest extends GenericBatchDigest {
final Logger logger = LoggerFactory.getLogger(getClass());
public static final String SHA256 = "SHA-256";

View File

@ -104,4 +104,11 @@ message BatchMessage {
int32 batchId = 2; // Unique identifier for the batch (unique per signer)
int32 serialNum = 3; // Location of the message in the batch: starting from 0
BatchData data = 4; // Actual data
}
// 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
}

View File

@ -26,6 +26,9 @@ import java.security.spec.InvalidKeySpecException;
* utilities for ECElgamal
*/
public class ECElGamalUtils {
private static GlobalCryptoSetup globalCryptoSetup = GlobalCryptoSetup.getInstance();
final static Logger logger = LoggerFactory.getLogger(ECElGamalUtils.class);
public final static String ENCRYPTION_KEY_ALGORITHM = "ECDH";
@ -43,7 +46,7 @@ public class ECElGamalUtils {
try {
KeyFactory fact = KeyFactory.getInstance(ENCRYPTION_KEY_ALGORITHM,
GlobalCryptoSetup.getBouncyCastleProvider());
globalCryptoSetup.getBouncyCastleProvider());
PublicKey javaPk = fact.generatePublic(pubKeySpec);
ConcreteCrypto.ElGamalPublicKey serializedPk = ConcreteCrypto.ElGamalPublicKey.newBuilder()
.setSubjectPublicKeyInfo(ByteString.copyFrom(javaPk.getEncoded())).build();

View File

@ -9,4 +9,8 @@ public interface Constants {
public static final String BULLETIN_BOARD_SERVER_PATH = "/bbserver";
public static final String READ_MESSAGES_PATH = "/readmessages";
public static final String POST_MESSAGE_PATH = "/postmessage";
public static final String BEGIN_BATCH_PATH = "/beginbatch";
public static final String POST_BATCH_PATH = "/postbatch";
public static final String CLOSE_BATCH_PATH = "/closebatch";
}