Block Chain Transactions, Commits multiple parts version
[iotcloud.git] / version2 / src / 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
10 /**
11  * This class provides a communication API to the webserver.  It also
12  * validates the HMACs on the slots and handles encryption.
13  * @author Brian Demsky <bdemsky@uci.edu>
14  * @version 1.0
15  */
16
17
18 class CloudComm {
19         String baseurl;
20         Cipher encryptCipher;
21         Cipher decryptCipher;
22         Mac mac;
23         String password;
24         SecureRandom random;
25         static final int SALT_SIZE = 8;
26         static final int TIMEOUT_MILLIS = 100;
27         byte salt[];
28         Table table;
29
30         /**
31          * Empty Constructor needed for child class.
32          */
33         CloudComm() {
34         }
35
36         /**
37          * Constructor for actual use. Takes in the url and password.
38          */
39         CloudComm(Table _table,  String _baseurl, String _password) {
40                 this.table = _table;
41                 this.baseurl = _baseurl;
42                 this.password = _password;
43                 this.random = new SecureRandom();
44         }
45
46         /**
47          * Generates Key from password.
48          */
49         private SecretKeySpec initKey() {
50                 try {
51                         PBEKeySpec keyspec = new PBEKeySpec(password.toCharArray(), salt, 65536, 128);
52                         SecretKey tmpkey = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256").generateSecret(keyspec);
53                         return new SecretKeySpec(tmpkey.getEncoded(), "AES");
54                 } catch (Exception e) {
55                         e.printStackTrace();
56                         throw new Error("Failed generating key.");
57                 }
58         }
59
60         /**
61          * Inits the HMAC generator.
62          */
63         private void initCrypt() {
64                 try {
65                         SecretKeySpec key = initKey();
66                         password = null; // drop password
67                         mac = Mac.getInstance("HmacSHA256");
68                         mac.init(key);
69                         encryptCipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
70                         encryptCipher.init(Cipher.ENCRYPT_MODE, key);
71                         decryptCipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
72                         decryptCipher.init(Cipher.DECRYPT_MODE, key);
73                 } catch (Exception e) {
74                         e.printStackTrace();
75                         throw new Error("Failed To Initialize Ciphers");
76                 }
77         }
78
79         /*
80          * Builds the URL for the given request.
81          */
82         private URL buildRequest(boolean isput, long sequencenumber, long maxentries) throws IOException {
83                 String reqstring = isput ? "req=putslot" : "req=getslot";
84                 String urlstr = baseurl + "?" + reqstring + "&seq=" + sequencenumber;
85                 if (maxentries != 0)
86                         urlstr += "&max=" + maxentries;
87                 return new URL(urlstr);
88         }
89
90         public void setSalt() throws ServerException {
91                 try {
92                         byte[] saltTmp = new byte[SALT_SIZE];
93                         random.nextBytes(saltTmp);
94                         URL url = new URL(baseurl + "?req=setsalt");
95                         URLConnection con = url.openConnection();
96                         HttpURLConnection http = (HttpURLConnection) con;
97                         http.setRequestMethod("POST");
98                         http.setFixedLengthStreamingMode(saltTmp.length);
99                         http.setDoOutput(true);
100                         http.setConnectTimeout(TIMEOUT_MILLIS);
101                         http.connect();
102                         OutputStream os = http.getOutputStream();
103                         os.write(saltTmp);
104                         int responsecode = http.getResponseCode();
105                         if (responsecode != HttpURLConnection.HTTP_OK) {
106                                 // TODO: Remove this print
107                                 // System.out.println(responsecode);
108                                 throw new Error("Invalid response");
109                         }
110
111                         salt = saltTmp;
112                 } catch (Exception e) {
113                         throw new ServerException("Failed setting salt", ServerException.TypeConnectTimeout);
114                 }
115                 initCrypt();
116         }
117
118         private void getSalt() throws ServerException {
119                 URL url = null;
120                 URLConnection con = null;
121                 HttpURLConnection http = null;
122
123                 try {
124                         url = new URL(baseurl + "?req=getsalt");
125                 } catch (Exception e) {
126                         e.printStackTrace();
127                         throw new Error("getSlot failed");
128                 }
129                 try {
130
131                         con = url.openConnection();
132                         http = (HttpURLConnection) con;
133                         http.setRequestMethod("POST");
134                         http.setConnectTimeout(TIMEOUT_MILLIS);
135                         http.setReadTimeout(TIMEOUT_MILLIS);
136                         http.connect();
137                 } catch (SocketTimeoutException e) {
138                         throw new ServerException("getSalt failed", ServerException.TypeConnectTimeout);
139                 } catch (Exception e) {
140                         e.printStackTrace();
141                         throw new Error("getSlot failed");
142                 }
143
144                 try {
145                         InputStream is = http.getInputStream();
146                         DataInputStream dis = new DataInputStream(is);
147                         int salt_length = dis.readInt();
148                         byte [] tmp = new byte[salt_length];
149                         dis.readFully(tmp);
150                         salt = tmp;
151                 } catch (SocketTimeoutException e) {
152                         throw new ServerException("getSalt failed", ServerException.TypeInputTimeout);
153                 } catch (Exception e) {
154                         e.printStackTrace();
155                         throw new Error("getSlot failed");
156                 }
157
158         }
159
160         /*
161          * API for putting a slot into the queue.  Returns null on success.
162          * On failure, the server will send slots with newer sequence
163          * numbers.
164          */
165         Slot[] putSlot(Slot slot, int max) throws ServerException {
166                 URL url = null;
167                 URLConnection con = null;
168                 HttpURLConnection http = null;
169
170                 try {
171                         if (salt == null) {
172                                 getSalt();
173                                 initCrypt();
174                         }
175
176                         long sequencenumber = slot.getSequenceNumber();
177                         byte[] bytes = slot.encode(mac);
178                         bytes = encryptCipher.doFinal(bytes);
179
180                         url = buildRequest(true, sequencenumber, max);
181                         con = url.openConnection();
182                         http = (HttpURLConnection) con;
183
184                         http.setRequestMethod("POST");
185                         http.setFixedLengthStreamingMode(bytes.length);
186                         http.setDoOutput(true);
187                         http.setConnectTimeout(TIMEOUT_MILLIS);
188                         http.setReadTimeout(TIMEOUT_MILLIS);
189                         http.connect();
190
191                         OutputStream os = http.getOutputStream();
192                         os.write(bytes);
193                         os.flush();
194                 } catch (SocketTimeoutException e) {
195                         throw new ServerException("putSlot failed", ServerException.TypeConnectTimeout);
196                 } catch (Exception e) {
197                         e.printStackTrace();
198                         throw new Error("putSlot failed");
199                 }
200
201
202
203                 try {
204                         InputStream is = http.getInputStream();
205                         DataInputStream dis = new DataInputStream(is);
206                         byte[] resptype = new byte[7];
207                         dis.readFully(resptype);
208
209                         if (Arrays.equals(resptype, "getslot".getBytes()))
210                                 return processSlots(dis);
211                         else if (Arrays.equals(resptype, "putslot".getBytes()))
212                                 return null;
213                         else
214                                 throw new Error("Bad response to putslot");
215
216                 } catch (SocketTimeoutException e) {
217                         throw new ServerException("putSlot failed", ServerException.TypeInputTimeout);
218                 } catch (Exception e) {
219                         e.printStackTrace();
220                         throw new Error("putSlot failed");
221                 }
222         }
223
224
225         /**
226          * Request the server to send all slots with the given
227          * sequencenumber or newer.
228          */
229         Slot[] getSlots(long sequencenumber) throws ServerException {
230                 URL url = null;
231                 URLConnection con = null;
232                 HttpURLConnection http = null;
233
234                 try {
235                         if (salt == null) {
236                                 getSalt();
237                                 initCrypt();
238                         }
239
240                         url = buildRequest(false, sequencenumber, 0);
241                         con = url.openConnection();
242                         http = (HttpURLConnection) con;
243                         http.setRequestMethod("POST");
244                         http.setConnectTimeout(TIMEOUT_MILLIS);
245                         http.setReadTimeout(TIMEOUT_MILLIS);
246
247
248                         http.connect();
249                 } catch (ServerException e) {
250                         throw new ServerException("getSlots failed", ServerException.TypeConnectTimeout);
251                 } catch (Exception e) {
252                         e.printStackTrace();
253                         throw new Error("getSlots failed");
254                 }
255
256                 try {
257                         InputStream is = http.getInputStream();
258                         DataInputStream dis = new DataInputStream(is);
259                         byte[] resptype = new byte[7];
260                         dis.readFully(resptype);
261                         if (!Arrays.equals(resptype, "getslot".getBytes()))
262                                 throw new Error("Bad Response: " + new String(resptype));
263                         else
264                                 return processSlots(dis);
265                 } catch (ServerException e) {
266                         throw new ServerException("getSlots failed", ServerException.TypeInputTimeout);
267                 } catch (Exception e) {
268                         e.printStackTrace();
269                         throw new Error("getSlots failed");
270                 }
271         }
272
273         /**
274          * Method that actually handles building Slot objects from the
275          * server response.  Shared by both putSlot and getSlots.
276          */
277         private Slot[] processSlots(DataInputStream dis) throws Exception {
278                 int numberofslots = dis.readInt();
279                 int[] sizesofslots = new int[numberofslots];
280                 Slot[] slots = new Slot[numberofslots];
281                 for (int i = 0; i < numberofslots; i++)
282                         sizesofslots[i] = dis.readInt();
283
284                 for (int i = 0; i < numberofslots; i++) {
285                         byte[] data = new byte[sizesofslots[i]];
286                         dis.readFully(data);
287
288                         data = decryptCipher.doFinal(data);
289
290                         slots[i] = Slot.decode(table, data, mac);
291                 }
292                 dis.close();
293                 return slots;
294         }
295 }