В посленее время мир «умного дома» стал более близок для начинающих энтузиастов, благодаря наличию большого количества аппаратных решений с низким порогом вхождения (речь конечно про платформу Arduino и немалому набору модулей/сенсоров для неё) и уже готовых библиотек и фреймворков для работы с ними. Как правило они имеют настройки по-умолчанию (мак-адреса, каналы и т.п.), которые нетронутыми и остаются в руках этих самых начинающих… Например фреймворк MySensors, упоминавщийся не так давно на Хабре, имеет файл настроек «MyConfig.h», который многие (мой незадачливый сосед в частности) даже не правят.
С одной стороны мне все равно, что в многоэтажном доме кто-то сможет «подслушать» температуру на кухне (а то и позвонить с вопросом «Что готовишь?»), но с другой стороны не хотелось бы, чтобы кто-то смог (хоть бы и теоретически) управлять силовыми нагрузками (включить любимую кофе-машину, например). Хочется быть чуть более уверенным, что комманда исходит от моего управляющего устройства, а не подставного (в криптографии, это известно как «проверка подлинности»).
Реализовать такой подход сравнительно просто…
Схема взаимодействия управляющего и исполнительного устройства построена на «подписи» случайной фразы внутренним ключом, известным обоим устройствам. Наглядней это можно продемонстрировать так:

Реализовать можно с помощью трех функций. Одна для генерации случайной фразы:
Вторая собственно создание MD5-хеша (MD5 library for the Arduino):
Третья для сверки «подписанной» фразы инициатора с «самоподписанной» фразой исполнителя:
Такой подход можно исполнять как в своих “велосипедах», так и в других фреймворках (таких как MySensors) и дает дополнительный плюсик к безопасности Вашего Интернета вещей.
Кстати, аутентификация будет в MySensors, на текущий момент она реализована в development-версии.
P.S.: Если будет интересно сообществу — сделаю update заметки на примере того же MySensors с примерами листингов для Serial Gateway и Relay…
С одной стороны мне все равно, что в многоэтажном доме кто-то сможет «подслушать» температуру на кухне (а то и позвонить с вопросом «Что готовишь?»), но с другой стороны не хотелось бы, чтобы кто-то смог (хоть бы и теоретически) управлять силовыми нагрузками (включить любимую кофе-машину, например). Хочется быть чуть более уверенным, что комманда исходит от моего управляющего устройства, а не подставного (в криптографии, это известно как «проверка подлинности»).
Реализовать такой подход сравнительно просто…
Схема взаимодействия управляющего и исполнительного устройства построена на «подписи» случайной фразы внутренним ключом, известным обоим устройствам. Наглядней это можно продемонстрировать так:

