Add HMAC
[iotcloud.git] / version2 / backup / src / java / iotcloud / Slot.java
1 package iotcloud;
2 import java.util.Vector;
3 import java.nio.ByteBuffer;
4 import javax.crypto.Mac;
5 import java.util.Arrays;
6
7 /**
8  * Data structuring for holding Slot information.
9  * @author Brian Demsky
10  * @version 1.0
11  */
12
13 class Slot implements Liveness {
14         /** Sets the slot size. */
15         static final int SLOT_SIZE = 2048;
16         /** Sets the size for the HMAC. */
17         static final int HMAC_SIZE = 32;
18
19         /** Sequence number of the slot. */
20         private long seqnum;
21         /** HMAC of previous slot. */
22         private byte[] prevhmac;
23         /** HMAC of this slot. */
24         private byte[] hmac;
25         /** Machine that sent this slot. */
26         private long machineid;
27         /** Vector of entries in this slot. */
28         private Vector<Entry> entries;
29         /** Pieces of information that are live. */
30         private int livecount;
31         /** Flag that indicates whether this slot is still live for
32          * recording the machine that sent it. */
33         private boolean seqnumlive;
34         /** Number of bytes of free space. */
35         private int freespace;
36         /** Reference to Table */
37         private Table table;
38
39         Slot(Table _table, long _seqnum, long _machineid, byte[] _prevhmac, byte[] _hmac) {
40                 seqnum = _seqnum;
41                 machineid = _machineid;
42                 prevhmac = _prevhmac;
43                 hmac = _hmac;
44                 entries = new Vector<Entry>();
45                 livecount = 1;
46                 seqnumlive = true;
47                 freespace = SLOT_SIZE - getBaseSize();
48                 table = _table;
49         }
50
51         Slot(Table _table, long _seqnum, long _machineid, byte[] _prevhmac) {
52                 this(_table, _seqnum, _machineid, _prevhmac, null);
53         }
54
55         Slot(Table _table, long _seqnum, long _machineid) {
56                 this(_table, _seqnum, _machineid, new byte[HMAC_SIZE], null);
57         }
58
59         byte[] getHMAC() {
60                 return hmac;
61         }
62
63         byte[] getPrevHMAC() {
64                 return prevhmac;
65         }
66
67         Entry addEntry(Entry e) {
68                 e = e.getCopy(this);
69                 entries.add(e);
70                 livecount++;
71                 freespace -= e.getSize();
72                 return e;
73         }
74
75         void removeEntry(Entry e) {
76                 entries.remove(e);
77                 livecount--;
78                 freespace += e.getSize();
79         }
80
81         private void addShallowEntry(Entry e) {
82                 entries.add(e);
83                 livecount++;
84                 freespace -= e.getSize();
85         }
86
87         /**
88          * Returns true if the slot has free space to hold the entry without
89          * using its reserved space. */
90
91         boolean hasSpace(Entry e) {
92                 int newfreespace = freespace - e.getSize();
93                 return newfreespace >= 0;
94         }
95
96         Vector<Entry> getEntries() {
97                 return entries;
98         }
99
100         static Slot decode(Table table, byte[] array, Mac mac) {
101                 mac.update(array, HMAC_SIZE, array.length - HMAC_SIZE);
102                 byte[] realmac = mac.doFinal();
103
104                 ByteBuffer bb = ByteBuffer.wrap(array);
105                 byte[] hmac = new byte[HMAC_SIZE];
106                 byte[] prevhmac = new byte[HMAC_SIZE];
107                 bb.get(hmac);
108                 bb.get(prevhmac);
109                 if (!Arrays.equals(realmac, hmac))
110                         throw new Error("Server Error: Invalid HMAC!  Potential Attack!");
111
112                 long seqnum = bb.getLong();
113                 long machineid = bb.getLong();
114                 int numentries = bb.getInt();
115                 Slot slot = new Slot(table, seqnum, machineid, prevhmac, hmac);
116
117                 for (int i = 0; i < numentries; i++) {
118                         slot.addShallowEntry(Entry.decode(slot, bb));
119                 }
120
121                 return slot;
122         }
123
124         byte[] encode(Mac mac) {
125                 byte[] array = new byte[SLOT_SIZE];
126                 ByteBuffer bb = ByteBuffer.wrap(array);
127                 /* Leave space for the slot HMAC.  */
128                 bb.position(HMAC_SIZE);
129                 bb.put(prevhmac);
130                 bb.putLong(seqnum);
131                 bb.putLong(machineid);
132                 bb.putInt(entries.size());
133                 for (Entry entry : entries) {
134                         entry.encode(bb);
135                 }
136                 /* Compute our HMAC */
137                 mac.update(array, HMAC_SIZE, array.length - HMAC_SIZE);
138                 byte[] realmac = mac.doFinal();
139                 hmac = realmac;
140                 bb.position(0);
141                 bb.put(realmac);
142                 return array;
143         }
144
145         /**
146          * Returns the empty size of a Slot. Includes 2 HMACs, the machine
147          * identifier, the sequence number, and the number of entries.
148          */
149         int getBaseSize() {
150                 return 2 * HMAC_SIZE + 2 * Long.BYTES + Integer.BYTES;
151         }
152
153         /**
154          * Returns the live set of entries for this Slot.  Generates a fake
155          * LastMessage entry to represent the information stored by the slot
156          * itself.
157          */
158
159         Vector<Entry> getLiveEntries(boolean resize) {
160                 Vector<Entry> liveEntries = new Vector<Entry>();
161                 for (Entry entry : entries) {
162                         if (entry.isLive()) {
163                                 if (!resize || entry.getType() != Entry.TypeTableStatus)
164                                         liveEntries.add(entry);
165                         }
166                 }
167
168                 if (seqnumlive && !resize)
169                         liveEntries.add(new LastMessage(this, machineid, seqnum));
170
171                 return liveEntries;
172         }
173
174         /**
175          * Returns the sequence number of the slot.
176          */
177
178         long getSequenceNumber() {
179                 return seqnum;
180         }
181
182         /**
183          * Returns the machine that sent this slot.
184          */
185
186         long getMachineID() {
187                 return machineid;
188         }
189
190         /**
191          * Records that a newer slot records the fact that this slot was
192          * sent by the relevant machine.
193          */
194
195         void setDead() {
196                 seqnumlive = false;
197                 decrementLiveCount();
198         }
199
200         /**
201          * Update the count of live entries.
202          */
203
204         void decrementLiveCount() {
205                 livecount--;
206                 if (livecount == 0) {
207                         table.decrementLiveCount();
208                 }
209         }
210
211         /**
212          * Returns whether the slot stores any live information.
213          */
214
215         boolean isLive() {
216                 return livecount > 0;
217         }
218
219         public String toString() {
220                 return "<" + getSequenceNumber() + ">";
221         }
222 }