744c0fe9dd9a8d26b8a9192636bbdbaf7709c48c
[iotcloud.git] / version2 / src / 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                         IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
485                         Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");
486                         cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
487
488                         byte[] encryptedBytes = cipher.doFinal(rawData);
489
490                         byte[] bytes = new byte[encryptedBytes.length + IV_SIZE];
491                         System.arraycopy(ivBytes, 0, bytes, 0, ivBytes.length);
492                         System.arraycopy(encryptedBytes, 0, bytes, IV_SIZE, encryptedBytes.length);
493
494                         return bytes;
495
496                 } catch (Exception e) {
497                         e.printStackTrace();
498                         throw new Error("Failed To Encrypt");
499                 }
500         }
501
502
503         private byte[] stripIVAndDecryptSlot(byte[] rawData) {
504                 try {
505                         byte[] ivBytes = new byte[IV_SIZE];
506                         byte[] encryptedBytes = new byte[rawData.length - IV_SIZE];
507                         System.arraycopy(rawData, 0, ivBytes, 0, IV_SIZE);
508                         System.arraycopy(rawData, IV_SIZE, encryptedBytes, 0 , encryptedBytes.length);
509
510                         IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
511
512                         Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");
513                         cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
514
515                         return cipher.doFinal(encryptedBytes);
516
517                 } catch (Exception e) {
518                         e.printStackTrace();
519                         throw new Error("Failed To Decrypt");
520                 }
521         }
522
523         /*
524          * API for putting a slot into the queue.  Returns null on success.
525          * On failure, the server will send slots with newer sequence
526          * numbers.
527          */
528         public Slot[] putSlot(Slot slot, int max) throws ServerException {
529                 URL url = null;
530                 URLConnection con = null;
531                 HttpURLConnection http = null;
532
533                 try {
534                         if (salt == null) {
535                                 if (!getSalt()) {
536                                         throw new ServerException("putSlot failed", ServerException.TypeSalt);
537                                 }
538                                 initCrypt();
539                         }
540                         long sequencenumber = slot.getSequenceNumber();
541                         byte[] slotBytes = slot.encode(mac);
542                         // slotBytes = encryptCipher.doFinal(slotBytes);
543
544                         // byte[] iVBytes = slot.getSlotCryptIV();
545
546                         // byte[] bytes = new byte[slotBytes.length + IV_SIZE];
547                         // System.arraycopy(iVBytes, 0, bytes, 0, iVBytes.length);
548                         // System.arraycopy(slotBytes, 0, bytes, IV_SIZE, slotBytes.length);
549
550
551                         byte[] bytes = encryptSlotAndPrependIV(slotBytes, slot.getSlotCryptIV());
552
553
554
555                         url = buildRequest(true, sequencenumber, max);
556
557                         timer.startTime();
558                         con = url.openConnection();
559                         http = (HttpURLConnection) con;
560
561                         http.setRequestMethod("POST");
562                         http.setFixedLengthStreamingMode(bytes.length);
563                         http.setDoOutput(true);
564                         http.setConnectTimeout(TIMEOUT_MILLIS);
565                         http.setReadTimeout(TIMEOUT_MILLIS);
566                         http.connect();
567
568                         OutputStream os = http.getOutputStream();
569                         os.write(bytes);
570                         os.flush();
571
572                         timer.endTime();
573
574
575                         // System.out.println("Bytes Sent: " + bytes.length);
576                 } catch (ServerException e) {
577                         timer.endTime();
578
579                         throw e;
580                 } catch (SocketTimeoutException e) {
581                         timer.endTime();
582
583                         throw new ServerException("putSlot failed", ServerException.TypeConnectTimeout);
584                 } catch (Exception e) {
585                         // e.printStackTrace();
586                         throw new Error("putSlot failed");
587                 }
588
589
590
591                 try {
592                         timer.startTime();
593                         InputStream is = http.getInputStream();
594                         DataInputStream dis = new DataInputStream(is);
595                         byte[] resptype = new byte[7];
596                         dis.readFully(resptype);
597                         timer.endTime();
598
599                         if (Arrays.equals(resptype, "getslot".getBytes()))
600                         {       
601                                 return processSlots(dis);
602                         }
603                         else if (Arrays.equals(resptype, "putslot".getBytes()))
604                         {
605                                 return null;
606                         }
607                         else
608                                 throw new Error("Bad response to putslot");
609
610                 } catch (SocketTimeoutException e) {
611                                 timer.endTime();
612                         throw new ServerException("putSlot failed", ServerException.TypeInputTimeout);
613                 } catch (Exception e) {
614                         // e.printStackTrace();
615                         throw new Error("putSlot failed");
616                 }
617         }
618
619         /**
620          * Request the server to send all slots with the given
621          * sequencenumber or newer.
622          */
623         public Slot[] getSlots(long sequencenumber) throws ServerException {
624                 URL url = null;
625                 URLConnection con = null;
626                 HttpURLConnection http = null;
627
628                 try {
629                         if (salt == null) {
630                                 if (!getSalt()) {
631                                         throw new ServerException("getSlots failed", ServerException.TypeSalt);
632                                 }
633                                 initCrypt();
634                         }
635
636                         url = buildRequest(false, sequencenumber, 0);
637                         timer.startTime();
638                         con = url.openConnection();
639                         http = (HttpURLConnection) con;
640                         http.setRequestMethod("POST");
641                         http.setConnectTimeout(TIMEOUT_MILLIS);
642                         http.setReadTimeout(TIMEOUT_MILLIS);
643
644                         
645
646                         http.connect();
647                         timer.endTime();
648
649                 } catch (SocketTimeoutException e) {
650                         timer.endTime();
651
652                         throw new ServerException("getSlots failed", ServerException.TypeConnectTimeout);
653                 } catch (ServerException e) {
654                         timer.endTime();
655
656                         throw e;
657                 } catch (Exception e) {
658                         // e.printStackTrace();
659                         throw new Error("getSlots failed   " + e.toString());
660                 }
661
662                 try {
663                         
664                         timer.startTime();
665                         InputStream is = http.getInputStream(); 
666                         DataInputStream dis = new DataInputStream(is);
667                         byte[] resptype = new byte[7];
668                         
669                         dis.readFully(resptype);
670                         timer.endTime();
671
672                         if (!Arrays.equals(resptype, "getslot".getBytes()))
673                                 throw new Error("Bad Response: " + new String(resptype));
674
675                         return processSlots(dis);
676                 } catch (SocketTimeoutException e) {
677                         timer.endTime();
678
679                         throw new ServerException("getSlots failed", ServerException.TypeInputTimeout);
680                 } catch (Exception e) {
681                         // e.printStackTrace();
682                         StringWriter sw = new StringWriter();
683                         PrintWriter pw = new PrintWriter(sw);
684                         e.printStackTrace(pw);
685                         throw new Error("getSlots failed   " + sw.toString());
686                 }
687         }
688
689         /**
690          * Method that actually handles building Slot objects from the
691          * server response.  Shared by both putSlot and getSlots.
692          */
693         private Slot[] processSlots(DataInputStream dis) throws Exception {
694                 int numberofslots = dis.readInt();
695                 int[] sizesofslots = new int[numberofslots];
696
697                 Slot[] slots = new Slot[numberofslots];
698                 for (int i = 0; i < numberofslots; i++)
699                         sizesofslots[i] = dis.readInt();
700
701                 for (int i = 0; i < numberofslots; i++) {
702
703                         byte[] rawData = new byte[sizesofslots[i]];
704                         dis.readFully(rawData);
705
706
707                         // byte[] data = new byte[rawData.length - IV_SIZE];
708                         // System.arraycopy(rawData, IV_SIZE, data, 0, data.length);
709
710
711                         byte[] data = stripIVAndDecryptSlot(rawData);
712
713                         slots[i] = Slot.decode(table, data, mac);
714
715                         Log.e("Ali::::", "Slot Process");
716                 }
717                 dis.close();
718                 return slots;
719         }
720
721         public byte[] sendLocalData(byte[] sendData, long localSequenceNumber, String host, int port) {
722
723                 if (salt == null) {
724                         return null;
725                 }
726                 try {
727
728                         System.out.println("Passing Locally");
729
730                         mac.update(sendData);
731                         byte[] genmac = mac.doFinal();
732                         byte[] totalData = new byte[sendData.length + genmac.length];
733                         System.arraycopy(sendData, 0, totalData, 0, sendData.length);
734                         System.arraycopy(genmac, 0, totalData, sendData.length, genmac.length);
735
736                         // Encrypt the data for sending
737                         // byte[] encryptedData = encryptCipher.doFinal(totalData);
738 //                      byte[] encryptedData = encryptCipher.doFinal(totalData);
739
740                         byte[] iv = createIV(table.getMachineId(), table.getLocalSequenceNumber());
741                         byte[] encryptedData = encryptSlotAndPrependIV(totalData, iv);
742
743
744
745                         // Open a TCP socket connection to a local device
746                         Socket socket = new Socket(host, port);
747                         socket.setReuseAddress(true);
748                         DataOutputStream output = new DataOutputStream(socket.getOutputStream());
749                         DataInputStream input = new DataInputStream(socket.getInputStream());
750
751
752                         timer.startTime();
753                         // Send data to output (length of data, the data)
754                         output.writeInt(encryptedData.length);
755                         output.write(encryptedData, 0, encryptedData.length);
756                         output.flush();
757
758                         int lengthOfReturnData = input.readInt();
759                         byte[] returnData = new byte[lengthOfReturnData];
760                         input.readFully(returnData);
761
762                         timer.endTime();
763
764 //                      returnData = decryptCipher.doFinal(returnData);
765                         returnData = stripIVAndDecryptSlot(returnData);
766
767                         // We are done with this socket
768                         socket.close();
769
770                         mac.update(returnData, 0, returnData.length - HMAC_SIZE);
771                         byte[] realmac = mac.doFinal();
772                         byte[] recmac = new byte[HMAC_SIZE];
773                         System.arraycopy(returnData, returnData.length - realmac.length, recmac, 0, realmac.length);
774
775                         if (!Arrays.equals(recmac, realmac))
776                                 throw new Error("Local Error: Invalid HMAC!  Potential Attack!");
777
778                         byte[] returnData2 = new byte[lengthOfReturnData - recmac.length];
779                         System.arraycopy(returnData, 0, returnData2, 0, returnData2.length);
780
781                         return returnData2;
782                 } catch (Exception e) {
783                         e.printStackTrace();
784                         // throw new Error("Local comms failure...");
785
786                 }
787
788                 return null;
789         }
790
791         private void localServerWorkerFunction() {
792
793                 ServerSocket inputSocket = null;
794
795                 try {
796                         // Local server socket
797                         inputSocket = new ServerSocket(listeningPort);
798                         inputSocket.setReuseAddress(true);
799                         inputSocket.setSoTimeout(TIMEOUT_MILLIS);
800                 } catch (Exception e) {
801                         e.printStackTrace();
802                         throw new Error("Local server setup failure...");
803                 }
804
805                 while (!doEnd) {
806
807                         try {
808                                 // Accept incoming socket
809                                 Socket socket = inputSocket.accept();
810
811                                 DataInputStream input = new DataInputStream(socket.getInputStream());
812                                 DataOutputStream output = new DataOutputStream(socket.getOutputStream());
813
814                                 // Get the encrypted data from the server
815                                 int dataSize = input.readInt();
816                                 byte[] readData = new byte[dataSize];
817                                 input.readFully(readData);
818
819                                 timer.endTime();
820
821                                 // Decrypt the data
822 //                              readData = decryptCipher.doFinal(readData);
823                                 readData = stripIVAndDecryptSlot(readData);
824
825
826                                 mac.update(readData, 0, readData.length - HMAC_SIZE);
827                                 byte[] genmac = mac.doFinal();
828                                 byte[] recmac = new byte[HMAC_SIZE];
829                                 System.arraycopy(readData, readData.length - recmac.length, recmac, 0, recmac.length);
830
831                                 if (!Arrays.equals(recmac, genmac))
832                                         throw new Error("Local Error: Invalid HMAC!  Potential Attack!");
833
834                                 byte[] returnData = new byte[readData.length - recmac.length];
835                                 System.arraycopy(readData, 0, returnData, 0, returnData.length);
836
837                                 // Process the data
838                                 // byte[] sendData = table.acceptDataFromLocal(readData);
839                                 byte[] sendData = table.acceptDataFromLocal(returnData);
840
841                                 mac.update(sendData);
842                                 byte[] realmac = mac.doFinal();
843                                 byte[] totalData = new byte[sendData.length + realmac.length];
844                                 System.arraycopy(sendData, 0, totalData, 0, sendData.length);
845                                 System.arraycopy(realmac, 0, totalData, sendData.length, realmac.length);
846
847                                 // Encrypt the data for sending
848 //                              byte[] encryptedData = encryptCipher.doFinal(totalData);
849                                 byte[] iv = createIV(table.getMachineId(), table.getLocalSequenceNumber());
850                                 byte[] encryptedData = encryptSlotAndPrependIV(totalData, iv);
851
852
853                                 timer.startTime();
854                                 // Send data to output (length of data, the data)
855                                 output.writeInt(encryptedData.length);
856                                 output.write(encryptedData, 0, encryptedData.length);
857                                 output.flush();
858
859                                 // close the socket
860                                 socket.close();
861                         } catch (Exception e) {
862                                 e.printStackTrace();
863                                 // throw new Error("Local comms failure...");
864
865                         }
866                 }
867
868                 if (inputSocket != null) {
869                         try {
870                                 inputSocket.close();
871                         } catch (Exception e) {
872                                 e.printStackTrace();
873                                 throw new Error("Local server close failure...");
874                         }
875                 }
876         }
877
878         public void close() {
879                 doEnd = true;
880
881                 if (localServerThread != null) {
882                         try {
883                                 localServerThread.join();
884                         } catch (Exception e) {
885                                 e.printStackTrace();
886                                 throw new Error("Local Server thread join issue...");
887                         }
888                 }
889
890                 // System.out.println("Done Closing Cloud Comm");
891         }
892
893         protected void finalize() throws Throwable {
894                 try {
895                         close();        // close open files
896                 } finally {
897                         super.finalize();
898                 }
899         }
900
901 }