Contactos

Operación XOR y cifrado básico de archivos. Stream Ciphers exclusivo o cifrado

Así, al ejecutar un OR exclusivo, siempre habrá un valor cero si las variables tienen los mismos valores.

La peculiaridad de XOR es que una misma función puede cifrar datos y descifrarlos. Este es un método simple de cifrado de datos, que puede piratearse con la suficiente rapidez si hay un texto cifrado lo suficientemente grande o un diccionario de contraseñas grande. Sin embargo, esto ya se puede utilizar para una pequeña protección de datos inicial.

En el contexto de Qt, el uso de XOR no es diferente de cómo se escribió el programa sin usar Qt. La pregunta aquí es cómo extraer datos para el cifrado de objetos QString, por ejemplo, si el texto se ingresó en QTextEdit.

Para hacer esto, escriba un programa que contenga:

  • QTextEdit, en el que se ingresará el texto, que será encriptado.
  • QLineEdit, en el que se introducirá la clave de cifrado.
  • QPushButton, en la ranura del controlador de clic del cual, se realizará el cifrado / descifrado de datos. Nuevamente, observo que el método se utilizará de la misma manera.

El programa se verá así:

Estructura del proyecto

El proyecto está escrito usando CMake, por lo que la estructura será la siguiente:

  • CMakeLists.txt
  • main.cpp
  • EncoderWidget.h
  • EncoderWidget.cpp

Función de cifrado / descifrado

Para implementar el cifrado / descifrado es necesario:

  • La presencia de una matriz de caracteres con los datos originales.
  • Longitud de la matriz con los datos originales
  • Clave como carácter de matriz
  • Longitud de la clave
  • Y también una matriz con datos de salida.
const char * input; int inputLength; const char * clave; int keyLength; salida de char; para (int i = 0; i< inputLength + 1; ++i) { output[i] = input[i] ^ key; }

Controlador de ranuras para cifrado

Como ya se mencionó, para cifrar el texto, debe extraer correctamente los datos de los campos de entrada. Para hacer esto, necesita traducir el texto en forma de una cadena QString a un QByteArray desde el cual extraer los datos en forma de const char *. Y también tome la longitud de estos datos.

Hay un punto. Esto de QString en QByteArray se traduce utilizando el método toLatin1 (), que dará como resultado datos en la tabla ASCII, lo que provocará daños en los datos si el texto se escribió en cirílico. Es decir, este enfoque del cifrado será relevante si solo se utilizan símbolos de la tabla ASCII, por ejemplo, para el inicio de sesión y la contraseña.

Void EncoderWidget :: encodeDecode () (const char * input = m_textEdit-> toPlainText (). ToLatin1 (). Data (); int inputLength = m_textEdit-> toPlainText (). ToLatin1 (). Length (); const char * clave = m_keyLineEdit-> text (). toLatin1 (). data (); int keyLength = m_keyLineEdit-> text (). toLatin1 (). length (); salida de caracteres; para (int i = 0; i< inputLength + 1; ++i) { output[i] = input[i] ^ key; } m_textEdit->setText (QString :: fromLatin1 (salida, inputLength)); )

Por definición, una operación XOR de dos lugares y un bit realiza las acciones que se muestran en la tabla. 19.1.

Cuadro 19.1. O exclusivo (HOYA)

Entrada 1

Entrada 2

Salida

