From 21d747167d1091985438f6a2c954cdc895031c07 Mon Sep 17 00:00:00 2001 From: Christian Pointner Date: Thu, 29 Nov 2018 00:03:23 +0100 Subject: radiohead with crypto support --- contrib/radiohead.patch | 279 +++++++++++++++++++++++++++++++++++- include.mk | 3 + radiohead.mk | 13 +- usb-lora-crypto/Makefile | 52 +++++++ usb-lora-crypto/usb-lora-crypto.cpp | 118 +++++++++++++++ 5 files changed, 461 insertions(+), 4 deletions(-) create mode 100644 usb-lora-crypto/Makefile create mode 100644 usb-lora-crypto/usb-lora-crypto.cpp diff --git a/contrib/radiohead.patch b/contrib/radiohead.patch index a7040a6..1636f91 100644 --- a/contrib/radiohead.patch +++ b/contrib/radiohead.patch @@ -1,6 +1,6 @@ diff -Nur RadioHead-orig/RadioHead.h RadioHead/RadioHead.h --- RadioHead-orig/RadioHead.h 2018-11-15 11:40:24.000000000 +0100 -+++ RadioHead/RadioHead.h 2018-11-24 03:40:03.395279378 +0100 ++++ RadioHead/RadioHead.h 2018-11-28 23:58:44.998151593 +0100 @@ -317,8 +317,6 @@ cd /tmp mkdir RadioHead @@ -26,3 +26,280 @@ diff -Nur RadioHead-orig/RadioHead.h RadioHead/RadioHead.h // For Steve Childress port to ARM M4 w/CMSIS with STM's Hardware Abstraction lib. // See ArduinoWorkarounds.h (not supplied) +diff -Nur RadioHead-orig/RHAEADDriver.cpp RadioHead/RHAEADDriver.cpp +--- RadioHead-orig/RHAEADDriver.cpp 1970-01-01 01:00:00.000000000 +0100 ++++ RadioHead/RHAEADDriver.cpp 2018-11-28 23:59:30.373774012 +0100 +@@ -0,0 +1,48 @@ ++// RHAEADDriver.cpp ++// ++// Author: Philippe.Rochat'at'gmail.com ++// Contributed to the RadioHead project by the author ++// $Id: RHAEADDriver.cpp,v 1.4 2018/09/23 23:54:01 mikem Exp $ ++ ++#include ++#ifdef RH_ENABLE_ENCRYPTION_MODULE ++ ++RHAEADDriver::RHAEADDriver(RHGenericDriver& driver, AuthenticatedCipher& aead) ++ : _driver(driver), ++ _aead(aead) ++{ ++ _buffer = (uint8_t *)calloc(_driver.maxMessageLength(), sizeof(uint8_t)); ++} ++ ++bool RHAEADDriver::recv(uint8_t* buf, uint8_t* len) ++{ ++ bool status = _driver.recv(_buffer, len); ++ if (status && buf && len) { ++ // TODO: implement this ++ } ++ return status; ++} ++ ++bool RHAEADDriver::send(const uint8_t* data, uint8_t len) ++{ ++ if (len > maxMessageLength()) ++ return false; ++ ++ bool status = true; ++ if (len == 0) // PassThru ++ return _driver.send(data, len); ++ ++ uint8_t out_len = 0; ++ // TODO: implement this ++ return _driver.send(_buffer, out_len); ++} ++ ++uint8_t RHAEADDriver::maxMessageLength() ++{ ++ int driver_len = _driver.maxMessageLength(); ++ // IV? ++ driver_len -= _aead.tagSize(); ++ return driver_len; ++} ++ ++#endif +diff -Nur RadioHead-orig/RHAEADDriver.h RadioHead/RHAEADDriver.h +--- RadioHead-orig/RHAEADDriver.h 1970-01-01 01:00:00.000000000 +0100 ++++ RadioHead/RHAEADDriver.h 2018-11-28 23:59:09.441948151 +0100 +@@ -0,0 +1,221 @@ ++// RHAEADDriver.h ++ ++// Generic encryption and authentication layer that could use any driver ++// But will encrypt and authenticate all data. ++// Requires the Arduinolibs/Crypto library: ++// https://github.com/rweather/arduinolibs ++// ++// Author: Christain Pointner equinox'at'spreadspace.org ++// Contributed to the RadioHead project by the author ++ ++#ifndef RHAEADDriver_h ++#define RHAEADDriver_h ++ ++#include ++#ifdef RH_ENABLE_ENCRYPTION_MODULE ++#include ++ ++///////////////////////////////////////////////////////////////////// ++/// \class RHAEADDriver RHAEADDriver ++/// \brief Virtual Driver to encrypt and authenticate data. Can be used with any other RadioHead driver. ++/// ++/// This driver acts as a wrapper for any other RadioHead driver, adding encryption and authentication of ++/// messages that are passed to and from the actual radio driver. Only the message payload is encrypted but ++/// the to/from address or flags is part of the authenticated portion. Any of the authenticated-ciphers ++/// supported by ArduinoLibs Cryptographic Library http://rweather.github.io/arduinolibs/crypto.html may be used. ++/// ++/// For successful communications, both sender and receiver must use the same cipher and the same key. ++/// ++/// In order to enable this module you must uncomment #define RH_ENABLE_AEAD_MODULE at the bottom of RadioHead.h ++/// But ensure you have installed the Crypto directory from arduinolibs first: ++/// http://rweather.github.io/arduinolibs/index.html ++ ++class RHAEADDriver : public RHGenericDriver ++{ ++public: ++ /// Constructor. ++ /// Adds a ciphering layer to messages sent and received by the actual transport driver. ++ /// \param[in] driver The RadioHead driver to use to transport messages. ++ /// \param[in] blockcipher The blockcipher (from arduinolibs) that crypt/decrypt data. Ensure that ++ /// the blockcipher has had its key set before sending or receiving messages. ++ RHAEADDriver(RHGenericDriver& driver, AuthenticatedCipher& aead); ++ ++ /// Calls the real driver's init() ++ /// \return The value returned from the driver init() method; ++ virtual bool init() { return _driver.init();}; ++ ++ /// Tests whether a new message is available ++ /// from the Driver. ++ /// On most drivers, this will also put the Driver into RHModeRx mode until ++ /// a message is actually received by the transport, when it wil be returned to RHModeIdle. ++ /// This can be called multiple times in a timeout loop ++ /// \return true if a new, complete, error-free uncollected message is available to be retreived by recv() ++ virtual bool available() { return _driver.available();}; ++ ++ /// Turns the receiver on if it not already on. ++ /// If there is a valid message available, copy it to buf and return true ++ /// else return false. ++ /// If a message is copied, *len is set to the length (Caution, 0 length messages are permitted). ++ /// You should be sure to call this function frequently enough to not miss any messages ++ /// It is recommended that you call it in your main loop. ++ /// \param[in] buf Location to copy the received message ++ /// \param[in,out] len Pointer to available space in buf. Set to the actual number of octets copied. ++ /// \return true if a valid message was copied to buf ++ virtual bool recv(uint8_t* buf, uint8_t* len); ++ ++ /// Waits until any previous transmit packet is finished being transmitted with waitPacketSent(). ++ /// Then optionally waits for Channel Activity Detection (CAD) ++ /// to show the channnel is clear (if the radio supports CAD) by calling waitCAD(). ++ /// Then loads a message into the transmitter and starts the transmitter. Note that a message length ++ /// of 0 is permitted. ++ /// \param[in] data Array of data to be sent ++ /// \param[in] len Number of bytes of data to send ++ /// specify the maximum time in ms to wait. If 0 (the default) do not wait for CAD before transmitting. ++ /// \return true if the message length was valid and it was correctly queued for transmit. Return false ++ /// if CAD was requested and the CAD timeout timed out before clear channel was detected. ++ virtual bool send(const uint8_t* data, uint8_t len); ++ ++ /// Returns the maximum message length ++ /// available in this Driver, which depends on the maximum length supported by the underlying transport driver. ++ /// \return The maximum legal message length ++ virtual uint8_t maxMessageLength(); ++ ++ /// Blocks until the transmitter ++ /// is no longer transmitting. ++ virtual bool waitPacketSent() { return _driver.waitPacketSent();} ; ++ ++ /// Blocks until the transmitter is no longer transmitting. ++ /// or until the timeout occuers, whichever happens first ++ /// \param[in] timeout Maximum time to wait in milliseconds. ++ /// \return true if the radio completed transmission within the timeout period. False if it timed out. ++ virtual bool waitPacketSent(uint16_t timeout) {return _driver.waitPacketSent(timeout);} ; ++ ++ /// Starts the receiver and blocks until a received message is available or a timeout ++ /// \param[in] timeout Maximum time to wait in milliseconds. ++ /// \return true if a message is available ++ virtual bool waitAvailableTimeout(uint16_t timeout) {return _driver.waitAvailableTimeout(timeout);}; ++ ++ /// Calls the waitCAD method in the driver ++ /// \return The return value from teh drivers waitCAD() method ++ virtual bool waitCAD() { return _driver.waitCAD();}; ++ ++ /// Sets the Channel Activity Detection timeout in milliseconds to be used by waitCAD(). ++ /// The default is 0, which means do not wait for CAD detection. ++ /// CAD detection depends on support for isChannelActive() by your particular radio. ++ void setCADTimeout(unsigned long cad_timeout) {_driver.setCADTimeout(cad_timeout);}; ++ ++ /// Determine if the currently selected radio channel is active. ++ /// This is expected to be subclassed by specific radios to implement their Channel Activity Detection ++ /// if supported. If the radio does not support CAD, returns true immediately. If a RadioHead radio ++ /// supports isChannelActive() it will be documented in the radio specific documentation. ++ /// This is called automatically by waitCAD(). ++ /// \return true if the radio-specific CAD (as returned by override of isChannelActive()) shows the ++ /// current radio channel as active, else false. If there is no radio-specific CAD, returns false. ++ virtual bool isChannelActive() { return _driver.isChannelActive();}; ++ ++ /// Sets the address of this node. Defaults to 0xFF. Subclasses or the user may want to change this. ++ /// This will be used to test the adddress in incoming messages. In non-promiscuous mode, ++ /// only messages with a TO header the same as thisAddress or the broadcast addess (0xFF) will be accepted. ++ /// In promiscuous mode, all messages will be accepted regardless of the TO header. ++ /// In a conventional multinode system, all nodes will have a unique address ++ /// (which you could store in EEPROM). ++ /// You would normally set the header FROM address to be the same as thisAddress (though you dont have to, ++ /// allowing the possibilty of address spoofing). ++ /// \param[in] thisAddress The address of this node. ++ virtual void setThisAddress(uint8_t thisAddress) { _driver.setThisAddress(thisAddress);}; ++ ++ /// Sets the TO header to be sent in all subsequent messages ++ /// \param[in] to The new TO header value ++ virtual void setHeaderTo(uint8_t to){ _driver.setHeaderTo(to);}; ++ ++ /// Sets the FROM header to be sent in all subsequent messages ++ /// \param[in] from The new FROM header value ++ virtual void setHeaderFrom(uint8_t from){ _driver.setHeaderFrom(from);}; ++ ++ /// Sets the ID header to be sent in all subsequent messages ++ /// \param[in] id The new ID header value ++ virtual void setHeaderId(uint8_t id){ _driver.setHeaderId(id);}; ++ ++ /// Sets and clears bits in the FLAGS header to be sent in all subsequent messages ++ /// First it clears he FLAGS according to the clear argument, then sets the flags according to the ++ /// set argument. The default for clear always clears the application specific flags. ++ /// \param[in] set bitmask of bits to be set. Flags are cleared with the clear mask before being set. ++ /// \param[in] clear bitmask of flags to clear. Defaults to RH_FLAGS_APPLICATION_SPECIFIC ++ /// which clears the application specific flags, resulting in new application specific flags ++ /// identical to the set. ++ virtual void setHeaderFlags(uint8_t set, uint8_t clear = RH_FLAGS_APPLICATION_SPECIFIC) { _driver.setHeaderFlags(set, clear);}; ++ ++ /// Tells the receiver to accept messages with any TO address, not just messages ++ /// addressed to thisAddress or the broadcast address ++ /// \param[in] promiscuous true if you wish to receive messages with any TO address ++ virtual void setPromiscuous(bool promiscuous){ _driver.setPromiscuous(promiscuous);}; ++ ++ /// Returns the TO header of the last received message ++ /// \return The TO header ++ virtual uint8_t headerTo() { return _driver.headerTo();}; ++ ++ /// Returns the FROM header of the last received message ++ /// \return The FROM header ++ virtual uint8_t headerFrom() { return _driver.headerFrom();}; ++ ++ /// Returns the ID header of the last received message ++ /// \return The ID header ++ virtual uint8_t headerId() { return _driver.headerId();}; ++ ++ /// Returns the FLAGS header of the last received message ++ /// \return The FLAGS header ++ virtual uint8_t headerFlags() { return _driver.headerFlags();}; ++ ++ /// Returns the most recent RSSI (Receiver Signal Strength Indicator). ++ /// Usually it is the RSSI of the last received message, which is measured when the preamble is received. ++ /// If you called readRssi() more recently, it will return that more recent value. ++ /// \return The most recent RSSI measurement in dBm. ++ int16_t lastRssi() { return _driver.lastRssi();}; ++ ++ /// Returns the operating mode of the library. ++ /// \return the current mode, one of RF69_MODE_* ++ RHMode mode() { return _driver.mode();}; ++ ++ /// Sets the operating mode of the transport. ++ void setMode(RHMode mode) { _driver.setMode(mode);}; ++ ++ /// Sets the transport hardware into low-power sleep mode ++ /// (if supported). May be overridden by specific drivers to initialte sleep mode. ++ /// If successful, the transport will stay in sleep mode until woken by ++ /// changing mode it idle, transmit or receive (eg by calling send(), recv(), available() etc) ++ /// \return true if sleep mode is supported by transport hardware and the RadioHead driver, and if sleep mode ++ /// was successfully entered. If sleep mode is not suported, return false. ++ virtual bool sleep() { return _driver.sleep();}; ++ ++ /// Returns the count of the number of bad received packets (ie packets with bad lengths, checksum etc) ++ /// which were rejected and not delivered to the application. ++ /// Caution: not all drivers can correctly report this count. Some underlying hardware only report ++ /// good packets. ++ /// \return The number of bad packets received. ++ virtual uint16_t rxBad() { return _driver.rxBad();}; ++ ++ /// Returns the count of the number of ++ /// good received packets ++ /// \return The number of good packets received. ++ virtual uint16_t rxGood() { return _driver.rxGood();}; ++ ++ /// Returns the count of the number of ++ /// packets successfully transmitted (though not necessarily received by the destination) ++ /// \return The number of packets successfully transmitted ++ virtual uint16_t txGood() { return _driver.txGood();}; ++ ++private: ++ /// The underlying transport river we are to use ++ RHGenericDriver& _driver; ++ ++ /// The AuthenticatedCipher we are to use for encrypting/decrypting ++ AuthenticatedCipher& _aead; ++ ++ /// Buffer to store encrypted/decrypted message ++ uint8_t* _buffer; ++}; ++ ++ ++ ++#endif ++#endif diff --git a/include.mk b/include.mk index 5c14178..d0f526e 100644 --- a/include.mk +++ b/include.mk @@ -86,6 +86,9 @@ ifdef RADIOHEAD_PATH CXXFLAGS += -I$(RADIOHEAD_PATH) CXXFLAGS += -DUSES_RADIOHEAD CXXFLAGS += -DRH_PLATFORM=RH_PLATFORM_GENERIC_AVR8 +ifdef RWEATHER_CRYPTO_PATH +CXXFLAGS += -DRH_ENABLE_ENCRYPTION_MODULE +endif CXXFLAGS += $(RADIOHEAD_OPTS) endif diff --git a/radiohead.mk b/radiohead.mk index 6352d5e..55fe943 100644 --- a/radiohead.mk +++ b/radiohead.mk @@ -25,13 +25,20 @@ include $(SPREADAVR_PATH)/defines.mk # TODO: add other components dependent on driver... RADIOHEAD_COMPONENTS := RH_$(RADIOHEAD_DRIVER) RHSPIDriver RHGenericDriver RHGenericSPI RHHardwareSPI -SRC:=$(foreach COMP,$(RADIOHEAD_COMPONENTS),$(RADIOHEAD_PATH)/$(COMP).cpp) -DEPLIBS := Arduino-SPI arduino-stub stdc++-minimal - CXXFLAGS += -I$(RADIOHEAD_PATH) CXXFLAGS += -DRH_PLATFORM=RH_PLATFORM_GENERIC_AVR8 CXXFLAGS += $(RADIOHEAD_OPTS) +ifdef RWEATHER_CRYPTO_PATH +CXXFLAGS += -DRH_ENABLE_ENCRYPTION_MODULE +CXXFLAGS += -I$(RWEATHER_CRYPTO_PATH)/libraries/Crypto +CXXFLAGS += $(RWEATHER_CRYPTO_OPTS) +RADIOHEAD_COMPONENTS += RHAEADDriver # RHEncryptedDriver +endif + +SRC:=$(foreach COMP,$(RADIOHEAD_COMPONENTS),$(RADIOHEAD_PATH)/$(COMP).cpp) +DEPLIBS := Arduino-SPI arduino-stub stdc++-minimal + OBJ = $(SRC:%.cpp=%.o) OBJ_DEPLIB = $(DEPLIBS:%=deplib-radiohead--%.o) diff --git a/usb-lora-crypto/Makefile b/usb-lora-crypto/Makefile new file mode 100644 index 0000000..0b61c94 --- /dev/null +++ b/usb-lora-crypto/Makefile @@ -0,0 +1,52 @@ +## +## spreadspace avr utils +## +## +## Copyright (C) 2013-2018 Christian Pointner +## +## This file is part of spreadspace avr utils. +## +## spreadspace avr utils is free software: you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation, either version 3 of the License, or +## any later version. +## +## spreadspace avr utils is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with spreadspace avr utils. If not, see . +## + +NAME := usb-lora-crypto +BOARD_TYPE := elecrow32u4lora +CXX_OBJ := $(NAME).o +LIBS := util led lufa-descriptor-usbserial usbio +EXTERNAL_LIBS := lufa radiohead rweather-crypto +SPREADAVR_PATH := .. + +LUFA_PATH := $(SPREADAVR_PATH)/contrib/lufa-LUFA-151115 +LUFA_OPTS = -D USB_DEVICE_ONLY +LUFA_OPTS += -D DEVICE_STATE_AS_GPIOR=0 +LUFA_OPTS += -D ORDERED_EP_CONFIG +LUFA_OPTS += -D FIXED_CONTROL_ENDPOINT_SIZE=8 +LUFA_OPTS += -D FIXED_NUM_CONFIGURATIONS=1 +LUFA_OPTS += -D USE_FLASH_DESCRIPTORS +LUFA_OPTS += -D USE_STATIC_OPTIONS="(USB_DEVICE_OPT_FULLSPEED | USB_OPT_REG_ENABLED | USB_OPT_AUTO_PLL)" +LUFA_OPTS += -D INTERRUPT_CONTROL_ENDPOINT + +LUFA_OPTS += -D USB_MANUFACTURER="L\"equinox\"" +LUFA_OPTS += -D USB_PRODUCT="L\"spreadspace usb-lora-crypto example\"" + +LUFA_COMPONENTS := USB USBCLASS + + +RADIOHEAD_PATH := $(SPREADAVR_PATH)/contrib/RadioHead + +RADIOHEAD_DRIVER := RF95 + +RWEATHER_CRYPTO_PATH := $(SPREADAVR_PATH)/contrib/rweather-crypto + +include $(SPREADAVR_PATH)/include.mk diff --git a/usb-lora-crypto/usb-lora-crypto.cpp b/usb-lora-crypto/usb-lora-crypto.cpp new file mode 100644 index 0000000..3af1dcb --- /dev/null +++ b/usb-lora-crypto/usb-lora-crypto.cpp @@ -0,0 +1,118 @@ +/* + * spreadspace avr utils + * + * + * Copyright (C) 2013-2016 Christian Pointner + * + * This file is part of spreadspace avr utils. + * + * spreadspace avr utils is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * spreadspace avr utils is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with spreadspace avr utils. If not, see . + */ + +#include +#include +#include +#include +#include + +#include "util.h" +#include "led.h" +#include "usbio.h" + +#include "RadioHead.h" +#include "RH_RF95.h" +#include +#include +#include "RHAEADDriver.h" + +ChaChaPoly cipher; +RH_RF95 lora; +RHAEADDriver aead(lora, cipher); + +void recv_aead_msg() +{ + uint8_t buf[RH_RF95_MAX_MESSAGE_LEN+1]; + uint8_t len = sizeof(buf)-1; + + led2_on(); + if(!aead.recv(buf, &len)) { + printf("aead.recv(): failed\r\n"); + led2_off(); + return; + } + buf[len] = 0; + + printf("aead.recv() got message: RSSI = %d, data = ", aead.lastRssi()); + for(uint8_t i = 0; i < len; ++i) printf("%s0x%02X", (i==0) ? "" : " ", buf[i]); + printf("\r\n"); + // printf("aead.recv() got message: RSSI = %d, data = %s\r\n", aead.lastRssi(), buf); + led2_off(); +} + +void send_aead_msg() +{ + uint8_t data[] = "spreadspace.org/avr-utils usb-aead test using radiohead library"; + printf("aead: sending message with %d bytes\r\n", sizeof(data)); + + led_on(); + if(!aead.send(data, sizeof(data))) { + printf("aead.send(): failed\r\n"); + led_off(); + return; + } + aead.waitPacketSent(); + printf("aead.send(): success\r\n"); + led_off(); +} + +void handle_cmd(uint8_t cmd) +{ + switch(cmd) { + case 's': send_aead_msg(); break; + default: printf("error\r\n"); return; + } +} + +int main(void) +{ + MCUSR &= ~(1 << WDRF); + wdt_disable(); + + cpu_init(); + led_init(); + usbio_init(); + arduino_init(); + sei(); + + lora.init(); + lora.setFrequency(868.0); + aead.init(); + + for(;;) { + if(aead.available()) { + recv_aead_msg(); + } + + int16_t BytesReceived = usbio_bytes_received(); + while(BytesReceived > 0) { + int ReceivedByte = fgetc(stdin); + if(ReceivedByte != EOF) { + handle_cmd(ReceivedByte); + } + BytesReceived--; + } + + usbio_task(); + } +} -- cgit v1.2.3