const len = 26; {Это символы для выбора столбца таблицы} alpha_hor: string = 'abcdefghijklmnopqrstuvwxyz'; {Здесь будут символы для выбора строки таблицы} alpha_ver: string = ''; var {Это собственно таблица кодирования} table: array[1 .. len, 1 .. len] of char; {функция получает строку S и возвращает строку, полученную из исходной сдвигом на N позиций} function shift(s: string; n: integer): string; begin s := copy(s, length(s) - pred(n), n) + s; delete(s, length(s) - pred(n), n); shift := s end; var i, j, row, col: integer; s: string; ch: char; key: string; is_russian: boolean; f_in: file of char; f_out, f_key: text; begin { Заполнение таблицы кодировки } for i := 1 to len do begin {получаем строку символов для текущей строки таблицы} s := shift(alpha_hor, pred(i)); for j := 1 to len do table[i, j] := s[j]; { не забываем первый символ заносить в "хранилище" для выбора строк по кодируемой букве } alpha_ver := alpha_ver + s[1] end; { связываем логические файлы программы с физическими файлами на диске } { файл с фразой для кодирования - открываем для чтения } assign(f_in, 'f_00in.txt'); {$i-} reset(f_in); {$i+} { файл для сохранения результата - открываем для записи } assign(f_out, 'f_00out.txt'); {$i-} rewrite(f_out); {$i+} { файл, содержащий ключевое слово - открываем для чтения, считываем слово в строковую переменную и закрываем файл } assign(f_key, 'f_00key.txt'); {$i-} reset(f_key); {$i+} readln(f_key, key); close(f_key); { счетчик закодированных символов } i := 0; {до конца кодируемого файла делаем следующее:} while not eof(f_in) do begin { читаем очередной символ } read(f_in, ch); { находим по нему строку таблицы } row := pos(ch, alpha_ver); { эта переменная содержит значение успеха предыдущей операции (если True, то символ присутствует в таблице, иначе False) } is_russian := (row > 0); if is_russian then begin { Если символ присутствует в таблице, его надо кодировать } { увеличиваем счетчик закодированных символов } inc(i); { находим столбец по значению ключевого символа (операция mod используется, чтобы исключить выход за пределы ключа, т.к. длина ключа обычно меньше длины шифруемой последовательности) } col := pos(key[i mod (length(key))], alpha_hor); { и заменяем простой символ на зашифрованный (из таблицы) } ch := table[row, col]; end; { если символ надо было шифровать, он уже зашифрован, если он не может быть зашифрован, он остался без изменений. Пишем его в выходной файл } write(f_out, ch) end; { И закрываем оба файла: исходный и зашифрованный } close(f_out); close(f_in) end.Некоторые фрагменты библейских текстов зашифрованы с помощью шифра, который назывался Атбаш. Правило зашифрования
function Atbash(toCode: string): string; var i: integer; begin for i := 1 to length(toCode) do toCode[ i ] := Chr(256 - Ord(toCode[ i ])); Atbash := toCode; end; { Использование: } var s: string; begin s := Atbash('Just a test'); { зашифровать } writeln(s); writeln('s = ', Atbash(s)); { расшифровать } end.Для дешифрования сообщения нужно просто повторно применить к нему этот же алгоритм. Шифр Цезаря реализует кодирование фразы путем «сдвига» всех букв фразы на определенное число n (в оригинальном шифре Цезаря число n равнялось 3). Если буква кодируемой фразы имеет в алфавите позицию j, то она в "шифровке" будет заменяться буквой, находящейся в алфавите на позиции j + n.
const n = 3; function CaesarEncipher(toCode: string): string; var i, T: integer; begin for i := 1 to length(toCode) do begin T := (Ord(toCode[ i ]) + n); if T >= 256 then dec(T, 256); toCode[ i ] := Chr(T); end; CaesarEncipher := toCode; end; function CaesarDecipher(toDecode: string): string; var i, T: integer; begin for i := 1 to length(toDecode) do begin T := (Ord(toDecode[ i ]) - n); if T < 0 then Inc(T, 256); toDecode[ i ] := Chr(T); end; CaesarDecipher := toDecode; end; { применение: } var s: string; begin s := CaesarEncipher('just a Caesar'); writeln(s); writeln('s = ', CaesarDecipher(s)); end.В Древней Греции (II в. до н.э.) был известен шифр, называемый "квадрат Полибия". Шифровальная таблица представляла собой квадрат с пятью столбцами и пятью строками, которые нумеровались цифрами от 1 до 5. В каждую клетку такого квадрата записывалась одна буква. В результате каждой букве соответствовала пара чисел, и шифрование сводилось к замене буквы парой чисел.
const TPolibius: array['A' .. 'E', 'A' .. 'E'] of char = ( ('A', 'B', 'C', 'D', 'E'), ('F', 'G', 'H', 'I', 'K'), ('L', 'M', 'N', 'O', 'P'), ('Q', 'R', 'S', 'T', 'U'), ('V', 'W', 'X', 'Y', 'Z') ); function PolibiusEncipher(toCode: string): string; var i: integer; ix, jx: char; s: string; begin s := ''; for i := 1 to length(toCode) do begin for ix := 'A' to 'E' do for jx := 'A' to 'E' do if TPolibius[ix, jx] = toCode[ i ] then begin s := s + ix + jx; break; end; end; PolibiusEncipher := s end; function PolibiusDecipher(toDecode: string): string; var i: integer; s: string; begin s := ''; i := 1; while i <= length(toDecode) do begin s := s + TPolibius[toDecode[ i ], toDecode[succ(i)]]; inc(i, 2); end; PolibiusDecipher := s end; var s: string; begin s := PolibiusEncipher('POLIBIUS'); writeln(s); writeln('s = ', PolibiusDecipher(s)); end.Важное усовершенствование многоалфавитных систем, состоящее в идее использования в качестве ключа текста самого сообщения или же шифрованного текста, принадлежит Джероламо Кардано и Блезу де Виженеру. Такой шифр был назван самоключом.
const TViginer: string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; function ViginerEncipher(toCode, K: string): string; var i: integer; currK: byte; s: string; begin s := ''; for i := 1 to length(toCode) do begin currK := pred(pos(K[ (pred(i) mod length(K)) + 1 ], TViginer)); s := s + TViginer[ ((pred(pos(toCode[ i ], TViginer)) + currK) mod length(TViginer)) + 1 ]; end; ViginerEncipher := s; end; function ViginerDecipher(toDecode, K: string): string; var i, T: integer; currK: byte; s: string; begin s := ''; for i := 1 to length(toDecode) do begin currK := pred(pos(K[ (pred(i) mod length(K)) + 1 ], TViginer)); T := pred(pos(toDecode[ i ], TViginer)) - currK; if T < 0 then inc(T, length(TViginer)); s := s + TViginer[T + 1]; end; ViginerDecipher := s; end; var s: string; begin s := ViginerEncipher('INFORMATION', 'BORED'); writeln(s); writeln('s = ', ViginerDecipher(s, 'BORED')); end.Алгоритм шифра Гронсфельда (созданный в 1734 году бельгийцем Хосе де Бронкхором, графом де Гронсфельд, военным и дипломатом), является модификацией шифра Цезаря, заключающейся в том, что величина сдвига не является постоянной, а задается ключом (гаммой).
function GronsfeldEncipher(toCode, K: string): string; var i, T, _T: integer; begin for i := 1 to length(toCode) do begin _T := ord(toCode[ i ]); T := (Ord(toCode[ i ]) + (Ord(K[(pred(i) mod length(K)) + 1]) - Ord('0')) ); if T >= 256 then dec(T, 256); toCode[ i ] := Chr(T); end; GronsfeldEncipher := toCode; end; function GronsfeldDecipher(toDecode, K: string): string; var i, T: integer; begin for i := 1 to length(toDecode) do begin T := (Ord(toDecode[i]) - (Ord(K[(pred(i) mod length(K)) + 1]) - Ord('0')) ); if T < 0 then Inc(T, 256); toDecode[ i ] := Chr(T); end; GronsfeldDecipher := toDecode; end; var s: string; begin s := GronsfeldEncipher('INFORMATION', '2178'); writeln(s); writeln('s = ', GronsfeldDecipher(s, '2178')); end.Неоднократно на форуме поднимался вопрос о шифровании текста с помощью решетки...
const n = 8; type sType = string[n]; matrix = array[1 .. n] of sType; const mask: matrix = ( 'x...x...', '.x...x..', '..x...x.', '...x...x', '..x...x.', '...x....', 'x...x..x', '..x..x..' ); st: string = 'сколькоцелыхчетырёхзначныхчиселможнополучитьизцифрнольодиндватри'; var encoded: matrix; masked: matrix; { Процедура поворота матрицы } procedure T(var res: matrix); var i, j: integer; mx: matrix; begin mx := res; for i := 1 to n do for j := 1 to n do res[j, n - i + 1] := mx[i, j]; end; { Зашифровка текста } procedure EncodeText(const s: string; const mask: matrix; var mx: matrix); var i, j, count: integer; masked: matrix; begin { Заполнение матрицы mx строками по N пробелов } for i := 1 to n do for j := 1 to n do mx[i] := mx[i] + #32; masked := mask; count := 1; while count <= length(s) do begin for i := 1 to n do for j := 1 to n do if masked[i, j] = 'x' then begin mx[i][j] := s[count]; inc(count) end; T(masked); end; end; { Расшифровка текста } function DecodeText(const mask, encoded: matrix): string; var s: string; i, j, count: integer; masked: matrix; begin masked := mask; count := 0; s := ''; while length(s) < n*n do begin for i := 1 to n do for j := 1 to n do if masked[i, j] = 'x' then s := s + encoded[i, j]; T(masked); end; DecodeText := s; end; var i: integer; begin EncodeText(st, mask, encoded); writeln('encoded text: '); for i := 1 to n do begin writeln(encoded[i]); end; writeln(DecodeText(mask, encoded)); end.Результат прогона программы:
encoded text: срофкжрё ноноолхп зльноакь олчонудц чыеиинлх чтдывьиа хтисчезе рлтцмыии сколькоцелыхчетырёхзначныхчиселможнополучитьизцифрнольодиндватри
Шифр Хилла (с длиной блока = 2)
| 3 3 | |15 17 | M = | |, и M-1 = | | | 2 5 | |20 9 |(вся арифметика ведется по модулю 26)
MP = C, где P и C - вектор столбцы длиной D. То есть, каждый набор из D букв исходного сообщения определяет вектор P, компонентами которого являются номера букв. В свою очередь, полученный вектор C также интерпретируется как набор из D букв.
|H| |7| |L| |11| P1 = | | = | | и P2 = | | = | | |E| |4| |P| |15|Из уравнений
|7| | 0| M*P1 = | | = C1 и M*P2 = | | = C2 |8| |19|получаем зашифрованный текст HIAT...
P = M-1 * C [mod 26]
type very_long = longint; { Тип матрицы - ключа } tkey = array[1 .. 2, 1 .. 2] of integer; { Матрица - столбец } tcolumn = array[1 .. 2] of integer; pmatrix = ^matrix; matrix = array [1 .. maxint div sizeof(tcolumn)] of tcolumn; function _inc(var x: integer): integer; begin inc(x); _inc := x; end; { Реализация расширенного алгоритма Евклида (используется для нахождения числа, обратного данному по модулю при вычислении определителя матрицы) } procedure extended_euclid(a, b: very_long; var x, y, d: very_long); var q, r, x1, x2, y1, y2: very_long; begin if b = 0 then begin d := a; x := 1; y := 0; exit end; x2 := 1; x1 := 0; y2 := 0; y1 := 1; while b > 0 do begin q := a div b; r := a - q * b; x := x2 - q * x1; y := y2 - q * y1; a := b; b := r; x2 := x1; x1 := x; y2 := y1; y1 := y; end; d := a; x := x2; y := y2; end; (* Вычисление числа, обратного A по модулю N *) function inverse(a, n: very_long): very_long; var d, x, y: very_long; begin extended_euclid(a, n, x, y, d); if d = 1 then inverse := x else inverse := 0; end; { Алфавит криптосистемы } const alpha: string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; { Преобразование массива столбцов длины Count в строку символов } function make_str(const arr: array of tcolumn; const count: integer): string; var res: string; i, j: integer; begin res := ''; for i := 0 to pred(count) do for j := 1 to 2 do res := res + alpha[succ(arr[i][j])]; make_str := res; end; { Преобразование строки символов S в массив столбцов (возвращается длина массива) } function make_columns(var arr: array of tcolumn; const s: string): integer; var i, count: integer; col: tcolumn; begin count := -1; for i := 1 to length(s) do begin col[2 - (i mod 2)] := pred(pos(s[i], alpha)); if not odd(i) then arr[_inc(count)] := col; end; make_columns := count + 1; end; { Функция шифрования сообщения S ключом K } function EncodeHill(const k: Tkey; const s: string): string; var i, j, count: integer; mx, Y: pmatrix; len: integer; begin len := sizeof(tcolumn) * ( (length(s) div 2) + byte(odd(length(s))) ); getmem(mx, len); getmem( Y, len); count := make_columns(mx^, s); for i := 1 to count do for j := 1 to 2 do Y^[i][j] := (K[j, 1] * mx^[i][1] + K[j, 2] * mx^[i][2]) mod length(alpha); EncodeHill := make_str(Y^, count); freemem( Y, len); freemem(mx, len); end; { Функция расшифровки шифротекста S известным ключом K } function DecodeHill(const k: Tkey; const s: string): string; function positive(X: integer): integer; begin repeat inc(X, length(alpha)); until X >= 0; positive := X; end; var inv_k: Tkey; det, i, j, count: integer; mx, Y: pmatrix; len: integer; begin det := k[1, 1] * k[2, 2] - k[1, 2] * k[2, 1]; if det < 0 then det := positive(det); det := inverse(det, length(alpha)); for i := 1 to 2 do for j := 1 to 2 do begin if i = j then inv_k[i, j] := det * k[3 - i, 3 - j] else inv_k[i, j] := - det * k[i, j]; if inv_k[i, j] < 0 then inv_k[i, j] := positive(inv_k[i, j]) else inv_k[i, j] := inv_k[i, j] mod 26; end; len := sizeof(tcolumn) * ( (length(s) div 2) + byte(odd(length(s))) ); getmem(mx, len); getmem( Y, len); count := make_columns(Y^, s); for i := 1 to count do for j := 1 to 2 do mx^[i][j] := (inv_k[j, 1] * Y^[i][1] + inv_k[j, 2] * Y^[i][2]) mod length(alpha); DecodeHill := make_str(mx^, count); freemem( Y, len); freemem(mx, len); end; { Тестируем работу функций кодирования/декодирования } const k_2: Tkey = ((1, 7), (3, 6)); begin writeln('encoding:'); writeln(EncodeHill(k_2, 'AFINEAFTERNOON')); writeln('decoding:'); { Декодируем результат работы предыдущей функции с тем же ключом } writeln(DecodeHill(k_2, 'JEVYEMIZTKHTBQ')); end.