2 #include "TimingSingleton.h"
3 #include "SecureRandom.h"
11 #include "ByteBuffer.h"
13 #include <sys/socket.h>
17 * Empty Constructor needed for child class.
19 CloudComm::CloudComm() :
28 localServerThread(NULL),
30 timer(TimingSingleton_getInstance()),
31 getslot(new Array<char>("getslot", 7)),
32 putslot(new Array<char>("putslot", 7))
36 void *threadWrapper(void *cloud) {
37 CloudComm *c = (CloudComm *) cloud;
38 c->localServerWorkerFunction();
43 * Constructor for actual use. Takes in the url and password.
45 CloudComm::CloudComm(Table *_table, IoTString *_baseurl, IoTString *_password, int _listeningPort) :
50 random(new SecureRandom()),
53 listeningPort(_listeningPort),
54 localServerThread(NULL),
56 timer(TimingSingleton_getInstance()) {
57 if (listeningPort > 0) {
58 pthread_create(&localServerThread, NULL, threadWrapper, this);
63 * Generates Key from password.
65 AESKey *CloudComm::initKey() {
67 AESKey *key = new AESKey(password->internalBytes(),
72 } catch (Exception *e) {
73 throw new Error("Failed generating key.");
78 * Inits all the security stuff
81 void CloudComm::initSecurity() {
82 // try to get the salt and if one does not exist set one
92 * Inits the HMAC generator.
94 void CloudComm::initCrypt() {
95 if (password == NULL) {
100 password = NULL;// drop password
103 } catch (Exception *e) {
104 throw new Error("Failed To Initialize Ciphers");
109 * Builds the URL for the given request.
111 IoTString *CloudComm::buildRequest(bool isput, int64_t sequencenumber, int64_t maxentries) {
112 const char *reqstring = isput ? "req=putslot" : "req=getslot";
113 char *buffer = (char *) malloc(baseurl->length() + 200);
114 memcpy(buffer, baseurl->internalBytes(), baseurl->length());
115 int offset = baseurl->length();
116 offset += sprintf(&buffer[offset], "?%s&seq=%" PRId64, reqstring, sequencenumber);
118 sprintf(&buffer[offset], "&max=%" PRId64, maxentries);
119 IoTString *urlstr = new IoTString(buffer);
123 int openURL(IoTString *url, bool isPost) {
127 int createSocket(IoTString *host, int port) {
131 int createSocket(int port) {
135 int acceptSocket(int socket) {
139 void writeSocketData(int fd, Array<char> *data) {}
141 void writeSocketInt(int fd, int value) {}
143 int readSocketInt(int fd) {return 0;}
145 void readSocketData(int fd, Array<char> *data) {}
147 void writeURLData(int fd, Array<char> *data) {
150 void readURLData(int fd, Array<char> *output) {
153 int readURLInt(int fd) {
157 int getResponseCode(int fd) {
161 void CloudComm::setSalt() {
163 // Salt already sent to server so don't set it again
169 Array<char> *saltTmp = new Array<char>(CloudComm_SALT_SIZE);
170 random->nextBytes(saltTmp);
172 char *buffer = (char *) malloc(baseurl->length() + 100);
173 memcpy(buffer, baseurl->internalBytes(), baseurl->length());
174 int offset = baseurl->length();
175 offset += sprintf(&buffer[offset], "?req=setsalt");
176 IoTString *urlstr = new IoTString(buffer);
180 fd = openURL(urlstr, true);
181 writeURLData(fd, saltTmp);
183 int responsecode = getResponseCode(fd);
184 if (responsecode != HttpURLConnection_HTTP_OK) {
185 throw new Error("Invalid response");
190 } catch (Exception *e) {
192 throw new ServerException("Failed setting salt", ServerException_TypeConnectTimeout);
196 bool CloudComm::getSalt() {
198 IoTString *urlstr = NULL;
201 char *buffer = (char *) malloc(baseurl->length() + 100);
202 memcpy(buffer, baseurl->internalBytes(), baseurl->length());
203 int offset = baseurl->length();
204 offset += sprintf(&buffer[offset], "?req=getsalt");
205 urlstr = new IoTString(buffer);
207 } catch (Exception *e) {
208 throw new Error("getSlot failed");
212 fd = openURL(urlstr, true);
214 } catch (SocketTimeoutException *e) {
216 throw new ServerException("getSalt failed", ServerException_TypeConnectTimeout);
217 } catch (Exception *e) {
218 throw new Error("getSlot failed");
223 int responsecode = getResponseCode(fd);
224 if (responsecode != HttpURLConnection_HTTP_OK) {
225 throw new Error("Invalid response");
227 int salt_length = readURLInt(fd);
228 Array<char> *tmp = new Array<char>(salt_length);
229 readURLData(fd, tmp);
233 } catch (SocketTimeoutException *e) {
235 throw new ServerException("getSalt failed", ServerException_TypeInputTimeout);
236 } catch (Exception *e) {
237 throw new Error("getSlot failed");
241 Array<char> *CloudComm::createIV(int64_t machineId, int64_t localSequenceNumber) {
242 ByteBuffer *buffer = ByteBuffer_allocate(CloudComm_IV_SIZE);
243 buffer->putLong(machineId);
244 int64_t localSequenceNumberShifted = localSequenceNumber << 16;
245 buffer->putLong(localSequenceNumberShifted);
246 return buffer->array();
249 Array<char> *AESEncrypt(Array<char> *ivBytes, AESKey *key, Array<char> *data) {
250 Array<char> * output=new Array<char>(data->length());
251 aes_encrypt_ctr((BYTE *)data->internalArray(), data->length(), (BYTE *) output->internalArray(), (WORD *)key->getKey()->internalArray(), key->getKey()->length()/(sizeof(WORD)/sizeof(BYTE)), (BYTE *)ivBytes->internalArray());
255 Array<char> *AESDecrypt(Array<char> *ivBytes, AESKey *key, Array<char> *data) {
256 Array<char> * output=new Array<char>(data->length());
257 aes_decrypt_ctr((BYTE *)data->internalArray(), data->length(), (BYTE *)output->internalArray(), (WORD *)key->getKey()->internalArray(), key->getKey()->length()/(sizeof(WORD)/sizeof(BYTE)), (BYTE *)ivBytes->internalArray());
261 Array<char> *CloudComm::encryptSlotAndPrependIV(Array<char> *rawData, Array<char> *ivBytes) {
263 Array<char> *encryptedBytes = AESEncrypt(ivBytes, key, rawData);
264 Array<char> *chars = new Array<char>(encryptedBytes->length() + CloudComm_IV_SIZE);
265 System_arraycopy(ivBytes, 0, chars, 0, ivBytes->length());
266 System_arraycopy(encryptedBytes, 0, chars, CloudComm_IV_SIZE, encryptedBytes->length());
269 } catch (Exception *e) {
270 throw new Error("Failed To Encrypt");
274 Array<char> *CloudComm::stripIVAndDecryptSlot(Array<char> *rawData) {
276 Array<char> *ivBytes = new Array<char>(CloudComm_IV_SIZE);
277 Array<char> *encryptedBytes = new Array<char>(rawData->length() - CloudComm_IV_SIZE);
278 System_arraycopy(rawData, 0, ivBytes, 0, CloudComm_IV_SIZE);
279 System_arraycopy(rawData, CloudComm_IV_SIZE, encryptedBytes, 0, encryptedBytes->length());
280 return AESDecrypt(ivBytes, key, encryptedBytes);
281 } catch (Exception *e) {
282 throw new Error("Failed To Decrypt");
287 * API for putting a slot into the queue. Returns NULL on success.
288 * On failure, the server will send slots with newer sequence
291 Array<Slot *> *CloudComm::putSlot(Slot *slot, int max) {
296 throw new ServerException("putSlot failed", ServerException_TypeSalt);
301 int64_t sequencenumber = slot->getSequenceNumber();
302 Array<char> *slotBytes = slot->encode(mac);
303 Array<char> *chars = encryptSlotAndPrependIV(slotBytes, slot->getSlotCryptIV());
304 IoTString *url = buildRequest(true, sequencenumber, max);
306 fd = openURL(url, true);
307 writeURLData(fd, chars);
309 } catch (ServerException *e) {
312 } catch (SocketTimeoutException *e) {
314 throw new ServerException("putSlot failed", ServerException_TypeConnectTimeout);
315 } catch (Exception *e) {
316 throw new Error("putSlot failed");
321 Array<char> *resptype = new Array<char>(7);
322 readURLData(fd, resptype);
325 if (resptype->equals(getslot)) {
326 return processSlots(fd);
327 } else if (resptype->equals(putslot)) {
330 throw new Error("Bad response to putslot");
331 } catch (SocketTimeoutException *e) {
333 throw new ServerException("putSlot failed", ServerException_TypeInputTimeout);
334 } catch (Exception *e) {
335 throw new Error("putSlot failed");
340 * Request the server to send all slots with the given
341 * sequencenumber or newer->
343 Array<Slot *> *CloudComm::getSlots(int64_t sequencenumber) {
348 throw new ServerException("getSlots failed", ServerException_TypeSalt);
353 IoTString *url = buildRequest(false, sequencenumber, 0);
355 fd = openURL(url, true);
357 } catch (SocketTimeoutException *e) {
359 throw new ServerException("getSlots failed", ServerException_TypeConnectTimeout);
360 } catch (ServerException *e) {
364 } catch (Exception *e) {
365 throw new Error("getSlots failed");
370 Array<char> *resptype = new Array<char>(7);
371 readURLData(fd, resptype);
373 if (!resptype->equals(getslot))
374 throw new Error("Bad Response: ");
376 return processSlots(fd);
377 } catch (SocketTimeoutException *e) {
379 throw new ServerException("getSlots failed", ServerException_TypeInputTimeout);
380 } catch (Exception *e) {
381 throw new Error("getSlots failed");
386 * Method that actually handles building Slot objects from the
387 * server response. Shared by both putSlot and getSlots.
389 Array<Slot *> *CloudComm::processSlots(int fd) {
390 int numberofslots = readURLInt(fd);
391 Array<int> *sizesofslots = new Array<int>(numberofslots);
392 Array<Slot *> *slots = new Array<Slot *>(numberofslots);
394 for (int i = 0; i < numberofslots; i++)
395 sizesofslots->set(i, readURLInt(fd));
396 for (int i = 0; i < numberofslots; i++) {
397 Array<char> *rawData = new Array<char>(sizesofslots->get(i));
398 readURLData(fd, rawData);
399 Array<char> *data = stripIVAndDecryptSlot(rawData);
400 slots->set(i, Slot_decode(table, data, mac));
405 Array<char> *CloudComm::sendLocalData(Array<char> *sendData, int64_t localSequenceNumber, IoTString *host, int port) {
409 printf("Passing Locally\n");
410 mac->update(sendData, 0, sendData->length());
411 Array<char> *genmac = mac->doFinal();
412 Array<char> *totalData = new Array<char>(sendData->length() + genmac->length());
413 System_arraycopy(sendData, 0, totalData, 0, sendData->length());
414 System_arraycopy(genmac, 0, totalData, sendData->length(), genmac->length());
416 // Encrypt the data for sending
417 Array<char> *iv = createIV(table->getMachineId(), table->getLocalSequenceNumber());
418 Array<char> *encryptedData = encryptSlotAndPrependIV(totalData, iv);
420 // Open a TCP socket connection to a local device
421 int socket = createSocket(host, port);
424 // Send data to output (length of data, the data)
425 writeSocketInt(socket, encryptedData->length());
426 writeSocketData(socket, encryptedData);
428 int lengthOfReturnData = readSocketInt(socket);
429 Array<char> *returnData = new Array<char>(lengthOfReturnData);
430 readSocketData(socket, returnData);
432 returnData = stripIVAndDecryptSlot(returnData);
434 // We are done with this socket
436 mac->update(returnData, 0, returnData->length() - CloudComm_HMAC_SIZE);
437 Array<char> *realmac = mac->doFinal();
438 Array<char> *recmac = new Array<char>(CloudComm_HMAC_SIZE);
439 System_arraycopy(returnData, returnData->length() - realmac->length(), recmac, 0, realmac->length());
441 if (!recmac->equals(realmac))
442 throw new Error("Local Error: Invalid HMAC! Potential Attack!");
444 Array<char> *returnData2 = new Array<char>(lengthOfReturnData - recmac->length());
445 System_arraycopy(returnData, 0, returnData2, 0, returnData2->length());
448 } catch (Exception *e) {
449 printf("Exception\n");
455 void CloudComm::localServerWorkerFunction() {
456 int inputSocket = -1;
459 // Local server socket
460 inputSocket = createSocket(listeningPort);
461 } catch (Exception *e) {
462 throw new Error("Local server setup failure...");
467 // Accept incoming socket
468 int socket = acceptSocket(inputSocket);
470 // Get the encrypted data from the server
471 int dataSize = readSocketInt(socket);
472 Array<char> *readData = new Array<char>(dataSize);
473 readSocketData(socket, readData);
477 readData = stripIVAndDecryptSlot(readData);
478 mac->update(readData, 0, readData->length() - CloudComm_HMAC_SIZE);
479 Array<char> *genmac = mac->doFinal();
480 Array<char> *recmac = new Array<char>(CloudComm_HMAC_SIZE);
481 System_arraycopy(readData, readData->length() - recmac->length(), recmac, 0, recmac->length());
483 if (!recmac->equals(genmac))
484 throw new Error("Local Error: Invalid HMAC! Potential Attack!");
486 Array<char> *returnData = new Array<char>(readData->length() - recmac->length());
487 System_arraycopy(readData, 0, returnData, 0, returnData->length());
490 Array<char> *sendData = table->acceptDataFromLocal(returnData);
491 mac->update(sendData, 0, sendData->length());
492 Array<char> *realmac = mac->doFinal();
493 Array<char> *totalData = new Array<char>(sendData->length() + realmac->length());
494 System_arraycopy(sendData, 0, totalData, 0, sendData->length());
495 System_arraycopy(realmac, 0, totalData, sendData->length(), realmac->length());
497 // Encrypt the data for sending
498 Array<char> *iv = createIV(table->getMachineId(), table->getLocalSequenceNumber());
499 Array<char> *encryptedData = encryptSlotAndPrependIV(totalData, iv);
502 // Send data to output (length of data, the data)
503 writeSocketInt(socket, encryptedData->length());
504 writeSocketData(socket, encryptedData);
506 } catch (Exception *e) {
510 if (inputSocket != -1) {
513 } catch (Exception *e) {
514 throw new Error("Local server close failure...");
519 void CloudComm::closeCloud() {
522 if (localServerThread != NULL) {
523 if (pthread_join(localServerThread, NULL) != 0)
524 throw new Error("Local Server thread join issue...");