Разработка электроники,

Систем автоматики,

Программного обеспечения

ООО "Антех ПСБ",
Санкт-Петербург

+79811865082

anteh@bk.ru

ООО Антех ПСБ примеры

AVR Assembler реализация ГОСТ28147-89 магма шифрование дешифрование режимы ECB и счетчика с гаммированием

22.09.2020 https://anteh.ru

Понадобилось потоковое шифрование. ГОСТ28147-89 оказался волшебным, оперативной памяти нужно всего 28 байт и реализация буквально в пару тройку десятков ассемблерных строк. Сохраняя алгоритм работы ГОСТ28147-89, отсебятины навертеть можно какой угодно без потери криптоустойчивости. По поводу криптоустойчивости, это предположения человека академически не разбирающегося в шифровании, но уже реализованы несколько алгоритмов шифрования для разных областей применения. Базовый алгоритм ГОСТ28147-89 реализован в соответствии с описанием. Используется стандартная таблица подстановок, превращающая ГОСТ28147-89 в Магму. Раундовые ключи никак не формируются, не вижу смысла, используются четверки байт прямо из ключа шифрования. Как не перемешивай байты в ключе, все равно получите ключ. Чередование раундовых ключей при шифровании/дешифровании сделано не через формирование отдельных раундовых ключей, а через предустановленную таблицу смещений JMap, тетрада каждого байта таблицы указывает на адрес 4х байтного раундового ключа в ключе шифрования.

При поиске примера реализации алгоритма, было трудно найти какой-то однозначный пример, постоянно у всех что-то отличалось. Потом стало понятно почему, у самого получилось что-то своё. Если Вам нужно строгое соответствие стандарту, допилить существующее не составит сложности. Основные алгоритмы в функциях GOST28147_89_ECB_encrypt GOST28147_89_ECB_decrypt реализованы согласно описанию. Алгорим счетчика с гаммированием реализован как отсебятина -однократно, для шифрования/дешифрования всего блока данных берется 8 константных случайных байт инициализации. Ими Инициализируется 8 байтный инкрементный счетчик. Перед шифрованием каждого 8 байтного блока данных счётчик увеличивается/изменяется и шифруется -получаем гамму и после шифрования 8 байтного блока данных накладываем гамму на зашифрованные данные. Т.е. шифрование производится 2 раза 8 байт счетчика для получения гаммы и 8 байт самих данных.

Таблица подстановок Sbox стандартная Магма, она сжатая, две строки тетрадами объединены в одну и записаны снизу вверх. Таблица JMap строго фиксированная она вместо формирования раундовых ключей, задаёт очерёдность применения раундовых ключей при шифровании дешифровании. В качестве изначального примера реализации ГОСТ28147-89 брался проект https://github.com/dlech/bccrypto-csharp файл bccrypto-csharp-master.zip там много всяких алгоритмов, одина беда с универсальностью перебор. Придется постараться, чтобы нужное откапать и лишнее, почти все, выкинуть, частично мишуру переписать и убедиться что ничего не сломали. Кое что про ГОСТ http://gostcrypto.com/doc/GostCipher.html

Ниже код AVR Assembler. Код не оптимизировался, чтобы было проще разобраться что к чему:

.dseg
RXBnCTRL: .BYTE 13
;==== !!! МЕСТАМИ НЕ МЕНЯТЬ !!! ====
GAMMABuf: .BYTE 8
GAMMA: .BYTE 8
;-----------------------------------

.cseg
;Однократный запуск при инициализации шифрования/дешифрования
rcall   GammaInit


;Применение:
rcall   GOST28147_89_ECB_encrypt        ;RXBnCTRL+1+4 адрес обрабатываемых 8 байт
rcall   GOST28147_89_ECB_decrypt
rcall   GOST28147_89_encrypt_CNTRgamma
rcall   GOST28147_89_decrypt_CNTRgamma


