Криптографическая библиотека для Arduino (шифр “МАГМА”, ГОСТ Р 34.12-2015)

Библиотека MagmaEncryptor для платформы Arduino позволяет использовать российский шифр “Магма” (стандартизован ГОСТ Р 34.12-2015) в целях защиты информации. Интерфейс библиотеки простой: две функции (или два метода) – lock() и unlock(), – соответственно, выполняют зашифрование и расшифрование, также имеется функция для загрузки ключей – setKeys(). Реализация сочетает в себе и шифрование, и аутентификацию передаваемых сообщений. То есть, помимо того, что данные будут зашифрованы, для них вычисляется код аутентификации (MAC, имитовставка), при помощи которого может быть проверена целостность и подлинность сообщения – проверяются функцией расшифрования.

Библиотека может быть использована как учебный материал или для самостоятельного программирования устройств “в быту”. Данная реализация заведомо не защищена от аппаратных атак (более того, ключи можно элементарно извлечь, получив устройство-носитель) и, из-за свойств шифра и микроконтроллера, от атак, основанных на анализе побочных наводок и излучений (этот момент, в основном, связан с использованием подстановок по таблице, возможно, я как-нибудь напишу отдельную записку по этой интересной теме). Очередной раз отмечу очевидное: несмотря на то, что я тщательно проверил код, в нём всё равно могут быть ошибки и уязвимости.

Внутренняя логика построения достаточно проста: шифр работает в режиме счётчика; после того, как открытый текст зашифрован, для шифротекста и значения nonce вычисляется код аутентификации, для его вычисления служит алгоритм CMAC, построенный на базе того же шифра (“Магма”). Полученный результат возвращается приложению. Для шифрования и получения MAC используются разные ключи (это важный момент). Исходный код библиотеки содержит достаточно подробные комментарии, поэтому с техническими деталями можно ознакомиться, заглянув внутрь.

Типичный сценарий использования библиотеки

1. Создаём экземпляр класса:

MagmaEncryptor M;

Если есть свободная память, то рекомендуется создавать два экземпляра, работающих с разными ключами – для отправки сообщений (записи) и их получения (чтения). Вот так:

MagmaEncryptor MWrite, MRead;

Дело в том, что корректное использование MAC подразумевает учёт случая, когда переданное сообщение копируется третьей стороной и позднее воспроизводится в сторону передавшего узла – соответственно, протокол должен подразумевать тот или иной контроль направления передачи.

2. Загружаем ключи:

M.setKeys(cipher_key, MAC_key);

Здесь cipher_key – это секретный ключ шифрования; MAC_key – секретный ключ для аутентификации. Оба ключа имеют разрядность 256 бит (соответствует разрядности ключа шифра), то есть, занимают 32 байта: byte cipher_key[32]; Ключи должны быть разными. Система симметричная, поэтому на всех узлах должны быть экземпляры ключей.

3. Для шифрования вызываем lock():

M.lock(nonce,plain_text,cipher_text,len,MAC);

Параметры: nonce – это начальное значение счётчика, оно должно использоваться только один раз (с конкретным ключом, при изменении ключа – можно повторно использовать); передаётся указатель на переменную – lock() записывает новое значение счётчика, это позволяет использовать nonce в последовательных вызовах lock(); plain_text – открытый текст (указатель на байтовый буфер); cipher_text – буфер для шифротекста; размеры буферов совпадают, и равны len (в байтах); MAC – буфер для записи кода аутентификации (64 бита, восемь байтов).

4. Для расшифрования вызываем unlock(), параметры совпадают с lock():

M.unlock(nonce,plain_text,cipher_text,len,MAC);

Функция unlock() должна получать значение nonce, соответствующее начальному значению в вызове lock(). Значение MAC – это значение кода аутентификации, полученное lock() для данного сообщения. Функция проверяет код аутентификации и расшифровывает сообщение, записывая результат в plain_text. Если значение MAC не совпало, то функция возвращает 0. Обратите внимание, что расшифрование производится в любом случае, однако если код аутентификации не корректен, использовать результат расшифрования нельзя.

5. Размеры блоков данных:

