Поточные шифры. Операция XOR и простейшее шифрование файлов Кодирование xor
Then we compare each bit with the XOR operand. Which will give you this:
Xor: 01111000 01101111 01110010
cle: 01100011 01101100 01100101
00011011 00000011 00010111
If we wanted to go back to the original input ("xor"), we would just have to reapply the XOR between the output and the key. You can note that XOR is a commutative function, as multiplications. Now what do we do if the key is smaller than the input ? This is the case in most situations. If the key was the same length than the input, it wouldn"t be interesting because you would also have to communicate the key to the person you want reading the input. In contrast, when the key and the input have the same length, it is impossible for someone to crack the cipher. So, when the key is smaller than the input, you just reapply it until you reach the end of input. If by instance I wanted to encrypt the word "bonjour" with my key "cle", I would do it this way:
Bonjour: 01100010 01101111 01101110 01101010 01101111 01110101 01110010
cleclec: 01100011 01101100 01100101 01100011 01101100 01100101 01100011
You can see that we simply repeat the key. The smaller you key, the easiest it will be to crack your cipher (obviously). The XOR cracking often are based on frequency analysis. But for that matter, I suggest you to do some researches.
Согласно определению, двухместная однобитовая операция "исключающее ИЛИ" выполняет действия, показанные в табл. 19.1.
Таблица 19.1. Исключающее ИЛИ(ХОЯ)
Вход 1 |
Вход 2 |
Выход |
Ее можно еще назвать "операция несовпадения"- на выходе логическая единица тогда, когда значения на входах не совпадают. Из этого определения легко вывести основное свойство этой операции: будучи применена дважды к одному и тому же операнду, она ничего не изменит, независимо от значения второго операнда, лишь бы он не менялся. Этим, в частности, широко пользуются в компьютерной графике: если приложить через операцию XOR к фону маску, состоящую из всех единиц (чисто белую), то изображение на этом месте инвертируется по цветам, при повторении того же действия все восстанавливается в неизменном виде (так, в частности, удобно производить выделение. см. главу 10).
Данное свойство и положено в основу практически всех алгоритмов шифрования, которые иногда могут быть очень навороченными, но мы не будем углубляться в этот вопрос, а просто попробуем применить этот метод в его изначальном виде. Для проверки можно использовать такую простейшую процедуру. Пусть мы хотим зашифровать некий текстовый файл. Так как мы дипломатические секреты не прячем, то нам хватит обычной в таких случаях длины ключа в 8 символов (64 бита)- этого достаточно, чтобы сделать взлом шифра методом перебора на обычном персональном компьютере задачей "с налета" нерешаемой. Для примера ключом будет служить слово "yvre- vich". Создавать такие ключи из своей фамилии на практике ни в коем случае нельзя, нельзя также употреблять любые словарные слова, даты, номера ге- лефонов- еще Вернам показал, что для эффективного шифрования ключ должен быть строго случайным, здесь это делается только в качестве примера (о том, как можно задавать случайный ключ, мы поговорим позже).
Создадим новый проект (в папке Glaval9\l) под названием ProbaCrypt, и разместим в той же папке некий файл на пробу. Я взял текст песни "Однажды мир прогнется под нас" из репертуара А. Макаревича (в текстовом же формате, файл mashinavremeni.txt) с аккордами, т. к. достаточно сложное форматирование этого текста сделает пример нагляднее 3 .
Поместим на форму компонент Memo, диалог OpenDialog и две кнопки Butt эл. В заголовке Buttoni напишем Зашифровать, в заголовке Buttoni - Расшифровать. Объявим такие переменные:
Forml: TForml ; fname,key:string; fi,fo:file of byte; i:integer; xb:byte;
При создании формы инициализируем ключ и диалог:
procedure TForml.FormCreate(Sender: TObject); begin key:=’yvrevich";
OpenDialogl.InitialDir:=ExtractFiieDir(Application.ExeName); end;
В обработчике нажатия кнопки Buttonl напишем следующий довольно длинный код:
procedure TForml.ButtonlClick(Sender: TObject); begin /Зашифровать/ OpenDialogl. FileName:?= 11 ; (очистим) OpenDialogl.Filter:=”; if OpenDialogl.Execute then fname:=OpenDialogl.FileName else exit; assignfile(fi,fname); {задали исходный файлI try
reset(fi); {открыли исходный) except
exit; {если не открывается – на выход) end;
assignfile(fo,ChangeFileExt(fname, 1 .sec’)) ; rewrite(fo);
read(fi,xb); {прочли первый байт)
Forml.Caption:=’ProbaCrypt: ‘+ExtractFileName(fname);
/название файла – в заголовок) Memol.Lines.Clear; Memol.Lines.Add(‘Подождите…’);
Application.ProcessMessages; Memol.Lines.Clear; while not eof(fi) do begin
xb:=xb xor ord(key[i]); {шифруем)
Memol.Text:=Kemol.Text+chi(xb); {выводим в Memo)
write(fo,xb); (записываем в файл)
read(fi,xb); {если конец файла – выходим) except break; end; end; end;
closefile(fi) ;
erase(fi); {уничтожаем исходник)
closefile(fo);
{зашифровали) end;
Тут мы складываем через операцию XOR каждый байт исходного файла с байтами ключа по очереди; когда ключ заканчивается, мы опять начинаем с его первого символа. Результаты пишем в файл с расширением sec (от "security") и выводим в Memoi. В точности ту же операцию мы производим при расшифровке, только уже с зашифрованным файлом, в результате чего исходный файл восстанавливается полностью:
procedure TForml.Buttor.2Click(Sender: TObject); begin {Pa оцифровать j OpenDialogl.FileName:=”; {очистим/ OpenDialogl.Filter: = ‘№4>poBaHHbie файлы| *.sec’ ; if OpenDialogl.Execute then fname:OpenDialogl.FileName else exit; assignfile(fi, fname); {открыли шифрованный) try
reset(fi); except exit; end;
assignfile(fo,ChangeFileExt(fname, ‘.txt’)); rewrite(fo); {перезаписываем старый) read(fi,xb);
Forml.Caption:=’ProbaCrypt: ‘+ExtractFileNajne (fname) ;
!название файла – в заголовокj
Memol.Lines.Clear;
Memol.Lines.Add(1 Подождите…’);
Application.ProcessMessages; {чтобы увидеть предупреждение) Memol.Lines.Clear; while not eof(fi) do begin /асе без изменений, как выше} for i:=l to length(key) do begin
xb:=xb xor ord(keyU));
Memol.Text:=Memol.Text+chr(xb);
write(fo,xb);
read(fi,xb); except break; end; end; end;
closefile(fi); closefile(fo); {pa сшифровали) end;
Обратите внимание, что я зашифрованный файл не уничтожаю - в принципе операцию шифрования можно производить сколько угодно раз над уже зашифрованным текстом, для его расшифровки придется также повторить се столько же раз, и этот прием часто применяют в "официальных" алгоритмах шифрования (только в данном случае придется зашифрованный файл переименовать вручную или несколько модифицировать программу, в имеющемся виде она вам не позволит открыть один и тот же файл и для расшифровывания и для шифрования). Естественно, процедуру шифрования можно применять к абсолютно любым файлам, не только текстовым.
Есть тонкий момент, связанный с уничтожением оригинала - как известно, при уничтожении дискового файла он не стирается, подобно музыкальной записи на магнитной ленте, а просто в заголовочных структурах FAT место, которое ои занимает, помечается, как свободное (примерно также происходит это и в NTFS). Именно с этой особенностью была связана работа DOS- программы Unerase (если кто помнит, что это такое). Поэтому, если вы даже удалите файл из Корзины (удаленный из нашей программы файл в Корзину, правда, итак не попадает), на диске останется его содержание до тех пор, пока туда не будет записано что-то еще. Поэтому для особо параноидальных личностей продвинутые программы шифрования предлагают опцию, при которой файлы после стирания уничтожаются гарантированно- на их место записываются нули. В нашем случае для этого в принципе достаточно не закрывая файл, заполнить его байтами с нулевым значением (или любым другим, но не увеличивая и не уменьшая размер файла), записать это на диск (закрыв файл), а потом уже его уничтожать. Правда, в Windows стопроцентной гарантии, что он запишется в точности на то же место, я дать не могу, поэтому лучше в таких случаях применять все же "официальные" программы, которые, кстати, уничтожают следы исходника не только в той области диска, где он хранился, но и SWAP-файле Windows, если они там остались.
Что касается генерации случайных ключей, то вот один из способов. I Указывать реализацию полностью я не буду, так как она проста. Генератор псевдослучайных чисел в Delphi (и не только в Delphi) устроен следующим образом: через переменную RandSeea задается начальное число генератора (но умолчанию оно равно 0). Тогда функция Random при последовательном обращении к ней всегда будет возвращать один и гот же набор чисел, независимо от того, в какой программе и когда мы ее используем. Отсюда и способ- вы устанавливаете в программе такой генератор и на его основе генерируете ключ, например. вот так можно сгенерировать случайный 16-байтный (128-битный) набор символов, который будет зависеть только от величины начального смещения генератора init:
:
init:integer; st:string;
st:=”;
RandSeed:=init; {начальное смещение} while length(st)<16 do begin xb:=Random(255); if xb>31 then st:=st+chr(xb); end;
При этом вы передаете вашему корреспонденту не сам ключ, а величину ir.j .. У него будет сгенерирован по идентичной процедуре в точности тот же ключ. Можно придумать, разумеется, и более хитрые механизмы реализации этого метода.
Хочу заметить, чтобы не затемнять суть дела, ранее в программе я использовал традиционное побайтное чтение из дискового файла. Резко ускорить процедуру можно при использовании одного из механизмов предварительного чтения файла в память- file mapping, как в главе 14, потокового чтения или любого другого способа организации динамических массивов в памяти (см. главу 21). В примере с использованием стеганографии, к которому мы сейчас приступим, частично положение будет исправлено.
Thus, when executing an exclusive OR, there will always be a zero value if the variables have the same values.
The peculiarity of XOR is that one and the same function can both encrypt data and decrypt it. This is a simple method of data encryption, which can be hacked quickly enough if there is a large enough encrypted text, or a large password dictionary. But nevertheless, this can already be used for small initial data protection.
In the context of Qt, the use of XOR is no different from how the program was written without using Qt. The question here is how to extract data for encryption from QString objects, for example, if the text was entered in QTextEdit.
To do this, write a program that contains:
- QTextEdit, In which the text will be entered, which will be encrypted.
- QLineEdit, In which the encryption key will be entered.
- QPushButton, In the slot of the click handler of which, data encryption / decryption will be performed. Again, I note that the method will be used the same.
The program will look like this:
Project structure
The project is written using CMake, so the structure will be as follows:
- CMakeLists.txt
- main.cpp
- EncoderWidget.h
- EncoderWidget.cpp
Encryption / decryption function
To implement encryption / decryption it is necessary:
- The presence of a char array with the original data
- Length of the array with the original data
- Key as array char
- Length of key
- And also an array with output data.
Slot handler for encryption
As already mentioned, to encrypt the text, you need to correctly extract data from the input fields. To do this, you need to translate the text in the form of a QString string into a QByteArray from which to extract the data in the form of const char *. And also take the length of this data.
There is one point. This from QString in QByteArray is translated using the toLatin1 () method, which will result in data to the ASCII table, which will lead to data corruption if the text was written in Cyrillic. That is, this approach to encryption will be relevant if only symbols from the ASCII table are used, for example, for the login and password.
Void EncoderWidget::encodeDecode() { const char* input = m_textEdit->toPlainText().toLatin1().data(); int inputLength = m_textEdit->toPlainText().toLatin1().length(); const char* key = m_keyLineEdit->text().toLatin1().data(); int keyLength = m_keyLineEdit->text().toLatin1().length(); char output; for (int i = 0; i < inputLength + 1; ++i) { output[i] = input[i] ^ key; } m_textEdit->setText(QString::fromLatin1(output, inputLength)); }
Однажды, при разработке сайта, появилась необходимость передавать данные в зашифрованном виде используя PHP . Конечно же сразу пришла мысль использовать mcrypt который уже есть на PHP ... Но эта идея сразу же отпала, поскольку не на всех хостингах этот модуль установлен.
Немного поразмыслив, оказалось, что пожалуй самым надежным и просто реализуемым методом шифрования будет XOR-метод (суммирование по модулю).
Немного расскажу как вообще работает XOR :
XOR - это побитовое сложение по модулю (с инвертированием при переполнении), например, 1+1=0 т.к. 1 - максимальное значение.
Все варианты:
0+0=0
0+1=1
1+1=0
Уникальность сложения по модулю состоит в его обратимости! Т.е. если придумать некоторое слово-код (назовем его гаммой) и выполнить XOR 2 раза к открытому тексту - то получим тот же открытый текст.
Пример работы XOR
Допустим, у нас есть открытый текст - "denik.od
"
Придумаем для него гамму: "12345678
" (гамма должна быть равной длине текста, т.к. мы весь текст хотим зашифровать)
Конкретный пример рассмотрим на первых 2-х байтах "de " (представим их в двоичной системе):
Для расшифровки, просто применим XOR еще раз:
В результате мы получили наши первые 2 байта (de ). Точно так же делается со всем текстом.
Все остальные усложнения уже заложены в методах генерации этой самой гаммы. Есть много способов ее генерации, но в основу своего способа, я заложил функцию sha1 (создание HEX хеша).
Логика проста, мы составляем гамму длиной в кодируемый текст примерно так:
$gamma .= sha1($passw . $gamma );Полный пример для PHP:
function strcode($str , $passw ="" ) { $salt = "Dn8*#2n!9j" ; $len = strlen($str ); $gamma = "" ; $n = $len >100 ? 8 : 2 ; while (strlen($gamma )<$len ) { $gamma .= substr(pack("H*" , sha1($passw .$gamma .$salt )), 0 , $n ); } return $str ^$gamma ; }