Показаны сообщения с ярлыком CRISP. Показать все сообщения
Показаны сообщения с ярлыком CRISP. Показать все сообщения

среда, 6 ноября 2019 г.

CRISP 1.0 -- Протокол защищенного обмена для индустриальных систем


Протокол не важен. Важно что это первый нормальный набор векторов для Magma (ГОСТ 34.12-2015)в режиме генерации имитовставки (IMIT) и в режим гаммирования (CTR) для данных не выровненных. Сами режимы описаны в документе (ГОСТ 34.13-2015).

Протокол защищенного обмена выпущен ТС26 (Технический комитет по стандартизации российской криптографии) в виде методических рекомендаций.
Основная проблема в реализации режимов блочного шифрования - Это, не глядя в чужой код, получить точное соответствие с векторами. Сразу никогда не получается.
Где-то это мои проблемы - читать не умею. Где-то проблемы разработчика - описано плохо. Но чаще всего - это неточности в описании алгоритма, который надо как-то отладить, и ошибок в реализации может быть больше одной на пути рабочей версии.
Почему путь тернист. Берем сам алгоритм блочного шифра, "Магма" (ГОСТ 34.12-2015): описан, есть примеры - тестовые вектора. Взял алгоритм, сделал реализацию, но - не работает. Нигде в стандарте не сказано, в каком представлении даны числа в тестовых векторах. Порядок следования байт может быть Little-Endian или Network (Big-Endian). С ключами шифрования - тоже самое, они могут быть в нормальном порядке, задом наперед по 64 бита, задом наперед/совсем задом наперед - все число от старшего бита к младшему вывернуто. В процессе отладки я подбираю, как правильно записать, в какой последовательности, входные и выходные данные, чтобы числа сошлись. Сходятся.
Потом беру описание режима блочного шифра (ГОСТ 34.13-2015). В тестовых векторах та же путаница не ясно, как должны сходится эти вектора с изменение порядка следования байт или без изменения. Примеры почему то даны только для случая выровненных данных. В случае с усечением или без выравнивания на 64 бита, алгоритм остается не отлаженным.


 Теоретически мы можем предположить, что алгоритм работает так:
(подготовка ключа- надо решить как его правильно выкрутить, чтобы из сетевого представления получить числовое) -- (подготовка данных из последовательного представления в числовое) -- (подать на вход ключ и данные) -- (результат выкрутить в "сетевое" представление). При этом существуют некоторые неточности изложения, в результате которых такие же неоднозначные преобразования могут содержаться внутри самого алгоритма. Парадокс в том, что при этом вектора сходятся, потому что дают те же числа только в ином порядке.

Например, смотрим определения, чем отличается "бинарное представление", "байтовое представление" и числовое. Как разгадать этот ребус. По-русски ведь написано.
x||y -- конкатенация двоичных строк x и y ... при которой левая часть - слева, а правая - справа.
Потом пишут 1||0 - вот это чего за число. или это 0x02? Или это число 0x01? Оказывается это число 0x80.Оказывается оно таким после серии проб и ошибок.
binary() -- это представление символьной строки в виде байтовой строки. -- отпад.
byte() -- это представление числа в виде байтовой строки, при котором соответствующая итоговой байтовой строке двоичная строка .... -- вынос мозга.
вот у меня есть число 0xBAD как его представить в виде байтовой строки: "0B AD" или "AD 0B". Отсюда и берутся такие неоднозначности. Числа можно представить в "сетевом" порядке или в порядке свойственном аппаратуре LE|BE.
На самом деле, если четко договориться по терминам, что такое "двоичная строка", что такое "байтовая строка", что такое "число", что такое "символьная строка", как одно в другое переводится, то проблем нет. Авторы пытаются прикрыть неточность выражения мыслей обилием математических выражений типа Zn||...||Z2||Z1||Z0 - такие выражения никак не облегчаются понимание вопроса по выравниванию данных. Биты которые однозначно слева в "байтовом представлении" могут быть как слева так и справа. Но после прочтения не всегда это ясно.

Вот например каким количеством вариантов можно описать действие
IV = LSB_32(byte(SeqNum, 6)) -- по сути это означает что мы должны взять младшие 32 бита от байтового представления числа, при генерации байтового представления нужно взять 6 байт от числа, т.е 48 бит. Пишу варианты:
uint64_t SeqNum;
uint32_t IV = htonll(SeqNum); (1)
uint32_t IV = htonll(SeqNum<<16); (2)
uint32_t IV = (SeqNum); (3)
uint32_t IV = htonll(SeqNum)>>32; (3)
...
Или вот другая формула SN = 0^5||MSB_35(byte(SeqNum,6))
Если под функцией byte() автор понимает преставление байт из числового представления в сетевое, то это запишется так:
uint64_t SN = (swap64(SeqNum<<16)) & ((1ULL<<35)-1)); (3)
uint64_t SN = ((SeqNum>>(48-35)) & ((1ULL<<35)-1)); (4)
-- я реально перебираю все эти варианты, ориентируясь всего на один признак, результат равен или нет тестовому вектору. После подбора я нахожу, что автор имел ввиду:
SN = старшие_биты(0^5)||число(MSB_35(число(SeqNum,6)). -- сшивание битовых строк/чисел.
При укладке данных SN в "байтовую строку" происходит преобразование "в сетевом" порядке следования байт.
Строка = ...|| byte(SN,5) ||... -- число SN (40бит, 5 байт) разворачивается в порядке Big-Endian, в сетевом представлении. Откуда это следует? -- Я догадался.
Можно вчитываться в документацию, пытаться понять, все ли точно выражено в описании или все таки бинарное представление итоговой байтовой строки от числового представления сдвинутого на пять бит делается как-то иначе. А если в изложении несколько таких неточностей, или я не так понял и в моей реализации неточность или ошибка.
Может автор прав, есть однозначная запись битовых строк, операция MSB применима к битовым строкам, операция byte() преобразует число в битовую строку. Проверяем определения... Я понял, все дело в том, что битовые строки почему-то неявным образом переводятся в байтовые с выравниванием по старшему биту.
При использовании блочного шифра, я почему-то упускаю: надо данные из сетевого представления переводить в числовое перед использованием функции шифрования или надо результат функции шифрования переводить из локального в сетевое представление перед использованием. Такие вот проблемы. 

Когда результат достигнут - все просто. Такие задачи я использую исключительно для тренировки. Перед началом нового проекта, я берусь реализовать какой-нибудь новый для себя криптографический алгоритм не глядя в чужие реализации, по документации. В этот раз неделю потратил.