;Код:
;=========================================================================
;GOST28147-89 режимы ECB и счетчика с гаммированием. 
;Основные алгоритмы соответствуют GOST28147-89, если нужно полное соответствие GOST28147-89 то дорабатываем самостоятельно
;RXBnCTRL -некий 1+4+8=13 байтный буфер содержащий 8 байт обрабатываемых данных. +1 -нигде не используется, оставлено чтобы текст сильно не править
;GOST28147_89_ECB_encrypt               -шифрование ECB. Простые подстановки.
;GOST28147_89_ECB_decrypt               -дешифрование ECB. Простые подстановки.
;       Если размер обрабатываемых данных не превышает 8 байт или не сильно превышает, то достаточно ECB режима
;GOST28147_89_encrypt_CNTRgamma -шифрование алгоритм счетчика с гаммированием
;GOST28147_89_decrypt_CNTRgamma -дешифрование алгоритм счетчика с гаммированием
;
;Раундовые ключи по факту и есть сам ключь шифрования. Все раундовые ключи 
;берутся из ключа без каких либо преобразований. Очередность применения
;раундовых ключей задаётся в JMap согласно GOST28147-89
;Инициализацию счетчика для гаммирования можете сделать свою
;=========================================================================
//Sbox 64 байта или 128 тетрад подстановок, таблица из ГОСТ Р 34.12-2015 МАГМА
//byte pi_0[] = {12, 4, 6, 2, 10, 5, 11, 9, 14, 8, 13, 7, 0, 3, 15, 1};
//byte pi_1[] = {6, 8, 2, 3, 9, 10, 5, 12, 1, 14, 4, 7, 11, 13, 0, 15};
//byte pi_2[] = {11, 3, 5, 8, 2, 15, 10, 13, 14, 1, 7, 4, 12, 9, 6, 0};
//byte pi_3[] = {12, 8, 2, 1, 13, 4, 15, 6, 7, 0, 10, 5, 3, 14, 9, 11};
//byte pi_4[] = {7, 15, 5, 10, 8, 1, 6, 13, 0, 9, 3, 14, 11, 4, 2, 12};
//byte pi_5[] = {5, 13, 15, 6, 9, 2, 12, 10, 11, 7, 8, 1, 4, 3, 14, 0};
//byte pi_6[] = {8, 14, 2, 5, 6, 9, 1, 12, 15, 4, 11, 0, 13, 10, 3, 7};
//byte pi_7[] = {1, 7, 14, 13, 0, 5, 8, 3, 4, 15, 10, 6, 9, 12, 11, 2};
Sbox: //Тетрады подстановок объединены в байты. Строки записаны старшие по младшему адресу
.db     0x18,0x7E,0xE2,0xD5,0x06,0x59,0x81,0x3C,0x4F,0xF4,0xAB,0x60,0x9D,0xCA,0xB3,0x27
.db     0x57,0xDF,0xF5,0x6A,0x98,0x21,0xC6,0xAD,0xB0,0x79,0x83,0x1E,0x4B,0x34,0xE2,0x0C
.db     0xCB,0x83,0x25,0x18,0xD2,0x4F,0xFA,0x6D,0x7E,0x01,0xA7,0x54,0x3C,0xE9,0x96,0xB0
.db     0x6C,0x84,0x26,0x32,0x9A,0xA5,0x5B,0xC9,0x1E,0xE8,0x4D,0x77,0xB0,0xD3,0x0F,0xF1

Key: //Ключ шифрования, он же раундовые ключи для шифрования и дешифрования
        .db     0xee, 0x37, 0x5a, 0x41
        .db     0x7f, 0x35, 0xd6, 0xab
        .db     0xc3, 0x4a, 0x3b, 0x12
        .db     0x45, 0x41, 0xab, 0x5e
        .db     0x58, 0xa8, 0x3b, 0xca
        .db     0x48, 0xa5, 0x5a, 0x3e
        .db     0x46, 0x64, 0xa4, 0x5f
        .db     0x61, 0xab, 0xc4, 0xeb
/*;Адреса раундовых ключей при шифровании. Первоначальный вариант
JMapEncrypt:    .db       28, 24, 20, 16, 12,  8,  4,  0
                                .db        0,  4,  8, 12, 16, 20, 24, 28
                                .db        0,  4,  8, 12, 16, 20, 24, 28
                                .db        0,  4,  8, 12, 16, 20, 24, 28
;Адреса раундовых ключей при дешифровании. Первоначальный вариант
JMapDecrypt:    .db       28, 24, 20, 16, 12,  8,  4,  0
                                .db       28, 24, 20, 16, 12,  8,  4,  0
                                .db       28, 24, 20, 16, 12,  8,  4,  0
                                .db        0,  4,  8, 12, 16, 20, 24, 28*/
;Адреса раундовых ключей в Sbox при шифровании/дешифровании компактный вариант
;Старшая тетрада содержит /2 указатель на раундовый ключ к шифрованию. /2 означает деленное на 2 адреса указателя чтобы влез в тетраду
;Младшая тетрада содержит /2 указатель на раундовый ключ к дешифрованию
JMap:   .db       0xEE, 0xCC, 0xAA, 0x88, 0x66, 0x44, 0x22, 0x00
                .db       0x0E, 0x2C, 0x4A, 0x68, 0x86, 0xA4, 0xC2, 0xE0
                .db       0x0E, 0x2C, 0x4A, 0x68, 0x86, 0xA4, 0xC2, 0xE0
                .db       0x00, 0x22, 0x44, 0x66, 0x88, 0xAA, 0xCC, 0xEE
