2 #include "TimingSingleton.h"
3 #include "SecureRandom.h"
10 * Empty Constructor needed for child class.
12 CloudComm::CloudComm() :
21 localServerThread(NULL),
23 timer(TimingSingleton_getInstance())
28 * Constructor for actual use. Takes in the url and password.
30 CloudComm::CloudComm(Table *_table, IoTString *_baseurl, IoTString *_password, int _listeningPort) :
35 random(new SecureRandom()),
38 listeningPort(_listeningPort),
39 localServerThread(NULL),
41 timer(TimingSingleton_getInstance()) {
42 if (listeningPort > 0) {
43 localServerThread = new Thread(new Runnable() {
45 localServerWorkerFunction();
48 localServerThread->start();
53 * Generates Key from password.
55 SecretKeySpec *CloudComm::initKey() {
57 PBEKeySpec *keyspec = new PBEKeySpec(password->internalBytes(),
61 SecretKey *tmpkey = SecretKeyFactory_getInstance("PBKDF2WithHmacSHA256")->generateSecret(keyspec);
62 return new SecretKeySpec(tmpkey->getEncoded(), "AES");
63 } catch (Exception *e) {
64 throw new Error("Failed generating key.");
69 * Inits all the security stuff
72 void CloudComm::initSecurity() {
73 // try to get the salt and if one does not exist set one
83 * Inits the HMAC generator.
85 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);
126 URL *url = new URL(baseurl + "?req=setsalt");
128 URLConnection *con = url->openConnection();
129 HttpURLConnection *http = (HttpURLConnection *) con;
131 http->setRequestMethod("POST");
132 http->setFixedLengthStreamingMode(saltTmp->length());
133 http->setDoOutput(true);
134 http->setConnectTimeout(CloudComm_TIMEOUT_MILLIS);
137 OutputStream *os = http->getOutputStream();
141 int responsecode = http->getResponseCode();
142 if (responsecode != HttpURLConnection_HTTP_OK) {
143 // TODO: Remove this print
144 printf("%d\n", responsecode);
145 throw new Error("Invalid response");
150 } catch (Exception *e) {
152 throw new ServerException("Failed setting salt", ServerException_TypeConnectTimeout);
156 bool CloudComm::getSalt() {
158 URLConnection *con = NULL;
159 HttpURLConnection *http = NULL;
162 url = new URL(baseurl + "?req=getsalt");
163 } catch (Exception *e) {
164 throw new Error("getSlot failed");
168 con = url->openConnection();
169 http = (HttpURLConnection *) con;
170 http->setRequestMethod("POST");
171 http->setConnectTimeout(CloudComm_TIMEOUT_MILLIS);
172 http->setReadTimeout(CloudComm_TIMEOUT_MILLIS);
175 } catch (SocketTimeoutException *e) {
177 throw new ServerException("getSalt failed", ServerException_TypeConnectTimeout);
178 } catch (Exception *e) {
179 throw new Error("getSlot failed");
184 int responsecode = http->getResponseCode();
185 if (responsecode != HttpURLConnection_HTTP_OK) {
186 throw new Error("Invalid response");
188 InputStream *is = http->getInputStream();
189 if (is->available() > 0) {
190 DataInputStream *dis = new DataInputStream(is);
191 int salt_length = dis->readInt();
192 Array<char> *tmp = new Array<char>(salt_length);
201 } catch (SocketTimeoutException *e) {
203 throw new ServerException("getSalt failed", ServerException_TypeInputTimeout);
204 } catch (Exception *e) {
205 throw new Error("getSlot failed");
209 Array<char> *CloudComm::createIV(int64_t machineId, int64_t localSequenceNumber) {
210 ByteBuffer *buffer = ByteBuffer_allocate(CloudComm_IV_SIZE);
211 buffer->putLong(machineId);
212 int64_t localSequenceNumberShifted = localSequenceNumber << 16;
213 buffer->putLong(localSequenceNumberShifted);
214 return buffer->array();
217 Array<char> *CloudComm::encryptSlotAndPrependIV(Array<char> *rawData, Array<char> *ivBytes) {
219 IvParameterSpec *ivSpec = new IvParameterSpec(ivBytes);
220 Cipher *cipher = Cipher_getInstance("AES/CTR/NoPadding");
221 cipher->init(Cipher_ENCRYPT_MODE, key, ivSpec);
223 Array<char> *encryptedBytes = cipher->doFinal(rawData);
225 Array<char> *chars = new Array<char>(encryptedBytes->length() + CloudComm_IV_SIZE);
226 System_arraycopy(ivBytes, 0, chars, 0, ivBytes->length());
227 System_arraycopy(encryptedBytes, 0, chars, CloudComm_IV_SIZE, encryptedBytes->length());
230 } catch (Exception *e) {
231 throw new Error("Failed To Encrypt");
235 Array<char> *CloudComm::stripIVAndDecryptSlot(Array<char> *rawData) {
237 Array<char> *ivBytes = new Array<char>(CloudComm_IV_SIZE);
238 Array<char> *encryptedBytes = new Array<char>(rawData->length() - CloudComm_IV_SIZE);
239 System_arraycopy(rawData, 0, ivBytes, 0, CloudComm_IV_SIZE);
240 System_arraycopy(rawData, CloudComm_IV_SIZE, encryptedBytes, 0, encryptedBytes->length);
242 IvParameterSpec *ivSpec = new IvParameterSpec(ivBytes);
243 Cipher *cipher = Cipher_getInstance("AES/CTR/NoPadding");
244 cipher->init(Cipher_DECRYPT_MODE, key, ivSpec);
245 return cipher->doFinal(encryptedBytes);
246 } catch (Exception *e) {
247 throw new Error("Failed To Decrypt");
252 * API for putting a slot into the queue. Returns NULL on success.
253 * On failure, the server will send slots with newer sequence
256 Array<Slot *> *CloudComm::putSlot(Slot *slot, int max) {
260 throw new ServerException("putSlot failed", ServerException_TypeSalt);
265 int64_t sequencenumber = slot->getSequenceNumber();
266 Array<char> *slotBytes = slot->encode(mac);
267 Array<char> *chars = encryptSlotAndPrependIV(slotBytes, slot->getSlotCryptIV());
268 URL *url = buildRequest(true, sequencenumber, max);
270 URLConnection *con = url->openConnection();
271 HttpURLConnection *http = (HttpURLConnection *) con;
272 http->setRequestMethod("POST");
273 http->setFixedLengthStreamingMode(chars->length);
274 http->setDoOutput(true);
275 http->setConnectTimeout(CloudComm_TIMEOUT_MILLIS);
276 http->setReadTimeout(CloudComm_TIMEOUT_MILLIS);
278 OutputStream *os = http->getOutputStream();
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 InputStream *is = http->getInputStream();
295 DataInputStream *dis = new DataInputStream(is);
296 Array<char> *resptype = new Array<char>(7);
297 dis->readFully(resptype);
300 if (Arrays->equals(resptype, "getslot"->getBytes())) {
301 return processSlots(dis);
302 } else if (Arrays->equals(resptype, "putslot"->getBytes())) {
305 throw new Error("Bad response to putslot");
306 } catch (SocketTimeoutException *e) {
308 throw new ServerException("putSlot failed", ServerException->TypeInputTimeout);
309 } catch (Exception *e) {
310 throw new Error("putSlot failed");
315 * Request the server to send all slots with the given
316 * sequencenumber or newer->
318 Array<Slot *> *CloudComm::getSlots(int64_t sequencenumber) {
322 throw new ServerException("getSlots failed", ServerException_TypeSalt);
327 URL *url = buildRequest(false, sequencenumber, 0);
329 URLConnection *con = url->openConnection();
330 HttpURLConnection *http = (HttpURLConnection *) con;
331 http->setRequestMethod("POST");
332 http->setConnectTimeout(CloudComm_TIMEOUT_MILLIS);
333 http->setReadTimeout(CloudComm_TIMEOUT_MILLIS);
336 } catch (SocketTimeoutException *e) {
338 throw new ServerException("getSlots failed", ServerException_TypeConnectTimeout);
339 } catch (ServerException *e) {
343 } catch (Exception *e) {
344 throw new Error("getSlots failed");
349 InputStream *is = http->getInputStream();
350 DataInputStream *dis = new DataInputStream(is);
351 Array<char> *resptype = new Array<char>(7);
352 dis->readFully(resptype);
354 if (!resptype->equals("getslot"->getBytes()))
355 throw new Error("Bad Response: " + new String(resptype));
357 return processSlots(dis);
358 } catch (SocketTimeoutException *e) {
360 throw new ServerException("getSlots failed", ServerException_TypeInputTimeout);
361 } catch (Exception *e) {
362 throw new Error("getSlots failed");
367 * Method that actually handles building Slot objects from the
368 * server response. Shared by both putSlot and getSlots.
370 Array<Slot *> *CloudComm::processSlots(DataInputStream *dis) {
371 int numberofslots = dis->readInt();
372 Array<int> *sizesofslots = new Array<int>(numberofslots);
373 Array<Slot *> *slots = new Array<Slot *>(numberofslots);
375 for (int i = 0; i < numberofslots; i++)
376 sizesofslots->set(i, dis->readInt());
377 for (int i = 0; i < numberofslots; i++) {
378 Array<char> *rawData = new Array<char>(sizesofslots->get(i));
379 dis->readFully(rawData);
380 Array<char> *data = stripIVAndDecryptSlot(rawData);
381 slots->set(i, Slot_decode(table, data, mac));
387 Array<char> *sendLocalData(Array<char> *sendData, int64_t localSequenceNumber, String host, int port) {
391 printf("Passing Locally\n");
392 mac->update(sendData);
393 Array<char> *genmac = mac->doFinal();
394 Array<char> *totalData = new Array<char>(sendData->length() + genmac->length());
395 System_arraycopy(sendData, 0, totalData, 0, sendData->length());
396 System_arraycopy(genmac, 0, totalData, sendData->length(), genmac->length());
398 // Encrypt the data for sending
399 Array<char> *iv = createIV(table->getMachineId(), table->getLocalSequenceNumber());
400 Array<char> *encryptedData = encryptSlotAndPrependIV(totalData, iv);
402 // Open a TCP socket connection to a local device
403 Socket *socket = new Socket(host, port);
404 socket->setReuseAddress(true);
405 DataOutputStream *output = new DataOutputStream(socket->getOutputStream());
406 DataInputStream *input = new DataInputStream(socket->getInputStream());
409 // Send data to output (length of data, the data)
410 output->writeInt(encryptedData->length);
411 output->write(encryptedData, 0, encryptedData->length);
414 int lengthOfReturnData = input->readInt();
415 Array<char> *returnData = new Array<char>(lengthOfReturnData);
416 input->readFully(returnData);
418 returnData = stripIVAndDecryptSlot(returnData);
420 // We are done with this socket
422 mac->update(returnData, 0, returnData->length - HMAC_SIZE);
423 Array<char> *realmac = mac->doFinal();
424 Array<char> *recmac = new Array<char>(HMAC_SIZE);
425 System_arraycopy(returnData, returnData->length - realmac->length, recmac, 0, realmac->length);
427 if (!recmac->equals(realmac))
428 throw new Error("Local Error: Invalid HMAC! Potential Attack!");
430 Array<char> *returnData2 = new Array<char>(lengthOfReturnData - recmac->length());
431 System_arraycopy(returnData, 0, returnData2, 0, returnData2->length);
434 } catch (Exception *e) {
435 printf("Exception\n");
441 void CloudComm::localServerWorkerFunction() {
442 ServerSocket *inputSocket = NULL;
445 // Local server socket
446 inputSocket = new ServerSocket(listeningPort);
447 inputSocket->setReuseAddress(true);
448 inputSocket->setSoTimeout(CloudComm_TIMEOUT_MILLIS);
449 } catch (Exception *e) {
450 throw new Error("Local server setup failure...");
455 // Accept incoming socket
456 Socket *socket = inputSocket->accept();
457 DataInputStream *input = new DataInputStream(socket->getInputStream());
458 DataOutputStream *output = new DataOutputStream(socket->getOutputStream());
460 // Get the encrypted data from the server
461 int dataSize = input->readInt();
462 Array<char> *readData = new Array<char>(dataSize);
463 input->readFully(readData);
467 readData = stripIVAndDecryptSlot(readData);
468 mac->update(readData, 0, readData->length - HMAC_SIZE);
469 Array<char> *genmac = mac->doFinal();
470 Array<char> *recmac = new Array<char>(HMAC_SIZE);
471 System_arraycopy(readData, readData->length() - recmac->length(), recmac, 0, recmac->length());
473 if (!recmac->equals(genmac))
474 throw new Error("Local Error: Invalid HMAC! Potential Attack!");
476 Array<char> *returnData = new Array<char>(readData->length() - recmac->length());
477 System_arraycopy(readData, 0, returnData, 0, returnData->length());
480 Array<char> *sendData = table->acceptDataFromLocal(returnData);
481 mac->update(sendData);
482 Array<char> *realmac = mac->doFinal();
483 Array<char> *totalData = new Array<char>(sendData->length() + realmac->length());
484 System_arraycopy(sendData, 0, totalData, 0, sendData->length());
485 System_arraycopy(realmac, 0, totalData, sendData->length(), realmac->length());
487 // Encrypt the data for sending
488 Array<char> *iv = createIV(table->getMachineId(), table->getLocalSequenceNumber());
489 Array<char> *encryptedData = encryptSlotAndPrependIV(totalData, iv);
492 // Send data to output (length of data, the data)
493 output->writeInt(encryptedData->length());
494 output->write(encryptedData, 0, encryptedData->length());
499 } catch (Exception *e) {
503 if (inputSocket != NULL) {
505 inputSocket->close();
506 } catch (Exception *e) {
507 throw new Error("Local server close failure...");
512 void CloudComm::close() {
515 if (localServerThread != NULL) {
517 localServerThread->join();
518 } catch (Exception *e) {
519 throw new Error("Local Server thread join issue...");