Делаем свой «умный дом» чуть более безопасным

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

Реализовать такой подход сравнительно просто…Схема взаимодействия управляющего и исполнительного устройства построена на «подписи» случайной фразы внутренним ключом, известным обоим устройствам. Наглядней это можно продемонстрировать так:

image

Реализовать можно с помощью трех функций. Одна для генерации случайной фразы:

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…

© Habrahabr.ru