00680abeb51b8dea56e81506a243e6c35a1eb36a
[iotcloud.git] / src / java / iotcloud / Table.java
1 package iotcloud;
2 import java.util.HashMap;
3 import java.util.Arrays;
4 import javax.crypto.spec.*;
5 import javax.crypto.*;
6
7 public class Table {
8         int numslots;
9         HashMap<IoTString, KeyValue> table=new HashMap<IoTString, KeyValue>();
10         HashMap<Long, Pair<Long, Liveness> > lastmessagetable=new HashMap<Long, Pair<Long, Liveness> >();
11         SlotBuffer buffer;
12         CloudComm cloud;
13         private Mac hmac;
14         long sequencenumber;
15         long localmachineid;
16
17         public Table(String baseurl, String password, long _localmachineid) {
18                 localmachineid=_localmachineid;
19                 buffer = new SlotBuffer();
20                 sequencenumber = 1;
21                 initCloud(baseurl, password);
22         }
23
24         private void initCloud(String baseurl, String password) {
25                 try {
26                         SecretKeySpec secret=getKey(password);
27                         Cipher encryptCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
28                         encryptCipher.init(Cipher.ENCRYPT_MODE, secret);
29                         Cipher decryptCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
30                         decryptCipher.init(Cipher.DECRYPT_MODE, secret);
31                         hmac = Mac.getInstance("HmacSHA256");
32                         hmac.init(secret);
33                         cloud=new CloudComm(baseurl, encryptCipher, decryptCipher, hmac);
34                 } catch (Exception e) {
35                         throw new Error("Failed To Initialize Ciphers");
36                 }
37         }
38
39         private SecretKeySpec getKey(String password) {
40                 try {
41                         PBEKeySpec keyspec = new PBEKeySpec(password.toCharArray());
42                         SecretKey key = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256").generateSecret(keyspec);
43                         SecretKeySpec secret = new SecretKeySpec(key.getEncoded(), "AES");
44                         return secret;
45                 } catch (Exception e) {
46                         throw new Error("Failed generating key.");
47                 }
48         }
49
50         public void update() {
51                 Slot[] newslots=cloud.getSlots(sequencenumber);
52                 validateandupdate(newslots);
53         }
54
55         public IoTString get(IoTString key) {
56                 KeyValue kv=table.get(key);
57                 if (kv != null)
58                         return kv.getValue();
59                 else
60                         return null;
61         }
62
63         public IoTString put(IoTString key, IoTString value) {
64                 return null;
65         }
66
67         void validateandupdate(Slot[] newslots) {
68                 //The cloud communication layer has checked slot HMACs already
69                 //before decoding
70                 if (newslots.length==0)
71                         return;
72
73                 long firstseqnum=newslots[0].getSequenceNumber();
74                 if (firstseqnum < sequencenumber)
75                         throw new Error("Server Error: Sent older slots!");
76
77                 SlotIndexer indexer = new SlotIndexer(newslots, buffer);
78                 checkHMACChain(indexer, newslots);
79                 for(Slot slot: newslots) {
80                         processSlot(indexer, slot);
81                 }
82
83         }
84
85         void processEntry(KeyValue entry, SlotIndexer indexer) {
86                 IoTString key=entry.getKey();
87                 KeyValue oldvalue=table.get(key);
88                 if (oldvalue != null) {
89                         oldvalue.decrementLiveCount();
90                 }
91                 table.put(key, entry);
92         }
93
94         void processEntry(LastMessage entry, SlotIndexer indexer) {
95                 updateLastMessage(entry.getMachineID(), entry.getSequenceNumber(), entry);
96         }
97
98         void processEntry(RejectedMessage entry, SlotIndexer indexer) {
99                 long oldseqnum=entry.getOldSeqNum();
100                 long newseqnum=entry.getNewSeqNum();
101                 boolean isequal=entry.getEqual();
102                 long machineid=entry.getMachineID();
103                 for(long seqnum=oldseqnum; seqnum<=newseqnum; seqnum++) {
104                         Slot slot=indexer.getSlot(seqnum);
105                         if (slot != null) {
106                                 long slotmachineid=slot.getMachineID();
107                                 if (isequal!=(slotmachineid==machineid)) {
108                                         throw new Error("Server Error: Trying to insert rejected message for slot "+seqnum);
109                                 }
110                         }
111                 }
112         }
113
114         void processEntry(TableStatus entry, SlotIndexer indexer, Slot slot) {
115
116         }
117
118         void updateLastMessage(long machineid, long seqnum, Liveness liveness) {
119                 Pair<Long, Liveness> lastmsgentry = lastmessagetable.put(machineid, new Pair<Long, Liveness>(seqnum, liveness));
120                 if (lastmsgentry == null)
121                         return;
122
123                 long lastmsgseqnum = lastmsgentry.getFirst();
124                 Liveness lastentry = lastmsgentry.getSecond();
125                 if (lastentry instanceof LastMessage) {
126                         ((LastMessage)lastentry).decrementLiveCount();
127                 } else if (lastentry instanceof Slot) {
128                         ((Slot)lastentry).decrementLiveCount();
129                 } else {
130                         throw new Error("Unrecognized type");
131                 }
132
133                 if (machineid == localmachineid) {
134                         if (lastmsgseqnum != seqnum)
135                                 throw new Error("Server Error: Mismatch on local machine sequence number");
136                 } else {
137                         if (lastmsgseqnum > seqnum)
138                                 throw new Error("Server Error: Rolback on remote machine sequence number");
139                 }
140         }
141
142         void processSlot(SlotIndexer indexer, Slot slot) {
143                 updateLastMessage(slot.getMachineID(), slot.getSequenceNumber(), slot);
144
145                 for(Entry entry : slot.getEntries()) {
146                         switch(entry.getType()) {
147                         case Entry.TypeKeyValue:
148                                 processEntry((KeyValue)entry, indexer);
149                                 break;
150
151                         case Entry.TypeLastMessage:
152                                 processEntry((LastMessage)entry, indexer);
153                                 break;
154
155                         case Entry.TypeRejectedMessage:
156                                 processEntry((RejectedMessage)entry, indexer);
157                                 break;
158
159                         case Entry.TypeTableStatus:
160                                 processEntry((TableStatus)entry, indexer, slot);
161                                 break;
162
163                         default:
164                                 throw new Error("Unrecognized type: "+entry.getType());
165                         }
166                 }
167         }
168
169         void checkHMACChain(SlotIndexer indexer, Slot[] newslots) {
170                 for(int i=0; i < newslots.length; i++) {
171                         Slot currslot=newslots[i];
172                         Slot prevslot=indexer.getSlot(currslot.getSequenceNumber()-1);
173                         if (prevslot != null &&
174                                         !Arrays.equals(prevslot.getHMAC(), currslot.getPrevHMAC()))
175                                 throw new Error("Server Error: Invalid HMAC Chain");
176                 }
177         }
178 }