//RXBnCTRL+1+4 -содержит 8 байт входных данных
//RXBnCTRL+1+4 -после преобразования будут содержать 8 выходных байт данных на тех же местах
//tmp = RXBnCTRL+1       - RXBnCTRL+1+3
//N1  = RXBnCTRL+1+4 - RXBnCTRL+1+7
//N2  = RXBnCTRL+1+8 - RXBnCTRL+1+11
GOST28147_89_ECB_encrypt:       ;RXBnCTRL+1+4 адрес шифруемых 8 байт
                andi    r21, 0b11111110 ;флаг шифрования
                rjmp    Efddrg5ed
GOST28147_89_ECB_decrypt:       ;RXBnCTRL+1+4 адрес дешифруемых 8 байт
                ori     r21, 0b00000001 ;флаг дешифрования
        Efddrg5ed:

                ldi     r20, 31 ;Загрузка адреса раундового ключа в JMap, тетрада выбирается флагом r21:0
        NextIterD:
                //tmp = N1
                ldi     r27, high(RXBnCTRL+1+4) ;N1
                ldi     r26, low(RXBnCTRL+1+4)
                ldi     r29, high(RXBnCTRL+1) ;tmp
                ldi     r28, low(RXBnCTRL+1)
                ldi     r16, 4
        Nxopderb:
                ld              r17, X+
                st              Y+, r17
                dec             r16
                brne    Nxopderb

                //N1 = N2 ^ Gost28147_mainStep(N1, workingKey[j]); // CM2
                rcall   Gost28147_mainStep
                ldi     r18, 4
        Nxde5gf:
                ld              r16, X  ;RXBnCTRL+1+4 N1
                adiw    r26, 4  
                ld              r17, X  ;RXBnCTRL+1+8 N2
                sbiw    r26, 4
                eor             r17, r16
                st              X+, r17 //N1 = N2 ^ Gost28147_mainStep(N1, workingKey[j]); // CM2
                dec             r18
                brne    Nxde5gf

                //N2 = tmp
                ldi     r29, high(RXBnCTRL+1) ;tmp
                ldi     r28, low(RXBnCTRL+1)
                ldi     r16, 4
        Nxdeksw:
                ld              r17, Y+
                st              X+, r17
                dec             r16
                brne    Nxdeksw

                dec             r20
                brpl    NextIterD       ;Переход на обработку следующего раунда

                //Шег не нужен. N2 = N2 ^ Gost28147_mainStep(N1, workingKey[0]);  // 32 step (N1=N1)
                //Результат 8 байт будет в tmp + N1 оно же 8 байт по адресу RXBnCTRL+1
                //Корректировка расположения байт
                ldi     r27, high(RXBnCTRL+1+4+4)
                ldi     r26,  low(RXBnCTRL+1+4+4)
                ldi     r16, 8
        Nxfjfssef:
                ld              r17, -X
                adiw    r26, 4
                st              X, r17
                sbiw    r26, 4
                dec             r16
                brne    Nxfjfssef
ret

//Gost28147_mainStep(N1, workingKey[j]) превращается в Gost28147_mainStep(RXBnCTRL+1+4, workingKey[r20])
//tmp = RXBnCTRL+1
//N1  = RXBnCTRL+1+4
//N2  = RXBnCTRL+1+4+4
//j = JMap(r20)
Gost28147_mainStep:
                //int cm = (key + n1); // CM1
                //RXBnCTRL+1+4 = RXBnCTRL+1+4 + key
                ldi     r31, high(JMap*2) ;Таблица указателей на раундовых ключи для шифрования/дешифрования
                ldi     r30, low(JMap*2)        
                add             r30, r20
                adc             r31, r15
                lpm             r16, Z          ;указатель на раундовый ключ
                sbrs    r21, 0
                swap    r16                     ;Выбор индексов для шифрования или дешифрования
                andi    r16, 0b00001111
                lsl             r16                     ;*2 в JMap указатели уменьшенны в 2 раза, чтобы они влезали в тетраду

                ldi     r31, high(Key*2) ;=== N1 + rKey[r20]
                ldi     r30, low(Key*2)
                add             r30, r16
                adc             r31, r15
                ldi     r27, high(RXBnCTRL+1+4+4) ;N1:L
                ldi     r26, low(RXBnCTRL+1+4+4)
                ldi             r16, 4  ;!!! Флаг переноса здесь гарантированно сброшен
        Nxsder4f:
                ld              r17, -X ;
                lpm             r18, Z+
                adc             r17, r18
                st              X, r17
                dec             r16
                brne    Nxsder4f        ;--- N1 + rKey[r20]
                adiw    r26, 4          

                ;операция подстановки
                ldi     r16, 3  ;4 обрабатываемых байта для операции подстановки
        Nxhgfjhtr6:             
                ldi     r31, high(Sbox*2)
                ldi     r30, low(Sbox*2)
                swap    r16             ;*4 настройка на строку Sbox
                add             r30, r16
                adc             r31, r15
                swap    r16             ;/4 возврат к исходному значению
                ld              r17, -X         ;Чтение N1:0 из N1:0 N1:1 N1:2 N1:3
                andi    r17, 0b00001111
                add             r30, r17
                adc             r31, r15
                lpm             r18, Z
                sub             r30, r17        ;Индекс возвращаем обраьно
                sbc             r31, r15
                andi    r18, 0b00001111 ;r18 накопитель двух тетрад после подстановки
                ld              r17, X          ;Снова чтение N1:0 из N1:0 N1:1 N1:2 N1:3
                swap    r17
                andi    r17, 0b00001111
                add             r30, r17
                adc             r31, r15
                lpm             r17, Z
                andi    r17, 0b11110000
                or              r18, r17
                st              X, r18
                dec             r16
                brpl    Nxhgfjhtr6
                
                ;Циклический сдвиг RXBnCTRL+1+4 на 11 влево. return om << 11 | om >>> (32-11); // 11-leftshift
                ld              r31, X+
                ld              r30, X+
                ld              r18, X+
                ld              r17, X
                ldi     r16, 3
        Nxref5f:
                clc
                rol             r17
                rol             r18
                rol             r30
                rol             r31
                adc             r17, r15
                dec             r16
                brne    Nxref5f
                st              X, r31
                st              -X, r17
                st              -X, r18
                st              -X, r30
