You are reading a single comment by @Gordon and its replies. Click here to read the full conversation.
  • I'd like to transmit some data wirelessly over a low-bandwidth, insecure channel - maybe 433Mhz, NRF24, etc.

    The data would be:

    • Sent in one direction only
    • Encrypted
    • Checked to see if it was corrupted
    • Only a handful of bytes longer than the original message

    The transmitter could be pre-loaded with an encryption key that didn't change, and the receiver with a decryption key.

    Does anyone know of an algorithm/protocol suitable for this? I hear all the time of people implementing their own home-grown solutions with massive security holes, so really want something tried and tested.

  • First I've seen this thread, but I can suggest an alternative less resource intensive solution that I have used...

    Assumptions... @Gordon initial premises of low resources, low message overhead, validating message integrity, use of a predefined secret, and use of the built-in SHA256 hash function; however, no message encryption. This can be extended to Puck.js security as well.

    Consider that sometimes authentication of the message can be more important than the actual message -- the message bears the kings seal! For example, if I want to read a sensor value or send a message to open/close a garage door, particularly within a small coverage area such as BLE vs the global Internet, I may not need to encrypt the message if I can validate that the message is authentic. In other words who cares what the message says if I can discriminate and trust who sent the message.

    This involves a much simpler and faster procedure. For a string of "data", you calculate an authentication hash equivalent say to the following pseudo-code.

    hash = SHA256(salt+epoch+secret+data);
    message = {data:data, salt:salt, epoch:epoch,hash:hash, version:1};
    

    Where salt is a random prefix string, typically 2-8 characters, to ensure sending the same message does not generate the same hash response; epoch is a time stamp that enables checking that the message was generated within a valid time window (i.e. sent just now, not a replay from yesterday. Also, need to be able to sync time.); secret is a piece of information known only by the sender and receiver and never passed openly (although this may be required one time in a learn mode, which presumably you can control); and the optional version provides identification of the scheme used that allows the receiver to discriminate different formats and enables a migration path to newer schemes.

    Note the salt, epoch, and the hash get passed with the data message. The receiver uses the same code to generate the same hash locally, using the locally known secret. If the sent message hash matches the locally generated hash, the message is authentic so act on it; otherwise, ignore the message or report it as a bad request perhaps. The hash also acts as a "checksum" to validate the integrity of the data. A single bit error will radically alter the generated hash.

    If the overhead of the hash, salt, and epoch represent to much bandwidth burden, many options exist. For example, send a simple string instead of a JS object with the fixed length version (1 byte), salt (2 byte), epoch (4 byte), and hash (32 bytes) prefixed to the data string, which the receiver can very easily parse by fixed substrings. Alternately, the hash can be truncated as a trade with security. A 4 byte hash for example still has 256^4 or ~1:4 billion chance of being replicated, probably good enough for most personal sensor/actuator applications. Assuming 5 byte hash, you only add 12 bytes overhead per message, which even works for the limited BLE message size.

    Another feature I like is that with an initial stored secret, the secret used for the hash can itself be adapted on the fly or on (authenticated) command. For example, you could hash the stored secret with a client provided random string, which changes the key for the specific message at the request of the client. Or roll the key based on a variable such as the day of the week. Or generate the key internally to the transmitter perhaps with a PUF -- see https://en.wikipedia.org/wiki/Physical_unclonable_function

    There are many variations on this theme, but...

    1. Always randomize the message (i.e. salt it).
    2. Never pass the secret (other than possibly a controlled one time learn).
    3. Pass time and build in a time syncing capability.


About

Avatar for Gordon @Gordon started