package iotcloud; import java.util.Vector; import java.nio.ByteBuffer; import javax.crypto.Mac; import java.util.Arrays; /** * Data structuring for holding Slot information. * @author Brian Demsky * @version 1.0 */ class Slot implements Liveness { /** Sets the slot size. */ static final int SLOT_SIZE = 2048; /** Sets the size for the HMAC. */ static final int HMAC_SIZE = 32; /** Sequence number of the slot. */ private long seqnum; /** HMAC of previous slot. */ private byte[] prevhmac; /** HMAC of this slot. */ private byte[] hmac; /** Machine that sent this slot. */ private long machineid; /** Vector of entries in this slot. */ private Vector entries; /** Pieces of information that are live. */ private int livecount; /** Flag that indicates whether this slot is still live for * recording the machine that sent it. */ private boolean seqnumlive; /** Number of bytes of free space. */ private int freespace; /** Reference to Table */ private Table table; private long localSequenceNumber; Slot(Table _table, long _seqnum, long _machineid, byte[] _prevhmac, byte[] _hmac, long _localSequenceNumber) { seqnum = _seqnum; machineid = _machineid; prevhmac = _prevhmac; hmac = _hmac; entries = new Vector(); livecount = 1; seqnumlive = true; freespace = SLOT_SIZE - getBaseSize(); table = _table; localSequenceNumber = _localSequenceNumber; } Slot(Table _table, long _seqnum, long _machineid, byte[] _prevhmac, long _localSequenceNumber) { this(_table, _seqnum, _machineid, _prevhmac, null, _localSequenceNumber); } Slot(Table _table, long _seqnum, long _machineid, long _localSequenceNumber) { this(_table, _seqnum, _machineid, new byte[HMAC_SIZE], null, _localSequenceNumber); } byte[] getHMAC() { return hmac; } byte[] getPrevHMAC() { return prevhmac; } Entry addEntry(Entry e) { e = e.getCopy(this); entries.add(e); livecount++; freespace -= e.getSize(); return e; } void removeEntry(Entry e) { entries.remove(e); livecount--; freespace += e.getSize(); } private void addShallowEntry(Entry e) { entries.add(e); livecount++; freespace -= e.getSize(); } /** * Returns true if the slot has free space to hold the entry without * using its reserved space. */ boolean hasSpace(Entry e) { int newfreespace = freespace - e.getSize(); return newfreespace >= 0; } Vector getEntries() { return entries; } static Slot decode(Table table, byte[] array, Mac mac) { mac.update(array, HMAC_SIZE, array.length - HMAC_SIZE); byte[] realmac = mac.doFinal(); ByteBuffer bb = ByteBuffer.wrap(array); byte[] hmac = new byte[HMAC_SIZE]; byte[] prevhmac = new byte[HMAC_SIZE]; bb.get(hmac); bb.get(prevhmac); if (!Arrays.equals(realmac, hmac)) throw new Error("Server Error: Invalid HMAC! Potential Attack!"); long seqnum = bb.getLong(); long machineid = bb.getLong(); int numentries = bb.getInt(); Slot slot = new Slot(table, seqnum, machineid, prevhmac, hmac, -1); for (int i = 0; i < numentries; i++) { slot.addShallowEntry(Entry.decode(slot, bb)); } return slot; } byte[] encode(Mac mac) { byte[] array = new byte[SLOT_SIZE]; ByteBuffer bb = ByteBuffer.wrap(array); /* Leave space for the slot HMAC. */ bb.position(HMAC_SIZE); bb.put(prevhmac); bb.putLong(seqnum); bb.putLong(machineid); bb.putInt(entries.size()); for (Entry entry : entries) { entry.encode(bb); } /* Compute our HMAC */ mac.update(array, HMAC_SIZE, array.length - HMAC_SIZE); byte[] realmac = mac.doFinal(); hmac = realmac; bb.position(0); bb.put(realmac); return array; } /** * Returns the empty size of a Slot. Includes 2 HMACs, the machine * identifier, the sequence number, and the number of entries. */ int getBaseSize() { return 2 * HMAC_SIZE + 2 * Long.BYTES + Integer.BYTES; } /** * Returns the live set of entries for this Slot. Generates a fake * LastMessage entry to represent the information stored by the slot * itself. */ Vector getLiveEntries(boolean resize) { Vector liveEntries = new Vector(); for (Entry entry : entries) { if (entry.isLive()) { if (!resize || entry.getType() != Entry.TypeTableStatus) liveEntries.add(entry); } } if (seqnumlive && !resize) liveEntries.add(new LastMessage(this, machineid, seqnum)); return liveEntries; } /** * Returns the sequence number of the slot. */ long getSequenceNumber() { return seqnum; } /** * Returns the machine that sent this slot. */ long getMachineID() { return machineid; } /** * Records that a newer slot records the fact that this slot was * sent by the relevant machine. */ void setDead() { seqnumlive = false; decrementLiveCount(); } /** * Update the count of live entries. */ void decrementLiveCount() { livecount--; if (livecount == 0) { table.decrementLiveCount(); } } /** * Returns whether the slot stores any live information. */ boolean isLive() { return livecount > 0; } public byte[] getSlotCryptIV() { ByteBuffer buffer = ByteBuffer.allocate(CloudComm.IV_SIZE); buffer.putLong(machineid); long localSequenceNumberShift = localSequenceNumber << 16; buffer.putLong(localSequenceNumberShift); return buffer.array(); } public String toString() { return "<" + getSequenceNumber() + ">"; } }