Sistema de cifrado por lote de texto plano

Iniciado por JoseluCross, 29 Marzo 2016, 17:37 PM

0 Miembros y 4 Visitantes están viendo este tema.

JoseluCross

Hola a todos, un amigo y yo hemos estado trasteando un poco con C y hemos creado un cifrado algo simplón y queremos saber como de fuerte es. Os comento un poco como funciona. Se mete un texto de entrada y uan contraseña y en la salida cada caracter de la entrada se sumará al caracter correspondiente de la contraseña siguiendo este esquema, tanto para el cifrado como el descifrado.

/*
*Title: crypt
*Description: It de/encrypt strings
*@param pass[]: string which we use like password
*@param text[]: string which we will encrypt
*@param x: false = encrypt, true = decrypt
*@param name[]: name of output
*@return text_length: text length
*/
void crypt(char pass[], char text[], bool x, char name[]) {
 int     pass_length;
 int     text_length;
 int     passPosition = 0; //Relative position in pass[]
 int     textPosition = 0; //Relative position in text[]
 pass_length = length(pass);
 text_length = length(text);
 int     sol; //output character

 FILE   *nom;

 nom = fopen(name, "w");

 for(textPosition = 0; textPosition < text_length; textPosition++) {
   if(passPosition == pass_length) {
     passPosition = 0;
   }
   if(x == false) {
     sol = text[textPosition] + pass[passPosition];
     while(sol > 126) {
sol -= 94;
     }
   } else {
     sol = text[textPosition] - pass[passPosition];
     while(sol < 32) {
sol += 94;
     }
   }
   passPosition++;
   fputc(sol, nom);
 }
 fclose(nom);
}

Además tiene la opción de generar texto aleatorio tras la salida para meter datos que no forman parte del texto original y creemos que permite una barrera contra un ataque por estadistica de palabras.
Todo el código está en https://github.com/JoseluCross/cryptoJKA/tree/master/cryptojka

Queremos saber si es seguro, o al menos, cuan seguro es. Muchas gracias
No hay problema sin solución, solo personas que no quieren resolverlo.

AlbertoBSD

Practico el ejemplo, para fines de aprendizaje sobre sistemas de cifrados es buen trabajo, aun que realmente seguro no es.

Lo de agregar texto random suena bien, sin embargo la seguridad atravez de la oscuridad no estan segura.

Aun asi buen ejemplo para fines practicos.
Donaciones
1Coffee1jV4gB5gaXfHgSHDz9xx9QSECVW

JoseluCross

Muchas gracias por tu comentario. ¿Como sería el modo para descifrar el contenido cifrado sin la clave correcta? Me interesa mucho el tema pero estoy algo verde. Muchas gracias.
No hay problema sin solución, solo personas que no quieren resolverlo.

AlbertoBSD

Como tu lo comentas un ataque por estadistica podria arrojar algo de luz sobre la distribución / frecuencia de los datos.


#include<stdio.h>
#include<string.h>

int main() {
char *pass = "HolaMundo";
char *text = "QWERT456Y76U76I7OP6A756S7D567F6G7H56J7K67L6Ñ7Z56X7C567V65B7N65M7q56w2e3r2t41213yuiopasdfghjklzxcugjh877yvbty7t6645r311nm";
int x = 1;
char *name = "salida.txt";
crypt(pass,text,x,name);
return 0;
}


Hice este codigo para testear tu algoritmo, deja checo la salida y la analizo.

Cambie los length por strlen y cambie los bool por int para podrelo compilar sin errores, no se que compilador estes usando, en fin con los cambios que le hice funciona bien.

Esta fue la salida:

