2 #include "TimingSingleton.h"
3 #include "SecureRandom.h"
10 #include "ByteBuffer.h"
13 * Empty Constructor needed for child class.
15 CloudComm::CloudComm() :
24 localServerThread(NULL),
26 timer(TimingSingleton_getInstance())
30 void * threadWrapper(void * cloud) {
31 CloudComm *c = (CloudComm *) cloud;
32 c->localServerWorkerFunction();
37 * Constructor for actual use. Takes in the url and password.
39 CloudComm::CloudComm(Table *_table, IoTString *_baseurl, IoTString *_password, int _listeningPort) :
44 random(new SecureRandom()),
47 listeningPort(_listeningPort),
48 localServerThread(NULL),
50 timer(TimingSingleton_getInstance()) {
51 if (listeningPort > 0) {
52 pthread_create(&localServerThread, NULL, threadWrapper, this);
57 * Generates Key from password.
59 AESKey *CloudComm::initKey() {
61 AESKey * key = new AESKey(password->internalBytes(),
66 } catch (Exception *e) {
67 throw new Error("Failed generating key.");
72 * Inits all the security stuff
75 void CloudComm::initSecurity() {
76 // try to get the salt and if one does not exist set one
86 * Inits the HMAC generator.
88 void CloudComm::initCrypt() {
89 if (password == NULL) {
94 password = NULL;// drop password
95 mac = Mac_getInstance("HmacSHA256");
97 } catch (Exception *e) {
98 throw new Error("Failed To Initialize Ciphers");
103 * Builds the URL for the given request.
105 IoTString *CloudComm::buildRequest(bool isput, int64_t sequencenumber, int64_t maxentries) {
106 const char *reqstring = isput ? "req=putslot" : "req=getslot";
107 char *buffer = (char *) malloc(baseurl->length() + 200);
108 memcpy(buffer, baseurl->internalBytes(), baseurl->length());
109 int offset = baseurl->length();
110 offset += sprintf(&buffer[offset], "?%s&seq=%" PRId64, reqstring, sequencenumber);
112 sprintf(&buffer[offset], "&max=%" PRId64, maxentries);
113 IoTString *urlstr = new IoTString(buffer);
117 int openURL(IoTString *url, bool isPost) {
121 void writeURLData(int fd, Array<char> *data) {
124 void readURLData(int fd, Array<char> * output) {
127 int readURLInt(int fd) {
131 int getResponseCode(int fd) {
135 void CloudComm::setSalt() {
137 // Salt already sent to server so don't set it again
143 Array<char> *saltTmp = new Array<char>(CloudComm_SALT_SIZE);
144 random->nextBytes(saltTmp);
146 char *buffer = (char *) malloc(baseurl->length() + 100);
147 memcpy(buffer, baseurl->internalBytes(), baseurl->length());
148 int offset = baseurl->length();
149 offset += sprintf(&buffer[offset], "?req=setsalt");
150 IoTString *urlstr = new IoTString(buffer);
154 fd = openURL(urlstr, true);
155 writeURLData(fd, saltTmp);
157 int responsecode = getResponseCode(fd);
158 if (responsecode != HttpURLConnection_HTTP_OK) {
159 throw new Error("Invalid response");
164 } catch (Exception *e) {
166 throw new ServerException("Failed setting salt", ServerException_TypeConnectTimeout);
170 bool CloudComm::getSalt() {
172 IoTString *urlstr = NULL;
175 char *buffer = (char *) malloc(baseurl->length() + 100);
176 memcpy(buffer, baseurl->internalBytes(), baseurl->length());
177 int offset = baseurl->length();
178 offset += sprintf(&buffer[offset], "?req=getsalt");
179 urlstr = new IoTString(buffer);
181 } catch (Exception *e) {
182 throw new Error("getSlot failed");
186 fd = openURL(urlstr, true);
188 } catch (SocketTimeoutException *e) {
190 throw new ServerException("getSalt failed", ServerException_TypeConnectTimeout);
191 } catch (Exception *e) {
192 throw new Error("getSlot failed");
197 int responsecode = getResponseCode(fd);
198 if (responsecode != HttpURLConnection_HTTP_OK) {
199 throw new Error("Invalid response");
201 if (is->available() > 0) {
202 int salt_length = readURLInt(fd);
203 Array<char> *tmp = new Array<char>(salt_length);
204 readURLData(fd, tmp);
212 } catch (SocketTimeoutException *e) {
214 throw new ServerException("getSalt failed", ServerException_TypeInputTimeout);
215 } catch (Exception *e) {
216 throw new Error("getSlot failed");
220 Array<char> *CloudComm::createIV(int64_t machineId, int64_t localSequenceNumber) {
221 ByteBuffer *buffer = ByteBuffer_allocate(CloudComm_IV_SIZE);
222 buffer->putLong(machineId);
223 int64_t localSequenceNumberShifted = localSequenceNumber << 16;
224 buffer->putLong(localSequenceNumberShifted);
225 return buffer->array();
228 Array<char> *CloudComm::encryptSlotAndPrependIV(Array<char> *rawData, Array<char> *ivBytes) {
230 IvParameterSpec *ivSpec = new IvParameterSpec(ivBytes);
231 Cipher *cipher = Cipher_getInstance("AES/CTR/NoPadding");
232 cipher->init(Cipher_ENCRYPT_MODE, key, ivSpec);
233 Array<char> *encryptedBytes = cipher->doFinal(rawData);
234 Array<char> *chars = new Array<char>(encryptedBytes->length() + CloudComm_IV_SIZE);
235 System_arraycopy(ivBytes, 0, chars, 0, ivBytes->length());
236 System_arraycopy(encryptedBytes, 0, chars, CloudComm_IV_SIZE, encryptedBytes->length());
239 } catch (Exception *e) {
240 throw new Error("Failed To Encrypt");
244 Array<char> *CloudComm::stripIVAndDecryptSlot(Array<char> *rawData) {
246 Array<char> *ivBytes = new Array<char>(CloudComm_IV_SIZE);
247 Array<char> *encryptedBytes = new Array<char>(rawData->length() - CloudComm_IV_SIZE);
248 System_arraycopy(rawData, 0, ivBytes, 0, CloudComm_IV_SIZE);
249 System_arraycopy(rawData, CloudComm_IV_SIZE, encryptedBytes, 0, encryptedBytes->length());
250 IvParameterSpec *ivSpec = new IvParameterSpec(ivBytes);
251 Cipher *cipher = Cipher_getInstance("AES/CTR/NoPadding");
252 cipher->init(Cipher_DECRYPT_MODE, key, ivSpec);
253 return cipher->doFinal(encryptedBytes);
254 } catch (Exception *e) {
255 throw new Error("Failed To Decrypt");
260 * API for putting a slot into the queue. Returns NULL on success.
261 * On failure, the server will send slots with newer sequence
264 Array<Slot *> *CloudComm::putSlot(Slot *slot, int max) {
269 throw new ServerException("putSlot failed", ServerException_TypeSalt);
274 int64_t sequencenumber = slot->getSequenceNumber();
275 Array<char> *slotBytes = slot->encode(mac);
276 Array<char> *chars = encryptSlotAndPrependIV(slotBytes, slot->getSlotCryptIV());
277 IoTString *url = buildRequest(true, sequencenumber, max);
279 fd = openURL(url, true);
280 writeURLData(fd, chars);
282 } catch (ServerException *e) {
285 } catch (SocketTimeoutException *e) {
287 throw new ServerException("putSlot failed", ServerException_TypeConnectTimeout);
288 } catch (Exception *e) {
289 throw new Error("putSlot failed");
294 Array<char> *resptype = new Array<char>(7);
295 readURLData(fd, resptype);
298 if (resptype->equals("getslot"->getBytes())) {
299 return processSlots(fd);
300 } else if (resptype->equals("putslot"->getBytes())) {
303 throw new Error("Bad response to putslot");
304 } catch (SocketTimeoutException *e) {
306 throw new ServerException("putSlot failed", ServerException->TypeInputTimeout);
307 } catch (Exception *e) {
308 throw new Error("putSlot failed");
313 * Request the server to send all slots with the given
314 * sequencenumber or newer->
316 Array<Slot *> *CloudComm::getSlots(int64_t sequencenumber) {
320 throw new ServerException("getSlots failed", ServerException_TypeSalt);
325 IoTString *url = buildRequest(false, sequencenumber, 0);
327 URLConnection *con = url->openConnection();
328 HttpURLConnection *http = (HttpURLConnection *) con;
329 http->setRequestMethod("POST");
330 http->setConnectTimeout(CloudComm_TIMEOUT_MILLIS);
331 http->setReadTimeout(CloudComm_TIMEOUT_MILLIS);
334 } catch (SocketTimeoutException *e) {
336 throw new ServerException("getSlots failed", ServerException_TypeConnectTimeout);
337 } catch (ServerException *e) {
341 } catch (Exception *e) {
342 throw new Error("getSlots failed");
347 Array<char> *resptype = new Array<char>(7);
348 readURLData(fd, resptype);
350 if (!resptype->equals("getslot"->getBytes()))
351 throw new Error("Bad Response: " + new String(resptype));
353 return processSlots(dis);
354 } catch (SocketTimeoutException *e) {
356 throw new ServerException("getSlots failed", ServerException_TypeInputTimeout);
357 } catch (Exception *e) {
358 throw new Error("getSlots failed");
363 * Method that actually handles building Slot objects from the
364 * server response. Shared by both putSlot and getSlots.
366 Array<Slot *> *CloudComm::processSlots(int fd) {
367 int numberofslots = readURLInt(fd);
368 Array<int> *sizesofslots = new Array<int>(numberofslots);
369 Array<Slot *> *slots = new Array<Slot *>(numberofslots);
371 for (int i = 0; i < numberofslots; i++)
372 sizesofslots->set(i, readURLInt(fd));
373 for (int i = 0; i < numberofslots; i++) {
374 Array<char> *rawData = new Array<char>(sizesofslots->get(i));
375 readURLData(rawData);
376 Array<char> *data = stripIVAndDecryptSlot(rawData);
377 slots->set(i, Slot_decode(table, data, mac));
382 Array<char> *CloudComm::sendLocalData(Array<char> *sendData, int64_t localSequenceNumber, String host, int port) {
386 printf("Passing Locally\n");
387 mac->update(sendData, 0, sendData->length());
388 Array<char> *genmac = mac->doFinal();
389 Array<char> *totalData = new Array<char>(sendData->length() + genmac->length());
390 System_arraycopy(sendData, 0, totalData, 0, sendData->length());
391 System_arraycopy(genmac, 0, totalData, sendData->length(), genmac->length());
393 // Encrypt the data for sending
394 Array<char> *iv = createIV(table->getMachineId(), table->getLocalSequenceNumber());
395 Array<char> *encryptedData = encryptSlotAndPrependIV(totalData, iv);
397 // Open a TCP socket connection to a local device
398 Socket *socket = new Socket(host, port);
399 socket->setReuseAddress(true);
400 DataOutputStream *output = new DataOutputStream(socket->getOutputStream());
401 DataInputStream *input = new DataInputStream(socket->getInputStream());
404 // Send data to output (length of data, the data)
405 output->writeInt(encryptedData->length());
406 output->write(encryptedData, 0, encryptedData->length());
409 int lengthOfReturnData = input->readInt();
410 Array<char> *returnData = new Array<char>(lengthOfReturnData);
411 input->readFully(returnData);
413 returnData = stripIVAndDecryptSlot(returnData);
415 // We are done with this socket
417 mac->update(returnData, 0, returnData->length() - CloudComm_HMAC_SIZE);
418 Array<char> *realmac = mac->doFinal();
419 Array<char> *recmac = new Array<char>(CloudComm_HMAC_SIZE);
420 System_arraycopy(returnData, returnData->length() - realmac->length(), recmac, 0, realmac->length());
422 if (!recmac->equals(realmac))
423 throw new Error("Local Error: Invalid HMAC! Potential Attack!");
425 Array<char> *returnData2 = new Array<char>(lengthOfReturnData - recmac->length());
426 System_arraycopy(returnData, 0, returnData2, 0, returnData2->length());
429 } catch (Exception *e) {
430 printf("Exception\n");
436 void CloudComm::localServerWorkerFunction() {
437 ServerSocket *inputSocket = NULL;
440 // Local server socket
441 inputSocket = new ServerSocket(listeningPort);
442 inputSocket->setReuseAddress(true);
443 inputSocket->setSoTimeout(CloudComm_TIMEOUT_MILLIS);
444 } catch (Exception *e) {
445 throw new Error("Local server setup failure...");
450 // Accept incoming socket
451 Socket *socket = inputSocket->accept();
452 DataInputStream *input = new DataInputStream(socket->getInputStream());
453 DataOutputStream *output = new DataOutputStream(socket->getOutputStream());
455 // Get the encrypted data from the server
456 int dataSize = input->readInt();
457 Array<char> *readData = new Array<char>(dataSize);
458 input->readFully(readData);
462 readData = stripIVAndDecryptSlot(readData);
463 mac->update(readData, 0, readData->length() - CloudComm_HMAC_SIZE);
464 Array<char> *genmac = mac->doFinal();
465 Array<char> *recmac = new Array<char>(CloudComm_HMAC_SIZE);
466 System_arraycopy(readData, readData->length() - recmac->length(), recmac, 0, recmac->length());
468 if (!recmac->equals(genmac))
469 throw new Error("Local Error: Invalid HMAC! Potential Attack!");
471 Array<char> *returnData = new Array<char>(readData->length() - recmac->length());
472 System_arraycopy(readData, 0, returnData, 0, returnData->length());
475 Array<char> *sendData = table->acceptDataFromLocal(returnData);
476 mac->update(sendData, 0, sendData->length());
477 Array<char> *realmac = mac->doFinal();
478 Array<char> *totalData = new Array<char>(sendData->length() + realmac->length());
479 System_arraycopy(sendData, 0, totalData, 0, sendData->length());
480 System_arraycopy(realmac, 0, totalData, sendData->length(), realmac->length());
482 // Encrypt the data for sending
483 Array<char> *iv = createIV(table->getMachineId(), table->getLocalSequenceNumber());
484 Array<char> *encryptedData = encryptSlotAndPrependIV(totalData, iv);
487 // Send data to output (length of data, the data)
488 output->writeInt(encryptedData->length());
489 output->write(encryptedData, 0, encryptedData->length());
494 } catch (Exception *e) {
498 if (inputSocket != NULL) {
500 inputSocket->close();
501 } catch (Exception *e) {
502 throw new Error("Local server close failure...");
507 void CloudComm::close() {
510 if (localServerThread != NULL) {
511 if (pthread_join(localServerThread, NULL) != 0)
512 throw new Error("Local Server thread join issue...");