X-Git-Url: http://plrg.eecs.uci.edu/git/?p=iotcloud.git;a=blobdiff_plain;f=version2%2Fsrc%2Fjava%2Fiotcloud%2FCloudComm.java;h=f12c2764e0a04520c94b6a094fd25c43266cf4f0;hp=5741019513afcad9027b1d866963bce07d6e77fe;hb=b7ed1849727b50e226f3b9d1c432d3071d739368;hpb=687b0282b4f0810b78de247d3f99b2ace038bf25 diff --git a/version2/src/java/iotcloud/CloudComm.java b/version2/src/java/iotcloud/CloudComm.java index 5741019..f12c276 100644 --- a/version2/src/java/iotcloud/CloudComm.java +++ b/version2/src/java/iotcloud/CloudComm.java @@ -6,6 +6,10 @@ import java.util.Arrays; import javax.crypto.*; import javax.crypto.spec.*; import java.security.SecureRandom; +import java.nio.ByteBuffer; + + +import java.util.*; /** * This class provides a communication API to the webserver. It also @@ -17,11 +21,14 @@ import java.security.SecureRandom; class CloudComm { private static final int SALT_SIZE = 8; - private static final int TIMEOUT_MILLIS = 100; + private static final int TIMEOUT_MILLIS = 2000; // 100 + public static final int IV_SIZE = 16; + + /** Sets the size for the HMAC. */ + static final int HMAC_SIZE = 32; private String baseurl; - private Cipher encryptCipher; - private Cipher decryptCipher; + private SecretKeySpec key; private Mac mac; private String password; private SecureRandom random; @@ -31,16 +38,20 @@ class CloudComm { private Thread localServerThread = null; private boolean doEnd = false; + private TimingSingleton timer = null; + /** * Empty Constructor needed for child class. */ CloudComm() { + timer = TimingSingleton.getInstance(); } /** * Constructor for actual use. Takes in the url and password. */ CloudComm(Table _table, String _baseurl, String _password, int _listeningPort) { + timer = TimingSingleton.getInstance(); this.table = _table; this.baseurl = _baseurl; this.password = _password; @@ -74,6 +85,19 @@ class CloudComm { } } + /** + * Inits all the security stuff + */ + public void initSecurity() throws ServerException { + // try to get the salt and if one does not exist set one + if (!getSalt()) { + //Set the salt + setSalt(); + } + + initCrypt(); + } + /** * Inits the HMAC generator. */ @@ -84,14 +108,10 @@ class CloudComm { } try { - SecretKeySpec key = initKey(); + key = initKey(); password = null; // drop password mac = Mac.getInstance("HmacSHA256"); mac.init(key); - encryptCipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); - encryptCipher.init(Cipher.ENCRYPT_MODE, key); - decryptCipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); - decryptCipher.init(Cipher.DECRYPT_MODE, key); } catch (Exception e) { e.printStackTrace(); throw new Error("Failed To Initialize Ciphers"); @@ -109,30 +129,40 @@ class CloudComm { return new URL(urlstr); } - public void setSalt() throws ServerException { + private void setSalt() throws ServerException { if (salt != null) { // Salt already sent to server so dont set it again return; } - byte[] saltTmp = new byte[SALT_SIZE]; - random.nextBytes(saltTmp); - - URL url = null; - URLConnection con = null; - HttpURLConnection http = null; try { - url = new URL(baseurl + "?req=setsalt"); - con = url.openConnection(); - http = (HttpURLConnection) con; + byte[] saltTmp = new byte[SALT_SIZE]; + random.nextBytes(saltTmp); + + for (int i = 0; i < SALT_SIZE; i++) { + System.out.println((int)saltTmp[i] & 255); + } + + + URL url = new URL(baseurl + "?req=setsalt"); + + timer.startTime(); + URLConnection con = url.openConnection(); + HttpURLConnection http = (HttpURLConnection) con; + http.setRequestMethod("POST"); http.setFixedLengthStreamingMode(saltTmp.length); http.setDoOutput(true); http.setConnectTimeout(TIMEOUT_MILLIS); + + http.connect(); + OutputStream os = http.getOutputStream(); os.write(saltTmp); + os.flush(); + int responsecode = http.getResponseCode(); if (responsecode != HttpURLConnection.HTTP_OK) { // TODO: Remove this print @@ -140,33 +170,17 @@ class CloudComm { throw new Error("Invalid response"); } - } catch (Exception e) { - throw new ServerException("Failed setting salt", ServerException.TypeConnectTimeout); - } - + timer.endTime(); - try { - InputStream is = http.getInputStream(); - DataInputStream dis = new DataInputStream(is); - // byte [] tmp = new byte[1]; - byte tmp = dis.readByte(); - - if (tmp == 0) { - salt = saltTmp; - initCrypt(); - } else { - getSalt(); // there was already a salt so we need to get it - } - - } catch (SocketTimeoutException e) { - throw new ServerException("setSalt failed", ServerException.TypeInputTimeout); + salt = saltTmp; } catch (Exception e) { - e.printStackTrace(); - throw new Error("setSlot failed"); + // e.printStackTrace(); + timer.endTime(); + throw new ServerException("Failed setting salt", ServerException.TypeConnectTimeout); } } - private void getSalt() throws ServerException { + private boolean getSalt() throws ServerException { URL url = null; URLConnection con = null; HttpURLConnection http = null; @@ -174,60 +188,149 @@ class CloudComm { try { url = new URL(baseurl + "?req=getsalt"); } catch (Exception e) { - e.printStackTrace(); + // e.printStackTrace(); throw new Error("getSlot failed"); } try { + timer.startTime(); con = url.openConnection(); http = (HttpURLConnection) con; http.setRequestMethod("POST"); http.setConnectTimeout(TIMEOUT_MILLIS); http.setReadTimeout(TIMEOUT_MILLIS); + + http.connect(); + timer.endTime(); } catch (SocketTimeoutException e) { + timer.endTime(); throw new ServerException("getSalt failed", ServerException.TypeConnectTimeout); } catch (Exception e) { - e.printStackTrace(); + // e.printStackTrace(); throw new Error("getSlot failed"); } try { + + timer.startTime(); + + int responsecode = http.getResponseCode(); + if (responsecode != HttpURLConnection.HTTP_OK) { + // TODO: Remove this print + // System.out.println(responsecode); + throw new Error("Invalid response"); + } + InputStream is = http.getInputStream(); - DataInputStream dis = new DataInputStream(is); - int salt_length = dis.readInt(); - byte [] tmp = new byte[salt_length]; - dis.readFully(tmp); - salt = tmp; + if (is.available() > 0) { + DataInputStream dis = new DataInputStream(is); + int salt_length = dis.readInt(); + byte [] tmp = new byte[salt_length]; + dis.readFully(tmp); + salt = tmp; + timer.endTime(); + + return true; + } else { + timer.endTime(); + + return false; + } } catch (SocketTimeoutException e) { + timer.endTime(); + throw new ServerException("getSalt failed", ServerException.TypeInputTimeout); } catch (Exception e) { - e.printStackTrace(); + // e.printStackTrace(); throw new Error("getSlot failed"); } } + private byte[] createIV(long machineId, long localSequenceNumber) { + ByteBuffer buffer = ByteBuffer.allocate(IV_SIZE); + buffer.putLong(machineId); + long localSequenceNumberShifted = localSequenceNumber << 16; + buffer.putLong(localSequenceNumberShifted); + return buffer.array(); + + } + + private byte[] encryptSlotAndPrependIV(byte[] rawData, byte[] ivBytes) { + try { + IvParameterSpec ivSpec = new IvParameterSpec(ivBytes); + Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding"); + cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec); + + byte[] encryptedBytes = cipher.doFinal(rawData); + + byte[] bytes = new byte[encryptedBytes.length + IV_SIZE]; + System.arraycopy(ivBytes, 0, bytes, 0, ivBytes.length); + System.arraycopy(encryptedBytes, 0, bytes, IV_SIZE, encryptedBytes.length); + + return bytes; + + } catch (Exception e) { + e.printStackTrace(); + throw new Error("Failed To Encrypt"); + } + } + + + private byte[] stripIVAndDecryptSlot(byte[] rawData) { + try { + byte[] ivBytes = new byte[IV_SIZE]; + byte[] encryptedBytes = new byte[rawData.length - IV_SIZE]; + System.arraycopy(rawData, 0, ivBytes, 0, IV_SIZE); + System.arraycopy(rawData, IV_SIZE, encryptedBytes, 0 , encryptedBytes.length); + + IvParameterSpec ivSpec = new IvParameterSpec(ivBytes); + + Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding"); + cipher.init(Cipher.DECRYPT_MODE, key, ivSpec); + return cipher.doFinal(encryptedBytes); + + } catch (Exception e) { + e.printStackTrace(); + throw new Error("Failed To Decrypt"); + } + } + + /* * API for putting a slot into the queue. Returns null on success. * On failure, the server will send slots with newer sequence * numbers. */ - Slot[] putSlot(Slot slot, int max) throws ServerException { + public Slot[] putSlot(Slot slot, int max) throws ServerException { URL url = null; URLConnection con = null; HttpURLConnection http = null; try { if (salt == null) { - getSalt(); + if (!getSalt()) { + throw new ServerException("putSlot failed", ServerException.TypeSalt); + } initCrypt(); } long sequencenumber = slot.getSequenceNumber(); - byte[] bytes = slot.encode(mac); - bytes = encryptCipher.doFinal(bytes); + byte[] slotBytes = slot.encode(mac); + // slotBytes = encryptCipher.doFinal(slotBytes); + + // byte[] iVBytes = slot.getSlotCryptIV(); + + // byte[] bytes = new byte[slotBytes.length + IV_SIZE]; + // System.arraycopy(iVBytes, 0, bytes, 0, iVBytes.length); + // System.arraycopy(slotBytes, 0, bytes, IV_SIZE, slotBytes.length); + + + byte[] bytes = encryptSlotAndPrependIV(slotBytes, slot.getSlotCryptIV()); url = buildRequest(true, sequencenumber, max); + + timer.startTime(); con = url.openConnection(); http = (HttpURLConnection) con; @@ -242,33 +345,45 @@ class CloudComm { os.write(bytes); os.flush(); + timer.endTime(); + + // System.out.println("Bytes Sent: " + bytes.length); + } catch (ServerException e) { + timer.endTime(); + + throw e; } catch (SocketTimeoutException e) { + timer.endTime(); + throw new ServerException("putSlot failed", ServerException.TypeConnectTimeout); } catch (Exception e) { - e.printStackTrace(); + // e.printStackTrace(); throw new Error("putSlot failed"); } try { + timer.startTime(); InputStream is = http.getInputStream(); DataInputStream dis = new DataInputStream(is); byte[] resptype = new byte[7]; dis.readFully(resptype); + timer.endTime(); - if (Arrays.equals(resptype, "getslot".getBytes())) + if (Arrays.equals(resptype, "getslot".getBytes())) { return processSlots(dis); - else if (Arrays.equals(resptype, "putslot".getBytes())) + } else if (Arrays.equals(resptype, "putslot".getBytes())) { return null; - else + } else throw new Error("Bad response to putslot"); } catch (SocketTimeoutException e) { + timer.endTime(); throw new ServerException("putSlot failed", ServerException.TypeInputTimeout); } catch (Exception e) { - e.printStackTrace(); + // e.printStackTrace(); throw new Error("putSlot failed"); } } @@ -277,46 +392,65 @@ class CloudComm { * Request the server to send all slots with the given * sequencenumber or newer. */ - Slot[] getSlots(long sequencenumber) throws ServerException { + public Slot[] getSlots(long sequencenumber) throws ServerException { URL url = null; URLConnection con = null; HttpURLConnection http = null; try { if (salt == null) { - getSalt(); + if (!getSalt()) { + throw new ServerException("getSlots failed", ServerException.TypeSalt); + } initCrypt(); } url = buildRequest(false, sequencenumber, 0); + timer.startTime(); con = url.openConnection(); http = (HttpURLConnection) con; http.setRequestMethod("POST"); http.setConnectTimeout(TIMEOUT_MILLIS); http.setReadTimeout(TIMEOUT_MILLIS); + + + http.connect(); + timer.endTime(); + } catch (SocketTimeoutException e) { + timer.endTime(); + throw new ServerException("getSlots failed", ServerException.TypeConnectTimeout); } catch (ServerException e) { + timer.endTime(); + throw e; } catch (Exception e) { - e.printStackTrace(); + // e.printStackTrace(); throw new Error("getSlots failed"); } try { + + timer.startTime(); InputStream is = http.getInputStream(); DataInputStream dis = new DataInputStream(is); byte[] resptype = new byte[7]; + dis.readFully(resptype); + timer.endTime(); + if (!Arrays.equals(resptype, "getslot".getBytes())) throw new Error("Bad Response: " + new String(resptype)); - else - return processSlots(dis); + + return processSlots(dis); } catch (SocketTimeoutException e) { + timer.endTime(); + throw new ServerException("getSlots failed", ServerException.TypeInputTimeout); } catch (Exception e) { - e.printStackTrace(); + // e.printStackTrace(); throw new Error("getSlots failed"); } } @@ -329,23 +463,23 @@ class CloudComm { int numberofslots = dis.readInt(); int[] sizesofslots = new int[numberofslots]; - - // System.out.println("number of slots: " + numberofslots); - - - Slot[] slots = new Slot[numberofslots]; for (int i = 0; i < numberofslots; i++) sizesofslots[i] = dis.readInt(); for (int i = 0; i < numberofslots; i++) { - // System.out.println("Size of slot: " + sizesofslots[i]); + byte[] rawData = new byte[sizesofslots[i]]; + dis.readFully(rawData); - byte[] data = new byte[sizesofslots[i]]; - dis.readFully(data); - data = decryptCipher.doFinal(data); + // byte[] data = new byte[rawData.length - IV_SIZE]; + // System.arraycopy(rawData, IV_SIZE, data, 0, data.length); + + + byte[] data = stripIVAndDecryptSlot(rawData); + + // data = decryptCipher.doFinal(data); slots[i] = Slot.decode(table, data, mac); } @@ -353,14 +487,25 @@ class CloudComm { return slots; } - public byte[] sendLocalData(byte[] sendData, String host, int port) { + public byte[] sendLocalData(byte[] sendData, long localSequenceNumber, String host, int port) { if (salt == null) { return null; } try { + System.out.println("Passing Locally"); + + mac.update(sendData); + byte[] genmac = mac.doFinal(); + byte[] totalData = new byte[sendData.length + genmac.length]; + System.arraycopy(sendData, 0, totalData, 0, sendData.length); + System.arraycopy(genmac, 0, totalData, sendData.length, genmac.length); + // Encrypt the data for sending - byte[] encryptedData = encryptCipher.doFinal(sendData); + // byte[] encryptedData = encryptCipher.doFinal(totalData); + // byte[] encryptedData = encryptCipher.doFinal(totalData); + byte[] iv = createIV(table.getMachineId(), table.getLocalSequenceNumber()); + byte[] encryptedData = encryptSlotAndPrependIV(totalData, iv); // Open a TCP socket connection to a local device Socket socket = new Socket(host, port); @@ -368,6 +513,8 @@ class CloudComm { DataOutputStream output = new DataOutputStream(socket.getOutputStream()); DataInputStream input = new DataInputStream(socket.getInputStream()); + + timer.startTime(); // Send data to output (length of data, the data) output.writeInt(encryptedData.length); output.write(encryptedData, 0, encryptedData.length); @@ -376,21 +523,31 @@ class CloudComm { int lengthOfReturnData = input.readInt(); byte[] returnData = new byte[lengthOfReturnData]; input.readFully(returnData); - returnData = decryptCipher.doFinal(returnData); - // We are dont with this socket - socket.close(); + timer.endTime(); - return returnData; - } catch (SocketTimeoutException e) { + // returnData = decryptCipher.doFinal(returnData); + returnData = stripIVAndDecryptSlot(returnData); + // returnData = decryptCipher.doFinal(returnData); - } catch (BadPaddingException e) { + // We are done with this socket + socket.close(); - } catch (IllegalBlockSizeException e) { + mac.update(returnData, 0, returnData.length - HMAC_SIZE); + byte[] realmac = mac.doFinal(); + byte[] recmac = new byte[HMAC_SIZE]; + System.arraycopy(returnData, returnData.length - realmac.length, recmac, 0, realmac.length); - } catch (UnknownHostException e) { + if (!Arrays.equals(recmac, realmac)) + throw new Error("Local Error: Invalid HMAC! Potential Attack!"); - } catch (IOException e) { + byte[] returnData2 = new byte[lengthOfReturnData - recmac.length]; + System.arraycopy(returnData, 0, returnData2, 0, returnData2.length); + + return returnData2; + } catch (Exception e) { + e.printStackTrace(); + // throw new Error("Local comms failure..."); } @@ -425,31 +582,49 @@ class CloudComm { byte[] readData = new byte[dataSize]; input.readFully(readData); + timer.endTime(); + // Decrypt the data - readData = decryptCipher.doFinal(readData); + // readData = decryptCipher.doFinal(readData); + readData = stripIVAndDecryptSlot(readData); + + mac.update(readData, 0, readData.length - HMAC_SIZE); + byte[] genmac = mac.doFinal(); + byte[] recmac = new byte[HMAC_SIZE]; + System.arraycopy(readData, readData.length - recmac.length, recmac, 0, recmac.length); + + if (!Arrays.equals(recmac, genmac)) + throw new Error("Local Error: Invalid HMAC! Potential Attack!"); + + byte[] returnData = new byte[readData.length - recmac.length]; + System.arraycopy(readData, 0, returnData, 0, returnData.length); // Process the data - byte[] sendData = table.acceptDataFromLocal(readData); + // byte[] sendData = table.acceptDataFromLocal(readData); + byte[] sendData = table.acceptDataFromLocal(returnData); + + + mac.update(sendData); + byte[] realmac = mac.doFinal(); + byte[] totalData = new byte[sendData.length + realmac.length]; + System.arraycopy(sendData, 0, totalData, 0, sendData.length); + System.arraycopy(realmac, 0, totalData, sendData.length, realmac.length); // Encrypt the data for sending - sendData = encryptCipher.doFinal(sendData); + // byte[] encryptedData = encryptCipher.doFinal(totalData); + byte[] iv = createIV(table.getMachineId(), table.getLocalSequenceNumber()); + byte[] encryptedData = encryptSlotAndPrependIV(totalData, iv); + + timer.startTime(); // Send data to output (length of data, the data) - output.writeInt(sendData.length); - output.write(sendData, 0, sendData.length); + output.writeInt(encryptedData.length); + output.write(encryptedData, 0, encryptedData.length); output.flush(); // close the socket socket.close(); - } catch (SocketTimeoutException e) { - - } catch (BadPaddingException e) { - - } catch (IllegalBlockSizeException e) { - - } catch (UnknownHostException e) { - - } catch (IOException e) { + } catch (Exception e) { } } @@ -467,14 +642,16 @@ class CloudComm { public void close() { doEnd = true; - try { - localServerThread.join(); - } catch (Exception e) { - e.printStackTrace(); - throw new Error("Local Server thread join issue..."); + if (localServerThread != null) { + try { + localServerThread.join(); + } catch (Exception e) { + e.printStackTrace(); + throw new Error("Local Server thread join issue..."); + } } - System.out.println("Done Closing"); + // System.out.println("Done Closing Cloud Comm"); } protected void finalize() throws Throwable { @@ -484,5 +661,4 @@ class CloudComm { super.finalize(); } } - }