Author Topic: Random gibberish data opens my garage door  (Read 2677 times)

luisr320

  • Sr. Member
  • ****
  • Posts: 255
  • Country: pt
Random gibberish data opens my garage door
« on: October 16, 2020, 05:52:50 AM »
Hi. I have a Moteino connected to my garage door which has been working flawlessly for several years now. Last night I suddenly got a notification that the garage door was opening. I closed but it kept on opening again after a while. I got a Moteino with a promiscuous mode set to TRUE to sniff any traffic on the network and picked up some gibberish once in a while, which is the cause of the problem.

I'm posting a screenshot of what I'm talking about picked up on Putty. In the image, the node ID is 179 but I don't have a node with that ID. All of the data is comming from the boiler, ID 3.

Any ideas of what I'm dealing with?

Felix

  • Administrator
  • Hero Member
  • *****
  • Posts: 6866
  • Country: us
    • LowPowerLab
Re: Getting random gibberish data opens my garage door
« Reply #1 on: October 16, 2020, 09:19:25 AM »
What sketch do you use on your Garage opener?

luisr320

  • Sr. Member
  • ****
  • Posts: 255
  • Country: pt
Re: Getting random gibberish data opens my garage door
« Reply #2 on: October 16, 2020, 09:45:34 AM »
Hello Felix. Thank you for your reply.
Here is the code:

Code: [Select]
// **********************************************************************************************************
// Garage Door Node
// Ver. 9.0 - 2020-09-14
// **********************************************************************************************************

#include <RFM69.h>
#include <RFM69_OTA.h>
#include <RFM69_ATC.h>
#include <SPIFlash.h>

#define THISNODEID 4//unique for each node on same network
#define GATEWAYID 1//Gateway number on this network
#define BLYNKGATEWAY 10 //Blynk Gateway
#define NETWORKID 100//the same on all nodes that talk to each other
#define FREQUENCY RF69_433MHZ
#define ENCRYPTKEY "XXXXXXXXXXXXXXXX"
#define ATC_RSSI -80
#define ACK_TIME 3000 //max # of ms to wait for an ack
#define LED 9 // Moteinos have LEDs on D9
#define RELAYPIN1 6 // Output pin connected to the relay
#define RELAYPIN2 7 // Output pin connected to the relay
#define POSITIONPOT A1 //Input pin connected to the potentiometer
#define POSITIONPOT_EN 5//Digital output to enable the potentiometer
#define RELAY_PULSE_MS 500 //just enough that the opener will pick it up
#define SERIAL_BAUD  115200
#define PROMISCUOUSMODE false //set to 'true' to sniff all packets on the same network


// Outbound Data Struct
typedef struct
{
char out_nodeType[6]; // Node Type
uint32_t out_data1; // Data 1 << Device Output - 1 = DOOR POSITION SENSOR
uint32_t out_data2; // Data 2 << Door position in %
float out_data3; // Data 3 << NIL

} Payload;
Payload outData;

//Inbound Data Struct
typedef struct
{
uint8_t in_data0; // Node Type
uint32_t in_data1;  // Data 1 >> Device Input - 1 = OPEN/STOP/CLOSE RELAY
uint32_t in_data2; // Data 2 >> INPUT A - 1 = OPEN/STOP/CLOSE
float in_data3; // Data 3 >> INPUT B - NIL

} in_Payload;
in_Payload inData;

//Initialize some variables
unsigned long timeNow = 0;
word blinkPeriod = 15000; //transmit a ping to gateway so often (in ms)
int16_t doorPosition = 0;
int16_t olddoorPosition = 0;

// Hardware Init
SPIFlash flash(8, 0xEF30); //EF30 for windbond 4mbit flash
RFM69_ATC radio;

void setup()
{
//Setup the pins
pinMode(LED, OUTPUT);
pinMode(RELAYPIN1, OUTPUT);
pinMode(RELAYPIN2, OUTPUT);
pinMode(POSITIONPOT, INPUT);
pinMode(POSITIONPOT_EN, OUTPUT);

//Turn on the position trim pot
digitalWrite(POSITIONPOT_EN, HIGH);

//Start the Serial port
Serial.begin(SERIAL_BAUD);

//Initialize the radio
radio.initialize(FREQUENCY, THISNODEID, NETWORKID);
radio.setHighPower();
radio.encrypt(ENCRYPTKEY);//Turn encryption ON
radio.promiscuous(PROMISCUOUSMODE);
radio.enableAutoPower(ATC_RSSI);
}

