Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Практическая криптография алгоритмы и их программирование

.pdf
Скачиваний:
0
Добавлен:
08.09.2026
Размер:
2 Мб
Скачать
Глава 4. Как устроены современные шифры 111
# преобразуем данные к числовому формату
$left = unpack("L", pack("a4", substr($data, 0, 4))); $right = unpack("L", pack("a4", substr($data, 4, 4))); $j=0; print "KEYS: "; grep {
# выбираем номер подключа для зашифрования/расшифрования $j=($_&7)-1; $j=7if$j<0; $decrypt ? ($_ >= 9) && ($j = (32 - $_) & 7)
: ($_ >= 25) && ($j = 32 - $_);
print $j, " ";
# шифруемся $left ^= f($right + $$key[$j]);
# меняем половинки местами $swap = $right; $right = $left; $left = $swap;
} (1..32); # 32 раунда
# меняем половинки местами
$swap = $right; $right = $left; $left = $swap; return pack("L", $left) . pack("L", $right);
}
# инициализируем 256 битов ключа $KEY[0] = 0x12345678; $KEY[1] = 0x9abcdef0; $KEY[2] = 0xf0debc9a; $KEY[3] = 0x78563412; $KEY[4] = 0x12345678; $KEY[5] = 0x9abcdef0; $KEY[6] = 0xf0debc9a; $KEY[7] = 0x78563412;
$a = "DatLDatR"; print "ORIGIN: $a\n";
print "@KEY\n"; # зашифруем $b = gostcrypt($a, \@KEY, 0); print "\nENCIPHERED: $b\n";
print "@KEY\n"; # расшифруем $c = gostcrypt($b, \@KEY, 1); print "\nDECIPHERED: $c\n";
Результат ее работы показан в листинге 4.22. Первые строчки чисел после слов ORIGIN и ENCIPHERED не что иное, как значения 32-битовых элементов главного ключа. Порядок использования ключей обозначен в следующих за ними
112 Глава 4. Как устроены современные шифры
строках (эти значения можно сверить с приведенными немного выше). Обратите внимание на то, как заданы узлы замен (массив SBOX содержит ссылки на масси­вы уже соответствующих узлов замен). Сами узлы замен представлены обычны­ми массивами по 16 элементов в каждом, содержащими значения от 0 до 15.
Листинг 4.22
X:\cryptobook\sources>perl gost.pl
ORIGIN: DatLDatR 305419896 2596069104 4041129114 2018915346 305419896 2596069104 4041129114 2018915346 KEYS:01234567012345670123456776543210 ENCIPHERED: øy(0Íd)! 305419896 2596069104 4041129114 2018915346 305419896 2596069104 4041129114 2018915346 KEYS:01234567765432107654321076543210 DECIPHERED: DatLDatR
Функция гаммирования вынесена в отдельную процедуру исключительно для наглядности. В настоящей реализации ее, конечно, следует внести внутрь основ­ной функции, «расписав» и развернув цикл в 32 последовательных вызова функ­ции (см. листинг 4.23).
Листинг 4.23
sub gostcrypt_linear($$$) {
my ($data, $key, $decrypt) = @_; my ($swap, $left, $right, $j); # преобразуем данные к числовому формату $left = unpack("L", pack("a4", substr($data, 0, 4))); $right = unpack("L", pack("a4", substr($data, 4, 4))); $j=0; grep {
# развернем четыре цикла $j = ($_ & 7)-1; $j=7 if $j<0;
$decrypt?($_>=9)&&($j=(32-$_)&7):($_>=25)&&($j=32-$_);
$left ^= f($right + $$key[$j]); $j = ($_ & 7)-1; $j=7 if $j<0;
$decrypt?($_>=9)&&($j=(32-$_)&7):($_>=25)&&($j=32-$_);
$right ^= f($left + $$key[$j]); $j = ($_ & 7)-1; $j=7 if $j<0;
$decrypt?($_>=9)&&($j=(32-$_)&7):($_>=25)&&($j=32-$_);
$left ^= f($right + $$key[$j]); $j = ($_ & 7)-1; $j=7 if $j<0;
$decrypt?($_>=9)&&($j=(32-$_)&7):($_>=25)&&($j=32-$_);
$right ^= f($left + $$key[$j]); $j = ($_ & 7)-1; $j=7 if $j<0;
$decrypt?($_>=9)&&($j=(32-$_)&7):($_>=25)&&($j=32-$_);
$left ^= f($right + $$key[$j]); $j = ($_ & 7)-1; $j=7 if $j<0;
$decrypt?($_>=9)&&($j=(32-$_)&7):($_>=25)&&($j=32-$_);
$right ^= f($left + $$key[$j]); $j = ($_ & 7)-1; $j=7 if $j<0;
Глава 4. Как устроены современные шифры 113
$decrypt?($_>=9)&&($j=(32-$_)&7):($_>=25)&&($j=32-$_);
$left ^= f($right + $$key[$j]); $j = ($_ & 7)-1; $j=7 if $j<0;
$decrypt?($_>=9)&&($j=(32-$_)&7):($_>=25)&&($j=32-$_);
$right ^= f($left + $$key[$j]); } (1..8); # уже 8 = 32/4 циклам (раундов 32, 4 развернуты) $swap = $right; $right = $left; $left = $swap; return pack("L", $left) . pack("L", $right);
}
Особенного внимания заслуживают манипуляции c входными данными с по-
мощью операторов pack и unpack:
$left = unpack("L", pack("a4", substr($data, 0, 4))); $right = unpack("L", pack("a4", substr($data, 4, 4)));
Такой подход используется для того, чтобы обеспечить универсальность ме­тода обработки данных. В этом случае данные, возможно представленные изна­чально в разных форматах, будут приведены к целочисленному формату 32-бито­вых чисел.
Раунды шифрования выполняются с помощью весьма эффективного в Perl оператора grep (еще один вариант реализации — использование оператора map). От остальных способов (например, в виде цикла) этот отличается относительной быстротой выполнения.
После того как код отлажен, его можно представить в удобочитаемом виде. Ведь мало у кого есть исходные тексты ГОСТ, написанные в пять строк кода, как, например, в листинге 4.24.
Листинг 4.24
sub F{$u=0;grep$u|=$S[$_][$_[0]>>$_*4&15]<<$_*4,reverse 0..7;$u<<11|$u>>21}sub R {int$_[0]*rand}sub N{vec$_[0],$_[1]/4,32}$e=join'',<>;@b=@t=0..15;for(;$i<length $p;$i+=4){srand($s^=N$p,$i)}while($c<8){grep{push@b,splice@b,R(9),5}@t;$R[$c]=R( 2**32);@{$S[$c++]}=@b}@h=0..7;@o=reverse@h;while($a<length$e){$v=N$e,$a;$w=N$e,( $a+=8)-4;grep$q++%2?$v^=F$w+$K[$_]:($w^=F$v+$K[$_]) ,$d?(@h,(@o)x3):((@h)x3,@o); $_.=pack N2,$w,$v}print
В заключение надо особо отметить принципы разработки процедур развора­чивания ключей. Несколько необходимых свойств заметны невооруженным взглядом даже неспециалисту. Первое из них заключается в необходимости нали­чия зависимости подключей от наибольшего числа битов основного ключа. При этом это не должно требовать слишком много работы (поскольку в этом случае
20
скорость алгоритмов смены ключей будет чрезвычайно замедленной
). Энтропия
подключей не должна быть значительно ниже чем у исходного ключа и не должна
20
Последняя часть правила работает не всегда. Например, в алгоритме блочного шифрова ния Blowfish процедура выработки цикловых подключей умышленно замедлена — для выработ ки очередного ключа используется весь алгоритм зашифрования с ранее зафиксированными ключами. И сделано это специально — для замедления процесса тотального опробования.
-
-
114 Глава 4. Как устроены современные шифры
снижаться по мере выработки каждого следующего подключа (проверяется это выработкой большого количества подключей из некоторого множества главных, а затем элементарными подсчетами по формулам энтропии и вероятностей). Не должно быть очевидно зависимых и слабых ключей, с учетом свойства компле­ментарности.
21
Для предотвращения появления слабых ключей обычно вводят циклические сдвиги подключей. Если их не производить, то очевидно будут суще­ствовать ключи, для которых все подключи будут равны.
Архитектура SQUARE: от 3-WAY до AES
NIST claimed that security was the key factor, yet they deci­ded to recommend Rijndael, the candidate with the smallest se­curity margin of the five finalists.
Из интервью с Ларсом Кнудсеном
В 1996 году тремя бельгийскими авторами, Йоаном Даэменом (Joan Daemen), Ларсом Кнудсеном (Lars Knudsen) и Винсентом Райменом (Vincent Rijmen), был предложен блочный шифр SQUARE, ставший впоследствии родоначальником но­вой архитектуры построения блочных шифров с одноименным названием.
Первоначально параметры шифра представляли собой достаточно типичные значения. Размер блока был равен 128 битам, столько же составляла длина клю­ча. Число раундов было весьма мало относительно других существующих блоч­ных шифров. При этом в своих статьях авторы утверждали, что алгоритм устой­чив к большинству существующих криптоаналитических атак.
Основными критериями при разработке шифра являлись, во-первых, устой­чивость по отношению к уже существующим криптоаналитическим атакам, во-вторых, простота архитектуры и исполнения и, в-третьих, скорость работы.
Одной из особенностей шифра являлась его ориентированность на использо­вание в качестве элементарных операндов не битов и не 32- или 16-битовых слов, а 8-битовых байтов. Для этих целей в алгоритме шифрования используется поле Галуа GF(2 ных значений в байте.
Элементы поля Галуа (и соответствующие им возможные значения обычного байта) можно представлять в самых различных формах. Классическим является полиномиальное представление. Для его задания каждому элементу поля сопо­ставляется некоторый набор коэффициентов a значений этих коэффициентов задается многочлен вида
8
), содержащее как раз 256 элементов, столько же, сколько и возмож-
в поле GF(2). Затем с помощью
i
X aaaaaaaa fx=⇔=()()
7
=+++++++ax ax ax ax ax ax ax ax
7
21
Имеется в виду свойство следующего характера: если взять произвольный ключ k èçà
6
76543210
6
5
5
4
4
3
3
2
2
1
1
0
.
0
шифровать произвольный текст x, то алгоритм зашифрования даст следующее равенство: E(x
) = E(x), ãäå y для произвольной битовой строки y является простой инверсией битов.
-
Глава 4. Как устроены современные шифры 115
Такое представление позволяет естественным образом описывать всевозмож­ные преобразования данных, записанных в двоичном формате (в виде наборов ко­эффициентов по восемь штук для различных полиномов). Так, сложение двух эле­ментов в поле описывается сложением соответствующих коэффициентов двух различных полиномов по модулю 2:
XYabababababababa+=⊕⊕⊕⊕⊕⊕⊕⊕(,,,,,,,
776655443322110b0
,
)
где за операцию отвечает сложение по модулю 2, то есть a b = c a + b = c. Умножение осуществляется немного сложнее, поскольку для его проведения до­полнительно необходим выбор неприводимого в этом поле многочлена. Неприво­димым многочленом называют многочлен, у которого отсутствуют корни, являю-
8
щиеся элементами данного поля. Для поля GF(2 пример, полином восьмой степени m(x) = x
) неприводимым является, на-
8
+ x4+ x3+ x + 1. Операция
умножения с его участием осуществляется для двух полиномов, представляющих значения байтов следующим образом:
axbx cx mx()() ()(mod ())=
,
ãäå (mod m(x)) определяет остаток от деления.
Например, для двух значений 0x57 (01010111b в двоичном виде) и 0x83 (10000011b в двоичном виде) многочлены будут выглядеть как a(x) = x
2
+ x
+ x + 1èb(x) = x7+ x + 1. Умножая их друг на друга, получим
axbx mx xxxx xx mx( ) ( )(mod ( )) ( )( )(mod ( ))=++++ ++ =
642 7
11 xx
6
+ x4+
76
1++
В используемом поле присутствуют нейтральные элементы: по сложению это 0 и по умножению — 1. В шифрах обычно используется процедура нахождения элемента обратного к данному, то есть такого, что выполнено равенство:
−=18
aa
ãäå a — некоторый элемент поля GF(2
12(mod )
8
); a−1— его обратный.
,
Аналогично может быть приведено представление элементов в поле с исполь­зованием коэффициентов многочленов из поля GF(2
8
), а не GF(2). Использование таких на первый взгляд довольно громоздких понятий на самом деле в значитель­ной степени упрощает описание алгоритма шифрования. Так, например, цикличе­ский сдвиг влево на один бит можно записать в этом виде как умножение полино­ма, представляющего заданную строку битов на x. Хотя в общем случае реализа­ция операции умножения произвольного полинома на x немного более сложна, чем простой сдвиг влево, тем не менее способ задания довольно удобен при опи­сании алгоритма и его криптоанализе, поскольку позволяет формализовать все выкладки с помощью понятного и логичного аппарата математических формул.
Допустим, например, что мы хотим умножить полином с коэффициентами b
Для этого мы можем поступить следующим образом: во-первых, если b все, что нам нужно, это просто умножить полином q(x) = b
+ b
x3+ b2x2+ b1x1+ b0x0на x; допустим, что b7отличен от нуля, тогда из результа-
3
x7+ b6x6+ b5x5+ b4x4+
7
= 0, òî
7
та мы должны еще вычесть m(x) — наш неприводимый полином, поскольку ре
.
.
i
-
116 Глава 4. Как устроены современные шифры
зультат вычисления xq(x) явно не меньше m(x)23, так что простым сдвигом здесь уже не обойтись.
Шифр SQUARE и одноименная архитектура не имеют ничего общего с архи­тектурой Файстеля и представляют совершенно иной подход, основанный на применении изначально обратимых преобразований, а не на преобразовании не­обратимых с помощью сети Файстеля в обратимые путем наложения гаммы и об­меном половин шифруемого блока. Архитектура SQUARE на самом деле являет­ся не чем иным, как логическим развитием идеи, заложенной в алгоритм 3-WAY (ThreeWay).
Алгоритм шифрования 3-WAY разработан в 1994 году. Он также не является шифром Файстеля, а является итерационным шифром. Рекомендованное автора­ми число раундов зашифрования равно 11. Размер блока и длина ключа совпада­ют и равны 96 битам. Алгоритм довольно удобен для аппаратной реализации на процессорах с эффективной обработкой битовых операций и может быть реализо­ван для работы со скоростью обработки данных до 1 гигабита в секунду.
При разработке алгоритма авторы сознательно отказались от использования структуры сети Файстеля, где половина информационного блока усложняется на каждом цикле, что в большинстве случаев приводит к проявлению функцией гам­мирования свойства линейности. Этот факт используется в методе дифференциа­льного и линейного криптоанализа (см. главу «Дешифрование современных шиф­ров»).
Вместо этого цикловая функция зашифрования строится из простых обрати­мых преобразований. При этом простота их структуры должна исключать воз­можность наличия потайных ходов и закладок
Используемые преобразования оперируют с двоичными векторами вида x = = (x
, x6, x5, x4, x3, x2, x1, x0), ãäå xiпредставляют собой единицу или ноль. Самое
7
простое преобразование, используемое в шифре, — µ-преобразование, являющее­ся простой перестановкой битов в x в обратном порядке:
µµ() (,,,,,,,) (,,,,,x xxxxxxxx xxxxxx==
При этом, очевидно, для обратного преобразования ство
µµµ() ( ())xx=
76543210 012345
−−11
.
,,)xx
1
выполнено равен-
()x
µ
.
67
Другое преобразование — γ-преобразование является нелинейной частью цикловой функции и описывает для каждого бита γ
результата действия преобра-
i
зования работу трехбитового узла замены:
γ
xx x=⊕ ⊕
iiimim
−+2
.
В более общем виде предлагаемый узел замены показан на рис. 4.13.
Еще одно преобразование — ϑ-преобразование, так же как и, является ли­нейным. Для его описания воспользуемся представлением двоичных векторов в
23
По одной простой причине мы увеличиваем степень полинома q(x) с максимальной сте пенью семь на единицу и получаем xq(x) с восьмой степенью. А полином m(x) уже имеет мак симальную степень восемь. Чтобы избежать «переполнения», мы должны будем вычесть m(x) èç xq(x).
-
-
Глава 4. Как устроены современные шифры 117
x1x2x
000111 001010 010100 011001 100011 101110 110101 111000
Ðèñ. 4.13
(x)
3
виде полиномов с коэффициентами над полем GF(2). Тогда само преобразование выглядит так:
bx x xxxxx xax x
() [ ]( )()(mod )= ++++++ +
1065 32
hh
11
.
Учитывая, что ϑ-преобразование определено для векторов длиной, кратной
12, константа h выбирается исходя из соотношения n = 12h, ãäå n — длина испо­льзуемого двоичного вектора. При этом само преобразование было выбрано так, что каждый выходной бит зависит как минимум от семи входных (это было крите­рием выбора многочлена в правой части уравнения).
Для описанных выше преобразований верными являются также равенства
−=1
γµγµ
oo
è
−=1
ϑµϑµ
oo
.
Дополнительно для преобразования данных используются преобразования π
è π2, являющиеся битовыми перестановками, для которых должно быть выполне­но равенство
oo =
πµπ µ
12
. Поэтому выбор одного из них фиксирует другой. Ав­торы алгоритма 3-WAY предложили использовать в их качестве циклические сдвиги, чтобы иметь возможность эффективной реализации на существующих ап­паратных платформах.
Все эти операции применяются к данным после того, как перед каждой итера­цией промежуточный результат побитово складывается по модулю 2 с подключом раунда. Процедура разворачивания подключей весьма проста и является самым
24
слабым местом алгоритма
. Каждый подключ вычисляется простым добавлением
к исходному ключу константы раунда с небольшим числом единиц.
Функция зашифрования ρ состоит из композиции описанных отображений:
ρπγπϑ=
oo o
21
.
До ее применения текущий блок суммируется с блоком подключа, номер ко­торого зависит от значения исходного ключа и номера раунда.
24
На сегодняшний день уже существует алгоритм дешифрования 3-WAY-подобных алго
ритмов, связанный со слабостями именно этой части алгоритма.
1
-
118 Глава 4. Как устроены современные шифры
Из-за особенностей построения всех отображений реализация 3-WAY доволь­но проста и может быть оптимизирована. Однако следует отметить, что не все ис­пользуемые в алгоритме шифрования преобразования можно реализовать в пол­ной мере эффективно в рамках архитектуры современных процессоров.
Так, например, перестановка битов в противоположном порядке осуществля­ется банальным использованием временного буфера, который заполняется в цик­ле при прохождении исходных данных с конца в начало (см. листинг 4.25).
Листинг 4.25
// изменяем порядок битов на обратный void mu(uint32_t *a) {
int i; uint32_t b[3];
b[0] = b[1] = b[2] = 0; for (i = 0; i < 32; i++) {
b[0] <<= 1; b[1] <<= 1; b[2] <<= 1;
if (a[0] & 0x1) b[2] |= 1;
if (a[1] & 0x1) b[1] |= 1;
if (a[2] & 0x1) b[0] |= 1;
a[0] >>= 1; a[1] >>= 1; a[2] >>= 1;
} a[0] = b[0]; a[1] = b[1]; a[2] = b[2];
}
Нелинейное преобразование γ осуществляется еще более простыми операци-
ями: «исключающее ИЛИ» и простое «ИЛИ» с инверсией (см. листинг 4.26).
Листинг 4.26
// нелинейное преобразование void gamma(uint32_t *a) {
uint32_t b[3];
b[0] = a[0] ^ (a[1] | (~a[2]));
b[1] = a[1] ^ (a[2] | (~a[0]));
b[2] = a[2] ^ (a[0] | (~a[1]));
a[0] = b[0]; a[1] = b[1]; a[2] = b[2];
}
Глава 4. Как устроены современные шифры 119
Аналогичным образом записывается линейное преобразование ϑ, специаль-
ные преобразования π
è π2. Все операции сводятся к операциям сдвига битов вле-
1
во и вправо, а также суммированию битов простым «ИЛИ».
Листинг 4.27
// линейное преобразование void theta(uint32_t *a) {
uint32_t b[3];
b[0] = a[0] ^ (a[0]>>16) ^ (a[1]<<16) ^ (a[1]>>16) ^ (a[2]<<16) ^
(a[1]>>24) ^ (a[2]<< 8) ^ (a[2]>> 8) ^ (a[0]<<24) ^ (a[2]>>16) ^ (a[0]<<16) ^ (a[2]>>24) ^ (a[0]<< 8);
b[1] = a[1] ^ (a[1]>>16) ^ (a[2]<<16) ^ (a[2]>>16) ^ (a[0]<<16) ^
(a[2]>>24) ^ (a[0]<< 8) ^ (a[0]>> 8) ^ (a[1]<<24) ^ (a[0]>>16) ^ (a[1]<<16) ^ (a[0]>>24) ^ (a[1]<< 8);
b[2] = a[2] ^ (a[2]>>16) ^ (a[0]<<16) ^ (a[0]>>16) ^ (a[1]<<16) ^
(a[0]>>24) ^ (a[1]<< 8) ^ (a[1]>> 8) ^ (a[2]<<24) ^ (a[1]>>16) ^ (a[2]<<16) ^ (a[1]>>24) ^ (a[2]<< 8);
a[0] = b[0]; a[1] = b[1]; a[2] = b[2];
}
// специальное преобразование - раз void pi_1(uint32_t *a) { a[0] = (a[0]>>10) ^ (a[0]<<22); a[2] = (a[2]<<1) ^ (a[2]>>31); }
// специальное преобразование - два void pi_2(uint32_t *a) {
a[0] = (a[0]<<1) ^ (a[0]>>31); a[2] = (a[2]>>10) ^ (a[2]<<22);
}
Вся цикловая функция зашифрования выглядит как набор из вызовов вышео­писанных операций и служит больше для наглядности исходного текста, чем для его эффективности и функциональности (см. листинг 4.28).
Листинг 4.28
// цикловая функция void rho(uint32_t *a) {
theta(a); pi_1(a); gamma(a); pi_2(a);
}
120 Глава 4. Как устроены современные шифры
В зависимости от направления шифрования (зашифрование или расшифрова­ние данных) вычисляются цикловые константы или обратные к ним. Затем прово­дится собственно шифрование с использованием всех вышеуказанных преобразо­ваний и цикловой функции (см. листинг 4.29).
Листинг 4.29
// вычисляем цикловые константы void rndcon_gen(uint32_t strt, uint32_t *rtab) {
int i;
// исполняем одиннадцать раундов
for (i = 0; i <= 11; i++) {
// заполняем цикловые константы
rtab[i] = strt; strt <<= 1; if (strt & 0x10000) strt ^= 0x11011;
}
}
// зашифрование void encrypt(uint32_t *a, uint32_t *k) {
int i; uint32_t rcon[11+1];
// вычисляем константы
rndcon_gen(0x0B0B, &rcon);
// проводим 11 раундов шифрования
for (i = 0; i < 11; i++) {
a[0] ^= k[0] ^ (rcon[i] << 16);
a[1] ^= k[1];
a[2] ^= k[2] ^ rcon[i]; rho(a);
}
// последний штрих
a[0] ^= k[0] ^ (rcon[11] << 16); a[1] ^= k[1]; a[2] ^= k[2] ^ rcon[11]; theta(a);
}
// расшифрование void decrypt(uint32_t *a, uint32_t *k) {
int i; uint32_t ki[3]; // инвертированный ключ uint32_t rcon[11+1] ; // инвертированные цикловые константы
// вычисляем инвертированный ключ
ki[0] = k[0]; ki[1] = k[1]; ki[2] = k[2]; theta(ki); mu(ki);
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]