Самый популярный и широко применяемый в системах защиты алгоритм это DES (Data Encrypted Standart), принятый в 1977г Национальным бюро стандартов США (NBS), ныне называемым Национальным институтом стандартов и технологий (NIST). Официальное название стандарта Federel Information Processing Standard 46 (FIPS PUB 46).
Шифруемые данные разбиваются на 64-битные блоки и шифруются поблочно с использованием ключа 56 бит. Многошаговый (16 раундов) алгоритм преобразует каждый исходный блок в 64-битный шифрованный блок. Тот же алгоритм с тем же ключом используется для обратного преобразования шифрованного блока в исходный вид.
В силу сложности алгоритма DES рассмотрим его суть на учебном алгоритме S-DES, который разработан в учебных целях профессором Эдвардом Шейфером (Edward Schaefer) из университета Санта-Клара и опубликован в 1996г. По свойствам и структуре он подобен DES, но имеет существенно меньшую размерность и потому проще в освоении, но практической криптостойкостью не обладает.
Учебный симметричный криптоалгоритм S-DES.
На вход поступают исходные 8-битовые блоки, для шифрования используется 10-битовый ключ, на основе ключа генерируется 2 подключа по 8 бит и производится 2 раунда шифрования, для дешифрования используется тот же алгоритм и тот же ключ, но подключи используются в обратном порядке. Пусть X - исходный блок, Y - результат шифрования, тогда процесс шифрования:
Y = IP-1( fk2( SW( fk1( IP( X ) ) ) ) )
Процесс дешифрования:
X = IP-1( fk1( SW( fk2( IP( Y ) ) ) ) )
Рассмотрим теперь модули, используемые при шифровании и дешифровании 8-битного блока. Рассмотрим их работу на примере блока: 00001011 (11)
Исходный блок X = 00001011(2) = 11(10)
1. Модуль IP. Шифрование начинается с модуля начальной перестановки IP , в котором 8 бит исходноо блока переставляются в соотвествии с таблицей:
IP: (1, 5, 2, 0, 3, 7, 4, 6)
Для нашего примера IP( 00001011 ) = 00011001.
Здесь же укажем таблицу, задающую последний модуль преобразования, перестановку, обратную начальной IP-1 :
IP-1: (3, 0, 2, 4, 6, 1, 7, 5)
Легко убедиться, что эта перестановка действительно является обратной по отношению к первой, т.е. IP-1( IP ( X ) ) = X . Примените ее к результату модуля IP нашего примера и получите исходный 8-битный блок.
2. Модуль fk - это самый сложный компонент, в этом модуле в преобразовании, кроме 8-битового результата модуля 1, участвует 8-битовый подключ, обозначим его SK (в первом раунде это будет K1, во втором - K2). Обозначим L и R - левую и правую половинки (по 4 бита) входного блока, а F() - некоторое преобразование в пространстве 4-х битовых строк, не обязательно взаимно-однозначное, подключ SK является параметром этого преобразования. Тогда модуль fk описывается следующим образом:
fk( L, R ) = ( L + F( R, SK ) , R )
где + -- обозначает побитовую операцию XOR
Правая половинка R без изменения передается на выход модуля и параллельно - на вход нелинейной функции F, зависящей также от подключа SK. Результат функции F(R,SK) складывается по операции XOR со входной левой полвинкой и передается на выход в качастве левой половинки. Итого на выходе опять 8-битовый блок.
Рассмотрим теперь нелинейную функцию F(R,SK). Она состоит из следующих модулей:
2.1. Входной блок функции ( 4-х битная правая половинка R) расширяется с перестановкой (модуль E/P) до 8 бит согласно следующей таблице:
E/P: (3, 0, 1, 2, 1, 2, 3, 0)
В нашем примере для правой половинки R (1001) на выходе модуля E/P будет получен результат: (11000011)
2.2 8-битовый результат модуля 2.1 складывается по операции XOR с 8-битовым подключом SK, 8-битовый результат представим в виде таблицы из 2-х строк (левые 4 бита -- 0-я строка, следующие 4 бита - 1-я строка):
В нашем примере подключ К1: 00100001, результат модуля E/P: 11000011, результат операции XOR: 11100010, представим результат в виде таблицы:
1 1 1 0 0 0 1 0
2.3. Матрицы кодирования (S-матрицы). Каждая строка результата п.2.2. подвергается преобразованию по своей S-матрице (матрица S0 служит для сжатия 0-й строки, а матрица S1 - для сжатия 1-й строки). В алгоритма S-DES всего 2 S-матрицы (S0 и S1). На вход каждой S-матрицы подаются по 4 бита, на выходе получается по 2 бита.
S0:
1 0 3 2 3 2 1 0 0 2 1 3 3 1 0 2
S1:
2 3 1 0 1 0 2 3 0 1 3 2 3 0 2 1
Эти S-модули работают следующим образом. последний и предпоследний биты входной последовательности рассматриваются как 2-битовое число, определяющее номер строки S-матрицы, а первый и нулевой как число, определяющее номер столбца. На пересечении соотвествующих строки и столбца находится число, задаюшее 2-битовое выходное значение.
В нашем примере на вход матрицы S0 поступает последовательность 1110. последний и предпоследний биты дают число 11, т.е. номер строки -- 3. Первый и нулевой биты дают число 10, т.е. номер столбца -- 2. В матрице S0 на пересечении строки 3 и столбца 2 стоит число 0, в битовом представлении это 00 . Таким образом входная 4-битовая последовательность 1110 преобразовалась в выходную 2-битовую последовательность 00.
На вход матрицы S1 поступает последовательность 0010. На пересечении строки 0 и столбца 2 матрицы S1 стоит число 1 (в битовом представлении 01). Таким образом входная последовательность 0010 преобразовалась в выходную 01.
Сцепляя (concatenation) результаты на выходе S-матриц получаем итоговый результат модуля 2.3: 0001. Итак 8-битовая последовательность на входе модуля S-матриц (2.3) 11100010 преобразована в 4-х битовую последовательность 0001.
2.4. Перестановка P4 задается таблицей:
P4: (0, 1, 2, 3)
Т.е. 4-му биту присваивается значение 0-го, 3-му - значение первого и т.д.
В нашем примере последовательность 0001 превращается в последовательность 1000 . Это и есть результат функции F( R, SK ).
2.5. Полученный результат функции F( R, SK ) складывается с помощью побитовой операции XOR с левой половинкой битовой последовательности на входе модуля fk (п.2). Полученный результат операции XOR cцепляется (concatenation) с правой половинкой входной последовательности модуля fk (п.2).
В нашем примере входная последовательность модуля fk (п.2) 00011001, отделим левую и правую половинки: 0001 1001. Результат L + F( R, SK ) = 0001 XOR 1000 = 1001.
Сцепляем этот результат с правой половинкой и получаем итоговый результат модуля fk в первом раунде алгоритма:
1001 1001 = 10011001.
3. Модуль SW -- это перестановка левой и правой половинок входной 8-битовой последовательности.
Для нашего примера входная последовательность 10011001, на выходе получаем 10011001. Это результат первого раунда алгоритма.
Во втором раунде входная последовательность на модуле fk:
Дешифрование проводится по тому же самому алгоритму, что и шифрование, но порядок подачи подключей в раунды обратный (в первом раунде - последний подключ, а в последнем раунде - первый подключ).
генерация ключей и общая схема алгоритма:
первый раунд S-DES:
второй раунда S-DES:
Программа разбита на 3 модуля
* unit uBits -- модуль для работы с двоичным представлением числа * unit uKeys -- модуль содержащий подпрограммы генерации подключей K1 и K2 * unit uSDES -- модуль содержащий подпрограммы кодирования данных
function get_bit(const value: Integer; const bit_num: Byte): TBit; var buffer: array[0 .. 15] of TBit; i: integer; begin fillchar(buffer, 16, 0); for i := 0 to pred(sizeof(value) * 8) do begin buffer[pred(sizeof(value) * 8) - i] := (value shr i) and $1; end; get_bit := buffer[pred(sizeof(value) * 8) - bit_num]; end;
procedure set_bit(var value: Integer; const bit_num: Byte; const bit_value: TBit); var tmp: Integer; begin tmp := round(exp(bit_num * ln(2)));
if bit_value = 1 then value := value or tmp else if (value and tmp) = tmp then value := value xor tmp end;
procedure print_bits(const bits: array of TBit; const bits_count: Byte); var i: Byte; begin writeln; for i := bits_count - 1 downto 0 do write(bits[i]); end;
procedure int2bits(const value: Integer; var bits: array of TBit; const bits_count: Byte); var i: Byte; begin for i := 0 to bits_count - 1 do bits[i] := get_bit(value, i); end;
function bits2int(const bits: array of TBit; const bits_count: Byte): Integer; var i: Byte; value: Integer; begin value := 0; for i := 0 to bits_count - 1 do set_bit(value, i, bits[i]); bits2int := value; end;
procedure cat_bits ( const source: array of TBit; var part1, part2: array of TBit; const source_len: Byte );
var i, middle: Byte; begin middle := source_len div 2 - 1;
for i := middle + 1 to source_len - 1 do part1[i - middle - 1] := source[i];
for i := 0 to middle do part2[i] := source[i]; end;
procedure concat_bits ( const part1, part2: array of TBit; var destination: array of TBit; const part_len: Byte );
var i: Byte; begin for i := 0 to part_len - 1 do destination[i] := part2[i];
for i := part_len to 2 * part_len - 1 do destination[i] := part1[i - part_len]; end; end.
UNIT uKeys
unit uKeys;
interface uses uBits;
const KEY1: Integer = 0; { в этой переменной будет храниться первый ключ } KEY2: Integer = 0; { в этой переменной будет храниться второй ключ }
{ пр-а генераципи 2-х 8бит ключей из 1-го 10бит ключа } procedure generate_keys(const key: Integer);
implementation
procedure generate_keys(const key: Integer);
procedure shift(var bits: TBit10); var i: Byte; tmp: TBit; begin tmp := bits[9]; for i := 9 downto 6 do bits[i] := bits[i - 1]; bits[5] := tmp;
tmp := bits[4]; for i := 4 downto 1 do bits[i] := bits[i - 1]; bits[0] := tmp end;
procedure module_P8(var bits8: TBit8; const bits10: TBit10); var i: Byte; begin for i := 0 to 7 do bits8[i] := bits10[ P8[7 - i] ]; end;
procedure module_P10(var bits10: TBit10); var i: Byte; begin for i := 0 to 9 do bits10[i] := get_bit(key, P10[9 - i]); end;
var i: Byte;
buff8: TBit8; buff10: TBit10;
begin { сохраняем в buff10 перестановку бит числа key по модулю p10 } module_P10(buff10);
{ модуль SHIFT1 } shift(buff10);
{ для получения ключа Key1 выполним перестановку по модулю P8 для "buff10" }
{ перестановка массива бит bits по модулю IP } procedure module_IP(var bits: TBit8); var i: Byte; temp: TBit8; begin for i := 0 to 7 do temp[i] := bits[ IP[i] ];
move(temp, bits, sizeof(temp)); end;
{ перестановка массива бит nits по модулю IP_ } procedure module_IP_(var bits: TBit8); var i: Byte; temp: TBit8; begin for i := 0 to 7 do temp[i] := bits[ IP_[i] ];
move(temp, bits, sizeof(temp)); end;
{ расширение 4-х битовой последователности до 8-ми бит, с использованием модуля EP } procedure module_EP(const bits4: TBit4; var bits8: TBit8); var i: Byte; begin for i := 0 to 7 do bits8[i] := bits4[ EP[7 - i] ]; end;
{ преобразование 4-битовой последователности по матрице кодирования S0 } procedure module_S0(const bits4: TBit4; var bits2: TBit2); var row, col: TBit2; begin move(bits4[0], col, 2 * sizeof(TBit)); move(bits4[2], row, 2 * sizeof(TBit));