void loop()
{
if (radio.receiveDone())//If some packet was received by the radio, wait for all its contents to come trough
{
CheckForWirelessHEX(radio, flash, true);// Check if it is a OTA upload

//If an ACK (acknowledge) was requested by the data packet transmitter radio, send the ACK back to it
if (radio.ACKRequested())
{
radio.sendACK();
}

//Extract data from the payload
inData = *(in_Payload*)radio.DATA; //assume radio.DATA actually contains our struct and not something else
   
//Now check what was the instruction sent by the Gateway to this node
if (inData.in_data1 == 1) // If it was a "1", a command was sent from the Gateway to the Output 1 - Garage Door Relay
{
if (inData.in_data2 == 1) // If it was a "1", a command was sent from the Gateway to toggle the relay
{
controlDoor();
}
inData.in_data1 = 0; //Reset the in_data2 Variable content
}
}
sendDoorPos(); // Get the door position from the potenciometer and sent it to the Gateway
}

void controlDoor()
{
digitalWrite(RELAYPIN1, HIGH);//Using two pins to doulbe the current
digitalWrite(RELAYPIN2, HIGH);
delay(RELAY_PULSE_MS);
digitalWrite(RELAYPIN1, LOW);//Using two pins to doulbe the current
digitalWrite(RELAYPIN2, LOW);
Blink(25);//Blink the node led to show a the command was successfully received
}

void sendDoorPos()
{
doorPosition = analogRead(POSITIONPOT); //Read Potenciometer to determine door position
doorPosition = map(doorPosition, 5, 920, 0, 100);
doorPosition = constrain(doorPosition, 0, 100);

//Check if door position has changed and if it did, send new position to the Gateway.
if (millis() / 1000 - timeNow / 1000 > 1)
{
timeNow = millis();
if (doorPosition != olddoorPosition)
{
olddoorPosition = doorPosition;
//fill in the struct with new door position
strcpy(outData.out_nodeType, "GGATE");
outData.out_data1 = 1; // Device output - Potenciometer
outData.out_data2 = doorPosition;
outData.out_data3 = analogRead(POSITIONPOT);
sendData(GATEWAYID);
sendData(BLYNKGATEWAY);
}
}
}

void sendData(byte destinationNode)
{
radio.sendWithRetry(destinationNode, (const void*)(&outData), sizeof(outData), 3, ACK_TIME); //Send new position to the Moteino Gateway
}

void Blink(byte DELAY_MS)//Local led blinking function
{
digitalWrite(LED, HIGH);
delay(DELAY_MS);
digitalWrite(LED, LOW);
}

Felix

  • Administrator
  • Hero Member
  • *****
  • Posts: 6866
  • Country: us
    • LowPowerLab
Re: Getting random gibberish data opens my garage door
« Reply #3 on: October 16, 2020, 10:34:50 AM »
This is very different than my GarageMote example. You're using a struct, so Im guessing that's where things go wrong - a stray packet from some other source gets through, gets decrypted as binary data, which then gets picked up into your struct without any checks of whether the data is valid.

You need some more checks in your code, do not just blindly execute stuff that is decoded from an unknown packet into a struct.

Code: [Select]
if (inData.in_data1 == 1)

The above code is NOT a good check  :-\

luisr320

  • Sr. Member
  • ****
  • Posts: 255
  • Country: pt
Re: Getting random gibberish data opens my garage door
« Reply #4 on: October 16, 2020, 11:16:20 AM »
At a point it opened both my small gate and the garage door at the same time. But not the outer gate.
Here is the code of the small gate>

Code: [Select]
// **********************************************************************************************************
// Outer Gate Node
// Ver. 4.0
// 2020-09-16
// **********************************************************************************************************

#include <RFM69.h>
#include <RFM69_ATC.h>
#include <RFM69_OTA.h>
#include <SPIFlash.h>

#define NODEID 5 //unique for each node on same network
#define GATEWAYID 1 //Gateway number on this network
#define NETWORKID 100 //the same on all nodes that talk to each other
#define FREQUENCY RF69_433MHZ
#define ENCRYPTKEY "XXXXXXXXXXXXXXXX" //exactly the same 16 characters/bytes on all nodes!
#define ATC_RSSI -80
#define ACK_TIME 3000 // max # of ms to wait for an ack
#define LED 9 // Moteinos have LEDs on D9
#define RELAY1 5 // Output pin connected to the relay
#define RELAY2 7 // Output pin connected to the relay
#define SERIAL_BAUD 115200
#define PROMISCUOUSMODE false //set to 'true' to sniff all packets on the same network

