From 723941bdf8587947c5dcfbc3b066f9f222a68ea0 Mon Sep 17 00:00:00 2001
From: Starbeamrainbowlabs <sbrl@starbeamrainbowlabs.com>
Date: Mon, 15 Jul 2019 13:49:44 +0100
Subject: [PATCH] Implement an extra layer of encryption, but it's untested.

---
 README.md                                |  6 ++++++
 iot/main/settings.custom.cpp.example     | 11 ++++++++---
 iot/main/settings.custom.h               |  7 +++++++
 iot/main/transmission.cpp                |  6 ++++++
 server/settings.default.toml             |  4 ++++
 server/ttn-app-server/DecodePayload.mjs  | 10 ++++++++--
 server/ttn-app-server/MessageHandler.mjs |  7 ++++++-
 7 files changed, 45 insertions(+), 6 deletions(-)

diff --git a/README.md b/README.md
index b0bc453..f5c3d60 100644
--- a/README.md
+++ b/README.md
@@ -20,8 +20,14 @@ Run `bash ./build setup` from the command line at the root of this repository.
 
 ### IoT Device
  - Copy `settings.custom.cpp.example` to `settings.custom.cpp` and fill in the fields there
+     - This Bash one-liner can be used to generate a new encryption key in the right formats: `head -c16 /dev/urandom | od -tx1 | awk '{ gsub(/^0+\s+/, "", $0); toml=$0; gsub(/\s+/, "", toml); print("settings.toml format: " toml); arduino=toupper($0); gsub(/\s+/, ", 0x", arduino); print("settings.custom.cpp format: 0x" arduino); exit }'`
  - Review `settings.h` to make sure it matches your setup
  - Copy the folders in `iot/libraries` to your Arduino IDE libraries folder
 
+### Server
+ - The `bash ./build setup` should have already installed the necessary dependencies.
+ - Create `settings.toml` in the root of the repository if it doesn't exist already.
+ - Review `server/settings.default.toml` and adjust `settings.toml` to fill in the values for the required fields, not forgetting the encryption key generated when setting up the IoT device.
+
 ## Useful Links
  - Entropy extraction from the watchdog timer vs the internal clock: https://github.com/taoyuan/Entropy
diff --git a/iot/main/settings.custom.cpp.example b/iot/main/settings.custom.cpp.example
index 11fdf42..1010322 100644
--- a/iot/main/settings.custom.cpp.example
+++ b/iot/main/settings.custom.cpp.example
@@ -8,19 +8,24 @@
 // LoRaWAN NwkSKey, network session key
 // This is the default Semtech key, which is used by the early prototype TTN
 // network.
-static const PROGMEM u1_t NWKSKEY[16] = { .... };
+const PROGMEM u1_t NWKSKEY[16] = { .... };
 
 // LoRaWAN AppSKey, application session key
 // This is the default Semtech key, which is used by the early prototype TTN
 // network.
-static const u1_t PROGMEM APPSKEY[16] = { ..... };
+const u1_t PROGMEM APPSKEY[16] = { ..... };
 
 // LoRaWAN end-device address (DevAddr)
-static const u4_t DEVADDR = 0x....; // <-- Change this address for every node!
+const u4_t DEVADDR = 0x....; // <-- Change this address for every node!
 
+// The RFM95 pin mapping
 const lmic_pinmap lmic_pins = {
 	.nss = PIN_SPI_CS_RFM95,
 	.rxtx = LMIC_UNUSED_PIN,
 	.rst = 9,
 	.dio = {2, 6, 7},
 };
+
+// The AES-ECB 128-bit key to encrypt the data with.
+// See the README for a Bash oneliner that generates a new key in the right format.
+uint8_t encrypt_key[16] = { ..... };
diff --git a/iot/main/settings.custom.h b/iot/main/settings.custom.h
index 28e4fee..5631a2d 100644
--- a/iot/main/settings.custom.h
+++ b/iot/main/settings.custom.h
@@ -7,8 +7,15 @@
  * it settings.cpp, and edit that instead.
  */
 
+// The network session key. From The Things Network console
 extern const PROGMEM u1_t NWKSKEY[16];
+// The application session key from The Things Network console
 extern const u1_t PROGMEM APPSKEY[16];
