1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
| #include <avr/eeprom.h>
#include <LiquidCrystal.h>
#include <VirtualWire.h>
#include <AESLib.h>
const int SAFEWINDOW = 50;
long lastcount;
char *controller;
unsigned char msg[16];
uint8_t key[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
#pragma pack 1
//uint8_t key[16];
typedef struct {
unsigned long serial;
long counter;
char command;
long extra;
int code;
char termin;
} PackedData;
typedef struct {
long counter;
unsigned long serial;
uint8_t skey[16];
} Settings;
typedef struct {
long counter;
unsigned long serial;
int magic;
} PairingData;
// select the pins used on the LCD panel
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
Settings StoredSettings;
int errorcounter=0;
int adc_key_in;
// read the buttons
int read_LCD_buttons()
{
adc_key_in = analogRead(0); // read the value from the sensor
if (adc_key_in < 50) return 1;// btnRIGHT;
if (adc_key_in < 250) return 2;// btnUP;
if (adc_key_in < 450) return 3;//btnDOWN;
if (adc_key_in < 650) return 4;//btnLEFT;
if (adc_key_in < 850) return 5;//btnSELECT;
return 0;//btnNONE; // when all others fail, return this...
}
void setup()
{
Serial.begin(9600);
lcd.begin(16, 2); // start the library
//memcpy (&StoredSettings.skey, &key[0], sizeof(key));
//eeprom_write_block((const void*)&StoredSettings, (void*)0, sizeof(StoredSettings));
delay(1000);
eeprom_read_block((void*)&StoredSettings, (void*)0, sizeof(StoredSettings));
//memcpy (&key[0],&StoredSettings.skey, sizeof(key));
Serial.print("EEPROMKEY:");
for(int k=0;k<16;k++)
Serial.print(StoredSettings.skey[k],HEX);
Serial.println("");
lcd.setCursor(0,1);
lcd.print(StoredSettings.counter); // print a simple message
lcd.setCursor(0,0);
lcd.print("SN: "); // print a simple message
lcd.setCursor(4,0);
lcd.print(StoredSettings.serial);
pinMode(2, OUTPUT);
vw_set_ptt_inverted(true); // Required for DR3100
vw_set_rx_pin(A5);
vw_setup(4000); // Bits per sec
vw_rx_start();
}
void pairing(void)
{
uint8_t buf[VW_MAX_MESSAGE_LEN];
uint8_t buflen = VW_MAX_MESSAGE_LEN;
lcd.setCursor(0,0);
lcd.print(" Pairing! ");
long start =millis();
int ledState = LOW;
long previousMillis = 0; // will store last time LED was updated
do{
//do pairing process and store serial and counter
if (vw_get_message(buf, &buflen)) // Non-blocking
{
lcd.clear();
PairingData * pairData = (PairingData *)buf;
if(pairData->magic==555){
lcd.clear();
lcd.setCursor(0,0);
lcd.print(pairData->serial);
lcd.setCursor(0,1);
lcd.print(pairData->counter);
StoredSettings.counter=pairData->counter;
StoredSettings.serial=pairData->serial;
eeprom_write_block((const void*)&StoredSettings, (void*)0, sizeof(StoredSettings));
delay(2000);
lcd.clear();
lcd.setCursor(0,0);
lcd.print(" Done!");
lcd.setCursor(0,1);
lcd.print(" Synced");
delay(2000);
break;
}
else
{
lcd.clear();
lcd.setCursor(0,0);
lcd.print(" Error!");
lcd.setCursor(0,1);
lcd.print("Try again!");
}
}
delay(100);
//end of pairing
unsigned long currentMillis = millis();
if(currentMillis - previousMillis > 100) {
previousMillis = currentMillis;
if (ledState == LOW)
ledState = HIGH;
else
ledState = LOW;
digitalWrite(2, ledState);
}
} while ((millis()-start)<10000); //for 5 seconds
}
void loop()
{
if (read_LCD_buttons()!=0)
{
pairing();
}
uint8_t buf[VW_MAX_MESSAGE_LEN];
uint8_t buflen = VW_MAX_MESSAGE_LEN;
if (vw_get_message(buf, &buflen)) // Non-blocking
{
Serial.print("Encrypted:");
memcpy(msg, buf, buflen);
for(int k=0;k<16;k++)
Serial.print(msg[k],HEX);
Serial.println("");
Serial.print("KEY:");
for(int k=0;k<buflen;k++)
Serial.print(key[k],HEX);
Serial.println("");
aes128_dec_single(key, msg);
PackedData * sdData = (PackedData *)msg;
Serial.print("Test Decrypt:");
Serial.print(sdData->serial);
Serial.println("");
lcd.clear();
lcd.setCursor(0,0);
lcd.print(sdData->serial);
lcd.setCursor(0,1);
lcd.print(sdData->counter);
digitalWrite(2,1); //for red led
long currentcounter;
if(sdData->code==555){
lcd.clear();
lcd.setCursor(0,0);
lcd.print(sdData->serial);
lcd.setCursor(0,1);
lcd.print(sdData->counter);
//do the job if the counter is with in safety window
currentcounter= sdData->counter;
if((currentcounter-StoredSettings.counter)<SAFEWINDOW && (currentcounter-StoredSettings.counter)>0)
{
lcd.setCursor(0,0);
lcd.print("Access Granted!");
StoredSettings.counter=sdData->counter;
eeprom_write_block((const void*)&StoredSettings, (void*)0, sizeof(StoredSettings));
//do the stuff here for eg running the servo or door lock motor
char command = sdData->command;
switch (command) {
case 'a':
lcd.clear();
lcd.setCursor(0,0);
lcd.print("OPENING DOOR");
//do the servo here
break;
case 'b':
lcd.clear();
lcd.setCursor(0,0);
lcd.print("CLOSING DOOR");
//do the servo here
break;
case 'c':
lcd.clear();
lcd.setCursor(0,0);
lcd.print("NEXT THING");
//do the servo here
break; }
}
else
{
errorcounter++;
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Needs Pairing!");
lcd.setCursor(8,1);
lcd.print(errorcounter);
lcd.setCursor(12,1);
lcd.print(StoredSettings.counter);
lcd.setCursor(0,1);
lcd.print(currentcounter);
}
}
else
{
lcd.clear();
lcd.setCursor(0,0);
lcd.setCursor(0,0);
lcd.print(" Error!");
lcd.setCursor(0,1);
lcd.print(" Wrong Key??");
}
delay(100);
digitalWrite(2,0);
}
delay(100); //just here to slow down a bit
}
|
Hello thanks for your post.
ReplyDeleteIt's the first post i found who someone use rf module with encrypt.
I think it's important part to use encrypt when i control critical fonction like Relay or open door garage.
It's like people use wifi without encryption. It's worse, because people use relay it's very dangerous.
I would like know, how much time the encrypt/decrypt do ?
Thanks by advance.
Sorry for my english i'm french
Thanks for the comments. Encryption is fast and there is no noticeable delay for switching a relay on and off. Adding a realtime clock and a time stamp will further improves security. Regards
DeleteHi, Do you have any example about what and where you mean "realtime clock and a time stamp". Thanks anyway. Great post.
DeleteThanks very much for your great blog,
ReplyDeletei have 2 question,
1- the pairing between transmetter and receiver it done one time at the rgistration or evry transmission data the transmitter must do the pairing
2- the registration in eeprom can be in other place (not a memory) because the number of writed memory in arduino is limitted.
thanks.
Thanks for the comments, and thw eeprom write is for pairing which is only one time. You are right about the single memory location write, as i never optimised it beyond a proof of concept/was a quick weekend project :)
DeleteHey, if I understand the purpose of the counter/key_counter thing, I'm not sure it works in you code... Seems that the key_counter is incremented at each loop on the transmitter side, whereas it is stored at initial pairing on the receiver side >> how the devil can it remains inside the safewindow?
ReplyDeleteThanks for the question and visit,
DeleteAfter successful message, it updates the counter in the eeprom. See receiver code
eeprom_write_block((const void*)&StoredSettings, (void*)0, sizeof(StoredSettings));
Regards
where is the part for rolling code?
ReplyDeletecounter/key_counter and the corresponding encrypted message send to the receiver will be changing every time to avoid replay attacks. There is a missing link here (using time) which can be used to block-capture-replay attacks
DeleteEvery time i communicate with the receiver EEPROM will be written. If I need a solution (mesh network) where each node sending message every 2 second will it be good to re write EEPROM for very communication. If I don't want to re write EEPROM and still want to have rolling code is it possible
ReplyDeleteYou can just encrypt and send your message. The problem is replay attacks. Instead of rolling counter, you can use a hash of (time stamp+ some secret string). There are several ways with varying degree of security. You can also keep the key in ram and attain the same results (need to use a battery backup) Goodluck with your projects!
DeleteRiya: thanks for the post! Could you comment on wiring for NRF24L01's 8-pin module? Is there a need to change the code?
ReplyDeleteYou are welcome!
DeleteNRF24l01 is a bit different and much better in some areas. It allows both transmit and receive and hence you could have a challenge-response strategy and better & secure key exchange. So the same code wont work and we need a diffrent one as the rf modules are totally different. The basic wiring of NRF module can be seen at:
http://www.riyas.org/2013/12/working-quick-start-guide-for-nrf24l01.html
A simple pseudo code is given below (assuming the key is already known to both parties otherwise add a delfie hellman key exchange a head of this)
1) The key will send a hellow message to car
2) The car replies with a unique code
3) The key will sign the unique code with its private key+command and sends back
4)The car will verify the signature and if it passes , it will execute the command
Best
Thanks again for the pseudo code... I assume it is for challenge-response authentication. But if I stick to the AES/one-way rolling code logic in your code above, besides loading the NRF library, anything in particular I should change?
DeleteYes, it should work. Just pass the data struct to nrf instead of virtualwire.
DeleteThanks for the NRF24L01 examples, Riya! I am able to build them and see the responses. A couple of observations: first I had to change baud rate to 9600 in sketch1 otherwise the terminal characters would turn into gibberish after a few normal outputs. Second, around 50% of the responses failed even though the sketch2 module is only 3 meters away. As I am trying to build a rolling code remote based on this article. Would these errors be a problem in this project once I adapt it for NRF24L01?
DeleteNrf modules are small but a lot of config options are there. Try using a clean power (battery) as they are sensitive to noice. Use a clear channel (see the nrf scanner). Make sure the antenna on the pcb is not close to the metal cases or other stuffs. A proper communication link is always needed so that the codes wont be missed (beyond the safety window). I used to get at least 6-10 mtrs range with those inexpensive modules. Good luck with your projects
DeleteNrf modules are small but a lot of config options are there. Try using a clean power (battery) as they are sensitive to noice. Use a clear channel (see the nrf scanner). Make sure the antenna on the pcb is not close to the metal cases or other stuffs. A proper communication link is always needed so that the codes wont be missed (beyond the safety window). I used to get at least 6-10 mtrs range with those inexpensive modules. Good luck with your projects
Deletehi,
ReplyDeletethank you for your code,
So i don't have lcd for receiver so can you explain me how i can do to put the receiver in paring mode.
I try to pare the two arduinos without success
thank you
Add seral.print for all lcd messages and use a serial monitor.
DeleteCould you explain how do the parring ? i don't undersant when i have to disconnect 3,3v and when i have to push button in lcd keypad. Thank you
DeletePress any button on the receiver lcd. It will go to pairing mode and then connect pin 8 on the transmitter to ground.
DeleteRiya,
ReplyDeleteCould you explain the wiring between the LCD module and Arduino? I understand Arduino pins used (8, 9, 4, 5, 6, 7) but how are they connected to the LCD module? How is the module powered?
Thanks!
My question is if I have a Arduino Mini, do I have to connect all pins just like how the LCD module is connected to the UNO, or only a few?
DeleteLCD needs only 6 pins LiquidCrystal(rs, enable, d4, d5, d6, d7) , and you can use any of the 6 pins on the nano, but make sure to set appropriate pin number in the call to LiquidCrystal(x,x,x,x..) function. In addition LCD needs a power (vcc) and ground connection. Additionally, wire a 10k pot to +5V and GND, with it's wiper (output) to LCD screens VO pin (pin3). A 220 ohm resistor is used to power the backlight of the display, usually on pin 15 and 16 of the LCD connector. It all depends on your lcd. If you use an i2c lcd, you need only 4 connections. See : https://www.arduino.cc/en/Tutorial/HelloWorld
DeleteHi RiYa, thanks for your code!
ReplyDeleteI have a remote with two channels that use rolling code. One channel is used to open a door gate. I would use other channel to open another door. Is it possible accomplish it using Arduino instead to buy manufacturer's original receiver?
It is a reverse engineering problem. It may or may not be possible based on the type of remote, pairing algorithm, key exchange etc. Possible routes are using an rtlsdr to analyse all traffics during a pairing process. A secure remote should use an encryption key during pairing ( the one i shown above is missing it )
DeleteCan you tell me how to make the sender remember key_counter?
ReplyDeleteI am not so familiar with eprom write.
My problem is that if I disconnect sender from power I will need new pairing because the sender starts with the SAME counter every reboot, while the receiver remembers the last counter.
In short: receiver writes counter to eprom, sender not.
besides that your code works great, I just added a second button for sending, so that it doesn't always send in a loop.
Also it helps to use the pinmode INPUT_PULLUP (and button to GND):
pinMode(PAIR_BUTTON,INPUT_PULLUP); //switch for pairing
pinMode(SEND_BUTTON,INPUT_PULLUP); //switch for sending
otherwise (in my case) the sender would stick to pairing loop.
I would highly appreciate a sender code that also writes its key_counter to eprom. It would be ok for me to use same memory adress just as in receiver(discussed above), i need just a short hint how to save key_counter.
In return I will publish a code (and hardware setup) for a two-winged garage door with two independent motors, angle controlled feedback for each door, RGB-LED-stripes on the doors for signalling and servo control for moving a mechanical lever for a duplex parking lot (behind my door). The sender will be a one-button-controlled Arduino, connected to a button in the dashboard (using park distance control button) including feedback with LED. This coding is already done and tested, so please help with the eeprom_write in Sender :-)
Best regards from Germany, Vince
Thanks Vince for the feedback and New year greetings! Seems a long time since i was experimenting with this and found some issues as you indicated. The idea while making the post was just to share the concept and the key counter in the transmitter is buggy as it is never saved and always resets in the loop (OMG!)
DeleteUpdated the transmitter code above ( i haven't tested it, all my toys are packed up in the storage room due to busy life)
So the changes are mainly eeprom read and write, also removed the looping in the transmit code. Just add a button to pin 9 and toggle it to transmit.
Best regards
Riyas
how download avr/eeprom.h
ReplyDeletemodule name with lcd please? thank you very much
ReplyDeletehello my friend
ReplyDeleteplease help me
I have error code
'aes128_dec_single' was not declared in this scope
Thanks for the code
ReplyDeleteHow to add Real time clock for more security?
Hello there. Great work ! Is it possible to run that code on esp32 ?
ReplyDelete