5 import java.util.Arrays;
7 import javax.crypto.spec.*;
8 import java.security.SecureRandom;
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.*;
18 * This class provides a communication API to the webserver. It also
19 * validates the HMACs on the slots and handles encryption.
20 * @author Brian Demsky <bdemsky@uci.edu>
26 private static final int SALT_SIZE = 8;
27 private static final int TIMEOUT_MILLIS = 2000; // 100
29 /** Sets the size for the HMAC. */
30 static final int HMAC_SIZE = 32;
32 private String baseurl;
33 private Cipher encryptCipher;
34 private Cipher decryptCipher;
36 private String password;
37 private SecureRandom random;
40 private int listeningPort = -1;
41 private Thread localServerThread = null;
42 private boolean doEnd = false;
44 private TimingSingleton timer = null;
46 private Context context;
52 * Empty Constructor needed for child class.
55 timer = TimingSingleton.getInstance();
58 private void deleteFile(Context context) {
59 File fd = context.getFileStreamPath("config.txt");
64 private void writeToFile(byte[] data,Context context) {
66 // OutputStreamWriter outputStreamWriter = new OutputStreamWriter(context.openFileOutput("config.txt", Context.MODE_PRIVATE));
67 // outputStreamWriter.write(data);
68 // outputStreamWriter.close();
70 FileOutputStream outputStreamWriter = context.openFileOutput("config.txt", Context.MODE_PRIVATE);
71 outputStreamWriter.write(data);
72 outputStreamWriter.close();
75 catch (IOException e) {
76 Log.e("Exception", "File write failed: " + e.toString());
80 private byte[] readFromFile(Context context) throws FileNotFoundException {
85 InputStream inputStream = context.openFileInput("config.txt");
87 if ( inputStream != null ) {
90 ret1 = new byte[inputStream.available()];
91 for(int i = 0; i < ret1.length;i++)
93 ret1[i] = (byte)inputStream.read();
99 // InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
100 // BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
101 // String receiveString = "";
102 // StringBuilder stringBuilder = new StringBuilder();
104 // while ( (receiveString = bufferedReader.readLine()) != null ) {
105 // stringBuilder.append(receiveString);
109 // ret = stringBuilder.toString();
112 catch (FileNotFoundException e) {
113 Log.e("login activity", "File not found: " + e.toString());
116 } catch (IOException e) {
117 Log.e("login activity", "Can not read file: " + e.toString());
126 * Constructor for actual use. Takes in the url and password.
128 CloudComm(Table _table, String _baseurl, String _password, int _listeningPort, Context _context) {
129 timer = TimingSingleton.getInstance();
131 this.baseurl = _baseurl;
132 this.password = _password;
133 this.random = new SecureRandom();
134 this.listeningPort = _listeningPort;
135 this.context = _context;
138 if (this.listeningPort > 0) {
139 localServerThread = new Thread(new Runnable() {
141 localServerWorkerFunction();
144 localServerThread.start();
149 * Generates Key from password.
151 private SecretKeySpec initKey() {
154 Log.e("Ali:::::", "KEY KEY KEY......");
158 boolean doCrypt = false;
160 byte[] keySaved = null;
163 // String file = readFromFile(context);
164 byte[] dat = readFromFile(context);//file.getBytes();
166 boolean saltMatch = true;
167 for(int i = 0; i < salt.length; i++)
170 Log.e("ALIasdasdaS:", " " + ((int) salt[i] & 255) + " " + ((int) dat[i] & 255));
172 if(dat[i] != salt[i])
181 keySaved = new byte[dat.length - salt.length];
182 for(int i = salt.length; i < dat.length;i++)
184 keySaved[i-salt.length] = dat[i];
190 Log.e("Ali:::::", "Salt No Match......");
207 Log.e("Ali:::::", "Doing Crypt......");
208 PKCS5S2ParametersGenerator generator = new PKCS5S2ParametersGenerator(new SHA256Digest());
209 generator.init(PBEParametersGenerator.PKCS5PasswordToUTF8Bytes(password.toCharArray()), salt, 65536);
210 KeyParameter key = (KeyParameter) generator.generateDerivedMacParameters(128);
213 // PBEKeySpec keyspec = new PBEKeySpec(password.toCharArray(),
217 // SecretKey tmpkey = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1").generateSecret(keyspec);
218 // SecretKey tmpkey = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256").generateSecret(keyspec);
221 // return new SecretKeySpec(tmpkey.getEncoded(), "AES");
224 byte[] keyDat = key.getKey();
225 byte[] saveDat = new byte[salt.length + keyDat.length];
227 for (int i = 0 ; i < salt.length;i++)
229 saveDat[i] = salt[i];
232 for (int i = 0 ; i < keyDat.length;i++)
234 saveDat[i + salt.length] = keyDat[i];
239 writeToFile(saveDat, context);
241 return new SecretKeySpec(key.getKey(), "AES");
245 Log.e("Ali:::::", "Using Saved......");
247 return new SecretKeySpec(keySaved, "AES");
251 } catch (Exception e) {
252 StringWriter sw = new StringWriter();
253 PrintWriter pw = new PrintWriter(sw);
254 e.printStackTrace(pw);
255 // stack trace as a string
258 throw new Error("Failed generating key. " + sw.toString());
263 * Inits all the security stuff
265 public void initSecurity() throws ServerException {
266 // try to get the salt and if one does not exist set one
276 * Inits the HMAC generator.
278 private void initCrypt() {
280 if (password == null) {
285 SecretKeySpec key = initKey();
286 password = null; // drop password
287 mac = Mac.getInstance("HmacSHA256");
289 encryptCipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
290 encryptCipher.init(Cipher.ENCRYPT_MODE, key);
291 decryptCipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
292 decryptCipher.init(Cipher.DECRYPT_MODE, key);
293 } catch (Exception e) {
295 throw new Error("Failed To Initialize Ciphers");
300 * Builds the URL for the given request.
302 private URL buildRequest(boolean isput, long sequencenumber, long maxentries) throws IOException {
303 String reqstring = isput ? "req=putslot" : "req=getslot";
304 String urlstr = baseurl + "?" + reqstring + "&seq=" + sequencenumber;
306 urlstr += "&max=" + maxentries;
307 return new URL(urlstr);
310 private void setSalt() throws ServerException {
313 // Salt already sent to server so dont set it again
318 byte[] saltTmp = new byte[SALT_SIZE];
319 random.nextBytes(saltTmp);
321 URL url = new URL(baseurl + "?req=setsalt");
324 URLConnection con = url.openConnection();
325 HttpURLConnection http = (HttpURLConnection) con;
327 http.setRequestMethod("POST");
328 http.setFixedLengthStreamingMode(saltTmp.length);
329 http.setDoOutput(true);
330 http.setConnectTimeout(TIMEOUT_MILLIS);
335 OutputStream os = http.getOutputStream();
339 int responsecode = http.getResponseCode();
340 if (responsecode != HttpURLConnection.HTTP_OK) {
341 // TODO: Remove this print
342 System.out.println(responsecode);
343 throw new Error("Invalid response");
349 } catch (Exception e) {
350 // e.printStackTrace();
352 throw new ServerException("Failed setting salt", ServerException.TypeConnectTimeout);
356 private boolean getSalt() throws ServerException {
358 URLConnection con = null;
359 HttpURLConnection http = null;
362 url = new URL(baseurl + "?req=getsalt");
363 } catch (Exception e) {
364 // e.printStackTrace();
365 throw new Error("getSlot failed");
370 con = url.openConnection();
371 http = (HttpURLConnection) con;
372 http.setRequestMethod("POST");
373 http.setConnectTimeout(TIMEOUT_MILLIS);
374 http.setReadTimeout(TIMEOUT_MILLIS);
381 } catch (SocketTimeoutException e) {
383 throw new ServerException("getSalt failed", ServerException.TypeConnectTimeout);
384 } catch (Exception e) {
385 // e.printStackTrace();
386 throw new Error("getSlot failed " + e.toString());
393 int responsecode = http.getResponseCode();
394 if (responsecode != HttpURLConnection.HTTP_OK) {
395 // TODO: Remove this print
396 // System.out.println(responsecode);
397 throw new Error("Invalid response");
400 // Log.e("Aaaaa", "Code " + responsecode);
403 InputStream is = http.getInputStream();
406 // BufferedReader rd= new BufferedReader(new InputStreamReader(is));
408 // StringBuilder sb= new StringBuilder();
409 // while ((line = rd.read())!= -1)
411 // sb.append((char)line);
412 // Log.e("Aaaaa", "line " + line);
417 // int sdfsdfds = (int)sb.toString().charAt(0);
418 // Log.e("Aaaaa", "length " + (int)sb.toString().charAt(0));
419 // Log.e("Aaaaa", "Res " + sb.toString().length());
422 // is = new ByteArrayInputStream(sb.toString().getBytes(StandardCharsets.UTF_8));
425 // if (is.available() > 0) {
426 // if (sb.toString().length() > 0) {
430 DataInputStream dis = new DataInputStream(is);
431 int salt_length = dis.readInt();
432 byte[] tmp = new byte[salt_length];
433 // byte [] tmp = new byte[8];
437 for (int i = 0; i < 8; i++) {
438 Log.e("ALIasdasdaS:", "asd " + ((int) salt[i] & 255));
450 Log.e("Aaaaa", "Salt No Data");
460 } catch (SocketTimeoutException e) {
463 throw new ServerException("getSalt failed", ServerException.TypeInputTimeout);
464 } catch (Exception e) {
466 throw new Error("getSlot failed + " + e);
471 * API for putting a slot into the queue. Returns null on success.
472 * On failure, the server will send slots with newer sequence
475 public Slot[] putSlot(Slot slot, int max) throws ServerException {
477 URLConnection con = null;
478 HttpURLConnection http = null;
483 throw new ServerException("putSlot failed", ServerException.TypeSalt);
488 long sequencenumber = slot.getSequenceNumber();
489 byte[] bytes = slot.encode(mac);
490 bytes = encryptCipher.doFinal(bytes);
495 url = buildRequest(true, sequencenumber, max);
498 con = url.openConnection();
499 http = (HttpURLConnection) con;
501 http.setRequestMethod("POST");
502 http.setFixedLengthStreamingMode(bytes.length);
503 http.setDoOutput(true);
504 http.setConnectTimeout(TIMEOUT_MILLIS);
505 http.setReadTimeout(TIMEOUT_MILLIS);
508 OutputStream os = http.getOutputStream();
515 // System.out.println("Bytes Sent: " + bytes.length);
516 } catch (ServerException e) {
520 } catch (SocketTimeoutException e) {
523 throw new ServerException("putSlot failed", ServerException.TypeConnectTimeout);
524 } catch (Exception e) {
525 // e.printStackTrace();
526 throw new Error("putSlot failed");
533 InputStream is = http.getInputStream();
534 DataInputStream dis = new DataInputStream(is);
535 byte[] resptype = new byte[7];
536 dis.readFully(resptype);
539 if (Arrays.equals(resptype, "getslot".getBytes()))
541 return processSlots(dis);
543 else if (Arrays.equals(resptype, "putslot".getBytes()))
548 throw new Error("Bad response to putslot");
550 } catch (SocketTimeoutException e) {
552 throw new ServerException("putSlot failed", ServerException.TypeInputTimeout);
553 } catch (Exception e) {
554 // e.printStackTrace();
555 throw new Error("putSlot failed");
560 * Request the server to send all slots with the given
561 * sequencenumber or newer.
563 public Slot[] getSlots(long sequencenumber) throws ServerException {
565 URLConnection con = null;
566 HttpURLConnection http = null;
571 throw new ServerException("getSlots failed", ServerException.TypeSalt);
576 url = buildRequest(false, sequencenumber, 0);
578 con = url.openConnection();
579 http = (HttpURLConnection) con;
580 http.setRequestMethod("POST");
581 http.setConnectTimeout(TIMEOUT_MILLIS);
582 http.setReadTimeout(TIMEOUT_MILLIS);
589 } catch (SocketTimeoutException e) {
592 throw new ServerException("getSlots failed", ServerException.TypeConnectTimeout);
593 } catch (ServerException e) {
597 } catch (Exception e) {
598 // e.printStackTrace();
599 throw new Error("getSlots failed " + e.toString());
605 InputStream is = http.getInputStream();
606 DataInputStream dis = new DataInputStream(is);
607 byte[] resptype = new byte[7];
609 dis.readFully(resptype);
612 if (!Arrays.equals(resptype, "getslot".getBytes()))
613 throw new Error("Bad Response: " + new String(resptype));
615 return processSlots(dis);
616 } catch (SocketTimeoutException e) {
619 throw new ServerException("getSlots failed", ServerException.TypeInputTimeout);
620 } catch (Exception e) {
621 // e.printStackTrace();
622 StringWriter sw = new StringWriter();
623 PrintWriter pw = new PrintWriter(sw);
624 e.printStackTrace(pw);
625 throw new Error("getSlots failed " + sw.toString());
630 * Method that actually handles building Slot objects from the
631 * server response. Shared by both putSlot and getSlots.
633 private Slot[] processSlots(DataInputStream dis) throws Exception {
634 int numberofslots = dis.readInt();
635 int[] sizesofslots = new int[numberofslots];
637 Slot[] slots = new Slot[numberofslots];
638 for (int i = 0; i < numberofslots; i++)
639 sizesofslots[i] = dis.readInt();
641 for (int i = 0; i < numberofslots; i++) {
643 byte[] data = new byte[sizesofslots[i]];
646 data = decryptCipher.doFinal(data);
648 slots[i] = Slot.decode(table, data, mac);
650 Log.e("Ali::::", "Slot Process");
656 public byte[] sendLocalData(byte[] sendData, String host, int port) {
663 System.out.println("Passing Locally");
665 mac.update(sendData);
666 byte[] genmac = mac.doFinal();
667 byte[] totalData = new byte[sendData.length + genmac.length];
668 System.arraycopy(sendData, 0, totalData, 0, sendData.length);
669 System.arraycopy(genmac, 0, totalData, sendData.length, genmac.length);
671 // Encrypt the data for sending
672 // byte[] encryptedData = encryptCipher.doFinal(totalData);
673 byte[] encryptedData = encryptCipher.doFinal(totalData);
675 // Open a TCP socket connection to a local device
676 Socket socket = new Socket(host, port);
677 socket.setReuseAddress(true);
678 DataOutputStream output = new DataOutputStream(socket.getOutputStream());
679 DataInputStream input = new DataInputStream(socket.getInputStream());
683 // Send data to output (length of data, the data)
684 output.writeInt(encryptedData.length);
685 output.write(encryptedData, 0, encryptedData.length);
688 int lengthOfReturnData = input.readInt();
689 byte[] returnData = new byte[lengthOfReturnData];
690 input.readFully(returnData);
694 returnData = decryptCipher.doFinal(returnData);
696 // We are done with this socket
699 mac.update(returnData, 0, returnData.length - HMAC_SIZE);
700 byte[] realmac = mac.doFinal();
701 byte[] recmac = new byte[HMAC_SIZE];
702 System.arraycopy(returnData, returnData.length - realmac.length, recmac, 0, realmac.length);
704 if (!Arrays.equals(recmac, realmac))
705 throw new Error("Local Error: Invalid HMAC! Potential Attack!");
707 byte[] returnData2 = new byte[lengthOfReturnData - recmac.length];
708 System.arraycopy(returnData, 0, returnData2, 0, returnData2.length);
711 } catch (SocketTimeoutException e) {
713 } catch (BadPaddingException e) {
715 } catch (IllegalBlockSizeException e) {
717 } catch (UnknownHostException e) {
719 } catch (IOException e) {
726 private void localServerWorkerFunction() {
728 ServerSocket inputSocket = null;
731 // Local server socket
732 inputSocket = new ServerSocket(listeningPort);
733 inputSocket.setReuseAddress(true);
734 inputSocket.setSoTimeout(TIMEOUT_MILLIS);
735 } catch (Exception e) {
737 throw new Error("Local server setup failure...");
743 // Accept incoming socket
744 Socket socket = inputSocket.accept();
746 DataInputStream input = new DataInputStream(socket.getInputStream());
747 DataOutputStream output = new DataOutputStream(socket.getOutputStream());
749 // Get the encrypted data from the server
750 int dataSize = input.readInt();
751 byte[] readData = new byte[dataSize];
752 input.readFully(readData);
757 readData = decryptCipher.doFinal(readData);
759 mac.update(readData, 0, readData.length - HMAC_SIZE);
760 byte[] genmac = mac.doFinal();
761 byte[] recmac = new byte[HMAC_SIZE];
762 System.arraycopy(readData, readData.length - recmac.length, recmac, 0, recmac.length);
764 if (!Arrays.equals(recmac, genmac))
765 throw new Error("Local Error: Invalid HMAC! Potential Attack!");
767 byte[] returnData = new byte[readData.length - recmac.length];
768 System.arraycopy(readData, 0, returnData, 0, returnData.length);
771 // byte[] sendData = table.acceptDataFromLocal(readData);
772 byte[] sendData = table.acceptDataFromLocal(returnData);
774 mac.update(sendData);
775 byte[] realmac = mac.doFinal();
776 byte[] totalData = new byte[sendData.length + realmac.length];
777 System.arraycopy(sendData, 0, totalData, 0, sendData.length);
778 System.arraycopy(realmac, 0, totalData, sendData.length, realmac.length);
780 // Encrypt the data for sending
781 byte[] encryptedData = encryptCipher.doFinal(totalData);
785 // Send data to output (length of data, the data)
786 output.writeInt(encryptedData.length);
787 output.write(encryptedData, 0, encryptedData.length);
792 } catch (SocketTimeoutException e) {
794 } catch (BadPaddingException e) {
796 } catch (IllegalBlockSizeException e) {
798 } catch (UnknownHostException e) {
800 } catch (IOException e) {
805 if (inputSocket != null) {
808 } catch (Exception e) {
810 throw new Error("Local server close failure...");
815 public void close() {
818 if (localServerThread != null) {
820 localServerThread.join();
821 } catch (Exception e) {
823 throw new Error("Local Server thread join issue...");
827 // System.out.println("Done Closing Cloud Comm");
830 protected void finalize() throws Throwable {
832 close(); // close open files