Вспомним таблицу истинности для данной операции:
Т.е. если количество единиц нечётно, то получаем истину, в противном
случае ложь. Используя данный факт, можно восстановить исходное сообщение.
Допустим выше x - это текст, y - это ключ, out - зашифрованное сообщение.
Таким образом, если применить к out повторную операцию с ключом y, то
можно получить исходный x.
Теперь рабочая реализация данного принципа.
x y out 0 0 0 0 1 1 1 0 1 1 1 0
Т.е. если количество единиц нечётно, то получаем истину, в противном
случае ложь. Используя данный факт, можно восстановить исходное сообщение.
Допустим выше x - это текст, y - это ключ, out - зашифрованное сообщение.
Таким образом, если применить к out повторную операцию с ключом y, то
можно получить исходный x.
y out x 0 0 0 1 1 0 0 1 1 1 0 1
Теперь рабочая реализация данного принципа.
Шаг 1
Подготовка.
#!/usr/bin/perl use strict; use warnings; use utf8; use open ':std', ":encoding(utf-8)"; # избавляемся от предупреждений # связанных с использование Юникода # при выводе на экран. $\="\n"; # позволит нам использовать print "foo", # вместо print "foo\n", аналог say.
Шаг 2
Задаём текст, который будет зашифрован и ключ, с помощью которого это
будет выполнено. Ключ и текст возьмём из двух весьма известных книг.
my $string = <<'EOF'; Свобода, свободный ум и наука заведут их в такие дебри и поставят пред такими чудами и неразрешимыми тайнами, что одни из них, непокорные и свирепые, истребят себя самих, другие, непокорные, но малосильные, истребят друг друга, а третьи, оставшиеся, слабосильные и несчастные, приползут к ногам нашим и возопиют к нам... EOF my $key = 'Кто затыкает ухо свое от вопля бедного, тот и сам будет вопить, — и не будет услышан.';
Шаг 3
Добавляем функцию дешифровки, которая по сути являеется синонимом для функции encode (см Шаг 4), с тем лишь отличием, что функция encode вызывается с еще одним дополнительным аргументом. Подробнее про goto.
sub decode { $_[2] = 1; goto &encode; }
Шаг 4
Добавляем каркас функции для шифрования текста.
sub encode { my $string = shift; my $key = shift; my $is_decode = shift; my $processed_string = ''; # Шаг 5. # Шаг 6. return $processed_string; }
Шаг 5
my $string_unpacked = $is_decode ? $string : unpack('U0B*', $string); my $key_unpacked = unpack('U0B*', $key);
Вспомним работу недооцененных функций pack и unpack.
Задача функции unpack - "расщепить" что-то, что идёт вторым аргументом на "части", вид которых описан в первом аргументе.
Задача же функции pack - "собрать" что-то новое из частей второго аргумента по чертежам, описанным в первом аргументе.
В данном месте, когда $is_decode - истино, а это происходит при дешифровки, при вызове функции decode, используется такой вызов unpack: unpack('U0B*', $string). Что он означает? Как сказано выше, unpack будет расщеплять строку $string на части, по схеме 'U0B*', которая обозначает:
U0 - показатель того, что имеем дело с Юникодом; поэтому unpack сможет правильно обработать символы Юникода, которые могут занимать более одного байта.
B* - непосредственно сама схема "разбивки" на части, которая говорит о том, что необходимо расщепить всю (знак * говорит об этом) строку $string на биты с убывающим порядком в каждом байте.
В итоге и $string_unpacked и $key_unpacked будут содержать строки, состоящие из нулей и единиц.
Памятка! pack и unpack обрабатывают строки, поэтому будьте внимательны, когда обрабатываете с их помощью числа. Например unpack("B*", 3) выдаст 00110011, так как воспринимает второй аргумент как строку "3", а не как число. Поэтому, чтобы получить то, что вам нужно, необходимо предварительно "упаковать" строку "3" в число 3, а уже затем обрабатывать это значение, например так: unpack("B*", pack("C*", 3)) что выдаст нам 00000011.
Шаг 6
Разбиваем $string_unpacked на куски, равные длине $key_unpacked.
unpack("(a$key_length)*", $string_unpacked) - эта запись говорит о том, что необходимо просто разбить строку $string_unpacked на куски (использование скобок () позволяет группировать правила в первом аргументе, что даёт дополнительну гибкость в обработке), длиной $key_length, без каких либо преобразований, об этом говорит символ "а". Затем каждый такой кусок, будет подвержен обработке в цикле (см Шаг 7,8)
my $key_length = length $key_unpacked; for my $part (unpack("(a$key_length)*", $string_unpacked)) { # Шаг 7. # Шаг 8. }
Шаг 7
Теперь для каждого блока применяем операцию XOR, не забывая перед этим преобразовать строку из нулей и единиц, непосредственно, в последовательность битов с помощью pack.
my $a = pack('B*', $part); my $b = pack('B*', $key_unpacked); my $c = $a ^ $b;
Шаг 8
Полученный результат распаковываем обратно в строку вида "0100110..." и добавляем её
к общему результату.
my $unpacked_c = unpack('B*', $c); $processed_string .= $unpacked_c;
Шаг 9
Проверка работы. Теперь при вызове encode с необходимыми параметрами, мы получим на выходе последовательность нулей и единиц, а применяя функцию decode к данной последовательности с использованием этого же ключа, мы получим исходное сообщение, закодированное в виде нулей и единиц конечно же. Чтобы получить исходный текст, просто преобразуем их в символы Юникод.
my $cipher = encode($string, $key); my $orig = decode($cipher, $key); print pack('U0B*', $orig);
И всё вместе
#!/usr/bin/perl use strict; use warnings; use utf8; use open ':std', ":encoding(utf-8)"; $\="\n"; my $string = <<'EOF'; Свобода, свободный ум и наука заведут их в такие дебри и поставят пред такими чудами и неразрешимыми тайнами, что одни из них, непокорные и свирепые, истребят себя самих, другие, непокорные, но малосильные, истребят друг друга, а третьи, оставшиеся, слабосильные и несчастные, приползут к ногам нашим и возопиют к нам... EOF my $key = 'Кто затыкает ухо свое от вопля бедного, тот и сам '. 'будет вопить, — и не будет услышан.'; sub encode { my $string = shift; my $key = shift; my $is_decode = shift; my $processed_string = ''; my $string_unpacked = $is_decode ? $string : unpack('U0B*', $string); my $key_unpacked = unpack('U0B*', $key); my $key_length = length $key_unpacked; for my $part (unpack("(a$key_length)*", $string_unpacked)) { my $a = pack('B*', $part); my $b = pack('B*', $key_unpacked); my $c = $a ^ $b; my $unpacked_c = unpack('B*', $c); $processed_string .= $unpacked_c; } return $processed_string; } sub decode { $_[2] = 1; goto &encode; } my $cipher = encode($string, $key); my $orig = decode($cipher, $key); print pack('U0B*', $orig);
Комментариев нет:
Отправить комментарий