//Initialize some variables
unsigned long timeNow = 0;

// Outbound Data Struct
typedef struct
{
char out_nodeType[6]; // Node Type
uint32_t out_data1; // Data 1 << Device Output - 1 = DOOR POSITION SENSOR
uint32_t out_data2; // Data 2 << Door position in %
float out_data3; // Data 3 << NIL

} Payload;
Payload outData;

//Inbound Data Struct
typedef struct
{
uint8_t in_data0; // Node Id
uint32_t in_data1;  // Data 1 >> Device Input - RELAY 1 and 2
uint32_t in_data2; // Data 2 >> INPUT A - 1 = OPEN; 0 = CLOSE
float in_data3; // Data 3 >> INPUT B - NIL

} in_Payload;
in_Payload inData;

// Hardware Init
SPIFlash flash(8, 0xEF30); //EF40 for 16mbit windbond chip
RFM69_ATC radio; // Call the radio

void setup()
{
//Setup the pins
pinMode(LED, OUTPUT);
pinMode(RELAY1, OUTPUT);
pinMode(RELAY2, OUTPUT);

//Start the Serial port
Serial.begin(SERIAL_BAUD);

//Initialize the radio
radio.initialize(FREQUENCY, NODEID, NETWORKID);
radio.setHighPower();
radio.encrypt(ENCRYPTKEY);//Turn encryption ON
radio.promiscuous(PROMISCUOUSMODE);
radio.enableAutoPower(ATC_RSSI);
}

void loop()
{
if (radio.receiveDone())//If some packet was received by the radio, wait for all its contents to come trough
{
CheckForWirelessHEX(radio, flash, true); // set to true for debug. This will check if there is a new wireless program to load

//Extract data from the incoming payload
inData = *(in_Payload*)radio.DATA;

//If an ACK (acknowledge) was requested by the data packet transmitter radio, send the ACK back to it
if (radio.ACKRequested())
{
radio.sendACK();
}

if (inData.in_data1 == 1) //If data is for Relays
{
if (inData.in_data2 == 1)// If a command was sent from the Gateway to toggle RELAY1, a "1" was received
{
digitalWrite(RELAY1, HIGH);
delay(500);
digitalWrite(RELAY1, LOW);
}
else if (inData.in_data2 == 2) // If a command was sent from the Gateway to toggle RELAY2, a "2" was received
{
digitalWrite(RELAY2, HIGH);
delay(500);
digitalWrite(RELAY2, LOW);
}
}
inData.in_data1 = 0; //Reset the in_data Variable content
inData.in_data2 = 0; //Reset the in_data Variable content
Blink(25);//Blink the transmitter led to show the data was successfully received
}
}

void Blink(byte DELAY_MS)//Local led blinking function
{
digitalWrite(LED, HIGH);
delay(DELAY_MS);
digitalWrite(LED, LOW);
}
« Last Edit: October 16, 2020, 12:55:07 PM by luisr320 »

luisr320

  • Sr. Member
  • ****
  • Posts: 255
  • Country: pt
Re: Getting random gibberish data opens my garage door
« Reply #5 on: October 16, 2020, 11:19:20 AM »
Ahhhh!!! I see. But if I use PROMICUOUS MODE is FALSE, shouldn't the node only receive traffic sent exclusively for it?
« Last Edit: October 16, 2020, 12:54:48 PM by luisr320 »

luisr320

  • Sr. Member
  • ****
  • Posts: 255
  • Country: pt
Re: Getting random gibberish data opens my garage door
« Reply #6 on: October 16, 2020, 12:13:15 PM »
So I guess I should have something like:

Code: [Select]
if (radio.SENDERID = GATEWAYID)
{
if (inData.in_data1 == 1) // If it was a "1", a command was sent from the Gateway to the Output 1 - Garage Door Relay
{
if (inData.in_data2 == 1) // If it was a "1", a command was sent from the Gateway to toggle the relay
{
controlDoor();
}
inData.in_data1 = 0; //Reset the in_data2 Variable content
}
}

Still I can't understand how the garbled data can be passed if spyMode is set to false
« Last Edit: October 18, 2020, 05:09:03 PM by luisr320 »

