Шифрование и расшифровка данных: различия между версиями

Материал из MIK32 микроконтроллер
Нет описания правки
 
(не показано 25 промежуточных версий 2 участников)
Строка 7: Строка 7:


Затем перейдем к настройке самого крипто-блока. Для этого откроем вкладку крипто-блок и нажмем включить. После этого появятся несколько настроек.  
Затем перейдем к настройке самого крипто-блока. Для этого откроем вкладку крипто-блок и нажмем включить. После этого появятся несколько настроек.  
[[Файл:Настройки крипто-блока в конфигураторе.png|мини|Настройки крипто-блока в конфигураторе]]
[[Файл:Настройки крипто-блока в конфигураторе.png|мини|Рисунок 2 - настройки крипто-блока в конфигураторе]]
Зададим им следующие значения:
Зададим им следующие значения:


Строка 15: Строка 15:
* Порядок загрузки/выгрузки - От старшего слова к младшему.
* Порядок загрузки/выгрузки - От старшего слова к младшему.


В итоге настройки таймера в конфигураторе должны выглядеть как на рисунке.  
В итоге настройки крипто-блока в конфигураторе должны выглядеть как на рисунке 2.  


Нажимаем кнопку сохранения и генерации. В итоге у нас появится проект для PlatformIo. Далее работа идет в visual studio code.
Нажимаем кнопку сохранения и генерации. В итоге у нас появится проект для PlatformIo. Далее работа идет в visual studio code.


== Использование библиотеки HAL_DAC ==
== Использование библиотеки HAL_Crypto ==
В сгенерированном проекте в файле main.c должна быть функция Crypto_Init, в которой будут заданы настройки для крипто-блока. Выглядит она так:
В сгенерированном проекте в файле main.c должна быть функция Crypto_Init, в которой будут заданы настройки для крипто-блока. Выглядит она так:


{{#spoiler:show=Развернуть код|hide=Свернуть код|
<syntaxhighlight lang="c" line="1">
<syntaxhighlight lang="c" line="1">
static void Crypto_Init(void)
static void Crypto_Init(void)
Строка 34: Строка 35:
     HAL_Crypto_Init(&hcrypto);
     HAL_Crypto_Init(&hcrypto);
}
}
</syntaxhighlight>Кроме этого в функции SystemClock_Config приведены настройки для тактирования. Убедитесь что в PeriphClkInit.PMClockAHB присутствует PM_CLOCK_CRYPTO_M. Сама функция должна выглядеть примерно так:
</syntaxhighlight>
}}


Кроме этого в функции SystemClock_Config приведены настройки для тактирования. Убедитесь, что в PeriphClkInit.PMClockAHB присутствует PM_CLOCK_CRYPTO_M. Сама функция должна выглядеть примерно так:
{{#spoiler:show=Развернуть код|hide=Свернуть код|
<syntaxhighlight lang="c" line="1">
<syntaxhighlight lang="c" line="1">
void SystemClock_Config(void)
void SystemClock_Config(void)
Строка 58: Строка 63:
     HAL_RCC_ClockConfig(&PeriphClkInit);
     HAL_RCC_ClockConfig(&PeriphClkInit);
}
}
</syntaxhighlight>Для демонстрации вывода текста в PeriphClkInit.PMClockAPB_P присутствует PM_CLOCK_UART_0_M. У вас его может не быть так как UART нужно включить отдельно. В начале main.c можно видеть объявление структуры с набором настроек для крипто-блока, которую использует функция инициализации Crypto_Init.   
</syntaxhighlight>
}}


Для демонстрации вывода текста в PeriphClkInit.PMClockAPB_P присутствует PM_CLOCK_UART_0_M. У вас его может не быть, так как UART нужно включить отдельно. Для этого нужно подключить библиотеки uart_lib и xprintf.
{{#spoiler:show=Развернуть код|hide=Свернуть код|
<syntaxhighlight lang="c" line="1">
#include "uart_lib.h"
#include "xprintf.h"
#include "mik32_hal_rcc.h"
#include "mik32_hal_crypto.h"
</syntaxhighlight>
}}
Для инициализации UART в функции main, после функции тактирования SystemClock_Config, следует написать:
<syntaxhighlight lang="c" line="1" start="1">
UART_Init(UART_0, 3333, UART_CONTROL1_TE_M | UART_CONTROL1_M_8BIT_M, 0, 0);
</syntaxhighlight>
Скорость UART задается делителем во втором аргументе функции. При такой записи скорость будет 9600 бод.
В начале main.c можно видеть объявление структуры с набором настроек для крипто-блока, которую использует функция инициализации Crypto_Init.   
{{#spoiler:show=Развернуть код|hide=Свернуть код|
<syntaxhighlight lang="c" line="1">
<syntaxhighlight lang="c" line="1">
Crypto_HandleTypeDef hcrypto;
Crypto_HandleTypeDef hcrypto;
Строка 66: Строка 92:
static void Crypto_Init(void);
static void Crypto_Init(void);
</syntaxhighlight>   
</syntaxhighlight>   
}}


Создадим два глобальных массива для ключа и вектора инициализации. Количество элементов массива можно задать с помощью макросов CRYPTO_KEY_KUZNECHIK (количество слов в ключе для алгоритма кузнечик).   
Создадим глобальный массив для ключа. Количество элементов массива можно задать с помощью макросов CRYPTO_KEY_KUZNECHIK (количество слов в ключе для алгоритма кузнечик).   


