Привет, ГТ! Всех с новыми и прошедшими днями! Продолжаю рассказ о том, как я подключил дверной замок к интернету.
Большое спасибо всем за ваши комментарии.
Действительно есть готовые решения, но есть и ряд нюансов, которые не позволили мне их использовать.
Квартира – это все-таки не отель, поэтому специализированные гостиничные замки сразу отбрасываем в сторону.
Мы также не рассматриваем клавиатуры с кодовым замком или антенны NFC, чтобы не пугать соседей.
Также невозможно использовать какую-либо карту, жетон или другой физический носитель; подразумевается, что нет возможности передать их гостю до того, как он откроет дверь.
Использовать Кево, Август или Локитрон не удалось, потому что.
В их основе лежит замок американского типа (ригель), который не очень удачен при установке на стальную дверь толщиной 65-70 мм.
Наш выбор – электромеханический CISA для бронедверей, стоимостью около 500 евро (пока не купил, ибо дорого).
А самое главное, хочется сделать что-то подобное своими руками, не ради корысти, а просто для хобби.
Напомню, что контроллер блокировки находится глубоко за NAT в локальной домашней сети, а потому настраивать веб-сервер на Arduino нет смысла; к нему невозможно получить доступ из Интернета.
Сервер написан на Python с использованием фреймворка Twisted, работает на Linux в другом месте, где есть реальный IP-адрес, а контроллер подключается к нему и ждет команд.
Теперь хочу поговорить о логике работы и криптографии.
Я не нашел ничего, что надежно шифровало бы трафик для Arduino, поэтому связь между замком и сервером будет идти по открытым каналам с незашифрованным трафиком.
В первом прототипе я использовал MD5, сейчас поменял на SHA-1, используя эту библиотеку Криптопакет .
Он потребляет меньше памяти, и уже есть готовая функция HMAC. Ключ представляет собой пароль длиной до 30 символов ASCII, хранящийся в памяти EEPROM контроллера.
В первом прототипе тот же пароль явно хранился на сервере, что, конечно, небезопасно.
Второй прототип требовал два пароля.
Первый предназначен для аутентификации блокировки при подключении к серверу; этот пароль нельзя использовать для открытия замка; он нужен для того, чтобы к серверу могли подключаться только правильные блокировки.
Замок подключается к серверу и первым делом сообщает ему свой идентификатор.
Сервер проверяет в базе данных, существует ли такая блокировка, и если да, отправляет в ответ случайно сгенерированную последовательность ASCII. Замок «подписывает» его паролем и отправляет хэш обратно.
Сервер сравнивает полученный хэш с тем, что вычислил самостоятельно, и если они совпадают, авторизует подключенный контроллер как «лок».
В противном случае соединение сбрасывается.
Второй пароль — это уже цифровой ключ; он не хранится на сервере.
Работает это следующим образом: пользовательское приложение вызывает функцию на сервере через SOAP, указывает идентификатор блокировки и команду, которую хочет ей отправить.
В результате функция возвращает идентификатор запроса.
Сервер пересылает эту команду контроллеру, контроллер отвечает, отправляя случайно сгенерированную последовательность ASCII, и ждет пару секунд «правильного» ответа.
Далее пользовательскому приложению необходимо получить его от сервера через тот же SOAP с использованием идентификатора запроса, подписать его цифровым ключом и отправить обратно.
Контроллер сравнивает полученный хэш с рассчитанным самостоятельно и если они совпадают, выполняет ранее полученную команду, о которой сообщает серверу.
Время подтверждения команды ограничено 2 секундами; если в течение этого времени ответ не получен, контроллер игнорирует ранее полученную команду.
Если хеши не совпадают, команда также игнорируется.
Таким образом я пытаюсь защитить себя от атаки «человек посередине», которую очень легко организовать на проводах моего интернет-провайдера.
Подключение там простое, никаких паролей и сертификатов, просто разрезаю витую пару в панели, обжимаю с двух сторон, подключаю к сети со стороны квартиры с подставленным IP-адресом командного сервера, эмулирую блокировку на другой конец (для отладки я также написал скрипт-эмулятор на Python), настроив такой себе «брандмауэр» с прослушкой.
В этом плане, на мой взгляд, замок получился вполне безопасным.
Даже взлом командного сервера и кража его базы данных с паролями для авторизации с замком не доставит особых хлопот, ведь открыть замок они не смогут. Остаётся только разобраться с гостями.
Это требования №6-8 из первой статьи.
Клавиатуры, прокси-карты, RFID и NFC не вариант, выше написал почему.
Но смартфон есть практически у каждого и во всех смартфонах есть Bluetooth, выбор очевиден!
В первом прототипе гостю был присвоен один код доступа.
В базе данных сервера для этого кода хранились идентификатор блокировки, дата и время начала и окончания действия гостевого доступа, а также текстовое поле для человекочитаемого имени гостя.
Гость подходит к замку, запускает на своем смартфоне мобильное приложение, оно передает по Bluetooth код доступа на контроллер, и от контроллера на сервер поступает сообщение о том, что к нему подошел гость с таким-то кодом доступа.
Сервер сверяет его с базой данных, и если нужный гость пришел в нужное время в нужное место, он посылает замку команду на открытие.
Фактически, вместо отправки открытого кода доступа гость отправит свой хэш HMAC, подписанный «отметкой времени» (строковое представление времени Unix, разделенное на 30 с отброшенной дробной частью).
Для проверки сервер делает запрос к базе данных с выбором всех действующих на данный момент кодов доступа для данного замка, затем последовательно выполняет итерацию и вычисляет для них аналогичный HMAC, если находит совпадение, отправляет команду на открытие, если перечисление кодов доступа заканчивается до того, как будет найдено их совпадение, на замок отправляется отрицательный ответ на попытку открытия.
Проблема в том, что цифровой ключ для такой авторизации нужно было хранить на сервере.
Для второго прототипа пришлось усложнить «гостевую» авторизацию.
Код гостевого доступа теперь состоит из двух ключей, назовем их «серверный» и «приватный».
Серверный нужен для создания гостевой учетной записи на сервере, а приватный — для открытия замка.
Серверный будет храниться явно на сервере, а приватный будет иметь только хеш, но не простой хэш, а HMAC с цифровым ключом блокировки.
Теперь сеанс гостевого входа выглядит так: 1. Гостевое приложение передает контроллеру «серверный» ключ (тот же, что и в первом прототипе, его хеш) и 2. Сервер проверяет его таким же образом, но вместо команды открытия отправляет хеш приватного ключа 3. Контроллер сравнивает хэш закрытого ключа, полученный от сервера, с рассчитанным самостоятельно, и если он совпадает, замок открывается.
Таким образом, на сервере не сохраняется ничего, что могло бы открыть замок.
Невозможно сгенерировать необходимый хэш, не зная цифрового ключа.
Тот же Bluetooth используется и для «главного» доступа без Интернета.
Контроллер получает через него команды и отвечает одноразовым кодом, который необходимо подписать правильным цифровым ключом в течение 2 секунд. Через него я хочу настроить основные параметры контроллера.
Владелец нажимает специальную кнопку на контроллере, после чего он начинает принимать расширенный набор команд, таких как установка идентификатора блокировки, секретных ключей, адреса-порта сервера, параметров сети и т.д. Но у Адруина кончилась память и эскиз не подойдет. В завершение небольшая демонстрация работы.
Теги: #python #iot #Сделай сам или сделай сам #Интернет вещей #Умный дом #arduino #skud #skud #smart lock
-
Бреге, Авраам Луи
19 Oct, 24