Felix

  • Administrator
  • Hero Member
  • *****
  • Posts: 6866
  • Country: us
    • LowPowerLab
Re: Getting random gibberish data opens my garage door
« Reply #7 on: October 16, 2020, 12:48:42 PM »
Read my response again for the explanation. PROMISCUOUS doesn't help you in any way to filter bad data.
Instead of checking 1 bit to trigger a OPEN or CLOSE, you need to check something like:

Code: [Select]
if (struct.magicToken == "AverySpecialThatWouldGuaranteeThisIsNotGibberishData")
{ all good, open my garage! }

That is pseudocode and grossly exaggerated, but I think you get the idea of what's needed before you release the heaviest moving object in your home without your presence  ;)

luisr320

  • Sr. Member
  • ****
  • Posts: 255
  • Country: pt
Re: Getting random gibberish data opens my garage door
« Reply #8 on: October 16, 2020, 12:52:33 PM »
AHHHHHHHHHH! OK!!!!! I get it. Thank you.  ;)

luisr320

  • Sr. Member
  • ****
  • Posts: 255
  • Country: pt
Re: Getting random gibberish data opens my garage door
« Reply #9 on: October 18, 2020, 04:43:16 PM »
Problem solved. Thank you Felix.
I introduced a "security token" on the message transmitted to all the gates that must match the token of each node attached to it before checking what is the command sent to it.

I was putting a lot of faith on the Moteino capability to only receive data sent exclusively to it with the radio.reveiveDone() function. As far as security was concerned, I was assuming that the message content was encrypted, and the radio only passed what it decrypted to the board's processor if the destination node number matched its own node ID number.

I guess that is what happens but eventually some garbled data may satisfy the node ID and pass an incorrect command to be processed, as it happened with my garage door and entrance gate.
By inserting a specific combination of characters as a security token it decreases the probability of a random match.

Regarding the encryption, is all the content of the transmitted message encrypted?

luisr320

  • Sr. Member
  • ****
  • Posts: 255
  • Country: pt
