2 #include "TimingSingleton.h"
3 #include "SecureRandom.h"
11 #include "ByteBuffer.h"
12 #include <sys/socket.h>
16 * Empty Constructor needed for child class.
18 CloudComm::CloudComm() :
27 localServerThread(NULL),
29 timer(TimingSingleton_getInstance()),
30 getslot(new Array<char>("getslot", 7)),
31 putslot(new Array<char>("putslot", 7))
35 void *threadWrapper(void *cloud) {
36 CloudComm *c = (CloudComm *) cloud;
37 c->localServerWorkerFunction();
42 * Constructor for actual use. Takes in the url and password.
44 CloudComm::CloudComm(Table *_table, IoTString *_baseurl, IoTString *_password, int _listeningPort) :
49 random(new SecureRandom()),
52 listeningPort(_listeningPort),
53 localServerThread(NULL),
55 timer(TimingSingleton_getInstance()) {
56 if (listeningPort > 0) {
57 pthread_create(&localServerThread, NULL, threadWrapper, this);
62 * Generates Key from password.
64 AESKey *CloudComm::initKey() {
66 AESKey *key = new AESKey(password->internalBytes(),
71 } catch (Exception *e) {
72 throw new Error("Failed generating key.");
77 * Inits all the security stuff
80 void CloudComm::initSecurity() {
81 // try to get the salt and if one does not exist set one
91 * Inits the HMAC generator.
93 void CloudComm::initCrypt() {
94 if (password == NULL) {
99 password = NULL;// drop password
100 mac = Mac_getInstance("HmacSHA256");
102 } catch (Exception *e) {
103 throw new Error("Failed To Initialize Ciphers");
108 * Builds the URL for the given request.
110 IoTString *CloudComm::buildRequest(bool isput, int64_t sequencenumber, int64_t maxentries) {
111 const char *reqstring = isput ? "req=putslot" : "req=getslot";
112 char *buffer = (char *) malloc(baseurl->length() + 200);
113 memcpy(buffer, baseurl->internalBytes(), baseurl->length());
114 int offset = baseurl->length();
115 offset += sprintf(&buffer[offset], "?%s&seq=%" PRId64, reqstring, sequencenumber);
117 sprintf(&buffer[offset], "&max=%" PRId64, maxentries);
118 IoTString *urlstr = new IoTString(buffer);
122 int openURL(IoTString *url, bool isPost) {
126 int createSocket(IoTString *host, int port) {
130 int createSocket(int port) {
134 int acceptSocket(int socket) {
138 void writeSocketData(int fd, Array<char> *data) {}
140 void writeSocketInt(int fd, int value) {}
142 int readSocketInt(int fd) {return 0;}
144 void readSocketData(int fd, Array<char> *data) {}
146 void writeURLData(int fd, Array<char> *data) {
149 void readURLData(int fd, Array<char> *output) {
152 int readURLInt(int fd) {
156 int getResponseCode(int fd) {
160 void CloudComm::setSalt() {
162 // Salt already sent to server so don't set it again
168 Array<char> *saltTmp = new Array<char>(CloudComm_SALT_SIZE);
169 random->nextBytes(saltTmp);
171 char *buffer = (char *) malloc(baseurl->length() + 100);
172 memcpy(buffer, baseurl->internalBytes(), baseurl->length());
173 int offset = baseurl->length();
174 offset += sprintf(&buffer[offset], "?req=setsalt");
175 IoTString *urlstr = new IoTString(buffer);
179 fd = openURL(urlstr, true);
180 writeURLData(fd, saltTmp);
182 int responsecode = getResponseCode(fd);
183 if (responsecode != HttpURLConnection_HTTP_OK) {
184 throw new Error("Invalid response");
189 } catch (Exception *e) {
191 throw new ServerException("Failed setting salt", ServerException_TypeConnectTimeout);
195 bool CloudComm::getSalt() {
197 IoTString *urlstr = NULL;
200 char *buffer = (char *) malloc(baseurl->length() + 100);
201 memcpy(buffer, baseurl->internalBytes(), baseurl->length());
202 int offset = baseurl->length();
203 offset += sprintf(&buffer[offset], "?req=getsalt");
204 urlstr = new IoTString(buffer);
206 } catch (Exception *e) {
207 throw new Error("getSlot failed");
211 fd = openURL(urlstr, true);
213 } catch (SocketTimeoutException *e) {
215 throw new ServerException("getSalt failed", ServerException_TypeConnectTimeout);
216 } catch (Exception *e) {
217 throw new Error("getSlot failed");
222 int responsecode = getResponseCode(fd);
223 if (responsecode != HttpURLConnection_HTTP_OK) {
224 throw new Error("Invalid response");
226 int salt_length = readURLInt(fd);
227 Array<char> *tmp = new Array<char>(salt_length);
228 readURLData(fd, tmp);
232 } catch (SocketTimeoutException *e) {
234 throw new ServerException("getSalt failed", ServerException_TypeInputTimeout);
235 } catch (Exception *e) {
236 throw new Error("getSlot failed");
240 Array<char> *CloudComm::createIV(int64_t machineId, int64_t localSequenceNumber) {
241 ByteBuffer *buffer = ByteBuffer_allocate(CloudComm_IV_SIZE);
242 buffer->putLong(machineId);
243 int64_t localSequenceNumberShifted = localSequenceNumber << 16;
244 buffer->putLong(localSequenceNumberShifted);
245 return buffer->array();
248 Array<char> *AESEncrypt(Array<char> *ivBytes, AESKey *key, Array<char> *data) {
252 Array<char> *AESDecrypt(Array<char> *ivBytes, AESKey *key, Array<char> *data) {
256 Array<char> *CloudComm::encryptSlotAndPrependIV(Array<char> *rawData, Array<char> *ivBytes) {
258 Array<char> *encryptedBytes = AESEncrypt(ivBytes, key, rawData);
259 Array<char> *chars = new Array<char>(encryptedBytes->length() + CloudComm_IV_SIZE);
260 System_arraycopy(ivBytes, 0, chars, 0, ivBytes->length());
261 System_arraycopy(encryptedBytes, 0, chars, CloudComm_IV_SIZE, encryptedBytes->length());
264 } catch (Exception *e) {
265 throw new Error("Failed To Encrypt");
269 Array<char> *CloudComm::stripIVAndDecryptSlot(Array<char> *rawData) {
271 Array<char> *ivBytes = new Array<char>(CloudComm_IV_SIZE);
272 Array<char> *encryptedBytes = new Array<char>(rawData->length() - CloudComm_IV_SIZE);
273 System_arraycopy(rawData, 0, ivBytes, 0, CloudComm_IV_SIZE);
274 System_arraycopy(rawData, CloudComm_IV_SIZE, encryptedBytes, 0, encryptedBytes->length());
275 return AESDecrypt(ivBytes, key, encryptedBytes);
276 } catch (Exception *e) {
277 throw new Error("Failed To Decrypt");
282 * API for putting a slot into the queue. Returns NULL on success.
283 * On failure, the server will send slots with newer sequence
286 Array<Slot *> *CloudComm::putSlot(Slot *slot, int max) {
291 throw new ServerException("putSlot failed", ServerException_TypeSalt);
296 int64_t sequencenumber = slot->getSequenceNumber();
297 Array<char> *slotBytes = slot->encode(mac);
298 Array<char> *chars = encryptSlotAndPrependIV(slotBytes, slot->getSlotCryptIV());
299 IoTString *url = buildRequest(true, sequencenumber, max);
301 fd = openURL(url, true);
302 writeURLData(fd, chars);
304 } catch (ServerException *e) {
307 } catch (SocketTimeoutException *e) {
309 throw new ServerException("putSlot failed", ServerException_TypeConnectTimeout);
310 } catch (Exception *e) {
311 throw new Error("putSlot failed");
316 Array<char> *resptype = new Array<char>(7);
317 readURLData(fd, resptype);
320 if (resptype->equals(getslot)) {
321 return processSlots(fd);
322 } else if (resptype->equals(putslot)) {
325 throw new Error("Bad response to putslot");
326 } catch (SocketTimeoutException *e) {
328 throw new ServerException("putSlot failed", ServerException_TypeInputTimeout);
329 } catch (Exception *e) {
330 throw new Error("putSlot failed");
335 * Request the server to send all slots with the given
336 * sequencenumber or newer->
338 Array<Slot *> *CloudComm::getSlots(int64_t sequencenumber) {
343 throw new ServerException("getSlots failed", ServerException_TypeSalt);
348 IoTString *url = buildRequest(false, sequencenumber, 0);
350 fd = openURL(url, true);
352 } catch (SocketTimeoutException *e) {
354 throw new ServerException("getSlots failed", ServerException_TypeConnectTimeout);
355 } catch (ServerException *e) {
359 } catch (Exception *e) {
360 throw new Error("getSlots failed");
365 Array<char> *resptype = new Array<char>(7);
366 readURLData(fd, resptype);
368 if (!resptype->equals(getslot))
369 throw new Error("Bad Response: ");
371 return processSlots(fd);
372 } catch (SocketTimeoutException *e) {
374 throw new ServerException("getSlots failed", ServerException_TypeInputTimeout);
375 } catch (Exception *e) {
376 throw new Error("getSlots failed");
381 * Method that actually handles building Slot objects from the
382 * server response. Shared by both putSlot and getSlots.
384 Array<Slot *> *CloudComm::processSlots(int fd) {
385 int numberofslots = readURLInt(fd);
386 Array<int> *sizesofslots = new Array<int>(numberofslots);
387 Array<Slot *> *slots = new Array<Slot *>(numberofslots);
389 for (int i = 0; i < numberofslots; i++)
390 sizesofslots->set(i, readURLInt(fd));
391 for (int i = 0; i < numberofslots; i++) {
392 Array<char> *rawData = new Array<char>(sizesofslots->get(i));
393 readURLData(fd, rawData);
394 Array<char> *data = stripIVAndDecryptSlot(rawData);
395 slots->set(i, Slot_decode(table, data, mac));
400 Array<char> *CloudComm::sendLocalData(Array<char> *sendData, int64_t localSequenceNumber, IoTString *host, int port) {
404 printf("Passing Locally\n");
405 mac->update(sendData, 0, sendData->length());
406 Array<char> *genmac = mac->doFinal();
407 Array<char> *totalData = new Array<char>(sendData->length() + genmac->length());
408 System_arraycopy(sendData, 0, totalData, 0, sendData->length());
409 System_arraycopy(genmac, 0, totalData, sendData->length(), genmac->length());
411 // Encrypt the data for sending
412 Array<char> *iv = createIV(table->getMachineId(), table->getLocalSequenceNumber());
413 Array<char> *encryptedData = encryptSlotAndPrependIV(totalData, iv);
415 // Open a TCP socket connection to a local device
416 int socket = createSocket(host, port);
419 // Send data to output (length of data, the data)
420 writeSocketInt(socket, encryptedData->length());
421 writeSocketData(socket, encryptedData);
423 int lengthOfReturnData = readSocketInt(socket);
424 Array<char> *returnData = new Array<char>(lengthOfReturnData);
425 readSocketData(socket, returnData);
427 returnData = stripIVAndDecryptSlot(returnData);
429 // We are done with this socket
431 mac->update(returnData, 0, returnData->length() - CloudComm_HMAC_SIZE);
432 Array<char> *realmac = mac->doFinal();
433 Array<char> *recmac = new Array<char>(CloudComm_HMAC_SIZE);
434 System_arraycopy(returnData, returnData->length() - realmac->length(), recmac, 0, realmac->length());
436 if (!recmac->equals(realmac))
437 throw new Error("Local Error: Invalid HMAC! Potential Attack!");
439 Array<char> *returnData2 = new Array<char>(lengthOfReturnData - recmac->length());
440 System_arraycopy(returnData, 0, returnData2, 0, returnData2->length());
443 } catch (Exception *e) {
444 printf("Exception\n");
450 void CloudComm::localServerWorkerFunction() {
451 int inputSocket = -1;
454 // Local server socket
455 inputSocket = createSocket(listeningPort);
456 } catch (Exception *e) {
457 throw new Error("Local server setup failure...");
462 // Accept incoming socket
463 int socket = acceptSocket(inputSocket);
465 // Get the encrypted data from the server
466 int dataSize = readSocketInt(socket);
467 Array<char> *readData = new Array<char>(dataSize);
468 readSocketData(socket, readData);
472 readData = stripIVAndDecryptSlot(readData);
473 mac->update(readData, 0, readData->length() - CloudComm_HMAC_SIZE);
474 Array<char> *genmac = mac->doFinal();
475 Array<char> *recmac = new Array<char>(CloudComm_HMAC_SIZE);
476 System_arraycopy(readData, readData->length() - recmac->length(), recmac, 0, recmac->length());
478 if (!recmac->equals(genmac))
479 throw new Error("Local Error: Invalid HMAC! Potential Attack!");
481 Array<char> *returnData = new Array<char>(readData->length() - recmac->length());
482 System_arraycopy(readData, 0, returnData, 0, returnData->length());
485 Array<char> *sendData = table->acceptDataFromLocal(returnData);
486 mac->update(sendData, 0, sendData->length());
487 Array<char> *realmac = mac->doFinal();
488 Array<char> *totalData = new Array<char>(sendData->length() + realmac->length());
489 System_arraycopy(sendData, 0, totalData, 0, sendData->length());
490 System_arraycopy(realmac, 0, totalData, sendData->length(), realmac->length());
492 // Encrypt the data for sending
493 Array<char> *iv = createIV(table->getMachineId(), table->getLocalSequenceNumber());
494 Array<char> *encryptedData = encryptSlotAndPrependIV(totalData, iv);
497 // Send data to output (length of data, the data)
498 writeSocketInt(socket, encryptedData->length());
499 writeSocketData(socket, encryptedData);
501 } catch (Exception *e) {
505 if (inputSocket != -1) {
508 } catch (Exception *e) {
509 throw new Error("Local server close failure...");
514 void CloudComm::closeCloud() {
517 if (localServerThread != NULL) {
518 if (pthread_join(localServerThread, NULL) != 0)
519 throw new Error("Local Server thread join issue...");