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