From 58c18163249e6ee5833ca329dc9e1dc4f8aa30be Mon Sep 17 00:00:00 2001 From: Arbel Deutsch Peled Date: Wed, 11 Nov 2015 14:12:34 +0200 Subject: [PATCH] Added basic Bulletin Board server functionality --- .gitignore | 3 + .../httpserver/BulletinBoardHttpServer.java | 101 ++++++++++++ .../sqlserver/BulletinBoardSQLServer.java | 147 ++++++++++++++++++ .../sqlserver/SQLiteBulletinBoardServer.java | 95 +++++++++++ .../bulletinboard/SQLiteIntegrationTest.java | 41 +++++ .../meerkat/bulletinboard/BulletinBoard.java | 1 - .../bulletinboard/BulletinBoardServer.java | 45 ++++++ .../meerkat/bulletinboard/MessageFilter.java | 17 +- .../meerkat/comm/CommunicationException.java | 29 ++++ .../src/main/java/meerkat/comm/MessageID.java | 10 -- .../src/main/proto/meerkat/voting.proto | 7 +- 11 files changed, 482 insertions(+), 14 deletions(-) create mode 100644 bulletin-board-server/src/main/java/meerkat/bulletinboard/httpserver/BulletinBoardHttpServer.java create mode 100644 bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/BulletinBoardSQLServer.java create mode 100644 bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/SQLiteBulletinBoardServer.java create mode 100644 bulletin-board-server/src/test/java/meerkat/bulletinboard/SQLiteIntegrationTest.java create mode 100644 meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardServer.java delete mode 100644 meerkat-common/src/main/java/meerkat/comm/MessageID.java diff --git a/.gitignore b/.gitignore index 93ba99d..429a385 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,6 @@ out *.ipr *.iws **/*.swp +*.prefs +*.project +*.classpath diff --git a/bulletin-board-server/src/main/java/meerkat/bulletinboard/httpserver/BulletinBoardHttpServer.java b/bulletin-board-server/src/main/java/meerkat/bulletinboard/httpserver/BulletinBoardHttpServer.java new file mode 100644 index 0000000..10683cb --- /dev/null +++ b/bulletin-board-server/src/main/java/meerkat/bulletinboard/httpserver/BulletinBoardHttpServer.java @@ -0,0 +1,101 @@ +package meerkat.bulletinboard.httpserver; + +import java.io.IOException; + +import javax.servlet.ServletConfig; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import meerkat.bulletinboard.BulletinBoardServer; +import meerkat.bulletinboard.sqlserver.SQLiteBulletinBoardServer; +import meerkat.comm.CommunicationException; +import meerkat.protobuf.Voting.BulletinBoardMessage; + +public class BulletinBoardHttpServer extends HttpServlet { + + /** + * Auto-generated UID. + */ + private static final long serialVersionUID = -1263665607729456165L; + + BulletinBoardServer bbs; + + @Override + public void init(ServletConfig config) throws ServletException { + //TODO: Make this generic + bbs = new SQLiteBulletinBoardServer(); + + try { + bbs.init(); + } catch (CommunicationException e) { + // TODO Log error + throw new ServletException("Servlet failed to initialize: " + e.getMessage()); + } + } + + /** + * This procedure handles (POST) requests to post messages to the Bulletin Board. + */ + @Override + protected void doPost( HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + + BulletinBoardMessage message; + + try{ + message = BulletinBoardMessage.newBuilder() + .mergeFrom(request.getInputStream()) + .build(); + } catch(Exception e){ + //TODO: Log invalid request + return; + } + + try { + bbs.postMessage(message); + } catch (CommunicationException e) { + // TODO Log DB communication error + } + + } + + /** + * This procedure handles (GET) requests which request data from the Bulletin Board. + */ + @Override + protected void doGet( HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + + BulletinBoardMessage message; + + try{ + message = BulletinBoardMessage.newBuilder() + .mergeFrom(request.getInputStream()) + .build(); + } catch(Exception e){ + //TODO: Log invalid request + return; + } + + try { + bbs.postMessage(message); + } catch (CommunicationException e) { + // TODO Log DB communication error + } + + } + + @Override + public void destroy() { + + try { + bbs.close(); + } catch (CommunicationException e) { + // TODO Log DB communication error + } + + } + +} 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 new file mode 100644 index 0000000..0ed06c9 --- /dev/null +++ b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/BulletinBoardSQLServer.java @@ -0,0 +1,147 @@ +package meerkat.bulletinboard.sqlserver; + +import java.util.List; + +import com.google.protobuf.ProtocolStringList; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +import meerkat.bulletinboard.BulletinBoardServer; +import meerkat.bulletinboard.MessageFilter; +import meerkat.comm.CommunicationException; +import meerkat.protobuf.Crypto.SignatureVerificationKey; +import meerkat.protobuf.Voting.BulletinBoardMessage; +import meerkat.crypto.Digest; +import meerkat.crypto.concrete.SHA256Digest; +import meerkat.protobuf.Voting.MessageID; + +public abstract class BulletinBoardSQLServer implements BulletinBoardServer{ + + protected Connection connection; + + protected Digest digest; + + protected List trusteeSignatureVerificationArray; + protected int minTrusteeSignatures; + + protected List> pollingCommitteeSignatureVerificationKeyArray; + protected int minCommiteeSignatures; + + /** + * This method initializes the signatures but does not implement the DB connection. + * Any full (non-abstract) extension of this class should + * 1. Establish a DB connection, and + * 2. Call this procedure + */ + @Override + public void init() throws CommunicationException { + // TODO write signature reading part. + + digest = new SHA256Digest(); + } + + /** + * This method verifies the authenticity of the received message based on + * the stored signatures. + * + * @param msg + * is the message to authenticate (containing the signature). + * @return TRUE if the message is authenticated and FALSE otherwise. + */ + private boolean verifyMessage(BulletinBoardMessage msg) { + return true; + } + + /** + * This procedure makes sure that all tags in the given list have an entry in the tags list. + * @param tagIterator + */ + protected abstract void insertNewTags(String[] tags) throws CommunicationException; + + @Override + public boolean postMessage(BulletinBoardMessage msg) throws CommunicationException { + if (!verifyMessage(msg)) { + return false; + } + + PreparedStatement pstmt; + String sql; + byte[] msgID; + + ProtocolStringList tagList; + String[] tags; + + digest.reset(); + digest.update(msg); + + msgID = digest.digest(); + + try { + + sql = "INSERT INTO MsgTable (Id, Data, Signature) VALUES(?,?,?)"; + pstmt = connection.prepareStatement(sql); + pstmt.setBytes(1, msgID); + pstmt.setBytes(2,msg.getMsg().getData().toByteArray()); + pstmt.setBytes(3,msg.getSig().toByteArray()); + pstmt.executeUpdate(); + pstmt.close(); + + tagList = msg.getMsg().getTagsList(); + tags = new String[tagList.size()]; + + insertNewTags(tags); + + sql = "INSERT INTO MsgTagTable (MsgId, TagId) SELECT (?, TagId) FROM TagTable WHERE tag=?"; + pstmt = connection.prepareStatement(sql); + + for (String tag : tags){ + pstmt.setBytes(1, msgID); + pstmt.setString(2, tag); + pstmt.addBatch(); + } + + pstmt.executeBatch(); + pstmt.close(); + + } catch (SQLException e) { + throw new CommunicationException("Error accessing DB: " + e.getMessage()); + } + + return true; + } + + @Override + public List readMessages(MessageFilter filter, int max) { + // TODO Auto-generated method stub + return null; + } + + public void testPrint(){ + + try { + + Statement statement = connection.createStatement(); + ResultSet rs = statement.executeQuery("select * from MsgTable"); + while (rs.next()) { + // read the result set + System.out.println("data = " + rs.getString("Data")); + System.out.println("id = " + rs.getInt("Id")); + } + + rs = statement.executeQuery("select * from MsgTagTable"); + while (rs.next()) { + // read the result set + System.out.println("MsgId = " + rs.getInt("MsgId")); + System.out.println("TagId = " + rs.getString("TagId")); + } + } catch(SQLException e){ + System.out.println("Error reading from DB"); + } + + } + +} diff --git a/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/SQLiteBulletinBoardServer.java b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/SQLiteBulletinBoardServer.java new file mode 100644 index 0000000..6c9cf40 --- /dev/null +++ b/bulletin-board-server/src/main/java/meerkat/bulletinboard/sqlserver/SQLiteBulletinBoardServer.java @@ -0,0 +1,95 @@ +package meerkat.bulletinboard.sqlserver; + +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.List; + +import meerkat.bulletinboard.MessageFilter; +import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer; +import meerkat.comm.CommunicationException; +import meerkat.protobuf.Voting.BulletinBoardMessage; + + +public class SQLiteBulletinBoardServer extends BulletinBoardSQLServer { + + protected static final int TIMEOUT = 20; + + /** + * This procedure initializes: + * 1. The database connection + * 2. The database tables (if they do not yet exist). + * 3. + */ + @Override + public void init() throws CommunicationException { + + try{ + + connection = DriverManager.getConnection("jdbc:sqlite:./local-instances/meerkat.db"); + Statement statement = connection.createStatement(); + statement.setQueryTimeout(TIMEOUT); + + statement.executeUpdate("CREATE TABLE IF NOT EXISTS MsgTable (EntryNum INTEGER, Id varbinary(1000) UNIQUE, Data varbinary(1000), Signature varbinary(1000), PRIMARY KEY (EntryNum ASC))"); + statement.executeUpdate("CREATE TABLE IF NOT EXISTS TagTable (Id int, Tag v archar(50) UNIQUE, PRIMARY KEY (Id ASC))"); + statement.executeUpdate("CREATE TABLE IF NOT EXISTS MsgTagTable (MsgId varbinary(1000), TagId int, FOREIGN KEY (MsgId) REFERENCES MsgTable(Id), FOREIGN KEY (TagId) REFERENCES TagTable(Id))"); + + statement.close(); + + super.init(); + + } catch (SQLException e) { + + throw new CommunicationException("Couldn't form a connection with the database"); + + } + + } + + + @Override + public List readMessages(MessageFilter filter, int max) { + // TODO Auto-generated method stub + return null; + } + + public void close() throws CommunicationException{ + + try{ + connection.close(); + } catch (SQLException e) { + + throw new CommunicationException("Couldn't close connection to the database"); + + } + + } + + + @Override + protected void insertNewTags(String[] tags) throws CommunicationException { + + PreparedStatement pstmt; + String sql; + + try { + + sql = "INSERT OR IGNORE INTO TagTable(Tag) VALUES (?)"; + pstmt = connection.prepareStatement(sql); + + for (String tag : tags){ + pstmt.setString(1, tag); + pstmt.addBatch(); + } + + pstmt.executeBatch(); + pstmt.close(); + + } catch (SQLException e){ + throw new CommunicationException("Error adding new tags to table"); + } + + } + +} diff --git a/bulletin-board-server/src/test/java/meerkat/bulletinboard/SQLiteIntegrationTest.java b/bulletin-board-server/src/test/java/meerkat/bulletinboard/SQLiteIntegrationTest.java new file mode 100644 index 0000000..0ebb8e0 --- /dev/null +++ b/bulletin-board-server/src/test/java/meerkat/bulletinboard/SQLiteIntegrationTest.java @@ -0,0 +1,41 @@ +package meerkat.bulletinboard; + + +import com.google.protobuf.ByteString; + +import meerkat.bulletinboard.sqlserver.SQLiteBulletinBoardServer; +import meerkat.comm.CommunicationException; +import meerkat.protobuf.Crypto.*; +import meerkat.protobuf.Voting.*; + +public class SQLiteIntegrationTest { + public static void main(){ + byte[] b1 = {(byte) 1, (byte)2, (byte) 3, (byte) 4}; + byte[] b2 = {(byte) 11, (byte)12, (byte) 13, (byte) 14}; + + BulletinBoardMessage msg = BulletinBoardMessage.newBuilder() + .setMsg(UnsignedBulletinBoardMessage.newBuilder() + .setTags(0, "signature") + .setTags(1, "Trustee") + .setData(ByteString.copyFrom(b1)) + .build()) + .setSig(Signature.newBuilder() + .setType(SignatureType.DSA) + .setData(ByteString.copyFrom(b2)) + .build()) + .build(); + + SQLiteBulletinBoardServer bbs = new SQLiteBulletinBoardServer(); + + try{ + bbs.init(); + + bbs.postMessage(msg); + bbs.testPrint(); + + bbs.close(); + } catch(CommunicationException e){ + System.out.println(e.getMessage()); + } + } +} diff --git a/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoard.java b/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoard.java index 980c8e9..eb31c6d 100644 --- a/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoard.java +++ b/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoard.java @@ -4,7 +4,6 @@ import meerkat.comm.*; import static meerkat.protobuf.Voting.*; import java.util.List; -import java.util.Set; /** * Created by talm on 24/10/15. diff --git a/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardServer.java b/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardServer.java new file mode 100644 index 0000000..fdd0b3c --- /dev/null +++ b/meerkat-common/src/main/java/meerkat/bulletinboard/BulletinBoardServer.java @@ -0,0 +1,45 @@ +package meerkat.bulletinboard; + +import java.util.List; + +import meerkat.comm.CommunicationException; +import meerkat.protobuf.Voting.BulletinBoardMessage; + +/** + * 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. + */ + +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. + */ + public void init() 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. + * @throws CommunicationException on DB connection error. + */ + public boolean postMessage(BulletinBoardMessage msg) throws CommunicationException; + + /** + * Read all messages posted matching the given filter. + * @param filter return only messages that match the filter (null means no filtering). + * @param max maximum number of messages to return (0=no limit) + * @return + */ + List readMessages(MessageFilter filter, int max); + + /** + * This method closes the connection to the DB. + * @throws CommunicationException on DB connection error. + */ + public void close() throws CommunicationException; +} diff --git a/meerkat-common/src/main/java/meerkat/bulletinboard/MessageFilter.java b/meerkat-common/src/main/java/meerkat/bulletinboard/MessageFilter.java index 45701f0..08dcb52 100644 --- a/meerkat-common/src/main/java/meerkat/bulletinboard/MessageFilter.java +++ b/meerkat-common/src/main/java/meerkat/bulletinboard/MessageFilter.java @@ -9,6 +9,19 @@ package meerkat.bulletinboard; * be efficiently run on the BB database. * */ -public interface MessageFilter { - +public abstract class MessageFilter { + public enum FilterType{ + ENTRY_NUM, + ID, + TAG, + SIGNER, + TIME + } + + FilterType filterType; + + public MessageFilter(FilterType filterType){ + + } + } diff --git a/meerkat-common/src/main/java/meerkat/comm/CommunicationException.java b/meerkat-common/src/main/java/meerkat/comm/CommunicationException.java index 6dacb21..05e7a22 100644 --- a/meerkat-common/src/main/java/meerkat/comm/CommunicationException.java +++ b/meerkat-common/src/main/java/meerkat/comm/CommunicationException.java @@ -4,4 +4,33 @@ package meerkat.comm; * Created by talm on 24/10/15. */ public class CommunicationException extends Exception { + + /** + * Generated serial. + */ + + private static final long serialVersionUID = 2279440129497891293L; + private String message; + + /** + * Default constructor. To be used only if error type is unknown. + */ + public CommunicationException(){ + message = "Unknown communication exception"; + } + + /** + * Constructor enabling specifying of an error message. + * @param errorMessage + */ + public CommunicationException(String errorMessage){ + message = errorMessage; + } + + /** + * @return the error message specified. + */ + public String getMessage(){ + return message; + } } diff --git a/meerkat-common/src/main/java/meerkat/comm/MessageID.java b/meerkat-common/src/main/java/meerkat/comm/MessageID.java deleted file mode 100644 index 2b97939..0000000 --- a/meerkat-common/src/main/java/meerkat/comm/MessageID.java +++ /dev/null @@ -1,10 +0,0 @@ -package meerkat.comm; - -/** - * Created by talm on 24/10/15. - * - * - */ -public class MessageID { -} - diff --git a/meerkat-common/src/main/proto/meerkat/voting.proto b/meerkat-common/src/main/proto/meerkat/voting.proto index cdfb139..5d2586a 100644 --- a/meerkat-common/src/main/proto/meerkat/voting.proto +++ b/meerkat-common/src/main/proto/meerkat/voting.proto @@ -6,8 +6,13 @@ import 'meerkat/crypto.proto'; option java_package = "meerkat.protobuf"; +message MessageID { + // The ID of a message for unique retrieval. + // Note that it is assumed that this ID is a function of the message itself. + bytes ID = 1; +} -message UnsignedBulletinBoardMessage { +message UnsignedBulletinBoardMessage { // Optional tags describing message repeated string tags = 1;