Реализовать можно с помощью трех функций. Одна для генерации случайной фразы:
byte alphabet[] = { 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a }; // a, b, c, d, etc
void getPhrase(byte *phrase) {
for (int i = 0; i < 8; i++) {
phrase[i] = alphabet[random(0, 26)];
phrase[i + 1] = '\0';
}
}
Вторая собственно создание MD5-хеша (MD5 library for the Arduino):
#include <MD5.h>
byte sessionPhrase[9];
byte salt[] = { 0x4c, 0x39, 0x78, 0x36, 0x73, 0x4c, 0x39, 0x78 }; // L9x6sL9x - внутренний ключ
void signPhrase(byte *phrase, char *signedPhrase) {
byte localPhrase[17];
for (int i = 0; i < 16; i++) {
if (i < 8) localPhrase[i] = salt[i];
else localPhrase[i] = sessionPhrase[i - 8];
localPhrase[i + 1] = '\0';
}
unsigned char* hash = MD5::make_hash((char *)localPhrase);
char *md5str = MD5::make_digest(hash, 16);
for (int i = 0; i < 16; i++) {
signedPhrase[i] = md5str[i];
signedPhrase[i + 1] = '\0';
}
free(hash);
free(md5str);
}
Третья для сверки «подписанной» фразы инициатора с «самоподписанной» фразой исполнителя:
bool compareSignedPhrases(char *signedPhrase) {
if (strcmp(signedPhrase, (char *)sessionMD5Phrase) == 0) {
return true;
}
return false;
}
Краткий код для проверки работы генерации фразы, её подписывания и сравнения
#include <MySensor.h>
#include <SPI.h>
#include <MD5.h>
#define DEBUG 1
byte sessionPhrase[9];
char sessionMD5Phrase[17];
byte salt[] = { 0x4c, 0x39, 0x78, 0x36, 0x73, 0x4c, 0x39, 0x78 }; // L9x6sL9x — внутренний ключ
byte alphabet[] = { 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a }; // a, b, c, d, etc
void setup() {
Serial.begin(115200);
}
void loop() {
/* Генерируем случайную фразу */
getPhrase(sessionPhrase);
signPhrase(sessionPhrase, sessionMD5Phrase); // Запоминаем для дальнейшего сравнения
/* Подписываем */
char signedPhrase[17];
signPhrase(sessionPhrase, signedPhrase);
#ifdef DEBUG
Serial.print(«signedPhrase: „);
Serial.println((char *)signedPhrase);
Serial.print(“copmpare: „);
Serial.println(compareSignedPhrases(signedPhrase));
Serial.println(“»);
#endif
delay(1000);
}
void getPhrase(byte *phrase) {
for (int i = 0; i < 8; i++) {
phrase[i] = alphabet[random(0, 26)];
phrase[i + 1] = '\0';
}
#ifdef DEBUG
Serial.print(«sessionPhrase: „);
Serial.println((char *)sessionPhrase);
#endif
}
void signPhrase(byte *phrase, char *signedPhrase) {
byte catPhrase[17];
for (int i = 0; i < 16; i++) {
if (i < 8) catPhrase[i] = salt[i];
else catPhrase[i] = sessionPhrase[i — 8];
catPhrase[i + 1] = '\0';
}
#ifdef DEBUG
Serial.print(“catPhrase: „);
Serial.println((char *)catPhrase);
#endif
unsigned char* hash = MD5::make_hash((char *)catPhrase);
char *md5str = MD5::make_digest(hash, 16);
for (int i = 0; i < 16; i++) {
signedPhrase[i] = md5str[i];
signedPhrase[i + 1] = '\0';
}
free(hash);
free(md5str);
}
bool compareSignedPhrases(char *signedPhrase) {
if (strcmp(signedPhrase, (char *)sessionMD5Phrase) == 0) {
return true;
}
return false;
}
#include <SPI.h>
#include <MD5.h>
#define DEBUG 1
byte sessionPhrase[9];
char sessionMD5Phrase[17];
byte salt[] = { 0x4c, 0x39, 0x78, 0x36, 0x73, 0x4c, 0x39, 0x78 }; // L9x6sL9x — внутренний ключ
byte alphabet[] = { 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a }; // a, b, c, d, etc
void setup() {
Serial.begin(115200);
}
void loop() {
/* Генерируем случайную фразу */
getPhrase(sessionPhrase);
signPhrase(sessionPhrase, sessionMD5Phrase); // Запоминаем для дальнейшего сравнения
/* Подписываем */
char signedPhrase[17];
signPhrase(sessionPhrase, signedPhrase);
#ifdef DEBUG
Serial.print(«signedPhrase: „);
Serial.println((char *)signedPhrase);
Serial.print(“copmpare: „);
Serial.println(compareSignedPhrases(signedPhrase));
Serial.println(“»);
#endif
delay(1000);
}
void getPhrase(byte *phrase) {
for (int i = 0; i < 8; i++) {
phrase[i] = alphabet[random(0, 26)];
phrase[i + 1] = '\0';
}
#ifdef DEBUG
Serial.print(«sessionPhrase: „);
Serial.println((char *)sessionPhrase);
#endif
}
void signPhrase(byte *phrase, char *signedPhrase) {
byte catPhrase[17];
for (int i = 0; i < 16; i++) {
if (i < 8) catPhrase[i] = salt[i];
else catPhrase[i] = sessionPhrase[i — 8];
catPhrase[i + 1] = '\0';
}
#ifdef DEBUG
Serial.print(“catPhrase: „);
Serial.println((char *)catPhrase);
#endif
unsigned char* hash = MD5::make_hash((char *)catPhrase);
char *md5str = MD5::make_digest(hash, 16);
for (int i = 0; i < 16; i++) {
signedPhrase[i] = md5str[i];
signedPhrase[i + 1] = '\0';
}
free(hash);
free(md5str);
}
bool compareSignedPhrases(char *signedPhrase) {
if (strcmp(signedPhrase, (char *)sessionMD5Phrase) == 0) {
return true;
}
return false;
}
Такой подход можно исполнять как в своих “велосипедах», так и в других фреймворках (таких как MySensors) и дает дополнительный плюсик к безопасности Вашего Интернета вещей.
Кстати, аутентификация будет в MySensors, на текущий момент она реализована в development-версии.
P.S.: Если будет интересно сообществу — сделаю update заметки на примере того же MySensors с примерами листингов для Serial Gateway и Relay…