Re: Getting random gibberish data opens my garage door
« Reply #10 on: October 18, 2020, 05:31:26 PM »
I have been reading your approach to the rolling code problem on your hackaday entry for the Moteino Framework and found it very interesting (https://hackaday.io/project/2197/logs?sort=newest&page=3):
Quote
I found this article that explains very elegantly how this all works and how the author was able to attack an auto lock using this method, via an inexpensive RTL-SDR device. Moreover, such devices as garage openers and perhaps key fob home door locks work this way. Scary.
To fix this we need to implement something that will require the receiver to be involved in each exchange. In SSL the receiver gives the sender a public key that can only be used to encrypt the message. Then the receiver uses a private key to decrypt that message. So in our case the messages are already AES128 encrypted so the messages contents are not readable to the attacker. We could then ask the receiver for a transactional token, a one time unique set of pseudorandom bytes generated by the receiver that will be used to "salt" the sender message. This technique will ensure the sender will send a different looking message every time *and also* that the receiver knows exactly what the unique expected token is. The attacker cannot spoof the token since they don't know the secret shared AES128 encryption key. One essential assumption is that the sender message will contain the security token in addition to the actual message. The encryption will ensure that 2 messages with the same content but with different security tokens will look completely different as far as modulated RF output.

Since both receiver and sender are involved in each exchange, the receiver should expect the sender to respond very quickly with the encrypted and salted message. A short message takes a few ms to send at 55.5kbps for instance which is the default at the time of writing for the RFM69 library. Now even if an attacker records and replays messages, they are obsolete by the time they are received.
The article referred to is this one: http://spencerwhyte.blogspot.com/2014/03/delay-attack-jam-intercept-and-replay.html.

Your idea, as I understand it, is for the transmitter to send the receiver a request for a random security token and then send the final command with that token in it to the receiver which it will use for a one time only. So even if someone manages to pick up and record the transaction, it will be too late to use it, since the security token should already expire.

I have a question, though: when should the random security token expire? Only after it is used, or after some time expired?

Felix

  • Administrator
  • Hero Member
  • *****
  • Posts: 6866
  • Country: us
    • LowPowerLab
Re: Getting random gibberish data opens my garage door
« Reply #11 on: October 19, 2020, 11:17:55 AM »
Encryption is NOT the same as authentication (or IOW one does not include the other). They are very distinct. So for most security you must have both. You only had encryption.

Rolling code would ensure that the same authentication, even if encrypted, could not be repeated by an attacker. A really sophisticated attacker would be required for this. I would use an expired token, IMO that is sufficient.

luisr320

  • Sr. Member
  • ****
  • Posts: 255
  • Country: pt
Re: Getting random gibberish data opens my garage door
« Reply #12 on: October 22, 2020, 06:44:09 PM »
So I spent some time studying this subject, and I went on and instead of just sending a
Code: [Select]
if (struct.magicToken == 
 Averyspecialstringherethattellsmethisisreallyforthegarageandnotsomethingelselikefakedata") { all good, open my garage! }
I decided to implement an Authentication method, using a MD5 HMAC, or keyed-hash message authentication code.

Mind you that I'm a copy/paste programmer, so take this with a bit of salt (pun intended  :) ).

The idea is simple:

I have two nodes, one is the Gateway and the other is a node that will control something (like the garage, for instance).
So in order to make sure that the garage node only receives a command from the gateway and from nothing else, the gateway has to be authenticated.
To do this, I assigned a common password (or key, if you wish), that is considered private. This key is known by all the nodes. It is a private key. (Not the encryption type of key, but a key, anyway).

Now imagine that the Gateway wants to send a "OPEN THE GARAGE DOOR" to the garage door node.
First it will request a public key from the garage node, one that can be captured by someone listening to this conversation. But this key is nothing more the the "salt" that will scramble the private key when it is sent over the radio and on its own it is useless.

So the Gateway requests a public key to the garage node.
The garage node will fabricate a random number, of 9 digits in this case, using the millis() as a seed to assure the randomness of this number, and sends it to the gateway.

The gateway receives this public key and adds it to the private key, another 9 digit fixed number, and creates a MD5 Digested Hash, a large hexadecimal number, that is always the same size, no matter the dimension of the numbers used to generate it. This MD5 Hash is created by a very simple library created by TZIKIS (https://github.com/tzikis/ArduinoMD5, trough two commands: one to create the Hash, and the other to create the Digested Hash that will be in a HEX format.

Now the Gateway sends this Digested Hash back to the garage node along with the intended command of "OPEN THE GARAGE DOOR".

The node receives this command and the Digested Hash and does the same process on its side: adds the private key with the public key it has already created and runs this trough the library to create the same Digested Hash, which will be compared with the received one.

If they match, the gateway is authenticated and the command is executed. If not, the node will wait a total of 200ms since it created the public key to receive the correct digested hash. After that that public key is expired and a new one must be requested.

All this takes about 30 to 50 ms.

If someone would receive the public key, not knowing what the private key is, the authentication digested hash cannot be recreated. And if the authenticated digested hash sent by the gateway is captured, it is no longer valid, since the node will only use it once.

So, I think this is a great step in the direction of increasing the security of this data transactions, beyond the AES128 encryption  that the radios are already capable of.

I read that MD5 is not very secure compared with SHA256, for instance, but its a first step. I'm starting to look a solution for SHA256, but I'm not sure if the time it takes to create the hash will make it feasible.

Anyway, I leave the codes I use on both nodes here for your appreciation and comment.

In the Gateway Node, the code sends a request to the Garage Node every 5 seconds, for this test.
Below is a screen capture of this transactions.

Gateway node
Code: [Select]
//*****************************
// Homeseer Moteino Gateway
// Ver. 2G 1.0
// Node 20
//*****************************


#include <RFM69.h>
#include <RFM69_ATC.h>
#include <SPIFlash.h>
#include <MD5.h>

#define THISNODEID 20 //unique for each node on same network
#define NETWORKID 100 //the same on all nodes that talk to each other
#define FREQUENCY RF69_433MHZ //Match with the correct radio frequency
#define ENCRYPTKEY "xxxxxxxxxxxxxxxx" //exactly the same 16 characters/bytes on all nodes!
#define ATC_RSSI -80
#define ACK_TIME 500 // max # of ms to wait for an ack
#define SERIAL_BAUD 115200//Make sure the script comport_open1.txt has the same baud rate
#define FLASH_SS 8 // and FLASH SS on D8
#define SPY false
#define PRIVATEKEY 189578193

//Define Hardware
SPIFlash flash(FLASH_SS, 0xEF30); //EF30 for windbond 4mbit flash
RFM69_ATC radio;


unsigned long last_millis = 0;
unsigned long timeNow = 0;
uint32_t authToken = 0;
uint32_t hash;
String authTokenSt;
char buf[10];

//Inbound Data Struct
typedef struct
{
uint8_t in_data0; // Data 0 - Incoming Node ID
uint8_t in_data1; // Data 1 - 3: Auth token incoming
uint32_t in_data2;  // Data 2 - NIL
uint32_t in_data3;  // Data 3 - Security Token
}in_Payload;
in_Payload inData;

//Outboud Data Struct
typedef struct
{
uint8_t out_data0;  // Data 0 - Destination Node ID
uint8_t out_data1; // Data 1 - 3: Request a Auth Token
uint32_t out_data2; // Data 2 - NIL
char out_data3[33]; // Data 3 - Security Token
}out_Payload;
out_Payload outData;


void setup()
{
//Initialize the Serial port at the specified baud rate
Serial.begin(SERIAL_BAUD);

pinMode(LED_BUILTIN, OUTPUT);

//Initialize the radio
radio.initialize(FREQUENCY, THISNODEID, NETWORKID);//Initialize the radio
radio.setHighPower();//Set the radio to high power
radio.encrypt(ENCRYPTKEY);//Turn the radio Encryption ON
radio.spyMode(SPY);//Turn the radio Promiscuous mode according to what in the promiscuousMode variable
radio.enableAutoPower(ATC_RSSI);

//Initialize FLASH
flash.initialize();

Serial.println("Gateway radio receiving in 433Mhz Mhz...");
Serial.println();
}


void loop()
{

//Every 5 seconds send
if (millis() > last_millis + 5000) //Time to send a new open/close instructions
{
last_millis = millis();

//Struct components to request the Salt
outData.out_data0 = 21;
outData.out_data1 = 3;
outData.out_data2 = 0;
strcpy(outData.out_data3, "");

timeNow = millis(); //Record the time to calculate how much time took the transaction
Serial.println();
Serial.println("Requesting a Auth Token...");
sendData(1);//Request a authentication token

}

if (radio.receiveDone())//If some packet was received by the radio, wait for all its contents to come trough
{
inData = *(in_Payload*)radio.DATA; //assume radio.DATA actually contains our struct and not something else

//If an ACK (acknowledge) was requested by the data packet transmitter radio, send the ACK back to it and display "ACK sent" on the serial monitor
if (radio.ACKRequested())
{
radio.sendACK();
Serial.println("[ACK sent!]");
}


if (inData.in_data1 == 3) //If the data is a Authentication Token
{

authToken = inData.in_data3 + PRIVATEKEY;
authTokenSt = String (authToken);
authTokenSt.toCharArray(buf, 11);

//generate the MD5 hash for our string
unsigned char* hash = MD5::make_hash(buf);
char *md5str = MD5::make_digest(hash, 16);
free(hash);

Serial.print("Auth Token received:");
Serial.println(inData.in_data3);
Serial.print("Elapsed Time:");
Serial.println(millis()-timeNow);// Display how much time it took for the Token to be available
Serial.print("DIGESTED HASH:");
Serial.println(md5str);
outData.out_data0 = radio.SENDERID;
outData.out_data1 = 1;
outData.out_data2 = 1;
strcpy(outData.out_data3, md5str);
free(md5str);
sendData(2);
Blink(50);
}



}

}

void sendData(int type)
{

if (radio.sendWithRetry(outData.out_data0, (const void*)(&outData), sizeof(outData), 5, ACK_TIME))//Send the data to the destinationNode via radio and wait for the ACK to be received
{
if (type == 1)
{
Serial.println("Token request sent and ACK received");
}
else if (type == 2)
{
Serial.println("Command sent and ACK received");
}
}

outData.out_data0 = 0;
outData.out_data1 = 0;
outData.out_data2 = 0;
strcpy(outData.out_data3, "");
}

void Blink(int DELAY_MS)//The led blinking function
{
digitalWrite(LED_BUILTIN, HIGH);
delay(DELAY_MS);
digitalWrite(LED_BUILTIN, LOW);
}

Garage node:
Code: [Select]
//*****************************
// Homeseer Moteino Gateway
// Ver. 2G 1.0
// Node 21
//*****************************


#include <RFM69.h>
#include <RFM69_ATC.h>
#include <SPIFlash.h>
#include <MD5.h>

#define THISNODEID 21 //unique for each node on same network
#define NETWORKID 100 //the same on all nodes that talk to each other
#define FREQUENCY RF69_433MHZ //Match with the correct radio frequency
#define ENCRYPTKEY "xxxxxxxxxxxxxxxx" //exactly the same 16 characters/bytes on all nodes!
#define ATC_RSSI -80
#define ACK_TIME 500 // max # of ms to wait for an ack
#define SERIAL_BAUD 115200//Make sure the script comport_open1.txt has the same baud rate
#define FLASH_SS 8 // and FLASH SS on D8
#define SPY false
#define PRIVATEKEY 189578193
#define TIMEOUT 200 //Auth Token timeout time in ms

//Define Hardware
SPIFlash flash(FLASH_SS, 0xEF30); //EF30 for windbond 4mbit flash
RFM69_ATC radio;

unsigned long last_millis = 0;
unsigned long int timeNow = 0;
uint32_t authToken = 0;
bool authTokenSent = false;
String authTokenSt;
char buf[10];

//Inbound Data Struct
typedef struct
{
uint8_t in_data0; // Data 0 - Incoming Node ID
uint8_t in_data1; // Data 1 - 3: Auth token incoming
uint32_t in_data2;  // Data 2 - Auth Token
char in_data3[33];  // Data 3 - Security Token
}in_Payload;
in_Payload inData;

//Outboud Data Struct
typedef struct
{
uint8_t out_data0;  // Data 0 - Sender Node ID
uint8_t out_data1; // Data 1 - 3: Request a Auth Token
uint32_t out_data2; // Data 2 - NIL
uint32_t out_data3; // Data 3 - Security Token
}out_Payload;
out_Payload outData;

void setup()
{
//Initialize the Serial port at the specified baud rate
Serial.begin(SERIAL_BAUD);

pinMode(LED_BUILTIN, OUTPUT);

//Initialize the radio
radio.initialize(FREQUENCY, THISNODEID, NETWORKID);//Initialize the radio
radio.setHighPower();//Set the radio to high power
radio.encrypt(ENCRYPTKEY);//Turn the radio Encryption ON
radio.spyMode(SPY);//Turn the radio Promiscuous mode according to what in the promiscuousMode variable
radio.enableAutoPower(ATC_RSSI);

//Initialize FLASH
flash.initialize();

Serial.println("Garage Node receiving in 433Mhz Mhz...");
Serial.println();

}


void loop()
{
if (radio.receiveDone())//If some packet was received by the radio, wait for all its contents to come trough
{
inData = *(in_Payload*)radio.DATA; //assume radio.DATA actually contains our struct and not something else

//If an ACK (acknowledge) was requested by the data packet transmitter radio, send the ACK back to it and display "ACK sent" on the serial monitor
if (radio.ACKRequested())
{
radio.sendACK();
Serial.println("[ACK sent!]");
}

// If the Auth Token was requested
if (inData.in_data1 == 3)
{
randomSeed(millis());
authToken = random(999999999);
Serial.println();
Serial.println("New request for a Auth Token received.");
Serial.print("New Auth Token:");
Serial.println(authToken);
outData.out_data0 = radio.SENDERID;
outData.out_data1 = 3;
outData.out_data2 = 0;
outData.out_data3 = authToken;
timeNow = millis();
sendData();
}
else if (inData.in_data1 == 1 && millis() - timeNow < TIMEOUT)
{

authToken = authToken + PRIVATEKEY; //Add the Salt to the Auth Token
authTokenSt = String (authToken); //Make the Auth Token a String
authTokenSt.toCharArray(buf, 11); //Convert the String into an array of chars

//generate the MD5 hash for our string
unsigned char* hash = MD5::make_hash(buf);
char *md5str = MD5::make_digest(hash, 16);//Convert the hash into HEX

Serial.print("Generated DIGESTED HASH:");
Serial.println(inData.in_data3);
Serial.print("Received DIGESTED HASH:");
Serial.println(md5str);
free(hash);// Release the hash memory blocks
if (strcmp(inData.in_data3, md5str) == 0)// See if the locally Digested Hash is equal to the remote Digested Hash received
{

free(md5str);//Release the md5str memory block
Serial.println("Command received!");
Serial.print("Elapsed Time:");
Serial.println(millis() - timeNow);// Show how much time passed sinced the requst for a token until a command was received
Blink(50);
authToken = 0;//Reset the Auth Token
}
}
else if (inData.in_data0 == 1 && millis() - timeNow >= TIMEOUT)
{
Serial.println("Auth Token Expired!");
}
}
}

void sendData()
{

if (radio.sendWithRetry(outData.out_data0, (const void*)(&outData), sizeof(outData), 5, ACK_TIME))//Send the data to the destinationNode via radio and wait for the ACK to be received
{
Serial.println("Auth Token sent and ACK received");
}


outData.out_data0 = 0;
outData.out_data1 = 0;
outData.out_data2 = 0;
outData.out_data3 = 0;
}

void Blink(int DELAY_MS)//The led blinking function
{
digitalWrite(LED_BUILTIN, HIGH);
delay(DELAY_MS);
digitalWrite(LED_BUILTIN, LOW);
}
« Last Edit: October 24, 2020, 05:19:27 AM by luisr320 »

luisr320

  • Sr. Member
  • ****
  • Posts: 255
  • Country: pt
Re: Random gibberish data opens my garage door
« Reply #13 on: October 23, 2020, 07:00:49 PM »
I have managed to create the authentication hash with the strength of SHA256. It takes 21ms to create a digested hash from the concatenation of a PRIVATE key and the PUBLIC KEY (the "salt").
So I guess that makes SHA256 hash authentication a viable solution.
Tomorrow I will create a two way transaction between two Moteinos and see how much time it takes in total for a full transaction to be completed (request PUBLIC KEY, create a one time PUBLIC KEY, send the PUBLIC KEY, concatenate the public key with the PRIVATE KEY, create a digested hash, send a command with the digested hash for authentication, receive the digested hash, create the same digested hash, compare both digested to approve the authentication and finally accept and execute the sent command.
On the MD5 it took about 30 to 50ms in total. Lets see if the difference is significative.

For the SHA256 authentication I'm using the Crypto library for the Arduino, by Dr. Brandon Wiley, available here: https://github.com/OperatorFoundation/Crypto
Just install the library as part of the Arduino libraries. I'm only using the #include <SHA256.h>, which is a part of the Crypto library.

The proof of concept code is this:
Code: [Select]
#include <SHA256.h>

#define PRIVATEKEY "myprivatekey"
#define HASH_SIZE 32

char data[70] = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq";

SHA256 sha256;
Hash *hash = &sha256;

char *out = (char*)malloc(33);

void setup()
{
int time = millis();
uint8_t value[HASH_SIZE];

Serial.begin(115200);

strcat(data, PRIVATEKEY);

// do the hash
hash->reset();
hash->update(data, strlen(data));
hash->finalize(value, sizeof(value));

Serial.print("Elapsed time:");
Serial.println(millis() - time);

int count = sizeof(value) / sizeof(value[0]);
for (int i = 0; i < count; i++)
snprintf(&(out[i * 2]), 16 * 2, "%02x", (unsigned int)value[i]);

Serial.println("The digested hash is:");
Serial.print(out);

}

void loop(){}

To check if the result is correct, use this SHA hash generator after concatenating your PUBLIC and PRIVATE KEYS:
https://passwordsgenerator.net/sha256-hash-generator/
« Last Edit: October 24, 2020, 05:18:03 AM by luisr320 »

luisr320

  • Sr. Member
  • ****
  • Posts: 255
  • Country: pt
Re: Random gibberish data opens my garage door
« Reply #14 on: October 24, 2020, 06:26:23 AM »
I need some help, please!

I'm trying to compile my sketch with the SHA256.h from the Crypto library, but I get this error:
Code: [Select]
LowPower.cpp.o (symbol from plugin)*: In function LowPowerClass::idle(period_t, adc_t, timer2_t, timer1_t, timer0_t, spi_t, usart0_t, twi_t)
(.text+0x0)*: multiple definition of __vector_6
RNG.cpp.o (symbol from plugin)*: (.text+0x0): first defined here

Error linking for board Moteino(-USB)
Build failed for project 'Moteino Auth Gateway Node SHA256 [20]'
 
collect2.exe*: error: ld returned 1 exit status

I can get rid of it if I comment this in the LowPower.cpp (freshly downloaded from here: https://github.com/LowPowerLab/LowPower
Code: [Select]
ISR (WDT_vect)
{
// WDIE & WDIF is cleared in hardware upon entering this ISR
wdt_disable();
}

First of, why is the LowPower library part of the compilation?
And what can I do to avoid this error without hacking the LowPower library?
« Last Edit: October 24, 2020, 08:04:34 AM by luisr320 »