4 * Empty Constructor needed for child class.
6 CloudComm::CloudComm() :
15 localServerThread(NULL),
17 timer(TimingSingleton_getInstance())
22 * Constructor for actual use. Takes in the url and password.
24 CloudComm::CloudComm(Table *_table, IoTString *_baseurl, IoTString *_password, int _listeningPort) :
29 random(new SecureRandom()),
32 listeningPort(_listeningPort),
33 localServerThread(NULL),
35 timer(TimingSingleton_getInstance()) {
36 if (listeningPort > 0) {
37 localServerThread = new Thread(new Runnable() {
39 localServerWorkerFunction();
42 localServerThread->start();
47 * Generates Key from password.
49 SecretKeySpec *CloudComm::initKey() {
51 PBEKeySpec keyspec = new PBEKeySpec(password->toCharArray(),
55 SecretKey tmpkey = SecretKeyFactory_getInstance("PBKDF2WithHmacSHA256")->generateSecret(keyspec);
56 return new SecretKeySpec(tmpkey->getEncoded(), "AES");
57 } catch (Exception e) {
59 throw new Error("Failed generating key.");
64 * Inits all the security stuff
67 void CloudComm::initSecurity() {
68 // try to get the salt and if one does not exist set one
78 * Inits the HMAC generator.
80 void CloudComm::initCrypt() {
82 if (password == NULL) {
88 password = NULL;// drop password
89 mac = Mac_getInstance("HmacSHA256");
91 } catch (Exception e) {
93 throw new Error("Failed To Initialize Ciphers");
98 * Builds the URL for the given request.
100 URL *CloudComm::buildRequest(bool isput, int64_t sequencenumber, int64_t maxentries) {
101 IoTString *reqstring = isput ? "req=putslot" : "req=getslot";
102 IoTString *urlstr = baseurl + "?" + reqstring + "&seq=" + sequencenumber;
104 urlstr += "&max=" + maxentries;
105 return new URL(urlstr);
108 void CloudComm::setSalt() {
111 // Salt already sent to server so dont set it again
116 Array<char> *saltTmp = new char[SALT_SIZE];
117 random->nextBytes(saltTmp);
119 for (int i = 0; i < SALT_SIZE; i++) {
120 printf("%d\n", (int)saltTmp[i] & 255);
124 URL url = new URL(baseurl + "?req=setsalt");
127 URLConnection con = url->openConnection();
128 HttpURLConnection http = (HttpURLConnection) con;
130 http->setRequestMethod("POST");
131 http->setFixedLengthStreamingMode(saltTmp->length());
132 http->setDoOutput(true);
133 http->setConnectTimeout(TIMEOUT_MILLIS);
138 OutputStream os = http->getOutputStream();
142 int responsecode = http->getResponseCode();
143 if (responsecode != HttpURLConnection.HTTP_OK) {
144 // TODO: Remove this print
145 System.out.println(responsecode);
146 throw new Error("Invalid response");
152 } catch (Exception e) {
153 // e.printStackTrace();
155 throw new ServerException("Failed setting salt", ServerException.TypeConnectTimeout);
159 bool CloudComm::getSalt() {
161 URLConnection *con = NULL;
162 HttpURLConnection *http = NULL;
165 url = new URL(baseurl + "?req=getsalt");
166 } catch (Exception e) {
167 // e.printStackTrace();
168 throw new Error("getSlot failed");
173 con = url->openConnection();
174 http = (HttpURLConnection) con;
175 http->setRequestMethod("POST");
176 http->setConnectTimeout(TIMEOUT_MILLIS);
177 http->setReadTimeout(TIMEOUT_MILLIS);
182 } catch (SocketTimeoutException e) {
184 throw new ServerException("getSalt failed", ServerException.TypeConnectTimeout);
185 } catch (Exception e) {
186 // e.printStackTrace();
187 throw new Error("getSlot failed");
194 int responsecode = http.getResponseCode();
195 if (responsecode != HttpURLConnection.HTTP_OK) {
196 // TODO: Remove this print
197 // System.out.println(responsecode);
198 throw new Error("Invalid response");
201 InputStream is = http->getInputStream();
202 if (is->available() > 0) {
203 DataInputStream dis = new DataInputStream(is);
204 int salt_length = dis->readInt();
205 char [] tmp = new char[salt_length];
216 } catch (SocketTimeoutException e) {
219 throw new ServerException("getSalt failed", ServerException.TypeInputTimeout);
220 } catch (Exception e) {
221 // e.printStackTrace();
222 throw new Error("getSlot failed");
226 Array<char> *CloudComm::createIV(int64_t machineId, int64_t localSequenceNumber) {
227 ByteBuffer buffer = ByteBuffer.allocate(IV_SIZE);
228 buffer->putLong(machineId);
229 int64_t localSequenceNumberShifted = localSequenceNumber << 16;
230 buffer->putLong(localSequenceNumberShifted);
231 return buffer->array();
234 Array<char> *CloudComm::encryptSlotAndPrependIV(Array<char> *rawData, Array<char> *ivBytes) {
236 IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
237 Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");
238 cipher->init(Cipher.ENCRYPT_MODE, key, ivSpec);
240 Array<char> *encryptedBytes = cipher->doFinal(rawData);
242 Array<char> *chars = new char[encryptedBytes->length + IV_SIZE];
243 System.arraycopy(ivBytes, 0, chars, 0, ivBytes.length);
244 System.arraycopy(encryptedBytes, 0, chars, IV_SIZE, encryptedBytes.length);
248 } catch (Exception e) {
250 throw new Error("Failed To Encrypt");
255 Array<char> *CloudComm::stripIVAndDecryptSlot(Array<char> *rawData) {
257 Array<char> *ivBytes = new char[IV_SIZE];
258 Array<char> *encryptedBytes = new char[rawData->length - IV_SIZE];
259 System.arraycopy(rawData, 0, ivBytes, 0, IV_SIZE);
260 System.arraycopy(rawData, IV_SIZE, encryptedBytes, 0, encryptedBytes->length);
262 IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
264 Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");
265 cipher->init(Cipher.DECRYPT_MODE, key, ivSpec);
266 return cipher->doFinal(encryptedBytes);
268 } catch (Exception e) {
270 throw new Error("Failed To Decrypt");
276 * API for putting a slot into the queue. Returns NULL on success.
277 * On failure, the server will send slots with newer sequence
280 Array<Slot *> *CloudComm::putSlot(Slot *slot, int max) {
282 URLConnection con = NULL;
283 HttpURLConnection http = NULL;
288 throw new ServerException("putSlot failed", ServerException.TypeSalt);
293 int64_t sequencenumber = slot->getSequenceNumber();
294 Array<char> *slotBytes = slot->encode(mac);
295 // slotBytes = encryptCipher.doFinal(slotBytes);
297 // Array<char> * iVBytes = slot.getSlotCryptIV();
299 // Array<char> * chars = new char[slotBytes.length + IV_SIZE];
300 // System.arraycopy(iVBytes, 0, chars, 0, iVBytes.length);
301 // System.arraycopy(slotBytes, 0, chars, IV_SIZE, slotBytes.length);
304 Array<char> *chars = encryptSlotAndPrependIV(slotBytes, slot->getSlotCryptIV());
306 url = buildRequest(true, sequencenumber, max);
309 con = url->openConnection();
310 http = (HttpURLConnection) con;
312 http->setRequestMethod("POST");
313 http->setFixedLengthStreamingMode(chars->length);
314 http->setDoOutput(true);
315 http->setConnectTimeout(TIMEOUT_MILLIS);
316 http->setReadTimeout(TIMEOUT_MILLIS);
319 OutputStream os = http->getOutputStream();
326 // System.out.println("Bytes Sent: " + chars.length);
327 } catch (ServerException e) {
331 } catch (SocketTimeoutException e) {
334 throw new ServerException("putSlot failed", ServerException.TypeConnectTimeout);
335 } catch (Exception e) {
336 // e.printStackTrace();
337 throw new Error("putSlot failed");
344 InputStream is = http->getInputStream();
345 DataInputStream dis = new DataInputStream(is);
346 Array<char> *resptype = new char[7];
347 dis->readFully(resptype);
350 if (Arrays->equals(resptype, "getslot"->getBytes())) {
351 return processSlots(dis);
352 } else if (Arrays->equals(resptype, "putslot"->getBytes())) {
355 throw new Error("Bad response to putslot");
357 } catch (SocketTimeoutException e) {
359 throw new ServerException("putSlot failed", ServerException->TypeInputTimeout);
360 } catch (Exception e) {
361 // e->printStackTrace();
362 throw new Error("putSlot failed");
367 * Request the server to send all slots with the given
368 * sequencenumber or newer->
370 Array<Slot *> *CloudComm::getSlots(int64_t sequencenumber) {
372 URLConnection con = NULL;
373 HttpURLConnection http = NULL;
378 throw new ServerException("getSlots failed", ServerException.TypeSalt);
383 url = buildRequest(false, sequencenumber, 0);
385 con = url->openConnection();
386 http = (HttpURLConnection) con;
387 http->setRequestMethod("POST");
388 http->setConnectTimeout(TIMEOUT_MILLIS);
389 http->setReadTimeout(TIMEOUT_MILLIS);
396 } catch (SocketTimeoutException e) {
399 throw new ServerException("getSlots failed", ServerException.TypeConnectTimeout);
400 } catch (ServerException e) {
404 } catch (Exception e) {
405 // e.printStackTrace();
406 throw new Error("getSlots failed");
412 InputStream is = http->getInputStream();
413 DataInputStream dis = new DataInputStream(is);
414 Array<char> *resptype = new char[7];
416 dis->readFully(resptype);
419 if (!Arrays.equals(resptype, "getslot".getBytes()))
420 throw new Error("Bad Response: " + new String(resptype));
422 return processSlots(dis);
423 } catch (SocketTimeoutException e) {
426 throw new ServerException("getSlots failed", ServerException.TypeInputTimeout);
427 } catch (Exception e) {
428 // e.printStackTrace();
429 throw new Error("getSlots failed");
434 * Method that actually handles building Slot objects from the
435 * server response. Shared by both putSlot and getSlots.
437 Array<Slot *> *CloudComm::processSlots(DataInputStream dis) {
438 int numberofslots = dis->readInt();
439 int[] sizesofslots = new int[numberofslots];
441 Slot[] slots = new Slot[numberofslots];
442 for (int i = 0; i < numberofslots; i++)
443 sizesofslots[i] = dis->readInt();
445 for (int i = 0; i < numberofslots; i++) {
447 Array<char> *rawData = new char[sizesofslots[i]];
448 dis->readFully(rawData);
451 // Array<char> * data = new char[rawData.length - IV_SIZE];
452 // System.arraycopy(rawData, IV_SIZE, data, 0, data.length);
455 Array<char> *data = stripIVAndDecryptSlot(rawData);
457 // data = decryptCipher.doFinal(data);
459 slots[i] = Slot->decode(table, data, mac);
465 Array<char> *sendLocalData(Array<char> *sendData, int64_t localSequenceNumber, String host, int port) {
471 System.out.println("Passing Locally");
473 mac->update(sendData);
474 Array<char> *genmac = mac->doFinal();
475 Array<char> *totalData = new char[sendData->length + genmac->length];
476 System.arraycopy(sendData, 0, totalData, 0, sendData.length);
477 System.arraycopy(genmac, 0, totalData, sendData.length, genmac->length);
479 // Encrypt the data for sending
480 // Array<char> * encryptedData = encryptCipher.doFinal(totalData);
481 // Array<char> * encryptedData = encryptCipher.doFinal(totalData);
482 Array<char> *iv = createIV(table->getMachineId(), table->getLocalSequenceNumber());
483 Array<char> *encryptedData = encryptSlotAndPrependIV(totalData, iv);
485 // Open a TCP socket connection to a local device
486 Socket socket = new Socket(host, port);
487 socket->setReuseAddress(true);
488 DataOutputStream output = new DataOutputStream(socket->getOutputStream());
489 DataInputStream input = new DataInputStream(socket->getInputStream());
493 // Send data to output (length of data, the data)
494 output->writeInt(encryptedData->length);
495 output->write(encryptedData, 0, encryptedData->length);
498 int lengthOfReturnData = input->readInt();
499 Array<char> *returnData = new char[lengthOfReturnData];
500 input->readFully(returnData);
504 // returnData = decryptCipher->doFinal(returnData);
505 returnData = stripIVAndDecryptSlot(returnData);
506 // returnData = decryptCipher->doFinal(returnData);
508 // We are done with this socket
511 mac->update(returnData, 0, returnData->length - HMAC_SIZE);
512 Array<char> *realmac = mac->doFinal();
513 Array<char> *recmac = new char[HMAC_SIZE];
514 System->arraycopy(returnData, returnData->length - realmac->length, recmac, 0, realmac->length);
516 if (!Arrays->equals(recmac, realmac))
517 throw new Error("Local Error: Invalid HMAC! Potential Attack!");
519 Array<char> *returnData2 = new char[lengthOfReturnData - recmac->length];
520 System->arraycopy(returnData, 0, returnData2, 0, returnData2->length);
523 } catch (Exception e) {
524 e->printStackTrace();
525 // throw new Error("Local comms failure...");
532 void CloudComm::localServerWorkerFunction() {
534 ServerSocket inputSocket = NULL;
537 // Local server socket
538 inputSocket = new ServerSocket(listeningPort);
539 inputSocket->setReuseAddress(true);
540 inputSocket->setSoTimeout(TIMEOUT_MILLIS);
541 } catch (Exception e) {
542 e->printStackTrace();
543 throw new Error("Local server setup failure...");
549 // Accept incoming socket
550 Socket socket = inputSocket->accept();
552 DataInputStream input = new DataInputStream(socket->getInputStream());
553 DataOutputStream output = new DataOutputStream(socket->getOutputStream());
555 // Get the encrypted data from the server
556 int dataSize = input->readInt();
557 Array<char> *readData = new char[dataSize];
558 input->readFully(readData);
563 // readData = decryptCipher->doFinal(readData);
564 readData = stripIVAndDecryptSlot(readData);
566 mac->update(readData, 0, readData->length - HMAC_SIZE);
567 Array<char> *genmac = mac->doFinal();
568 Array<char> *recmac = new char[HMAC_SIZE];
569 System->arraycopy(readData, readData->length - recmac->length, recmac, 0, recmac->length);
571 if (!Arrays->equals(recmac, genmac))
572 throw new Error("Local Error: Invalid HMAC! Potential Attack!");
574 Array<char> *returnData = new char[readData->length - recmac->length];
575 System->arraycopy(readData, 0, returnData, 0, returnData->length);
578 // Array<char> * sendData = table->acceptDataFromLocal(readData);
579 Array<char> *sendData = table->acceptDataFromLocal(returnData);
582 mac->update(sendData);
583 Array<char> *realmac = mac->doFinal();
584 Array<char> *totalData = new char[sendData->length + realmac->length];
585 System->arraycopy(sendData, 0, totalData, 0, sendData->length);
586 System->arraycopy(realmac, 0, totalData, sendData->length, realmac->length);
588 // Encrypt the data for sending
589 // Array<char> * encryptedData = encryptCipher->doFinal(totalData);
590 Array<char> *iv = createIV(table->getMachineId(), table->getLocalSequenceNumber());
591 Array<char> *encryptedData = encryptSlotAndPrependIV(totalData, iv);
595 // Send data to output (length of data, the data)
596 output->writeInt(encryptedData->length);
597 output->write(encryptedData, 0, encryptedData->length);
602 } catch (Exception e) {
607 if (inputSocket != NULL) {
609 inputSocket->close();
610 } catch (Exception e) {
611 e->printStackTrace();
612 throw new Error("Local server close failure...");
617 void CloudComm::close() {
620 if (localServerThread != NULL) {
622 localServerThread->join();
623 } catch (Exception e) {
624 e->printStackTrace();
625 throw new Error("Local Server thread join issue...");
629 // System.out.println("Done Closing Cloud Comm");