Working version of Batch messages on Server-Side

Bulletin-Board-Batch
Arbel Deutsch Peled 2015-12-27 11:21:17 +02:00
parent b5237d6c9f
commit 88b8f6d8ea
11 changed files with 468 additions and 104 deletions

View File

@ -79,6 +79,11 @@ test {
exclude '**/*IntegrationTest*' exclude '**/*IntegrationTest*'
} }
task myTest(type: Test) {
include '**/*MySQL*Test*'
outputs.upToDateWhen { false }
}
task dbTest(type: Test) { task dbTest(type: Test) {
include '**/*H2*Test*' include '**/*H2*Test*'
include '**/*MySQL*Test*' include '**/*MySQL*Test*'

View File

@ -51,25 +51,87 @@ public class BulletinBoardSQLServer implements BulletinBoardServer{
*/ */
public static enum QueryType { public static enum QueryType {
FIND_MSG_ID(new String[] {"MsgId"}), FIND_MSG_ID(
INSERT_MSG(new String[] {"MsgId","Msg"}), new String[] {"MsgId"},
INSERT_NEW_TAG(new String[] {"Tag"}), new int[] {Types.BLOB}
CONNECT_TAG(new String[] {"EntryNum","Tag"}), ),
ADD_SIGNATURE(new String[] {"EntryNum","SignerId","Signature"}),
GET_SIGNATURES(new String[] {"EntryNum"}), FIND_TAG_ID(
GET_MESSAGES(new String[] {}), new String[] {"Tag"},
GET_BATCH_MESSAGE_ENTRY(new String[] {"SignerId", "BatchId"}), new int[] {Types.VARCHAR}
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"}), INSERT_MSG(
CONNECT_BATCH_TAG(new String[] {"SignerId", "BatchId", "Tag"}), new String[] {"MsgId","Msg"},
GET_BATCH_TAGS(new String[] {"SignerId", "BatchId"}), new int[] {Types.BLOB, Types.BLOB}
MOVE_BATCH_TAGS(new String[] {"EntryNum", "SignerId", "BatchId"}); ),
INSERT_NEW_TAG(
new String[] {"Tag"},
new int[] {Types.VARCHAR}
),
CONNECT_TAG(
new String[] {"EntryNum","Tag"},
new int[] {Types.INTEGER, Types.VARCHAR}
),
ADD_SIGNATURE(
new String[] {"EntryNum","SignerId","Signature"},
new int[] {Types.INTEGER, Types.BLOB, Types.BLOB}
),
GET_SIGNATURES(
new String[] {"EntryNum"},
new int[] {Types.INTEGER}
),
GET_MESSAGES(
new String[] {},
new int[] {}
),
GET_BATCH_MESSAGE_ENTRY(
new String[] {"SignerId", "BatchId"},
new int[] {Types.BLOB, Types.INTEGER}
),
CHECK_BATCH_LENGTH(
new String[] {"SignerId", "BatchId"},
new int[] {Types.BLOB, Types.INTEGER}
),
GET_BATCH_MESSAGE_DATA(
new String[] {"SignerId", "BatchId", "StartPosition"},
new int[] {Types.BLOB, Types.INTEGER, Types.INTEGER}
),
INSERT_BATCH_DATA(
new String[] {"SignerId", "BatchId", "SerialNum", "Data"},
new int[] {Types.BLOB, Types.INTEGER, Types.INTEGER, Types.BLOB}
),
CONNECT_BATCH_TAG(
new String[] {"SignerId", "BatchId", "Tag"},
new int[] {Types.BLOB, Types.INTEGER, Types.VARCHAR}
),
GET_BATCH_TAGS(
new String[] {"SignerId", "BatchId"},
new int[] {Types.BLOB, Types.INTEGER}
),
REMOVE_BATCH_TAGS(
new String[] {"SignerId", "BatchId"},
new int[] {Types.BLOB, Types.INTEGER}
);
private String[] paramNames; private String[] paramNames;
private int[] paramTypes;
private QueryType(String[] paramNames) { private QueryType(String[] paramNames, int[] paramTypes) {
this.paramNames = paramNames; this.paramNames = paramNames;
this.paramTypes = paramTypes;
} }
public String[] getParamNames() { public String[] getParamNames() {
@ -80,6 +142,14 @@ public class BulletinBoardSQLServer implements BulletinBoardServer{
return paramNames[num]; return paramNames[num];
} }
public int[] getParamTypes() {
return paramTypes;
}
public int getParamType(int num) {
return paramTypes[num];
}
} }
/** /**
@ -316,10 +386,17 @@ public class BulletinBoardSQLServer implements BulletinBoardServer{
.build(); .build();
} }
@Override
public BoolMsg postMessage(BulletinBoardMessage msg) throws CommunicationException {
if (!verifyMessage(msg)) { /**
* This method posts a messages to the server
* @param msg is the message to post
* @param checkSignature decides whether ot not the method should check the signature before it posts the message
* @return TRUE if the post is successful and FALSE otherwise
* @throws CommunicationException
*/
public BoolMsg postMessage(BulletinBoardMessage msg, boolean checkSignature) throws CommunicationException{
if (checkSignature && !verifyMessage(msg)) {
return boolToBoolMsg(false); return boolToBoolMsg(false);
} }
@ -417,6 +494,12 @@ public class BulletinBoardSQLServer implements BulletinBoardServer{
jdbcTemplate.batchUpdate(sql,namedParameterArray); jdbcTemplate.batchUpdate(sql,namedParameterArray);
return boolToBoolMsg(true); return boolToBoolMsg(true);
}
@Override
public BoolMsg postMessage(BulletinBoardMessage msg) throws CommunicationException {
return postMessage(msg, true); // Perform a post and check the signature for authenticity
} }
@Override @Override
@ -477,7 +560,8 @@ public class BulletinBoardSQLServer implements BulletinBoardServer{
// Run query // Run query
List<BulletinBoardMessage.Builder> msgBuilders = jdbcTemplate.query(sqlBuilder.toString(), namedParameters, messageMapper); List<BulletinBoardMessage.Builder> msgBuilders =
jdbcTemplate.query(sqlBuilder.toString(), namedParameters, messageMapper);
// Compile list of messages // Compile list of messages
@ -507,23 +591,41 @@ public class BulletinBoardSQLServer implements BulletinBoardServer{
} }
/**
* 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 signerId is the ID of the publisher of the batch
* @param batchId is the unique (per signer) batch ID * @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){ private boolean isBatchClosed(ByteString signerId, int batchId) throws CommunicationException {
String sql = sqlQueryProvider.getSQLString(QueryType.GET_BATCH_MESSAGE_ENTRY); MessageFilterList filterList = MessageFilterList.newBuilder()
.addFilter(MessageFilter.newBuilder()
.setType(FilterType.SIGNER_ID)
.setId(signerId)
.build())
.addFilter(MessageFilter.newBuilder()
.setType(FilterType.TAG)
.setTag(BATCH_TAG)
.build())
.addFilter(MessageFilter.newBuilder()
.setType(FilterType.TAG)
.setTag(batchIdToTag(batchId))
.build())
.build();
MapSqlParameterSource namedParameters = new MapSqlParameterSource(); BulletinBoardMessageList messageList = readMessages(filterList);
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 (messageList.getMessageList().size() > 0);
return (result.size() > 0);
} }
@ -562,7 +664,7 @@ public class BulletinBoardSQLServer implements BulletinBoardServer{
} }
@Override @Override
public BoolMsg postBatchMessage(BatchMessage batchMessage) { public BoolMsg postBatchMessage(BatchMessage batchMessage) throws CommunicationException{
// Check if batch is closed // Check if batch is closed
if (isBatchClosed(batchMessage.getSignerId(), batchMessage.getBatchId())) { if (isBatchClosed(batchMessage.getSignerId(), batchMessage.getBatchId())) {
@ -576,7 +678,7 @@ public class BulletinBoardSQLServer implements BulletinBoardServer{
namedParameters.addValue(QueryType.INSERT_BATCH_DATA.getParamName(0),batchMessage.getSignerId()); 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(1),batchMessage.getBatchId());
namedParameters.addValue(QueryType.INSERT_BATCH_DATA.getParamName(2),batchMessage.getSerialNum()); namedParameters.addValue(QueryType.INSERT_BATCH_DATA.getParamName(2),batchMessage.getSerialNum());
namedParameters.addValue(QueryType.INSERT_BATCH_DATA.getParamName(3),batchMessage.getData()); namedParameters.addValue(QueryType.INSERT_BATCH_DATA.getParamName(3),batchMessage.getData().toByteArray());
jdbcTemplate.update(sql, namedParameters); jdbcTemplate.update(sql, namedParameters);
@ -641,11 +743,12 @@ public class BulletinBoardSQLServer implements BulletinBoardServer{
completeBatch.setSignature(message.getSig()); completeBatch.setSignature(message.getSig());
try { // try {
signer.verify(completeBatch); // TODO: Actual verification
} catch (CertificateException | InvalidKeyException | SignatureException e) { // //signer.verify(completeBatch);
return BoolMsg.newBuilder().setValue(false).build(); // } catch (CertificateException | InvalidKeyException | SignatureException e) {
} // return BoolMsg.newBuilder().setValue(false).build();
// }
// Batch verified: finalize it // Batch verified: finalize it
@ -660,28 +763,25 @@ public class BulletinBoardSQLServer implements BulletinBoardServer{
.setMsg(UnsignedBulletinBoardMessage.newBuilder() .setMsg(UnsignedBulletinBoardMessage.newBuilder()
.addAllTag(tags) .addAllTag(tags)
.addTag(BATCH_TAG) .addTag(BATCH_TAG)
.addTag(batchIdToTag(batchId))
.setData(message.getSig().getSignerId()) .setData(message.getSig().getSignerId())
.build()) .build())
.build(); .build();
sql = sqlQueryProvider.getSQLString(QueryType.INSERT_MSG); // Post message without checking signature validity
postMessage(bulletinBoardMessage, false);
// Remove tags from temporary table
sql = sqlQueryProvider.getSQLString(QueryType.REMOVE_BATCH_TAGS);
namedParameters = new MapSqlParameterSource(); namedParameters = new MapSqlParameterSource();
namedParameters.addValue(QueryType.INSERT_MSG.getParamName(0), msgID); namedParameters.addValue(QueryType.REMOVE_BATCH_TAGS.getParamName(0), signerId);
namedParameters.addValue(QueryType.INSERT_MSG.getParamName(1), bulletinBoardMessage); namedParameters.addValue(QueryType.REMOVE_BATCH_TAGS.getParamName(1), batchId);
jdbcTemplate.update(sql, namedParameters, keyHolder);
long entryNum = keyHolder.getKey().longValue();
sql = sqlQueryProvider.getSQLString(QueryType.MOVE_BATCH_TAGS);
namedParameters = new MapSqlParameterSource();
namedParameters.addValue(QueryType.MOVE_BATCH_TAGS.getParamName(0), entryNum);
namedParameters.addValue(QueryType.MOVE_BATCH_TAGS.getParamName(1), signerId);
namedParameters.addValue(QueryType.MOVE_BATCH_TAGS.getParamName(2), batchId);
jdbcTemplate.update(sql, namedParameters); jdbcTemplate.update(sql, namedParameters);
// Return TRUE
return BoolMsg.newBuilder().setValue(true).build(); return BoolMsg.newBuilder().setValue(true).build();
} }
@ -698,6 +798,7 @@ public class BulletinBoardSQLServer implements BulletinBoardServer{
namedParameters.addValue(QueryType.GET_BATCH_MESSAGE_DATA.getParamName(0),message.getSignerId()); namedParameters.addValue(QueryType.GET_BATCH_MESSAGE_DATA.getParamName(0),message.getSignerId());
namedParameters.addValue(QueryType.GET_BATCH_MESSAGE_DATA.getParamName(1),message.getBatchId()); namedParameters.addValue(QueryType.GET_BATCH_MESSAGE_DATA.getParamName(1),message.getBatchId());
namedParameters.addValue(QueryType.GET_BATCH_MESSAGE_DATA.getParamName(2),message.getStartPosition());
return jdbcTemplate.query(sql, namedParameters, new BatchDataMapper()); return jdbcTemplate.query(sql, namedParameters, new BatchDataMapper());
} }

View File

@ -1,6 +1,7 @@
package meerkat.bulletinboard.sqlserver; package meerkat.bulletinboard.sqlserver;
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource; import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
import meerkat.bulletinboard.BulletinBoardConstants;
import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer.SQLQueryProvider; import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer.SQLQueryProvider;
import meerkat.protobuf.BulletinBoardAPI.FilterType; import meerkat.protobuf.BulletinBoardAPI.FilterType;
@ -53,6 +54,11 @@ public class MySQLQueryProvider implements SQLQueryProvider {
"SELECT EntryNum From MsgTable WHERE MsgId = :{0}", "SELECT EntryNum From MsgTable WHERE MsgId = :{0}",
QueryType.FIND_MSG_ID.getParamName(0)); QueryType.FIND_MSG_ID.getParamName(0));
case FIND_TAG_ID:
return MessageFormat.format(
"SELECT TagId FROM TagTable WHERE Tag = :{0}",
QueryType.FIND_TAG_ID.getParamName(0));
case GET_MESSAGES: case GET_MESSAGES:
return "SELECT MsgTable.EntryNum, MsgTable.Msg FROM MsgTable"; return "SELECT MsgTable.EntryNum, MsgTable.Msg FROM MsgTable";
@ -92,12 +98,21 @@ public class MySQLQueryProvider implements SQLQueryProvider {
QueryType.GET_BATCH_MESSAGE_DATA.getParamName(1), QueryType.GET_BATCH_MESSAGE_DATA.getParamName(1),
QueryType.GET_BATCH_MESSAGE_DATA.getParamName(2)); QueryType.GET_BATCH_MESSAGE_DATA.getParamName(2));
case INSERT_BATCH_DATA:
return MessageFormat.format(
"INSERT INTO BatchTable (SignerId, BatchId, SerialNum, Data)"
+ " VALUES (:{0}, :{1}, :{2}, :{3})",
QueryType.INSERT_BATCH_DATA.getParamName(0),
QueryType.INSERT_BATCH_DATA.getParamName(1),
QueryType.INSERT_BATCH_DATA.getParamName(2),
QueryType.INSERT_BATCH_DATA.getParamName(3));
case CHECK_BATCH_LENGTH: case CHECK_BATCH_LENGTH:
return MessageFormat.format( return MessageFormat.format(
"SELECT COUNT(Data) AS BatchLength FROM BatchTable" "SELECT COUNT(Data) AS BatchLength FROM BatchTable"
+ " WHERE SignerId = :{0} AND BatchId = :{1}", + " WHERE SignerId = :{0} AND BatchId = :{1}",
QueryType.GET_BATCH_MESSAGE_DATA.getParamName(0), QueryType.CHECK_BATCH_LENGTH.getParamName(0),
QueryType.GET_BATCH_MESSAGE_DATA.getParamName(1)); QueryType.CHECK_BATCH_LENGTH.getParamName(1));
case CONNECT_BATCH_TAG: case CONNECT_BATCH_TAG:
return MessageFormat.format( return MessageFormat.format(
@ -109,19 +124,16 @@ public class MySQLQueryProvider implements SQLQueryProvider {
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.Tag ID" "SELECT Tag FROM TagTable INNER JOIN BatchTagTable ON TagTable.TagId = BatchTagTable.TagId"
+ " WHERE SignerId = :{0} AND BatchId = :{1} ORDER BY Tag ASC", + " 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));
case MOVE_BATCH_TAGS: case REMOVE_BATCH_TAGS:
return MessageFormat.format( return MessageFormat.format(
"INSERT INTO MsgTagTable (EntryNum, TagId) " "DELETE FROM BatchTagTable WHERE SignerId = :{0} AND BatchId = :{1}",
+ " SELECT {0}, TagId FROM BatchTagTable WHERE SignerId = {1} AND BatchId = {2};" QueryType.REMOVE_BATCH_TAGS.getParamName(0),
+ " DELETE FROM BatchTagTable WHERE SignerId = {1} AND BatchId = {2}", QueryType.REMOVE_BATCH_TAGS.getParamName(1));
QueryType.GET_BATCH_TAGS.getParamName(0),
QueryType.GET_BATCH_TAGS.getParamName(1),
QueryType.GET_BATCH_TAGS.getParamName(2));
default: default:
throw new IllegalArgumentException("Cannot serve a query of type " + queryType); throw new IllegalArgumentException("Cannot serve a query of type " + queryType);
@ -187,6 +199,7 @@ public class MySQLQueryProvider implements SQLQueryProvider {
dataSource.setDatabaseName(dbName); dataSource.setDatabaseName(dbName);
dataSource.setUser(username); dataSource.setUser(username);
dataSource.setPassword(password); dataSource.setPassword(password);
dataSource.setAllowMultiQueries(true);
return dataSource; return dataSource;
} }

View File

@ -1,6 +1,7 @@
package meerkat.bulletinboard.sqlserver.mappers; package meerkat.bulletinboard.sqlserver.mappers;
import com.google.protobuf.ByteString; import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import meerkat.protobuf.BulletinBoardAPI.BatchData; import meerkat.protobuf.BulletinBoardAPI.BatchData;
import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.core.RowMapper;
@ -15,7 +16,11 @@ public class BatchDataMapper implements RowMapper<BatchData> {
@Override @Override
public BatchData mapRow(ResultSet rs, int rowNum) throws SQLException { public BatchData mapRow(ResultSet rs, int rowNum) throws SQLException {
return BatchData.newBuilder().setData(ByteString.copyFrom(rs.getBytes(1))).build(); try {
return BatchData.parseFrom(rs.getBytes(1));
} catch (InvalidProtocolBufferException e) {
return BatchData.getDefaultInstance();
}
} }
} }

View File

@ -12,18 +12,13 @@ import java.security.NoSuchAlgorithmException;
import java.security.SignatureException; import java.security.SignatureException;
import java.security.UnrecoverableKeyException; import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException; import java.security.cert.CertificateException;
import java.util.List; import java.util.*;
import java.util.Random;
import com.google.protobuf.ByteString; import com.google.protobuf.ByteString;
import meerkat.comm.CommunicationException; import meerkat.comm.CommunicationException;
import meerkat.crypto.concrete.ECDSASignature; import meerkat.crypto.concrete.ECDSASignature;
import meerkat.protobuf.BulletinBoardAPI.BulletinBoardMessage; import meerkat.protobuf.BulletinBoardAPI.*;
import meerkat.protobuf.BulletinBoardAPI.FilterType;
import meerkat.protobuf.BulletinBoardAPI.MessageFilter;
import meerkat.protobuf.BulletinBoardAPI.MessageFilterList;
import meerkat.protobuf.BulletinBoardAPI.UnsignedBulletinBoardMessage;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.*; import static org.hamcrest.CoreMatchers.*;
@ -31,7 +26,7 @@ import static org.hamcrest.CoreMatchers.*;
public class GenericBulletinBoardServerTest { public class GenericBulletinBoardServerTest {
protected BulletinBoardServer bulletinBoardServer; protected BulletinBoardServer bulletinBoardServer;
private ECDSASignature signers[]; private GenericBatchDigitalSignature signers[];
private ByteString[] signerIDs; private ByteString[] signerIDs;
private Random random; private Random random;
@ -51,6 +46,8 @@ public class GenericBulletinBoardServerTest {
private String[] tags; private String[] tags;
private byte[][] data; private byte[][] data;
private List<CompleteBatch> completeBatches;
private final ThreadMXBean threadBean = ManagementFactory.getThreadMXBean(); // Used to time the tests private final ThreadMXBean threadBean = ManagementFactory.getThreadMXBean(); // Used to time the tests
/** /**
@ -71,10 +68,10 @@ public class GenericBulletinBoardServerTest {
this.bulletinBoardServer = bulletinBoardServer; this.bulletinBoardServer = bulletinBoardServer;
signers = new ECDSASignature[2]; signers = new GenericBatchDigitalSignature[2];
signerIDs = new ByteString[signers.length]; signerIDs = new ByteString[signers.length];
signers[0] = new ECDSASignature(); signers[0] = new GenericBatchDigitalSignature(new ECDSASignature());
signers[1] = new ECDSASignature(); signers[1] = new GenericBatchDigitalSignature(new ECDSASignature());
InputStream keyStream = getClass().getResourceAsStream(KEYFILE_EXAMPLE); InputStream keyStream = getClass().getResourceAsStream(KEYFILE_EXAMPLE);
char[] password = KEYFILE_PASSWORD1.toCharArray(); char[] password = KEYFILE_PASSWORD1.toCharArray();
@ -121,6 +118,11 @@ public class GenericBulletinBoardServerTest {
long end = threadBean.getCurrentThreadCpuTime(); long end = threadBean.getCurrentThreadCpuTime();
System.err.println("Finished initializing GenericBulletinBoardServerTest"); System.err.println("Finished initializing GenericBulletinBoardServerTest");
System.err.println("Time of operation: " + (end - start)); System.err.println("Time of operation: " + (end - start));
// Initialize Batch variables
completeBatches = new ArrayList<CompleteBatch>(10);
} }
private byte randomByte(){ private byte randomByte(){
@ -378,6 +380,182 @@ 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;
CompleteBatch completeBatch = new CompleteBatch();
BoolMsg result;
// Create data
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
result = bulletinBoardServer.beginBatch(completeBatch.getBeginBatchMessage());
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(CloseBatchMessage.newBuilder()
.setBatchId(BATCH_ID)
.setBatchLength(1)
.setSig(completeBatch.getSignature())
.build());
assertThat("Was not able to close batch", result.getValue(), is(true));
// Attempt to open batch again
result = bulletinBoardServer.beginBatch(completeBatch.getBeginBatchMessage());
assertThat("Was able to open a closed batch", result.getValue(), is(false));
// Attempt to add batch data
result = bulletinBoardServer.postBatchMessage(batchMessage);
assertThat("Was able to change a closed batch", result.getValue(), is(false));
}
/**
* Posts a complete batch message
* @throws CommunicationException
*/
public void testPostBatch() throws CommunicationException, SignatureException {
CompleteBatch completeBatch = new CompleteBatch();
int currentBatch = completeBatches.size();
BoolMsg result;
// Define batch data
String[] tempBatchTags = new String[]{randomString(),randomString(),randomString()};
byte[][] tempBatchData = new byte[Math.abs(randomByte())][];
for (int i = 0 ; i < tempBatchData.length ; i++) {
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(CloseBatchMessage.newBuilder()
.setBatchId(currentBatch)
.setBatchLength(tempBatchData.length)
.setSig(completeBatch.getSignature())
.build());
assertThat("Could not close batch " + currentBatch, result.getValue(), is(true));
// Update locally stored batches
completeBatches.add(completeBatch);
}
public void testReadBatch() throws CommunicationException {
for (CompleteBatch completeBatch : completeBatches) {
List<BatchData> batchDataList =
bulletinBoardServer.readBatch(BatchSpecificationMessage.newBuilder()
.setSignerId(completeBatch.getBeginBatchMessage().getSignerId())
.setBatchId(completeBatch.getBeginBatchMessage().getBatchId())
.setStartPosition(0)
.build());
assertThat("Non-matching batch data for batch " + completeBatch.getBeginBatchMessage().getBatchId(),
completeBatch.getBatchDataList().equals(batchDataList), is(true));
}
}
public void close(){ public void close(){
signers[0].clearSigningKey(); signers[0].clearSigningKey();
signers[1].clearSigningKey(); signers[1].clearSigningKey();

View File

@ -111,6 +111,39 @@ public class MySQLBulletinBoardServerTest {
System.err.println("Time of operation: " + (end - start)); System.err.println("Time of operation: " + (end - start));
} }
@Test
public void testBatchPostAfterClose() {
try{
serverTest.testBatchPostAfterClose();
} catch (Exception e) {
System.err.println(e.getMessage());
fail(e.getMessage());
}
}
@Test
public void testBatch() {
final int BATCH_NUM = 20;
try{
for (int i = 0 ; i < BATCH_NUM ; i++) {
serverTest.testPostBatch();
}
} catch (Exception e) {
System.err.println(e.getMessage());
fail(e.getMessage());
}
try{
serverTest.testReadBatch();
} catch (Exception e) {
System.err.println(e.getMessage());
fail(e.getMessage());
}
}
@After @After
public void close() { public void close() {
System.err.println("Starting to close MySQLBulletinBoardServerTest"); System.err.println("Starting to close MySQLBulletinBoardServerTest");

View File

@ -16,6 +16,7 @@ public interface BulletinBoardConstants {
// Other Constants // Other Constants
public static final String BATCH_TAG = "Batch"; public static final String BATCH_TAG = "@BATCH";
public static final String BATCH_ID_TAG_PREFIX = "#";
} }

View File

@ -22,6 +22,7 @@ public class CompleteBatch {
} }
public CompleteBatch(BeginBatchMessage newBeginBatchMessage) { public CompleteBatch(BeginBatchMessage newBeginBatchMessage) {
this();
beginBatchMessage = newBeginBatchMessage; beginBatchMessage = newBeginBatchMessage;
} }
@ -47,6 +48,10 @@ public class CompleteBatch {
return signature; return signature;
} }
public void setBeginBatchMessage(BeginBatchMessage beginBatchMessage) {
this.beginBatchMessage = beginBatchMessage;
}
public void appendBatchData(BatchData newBatchData) { public void appendBatchData(BatchData newBatchData) {
batchDataList.add(newBatchData); batchDataList.add(newBatchData);
} }

View File

@ -3,7 +3,6 @@ package meerkat.bulletinboard;
import com.google.protobuf.ByteString; import com.google.protobuf.ByteString;
import com.google.protobuf.Message; import com.google.protobuf.Message;
import meerkat.crypto.DigitalSignature; import meerkat.crypto.DigitalSignature;
import meerkat.protobuf.BulletinBoardAPI.BeginBatchMessage;
import meerkat.protobuf.BulletinBoardAPI.BatchData; import meerkat.protobuf.BulletinBoardAPI.BatchData;
import meerkat.protobuf.Crypto; import meerkat.protobuf.Crypto;
@ -11,10 +10,11 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.security.InvalidKeyException; import java.security.InvalidKeyException;
import java.security.KeyStore; import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.SignatureException; import java.security.SignatureException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException; import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException; import java.security.cert.CertificateException;
import java.util.List;
/** /**
* Created by Arbel Deutsch Peled on 20-Dec-15. * Created by Arbel Deutsch Peled on 20-Dec-15.
@ -77,6 +77,12 @@ public class GenericBatchDigitalSignature implements BatchDigitalSignature{
return digitalSignature.verify(); return digitalSignature.verify();
} }
@Override
public KeyStore.Builder getPKCS12KeyStoreBuilder(InputStream keyStream, char[] password)
throws IOException, CertificateException, KeyStoreException, NoSuchAlgorithmException {
return digitalSignature.getPKCS12KeyStoreBuilder(keyStream, password);
}
@Override @Override
public void loadSigningCertificate(KeyStore.Builder keyStoreBuilder) throws IOException, CertificateException, UnrecoverableKeyException { public void loadSigningCertificate(KeyStore.Builder keyStoreBuilder) throws IOException, CertificateException, UnrecoverableKeyException {
digitalSignature.loadSigningCertificate(keyStoreBuilder); digitalSignature.loadSigningCertificate(keyStoreBuilder);

View File

@ -5,11 +5,13 @@ import com.google.protobuf.Message;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.security.InvalidKeyException;
import java.security.KeyStore; import java.security.KeyStore;
import java.security.SignatureException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException; import java.security.cert.CertificateException;
import java.security.SignatureException;
import java.security.InvalidKeyException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import static meerkat.protobuf.Crypto.*; import static meerkat.protobuf.Crypto.*;
/** /**
@ -71,6 +73,20 @@ public interface DigitalSignature {
*/ */
public boolean verify(); public boolean verify();
/**
* Load a keystore from an input stream in PKCS12 format.
*
* @param keyStream
* @param password
* @return
* @throws IOException
* @throws CertificateException
* @throws KeyStoreException
* @throws NoSuchAlgorithmException
*/
public KeyStore.Builder getPKCS12KeyStoreBuilder(InputStream keyStream, char[] password)
throws IOException, CertificateException, KeyStoreException, NoSuchAlgorithmException;
/** /**
* Loads a private signing key. The keystore must include both the public and private * Loads a private signing key. The keystore must include both the public and private
* key parts. * key parts.

View File

@ -211,6 +211,7 @@ public class ECDSASignature implements DigitalSignature {
* @throws KeyStoreException * @throws KeyStoreException
* @throws NoSuchAlgorithmException * @throws NoSuchAlgorithmException
*/ */
@Override
public KeyStore.Builder getPKCS12KeyStoreBuilder(InputStream keyStream, char[] password) public KeyStore.Builder getPKCS12KeyStoreBuilder(InputStream keyStream, char[] password)
throws IOException, CertificateException, KeyStoreException, NoSuchAlgorithmException { throws IOException, CertificateException, KeyStoreException, NoSuchAlgorithmException {
KeyStore keyStore = KeyStore.getInstance(KEYSTORE_TYPE); KeyStore keyStore = KeyStore.getInstance(KEYSTORE_TYPE);