Делаем свой «умный дом» чуть более безопасным
В посленее время мир «умного дома» стал более близок для начинающих энтузиастов, благодаря наличию большого количества аппаратных решений с низким порогом вхождения (речь конечно про платформу Arduino и немалому набору модулей/сенсоров для неё) и уже готовых библиотек и фреймворков для работы с ними. Как правило они имеют настройки по-умолчанию (мак-адреса, каналы и т.п.), которые нетронутыми и остаются в руках этих самых начинающих… Например фреймворк MySensors, упоминавщийся не так давно на Хабре, имеет файл настроек «MyConfig.h», который многие (мой незадачливый сосед в частности) даже не правят.С одной стороны мне все равно, что в многоэтажном доме кто-то сможет «подслушать» температуру на кухне (а то и позвонить с вопросом «Что готовишь?»), но с другой стороны не хотелось бы, чтобы кто-то смог (хоть бы и теоретически) управлять силовыми нагрузками (включить любимую кофе-машину, например). Хочется быть чуть более уверенным, что комманда исходит от моего управляющего устройства, а не подставного (в криптографии, это известно как «проверка подлинности»).
Реализовать такой подход сравнительно просто…Схема взаимодействия управляющего и исполнительного устройства построена на «подписи» случайной фразы внутренним ключом, известным обоим устройствам. Наглядней это можно продемонстрировать так:
Реализовать можно с помощью трех функций. Одна для генерации случайной фразы:
byte alphabet[] = { 0×61, 0×62, 0×63, 0×64, 0×65, 0×66, 0×67, 0×68, 0×69, 0×6a, 0×6b, 0×6c, 0×6d, 0×6e, 0×6f, 0×70, 0×71, 0×72, 0×73, 0×74, 0×75, 0×76, 0×77, 0×78, 0×79, 0×7a }; // 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
byte sessionPhrase[9]; byte salt[] = { 0×4c, 0×39, 0×78, 0×36, 0×73, 0×4c, 0×39, 0×78 }; // L9×6sL9x — внутренний ключ
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 #include #include #define DEBUG 1
byte sessionPhrase[9]; char sessionMD5Phrase[17]; byte salt[] = { 0×4c, 0×39, 0×78, 0×36, 0×73, 0×4c, 0×39, 0×78 }; // L9×6sL9x — внутренний ключbyte alphabet[] = { 0×61, 0×62, 0×63, 0×64, 0×65, 0×66, 0×67, 0×68, 0×69, 0×6a, 0×6b, 0×6c, 0×6d, 0×6e, 0×6f, 0×70, 0×71, 0×72, 0×73, 0×74, 0×75, 0×76, 0×77, 0×78, 0×79, 0×7a }; // a, b, c, d, etc
void setup () {Serial.begin (115200);}
void loop () {/* Генерируем случайную фразу */getPhrase (sessionPhrase); signPhrase (sessionPhrase, sessionMD5Phrase); // Запоминаем для дальнейшего сравнения
/* Подписываем */char signedPhrase[17]; signPhrase (sessionPhrase, signedPhrase);
#ifdef DEBUGSerial.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 DEBUGSerial.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 DEBUGSerial.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…