Changing encryption in the phone app of the fourth benchmark from AES-ECB to AES...
[iot2.git] / benchmarks / other / PhoneInterface / Control / app / src / main / java / iotcloud / CloudComm.java
1 package iotcloud;
2
3 import java.io.*;
4 import java.net.*;
5 import java.util.Arrays;
6 import javax.crypto.*;
7 import javax.crypto.spec.*;
8 import java.security.SecureRandom;
9 import android.util.*;
10 import java.nio.charset.StandardCharsets;
11 import org.spongycastle.crypto.generators.PKCS5S2ParametersGenerator;
12 import org.spongycastle.crypto.digests.SHA256Digest;
13 import org.spongycastle.crypto.params.KeyParameter;
14 import org.spongycastle.crypto.PBEParametersGenerator;
15 import android.content.*;
16 import java.nio.ByteBuffer;
17
18
19 /**
20  * This class provides a communication API to the webserver.  It also
21  * validates the HMACs on the slots and handles encryption.
22  * @author Brian Demsky <bdemsky@uci.edu>
23  * @version 1.0
24  */
25
26
27 class CloudComm {
28         private static final int SALT_SIZE = 8;
29         private static final int TIMEOUT_MILLIS = 2000; // 100
30         public static final int IV_SIZE = 16;
31
32         /** Sets the size for the HMAC. */
33         static final int HMAC_SIZE = 32;
34
35         private String baseurl;
36         private SecretKeySpec key;
37         private Mac mac;
38         private String password;
39         private SecureRandom random;
40         private byte salt[];
41         private Table table;
42         private int listeningPort = -1;
43         private Thread localServerThread = null;
44         private boolean doEnd = false;
45
46         private TimingSingleton timer = null;
47
48         private Context context;
49
50
51
52
53         /**
54          * Empty Constructor needed for child class.
55          */
56         CloudComm() {
57                 timer = TimingSingleton.getInstance();
58         }
59
60         private void deleteFile(Context context) {
61                 File fd = context.getFileStreamPath("config.txt");
62                 fd.delete();
63         }
64
65
66         private void writeToFile(byte[] data,Context context) {
67                 try {
68 //                      OutputStreamWriter outputStreamWriter = new OutputStreamWriter(context.openFileOutput("config.txt", Context.MODE_PRIVATE));
69 //                      outputStreamWriter.write(data);
70 //                      outputStreamWriter.close();
71
72                         FileOutputStream outputStreamWriter = context.openFileOutput("config.txt", Context.MODE_PRIVATE);
73                         outputStreamWriter.write(data);
74                         outputStreamWriter.close();
75
76                 }
77                 catch (IOException e) {
78                         Log.e("Exception", "File write failed: " + e.toString());
79                 }
80         }
81
82         private byte[] readFromFile(Context context) throws FileNotFoundException {
83
84                 byte[] ret1 = null;
85
86                 try {
87                         InputStream inputStream = context.openFileInput("config.txt");
88
89                         if ( inputStream != null ) {
90
91
92                                 ret1 = new byte[inputStream.available()];
93                                 for(int i = 0; i < ret1.length;i++)
94                                 {
95                                         ret1[i] = (byte)inputStream.read();
96                                 }
97
98
99
100
101 //                              InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
102 //                              BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
103 //                              String receiveString = "";
104 //                              StringBuilder stringBuilder = new StringBuilder();
105
106 //                              while ( (receiveString = bufferedReader.readLine()) != null ) {
107 //                                      stringBuilder.append(receiveString);
108 //                              }
109
110                                 inputStream.close();
111 //                              ret = stringBuilder.toString();
112                         }
113                 }
114                 catch (FileNotFoundException e) {
115                         Log.e("login activity", "File not found: " + e.toString());
116
117                         throw e;
118                 } catch (IOException e) {
119                         Log.e("login activity", "Can not read file: " + e.toString());
120                 }
121
122                 return ret1;
123         }
124
125
126
127         /**
128          * Constructor for actual use. Takes in the url and password.
129          */
130         CloudComm(Table _table,  String _baseurl, String _password, int _listeningPort, Context _context) {
131                 timer = TimingSingleton.getInstance();
132                 this.table = _table;
133                 this.baseurl = _baseurl;
134                 this.password = _password;
135                 this.random = new SecureRandom();
136                 this.listeningPort = _listeningPort;
137                 this.context = _context;
138
139
140                 if (this.listeningPort > 0) {
141                         localServerThread = new Thread(new Runnable() {
142                                 public void run() {
143                                         localServerWorkerFunction();
144                                 }
145                         });
146                         localServerThread.start();
147                 }
148         }
149
150         /**
151          * Generates Key from password.
152          */
153         private SecretKeySpec initKey() {
154                 try {
155
156                         Log.e("Ali:::::", "KEY KEY KEY......");
157
158
159
160                         boolean doCrypt = false;
161
162                         byte[] keySaved = null;
163
164                         try {
165 //                              String file = readFromFile(context);
166                                 byte[] dat = readFromFile(context);//file.getBytes();
167
168                                 boolean saltMatch = true;
169                                 for(int i = 0; i < salt.length; i++)
170                                 {
171
172                                         Log.e("ALIasdasdaS:", " " + ((int) salt[i] & 255) + " " + ((int) dat[i] & 255));
173
174                                         if(dat[i] != salt[i])
175                                         {
176                                                 saltMatch = false;
177 //                                              break;
178                                         }
179                                 }
180
181                                 if(saltMatch )
182                                 {
183                                         keySaved = new byte[dat.length - salt.length];
184                                         for(int i = salt.length; i < dat.length;i++)
185                                         {
186                                                 keySaved[i-salt.length] = dat[i];
187                                         }
188                                 }
189                                 else
190                                 {
191                                         doCrypt = true;
192                                         Log.e("Ali:::::", "Salt No Match......");
193
194                                 }
195
196
197
198
199
200                         }
201                         catch (Exception e)
202                         {
203                                 doCrypt = true;
204                         }
205
206
207
208                         if(doCrypt) {
209                                 Log.e("Ali:::::", "Doing Crypt......");
210                                 PKCS5S2ParametersGenerator generator = new PKCS5S2ParametersGenerator(new SHA256Digest());
211                                 generator.init(PBEParametersGenerator.PKCS5PasswordToUTF8Bytes(password.toCharArray()), salt, 65536);
212                                 KeyParameter key = (KeyParameter) generator.generateDerivedMacParameters(128);
213
214
215 //                      PBEKeySpec keyspec = new PBEKeySpec(password.toCharArray(),
216 //                                                          salt,
217 //                                                          65536,
218 //                                                          128);
219 //                      SecretKey tmpkey = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1").generateSecret(keyspec);
220 //                      SecretKey tmpkey = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256").generateSecret(keyspec);
221
222
223 //                      return new SecretKeySpec(tmpkey.getEncoded(), "AES");
224
225
226                                 byte[] keyDat = key.getKey();
227                                 byte[] saveDat = new byte[salt.length + keyDat.length];
228
229                                 for (int i = 0 ; i < salt.length;i++)
230                                 {
231                                         saveDat[i] = salt[i];
232                                 }
233
234                                 for (int i = 0 ; i < keyDat.length;i++)
235                                 {
236                                         saveDat[i + salt.length] = keyDat[i];
237                                 }
238
239
240                                 deleteFile(context);
241                                 writeToFile(saveDat, context);
242
243                                 return new SecretKeySpec(key.getKey(), "AES");
244                         }
245                         else{
246
247                                 Log.e("Ali:::::", "Using Saved......");
248
249                                 return new SecretKeySpec(keySaved, "AES");
250                         }
251
252
253                 } catch (Exception e) {
254                         StringWriter sw = new StringWriter();
255                         PrintWriter pw = new PrintWriter(sw);
256                         e.printStackTrace(pw);
257                          // stack trace as a string
258
259
260                         throw new Error("Failed generating key.   "  + sw.toString());
261                 }
262         }
263
264         /**
265          * Inits all the security stuff
266          */
267         public void initSecurity() throws ServerException {
268                 // try to get the salt and if one does not exist set one
269                 if (!getSalt()) {
270                         //Set the salt
271                         setSalt();
272                 }
273
274                 initCrypt();
275         }
276
277         /**
278          * Inits the HMAC generator.
279          */
280         /**
281          * Inits the HMAC generator.
282          */
283         private void initCrypt() {
284
285                 if (password == null) {
286                         return;
287                 }
288
289                 try {
290                         key = initKey();
291                         password = null; // drop password
292                         mac = Mac.getInstance("HmacSHA256");
293                         mac.init(key);
294                 } catch (Exception e) {
295                         e.printStackTrace();
296                         throw new Error("Failed To Initialize Ciphers");
297                 }
298         }
299
300
301         /*
302          * Builds the URL for the given request.
303          */
304         private URL buildRequest(boolean isput, long sequencenumber, long maxentries) throws IOException {
305                 String reqstring = isput ? "req=putslot" : "req=getslot";
306                 String urlstr = baseurl + "?" + reqstring + "&seq=" + sequencenumber;
307                 if (maxentries != 0)
308                         urlstr += "&max=" + maxentries;
309                 return new URL(urlstr);
310         }
311
312         private void setSalt() throws ServerException {
313
314                 if (salt != null) {
315                         // Salt already sent to server so dont set it again
316                         return;
317                 }
318
319                 try {
320                         byte[] saltTmp = new byte[SALT_SIZE];
321                         random.nextBytes(saltTmp);
322
323                         URL url = new URL(baseurl + "?req=setsalt");
324                         
325                         timer.startTime();
326                         URLConnection con = url.openConnection();
327                         HttpURLConnection http = (HttpURLConnection) con;
328
329                         http.setRequestMethod("POST");
330                         http.setFixedLengthStreamingMode(saltTmp.length);
331                         http.setDoOutput(true);
332                         http.setConnectTimeout(TIMEOUT_MILLIS);
333
334                         
335                         http.connect();
336
337                         OutputStream os = http.getOutputStream();
338                         os.write(saltTmp);
339                         os.flush();
340                         
341                         int responsecode = http.getResponseCode();
342                         if (responsecode != HttpURLConnection.HTTP_OK) {
343                                 // TODO: Remove this print
344                                 System.out.println(responsecode);
345                                 throw new Error("Invalid response");
346                         }
347
348                         timer.endTime();
349
350                         salt = saltTmp;
351                 } catch (Exception e) {
352                         // e.printStackTrace();
353                         timer.endTime();
354                         throw new ServerException("Failed setting salt", ServerException.TypeConnectTimeout);
355                 }
356         }
357
358         private boolean getSalt() throws ServerException {
359                 URL url = null;
360                 URLConnection con = null;
361                 HttpURLConnection http = null;
362
363                 try {
364                         url = new URL(baseurl + "?req=getsalt");
365                 } catch (Exception e) {
366                         // e.printStackTrace();
367                         throw new Error("getSlot failed");
368                 }
369                 try {
370
371                         timer.startTime();
372                         con = url.openConnection();
373                         http = (HttpURLConnection) con;
374                         http.setRequestMethod("POST");
375                         http.setConnectTimeout(TIMEOUT_MILLIS);
376                         http.setReadTimeout(TIMEOUT_MILLIS);
377
378                         
379                         http.connect();
380                         timer.endTime();
381
382
383                 } catch (SocketTimeoutException e) {
384                         timer.endTime();
385                         throw new ServerException("getSalt failed", ServerException.TypeConnectTimeout);
386                 } catch (Exception e) {
387                         // e.printStackTrace();
388                         throw new Error("getSlot failed  " + e.toString());
389                 }
390
391                 try {
392
393                         timer.startTime();
394
395                         int responsecode = http.getResponseCode();
396                         if (responsecode != HttpURLConnection.HTTP_OK) {
397                                 // TODO: Remove this print
398                                 // System.out.println(responsecode);
399                                 throw new Error("Invalid response");
400                         }
401
402 //                      Log.e("Aaaaa", "Code " + responsecode);
403
404
405                         InputStream is = http.getInputStream();
406 //
407 //
408 //                      BufferedReader rd= new BufferedReader(new InputStreamReader(is));
409 //                      int line;
410 //                      StringBuilder sb= new StringBuilder();
411 //                      while ((line = rd.read())!= -1)
412 //                      {
413 //                              sb.append((char)line);
414 //                              Log.e("Aaaaa", "line " + line);
415 //
416 //                      }
417 //
418 //
419 //                      int sdfsdfds = (int)sb.toString().charAt(0);
420 //                      Log.e("Aaaaa", "length " + (int)sb.toString().charAt(0));
421 //                      Log.e("Aaaaa", "Res " + sb.toString().length());
422
423
424 //                      is = new ByteArrayInputStream(sb.toString().getBytes(StandardCharsets.UTF_8));
425
426
427 //                      if (is.available() > 0) {
428 //                      if (sb.toString().length() > 0) {
429                         if(true)
430                         {
431                                 try {
432                                         DataInputStream dis = new DataInputStream(is);
433                                         int salt_length = dis.readInt();
434                                         byte[] tmp = new byte[salt_length];
435 //                              byte [] tmp = new byte[8];
436                                         dis.readFully(tmp);
437                                         salt = tmp;
438
439                                         for (int i = 0; i < 8; i++) {
440                                                 Log.e("ALIasdasdaS:", "asd " + ((int) salt[i] & 255));
441                                         }
442
443
444                                         timer.endTime();
445
446                                         return true;
447                                 }
448                                 catch (Exception e)
449                                 {
450                                         timer.endTime();
451
452                                         Log.e("Aaaaa", "Salt No Data");
453
454                                         return false;
455                                 }
456                         }
457                         else {
458
459
460                                 return false;
461                         }
462                 } catch (SocketTimeoutException e) {
463                         timer.endTime();
464
465                         throw new ServerException("getSalt failed", ServerException.TypeInputTimeout);
466                 } catch (Exception e) {
467
468                         throw new Error("getSlot failed + " + e);
469                 }
470         }
471
472
473         private byte[] createIV(long machineId, long localSequenceNumber) {
474                 ByteBuffer buffer = ByteBuffer.allocate(IV_SIZE);
475                 buffer.putLong(machineId);
476                 long localSequenceNumberShifted = localSequenceNumber << 16;
477                 buffer.putLong(localSequenceNumberShifted);
478                 return buffer.array();
479
480         }
481
482         private byte[] encryptSlotAndPrependIV(byte[] rawData, byte[] ivBytes) {
483                 try {
484                         ivBytes = new byte[IV_SIZE];
485                         IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
486                         //Cipher cipher = Cipher.getInstance("AES/CTR/PKCS5Padding");
487                         // We no longer need PKCS5Padding for CTR mode
488                         // There was a bug that the crypto library for Android that adds 16 more bytes into
489                         // the existing 2048 bytes after HMAC is calculated so that this causes HMAC mismatch
490                         // on the Java side that is expecting exactly 2048 bytes of padding.
491                         Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");
492                         cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
493
494                         byte[] encryptedBytes = cipher.doFinal(rawData);
495                         byte[] bytes = new byte[encryptedBytes.length + IV_SIZE];
496                         System.arraycopy(ivBytes, 0, bytes, 0, ivBytes.length);
497                         System.arraycopy(encryptedBytes, 0, bytes, IV_SIZE, encryptedBytes.length);
498
499                         return bytes;
500
501                 } catch (Exception e) {
502                         e.printStackTrace();
503                         throw new Error("Failed To Encrypt");
504                 }
505         }
506
507
508         private byte[] stripIVAndDecryptSlot(byte[] rawData) {
509                 try {
510                         byte[] ivBytes = new byte[IV_SIZE];
511                         byte[] encryptedBytes = new byte[rawData.length - IV_SIZE];
512                         System.arraycopy(rawData, 0, ivBytes, 0, IV_SIZE);
513                         System.arraycopy(rawData, IV_SIZE, encryptedBytes, 0 , encryptedBytes.length);
514
515                         IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
516
517                         //Cipher cipher = Cipher.getInstance("AES/CTR/PKCS5Padding");
518                         // We no longer need PKCS5Padding for CTR mode
519                         // There was a bug that the crypto library for Android that adds 16 more bytes into
520                         // the existing 2048 bytes after HMAC is calculated so that this causes HMAC mismatch
521                         // on the Java side that is expecting exactly 2048 bytes of padding.
522                         Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");
523                         cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
524
525                         return cipher.doFinal(encryptedBytes);
526
527                 } catch (Exception e) {
528                         e.printStackTrace();
529                         throw new Error("Failed To Decrypt");
530                 }
531         }
532
533         /*
534          * API for putting a slot into the queue.  Returns null on success.
535          * On failure, the server will send slots with newer sequence
536          * numbers.
537          */
538         public Slot[] putSlot(Slot slot, int max) throws ServerException {
539                 URL url = null;
540                 URLConnection con = null;
541                 HttpURLConnection http = null;
542
543                 try {
544                         if (salt == null) {
545                                 if (!getSalt()) {
546                                         throw new ServerException("putSlot failed", ServerException.TypeSalt);
547                                 }
548                                 initCrypt();
549                         }
550                         long sequencenumber = slot.getSequenceNumber();
551                         byte[] slotBytes = slot.encode(mac);
552                         // slotBytes = encryptCipher.doFinal(slotBytes);
553
554                         // byte[] iVBytes = slot.getSlotCryptIV();
555
556                         // byte[] bytes = new byte[slotBytes.length + IV_SIZE];
557                         // System.arraycopy(iVBytes, 0, bytes, 0, iVBytes.length);
558                         // System.arraycopy(slotBytes, 0, bytes, IV_SIZE, slotBytes.length);
559
560
561                         byte[] bytes = encryptSlotAndPrependIV(slotBytes, slot.getSlotCryptIV());
562
563
564
565                         url = buildRequest(true, sequencenumber, max);
566
567                         timer.startTime();
568                         con = url.openConnection();
569                         http = (HttpURLConnection) con;
570
571                         http.setRequestMethod("POST");
572                         http.setFixedLengthStreamingMode(bytes.length);
573                         http.setDoOutput(true);
574                         http.setConnectTimeout(TIMEOUT_MILLIS);
575                         http.setReadTimeout(TIMEOUT_MILLIS);
576                         http.connect();
577
578                         OutputStream os = http.getOutputStream();
579                         os.write(bytes);
580                         os.flush();
581
582                         timer.endTime();
583
584
585                         // System.out.println("Bytes Sent: " + bytes.length);
586                 } catch (ServerException e) {
587                         timer.endTime();
588
589                         throw e;
590                 } catch (SocketTimeoutException e) {
591                         timer.endTime();
592
593                         throw new ServerException("putSlot failed", ServerException.TypeConnectTimeout);
594                 } catch (Exception e) {
595                         // e.printStackTrace();
596                         throw new Error("putSlot failed");
597                 }
598
599
600
601                 try {
602                         timer.startTime();
603                         InputStream is = http.getInputStream();
604                         DataInputStream dis = new DataInputStream(is);
605                         byte[] resptype = new byte[7];
606                         dis.readFully(resptype);
607                         timer.endTime();
608
609                         if (Arrays.equals(resptype, "getslot".getBytes()))
610                         {       
611                                 return processSlots(dis);
612                         }
613                         else if (Arrays.equals(resptype, "putslot".getBytes()))
614                         {
615                                 return null;
616                         }
617                         else
618                                 throw new Error("Bad response to putslot");
619
620                 } catch (SocketTimeoutException e) {
621                                 timer.endTime();
622                         throw new ServerException("putSlot failed", ServerException.TypeInputTimeout);
623                 } catch (Exception e) {
624                         // e.printStackTrace();
625                         throw new Error("putSlot failed");
626                 }
627         }
628
629         /**
630          * Request the server to send all slots with the given
631          * sequencenumber or newer.
632          */
633         public Slot[] getSlots(long sequencenumber) throws ServerException {
634                 URL url = null;
635                 URLConnection con = null;
636                 HttpURLConnection http = null;
637
638                 try {
639                         if (salt == null) {
640                                 if (!getSalt()) {
641                                         throw new ServerException("getSlots failed", ServerException.TypeSalt);
642                                 }
643                                 initCrypt();
644                         }
645
646                         url = buildRequest(false, sequencenumber, 0);
647                         timer.startTime();
648                         con = url.openConnection();
649                         http = (HttpURLConnection) con;
650                         http.setRequestMethod("POST");
651                         http.setConnectTimeout(TIMEOUT_MILLIS);
652                         http.setReadTimeout(TIMEOUT_MILLIS);
653
654                         
655
656                         http.connect();
657                         timer.endTime();
658
659                 } catch (SocketTimeoutException e) {
660                         timer.endTime();
661
662                         throw new ServerException("getSlots failed", ServerException.TypeConnectTimeout);
663                 } catch (ServerException e) {
664                         timer.endTime();
665
666                         throw e;
667                 } catch (Exception e) {
668                         // e.printStackTrace();
669                         throw new Error("getSlots failed   " + e.toString());
670                 }
671
672                 try {
673                         
674                         timer.startTime();
675                         InputStream is = http.getInputStream(); 
676                         DataInputStream dis = new DataInputStream(is);
677                         byte[] resptype = new byte[7];
678                         
679                         dis.readFully(resptype);
680                         timer.endTime();
681
682                         if (!Arrays.equals(resptype, "getslot".getBytes()))
683                                 throw new Error("Bad Response: " + new String(resptype));
684
685                         return processSlots(dis);
686                 } catch (SocketTimeoutException e) {
687                         timer.endTime();
688
689                         throw new ServerException("getSlots failed", ServerException.TypeInputTimeout);
690                 } catch (Exception e) {
691                         // e.printStackTrace();
692                         StringWriter sw = new StringWriter();
693                         PrintWriter pw = new PrintWriter(sw);
694                         e.printStackTrace(pw);
695                         throw new Error("getSlots failed   " + sw.toString());
696                 }
697         }
698
699         /**
700          * Method that actually handles building Slot objects from the
701          * server response.  Shared by both putSlot and getSlots.
702          */
703         private Slot[] processSlots(DataInputStream dis) throws Exception {
704                 int numberofslots = dis.readInt();
705                 int[] sizesofslots = new int[numberofslots];
706
707                 Slot[] slots = new Slot[numberofslots];
708                 for (int i = 0; i < numberofslots; i++)
709                         sizesofslots[i] = dis.readInt();
710
711                 for (int i = 0; i < numberofslots; i++) {
712
713                         byte[] rawData = new byte[sizesofslots[i]];
714                         dis.readFully(rawData);
715
716
717                         // byte[] data = new byte[rawData.length - IV_SIZE];
718                         // System.arraycopy(rawData, IV_SIZE, data, 0, data.length);
719
720
721                         byte[] data = stripIVAndDecryptSlot(rawData);
722
723                         slots[i] = Slot.decode(table, data, mac);
724
725                         Log.e("Ali::::", "Slot Process");
726                 }
727                 dis.close();
728                 return slots;
729         }
730
731         public byte[] sendLocalData(byte[] sendData, long localSequenceNumber, String host, int port) {
732
733                 if (salt == null) {
734                         return null;
735                 }
736                 try {
737
738                         System.out.println("Passing Locally");
739
740                         mac.update(sendData);
741                         byte[] genmac = mac.doFinal();
742                         byte[] totalData = new byte[sendData.length + genmac.length];
743                         System.arraycopy(sendData, 0, totalData, 0, sendData.length);
744                         System.arraycopy(genmac, 0, totalData, sendData.length, genmac.length);
745
746                         // Encrypt the data for sending
747                         // byte[] encryptedData = encryptCipher.doFinal(totalData);
748 //                      byte[] encryptedData = encryptCipher.doFinal(totalData);
749
750                         byte[] iv = createIV(table.getMachineId(), table.getLocalSequenceNumber());
751                         byte[] encryptedData = encryptSlotAndPrependIV(totalData, iv);
752
753
754
755                         // Open a TCP socket connection to a local device
756                         Socket socket = new Socket(host, port);
757                         socket.setReuseAddress(true);
758                         DataOutputStream output = new DataOutputStream(socket.getOutputStream());
759                         DataInputStream input = new DataInputStream(socket.getInputStream());
760
761
762                         timer.startTime();
763                         // Send data to output (length of data, the data)
764                         output.writeInt(encryptedData.length);
765                         output.write(encryptedData, 0, encryptedData.length);
766                         output.flush();
767
768                         int lengthOfReturnData = input.readInt();
769                         byte[] returnData = new byte[lengthOfReturnData];
770                         input.readFully(returnData);
771
772                         timer.endTime();
773
774 //                      returnData = decryptCipher.doFinal(returnData);
775                         returnData = stripIVAndDecryptSlot(returnData);
776
777                         // We are done with this socket
778                         socket.close();
779
780                         mac.update(returnData, 0, returnData.length - HMAC_SIZE);
781                         byte[] realmac = mac.doFinal();
782                         byte[] recmac = new byte[HMAC_SIZE];
783                         System.arraycopy(returnData, returnData.length - realmac.length, recmac, 0, realmac.length);
784
785                         if (!Arrays.equals(recmac, realmac))
786                                 throw new Error("Local Error: Invalid HMAC!  Potential Attack!");
787
788                         byte[] returnData2 = new byte[lengthOfReturnData - recmac.length];
789                         System.arraycopy(returnData, 0, returnData2, 0, returnData2.length);
790
791                         return returnData2;
792                 } catch (Exception e) {
793                         e.printStackTrace();
794                         // throw new Error("Local comms failure...");
795
796                 }
797
798                 return null;
799         }
800
801         private void localServerWorkerFunction() {
802
803                 ServerSocket inputSocket = null;
804
805                 try {
806                         // Local server socket
807                         inputSocket = new ServerSocket(listeningPort);
808                         inputSocket.setReuseAddress(true);
809                         inputSocket.setSoTimeout(TIMEOUT_MILLIS);
810                 } catch (Exception e) {
811                         e.printStackTrace();
812                         throw new Error("Local server setup failure...");
813                 }
814
815                 while (!doEnd) {
816
817                         try {
818                                 // Accept incoming socket
819                                 Socket socket = inputSocket.accept();
820
821                                 DataInputStream input = new DataInputStream(socket.getInputStream());
822                                 DataOutputStream output = new DataOutputStream(socket.getOutputStream());
823
824                                 // Get the encrypted data from the server
825                                 int dataSize = input.readInt();
826                                 byte[] readData = new byte[dataSize];
827                                 input.readFully(readData);
828
829                                 timer.endTime();
830
831                                 // Decrypt the data
832 //                              readData = decryptCipher.doFinal(readData);
833                                 readData = stripIVAndDecryptSlot(readData);
834
835
836                                 mac.update(readData, 0, readData.length - HMAC_SIZE);
837                                 byte[] genmac = mac.doFinal();
838                                 byte[] recmac = new byte[HMAC_SIZE];
839                                 System.arraycopy(readData, readData.length - recmac.length, recmac, 0, recmac.length);
840
841                                 if (!Arrays.equals(recmac, genmac))
842                                         throw new Error("Local Error: Invalid HMAC!  Potential Attack!");
843
844                                 byte[] returnData = new byte[readData.length - recmac.length];
845                                 System.arraycopy(readData, 0, returnData, 0, returnData.length);
846
847                                 // Process the data
848                                 // byte[] sendData = table.acceptDataFromLocal(readData);
849                                 byte[] sendData = table.acceptDataFromLocal(returnData);
850
851                                 mac.update(sendData);
852                                 byte[] realmac = mac.doFinal();
853                                 byte[] totalData = new byte[sendData.length + realmac.length];
854                                 System.arraycopy(sendData, 0, totalData, 0, sendData.length);
855                                 System.arraycopy(realmac, 0, totalData, sendData.length, realmac.length);
856
857                                 // Encrypt the data for sending
858 //                              byte[] encryptedData = encryptCipher.doFinal(totalData);
859                                 byte[] iv = createIV(table.getMachineId(), table.getLocalSequenceNumber());
860                                 byte[] encryptedData = encryptSlotAndPrependIV(totalData, iv);
861
862
863                                 timer.startTime();
864                                 // Send data to output (length of data, the data)
865                                 output.writeInt(encryptedData.length);
866                                 output.write(encryptedData, 0, encryptedData.length);
867                                 output.flush();
868
869                                 // close the socket
870                                 socket.close();
871                         } catch (Exception e) {
872                                 e.printStackTrace();
873                                 // throw new Error("Local comms failure...");
874
875                         }
876                 }
877
878                 if (inputSocket != null) {
879                         try {
880                                 inputSocket.close();
881                         } catch (Exception e) {
882                                 e.printStackTrace();
883                                 throw new Error("Local server close failure...");
884                         }
885                 }
886         }
887
888         public void close() {
889                 doEnd = true;
890
891                 if (localServerThread != null) {
892                         try {
893                                 localServerThread.join();
894                         } catch (Exception e) {
895                                 e.printStackTrace();
896                                 throw new Error("Local Server thread join issue...");
897                         }
898                 }
899
900                 // System.out.println("Done Closing Cloud Comm");
901         }
902
903         protected void finalize() throws Throwable {
904                 try {
905                         close();        // close open files
906                 } finally {
907                         super.finalize();
908                 }
909         }
910
911 }