(Разрядность шифра и MAC – 64 бита; разрядность ключей – 256 бит.)

Ключи – 32 байта, byte key[32];
MAC – 8 байтов, byte MAC[8];
Nonce – 4 байта, uint32_t nonce;

Обратите внимание на то, что библиотека не следит за использованием ключей и nonce – предполагается, что это делает само приложение, вызывающее библиотеку.

Ограничения по объёму данных

Отдельную задачу представляет оценка максимального числа блоков, которые можно защитить данной библиотекой (для одного набора ключей), при условии идеальной реализации и отсутствия утечек. Попробуем оценить это число приблизительно. Очевидно, что строгий верхний предел ставит разрядность счётчика – 32 бита (или 232 блоков). Но отталкиваться нужно от стойкости MAC. Традиционная консервативная оценка для 64-разрядного CMAC – 221 блоков, при корректном использовании. Полагаю, что для “бытовых применений” вполне можно остановиться на оценке 220 блоков, что примерно соответствует восьми мегабайтам переданных данных (кто всё это будет записывать?). Или нескольким сотням тысяч команд и ответов устройств. Не менее разумным будет просто остановиться на разрядности двухбайтового счётчика (его проще вести на 8-битном контроллере) – 216. После того, как число блоков приблизилось к установленному максимальному значению, следует перейти на другой набор ключей – для этого в библиотеке предусмотрена функция KDF(), генерирующая новые ключи на основе заданных.

Установка библиотеки

Библиотеку можно скачать в виде сжатого архива и импортировать в Arduino IDE штатными средствами – Sketch -> Include Library -> Add .ZIP library. (Можно просто скопировать содержимое архива в директорию libraries IDE.) В составе библиотеки находится небольшой пример кода, который также служит для тестирования корректности сборки в конкретном окружении (после установки библиотеки пример будет доступен в разделе File -> Examples -> MagmaEncryptor).

Библиотека: MagmaEncryptor.zip.
SHA-256: 12b214d719bd36c3dd2e1374cc7d184945498f8cb4040e0ba1417db5162550a1

Исходный код (копия, в архиве библиотеки – эти же файлы): MagmaEncryptor_01beta.h, MagmaEncryptor_01beta.cpp.

(Проверено на Arduino Uno и Nano, все с ATmega328.)

Вопросы, поправки, пожелания – весьма приветствуются.

Ссылки по теме:

Симметричные блочные шифры на примере ГОСТ Р 34.12-2015 “Магма”;
Интернет вещей: шифр “Магма” (ГОСТ Р 34.12-2015) для Arduino

Комментарии читателей блога: 5

  • 1. 19th February 2017, 11:48 // Читатель Юрий написал:

    Прекрасная работа! Спасибо!

    Очень хотелось бы почитать ваше сравнение с IoT шифрами от АНБ. Кто круче так сказать ;)

  • 2. 6th March 2017, 12:36 // Читатель Nik написал:

    Не получается запустить пример, при компиляции ошибка, не могу разобраться в чем дело ((
    error: can’t find a register in class ‘POINTER_Y_REGS’ while reloading ‘asm’

  • 3. 6th March 2017, 13:10 // Александр Венедюхин ответил:

    Какую версию компилятора (avr-gcc) используете? Я проверял с avr-gcc (GCC) 4.8.1.
    Причина может быть в старой версии.

  • 4. 6th March 2017, 16:07 // Читатель Nik написал:

    Все разобрался, поменял уровень оптимизации с Os на O1, и скетч скомпилировался
    В папке с arduino ide лежит avr-gcc-4.9.2, думаю эта версия…

  • 5. 6th March 2017, 18:55 // Александр Венедюхин ответил:

    Да, 4.9 – подходящая версия. Уровень оптимизации как раз был вторым кандидатом на роль источника проблемы: -Os в gcc, похоже, не со всеми ассемблерными конструкциями уживается.

Написать комментарий

Ваш комментарий:

Преграда для ботов: *

Если видите "капчу", то решите её. Это необходимо для отправки комментария. Обычно, комментарии поступают на премодерацию. Премодерация иногда занимает заметное время.