<syntaxhighlight lang="c" line="1">
<syntaxhighlight lang="c" line="1">
uint32_t crypto_key[CRYPTO_KEY_KUZNECHIK] = {0x8899aabb, 0xccddeeff, 0x00112233, 0x44556677, 0xfedcba98, 0x76543210, 0x01234567, 0x89abcdef};
uint32_t crypto_key[CRYPTO_KEY_KUZNECHIK] = {0x8899aabb, 0xccddeeff, 0x00112233, 0x44556677, 0xfedcba98, 0x76543210, 0x01234567, 0x89abcdef};
</syntaxhighlight>В функции main после функции инициализации Crypto_Init запишем ключ в регистр KEY с помощью функции HAL_Crypto_SetKey.
</syntaxhighlight>
 
Для режимов шифрования ECB и CBC длина данных должна быть кратна длине блока. Иначе данные следует дополнить.
 
Для режима CTR длинна данных может быть любой. В этом режиме дополнения не требуются.
 
Синхропосылка (вектор инициализации), в соответствии с ГОСТ 34.13—2015, для разных режимов может иметь разную длину:


*В режиме ECB синхропосылка не используется;
*В режиме CBC синхропосылка должна быть длинной m*z, где m - длина блока (128 бит для алгоритма Кузнечик), z - целое число. Поддерживается только случай z=1, m=n, где n - длина блока (4 слова для Кузнечика и AES, 2 слова для Магмы);
*В режиме CTR в регистр INIT следует записывать значение счетчика состоящего из синхропосылки, длинна которой равна половине длине блока, и такого же количества нулей. В итоге размер синхропосылки будет равен размеру блока.
===Режим простой замены (Electronic Codebook, ЕСВ)===
Теперь напишем функции для зашифровки и расшифровки данных в режиме шифрования ECB - kuznechik_ECB_code и kuznechik_ECB_decode соответственно.  
Теперь напишем функции для зашифровки и расшифровки данных в режиме шифрования ECB - kuznechik_ECB_code и kuznechik_ECB_decode соответственно.  


Строка 78: Строка 116:


*plain_text - незашифрованные данные;
*plain_text - незашифрованные данные;
*expect_cipher_text - зашифрованные данные, которые ожидается получить;
*cipher_text - полученные зашифрованные данные;
*cipher_text - полученные зашифрованные данные.
*expect_cipher_text - зашифрованные данные, которые ожидается получить.
Количество слов в данных должно быть кратно 4 (блок - 128 бит).  Если данных меньше, то их нужно дополнить вручную. ГОСТ 34.13—2015 определяет три возможные процедуры дополнения. Можно, например, дополнить остаток нулями до размера блока.


Кроме этого нам понадобится функция HAL_Crypto_Encode для зашифровки данных. После этого поочередно выведем каждый из массивов в UART. После этого проведем сравнение полученных данных и ожидаемых.
Количество слов в данных для зашифровки должно быть кратно размеру блока (для кузнечика 4 слова или 128 бит).  Если данных меньше, то их нужно дополнить вручную. ГОСТ 34.13—2015 определяет три возможные процедуры дополнения. Можно, например, дополнить остаток нулями до размера блока.


