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