Added basic Bulletin Board server functionality

signature-implementation
Arbel Deutsch Peled 2015-11-11 14:12:34 +02:00
parent aaf26dc2b1
commit 58c1816324
11 changed files with 482 additions and 14 deletions

3
.gitignore vendored
View File

@ -6,3 +6,6 @@ out
*.ipr
*.iws
**/*.swp
*.prefs
*.project
*.classpath

View File

@ -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
}
}
}

View File

@ -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<SignatureVerificationKey> trusteeSignatureVerificationArray;
protected int minTrusteeSignatures;
protected List<List<SignatureVerificationKey>> 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<BulletinBoardMessage> 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");
}
}
}

View File

@ -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<BulletinBoardMessage> 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");
}
}
}

View File

@ -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());
}
}
}

View File

@ -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.

View File

@ -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<BulletinBoardMessage> readMessages(MessageFilter filter, int max);
/**
* This method closes the connection to the DB.
* @throws CommunicationException on DB connection error.
*/
public void close() throws CommunicationException;
}

View File

@ -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){
}
}

View File

@ -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;
}
}

View File

@ -1,10 +0,0 @@
package meerkat.comm;
/**
* Created by talm on 24/10/15.
*
*
*/
public class MessageID {
}

View File

@ -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;