115c76120f9382dc2c0f47b6a82851b4f458b68b
[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 class Slot implements Liveness {
8         static final int SLOT_SIZE=2048;
9         static final int RESERVED_SPACE=64;
10         static final int HMAC_SIZE=32;
11
12         private long seqnum;
13         private byte[] prevhmac;
14         private byte[] hmac;
15         private long machineid;
16         private Vector<Entry> entries;
17         private int livecount;
18         private boolean seqnumlive;
19         private int freespace;
20         
21         Slot(long _seqnum, long _machineid, byte[] _prevhmac, byte[] _hmac) {
22                 seqnum=_seqnum;
23                 machineid=_machineid;
24                 prevhmac=_prevhmac;
25                 hmac=_hmac;
26                 entries=new Vector<Entry>();
27                 livecount=1;
28                 seqnumlive=true;
29                 freespace = SLOT_SIZE - getBaseSize();
30         }
31
32         Slot(long _seqnum, long _machineid, byte[] _prevhmac) {
33                 this(_seqnum, _machineid, _prevhmac, new byte[HMAC_SIZE]);
34         }
35
36         Slot(long _seqnum, long _machineid) {
37                 this(_seqnum, _machineid, new byte[HMAC_SIZE], new byte[HMAC_SIZE]);
38         }
39
40         byte[] getHMAC() {
41                 return hmac;
42         }
43
44         byte[] getPrevHMAC() {
45                 return prevhmac;
46         }
47
48         void addEntry(Entry e) {
49                 entries.add(e);
50                 livecount++;
51                 freespace -= e.getSize();
52         }
53
54         boolean hasSpace(Entry e) {
55                 int newfreespace = freespace - e.getSize();
56                 return newfreespace > RESERVED_SPACE;
57         }
58
59         boolean canFit(Entry e) {
60                 int newfreespace = freespace - e.getSize();
61                 return newfreespace >= 0;
62         }
63         
64         Vector<Entry> getEntries() {
65                 return entries;
66         }
67
68         static Slot decode(byte[] array, Mac mac) {
69                 mac.update(array, HMAC_SIZE, array.length-HMAC_SIZE);
70                 byte[] realmac=mac.doFinal();
71
72                 ByteBuffer bb=ByteBuffer.wrap(array);
73                 byte[] hmac=new byte[HMAC_SIZE];
74                 byte[] prevhmac=new byte[HMAC_SIZE];
75                 bb.get(hmac);
76                 bb.get(prevhmac);
77                 if (!Arrays.equals(realmac, hmac))
78                         throw new Error("Server Error: Invalid HMAC!  Potential Attack!");
79
80                 long seqnum=bb.getLong();
81                 long machineid=bb.getLong();
82                 int numentries=bb.getInt();
83                 Slot slot=new Slot(seqnum, machineid, prevhmac, hmac);
84
85                 for(int i=0; i<numentries; i++) {
86                         slot.addEntry(Entry.decode(slot, bb));
87                 }
88
89                 return slot;
90         }
91
92         byte[] encode(Mac mac) {
93                 byte[] array=new byte[SLOT_SIZE];
94                 ByteBuffer bb=ByteBuffer.wrap(array);
95                 bb.position(HMAC_SIZE);                                                                                                                                                                                                                                                 //Leave space for the HMACs
96                 bb.put(prevhmac);
97                 bb.putLong(seqnum);
98                 bb.putLong(machineid);
99                 bb.putInt(entries.size());
100                 for(Entry entry:entries) {
101                         entry.encode(bb);
102                 }
103                 //Compute our HMAC
104                 mac.update(array, HMAC_SIZE, array.length-HMAC_SIZE);
105                 byte[] realmac=mac.doFinal();
106                 bb.position(0);
107                 bb.put(realmac);
108                 return array;
109         }
110
111         int getBaseSize() {
112                 return 2*HMAC_SIZE+2*Long.BYTES+Integer.BYTES;
113         }
114         
115         Vector<Entry> getLiveEntries() {
116                 Vector<Entry> liveEntries=new Vector<Entry>();
117                 for(Entry entry: entries)
118                         if (entry.isLive())
119                                 liveEntries.add(entry);
120
121                 if (seqnumlive)
122                         liveEntries.add(new LastMessage(this, machineid, seqnum));
123                 
124                 return liveEntries;
125         }
126         
127         long getSequenceNumber() {
128                 return seqnum;
129         }
130
131         long getMachineID() {
132                 return machineid;
133         }
134
135         byte[] getBytes() {
136                 return null;
137         }
138
139         void setDead() {
140                 decrementLiveCount();
141                 seqnumlive=false;
142         }
143         
144         void decrementLiveCount() {
145                 livecount--;
146         }
147
148         boolean isLive() {
149                 return livecount > 0;
150         }
151
152         public String toString() {
153                 return "<"+getSequenceNumber()+", "+new String(getBytes())+">";
154         }
155 }