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() {
85 if (password == NULL) {
91 password = NULL;// drop password
92 mac = Mac_getInstance("HmacSHA256");
94 } catch (Exception *e) {
95 throw new Error("Failed To Initialize Ciphers");
100 * Builds the URL for the given request.
102 URL *CloudComm::buildRequest(bool isput, int64_t sequencenumber, int64_t maxentries) {
103 IoTString *reqstring = isput ? "req=putslot" : "req=getslot";
104 IoTString *urlstr = baseurl + "?" + reqstring + "&seq=" + sequencenumber;
106 urlstr += "&max=" + maxentries;
107 return new URL(urlstr);
110 void CloudComm::setSalt() {
113 // Salt already sent to server so dont set it again
118 Array<char> *saltTmp = new Array<char>(CloudComm_SALT_SIZE);
119 random->nextBytes(saltTmp);
121 for (int i = 0; i < CloudComm_SALT_SIZE; i++) {
122 printf("%d\n", (int)saltTmp->get(i) & 255);
125 URL *url = new URL(baseurl + "?req=setsalt");
127 URLConnection *con = url->openConnection();
128 HttpURLConnection *http = (HttpURLConnection *) con;
130 http->setRequestMethod("POST");
131 http->setFixedLengthStreamingMode(saltTmp->length());
132 http->setDoOutput(true);
133 http->setConnectTimeout(CloudComm_TIMEOUT_MILLIS);
136 OutputStream *os = http->getOutputStream();
140 int responsecode = http->getResponseCode();
141 if (responsecode != HttpURLConnection_HTTP_OK) {
142 // TODO: Remove this print
143 printf("%d\n", responsecode);
144 throw new Error("Invalid response");
149 } catch (Exception *e) {
151 throw new ServerException("Failed setting salt", ServerException_TypeConnectTimeout);
155 bool CloudComm::getSalt() {
157 URLConnection *con = NULL;
158 HttpURLConnection *http = NULL;
161 url = new URL(baseurl + "?req=getsalt");
162 } catch (Exception *e) {
163 throw new Error("getSlot failed");
167 con = url->openConnection();
168 http = (HttpURLConnection *) con;
169 http->setRequestMethod("POST");
170 http->setConnectTimeout(CloudComm_TIMEOUT_MILLIS);
171 http->setReadTimeout(CloudComm_TIMEOUT_MILLIS);
174 } catch (SocketTimeoutException *e) {
176 throw new ServerException("getSalt failed", ServerException_TypeConnectTimeout);
177 } catch (Exception *e) {
178 throw new Error("getSlot failed");
183 int responsecode = http->getResponseCode();
184 if (responsecode != HttpURLConnection_HTTP_OK) {
185 throw new Error("Invalid response");
187 InputStream *is = http->getInputStream();
188 if (is->available() > 0) {
189 DataInputStream *dis = new DataInputStream(is);
190 int salt_length = dis->readInt();
191 Array<char> *tmp = new Array<char>(salt_length);
200 } catch (SocketTimeoutException *e) {
202 throw new ServerException("getSalt failed", ServerException_TypeInputTimeout);
203 } catch (Exception *e) {
204 throw new Error("getSlot failed");
208 Array<char> *CloudComm::createIV(int64_t machineId, int64_t localSequenceNumber) {
209 ByteBuffer *buffer = ByteBuffer_allocate(CloudComm_IV_SIZE);
210 buffer->putLong(machineId);
211 int64_t localSequenceNumberShifted = localSequenceNumber << 16;
212 buffer->putLong(localSequenceNumberShifted);
213 return buffer->array();
216 Array<char> *CloudComm::encryptSlotAndPrependIV(Array<char> *rawData, Array<char> *ivBytes) {
218 IvParameterSpec *ivSpec = new IvParameterSpec(ivBytes);
219 Cipher *cipher = Cipher_getInstance("AES/CTR/NoPadding");
220 cipher->init(Cipher_ENCRYPT_MODE, key, ivSpec);
222 Array<char> *encryptedBytes = cipher->doFinal(rawData);
224 Array<char> *chars = new Array<char>(encryptedBytes->length() + CloudComm_IV_SIZE);
225 System_arraycopy(ivBytes, 0, chars, 0, ivBytes->length());
226 System_arraycopy(encryptedBytes, 0, chars, CloudComm_IV_SIZE, encryptedBytes->length());
229 } catch (Exception *e) {
230 throw new Error("Failed To Encrypt");
234 Array<char> *CloudComm::stripIVAndDecryptSlot(Array<char> *rawData) {
236 Array<char> *ivBytes = new Array<char>(CloudComm_IV_SIZE);
237 Array<char> *encryptedBytes = new Array<char>(rawData->length() - CloudComm_IV_SIZE);
238 System_arraycopy(rawData, 0, ivBytes, 0, CloudComm_IV_SIZE);
239 System_arraycopy(rawData, CloudComm_IV_SIZE, encryptedBytes, 0, encryptedBytes->length);
241 IvParameterSpec *ivSpec = new IvParameterSpec(ivBytes);
242 Cipher *cipher = Cipher_getInstance("AES/CTR/NoPadding");
243 cipher->init(Cipher_DECRYPT_MODE, key, ivSpec);
244 return cipher->doFinal(encryptedBytes);
245 } catch (Exception *e) {
246 throw new Error("Failed To Decrypt");
251 * API for putting a slot into the queue. Returns NULL on success.
252 * On failure, the server will send slots with newer sequence
255 Array<Slot *> *CloudComm::putSlot(Slot *slot, int max) {
259 throw new ServerException("putSlot failed", ServerException_TypeSalt);
264 int64_t sequencenumber = slot->getSequenceNumber();
265 Array<char> *slotBytes = slot->encode(mac);
266 Array<char> *chars = encryptSlotAndPrependIV(slotBytes, slot->getSlotCryptIV());
267 URL *url = buildRequest(true, sequencenumber, max);
269 URLConnection *con = url->openConnection();
270 HttpURLConnection *http = (HttpURLConnection *) con;
271 http->setRequestMethod("POST");
272 http->setFixedLengthStreamingMode(chars->length);
273 http->setDoOutput(true);
274 http->setConnectTimeout(CloudComm_TIMEOUT_MILLIS);
275 http->setReadTimeout(CloudComm_TIMEOUT_MILLIS);
277 OutputStream *os = http->getOutputStream();
281 } catch (ServerException *e) {
284 } catch (SocketTimeoutException *e) {
286 throw new ServerException("putSlot failed", ServerException_TypeConnectTimeout);
287 } catch (Exception *e) {
288 throw new Error("putSlot failed");
293 InputStream *is = http->getInputStream();
294 DataInputStream *dis = new DataInputStream(is);
295 Array<char> *resptype = new Array<char>(7);
296 dis->readFully(resptype);
299 if (Arrays->equals(resptype, "getslot"->getBytes())) {
300 return processSlots(dis);
301 } else if (Arrays->equals(resptype, "putslot"->getBytes())) {
304 throw new Error("Bad response to putslot");
305 } catch (SocketTimeoutException *e) {
307 throw new ServerException("putSlot failed", ServerException->TypeInputTimeout);
308 } catch (Exception *e) {
309 throw new Error("putSlot failed");
314 * Request the server to send all slots with the given
315 * sequencenumber or newer->
317 Array<Slot *> *CloudComm::getSlots(int64_t sequencenumber) {
321 throw new ServerException("getSlots failed", ServerException_TypeSalt);
326 URL *url = buildRequest(false, sequencenumber, 0);
328 URLConnection *con = url->openConnection();
329 HttpURLConnection *http = (HttpURLConnection *) con;
330 http->setRequestMethod("POST");
331 http->setConnectTimeout(CloudComm_TIMEOUT_MILLIS);
332 http->setReadTimeout(CloudComm_TIMEOUT_MILLIS);
335 } catch (SocketTimeoutException *e) {
337 throw new ServerException("getSlots failed", ServerException_TypeConnectTimeout);
338 } catch (ServerException *e) {
342 } catch (Exception *e) {
343 throw new Error("getSlots failed");
348 InputStream *is = http->getInputStream();
349 DataInputStream *dis = new DataInputStream(is);
350 Array<char> *resptype = new Array<char>(7);
351 dis->readFully(resptype);
353 if (!resptype->equals("getslot"->getBytes()))
354 throw new Error("Bad Response: " + new String(resptype));
356 return processSlots(dis);
357 } catch (SocketTimeoutException *e) {
359 throw new ServerException("getSlots failed", ServerException_TypeInputTimeout);
360 } catch (Exception *e) {
361 throw new Error("getSlots failed");
366 * Method that actually handles building Slot objects from the
367 * server response. Shared by both putSlot and getSlots.
369 Array<Slot *> *CloudComm::processSlots(DataInputStream *dis) {
370 int numberofslots = dis->readInt();
371 Array<int> *sizesofslots = new Array<int>(numberofslots);
372 Array<Slot *> *slots = new Array<Slot *>(numberofslots);
374 for (int i = 0; i < numberofslots; i++)
375 sizesofslots->set(i, dis->readInt());
376 for (int i = 0; i < numberofslots; i++) {
377 Array<char> *rawData = new Array<char>(sizesofslots->get(i));
378 dis->readFully(rawData);
379 Array<char> *data = stripIVAndDecryptSlot(rawData);
380 slots->set(i, Slot_decode(table, data, mac));
386 Array<char> *sendLocalData(Array<char> *sendData, int64_t localSequenceNumber, String host, int port) {
390 printf("Passing Locally\n");
391 mac->update(sendData);
392 Array<char> *genmac = mac->doFinal();
393 Array<char> *totalData = new Array<char>(sendData->length() + genmac->length());
394 System_arraycopy(sendData, 0, totalData, 0, sendData->length());
395 System_arraycopy(genmac, 0, totalData, sendData->length(), genmac->length());
397 // Encrypt the data for sending
398 Array<char> *iv = createIV(table->getMachineId(), table->getLocalSequenceNumber());
399 Array<char> *encryptedData = encryptSlotAndPrependIV(totalData, iv);
401 // Open a TCP socket connection to a local device
402 Socket *socket = new Socket(host, port);
403 socket->setReuseAddress(true);
404 DataOutputStream *output = new DataOutputStream(socket->getOutputStream());
405 DataInputStream *input = new DataInputStream(socket->getInputStream());
408 // Send data to output (length of data, the data)
409 output->writeInt(encryptedData->length);
410 output->write(encryptedData, 0, encryptedData->length);
413 int lengthOfReturnData = input->readInt();
414 Array<char> *returnData = new Array<char>(lengthOfReturnData);
415 input->readFully(returnData);
417 returnData = stripIVAndDecryptSlot(returnData);
419 // We are done with this socket
421 mac->update(returnData, 0, returnData->length - HMAC_SIZE);
422 Array<char> *realmac = mac->doFinal();
423 Array<char> *recmac = new Array<char>(HMAC_SIZE);
424 System_arraycopy(returnData, returnData->length - realmac->length, recmac, 0, realmac->length);
426 if (!recmac->equals(realmac))
427 throw new Error("Local Error: Invalid HMAC! Potential Attack!");
429 Array<char> *returnData2 = new Array<char>(lengthOfReturnData - recmac->length());
430 System_arraycopy(returnData, 0, returnData2, 0, returnData2->length);
433 } catch (Exception *e) {
434 printf("Exception\n");
440 void CloudComm::localServerWorkerFunction() {
441 ServerSocket *inputSocket = NULL;
444 // Local server socket
445 inputSocket = new ServerSocket(listeningPort);
446 inputSocket->setReuseAddress(true);
447 inputSocket->setSoTimeout(CloudComm_TIMEOUT_MILLIS);
448 } catch (Exception *e) {
449 throw new Error("Local server setup failure...");
454 // Accept incoming socket
455 Socket *socket = inputSocket->accept();
456 DataInputStream *input = new DataInputStream(socket->getInputStream());
457 DataOutputStream *output = new DataOutputStream(socket->getOutputStream());
459 // Get the encrypted data from the server
460 int dataSize = input->readInt();
461 Array<char> *readData = new Array<char>(dataSize);
462 input->readFully(readData);
466 readData = stripIVAndDecryptSlot(readData);
467 mac->update(readData, 0, readData->length - HMAC_SIZE);
468 Array<char> *genmac = mac->doFinal();
469 Array<char> *recmac = new Array<char>(HMAC_SIZE);
470 System_arraycopy(readData, readData->length() - recmac->length(), recmac, 0, recmac->length());
472 if (!recmac->equals(genmac))
473 throw new Error("Local Error: Invalid HMAC! Potential Attack!");
475 Array<char> *returnData = new Array<char>(readData->length() - recmac->length());
476 System_arraycopy(readData, 0, returnData, 0, returnData->length());
479 Array<char> *sendData = table->acceptDataFromLocal(returnData);
480 mac->update(sendData);
481 Array<char> *realmac = mac->doFinal();
482 Array<char> *totalData = new Array<char>(sendData->length() + realmac->length());
483 System_arraycopy(sendData, 0, totalData, 0, sendData->length());
484 System_arraycopy(realmac, 0, totalData, sendData->length(), realmac->length());
486 // Encrypt the data for sending
487 Array<char> *iv = createIV(table->getMachineId(), table->getLocalSequenceNumber());
488 Array<char> *encryptedData = encryptSlotAndPrependIV(totalData, iv);
491 // Send data to output (length of data, the data)
492 output->writeInt(encryptedData->length());
493 output->write(encryptedData, 0, encryptedData->length());
498 } catch (Exception *e) {
502 if (inputSocket != NULL) {
504 inputSocket->close();
505 } catch (Exception *e) {
506 throw new Error("Local server close failure...");
511 void CloudComm::close() {
514 if (localServerThread != NULL) {
516 localServerThread->join();
517 } catch (Exception *e) {
518 throw new Error("Local Server thread join issue...");