+// The device address
 extern const u4_t DEVADDR;
 
+// The pin mapping for the RFM95
 extern const lmic_pinmap lmic_pins;
+
+// The key to encrypt the data with
+extern uint8_t encrypt_key[16];
diff --git a/iot/main/transmission.cpp b/iot/main/transmission.cpp
index 8a74596..0088416 100644
--- a/iot/main/transmission.cpp
+++ b/iot/main/transmission.cpp
@@ -8,6 +8,9 @@
 // Global static variable that's used to detect when LMIC has finished doing it's thing
 bool is_sending_complete = false;
 
+// Defined as part of LMIC, but LMIC is compiled and linked in separately
+extern "C" void lmic_aes_encrypt(unsigned char *Data, unsigned char *Key);
+
 
 void transmit_counter_save() {
 	store_eeprom_uint32_save(0, LMIC.seqnoUp);
@@ -82,6 +85,9 @@ void transmit_init() {
  * @param length The length of the given message.
  */
 bool transmit_send(uint8_t* data, int length) {
+	// Encrypt the data with the extra encryption key
+	lmic_aes_encrypt(data, encrypt_key);
+	
 	// Check if there is not a current TX/RX job running
 	if (LMIC.opmode & OP_TXRXPEND) {
 		// Serial.println(F("OP_TXRXPEND: Job running, can't send"));
diff --git a/server/settings.default.toml b/server/settings.default.toml
index 456f51d..5fdd40f 100644
--- a/server/settings.default.toml
+++ b/server/settings.default.toml
@@ -40,6 +40,10 @@ app_id = "CHANGE_THIS"
 # there.
 access_key = "CHANGE_THIS"
 
+# The additional encryption key, in hex, generated when setting up the IoT device.
+# This is used as an exrtra layer of encryption to ensure that The Things Network does not have access to the decrypted data.
+encryption_key = "CHANGE_THIS"
+
 # A list of devices to monitor.
 # If a device isn't specified here, then we won't hear messages from it.
 # FUTURE: Automatically fetch a list of devices from the TTN API 
diff --git a/server/ttn-app-server/DecodePayload.mjs b/server/ttn-app-server/DecodePayload.mjs
index a7173a7..16960a6 100644
--- a/server/ttn-app-server/DecodePayload.mjs
+++ b/server/ttn-app-server/DecodePayload.mjs
@@ -1,11 +1,17 @@
 "use strict";
 
+import aes from 'aes-js';
+
 const offset_id = 0;
 const offset_lat = 32 / 8;
 const offset_lng = (32 / 8) + (32 / 8);
 
-function decode_payload(payload_base64) {
-	let payload_buffer = Buffer.from(payload_base64, "base64");
+function decode_payload(payload_base64, key_hex) {
+	let payload_buffer_enc = Buffer.from(payload_base64, "base64");
+	let key_buffer = Buffer.from(key_hex, "hex");
+	
+	let aes_ecb = new aes.ModeOfOperation.ecb(key_buffer);
+	let payload_buffer = aes_ecb.decrypt(payload_buffer_enc);
 	
 	// console.debug(`Offsets: ${offset_id}, ${offset_lat}, ${offset_lng}`);
 	// console.debug(`Message hex: ${payload_buffer.toString("hex")}`)
diff --git a/server/ttn-app-server/MessageHandler.mjs b/server/ttn-app-server/MessageHandler.mjs
index 87fc7ca..c1c1817 100644
--- a/server/ttn-app-server/MessageHandler.mjs
+++ b/server/ttn-app-server/MessageHandler.mjs
@@ -16,7 +16,12 @@ class MessageHandler {
 	async handle(message) {
 		if(this.debug) console.log(message);
 		
-		let decoded_payload = decode_payload(message.payload_raw);
+		if(this.settings.ttn.encryption_key == "CHANGE_THIS") {
+			this.log.error(`Error: No encryption key specified. Try filling in the required values in settings.toml. If they don't exist yet, try using server/settings.default.toml as a reference.`);
+			return false;
+		}
+		
+		let decoded_payload = decode_payload(message.payload_raw, this.settings.ttn.encryption_key);
 		if(this.debug) console.log(decoded_payload);
 		
 		let rssis = [];
