add makefile
[iotcloud.git] / src / java / iotcloud / Table.java
index beceb7cc9a04f9fe2824c9d6ec2dc177a01c75b7..6cd2b323d9cf6a07789f2b8dcd9da318ad7da73e 100644 (file)
@@ -1,6 +1,175 @@
 package iotcloud;
+import java.util.HashMap;
+import java.util.Arrays;
+import javax.crypto.spec.*;
+import javax.crypto.*;
 
 public class Table {
-       int tablesize;
+       int numslots;
+       HashMap<IoTString, KeyValue> table=new HashMap<IoTString, KeyValue>();
+       HashMap<Long, Pair<Long, Liveness>> lastmessagetable=new HashMap<Long, Pair<Long, Liveness>>();
+       SlotBuffer buffer;
+       CloudComm cloud;
+       private Mac hmac;
+       long sequencenumber;
+       long localmachineid;
        
+       public Table(String baseurl, String password, long _localmachineid) {
+               localmachineid=_localmachineid;
+               buffer = new SlotBuffer();
+               sequencenumber = 1;
+               initCloud(baseurl, password);
+       }
+
+       private void initCloud(String baseurl, String password) {
+               try {
+                       SecretKeySpec secret=getKey(password);
+                       Cipher encryptCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
+                       encryptCipher.init(Cipher.ENCRYPT_MODE, secret);
+                       Cipher decryptCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
+                       decryptCipher.init(Cipher.DECRYPT_MODE, secret);
+                       hmac = Mac.getInstance("HmacSHA256");
+                       hmac.init(secret);
+                       cloud=new CloudComm(baseurl, encryptCipher, decryptCipher, hmac);
+               } catch (Exception e) {
+                       throw new Error("Failed To Initialize Ciphers");
+               }
+       }
+
+       private SecretKeySpec getKey(String password) {
+               try {
+                       PBEKeySpec keyspec = new PBEKeySpec(password.toCharArray());
+                       SecretKey key = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256").generateSecret(keyspec);
+                       SecretKeySpec secret = new SecretKeySpec(key.getEncoded(), "AES");
+                       return secret;
+               } catch (Exception e) {
+                       throw new Error("Failed generating key.");
+               }
+       }
+
+       public void update() {
+               Slot[] newslots=cloud.getSlots(sequencenumber);
+               validateandupdate(newslots);
+       }
+
+       public IoTString get(IoTString key) {
+               KeyValue kv=table.get(key);
+               if (kv != null)
+                       return kv.getValue();
+               else
+                       return null;
+       }
+
+       public IoTString put(IoTString key, IoTString value) {
+               return null;
+       }
+       
+       void validateandupdate(Slot[] newslots) {
+               //The cloud communication layer has checked slot HMACs already
+               //before decoding
+               if (newslots.length==0)
+                       return;
+
+               long firstseqnum=newslots[0].getSequenceNumber();
+               if (firstseqnum < sequencenumber)
+                       throw new Error("Server Error: Sent older slots!");
+
+               SlotIndexer indexer = new SlotIndexer(newslots, buffer);
+               checkHMACChain(indexer, newslots);
+               for(Slot slot: newslots) {
+                       processSlot(indexer, slot);
+               }
+               
+       }
+
+       void processEntry(KeyValue entry, SlotIndexer indexer) {
+               IoTString key=entry.getKey();
+               KeyValue oldvalue=table.get(key);
+               if (oldvalue != null) {
+                       oldvalue.decrementLiveCount();
+               }
+               table.put(key, entry);
+       }
+
+       void processEntry(LastMessage entry, SlotIndexer indexer) {
+               updateLastMessage(entry.getMachineID(), entry.getSequenceNumber(), entry);
+       }
+
+       void processEntry(RejectedMessage entry, SlotIndexer indexer) {
+               long oldseqnum=entry.getOldSeqNum();
+               long newseqnum=entry.getNewSeqNum();
+               boolean isequal=entry.getEqual();
+               long machineid=entry.getMachineID();
+               for(long seqnum=oldseqnum;seqnum<=newseqnum;seqnum++) {
+                       Slot slot=indexer.getSlot(seqnum);
+                       if (slot != null) {
+                               long slotmachineid=slot.getMachineID();
+                               if (isequal!=(slotmachineid==machineid)) {
+                                       throw new Error("Server Error: Trying to insert rejected message for slot "+seqnum);
+                               }
+                       }
+               }
+       }
+       
+       void processEntry(TableStatus entry, SlotIndexer indexer, Slot slot) {
+               
+       }
+
+       void updateLastMessage(long machineid, long seqnum, Liveness liveness) {
+               Pair<Long, Liveness> lastmsgentry = lastmessagetable.put(machineid, new Pair<Long, Liveness>(seqnum, liveness));
+               if (lastmsgentry == null)
+                       return;
+
+               long lastmsgseqnum = lastmsgentry.getFirst();
+               Liveness lastentry = lastmsgentry.getSecond();
+               if (lastentry instanceof LastMessage) {
+                       ((LastMessage)lastentry).decrementLiveCount();
+               } else if (lastentry instanceof Slot) {
+                       ((Slot)lastentry).decrementLiveCount();
+               } else {
+                       throw new Error("Unrecognized type");
+               }
+               
+               //Check that nothing funny happened
+               if (machineid == localmachineid) {
+                       if (lastmsgseqnum != seqnum)
+                               throw new Error("Server Error: Mismatch on local machine sequence number");
+               } else {
+                       if (lastmsgseqnum > seqnum)
+                               throw new Error("Server Error: Rolback on remote machine sequence number");
+               }
+       }
+       
+       void processSlot(SlotIndexer indexer, Slot slot) {
+               updateLastMessage(slot.getMachineID(), slot.getSequenceNumber(), slot);
+
+               for(Entry entry : slot.getEntries()) {
+                       switch(entry.getType()) {
+                       case Entry.TypeKeyValue:
+                               processEntry((KeyValue)entry, indexer);
+                               break;
+                       case Entry.TypeLastMessage:
+                               processEntry((LastMessage)entry, indexer);
+                               break;
+                       case Entry.TypeRejectedMessage:
+                               processEntry((RejectedMessage)entry, indexer);
+                               break;
+                       case Entry.TypeTableStatus:
+                               processEntry((TableStatus)entry, indexer, slot);
+                               break;
+                       default:
+                               throw new Error("Unrecognized type: "+entry.getType());
+                       }
+               }
+       }
+       
+       void checkHMACChain(SlotIndexer indexer, Slot[] newslots) {
+               for(int i=0; i < newslots.length; i++) {
+                       Slot currslot=newslots[i];
+                       Slot prevslot=indexer.getSlot(currslot.getSequenceNumber()-1);
+                       if (prevslot != null &&
+                                       !Arrays.equals(prevslot.getHMAC(), currslot.getPrevHMAC()))
+                               throw new Error("Server Error: Invalid HMAC Chain");
+               }
+       }
 }