ret

;Начальное значение инкриментного счетчика GAMMA взято из ключа шифрования,
;без тайного смысла для удобства, начальное сочтояние счётчика для 
;формирования гаммыможете задать самостоятельно
GammaInit:
                ldi     r31, high(Key*2)
                ldi     r30, low(Key*2)
                ldi     r27, high(GAMMA)
                ldi     r26, low(GAMMA)
                ldi     r16, 8
        Nxdfeeffgs:
                lpm             r17, Z+
                andi    r17, 0b00110010 ;Защита от переполнения + разнообразие
                st              X+, r17
                dec     r16
                brne    Nxdfeeffgs
ret
;Инкримент 8 байтного счетчика на 1
;!!! Переполнение счетчика и запарывание чужого байта по адресу GAMMA-1 новозможно
GammaIncr:
                ldi     r29, high(GAMMA+8)
                ldi     r28, low(GAMMA+8)
        Nxdfeeffgsr:
                ld              r17, -Y
                inc             r17
                st              Y, r17
                breq    Nxdfeeffgsr
ret

;Шифрование алгоритм счётчика с гаммированием
;RXBnCTRL+1+4 адрес шифруемых / дешифруемых 8 байт
GOST28147_89_encrypt_CNTRgamma:
                andi    r21, 0b11111101 ;Шифрование или сброс флага дешифрования
                rcall   GOST28147_89_ECB_encrypt
                rjmp    jhfgyrfs
GOST28147_89_decrypt_CNTRgamma:
                ori             r21, 0b00000010 ;Установка флага дешифрования или сброс шифрования
        jhfgyrfs:
                rcall   GammaIncr       ;инкримент счетчика гаммы

        ;Сохраняем зашифрованные данные в буфер GAMMABuf
                ldi     r27, high(RXBnCTRL+1+4)
                ldi     r26, low(RXBnCTRL+1+4)
                ldi     r29, high(GAMMABuf)
                ldi     r28, low(GAMMABuf)
                ldi     r16, 8
        dfjf4df:
                ld              r17, X+
                st              Y+, r17
                dec             r16
                brne    dfjf4df

                        ;Копируем гамму в буфер шифрования
                ldi     r29, high(GAMMA)
                ldi     r28, low(GAMMA)
                ldi     r27, high(RXBnCTRL+1+4)
                ldi     r26, low(RXBnCTRL+1+4)
                ldi     r16, 8
        hekjwder:
                ld              r17, Y+         ;GAMMA
                st              X+, r17
                dec             r16
                brne    hekjwder

                rcall   GOST28147_89_ECB_encrypt ;Шифруем/создаём гамму

                ;Накладываем гамму на зашифрованные данные
                ldi     r27, high(RXBnCTRL+1+4+8)
                ldi     r26, low(RXBnCTRL+1+4+8)
                ldi     r29, high(GAMMABuf+8)
                ldi     r28, low(GAMMABuf+8)
                ldi             r16, 8
        dhjfsdfrq:
                ld              r17, -Y
                ld              r18, -X
                eor             r18, r17
                st              X, r18
                dec             r16
                brne    dhjfsdfrq

                sbrc    r21, 1          ;Если шифрование то пропуск
                rcall GOST28147_89_ECB_decrypt
ret
;-----------------------------------------------------

Copyright ©Новиков Алексей Александрович,

2023 Санкт-Петербург, 197372, ООО "Антех ПСБ",

anteh собака bk.ru