Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Практическая криптография алгоритмы и их программирование
.pdf
Глава 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 decided to recommend Rijndael, the candidate with the smallest security 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);
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
