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