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

    В посленее время мир «умного дома» стал более близок для начинающих энтузиастов, благодаря наличию большого количества аппаратных решений с низким порогом вхождения (речь конечно про платформу 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…

    Similar posts

    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 10

      +1
      Но md5 для целей криптографии не рекомендуют использовать уже давным давно, так как он достаточно легко вскрывается перебором. Я бы на вашем месте лучше попробовал прикрутить библиотеку с SHA-2 или чем-то вроде того.
        +2
        Воспримите это как некий вектор движения начинающего в сторону уменьшения паранои. Да и в плейграунде проекта Arduino одной из первых указана именно эта библиотека.
          +1
          md5 плох для хранения паролей, в случае с подписью, при достаточной длине ключа, — вполне уместен.
        • UFO just landed and posted this here
            0
            Спасибо за дельные советы! Многим нравится именно обсуждения. Это и есть формат Хабра!
            Не нужно рассчитывать что кто то будет присылать строки

            Возможно Иван не совсем правильно понял, но общение между Инициатором и Исполнителем происходит по Радио-каналу именно по строковым данным (в заметке упоминается не один раз фраймворк MySensors), именно в связи с этим такой алгоритм действий, который можно применить и к другим системам.

            К тому же на каждое обращение генерируется новая «случайная» фраза, что позволит обойти «перебор», описанный в комментарии выше…
              0
              > К тому же на каждое обращение генерируется новая «случайная» фраза, что позволит обойти «перебор»

              Это всё замечательно, только у вас защиты от подмены сообщений нет. Достаточно «включи свет в ванной» на «убей всех человеков» заменить и дальше само всё подпишется.

              Вообще, криптография — это не просто пару хешей посчитать. Писать самому… С тем же эффектом (и гораздо проще) прогнать XORом сообщение по ключу

                0
                Достаточно «включи свет в ванной» на «убей всех человеков» заменить...

                Естественно, исполнитель имеет свой ограниченный функционал и игнорирует команды, которые в него не заложены. Например в том же MySensors можно проверять тип входящего сообщения (message.type == V_LIGHT) и адресность конкретного сенсора (message.sensor == PRIMARY_CHILD_ID)…
                  0
                  > и игнорирует команды, которые в него не заложены

                  естественно я не рассчитываю, что лампочка будет варить кофе
            +1
            Но тут ещё одна проблема. К примеру, на Arduino, использование простейшего md5 добавляет 13К к коду, плюс столько же библиотека взаимодействия с устройством и в итоге нам останется всего 10К для нашей программы. При реализации взаимодействия с Arduino через EtherCard я думал об использовании простейшей подписи, но когда увидел размеры кода для подписи — отказался от данного решения. Нужно придумывать свою упрощённую реализацию со скидкой на «Неуловимого Джо».
              0
              Да, конечно, здесь по большей части обозначен «вектор движения». Можно экпериментировать, например с DES and 3DES Encryption Library.

            Only users with full accounts can post comments. Log in, please.