2 #include "TimingSingleton.h"
3 #include "SecureRandom.h"
9 * Empty Constructor needed for child class.
11 CloudComm::CloudComm() :
20 localServerThread(NULL),
22 timer(TimingSingleton_getInstance())
27 * Constructor for actual use. Takes in the url and password.
29 CloudComm::CloudComm(Table *_table, IoTString *_baseurl, IoTString *_password, int _listeningPort) :
34 random(new SecureRandom()),
37 listeningPort(_listeningPort),
38 localServerThread(NULL),
40 timer(TimingSingleton_getInstance()) {
41 if (listeningPort > 0) {
42 localServerThread = new Thread(new Runnable() {
44 localServerWorkerFunction();
47 localServerThread->start();
52 * Generates Key from password.
54 SecretKeySpec *CloudComm::initKey() {
56 PBEKeySpec keyspec = new PBEKeySpec(password->internalBytes(),
60 SecretKey tmpkey = SecretKeyFactory_getInstance("PBKDF2WithHmacSHA256")->generateSecret(keyspec);
61 return new SecretKeySpec(tmpkey->getEncoded(), "AES");
62 } catch (Exception *e) {
63 throw new Error("Failed generating key.");
68 * Inits all the security stuff
71 void CloudComm::initSecurity() {
72 // try to get the salt and if one does not exist set one
82 * Inits the HMAC generator.
84 void CloudComm::initCrypt() {
86 if (password == NULL) {
92 password = NULL;// drop password
93 mac = Mac_getInstance("HmacSHA256");
95 } catch (Exception *e) {
96 throw new Error("Failed To Initialize Ciphers");
101 * Builds the URL for the given request.
103 URL *CloudComm::buildRequest(bool isput, int64_t sequencenumber, int64_t maxentries) {
104 IoTString *reqstring = isput ? "req=putslot" : "req=getslot";
105 IoTString *urlstr = baseurl + "?" + reqstring + "&seq=" + sequencenumber;
107 urlstr += "&max=" + maxentries;
108 return new URL(urlstr);
111 void CloudComm::setSalt() {
114 // Salt already sent to server so dont set it again
119 Array<char> *saltTmp = new Array<char>(CloudComm_SALT_SIZE);
120 random->nextBytes(saltTmp);
122 for (int i = 0; i < CloudComm_SALT_SIZE; i++) {
123 printf("%d\n", (int)saltTmp->get(i) & 255);
127 URL* url = new URL(baseurl + "?req=setsalt");
130 URLConnection con = url->openConnection();
131 HttpURLConnection http = (HttpURLConnection) con;
133 http->setRequestMethod("POST");
134 http->setFixedLengthStreamingMode(saltTmp->length());
135 http->setDoOutput(true);
136 http->setConnectTimeout(CloudComm_TIMEOUT_MILLIS);
141 OutputStream* os = http->getOutputStream();
145 int responsecode = http->getResponseCode();
146 if (responsecode != HttpURLConnection.HTTP_OK) {
147 // TODO: Remove this print
148 System.out.println(responsecode);
149 throw new Error("Invalid response");
155 } catch (Exception *e) {
157 throw new ServerException("Failed setting salt", ServerException.TypeConnectTimeout);
161 bool CloudComm::getSalt() {
163 URLConnection *con = NULL;
164 HttpURLConnection *http = NULL;
167 url = new URL(baseurl + "?req=getsalt");
168 } catch (Exception *e) {
169 throw new Error("getSlot failed");
174 con = url->openConnection();
175 http = (HttpURLConnection) con;
176 http->setRequestMethod("POST");
177 http->setConnectTimeout(CloudComm_TIMEOUT_MILLIS);
178 http->setReadTimeout(CloudComm_TIMEOUT_MILLIS);
183 } catch (SocketTimeoutException *e) {
185 throw new ServerException("getSalt failed", ServerException.TypeConnectTimeout);
186 } catch (Exception *e) {
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 Array<char> * tmp = new Array<char>(salt_length);
216 } catch (SocketTimeoutException *e) {
219 throw new ServerException("getSalt failed", ServerException.TypeInputTimeout);
220 } catch (Exception *e) {
221 throw new Error("getSlot failed");
225 Array<char> *CloudComm::createIV(int64_t machineId, int64_t localSequenceNumber) {
226 ByteBuffer buffer = ByteBuffer.allocate(CloudComm_IV_SIZE);
227 buffer->putLong(machineId);
228 int64_t localSequenceNumberShifted = localSequenceNumber << 16;
229 buffer->putLong(localSequenceNumberShifted);
230 return buffer->array();
233 Array<char> *CloudComm::encryptSlotAndPrependIV(Array<char> *rawData, Array<char> *ivBytes) {
235 IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
236 Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");
237 cipher->init(Cipher.ENCRYPT_MODE, key, ivSpec);
239 Array<char> *encryptedBytes = cipher->doFinal(rawData);
241 Array<char> *chars = new Array<char>(encryptedBytes->length() + CloudComm_IV_SIZE);
242 System_arraycopy(ivBytes, 0, chars, 0, ivBytes.length());
243 System_arraycopy(encryptedBytes, 0, chars, CloudComm_IV_SIZE, encryptedBytes.length);
247 } catch (Exception *e) {
248 throw new Error("Failed To Encrypt");
253 Array<char> *CloudComm::stripIVAndDecryptSlot(Array<char> *rawData) {
255 Array<char> *ivBytes = new Array<char>(CloudComm_IV_SIZE);
256 Array<char> *encryptedBytes = new Array<char>(rawData->length() - CloudComm_IV_SIZE);
257 System_arraycopy(rawData, 0, ivBytes, 0, CloudComm_IV_SIZE);
258 System_arraycopy(rawData, CloudComm_IV_SIZE, encryptedBytes, 0, encryptedBytes->length);
260 IvParameterSpec* ivSpec = new IvParameterSpec(ivBytes);
262 Cipher* cipher = Cipher_getInstance("AES/CTR/NoPadding");
263 cipher->init(Cipher_DECRYPT_MODE, key, ivSpec);
264 return cipher->doFinal(encryptedBytes);
266 } catch (Exception *e) {
267 throw new Error("Failed To Decrypt");
273 * API for putting a slot into the queue. Returns NULL on success.
274 * On failure, the server will send slots with newer sequence
277 Array<Slot *> *CloudComm::putSlot(Slot *slot, int max) {
281 throw new ServerException("putSlot failed", ServerException.TypeSalt);
286 int64_t sequencenumber = slot->getSequenceNumber();
287 Array<char> *slotBytes = slot->encode(mac);
289 Array<char> *chars = encryptSlotAndPrependIV(slotBytes, slot->getSlotCryptIV());
291 URL *url = buildRequest(true, sequencenumber, max);
294 URLConnection * con = url->openConnection();
295 HttpURLConnection * http = (HttpURLConnection *) con;
297 http->setRequestMethod("POST");
298 http->setFixedLengthStreamingMode(chars->length);
299 http->setDoOutput(true);
300 http->setConnectTimeout(CloudComm_TIMEOUT_MILLIS);
301 http->setReadTimeout(CloudComm_TIMEOUT_MILLIS);
304 OutputStream * os = http->getOutputStream();
311 } catch (ServerException *e) {
315 } catch (SocketTimeoutException *e) {
318 throw new ServerException("putSlot failed", ServerException.TypeConnectTimeout);
319 } catch (Exception *e) {
320 throw new Error("putSlot failed");
327 InputStream is = http->getInputStream();
328 DataInputStream * dis = new DataInputStream(is);
329 Array<char> *resptype = new char[7];
330 dis->readFully(resptype);
333 if (Arrays->equals(resptype, "getslot"->getBytes())) {
334 return processSlots(dis);
335 } else if (Arrays->equals(resptype, "putslot"->getBytes())) {
338 throw new Error("Bad response to putslot");
340 } catch (SocketTimeoutException *e) {
342 throw new ServerException("putSlot failed", ServerException->TypeInputTimeout);
343 } catch (Exception *e) {
344 throw new Error("putSlot failed");
349 * Request the server to send all slots with the given
350 * sequencenumber or newer->
352 Array<Slot *> *CloudComm::getSlots(int64_t sequencenumber) {
356 throw new ServerException("getSlots failed", ServerException.TypeSalt);
361 URL *url = buildRequest(false, sequencenumber, 0);
363 URLConnection *con = url->openConnection();
364 HttpURLConnection *http = (HttpURLConnection) con;
365 http->setRequestMethod("POST");
366 http->setConnectTimeout(CloudComm_TIMEOUT_MILLIS);
367 http->setReadTimeout(CloudComm_TIMEOUT_MILLIS);
374 } catch (SocketTimeoutException *e) {
377 throw new ServerException("getSlots failed", ServerException.TypeConnectTimeout);
378 } catch (ServerException *e) {
382 } catch (Exception *e) {
383 throw new Error("getSlots failed");
389 InputStream *is = http->getInputStream();
390 DataInputStream *dis = new DataInputStream(is);
391 Array<char> *resptype = new Array<char>(7);
393 dis->readFully(resptype);
396 if (!resptype->equals("getslot".getBytes()))
397 throw new Error("Bad Response: " + new String(resptype));
399 return processSlots(dis);
400 } catch (SocketTimeoutException *e) {
403 throw new ServerException("getSlots failed", ServerException.TypeInputTimeout);
404 } catch (Exception *e) {
405 throw new Error("getSlots failed");
410 * Method that actually handles building Slot objects from the
411 * server response. Shared by both putSlot and getSlots.
413 Array<Slot *> *CloudComm::processSlots(DataInputStream *dis) {
414 int numberofslots = dis->readInt();
415 Array<int> * sizesofslots = new Array<int>(numberofslots);
417 Array<Slot*> * slots = new Array<Slot*>(numberofslots);
418 for (int i = 0; i < numberofslots; i++)
419 sizesofslots->set(i], dis->readInt());
421 for (int i = 0; i < numberofslots; i++) {
423 Array<char> *rawData = new Array<char>(sizesofslots->get(i));
424 dis->readFully(rawData);
427 // Array<char> * data = new char[rawData.length - IV_SIZE];
428 // System.arraycopy(rawData, IV_SIZE, data, 0, data.length);
431 Array<char> *data = stripIVAndDecryptSlot(rawData);
433 // data = decryptCipher.doFinal(data);
435 slots->set(i, Slot_decode(table, data, mac));
441 Array<char> *sendLocalData(Array<char> *sendData, int64_t localSequenceNumber, String host, int port) {
446 printf("Passing Locally"\m);
448 mac->update(sendData);
449 Array<char> *genmac = mac->doFinal();
450 Array<char> *totalData = new Array<char>(sendData->length() + genmac->length());
451 System_arraycopy(sendData, 0, totalData, 0, sendData.length());
452 System-arraycopy(genmac, 0, totalData, sendData.length, genmac->length());
454 // Encrypt the data for sending
455 Array<char> *iv = createIV(table->getMachineId(), table->getLocalSequenceNumber());
456 Array<char> *encryptedData = encryptSlotAndPrependIV(totalData, iv);
458 // Open a TCP socket connection to a local device
459 Socket* socket = new Socket(host, port);
460 socket->setReuseAddress(true);
461 DataOutputStream* output = new DataOutputStream(socket->getOutputStream());
462 DataInputStream* input = new DataInputStream(socket->getInputStream());
465 // Send data to output (length of data, the data)
466 output->writeInt(encryptedData->length);
467 output->write(encryptedData, 0, encryptedData->length);
470 int lengthOfReturnData = input->readInt();
471 Array<char> *returnData = new Array<char>(lengthOfReturnData);
472 input->readFully(returnData);
476 returnData = stripIVAndDecryptSlot(returnData);
478 // We are done with this socket
481 mac->update(returnData, 0, returnData->length - HMAC_SIZE);
482 Array<char> *realmac = mac->doFinal();
483 Array<char> *recmac = new Array<char>(HMAC_SIZE);
484 System_arraycopy(returnData, returnData->length - realmac->length, recmac, 0, realmac->length);
486 if (!Arrays->equals(recmac, realmac))
487 throw new Error("Local Error: Invalid HMAC! Potential Attack!");
489 Array<char> *returnData2 = new Array<char>(lengthOfReturnData - recmac->length());
490 System_arraycopy(returnData, 0, returnData2, 0, returnData2->length);
493 } catch (Exception *e) {
494 printf("Exception\n");
500 void CloudComm::localServerWorkerFunction() {
501 ServerSocket *inputSocket = NULL;
504 // Local server socket
505 inputSocket = new ServerSocket(listeningPort);
506 inputSocket->setReuseAddress(true);
507 inputSocket->setSoTimeout(CloudComm_TIMEOUT_MILLIS);
508 } catch (Exception *e) {
509 throw new Error("Local server setup failure...");
514 // Accept incoming socket
515 Socket *socket = inputSocket->accept();
517 DataInputStream *input = new DataInputStream(socket->getInputStream());
518 DataOutputStream *output = new DataOutputStream(socket->getOutputStream());
520 // Get the encrypted data from the server
521 int dataSize = input->readInt();
522 Array<char> *readData = new Array<char>(dataSize);
523 input->readFully(readData);
528 readData = stripIVAndDecryptSlot(readData);
530 mac->update(readData, 0, readData->length - HMAC_SIZE);
531 Array<char> *genmac = mac->doFinal();
532 Array<char> *recmac = new Array<char>(HMAC_SIZE);
533 System_arraycopy(readData, readData->length() - recmac->length(), recmac, 0, recmac->length());
535 if (!recmac->equals(genmac))
536 throw new Error("Local Error: Invalid HMAC! Potential Attack!");
538 Array<char> *returnData = new Array<char>(readData->length() - recmac->length());
539 System_arraycopy(readData, 0, returnData, 0, returnData->length());
542 Array<char> *sendData = table->acceptDataFromLocal(returnData);
545 mac->update(sendData);
546 Array<char> *realmac = mac->doFinal();
547 Array<char> *totalData = new Array<char>(sendData->length() + realmac->length());
548 System_arraycopy(sendData, 0, totalData, 0, sendData->length());
549 System_arraycopy(realmac, 0, totalData, sendData->length(), realmac->length());
551 // Encrypt the data for sending
552 Array<char> *iv = createIV(table->getMachineId(), table->getLocalSequenceNumber());
553 Array<char> *encryptedData = encryptSlotAndPrependIV(totalData, iv);
557 // Send data to output (length of data, the data)
558 output->writeInt(encryptedData->length());
559 output->write(encryptedData, 0, encryptedData->length());
564 } catch (Exception *e) {
568 if (inputSocket != NULL) {
570 inputSocket->close();
571 } catch (Exception *e) {
572 throw new Error("Local server close failure...");
577 void CloudComm::close() {
580 if (localServerThread != NULL) {
582 localServerThread->join();
583 } catch (Exception *e) {
584 throw new Error("Local Server thread join issue...");