CitargF7Oe{%0HM%G4G2'I?L0)2G<'>$L&83X 8/%`&=3H5&y<MI'3i 3/%ME(2S >0$c&c2G`"_"*!f1By!-h-Xamr\T`V Y]i-aSoV"W*4Hbf\c1&f3G{%l"G `j
Saludos!
Donaciones
1Coffee1jV4gB5gaXfHgSHDz9xx9QSECVW

JoseluCross

Yo uso gcc, la funcion lenght es una función que implementé en el código. El código completo es

Del data.h

//Here all constant are defined

#define MAX_TEXT 1048576
#define MAX_PASS 64
#define VERSION "0.4.2"

del main.c
/*Title: cryptojka
*Descripton: cryptation character by character
*Autor: José Luis Garrido Labrador (JoseluCross) and Kevin Puertas Ruiz (Kprkpr)
*Version: 0.4.2 - mar/16
*/
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include "data.h"

#include "methods.c"

int     clean_stdin(void);
void    crypt(char[], char[], bool, char[]);
int     length(char[]);

int main(int argc, char *argv[]) {
  bool    state; //false when encrypt, true when decrypt
  bool    ran = false; //false: not random generation, true: random generation
  bool    fil; //false: not file, true, with file
  bool    cond = false; //If false = not argument
  int     cant = 0; //number of characters in random generation
  char    text[MAX_TEXT] = "text"; //imput character
  char    pass[MAX_PASS] = "pass"; //Imput pass
  FILE   *in; //Input file
  char    out[35] = "crypt.out"; //output file

  //Flags options
  int     i;
  for(i = 0; i < argc; i++) {
    if(strcmp(argv[i], "-e") == 0) {
      state = false;
      cond = true;
    } else if(strcmp(argv[i], "-d") == 0) {
      state = true;
      cond = true;
    } else if(strcmp(argv[i], "-f") == 0) {
      in = fopen(argv[i + 1], "r");
      fil = true;
      cond = true;
    } else if(strcmp(argv[i], "-o") == 0) {
      strcpy(out, argv[i + 1]);
      cond = true;
    } else if(strcmp(argv[i], "-p") == 0) {
      strcpy(pass, argv[i + 1]);
      cond = true;
    } else if(strcmp(argv[i], "-t") == 0) {
      strcpy(text, argv[i + 1]);
      fil = false;
      cond = true;
    } else if(strcmp(argv[i], "-r") == 0) {
      ran = true;
      cant = atoi(argv[i + 1]);
      cond = true;
    } else if(strcmp(argv[i], "--version") == 0
      || strcmp(argv[i], "-v") == 0) {
      printf("cryptoJKA, version: %s\n\n", VERSION);
      return 0;
    } else if(strcmp(argv[i], "-h") == 0) {
      helpbox(); //In methods.c
      return 0;
    }
  }

  if(cond == false) {
    printf("No option specified\n");
    helpbox();
    return 0;
  }

  if(state == false) {
    if(fil == false) {
      crypt(pass, text, false, out);
    } else {
      for(i = 0; feof(in) == 0; i++) {
text[i] = fgetc(in);
      }
      for(i = 0; i < length(text); i++) {
if(text[i] == '\n') {
  text[i] = ' ';
}
      }
      crypt(pass, text, false, out);
    }
    if(ran == true) {
      rangen(cant, out); //In methods.c
    }
  } else {
    if(fil == false) {
      crypt(pass, text, true, out);
    } else {
      for(i = 0; feof(in) == 0; i++) {
text[i] = fgetc(in);
      }

    }
    crypt(pass, text, true, out);
  }

  printf("\n");
  showFile(out, MAX_TEXT);

  return 0;
}

/*
*Title: crypt
*Description: It de/encrypt strings
*@param pass[]: string which we use like password
*@param text[]: string which we will encrypt
*@param x: false = encrypt, true = decrypt
*@param name[]: name of output
*@return text_length: text length
*/
void crypt(char pass[], char text[], bool x, char name[]) {
  int     pass_length;
  int     text_length;
  int     passPosition = 0; //Relative position in pass[]
  int     textPosition = 0; //Relative position in text[]
  pass_length = length(pass);
  text_length = length(text);
  int     sol; //output character

  FILE   *nom;

  nom = fopen(name, "w");

  for(textPosition = 0; textPosition < text_length; textPosition++) {
    if(passPosition == pass_length) {
      passPosition = 0;
    }
    if(x == false) {
      sol = text[textPosition] + pass[passPosition];
      while(sol > 126) {
sol -= 94;
      }
    } else {
      sol = text[textPosition] - pass[passPosition];
      while(sol < 32) {
sol += 94;
      }
    }
    passPosition++;
    fputc(sol, nom);
  }
  fclose(nom);
}

/*
*Title: length
*Description: It count logic string length
*@param l[]: string
*@return m: lenght
*/
int length(char l[]) {
  int     m = 0;
  while(l[m] != '\0') {
    m++;
  }
  return m;
}

y del methods.c es
#include "data.h"

/*
*Title: showFile
*Description: Print in screen a file
*@param n[]:filename
*@param tam: length of file
*/
void showFile(char n[],int tam){
char f[tam];//output string

FILE *show;
show = fopen(n, "r");
fgets(f,tam,show);
printf("%s\n",f);
}

/*
*Title: helpbox
*Description: Show the help menu
*/
void helpbox(){
printf("\n");
printf("\tcryptoJKA from JKA Network - Version: %s\n", VERSION);
printf("\n");
printf("\tThe text must be between ASCII 32 and ASCII 125\n\n");
printf("\tOptions:\n");
printf("\t -f [file_name],\tinput file\n");
printf("\t -o [file_name],\toutput file (default : crypt.out)\n");
printf("\t -p [text],\t\tpassword (default: pass)\n");
printf("\t -t [text],\t\ttext (default: text)\n\t\t\t\t\tIf you put -f and -t, text have preference\n");
printf("\t -e,\t\t\tencrypt mode\n");
printf("\t -d,\t\t\tdecrypt mode\n");
printf("\t -r [number],\t\twith random generation [number of character]\n");
printf("\t -h,\t\t\tshow this box\n");
printf("\t -v, --version,\t\tshow version\n\n");
printf("\t Examples:\n\n");
printf("\t\tcryptojka -e -t \"Example text\" -p password -o file_name -r 600\n");
printf("\t\tcryptojka -d -f file_name -p password\n\n");
}

/*
*Title: random
*Description: It generates random text between ASCII 32 and 126
*@param tam: max length of string
*@param name[]: output name
*/
void rangen(int tam, char name[]){
FILE *out_text;

out_text = fopen(name, "a");

int p;//Generated num
int m;//Relative position
for(m=0;m<tam;m++){
p=(rand() % 95)+32; //p is between 32 and 126
fputc(p, out_text);
}
fclose(out_text);
}
No hay problema sin solución, solo personas que no quieren resolverlo.

LaiaxanIV

Lo que has implementado es un algoritmo ya existente llamado Vigènere. Este algoritmo de cifrado es bastante fácil de descifrar. Lo único que se tiene que hacer es buscar patrones repetidos en el texto cifrado, así puedes conocer la longitud de la clave.
A partir de ahí, se crea una matriz de N x M, donde N es la longitud de la clave y M longitud del texto cifrado/N.
Así, finalmente, se empieza a jugar con las frecuencias de las letras que aparecen en las columnas de esta matriz y a compararlas con el idioma del texto cifrado -si no lo conoces vas probando :P - (ej.: en inglés la letra más repetida es la e, después la t, la a...) y ya lo tienes.
Hace poco hice una práctica que iba de eso. Lamentablemente perdí el código por un error al borrar la carpeta del github donde lo teníamos.

kub0x

Vigènere depende del tamaño de clave, básicamente si el tamaño de clave es inferior al tamaño del texto a cifrar (plaintext) se reutilizará la misma clave, pudiendo observar patrones en el ciphertext, deduciendo así la clave.

Si por algún motivo el atacante tiene en su poder ciertas partes del plaintext, le sería mucho más fácil obtener ciertas partes de la clave, por lo que podría deducir aún más carácteres del plaintext. Remarcar que si el tamaño de clave es igual al tamaño del plaintext se equipararía con un One-Time-Pad (se podría decir que a cada carácter una clave).

Saludos!
Viejos siempre viejos,
Ellos tienen el poder,
Y la juventud,
¡En el ataúd! Criaturas Al poder.

Visita mi perfil en ResearchGate


JoseluCross

No sabía que el cifrado tenía nombre, gracias por aclararlo.

Conociendo el problema de la estadistica implementamos un sistema para añadir caracteres aleatorios al final del texto cifrado, obviamente no sirve de nada si el atacante conoce la cantidad de caracteres son aleatorios porque siempre se colocan al final, pero si esto es desconocido ¿Es más seguro? Y otra pregunta, ¿cómo se buscan los patrones que mencionáis para ver repeticiones en el texto cifrado? y reconociendo los patrones ¿como tengo que aplicar el criptoanalisis existiendo 94 caracteres posibles? Muchas gracias nuevamente por todas sus opiniones.
No hay problema sin solución, solo personas que no quieren resolverlo.

LaiaxanIV

#8
Aunque se desconociese el número de caracteres aleatorios se podría usar la misma estrategia para descifrar el mensaje, lo único que será más costoso ya que me puedes variar el orden de repeticiones. La única manera de hacerlo completamente seguro es que la longitud de la clave sea la misma longitud que la del texto (esto es un estándar del NIST lo que ahora no me acuerdo bien del nombre).
Te pongo un ejemplo:
Clave: "HOLA"
Mensaje: este es un mensaje super secreto y nadie nunca lo va a descifrar
Cifrado: lgee lg fn tsyshxp sbdpr zsnrlhz y uooil bfnjo wo co l dlgnimflr

A simple vista, se observa como la letra l se repite cada 4 posiciones. Seguro que alomejor otras letras tambien (ten en cuenta que en textos muy largos las repeticiones llegan a ser de mas letras cosa que te asegura más precisión).

lgee
lgfn
tsys
hxps
bdpr
zsnr
lhzy
uooi
lbfn
jowo
cold
lgni
mflr


Supongamos que se que es español, por lo tanto las frecuencias son E,A,O,S,N,R.

Una vez tienes esto, solo tienes que contar por columnas la letra que mas aparece. En el primer caso es la l que aparece 5 veces. Pues para conseguir una E con una L utilizamos la H. Para conseguir la A utilizamos una L.
La segunda columna estamos entre S y G que aparecen tres veces. Vamos a elegir la S porque es la buena (jijijiji) tendrías que probar xD. Por tanto para obtener la E con S... Una O!
Seguiríamos así hasta haber hecho las 4 columnas.

Ten en cuenta que no siempre la E es la letra indicada, tendremos que usar la A, la O, la S... Dependerá un poco del texto. Fíjate que la última columna la letra más repetida es la R que es la 6a letra en el orden de frecuencias, para llegar a obtener la A tendríamos que haber pasado por las otras. Así tendremos que probar con unas cuantas claves, descifraremos el texto con todas las claves y elegiremos el bueno!

JoseluCross

Si, lo veo, pero mi cifrado no es vigenere puro, funciona con ascii(32-126) no con el alfabeto por lo que una mayúscula y una minuscula bajo la misma letra son distintas, los espacios tambien se codifican, las comas, los puntos etc. En ese caso cual es la forma de vulnerar el cifrado. Por ejemplo

CitarOdbHhVaa__lPO_aDTVlV_aSU]VQUOe]aco\DNZSaXf\FKoZRhgOaKoRH]TWI\R`
Sería "este es un mensaje super secreto y nadie nunca lo va a descifrar" con la contraseña HOLA

Muchas gracias, estoy aprendiendo muchisimo
No hay problema sin solución, solo personas que no quieren resolverlo.