También puede denominarse "operación de desajuste": la salida es una unidad lógica cuando los valores de las entradas no coinciden. De esta definición es fácil deducir la propiedad principal de esta operación: si se aplica dos veces al mismo operando, no cambiará nada, independientemente del valor del segundo operando, siempre que no cambie. Esto, en particular, es muy utilizado en gráficos por computadora: si aplica una máscara que consta de todas las unidades (blanco puro) al fondo a través de la operación XOR, entonces la imagen en este lugar se invierte en colores, cuando repite la misma acción , todo se restaura sin cambios (por lo que, en particular, es conveniente hacer una selección (ver capítulo 10).

Esta propiedad es la base de casi todos los algoritmos de cifrado, que en ocasiones pueden ser muy sofisticados, pero no profundizaremos en este tema, sino que simplemente intentaremos aplicar este método en su forma original. Para la verificación, puede utilizar el siguiente procedimiento simple. Digamos que queremos cifrar algún archivo de texto. Dado que no ocultamos secretos diplomáticos, la longitud de clave habitual de 8 caracteres (64 bits) es suficiente para nosotros; esto es suficiente para hacer que romper el cifrado por la fuerza bruta en una computadora personal normal sea una tarea sin solución. Por ejemplo, la clave será la palabra "yvrevich". En la práctica, en la práctica, no puede crear tales claves a partir de su apellido, no puede usar palabras de diccionario, fechas, números de teléfono; incluso Vernam demostró que para un cifrado efectivo la clave debe ser estrictamente aleatoria, aquí esto se hace solo como un ejemplo (hablaremos sobre cómo puede configurar una clave aleatoria más adelante).

Creemos un nuevo proyecto (en la carpeta Glaval9 \ l) llamado ProbaCrypt y coloquemos un archivo en la misma carpeta para probarlo. Tomé el texto de la canción "Un día el mundo se doblará debajo de nosotros" del repertorio de A. Makarevich (en formato de texto, archivo mashinavremeni.txt) con acordes, ya que un formato bastante complejo de este texto hará que el ejemplo sea más claro 3 .

Coloque un componente Memo, un cuadro de diálogo OpenDialog y dos botones de correo electrónico Butt en el formulario. En el encabezado de Buttoni, escriba Cifrar, en el encabezado de Buttoni - Descifrar. Declaremos tales variables:

Forml: TForml; fname, clave: cadena; fi, fo: archivo de bytes; i: entero; xb: byte;

Al crear un formulario, inicializamos la clave y el diálogo:

procedimiento TForml.FormCreate (Remitente: TObject); tecla de inicio: = ’yvrevich";

OpenDialogl.InitialDir: = ExtractFiieDir (Application.ExeName); fin;

En el controlador de clic de botón Buttonl, escribamos el siguiente código bastante largo:

procedimiento TForml.ButtonlClick (Remitente: TObject); begin / Encrypt / OpenDialogl. Nombre de archivo :? = 11;(limpiar) OpenDialogl.Filter: = ”; si OpenDialogl.Execute entonces fname: = OpenDialogl.FileName else sale; asignar archivo (fi, fname);(establezca el archivo fuente I tratar

restablecer (fi); (fuente abierta) excepto

Salida; (Si no abre - para salir) fin;

asignar archivo (fo, ChangeFileExt (fname, 1 .sec ')); reescribir (fo);

leer (fi, xb); (lee el primer byte)

Forml.Caption: = ’ProbaCrypt:’ + ExtractFileName (fname);

/título Archivo - v título) Memol.Lines.Clear; Memol.Lines.Add ('Espera ...');

Application.ProcessMessages; Memol.Lines.Clear; mientras que no eof (fi) empiecen

xb: = xb xor ord (tecla [i]);(encriptar)

Memol.Text: = Kemol.Text + chi (xb);(salida a Memo)

escribir (fo, xb); (escribir en archivo)

leer (fi, xb); (si el final del archivo - salir) excepto romper; fin; fin; fin;

closefile (fi);

borrar (fi); (destruimos la fuente)

closefile (fo);

(encriptado) fin;

Aquí hacemos XOR de cada byte del archivo fuente con los bytes clave uno a uno; cuando la clave termina, comenzamos de nuevo en su primer carácter. Los resultados se escriben en un archivo con la extensión sec (de "seguridad") y se envían a Memoi. Realizamos exactamente la misma operación durante el descifrado, solo con el archivo cifrado, como resultado de lo cual el archivo original se restaura por completo:

procedimiento TForml.Buttor.2Click (Remitente: TObject); comenzar(Pa digitalizar j OpenDialogl.FileName: = ”;(claro / OpenDialogl.Filter: = '# 4> archivos poBaHHbie | * .sec '; si OpenDialogl.Execute entonces fname: OpenDialogl.FileName else sale; asignar archivo (fi, fname);(abierto encriptado) tratar

restablecer (fi); excepto salida; fin;

asignar archivo (fo, ChangeFileExt (fname, '.txt')); reescribir (fo);(sobrescribe el anterior) leer (fi, xb);

Forml.Caption: = ’ProbaCrypt:’ + ExtractFileNajne (fname);

! nombre de archivo - en el encabezado j

Memol.Lines.Clear;

Memol.Lines.Add (1 Espera ... ');

Application.ProcessMessages;(ver la advertencia) Memol.Lines.Clear; mientras que no eof (fi) empiecen cambia como arriba) para i: = l hasta la longitud (clave) comience

xb: = xb xor ord (teclaU));

Memol.Text: = Memol.Text + chr (xb);

escribir (fo, xb);

leer (fi, xb); excepto romper; fin; fin; fin;

closefile (fi); closefile (fo);(pa encriptado) fin;

Tenga en cuenta que no destruyo el archivo cifrado; en principio, la operación de cifrado se puede realizar tantas veces como desee en el texto ya cifrado, para descifrarlo también tendrá que repetirlo la misma cantidad de veces, y esto La técnica se utiliza a menudo en algoritmos de cifrado "oficiales" (solo en este Si tiene que renombrar manualmente el archivo cifrado o modificar un poco el programa, tal como está, no le permitirá abrir el mismo archivo para el descifrado y el cifrado). Naturalmente, el procedimiento de cifrado se puede aplicar a absolutamente cualquier archivo, no solo a los archivos de texto.

Hay un punto sutil relacionado con la destrucción del original: como saben, cuando se destruye un archivo de disco, no se borra, como una grabación musical en una cinta magnética, sino que simplemente en el encabezado FAT estructura el espacio que ocupa. está marcado como gratuito (aproximadamente lo mismo sucede en NTFS). Fue con esta característica que se asoció el trabajo del programa de DOS Unerase (si alguien recuerda de qué se trata). Por lo tanto, incluso si elimina un archivo de la Papelera de reciclaje (un archivo eliminado de nuestro programa en la Papelera de reciclaje, sin embargo, no llega a la Papelera de reciclaje), su contenido permanecerá en el disco hasta que se escriba algo más allí. Por lo tanto, para las personas especialmente paranoicas, los programas de cifrado avanzado ofrecen una opción en la que los archivos se destruyen después de borrarlos y se escriben ceros en su lugar. En nuestro caso, para esto, en principio, basta sin cerrar el archivo, llenarlo de bytes con un valor cero (o cualquier otro, pero sin aumentar ni disminuir el tamaño del archivo), escribirlo en disco (cerrar el archivo) y luego destruirlo. Es cierto que en Windows no puedo garantizar al 100% que se escribirá exactamente en el mismo lugar, por lo que es mejor en tales casos utilizar los mismos programas "oficiales", que, por cierto, destruyen los rastros del fuente no solo en el área del disco donde se almacenó, sino también en el archivo SWAP de Windows, si lo hubiera.

En cuanto a la generación de claves aleatorias, aquí hay una forma. No voy a indicar la implementación en su totalidad, ya que es simple. El generador de números pseudoaleatorios en Delphi (y no solo en Delphi) está estructurado de la siguiente manera: el número inicial del generador se establece a través de la variable RandSeea (pero por defecto es igual a 0). Entonces, la función Random, cuando se usa secuencialmente, siempre devolverá el mismo conjunto de números, independientemente del programa y cuándo lo usemos. A partir de aquí y de la forma en que instala dicho generador en el programa y sobre su base genera una clave, por ejemplo. así es como puede generar un conjunto de caracteres aleatorio de 16 bytes (128 bits), que dependerá solo del valor del desplazamiento inicial del generador de inicio:

:

init: entero; st: cuerda;

st: = ”;

RandSeed: = init;(inicio de compensación) mientras que la longitud (st)<16 do begin xb:=Random(255); if xb>31 luego st: = st + chr (xb); fin;

En este caso, le está pasando a su interlocutor no la clave en sí, sino el valor ir.j .. Se generará exactamente la misma clave para él mediante un procedimiento idéntico. Por supuesto, puede idear mecanismos más astutos para implementar este método.

Quiero señalar, para no oscurecer la esencia del asunto, anteriormente en el programa usé la lectura tradicional byte por byte de un archivo de disco. Puede acelerar drásticamente el procedimiento utilizando uno de los mecanismos para leer previamente un archivo en la memoria: mapeo de archivos, como en el Capítulo 14, lectura en continuo o cualquier otra forma de organizar matrices dinámicas en la memoria (consulte el Capítulo 21). En el ejemplo con el uso de esteganografía, al que ahora procederemos, la situación se corregirá parcialmente.

Un cifrado de flujo realiza operaciones en bits o caracteres (por ejemplo, 8, 16 o 32 bits). Un cifrado de flujo convierte el mismo carácter de texto sin formato en diferentes caracteres de texto cifrado, por ejemplo, dependiendo de cuántos y qué caracteres se procesaron previamente.

Cifrado Xor

En muchos cifrados de flujo, el cifrado se realiza de la siguiente manera. El generador de números pseudoaleatorios produce una secuencia de bits (gamma). Esta gama se aplica al texto sin formato mediante una operación XOR bit a bit. El resultado es un texto cifrado. Para descifrar, debe seguir exactamente el mismo procedimiento, solo superponer la gamma obtenida utilizando un generador idéntico con exactamente el mismo estado inicial en el texto cifrado.

Consideremos la idea de este método más simple. Como se sabe del álgebra de Boole, la operación de adición lógica "⊕" módulo 2 (o lógica exclusiva OR - XOR, eXclusiva OR) tiene la siguiente semántica:

Tabla de verdad para XOR:

X yo ⊕ y I

X = 10011 101

y= 01001 100

z= 11010 001

Es decir, la operación z= Xy esencialmente bit a bit (bit a bit - el resultado no depende de bits adyacentes). Si solo uno de los bits correspondientes es 1, entonces el resultado es 1. Y si ambos son 0 o ambos son 1, entonces el resultado es 0. Si observa de cerca el resultado de aplicar XOR a dos números binarios, entonces puede ver que podemos restaurar uno de los términos usando el segundo: X= zy o y= zX.

De esto podemos sacar las siguientes conclusiones: conocer el número y y aplicando XOR A X, Nosotros recibiremos z... Luego, usamos de nuevo y, obtenemos de z número atrasado X... Así, podemos transformar una secuencia de números ( X) I en la secuencia z) I... Ahora podemos llamar al número y clave de codificación (o encriptación). Si una persona no conoce la clave, entonces no puede restaurar la secuencia original de números ( X) I... Pero si ( X) I son la representación en bytes de las letras del texto, un usuario experimentado podrá descifrar el texto cifrado. Dado que cada letra estará representada en el texto cifrado por el mismo código z, luego, utilizando el diccionario de frecuencia, el cracker podrá calcular la clave de cifrado y si tiene un texto cifrado suficientemente largo a su disposición.

A la luz del último razonamiento, llegamos a la conclusión de que es imposible codificar directamente un texto simple. Primero, el número que representa el espacio seguirá separando palabras en el texto cifrado. Al resaltar este número que ocurre con frecuencia, el usuario adivinará que se trata de un espacio codificado. En segundo lugar, las conjunciones y preposiciones comunes breves también ayudarán al cracker a determinar la clave. Por tanto, la forma más eficaz es utilizar una clave larga que cubra varias letras, o mejor, de la misma longitud que el mensaje en sí. Entonces, si codificamos un mensaje lo suficientemente largo (al menos 5-10 oraciones) usando una clave aleatoria de la misma longitud, entonces dicho mensaje es muy difícil de descifrar. Se pueden lograr resultados aún más altos en términos de confiabilidad si antes del cifrado, por ejemplo, el texto es comprimido por algún archivador. Además, si el mensaje es corto, puede agregar secuencias aleatorias de caracteres al principio y al final del mensaje.

Por tanto, la robustez del algoritmo depende únicamente de las características de la gamma producida por el generador. Si la gamma consta solo de ceros (caso degenerado), los datos durante el cifrado no cambian en absoluto. Si la gamma tiene un período corto (por ejemplo, 32 bits), entonces el cifrado se reduce a una operación XOR con una constante de 32 bits. Si la gamma es un conjunto aleatorio de bits que no obedece a ninguna regularidad, se obtiene un análogo de un pad de cifrado de un solo uso, que proporciona una protección absoluta. Por supuesto, el algoritmo determinista utilizado en el generador de gamma no puede producir una secuencia verdaderamente aleatoria. Si la secuencia no se puede repetir, el mensaje tampoco se puede descifrar.

Si dos mensajes se cifraron utilizando la misma gama y para uno de los mensajes (el más largo) fue posible obtener el texto sin formato de alguna manera, entonces es fácil obtener el texto sin formato para el otro mensaje también. Al aplicar la operación XOR al texto sin formato y al texto cifrado del primer mensaje, obtenemos un fragmento de la gamma. Y al imponer una escala al texto cifrado del segundo mensaje, obtenemos su texto sin formato. Es por eso que no se debe permitir el uso de la misma gama al cifrar dos transmisiones o mensajes diferentes.

Si la gamma se genera independientemente del contenido del mensaje, dicho algoritmo de transmisión se llama síncrono. Normalmente, en los cifrados de flujo síncrono, se utiliza una clave de cifrado para establecer el estado interno inicial del generador de gamma.



¿Te gustó el artículo? Compártelo