Внутри  kuznechik_ECB_code сначала задается режим шифрования функцией HAL_Crypto_SetCipherMode (если другие режимы использовать не планируется, то этого можно не делать, так как этот режим шифрования уже задан после инициализации функцией Crypto_Init), затем устанавливается мастер-ключ функцией HAL_Crypto_SetKey. Так как ключ должен быть инициализирован в режиме шифрования, то после функции HAL_Crypto_SetKey блок находится в режиме зашифровки данных. После этого используется функция HAL_Crypto_Encode для зашифровки данных.
В конце поочередно выведем каждый из массивов в UART. После этого проведем сравнение полученных данных и ожидаемых.
{{#spoiler:show=Развернуть код|hide=Свернуть код|
Функция kuznechik_ECB_code<syntaxhighlight lang="c" line="1">
Функция kuznechik_ECB_code<syntaxhighlight lang="c" line="1">
void kuznechik_ECB_code()
void kuznechik_ECB_code()
{
{
     uint32_t plain_text[] = {             
     uint32_t plain_text[] =     {             
                                0x11223344, 0x55667700, 0xffeeddcc, 0xbbaa9988,
                                    0x11223344, 0x55667700, 0xffeeddcc, 0xbbaa9988,
                                0x00112233, 0x44556677, 0x8899aabb, 0xcceeff0a,
                                    0x00112233, 0x44556677, 0x8899aabb, 0xcceeff0a,
                                0x11223344, 0x55667788, 0x99aabbcc, 0xeeff0a00,
                                    0x11223344, 0x55667788, 0x99aabbcc, 0xeeff0a00,
                                0x22334455, 0x66778899, 0xaabbccee, 0xff0a0011
                                    0x22334455, 0x66778899, 0xaabbccee, 0xff0a0011
                            };
                                };
 
   
                   
    uint32_t cipher_text[] = { 
                                0x0, 0x0, 0x0, 0x0,
                                0x0, 0x0, 0x0, 0x0,
                                0x0, 0x0, 0x0, 0x0,
                                0x0, 0x0, 0x0, 0x0
                            };


     uint32_t expect_cipher_text[] = {
     uint32_t expect_cipher_text[] = {
Строка 104: Строка 151:




    uint32_t key_length = sizeof(crypto_key)/sizeof(*crypto_key);
     uint32_t plain_text_length = sizeof(plain_text)/sizeof(*plain_text);
     uint32_t plain_text_length = sizeof(plain_text)/sizeof(*plain_text);


     uint32_t cipher_text[plain_text_length];
 
      
    /* Задать режим шифрования */
    HAL_Crypto_SetCipherMode(&hcrypto, CRYPTO_CIPHER_MODE_ECB);
     /* Установка ключа */
    HAL_Crypto_SetKey(&hcrypto, crypto_key);
 
 
     /* Зашифровать данные */
     HAL_Crypto_Encode(&hcrypto, plain_text, cipher_text, plain_text_length);  
     HAL_Crypto_Encode(&hcrypto, plain_text, cipher_text, plain_text_length);  


     xprintf("KEY ");
     xprintf("KEY ");
     for (uint32_t i = 0; i < CRYPTO_KEY_KUZNECHIK; i++)
     for (uint32_t i = 0; i < key_length; i++)
     {
     {
         xprintf("0x%08x ", crypto_key[i]);
         xprintf("0x%08x, ", crypto_key[i]);
     }
     }
     xprintf("\n");   
     xprintf("\n");   
Строка 138: Строка 193:
     xprintf("\n");
     xprintf("\n");


    uint8_t error = 0;
     for (uint32_t i = 0; i < plain_text_length; i++)
     for (uint32_t i = 0; i < plain_text_length; i++)
     {
     {
         if (expect_cipher_text[i] != cipher_text[i])
         if(expect_cipher_text[i] != cipher_text[i])
        {
            xprintf("error\n");
            break;
        }
        else if ((i+1) == plain_text_length)
         {
         {
             error = 1;
             xprintf("Successfull\n");
         }
         }
     }
     }
     if (error)
     xprintf("\n");
    {
        xprintf("Error\n");
    }
    else
    {
        xprintf("Matched\n");
    }
   
}
}
</syntaxhighlight>Функция kuznechik_ECB_decode аналогична, но в ней используются другие массивы:
</syntaxhighlight>
}}
Функция kuznechik_ECB_decode аналогична, но в ней используются другие массивы:


*plain_text - полученные расшифрованные данные;
*cipher_text - зашифрованные данные;
*cipher_text - зашифрованные данные;
*expect_plain_text - расшифрованные данные, которые ожидается получить;
*expect_plain_text - расшифрованные данные, которые ожидается получить.
*plain_text - полученные расшифрованные данные.


Для расшифровки данных используется функция HAL_Crypto_Decode.
Для расшифровки данных используется функция HAL_Crypto_Decode.


{{#spoiler:show=Развернуть код|hide=Свернуть код|
Функция kuznechik_ECB_decode<syntaxhighlight lang="c" line="1">
Функция kuznechik_ECB_decode<syntaxhighlight lang="c" line="1">
void kuznechik_ECB_decode()
void kuznechik_ECB_decode()
{
{
    uint32_t plain_text[] =    {           
                                    0x0, 0x0, 0x0, 0x0,
                                    0x0, 0x0, 0x0, 0x0,
                                    0x0, 0x0, 0x0, 0x0,
                                    0x0, 0x0, 0x0, 0x0
                                };
   
    uint32_t cipher_text[] = { 
                                0x7f679d90, 0xbebc2430, 0x5a468d42, 0xb9d4edcd,
                                0xb429912c, 0x6e0032f9, 0x285452d7, 0x6718d08b,
                                0xf0ca3354, 0x9d247cee, 0xf3f5a531, 0x3bd4b157,
                                0xd0b09ccd, 0xe830b9eb, 0x3a02c4c5, 0xaa8ada98
                            };
   
     uint32_t expect_plain_text[] =  {             
     uint32_t expect_plain_text[] =  {             
                                         0x11223344, 0x55667700, 0xffeeddcc, 0xbbaa9988,
                                         0x11223344, 0x55667700, 0xffeeddcc, 0xbbaa9988,
Строка 174: Строка 242:
                                     };
                                     };


               
    uint32_t key_length = sizeof(crypto_key)/sizeof(*crypto_key);
     uint32_t cipher_text[] = { 
     uint32_t cipher_text_length = sizeof(cipher_text)/sizeof(*cipher_text);
                                0x7f679d90, 0xbebc2430, 0x5a468d42, 0xb9d4edcd,
 
                                0xb429912c, 0x6e0032f9, 0x285452d7, 0x6718d08b,  
 
                                0xf0ca3354, 0x9d247cee, 0xf3f5a531, 0x3bd4b157,
    /* Задать режим шифрования */
                                0xd0b09ccd, 0xe830b9eb, 0x3a02c4c5, 0xaa8ada98
    HAL_Crypto_SetCipherMode(&hcrypto, CRYPTO_CIPHER_MODE_ECB);
                            };
    /* Установка ключа */
    HAL_Crypto_SetKey(&hcrypto, crypto_key);


    uint32_t cipher_text_length = sizeof(cipher_text)/sizeof(*cipher_text);


     uint32_t plain_text[cipher_text_length];
     /* Расшифровать данные */
   
     HAL_Crypto_Decode(&hcrypto, cipher_text, plain_text, cipher_text_length);  
     HAL_Crypto_Decode(&hcrypto, cipher_text, plain_text, cipher_text_length);  


     xprintf("KEY ");
     xprintf("KEY ");
     for (uint32_t i = 0; i < CRYPTO_KEY_KUZNECHIK; i++)
     for (uint32_t i = 0; i < key_length; i++)
     {
     {
         xprintf("0x%08x, ", crypto_key[i]);
         xprintf("0x%08x, ", crypto_key[i]);
Строка 216: Строка 284:
     xprintf("\n");
     xprintf("\n");


    uint8_t error = 0;
     for (uint32_t i = 0; i < cipher_text_length; i++)
     for (uint32_t i = 0; i < cipher_text_length; i++)
     {
     {
         if (expect_plain_text[i] != plain_text[i])
         if(expect_plain_text[i] != plain_text[i])
         {
         {
             error = 1;
             xprintf("error\n");
        } 
            break;
    }
        }
    if (error)
        else if ((i+1) == cipher_text_length)
    {
        {
        xprintf("Error\n");
            xprintf("Successfull\n");
    }
        }
    else
    {
        xprintf("Matched\n");
     }
     }
    xprintf("\n");
}
}
</syntaxhighlight>В функции main перед вызовом kuznechik_ECB_code для наглядности можно добавить функцию HAL_Crypto_SetCipherMode для активации нужного режима шифрования. Но этого можно не делать так как режим шифрования уже задан в Crypto_Init.
</syntaxhighlight>
}}


Функция main<syntaxhighlight lang="c" line="1">
Функция main.
 
{{#spoiler:show=Развернуть код|hide=Свернуть код|
<syntaxhighlight lang="c" line="1">
int main()
int main()
{     
{     
    SystemClock_Config();


     SystemClock_Config();
     UART_Init(UART_0, 3333, UART_CONTROL1_TE_M | UART_CONTROL1_M_8BIT_M, 0, 0);


     Crypto_Init();
     Crypto_Init();
    /* Установка ключа */
    HAL_Crypto_SetKey(&hcrypto, crypto_key);


     xprintf("\nkuznechik_ECB_code\n");
     xprintf("\nkuznechik_ECB_code\n");
    HAL_Crypto_SetCipherMode(&hcrypto, CRYPTO_CIPHER_MODE_ECB);    /* Настройка режима шифрования */
     kuznechik_ECB_code();
     kuznechik_ECB_code();
     xprintf("\nkuznechik_ECB_decode\n");   
     xprintf("\nkuznechik_ECB_decode\n");   
Строка 259: Строка 325:
}
}
</syntaxhighlight>
</syntaxhighlight>
[[Файл:Вывод в UART. Режим ECB.png|мини|Вывод в UART]]
}}
Вывод в UART изображен на рисунке.
[[Файл:Kuznechik ECB.png|мини|Рисунок 3 - вывод в UART]]
Вывод в UART изображен на рисунке 3.


Функции для режимов CBC и CTR аналогичны, но содержат другие данные в массивах.
Функции для режимов CBC и CTR аналогичны, но содержат другие данные в массивах.
Строка 266: Строка 333:
Отличие этих режимов в том, что они перед каждой новой расшифровкой или зашифровкой требуют записи вектора инициализации с помощью функции HAL_Crypto_SetINIT.  
Отличие этих режимов в том, что они перед каждой новой расшифровкой или зашифровкой требуют записи вектора инициализации с помощью функции HAL_Crypto_SetINIT.  


Для режимов ECB и CBC длина данных должна быть кратна длине блока. Иначе данные следует дополнить.
===Режим простой замены с зацеплением (Cipher Block Chaining, СВС)===
Размер массива для вектора инициализации можно задать с помощью макросов:
 
* IV_LENGTH_KUZNECHIK_CBC
* IV_LENGTH_MAGMA_CBC   
* IV_LENGTH_AES_CBC     
 
Количество слов в данных для зашифровки должно быть кратно размеру блока (для кузнечика 4 слова или 128 бит).  Если данных меньше, то их нужно дополнить вручную.
 
{{#spoiler:show=Развернуть код|hide=Свернуть код|
Функция kuznechik_CBC_code<syntaxhighlight lang="c" line="1">
void kuznechik_CBC_code()
{
    uint32_t init_vector[IV_LENGTH_KUZNECHIK_CBC] = {0x12341234, 0x11114444, 0xABCDABCD, 0xAAAABBBB};
   
    uint32_t plain_text[] = {           
                                0x11223344, 0x55667700, 0xffeeddcc, 0xbbaa9988,
                                0x00112233, 0x44556677, 0x8899aabb, 0xcceeff0a,
                                0x11223344, 0x55667788, 0x99aabbcc, 0xeeff0a00,
                                0x22334455, 0x66778899, 0xaabbccee, 0xff0a0011
                            };
 
    uint32_t cipher_text[] = { 
                                0x0, 0x0, 0x0, 0x0,
                                0x0, 0x0, 0x0, 0x0,
                                0x0, 0x0, 0x0, 0x0,
                                0x0, 0x0, 0x0, 0x0
                            };
 
    uint32_t expect_cipher_text[] = {
                                        0x50796e7f, 0x4094ce10, 0xbab7374c, 0x981047e3,
                                        0x1ee4f83b, 0x334948ed, 0x86a0873c, 0x86bff9a2,
                                        0xa084f5fa, 0x965481e4, 0xb64be9bd, 0x32ef21e3,
                                        0xa6e376cf, 0x95e8a097, 0x9a46ba33, 0x152b1843
                                    };
 
    uint32_t key_length = sizeof(crypto_key)/sizeof(*crypto_key);
    uint32_t plain_text_length = sizeof(plain_text)/sizeof(*plain_text);
 
 
    /* Задать режим шифрования */
    HAL_Crypto_SetCipherMode(&hcrypto, CRYPTO_CIPHER_MODE_CBC);
    /* Установка вектора инициализации */ 
    HAL_Crypto_SetIV(&hcrypto, init_vector, sizeof(init_vector)/sizeof(*init_vector));
    /* Установка ключа */
    HAL_Crypto_SetKey(&hcrypto, crypto_key);
 
 
    /* Зашифровать данные */
    HAL_Crypto_Encode(&hcrypto, plain_text, cipher_text, plain_text_length);   
 
    xprintf("KEY ");
    for (uint32_t i = 0; i < key_length; i++)
    {
        xprintf("0x%08x, ", crypto_key[i]);
    }
    xprintf("\n"); 
 
    xprintf("plain: ");
    for (uint32_t i = 0; i < plain_text_length; i++)
    {
        xprintf("0x%08x, ", plain_text[i]);
    }
    xprintf("\n"); 
 
    xprintf("cipher: ");
    for (uint32_t i = 0; i < plain_text_length; i++)
    {
        xprintf("0x%08x, ", cipher_text[i]);
    }
    xprintf("\n");
 
    xprintf("expect: ");
    for (uint32_t i = 0; i < plain_text_length; i++)
    {
        xprintf("0x%08x, ", expect_cipher_text[i]);
    }
    xprintf("\n");
 
    for (uint32_t i = 0; i < plain_text_length; i++)
    {
        if(expect_cipher_text[i] != cipher_text[i])
        {
            xprintf("error\n");
            break;
        }
        else if ((i+1) == plain_text_length)
        {
            xprintf("Successfull\n");
        }
    }
    xprintf("\n");
}
</syntaxhighlight>
}}
 
Функция kuznechik_CBC_decode для расшифровки.
 
{{#spoiler:show=Развернуть код|hide=Свернуть код|
<syntaxhighlight lang="c" line="1">
void kuznechik_CBC_decode()
{
    uint32_t init_vector[IV_LENGTH_KUZNECHIK_CBC] = {0x12341234, 0x11114444, 0xABCDABCD, 0xAAAABBBB};
   
    uint32_t plain_text[] =    {           
                                    0x0, 0x0, 0x0, 0x0,
                                    0x0, 0x0, 0x0, 0x0,
                                    0x0, 0x0, 0x0, 0x0,
                                    0x0, 0x0, 0x0, 0x0
                                };
   
    uint32_t cipher_text[] = { 
                                0x50796e7f, 0x4094ce10, 0xbab7374c, 0x981047e3,
                                0x1ee4f83b, 0x334948ed, 0x86a0873c, 0x86bff9a2,
                                0xa084f5fa, 0x965481e4, 0xb64be9bd, 0x32ef21e3,
                                0xa6e376cf, 0x95e8a097, 0x9a46ba33, 0x152b1843
                            };
 
    uint32_t expect_plain_text[] =  {           
                                        0x11223344, 0x55667700, 0xffeeddcc, 0xbbaa9988,
                                        0x00112233, 0x44556677, 0x8899aabb, 0xcceeff0a,
                                        0x11223344, 0x55667788, 0x99aabbcc, 0xeeff0a00,
                                        0x22334455, 0x66778899, 0xaabbccee, 0xff0a0011
                                    };
   
 
    uint32_t key_length = sizeof(crypto_key)/sizeof(*crypto_key);
    uint32_t cipher_text_length = sizeof(cipher_text)/sizeof(*cipher_text);
 
   
 
    /* Задать режим шифрования */
    HAL_Crypto_SetCipherMode(&hcrypto, CRYPTO_CIPHER_MODE_CBC);
    /* Установка вектора инициализации */ 
    HAL_Crypto_SetIV(&hcrypto, init_vector, sizeof(init_vector)/sizeof(*init_vector));
    /* Установка ключа */
    HAL_Crypto_SetKey(&hcrypto, crypto_key);
 
 
    /* Расшифровать данные */
    HAL_Crypto_Decode(&hcrypto, cipher_text, plain_text, cipher_text_length);
 
    xprintf("KEY ");
    for (uint32_t i = 0; i < key_length; i++)
    {
        xprintf("0x%08x, ", crypto_key[i]);
    }
    xprintf("\n"); 
 
    xprintf("cipher: ");
    for (uint32_t i = 0; i < cipher_text_length; i++)
    {
        xprintf("0x%08x, ", cipher_text[i]);
    }
    xprintf("\n"); 
 
    xprintf("plain: ");
    for (uint32_t i = 0; i < cipher_text_length; i++)
    {
        xprintf("0x%08x, ", plain_text[i]);
    }
    xprintf("\n");
 
    xprintf("expect: ");
    for (uint32_t i = 0; i < cipher_text_length; i++)
    {
        xprintf("0x%08x, ", expect_plain_text[i]);
    }
    xprintf("\n");
 
    for (uint32_t i = 0; i < cipher_text_length; i++)
    {
        if(expect_plain_text[i] != plain_text[i])
        {
            xprintf("error\n");
            break;
        }
        else if ((i+1) == cipher_text_length)
        {
            xprintf("Successfull\n");
        }
    }
    xprintf("\n");
}
</syntaxhighlight>
}}
 
функция main.
 
{{#spoiler:show=Развернуть код|hide=Свернуть код|
<syntaxhighlight lang="c" line="1">
int main()
{   
 
    SystemClock_Config();
 
    UART_Init(UART_0, 3333, UART_CONTROL1_TE_M | UART_CONTROL1_M_8BIT_M, 0, 0);
 
    Crypto_Init();
 
    xprintf("\nkuznechik_CBC_code\n");
    kuznechik_CBC_code();
    xprintf("\nkuznechik_CBC_decode\n"); 
    kuznechik_CBC_decode();
 
    while (1)
    {   
       
    }
     
}
</syntaxhighlight>
}}
[[Файл:Kuznechik CBC.png|мини|Рисунок 4 - вывод в UART. CBC]]
Вывод в UART изображен на рисунке 4.
 
===Режим гаммирования (Counter, CTR)===
Размер массива для вектора инициализации можно задать с помощью макросов:   
* IV_LENGTH_KUZNECHIK_CTR
* IV_LENGTH_MAGMA_CTR   
* IV_LENGTH_AES_CTR   
Количество слов в данных для зашифровки должно быть кратно размеру блока (для кузнечика 4 слова или 128 бит). Если данных меньше, то их нужно дополнить вручную.
 
Функция kuznechik_CTR_code.
{{#spoiler:show=Развернуть код|hide=Свернуть код|
<syntaxhighlight lang="c" line="1">
void kuznechik_CTR_code()
{
    uint32_t init_vector[IV_LENGTH_KUZNECHIK_CTR] = {0x12345678, 0x90ABCEF0};
   
    uint32_t plain_text[] = {           
                                0x11223344, 0x55667700, 0xffeeddcc, 0xbbaa9988,
                                0x00112233, 0x44556677, 0x8899aabb, 0xcceeff0a,
                                0x11223344, 0x55667788, 0x99aabbcc, 0xeeff0a00,
                                0x22334455, 0x66778899, 0xaabbccee, 0xff0a0011
                            };
   
    uint32_t cipher_text[] = { 
                                0x0, 0x0, 0x0, 0x0,
                                0x0, 0x0, 0x0, 0x0,
                                0x0, 0x0, 0x0, 0x0,
                                0x0, 0x0, 0x0, 0x0
                            };
 
    uint32_t expect_cipher_text[] = {
                                        0xf195d8be, 0xc10ed1db, 0xd57b5fa2, 0x40bda1b8,
                                        0x85eee733, 0xf6a13e5d, 0xf33ce4b3, 0x3c45dee4,
                                        0xa5eae88b, 0xe6356ed3, 0xd5e877f1, 0x3564a3a5,
                                        0xcb91fab1, 0xf20cbab6, 0xd1c6d158, 0x20bdba73
                                    };
 
 
    uint32_t key_length = sizeof(crypto_key)/sizeof(*crypto_key);
    uint32_t plain_text_length = sizeof(plain_text)/sizeof(*plain_text);
 
 
    /* Задать режим шифрования */
    HAL_Crypto_SetCipherMode(&hcrypto, CRYPTO_CIPHER_MODE_CTR);
    /* Установка вектора инициализации */ 
    HAL_Crypto_SetIV(&hcrypto, init_vector, sizeof(init_vector)/sizeof(*init_vector));
    /* Установка ключа */
    HAL_Crypto_SetKey(&hcrypto, crypto_key);
 
    /* Зашифровать данные */
    HAL_Crypto_Encode(&hcrypto, plain_text, cipher_text, plain_text_length);
 
    xprintf("KEY ");
    for (uint32_t i = 0; i < key_length; i++)
    {
        xprintf("0x%08x, ", crypto_key[i]);
    }
    xprintf("\n"); 
 
    xprintf("plain: ");
    for (uint32_t i = 0; i < plain_text_length; i++)
    {
        xprintf("0x%08x, ", plain_text[i]);
    }
    xprintf("\n"); 
 
    xprintf("cipher: ");
    for (uint32_t i = 0; i < plain_text_length; i++)
    {
        xprintf("0x%08x, ", cipher_text[i]);
    }
    xprintf("\n");
 
    xprintf("expect: ");
    for (uint32_t i = 0; i < plain_text_length; i++)
    {
        xprintf("0x%08x, ", expect_cipher_text[i]);
    }
    xprintf("\n");
 
    for (uint32_t i = 0; i < plain_text_length; i++)
    {
        if(expect_cipher_text[i] != cipher_text[i])
        {
            xprintf("error\n");
            break;
        }
        else if ((i+1) == plain_text_length)
        {
            xprintf("Successful\n");
        }
    }
    xprintf("\n");
}
</syntaxhighlight>
}}
 
Функция kuznechik_CTR_decode.
 
{{#spoiler:show=Развернуть код|hide=Свернуть код|
<syntaxhighlight lang="c" line="1">
void kuznechik_CTR_decode()
{
    uint32_t init_vector[IV_LENGTH_KUZNECHIK_CTR] = {0x12345678, 0x90ABCEF0};
   
    uint32_t plain_text[] =  { 
                                0x0, 0x0, 0x0, 0x0,
                                0x0, 0x0, 0x0, 0x0,
                                0x0, 0x0, 0x0, 0x0,
                                0x0, 0x0, 0x0, 0x0
                            };
   
    uint32_t cipher_text[] = {
                                0xf195d8be, 0xc10ed1db, 0xd57b5fa2, 0x40bda1b8,
                                0x85eee733, 0xf6a13e5d, 0xf33ce4b3, 0x3c45dee4,
                                0xa5eae88b, 0xe6356ed3, 0xd5e877f1, 0x3564a3a5
                            };
 
    uint32_t expect_plain_text[] =  {           
                                        0x11223344, 0x55667700, 0xffeeddcc, 0xbbaa9988,
                                        0x00112233, 0x44556677, 0x8899aabb, 0xcceeff0a,
                                        0x11223344, 0x55667788, 0x99aabbcc, 0xeeff0a00,
                                        0x22334455, 0x66778899, 0xaabbccee, 0xff0a0011
                                    };
 
 
    uint32_t key_length = sizeof(crypto_key)/sizeof(*crypto_key);
    uint32_t cipher_text_length = sizeof(cipher_text)/sizeof(*cipher_text);
 
 
    /* Задать режим шифрования */
    HAL_Crypto_SetCipherMode(&hcrypto, CRYPTO_CIPHER_MODE_CTR);
    /* Установка вектора инициализации */ 
    HAL_Crypto_SetIV(&hcrypto, init_vector, sizeof(init_vector)/sizeof(*init_vector));
    /* Установка ключа */
    HAL_Crypto_SetKey(&hcrypto, crypto_key);
 
 
    /* Расшифровать данные */
    HAL_Crypto_Decode(&hcrypto, cipher_text, plain_text, cipher_text_length);
 
    xprintf("KEY ");
    for (uint32_t i = 0; i < key_length; i++)
    {
        xprintf("0x%08x, ", crypto_key[i]);
    }
    xprintf("\n"); 
 
    xprintf("cipher: ");
    for (uint32_t i = 0; i < cipher_text_length; i++)
    {
        xprintf("0x%08x, ", cipher_text[i]);
    }
    xprintf("\n"); 
 
    xprintf("plain: ");
    for (uint32_t i = 0; i < cipher_text_length; i++)
    {
        xprintf("0x%08x, ", plain_text[i]);
    }
    xprintf("\n");
 
    xprintf("expect: ");
    for (uint32_t i = 0; i < cipher_text_length; i++)
    {
        xprintf("0x%08x, ", expect_plain_text[i]);
    }
    xprintf("\n");
 
    for (uint32_t i = 0; i < cipher_text_length; i++)
    {
        if(expect_plain_text[i] != plain_text[i])
        {
            xprintf("error\n");
            break;
        }
        else if ((i+1) == cipher_text_length)
        {
            xprintf("Successful\n");
        }
    }
    xprintf("\n");
}
</syntaxhighlight>
}}
 
Функция main.
 
{{#spoiler:show=Развернуть код|hide=Свернуть код|
<syntaxhighlight lang="c" line="1">
int main()
{   
 
    SystemClock_Config();
 
    UART_Init(UART_0, 3333, UART_CONTROL1_TE_M | UART_CONTROL1_M_8BIT_M, 0, 0);


Для режима CTR длинна данных может быть любой. В этом режиме дополнения не требуются.
    Crypto_Init();


Синхропосылка(вектор инициализации) для разных режимов может иметь разную длину:
    xprintf("\nkuznechik_CTR_code\n");
    kuznechik_CTR_code();
    xprintf("\nkuznechik_CTR_decode\n"); 
    kuznechik_CTR_decode();


* В режиме ECB синхропосылка не используется;
    while (1)
* В режиме CBC синхропосылка должна быть длинной m*z, где m - длина блока (128 бит для алгоритма Кузнечик), z - целое число;
    {   
* В режиме CTR длинна синхропосылки равна половине длинны блока.
       
    }
     
}
</syntaxhighlight>
}}
[[Файл:Kuznechik CTR.png|мини|Рисунок 5 - вывод в UART. CTR]]
Вывод в UART изображен на рисунке 5.

Текущая версия от 09:24, 31 марта 2023

В примере будет зашифрованы и расшифрованы данные алгоритмом кузнечик. Данный процесс будет рассмотрен с различными режимами шифрования. Шифрования для алгоритмов «Магма» и AES128 полностью аналогичны, за исключением разрядности обрабатываемых данных.

Работа с конфигуратором (В разработке)

Для начала настроем в конфигураторе тактирование mik32, например, от внешнего кварца 32МГц. Затем настроем делители шины. Так как крипто-блок тактируется от шины AHB_CLK, то зададим делитель AHB_DIV. В данном примере оставим делитель по умолчанию. В итоге вкладка с тактированием должна выглядеть так:

(Картинка тактирования из конфигуратора. В работе)

Затем перейдем к настройке самого крипто-блока. Для этого откроем вкладку крипто-блок и нажмем включить. После этого появятся несколько настроек.

Рисунок 2 - настройки крипто-блока в конфигураторе

Зададим им следующие значения:

  • Алгоритм шифрования - Кузнечик;
  • Режим шифрования - ECB;
  • В перестановке слова - нет перестановки;
  • Порядок загрузки/выгрузки - От старшего слова к младшему.

В итоге настройки крипто-блока в конфигураторе должны выглядеть как на рисунке 2.

Нажимаем кнопку сохранения и генерации. В итоге у нас появится проект для PlatformIo. Далее работа идет в visual studio code.

Использование библиотеки HAL_Crypto

В сгенерированном проекте в файле main.c должна быть функция Crypto_Init, в которой будут заданы настройки для крипто-блока. Выглядит она так:

Кроме этого в функции SystemClock_Config приведены настройки для тактирования. Убедитесь, что в PeriphClkInit.PMClockAHB присутствует PM_CLOCK_CRYPTO_M. Сама функция должна выглядеть примерно так:

Для демонстрации вывода текста в PeriphClkInit.PMClockAPB_P присутствует PM_CLOCK_UART_0_M. У вас его может не быть, так как UART нужно включить отдельно. Для этого нужно подключить библиотеки uart_lib и xprintf.

Для инициализации UART в функции main, после функции тактирования SystemClock_Config, следует написать:

UART_Init(UART_0, 3333, UART_CONTROL1_TE_M | UART_CONTROL1_M_8BIT_M, 0, 0);

Скорость UART задается делителем во втором аргументе функции. При такой записи скорость будет 9600 бод.

В начале main.c можно видеть объявление структуры с набором настроек для крипто-блока, которую использует функция инициализации Crypto_Init.

Создадим глобальный массив для ключа. Количество элементов массива можно задать с помощью макросов CRYPTO_KEY_KUZNECHIK (количество слов в ключе для алгоритма кузнечик).

uint32_t crypto_key[CRYPTO_KEY_KUZNECHIK] = {0x8899aabb, 0xccddeeff, 0x00112233, 0x44556677, 0xfedcba98, 0x76543210, 0x01234567, 0x89abcdef};

Для режимов шифрования ECB и CBC длина данных должна быть кратна длине блока. Иначе данные следует дополнить.

Для режима CTR длинна данных может быть любой. В этом режиме дополнения не требуются.

Синхропосылка (вектор инициализации), в соответствии с ГОСТ 34.13—2015, для разных режимов может иметь разную длину:

  • В режиме ECB синхропосылка не используется;
  • В режиме CBC синхропосылка должна быть длинной m*z, где m - длина блока (128 бит для алгоритма Кузнечик), z - целое число. Поддерживается только случай z=1, m=n, где n - длина блока (4 слова для Кузнечика и AES, 2 слова для Магмы);
  • В режиме CTR в регистр INIT следует записывать значение счетчика состоящего из синхропосылки, длинна которой равна половине длине блока, и такого же количества нулей. В итоге размер синхропосылки будет равен размеру блока.

Режим простой замены (Electronic Codebook, ЕСВ)

Теперь напишем функции для зашифровки и расшифровки данных в режиме шифрования ECB - kuznechik_ECB_code и kuznechik_ECB_decode соответственно.

В функции kuznechik_ECB_code будет три массива данных:

  • plain_text - незашифрованные данные;
  • cipher_text - полученные зашифрованные данные;
  • expect_cipher_text - зашифрованные данные, которые ожидается получить.

Количество слов в данных для зашифровки должно быть кратно размеру блока (для кузнечика 4 слова или 128 бит). Если данных меньше, то их нужно дополнить вручную. ГОСТ 34.13—2015 определяет три возможные процедуры дополнения. Можно, например, дополнить остаток нулями до размера блока.

Внутри kuznechik_ECB_code сначала задается режим шифрования функцией HAL_Crypto_SetCipherMode (если другие режимы использовать не планируется, то этого можно не делать, так как этот режим шифрования уже задан после инициализации функцией Crypto_Init), затем устанавливается мастер-ключ функцией HAL_Crypto_SetKey. Так как ключ должен быть инициализирован в режиме шифрования, то после функции HAL_Crypto_SetKey блок находится в режиме зашифровки данных. После этого используется функция HAL_Crypto_Encode для зашифровки данных.

В конце поочередно выведем каждый из массивов в UART. После этого проведем сравнение полученных данных и ожидаемых.

Функция kuznechik_ECB_decode аналогична, но в ней используются другие массивы:

  • plain_text - полученные расшифрованные данные;
  • cipher_text - зашифрованные данные;
  • expect_plain_text - расшифрованные данные, которые ожидается получить.

Для расшифровки данных используется функция HAL_Crypto_Decode.

Функция main.

Рисунок 3 - вывод в UART

Вывод в UART изображен на рисунке 3.

Функции для режимов CBC и CTR аналогичны, но содержат другие данные в массивах.

Отличие этих режимов в том, что они перед каждой новой расшифровкой или зашифровкой требуют записи вектора инициализации с помощью функции HAL_Crypto_SetINIT.

Режим простой замены с зацеплением (Cipher Block Chaining, СВС)

Размер массива для вектора инициализации можно задать с помощью макросов:

  • IV_LENGTH_KUZNECHIK_CBC
  • IV_LENGTH_MAGMA_CBC   
  • IV_LENGTH_AES_CBC     

Количество слов в данных для зашифровки должно быть кратно размеру блока (для кузнечика 4 слова или 128 бит). Если данных меньше, то их нужно дополнить вручную.

Функция kuznechik_CBC_decode для расшифровки.

функция main.

Рисунок 4 - вывод в UART. CBC

Вывод в UART изображен на рисунке 4.

Режим гаммирования (Counter, CTR)

Размер массива для вектора инициализации можно задать с помощью макросов:   

  • IV_LENGTH_KUZNECHIK_CTR
  • IV_LENGTH_MAGMA_CTR   
  • IV_LENGTH_AES_CTR   

Количество слов в данных для зашифровки должно быть кратно размеру блока (для кузнечика 4 слова или 128 бит). Если данных меньше, то их нужно дополнить вручную.

Функция kuznechik_CTR_code.

Функция kuznechik_CTR_decode.

Функция main.

Рисунок 5 - вывод в UART. CTR

Вывод в UART изображен на рисунке 5.