Neutrino is designed to be a flexible sensor platform with reusable components. It currently consists of a wireless temperature/humidity/pressure/proximity sensor for HVAC and door/window position sensing and a controller board which houses a wireless receiver, six relays, and a 24VAC to 5V power supply. Controller boards exist for both the Raspberry Pi and the BeagleBone Black. A controller board is connected to an RPi or BBB, and software is run on the device to collect data and control relays. A device that listens to the sensors is referred to as a “sensor hub”, while a device that controls external devices via the relays is referred to as a “controller”. A single RPi or BBB with a controller board attached could act as one or both of these, or IP network connectivity can be used to spread duties across systems. A DIY enthusiast could buy just the sensors and build their own sensor hub, buy the sensors and the controller board and write their own software to listen and control, or buy all of the hardware and deploy the neutrino software as-is.
The sensor software runs on the sensor hardware, programmed into the microcontroller. It wakes up once a minute, takes environmental measurements, and broadcasts those measurements on the radio channel and address assigned to that sensor.
The sensor hub software requires a controller board, specifically for the radio module. It retrieves its hub id and radio channel from a config file, then listens on the radio for sensors. When it receives a sensor broadcast, it publishes that sensor data to a database (and optionally to a Zabbix monitor), noting the sensor address and hub id along with the specific environmental data received.
The controller software requires a controller board, specifically for the relay modules. It reads configurations from the database (setpoints, etc), as well as the sensor data, and makes decisions about which relays to switch based on this information. It should include failsafe logic in case current sensor data is not found.
The monitor software only requires access to the database. It watches for sensor updates and will send notifications on things like low battery in a sensor, haven’t heard from a sensor within a given timeframe, etc.
The web service only requires access to the database. It provides a UI and REST API for consumers. Services include things like setting configuration (locale, unit preferences, etc), viewing sensor graphs, updating controller settings, etc.
In its simplest form, a fully-functioning system used for HVAC control would consist of one or more sensors, a sensor hub, a controller, a database, and a web UI. The monitor software is optional.
The user would then create “sensor groups” based on their own ideas or experimentation. The purpose of a sensor group is threefold: sensor groups will be graphed together in the UI, sensor groups can optionally have controllers attached to them, and data from all sensors in the group will be used to drive the attached controller. As such, a group can be passive, consisting only of sensors, or active, consisting of sensors and a controller.
Physically speaking, the simple deployment would consist of a BeagleBone Black or Raspberry Pi, a controller board attached to it, and sensors. The device would act as a sensor hub, a controller, a database, and a web server.
However, the platform is flexible enough to host multiple combinations of these, deployed however the user sees fit. There is support for 32 sensors per sensor hub. There is no limit to controllers, theoretically this would be a limit on database scalability. One could deploy a single sensor or many sensors, create N sensor groups including these, and deploy N controllers attached to these sensor groups.
The software and database can also be spread across distinct hardware, limited only by IP network connectivity. One could theoretically host the database/Web UI in the cloud, given the sensor hub and controller software have internet access.
The sensor device consists of a PCB, a low-power microcontroller, temperature/humidity/pressure/proximity sensors, a CR2450 battery, and supporting electronic components, all housed in a simple plastic shell. The device has physical jumpers to select sensor address and radio channel. The address should be unique per channel, and the channel selection effectively assigns the sensor to a sensor hub (as it will be configured to listen to a specific channel).
The controller board consists of a radio module, relays, supporting electronic components (led indicators, resistors, etc) and headers to connect to the computer device and HVAC controls. For convenience, it also contains a 24v AC to 5v power converter, so that the computer can be powered from the 24v HVAC relay. There is a relay assigned to heat, cooling, humidification, and fan. Each relay has a jumper that allows it to leverage a unified 24v power source (single HVAC transformer), or act in isolation. In isolation, a relay could have its own transformer, or something custom could be done like using the relay as a switch to turn on a lightbulb or misters, limited only by the current/voltage limits of the relays.
24VAC to USB
An optional component, the 24VAC to USB converter is a PCB board that rectifies and regulates the signal from a HVAC transformer and makes it available on a female USB port. The board is similar in design to the integrated power supply on the controller board. Its purpose is for providing power to a tablet mounted in the existing location of a thermostat, for running the web UI.
There are two main points of consideration for securing the Neutrino system. 1) We want to ensure that unauthorized listeners can’t glean data, and 2) We want to ensure that bogus data can’t be injected by unauthorized broadcasters. Physical security is largely ignored; that is, if someone has physical access to the sensors or systems, there are no measures taken to prevent someone from decoding, reprogramming, or otherwise tampering with it. In practical terms, for home users the primary concern is that neighbors’ sensors aren’t erroneously picked up and used, but there may also be a concern that passersby could eavesdrop on environmental data.
For the first point, the common way to ensure that an unauthorized eavesdropper can’t get data is via encryption. The Neutrino system currently leverages the skipjack block cipher for encryption, chosen because of its low resource requirements suitable for AVR microcontrollers. It has a relatively small key size (80 bits) which is good for microcontrollers, and is currently unbroken, though experts recommend against its use for mission-critical applications. There are known partial attacks, but the most effective aren’t much better than brute force search. Given there are about 1,208,9258,000,000,000,000,000,000 possible keys in a 2^80 bit key, and a quick test of a skipjack C decoder looping (not even stopping to inspect the result) yields about 1,000,000 tries per second on a high end laptop, that gives about 5703982330330 centuries for a brute force attack on general purpose CPU hardware (albeit single-threaded), divided by two for average time to crack. In addition, the attacker can try all the keys they like, but they have no idea if they guessed the right key. That is, they have no idea if the result of their decrypt is garbage or good data, though they may be able to make a reasonable guess based on the data format and ranges the data should fall into. This holds true even if an exploit is found that significantly decreases the time to crack the key, you have to have some way to know what you’re looking for in order to know you found it.
Short of a breakthrough in cracking all 32 rounds of the cipher that is substantially faster than brute force, or purpose-built ASIC hardware that is dozens of orders of magnitude faster than a general purpose CPU. I think that skipjack is sufficient to protect our simple environmental data from being read by someone.
The skipjack 80 bit key is generated by reading an unconnected analog pin and seeding the random() function with it, then generating 10 bytes (there may be an attack vector here, I haven’t done a lot of looking into whether or not the random function with a random 10-bit analog input is semi-predictable in any way, perhaps we can read from two pins, or something). This value is then written to the EEPROM of the microcontroller at address 0x00. The function that generates the key is run once when the microcontroller is first powered, and generates a key if the EEPROM address containing the key is empty, which happens whenever the microcontroller is reflashed. For development purposes, there’s a special ‘make installdev’ target for the AVR code that writes a known key, so that the microcontroller can be reflashed repeatedly without having to go through the pairing process.
Now the data is encrypted, but it doesn’t stop someone from broadcasting, claiming they are sensor “2” and feeding us garbage data just to torment us. We would decrypt it, which would still be garbage, and end up with wildly incorrect and seemingly random results injected into our environmental data. To tackle that, we would need to sign the encrypted data with a secret key, which would let us know that the decrypted result is valid and not garbage data. To decrease code size and complexity on the AVR, we could use our secret encryption key as a salt with which to hash the data. The downside of this is that someone were brute forcing the encryption, they could use the same key they are guessing and try to hash the message, and they now have a surefire way to determine if they came across the right key, although such an operation adds to the brute force time. If we use a separate key, an attacker would have to brute force every possible hash key for every possible encryption key they try. This would be useful in the event that the attacker is attempting to validate their decrypted data (e.g. if %.001 of brute decryption attempts results in a reasonable-sounding voltage/temp/humidity readings, they would still have to brute force the hash key for each of those to verify they found the encryption key, vs just hashing with the guessed encryption key). In reality though, space is at a premium in our small AVR message (we want a max of 32 bytes, as that’s what the radio can send in one message, and will result in the most power-efficiency and reliable transmission), so we don’t have room for an extremely long signing key. Given that current hash cracking algorithms can rank in the tens of megahashes per second on general purpose hardware, and many have been run in the tens of gigahashes on GPU (as well as the proliferation of ASIC hardware for digital currency mining), we can’t provide a sufficiently long key to make any attempt to validate the decryption via signature impossible, however, with a 40 bit key we can add a few hours on a GPU… significant if an attacker has millions or billions of decryption attempts to validate.
The signing key will most likely be the common HMAC_MD5 (and a truncated version at that). Collisions are extremely unlikely; it will be sufficient for our purpose. Note that there are currently published methods with which to generate a collision with MD5, but it’s irrelevant to our use of it in signing, because in order to use it you have to know what signature is expected, and to do that, you need to have the secret key. Generating a collision as an exploit is troublesome when you’re signing public data, as in software distribution or file verification. An attacker could generate and distribute their own code that matches the hash of the ‘good’ code, and masquerade as it. In our use, without the secret key, an attacker can generate a rogue message, but doesn’t know which signature the Neutrino code will expect from that message as it is a hash of known and secret data, therefore they can’t craft an attack message that matches an unknown signature. However, it’s probably important to note that in the previous paragraph we basically said that if an attacker could successfully decrypt the message, they could then also brute force the signature, and in fact cracking the ecryption via brute force requires brute forcing the key as well to validate the crack anyway. Therefore, the signature is strictly in place to verify that decryption succeeded, not a means with which to verify that the data has not been tampered with.
Finally, we need to protect against replay attacks. An attacker that can’t read the data and can’t inject their own can still capture one of our broadcasts and replay it, injecting garbage data. There is no real-time clock on our microcontroller, so we’ll need to provide some way to detect duplicate messages, a message id/counter or something else.
Pairing is currently accomplished by toggling a switch on the sensor, which effectively enables or disables encryption. While encryption is disabled, the sensor hub software can learn of new sensors and store their keys. This presents an issue for high-value targets where it is worthwhile for someone to monitor the location constantly or during a known installation window for pairing traffic. For these situations, a more complex pairing process could be used whereby the key is read from the sensor and injected into the database using inexpensive equipment.