Привет GT! Всех с наступившим и прошедшими!
Продолжаю рассказ о том, как я подключал дверной замок к Интернету. Начало тут.
Большое спасибо всем за комментарии. Готовые решения действительно есть, но так же есть ряд нюансов, не позволивших мне ими воспользоваться. Квартира это все-таки не отель, так что специализированные гостиничные замки мы отметаем сразу. Клавиатуры кодовых замков, антенны для NFC дабы не пугать соседей тоже не рассматриваем. Никакой карты, токена или иного физического носителя использовать тоже не получится, подразумевается, что нет возможности их передать гостю до того, как он откроет дверь.
Kevo, August или Lockitron использовать не получилось, т.к. они основаны на американском типе замка (deadbolt), что не очень удачно устанавливается на стальную дверь толщиной 65-70 мм. Наш выбор это электромеханический CISA для бронированных дверей, ценой около 500 евро (пока не купил, ибо дорого).
И самое главное, хочется что-то такое сделать своими руками, не корысти ради токма увлечения для.
Напомню, что контроллер замка находится глубоко за NAT в локальной домашней сети, а следовательно поднимать web-сервер на Ардуине нет смысла, из Интернета до него не достучаться. Сервер написан на Python с использованием фреймворка Twisted, работает под Linux в другом месте, где есть настоящий IP адрес, а контроллер подключается к нему и ждет команд.
Сейчас я хочу рассказать о логике работы и о криптографии.
Что-то надежно шифрующее трафик для Ардуины я не нашел, потому коммуникации замка и сервера будут идти по открытым каналам нешифрованным трафиком.
В первом прототипе я ипользовал MD5, теперь сменил его на SHA-1, вот на этой библиотеке Cryptosuite. Памяти она потребляет поменьше, и уже есть готовая функция HMAC.
Ключ — это пароль до 30 ASCII символов, храниться в EEPROM памяти контроллера
В первом прототипе этот же пароль хранился в явном виде и на сервере, что конечно же не секьюрно. Для второго прототипа потребовалось два пароля. Первый для аутентификации замка при подключении к серверу, этим паролем открыть замок нельзя, он нужен для того, что бы только правильные замки могли подключаться к серверу.
Замок подключается к серверу и первым делом сообщает ему свой идентификатор. Сервер проверяет в базе данных, есть ли такой замок, и если есть, высылает в ответ случайным образом сгенерированную ASCII последовательность. Замок «подписывает» ее паролем и отправляет хеш обратно. Сервер сравнивает полученный хеш с тем, что он рассчитал самостоятельно, и если они совпали, авторизирует подключившийся контроллер как «замок».
В противном случае соединение сбрасывается.
Второй пароль, это уже цифровой ключ, на сервере он не храниться.
Работает следующим образом: пользовательское приложение вызывает через SOAP функцию на сервере, указывает ID замка и команду, которую он хочет ему отправить. Результатом функция возвращает ID запроса. Сервер переправляет эту команду на контроллер, контроллер в ответ отправляет случайным образом сгенерированную ASCII последовательность, и ждет «правильный» ответ пару секунд. Далее пользовательскому приложению нужно получить ее от сервера, через тот же SOAP по ID запроса, подписать ее цифровым ключом и отправить обратно.
Контроллер сравнивает полученный хеш с рассчитанным самостоятельно и если они совпадают, выполняет ранее полученную команду, о чем отчитывается серверу.
Время на подтверждение команды ограничено 2-я секундами, если в течение этого времени ответа не придет, контроллер игнорирует ранее полученную команду. Если хеши не совпадут, команда так же игнорируется.
Таким образом я пытаюсь защититься от атаки «человек посередине», которую на проводах моего провайдера устроить очень просто. Соединение там простое, никаких паролей и сертификатов, достаточно обрезать витую пару в щитке, обжать с обоих сторон, со стороны квартиры воткнуть в сеть с подмененным IP-адресом командного сервера, на другом конце эмулировать замок (скрипт эмулятора я на питоне тоже написал для отладки), устроив такой «межсетевой экран» с прослушкой.
В этом плане, на мой взгляд, замок получился достаточно защищенный.
Даже взлом командного сервера и кража его базы с паролями для авторизации замком особой беды не наделают, ибо открыть ими замок не получится.
Осталось только разобраться с гостями. Это требования №№6-8 из первой статьи.
Клавиатуры, прокси-карты, RFID и NFC не вариант, выше написал почему. Но почти у каждого есть смартфон и во всех смартфонах есть Bluetooth, выбор очевиден!
В первом прототипе гость был ассоциирован с одним единственным кодом доступа. В базе сервера для этого кода хранились ID замка, дата-время начала и конца действия гостевого доступа, ну и текстовое поле для человеко-читаемого наименования гостя.
Гость подходит к замку, запускает мобильное приложение на своем смартфоне, оно передает через Bluetooth на контроллер код доступа, от контроллера на сервер приходит сообщение о том, что к нему подошел гость с таким-то кодом доступа. Сервер проверяет его по базе данных, и если правильный гость пришел в правильное время в правильное место, отправляет замку команду на открытие. На самом деле вместо кода доступа в открытом виде гость будет отправлять его HMAC-хеш, подписанный «временным штампом» (строковое представление юниксового времени, деленное на 30 с отброшенной дробной частью). Сервер для проверки делает запрос к базе с селектом всех действительных в текущий момент для данного замка кодов доступа, затем последовательно перебирает и вычисляет для них аналогичный HMAC, если находит совпадение, отправляет команду на открытие, если перебор кодов доступа заканчивается до того, как удается найти совпадение, замку отправляется отрицательный ответ на попытку открытия.
Проблема в том, что цифровой ключ для такой авторизации надо было хранить на сервере.
Пришлось для второго прототипа усложнить «гостевую» авторизацию. Гостевой код доступа теперь состоит из двух ключей, назовем их «серверный» и «приватный». Серверный нужен для заведения учетной записи гостя на сервере, а приватный собственно для открытия замка. Серверный в явном виде будет храниться на сервере, а от приватного будет только хеш, но не простой, а HMAC с цифровым ключом замка.
Теперь сессия авторизации гостя выглядит так:
1. Гостевое приложение передает на контроллер «серверный» ключ (так же как и в первом прототипе его хеш) и
2. Сервер точно так же его проверяет, но вместо команды открытия отправляет хеш приватного ключа
3. Контроллер сравнивает хеш приватного ключа, полученный от сервера с рассчитанным самостоятельно и если оно совпало, замок открывается.
Таким образом на сервере не храниться ничего, что могло бы открыть замок. Сгенерировать нужный хеш, не зная цифрового ключа, не получится.
Тот же Bluetooth используется и для «хозяйского» доступа без Интернета. Контроллер принимает через него команды, отвечает одноразовым кодом, который нужно подписать правильным цифровым ключом в течение 2-х секунд.
Через него же хочу сделать настройку основных параметров контроллера. Хозяин нажимает на специальную кнопку контроллера, после чего он начинает принимать расширенный набор команд, такие как установка ID замка, секретных ключей, адреса-порта сервера, параметры сети и т.п. Но в Адруине кончилась память и скетч не влезает.
В завершении короткая демонстрация работы.
Автор: kuld