diff --git a/bulletin-board-server/build.gradle b/bulletin-board-server/build.gradle index 62e4b0c..63ba47d 100644 --- a/bulletin-board-server/build.gradle +++ b/bulletin-board-server/build.gradle @@ -79,6 +79,11 @@ test { exclude '**/*IntegrationTest*' } +task myTest(type: Test) { + include '**/*MySQL*Test*' + outputs.upToDateWhen { false } +} + task dbTest(type: Test) { include '**/*H2*Test*' include '**/*MySQL*Test*' diff --git a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/BulletinBoardSQLServer.java b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/BulletinBoardSQLServer.java index 1bb32d8..5b4fac9 100644 --- a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/BulletinBoardSQLServer.java +++ b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/BulletinBoardSQLServer.java @@ -51,25 +51,87 @@ public class BulletinBoardSQLServer implements BulletinBoardServer{ */ public static enum QueryType { - FIND_MSG_ID(new String[] {"MsgId"}), - INSERT_MSG(new String[] {"MsgId","Msg"}), - INSERT_NEW_TAG(new String[] {"Tag"}), - CONNECT_TAG(new String[] {"EntryNum","Tag"}), - ADD_SIGNATURE(new String[] {"EntryNum","SignerId","Signature"}), - GET_SIGNATURES(new String[] {"EntryNum"}), - 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"}), - GET_BATCH_TAGS(new String[] {"SignerId", "BatchId"}), - MOVE_BATCH_TAGS(new String[] {"EntryNum", "SignerId", "BatchId"}); + FIND_MSG_ID( + new String[] {"MsgId"}, + new int[] {Types.BLOB} + ), + + FIND_TAG_ID( + new String[] {"Tag"}, + new int[] {Types.VARCHAR} + ), + + INSERT_MSG( + new String[] {"MsgId","Msg"}, + new int[] {Types.BLOB, Types.BLOB} + ), + + 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 int[] paramTypes; - private QueryType(String[] paramNames) { + private QueryType(String[] paramNames, int[] paramTypes) { this.paramNames = paramNames; + this.paramTypes = paramTypes; } public String[] getParamNames() { @@ -80,6 +142,14 @@ public class BulletinBoardSQLServer implements BulletinBoardServer{ 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(); } - @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); } @@ -328,23 +405,23 @@ public class BulletinBoardSQLServer implements BulletinBoardServer{ byte[] msgID; long entryNum; - + ProtocolStringList tagList; String[] tags; - + List signatureList; Signature[] signatures; - + // Calculate message ID (depending only on the the unsigned message) - + digest.reset(); digest.update(msg.getMsg()); - + msgID = digest.digest(); - + // Add message to table if needed and store entry number of message. - + sql = sqlQueryProvider.getSQLString(QueryType.FIND_MSG_ID); Map namedParameters = new HashMap(); namedParameters.put(QueryType.FIND_MSG_ID.getParamName(0),msgID); @@ -366,21 +443,21 @@ public class BulletinBoardSQLServer implements BulletinBoardServer{ entryNum = keyHolder.getKey().longValue(); } - + // Retrieve tags and store new ones in tag table. - + try { - - tagList = msg.getMsg().getTagList(); - tags = new String[tagList.size()]; - tags = tagList.toArray(tags); - - insertNewTags(tags); - + + tagList = msg.getMsg().getTagList(); + tags = new String[tagList.size()]; + tags = tagList.toArray(tags); + + insertNewTags(tags); + } catch (SQLException e) { throw new CommunicationException(e.getMessage()); } - + // Connect message to tags. sql = sqlQueryProvider.getSQLString(QueryType.CONNECT_TAG); @@ -394,13 +471,13 @@ public class BulletinBoardSQLServer implements BulletinBoardServer{ } jdbcTemplate.batchUpdate(sql, namedParameterArray); - + // Retrieve signatures. - - signatureList = msg.getSigList(); - signatures = new Signature[signatureList.size()]; - signatures = signatureList.toArray(signatures); - + + signatureList = msg.getSigList(); + signatures = new Signature[signatureList.size()]; + signatures = signatureList.toArray(signatures); + // Connect message to signatures. sql = sqlQueryProvider.getSQLString(QueryType.ADD_SIGNATURE); @@ -417,6 +494,12 @@ public class BulletinBoardSQLServer implements BulletinBoardServer{ jdbcTemplate.batchUpdate(sql,namedParameterArray); 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 @@ -477,7 +560,8 @@ public class BulletinBoardSQLServer implements BulletinBoardServer{ // Run query - List msgBuilders = jdbcTemplate.query(sqlBuilder.toString(), namedParameters, messageMapper); + List msgBuilders = + jdbcTemplate.query(sqlBuilder.toString(), namedParameters, messageMapper); // 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 * @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){ + 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(); - namedParameters.addValue(QueryType.GET_BATCH_MESSAGE_ENTRY.getParamName(0), signerId); - namedParameters.addValue(QueryType.GET_BATCH_MESSAGE_ENTRY.getParamName(1), batchId); + BulletinBoardMessageList messageList = readMessages(filterList); - List result = jdbcTemplate.query(sql,namedParameters,new LongMapper()); - - return (result.size() > 0); + return (messageList.getMessageList().size() > 0); } @@ -562,7 +664,7 @@ public class BulletinBoardSQLServer implements BulletinBoardServer{ } @Override - public BoolMsg postBatchMessage(BatchMessage batchMessage) { + public BoolMsg postBatchMessage(BatchMessage batchMessage) throws CommunicationException{ // Check if batch is closed 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(1),batchMessage.getBatchId()); 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); @@ -641,11 +743,12 @@ public class BulletinBoardSQLServer implements BulletinBoardServer{ completeBatch.setSignature(message.getSig()); - try { - signer.verify(completeBatch); - } catch (CertificateException | InvalidKeyException | SignatureException e) { - return BoolMsg.newBuilder().setValue(false).build(); - } +// try { +// TODO: Actual verification +// //signer.verify(completeBatch); +// } catch (CertificateException | InvalidKeyException | SignatureException e) { +// return BoolMsg.newBuilder().setValue(false).build(); +// } // Batch verified: finalize it @@ -660,28 +763,25 @@ public class BulletinBoardSQLServer implements BulletinBoardServer{ .setMsg(UnsignedBulletinBoardMessage.newBuilder() .addAllTag(tags) .addTag(BATCH_TAG) + .addTag(batchIdToTag(batchId)) .setData(message.getSig().getSignerId()) .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.addValue(QueryType.INSERT_MSG.getParamName(0), msgID); - namedParameters.addValue(QueryType.INSERT_MSG.getParamName(1), bulletinBoardMessage); + namedParameters.addValue(QueryType.REMOVE_BATCH_TAGS.getParamName(0), signerId); + 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); + // Return TRUE + 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(1),message.getBatchId()); + namedParameters.addValue(QueryType.GET_BATCH_MESSAGE_DATA.getParamName(2),message.getStartPosition()); return jdbcTemplate.query(sql, namedParameters, new BatchDataMapper()); } diff --git a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/MySQLQueryProvider.java b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/MySQLQueryProvider.java index 59e6106..c8357a3 100644 --- a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/MySQLQueryProvider.java +++ b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/MySQLQueryProvider.java @@ -1,6 +1,7 @@ package meerkat.bulletinboard.sqlserver; import com.mysql.jdbc.jdbc2.optional.MysqlDataSource; +import meerkat.bulletinboard.BulletinBoardConstants; import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer.SQLQueryProvider; import meerkat.protobuf.BulletinBoardAPI.FilterType; @@ -53,6 +54,11 @@ public class MySQLQueryProvider implements SQLQueryProvider { "SELECT EntryNum From MsgTable WHERE MsgId = :{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: return "SELECT MsgTable.EntryNum, MsgTable.Msg FROM MsgTable"; @@ -75,11 +81,11 @@ public class MySQLQueryProvider implements SQLQueryProvider { 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}", + + " 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)); @@ -92,12 +98,21 @@ public class MySQLQueryProvider implements SQLQueryProvider { QueryType.GET_BATCH_MESSAGE_DATA.getParamName(1), 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: 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)); + QueryType.CHECK_BATCH_LENGTH.getParamName(0), + QueryType.CHECK_BATCH_LENGTH.getParamName(1)); case CONNECT_BATCH_TAG: return MessageFormat.format( @@ -109,19 +124,16 @@ public class MySQLQueryProvider implements SQLQueryProvider { case GET_BATCH_TAGS: 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", QueryType.GET_BATCH_TAGS.getParamName(0), QueryType.GET_BATCH_TAGS.getParamName(1)); - case MOVE_BATCH_TAGS: + case REMOVE_BATCH_TAGS: return MessageFormat.format( - "INSERT INTO MsgTagTable (EntryNum, TagId) " - + " SELECT {0}, TagId FROM BatchTagTable WHERE SignerId = {1} AND BatchId = {2};" - + " DELETE FROM BatchTagTable WHERE SignerId = {1} AND BatchId = {2}", - QueryType.GET_BATCH_TAGS.getParamName(0), - QueryType.GET_BATCH_TAGS.getParamName(1), - QueryType.GET_BATCH_TAGS.getParamName(2)); + "DELETE FROM BatchTagTable WHERE SignerId = :{0} AND BatchId = :{1}", + QueryType.REMOVE_BATCH_TAGS.getParamName(0), + QueryType.REMOVE_BATCH_TAGS.getParamName(1)); default: throw new IllegalArgumentException("Cannot serve a query of type " + queryType); @@ -187,6 +199,7 @@ public class MySQLQueryProvider implements SQLQueryProvider { dataSource.setDatabaseName(dbName); dataSource.setUser(username); dataSource.setPassword(password); + dataSource.setAllowMultiQueries(true); return dataSource; } diff --git a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/mappers/BatchDataMapper.java b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/mappers/BatchDataMapper.java index 81e6cda..bc4ea26 100644 --- a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/mappers/BatchDataMapper.java +++ b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/mappers/BatchDataMapper.java @@ -1,6 +1,7 @@ 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; @@ -15,7 +16,11 @@ public class BatchDataMapper implements RowMapper { @Override 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(); + } } } diff --git a/bulletin-board-server/src/test/java/meerkat/bulletinboard/GenericBulletinBoardServerTest.java b/bulletin-board-server/src/test/java/meerkat/bulletinboard/GenericBulletinBoardServerTest.java index 4799e0d..86a32de 100644 --- a/bulletin-board-server/src/test/java/meerkat/bulletinboard/GenericBulletinBoardServerTest.java +++ b/bulletin-board-server/src/test/java/meerkat/bulletinboard/GenericBulletinBoardServerTest.java @@ -12,18 +12,13 @@ import java.security.NoSuchAlgorithmException; import java.security.SignatureException; import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; -import java.util.List; -import java.util.Random; +import java.util.*; import com.google.protobuf.ByteString; import meerkat.comm.CommunicationException; import meerkat.crypto.concrete.ECDSASignature; -import meerkat.protobuf.BulletinBoardAPI.BulletinBoardMessage; -import meerkat.protobuf.BulletinBoardAPI.FilterType; -import meerkat.protobuf.BulletinBoardAPI.MessageFilter; -import meerkat.protobuf.BulletinBoardAPI.MessageFilterList; -import meerkat.protobuf.BulletinBoardAPI.UnsignedBulletinBoardMessage; +import meerkat.protobuf.BulletinBoardAPI.*; import static org.junit.Assert.*; import static org.hamcrest.CoreMatchers.*; @@ -31,7 +26,7 @@ import static org.hamcrest.CoreMatchers.*; public class GenericBulletinBoardServerTest { protected BulletinBoardServer bulletinBoardServer; - private ECDSASignature signers[]; + private GenericBatchDigitalSignature signers[]; private ByteString[] signerIDs; private Random random; @@ -51,6 +46,8 @@ public class GenericBulletinBoardServerTest { private String[] tags; private byte[][] data; + private List completeBatches; + private final ThreadMXBean threadBean = ManagementFactory.getThreadMXBean(); // Used to time the tests /** @@ -71,10 +68,10 @@ public class GenericBulletinBoardServerTest { this.bulletinBoardServer = bulletinBoardServer; - signers = new ECDSASignature[2]; + signers = new GenericBatchDigitalSignature[2]; signerIDs = new ByteString[signers.length]; - signers[0] = new ECDSASignature(); - signers[1] = new ECDSASignature(); + signers[0] = new GenericBatchDigitalSignature(new ECDSASignature()); + signers[1] = new GenericBatchDigitalSignature(new ECDSASignature()); InputStream keyStream = getClass().getResourceAsStream(KEYFILE_EXAMPLE); char[] password = KEYFILE_PASSWORD1.toCharArray(); @@ -121,6 +118,11 @@ public class GenericBulletinBoardServerTest { long end = threadBean.getCurrentThreadCpuTime(); System.err.println("Finished initializing GenericBulletinBoardServerTest"); System.err.println("Time of operation: " + (end - start)); + + // Initialize Batch variables + + completeBatches = new ArrayList(10); + } private byte randomByte(){ @@ -377,6 +379,182 @@ public class GenericBulletinBoardServerTest { System.err.println("Time of operation: " + (end - start)); } + + /** + * 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 dataOrder = new ArrayList(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 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(){ signers[0].clearSigningKey(); diff --git a/bulletin-board-server/src/test/java/meerkat/bulletinboard/MySQLBulletinBoardServerTest.java b/bulletin-board-server/src/test/java/meerkat/bulletinboard/MySQLBulletinBoardServerTest.java index e473931..42c11f7 100644 --- a/bulletin-board-server/src/test/java/meerkat/bulletinboard/MySQLBulletinBoardServerTest.java +++ b/bulletin-board-server/src/test/java/meerkat/bulletinboard/MySQLBulletinBoardServerTest.java @@ -111,6 +111,39 @@ public class MySQLBulletinBoardServerTest { 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 public void close() { System.err.println("Starting to close MySQLBulletinBoardServerTest"); diff --git a/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardConstants.java b/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardConstants.java index 7fe3a43..0db1d3f 100644 --- a/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardConstants.java +++ b/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardConstants.java @@ -16,6 +16,7 @@ public interface BulletinBoardConstants { // Other Constants - public static final String BATCH_TAG = "Batch"; + public static final String BATCH_TAG = "@BATCH"; + public static final String BATCH_ID_TAG_PREFIX = "#"; } diff --git a/meerkat-common/src/main/java/meerkat/bulletinboard/CompleteBatch.java b/meerkat-common/src/main/java/meerkat/bulletinboard/CompleteBatch.java index 6deec77..19c57e3 100644 --- a/meerkat-common/src/main/java/meerkat/bulletinboard/CompleteBatch.java +++ b/meerkat-common/src/main/java/meerkat/bulletinboard/CompleteBatch.java @@ -22,6 +22,7 @@ public class CompleteBatch { } public CompleteBatch(BeginBatchMessage newBeginBatchMessage) { + this(); beginBatchMessage = newBeginBatchMessage; } @@ -47,6 +48,10 @@ public class CompleteBatch { return signature; } + public void setBeginBatchMessage(BeginBatchMessage beginBatchMessage) { + this.beginBatchMessage = beginBatchMessage; + } + public void appendBatchData(BatchData newBatchData) { batchDataList.add(newBatchData); } diff --git a/meerkat-common/src/main/java/meerkat/bulletinboard/GenericBatchDigitalSignature.java b/meerkat-common/src/main/java/meerkat/bulletinboard/GenericBatchDigitalSignature.java index 422e8b0..7174b42 100644 --- a/meerkat-common/src/main/java/meerkat/bulletinboard/GenericBatchDigitalSignature.java +++ b/meerkat-common/src/main/java/meerkat/bulletinboard/GenericBatchDigitalSignature.java @@ -3,7 +3,6 @@ package meerkat.bulletinboard; import com.google.protobuf.ByteString; import com.google.protobuf.Message; import meerkat.crypto.DigitalSignature; -import meerkat.protobuf.BulletinBoardAPI.BeginBatchMessage; import meerkat.protobuf.BulletinBoardAPI.BatchData; import meerkat.protobuf.Crypto; @@ -11,10 +10,11 @@ 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; -import java.util.List; /** * Created by Arbel Deutsch Peled on 20-Dec-15. @@ -77,6 +77,12 @@ public class GenericBatchDigitalSignature implements BatchDigitalSignature{ 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); diff --git a/meerkat-common/src/main/java/meerkat/crypto/DigitalSignature.java b/meerkat-common/src/main/java/meerkat/crypto/DigitalSignature.java index e7b64e5..76c32a6 100644 --- a/meerkat-common/src/main/java/meerkat/crypto/DigitalSignature.java +++ b/meerkat-common/src/main/java/meerkat/crypto/DigitalSignature.java @@ -5,11 +5,13 @@ import com.google.protobuf.Message; import java.io.IOException; import java.io.InputStream; -import java.security.InvalidKeyException; import java.security.KeyStore; -import java.security.SignatureException; -import java.security.UnrecoverableKeyException; 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.*; /** @@ -71,6 +73,20 @@ public interface DigitalSignature { */ 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 * key parts. diff --git a/meerkat-common/src/main/java/meerkat/crypto/concrete/ECDSASignature.java b/meerkat-common/src/main/java/meerkat/crypto/concrete/ECDSASignature.java index 28e4600..ab8084b 100644 --- a/meerkat-common/src/main/java/meerkat/crypto/concrete/ECDSASignature.java +++ b/meerkat-common/src/main/java/meerkat/crypto/concrete/ECDSASignature.java @@ -211,6 +211,7 @@ public class ECDSASignature implements DigitalSignature { * @throws KeyStoreException * @throws NoSuchAlgorithmException */ + @Override public KeyStore.Builder getPKCS12KeyStoreBuilder(InputStream keyStream, char[] password) throws IOException, CertificateException, KeyStoreException, NoSuchAlgorithmException { KeyStore keyStore = KeyStore.getInstance(KEYSTORE_TYPE);