Pull to refresh

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

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

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

Реализовать такой подход сравнительно просто…

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

image

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

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;
}


Такой подход можно исполнять как в своих “велосипедах», так и в других фреймворках (таких как MySensors) и дает дополнительный плюсик к безопасности Вашего Интернета вещей.

Кстати, аутентификация будет в MySensors, на текущий момент она реализована в development-версии.

P.S.: Если будет интересно сообществу — сделаю update заметки на примере того же MySensors с примерами листингов для Serial Gateway и Relay…
Tags:
Hubs:
+10
Comments 10
Comments Comments 10

Articles