[Aporte] Funciones para Cifrado Vernan. Cifrado Simétrico(con clave).

Iniciado por @XSStringManolo, 5 Octubre 2019, 20:27 PM

0 Miembros y 1 Visitante están viendo este tema.

@XSStringManolo

Estoy haciendo un proyecto en el que para aumentar la seguridad me decidí a diseñar e implementar mi propio cifrado de 0 sin guiarme por nada.
Por casualidad(o por sencillez) llegué a la misma conclusión que a la que llegó Vernan con su cifrado hace años. Asique en gran parte del proceso reinventé la rueda.
Entonces se puede considerar este código como una implementación del cifrado de Vernan.
Las funciones no están muy debugeadas ni testeadas. De momento me funcionan bien en los códigos que utilicé. Comenté su funcionamiento y varios factores a tener en cuenta para utilizar el cifrado de forma segura en el propio código.

El cifrado lo tengo todo diseñado en papel. Parte implementada en javascript y ahora lo estoy rescribiendo en PHP.
Publicaré en el foro otras partes del cifrado como el CSPRNG que estoy acabando de mejorar para obtener numerosas fuentes de entropia a partir de la propia clave. La implementación del método de transposición, etc.

Cualquier error en el código, duda, sugerencia, comentario u opinion es bienvenida.

Descripción breve:
SMTaD($stringCipherText);
-Pasa el texto de un string a su posición decimal en el abecedario.
Retorna un array con las posiciones.
"abcdefg" -> [1,2,3,4,5,6,7]
"hola" -> [8,16,12,1]

SMDpD($arrayDecimalDeTextoPorCifrar, $arrayContraseñaDecimal);
-Suma los valores de cada posición de ambos arrays.
Retorna un nuevo array con la suma de ambos.
La contraseña debe ser el mismo o mayor tamaño.
[8,16,12,1] + [1,2,3,4,5,6,7] -> [9,18,15,5]

SMDaT($arrayDecimal)
-Transforma cada grupo de decimales al texto correspondiente a esa posición en el abecedario.
Retrona un nuevo array con los caracteres correspondientes.
[9,18,15,5] -> ['i','q','ñ','e']

Se usan diccionarios para definir el "abecedario".


 
Código (php) [Seleccionar]
function SMTaD($cipherText)
  {

  $cipherTextTam = strlen($cipherText);
  $cipherText = strtolower($cipherText);
  $diccionario = array('a','b','c','d','e','f','g','h','i','j','k','l',
'm','n','ñ','o','p','q','r','s','t','u','v','w','x','y','z','A','B','C',
'D','E','F','G','H','I','J','K','L','M','N','Ñ','O','P','Q','R','S',
'T','U','V','W','X','Y','Z','1','2','3','4','5','6','7','8','9','0','.',
':','/',' ');
  $diccionarioTam = count($diccionario);
  $cipherTextDec = [];


  $y = 0;
     for($i = 0; $i < $cipherTextTam; ++$i)
     {
        for($x = 0; $x < $diccionarioTam; ++$x)
        {
           if($cipherText[$i] == $diccionario[$x])
           {
           $cipherTextDec[$y] = 1+$x;
           ++$y;
           }

           else
           {
           /*Caracter no está en el diccionario*/
           }
        }
     }
  return $cipherTextDec;
  }
/*StringManolo Texto a Decimal.
Esta función toma como argumento un
string $cipherText.

*Se recorre todo el string y se compara cada
caracter con los caracteres que contiene el diccionario.

*En el caso de que el caracter a comprobar del
$cipherText coincida con alguno de los caracteres
del diccionario, se añade a un array la posición de
ese caracter+1 (alfabéticamente) en el diccionario.

*Finalmente se retorna este array que hace referencia
al orden alfabético de cada caracter.

*EJEMPLO:
-Si $cipherText contiene la frase "Me van a cifrar".

1) -Se busca el caracter M en el diccionario.

-La letra 'M' ocupa la posición 40 en el diccionario.

-En el array diccionario 'M' en realidad corresponde al
índice 39. Debido a que el array utiliza el 0 como
primer índice y no el 1. Debido a esto se suma +1 para
obtener 40.

-Si no se encuentra 'M' en el diccionario o si
ya se añadio el numero 40 se pasa a la siguiente letra.

2) Se repite el proceso anterior para la letra 'e'.

3) El resultado final de pasar el string "Me van a cifrar"
seria el mismo que al hacer:
$TextoEnDecimal = array('40','5','68','23','1','14','68','1','68','3','9','6','19','1','19');
Hace referencia a: array('M','e',' ','v','a','n',' ','a',' ','c','i','f','r','a','r');

*Utilizo strToLower para convertir todas las letras mayúsculas
a minúsculas. Comenta o borra esa línea si te interesa mantener la
información original.

*Asegurate que todos los caracteres del
texto a cifrar estén contemplados en el diccionario.
Puedes crear una funcion para obtener todos los caracteres
distintos del texto a cifrar y crear el diccionario en base a ellos.
Así evitas comprobaciones innecesarias con caracteres que
sabes de antemano que no vas a encotrar.
Por ejemplo si aplicas base64 antes de la función, puedes
eliminar ' ',':','.' del diccionario y añadir '+','=' ya que sabes de
antemano que un texto cifrado en base64 no puede contener
espacios, dos puntos o puntos.

**Obtener una referencia numérica de las letras tiene entre
otras utilidades poder operar matemáticamente con los caracteres.
*/



  function SMDpD($decimal1, $decimal2)
  {
   
  $diccionario = array('1','2','3','4','5','6','7','8','9',
'10','11','12','13','14','15','16','17','18','19','20','21',
'22','23','24','25','26','27','28','29','30','31','32','33',
'34','35','36','37','38','39','40','41','42','43','44','45',
'46','47','48','49','50','51','52','53','54','55','56','57',
'58','59','60','61','62','63','64','65','66','67','68','69');
  $diccionarioTam = count($diccionario);
  $decimal1Tam = count($decimal1);
  $decimal2Tam = count($decimal2);
  $cipherTextSum = [];

     $y = 0;
     for($i = 0; $i < $decimal1Tam; ++$i)
     {
        for($x = 0; $x < $diccionarioTam; ++$x)
        {
           if($decimal1[$i] == $diccionario[$x])
           {
           $cipherTextSum[$y] = $decimal1[$i] + $decimal2[$y];
           ++$y;
           }
        }
     }
  return $cipherTextSum;
  }
/*StringManolo Decimal plus Decimal.
StringManolo Decimal + Decimal.
Esta función toma como argumentos 2 arrays
que contienen numeros y los suma.

Durante el diseño de este cifrado llegué a la
conclusión de que esta suma modular de 2 caracteres
ya se utilizaba por el Cifrado Vernan. El Cifrado Vernan
sentó las bases para otros cifrados de uso frecuente
a lo largo de la historia. Uno de los principios más
seguros es el conocido como Libreta De Un Solo Uso.
https://es.m.wikipedia.org/wiki/Libreta_de_un_solo_uso

IMPORTANTE:
Este cifrado está probado como irrompible bajo ciertas
condiciones. Esta implementación deja a cargo del
usuario de estas condiciones. Y algunas
consideraciones. Paso a comentar estos puntos a
continuación:
1)Se espera que se utilize como uno de los parámetros
de la función una cadena *Aleatoria del mismo o mayor
tamaño que el texto a cifrar.

En computación (sin entrar propiedades cuánticas)
es imposible generar un número totalmente aleatorio.

Para suplir este inconveniente se
recurre a utilizar algoritmos para generar numeros
pseudoaleatorios resistentes a criptoanálisis.
Estos algoritmos son conocidos como CSPRNG.
Algunos de los provados como más seguros son
aquellos que generan secuencias pseudoaleatorias
utilizaneo como entropia el "ruido de fondo" o "ruido"
ambiente captado por el micrófono.
En realidad puedes escribir letras aleatoria como clave
para el cifrado siempre y cuando:
-No sigas ningún patrón que se pueda replicar para
genear la clave que vas a utilizar.
-El cifrado lo debes generar en un equipo offline únicamente
dedicado para ello. En caso de que el equipo se conecte
a otros equipos, un atacante podría llegar a infiltrarse
en el sistema y obtener la clave utilizada.
El cifrado de la Libreta De Un Solo Uso se basa en que una
vez utilizada la clave esta se destruya.


2) El tamaño de la clave debe ser igual o mayor que el tamaño
del texto a cifrar.

Consideraciones:
-No deja de ser un cifrado de substitución. Si la
clave es insegura "aaaaaaaaa" o se tienen indicios del
idioma en el que se escribió el texto a cifrar, se pueden realizar
diversos analisis de frecuencia. Para eliminar esta posibilidad
recomiendo utilizar algún cifrado de transposición para desordenar
el texto a cifrar. Mas adelante recomiendo cual utilizar.

-No debe reutilizarse la clave ni dejar a un usuario cifrar con la misma
clave. Si estas utilizando la misma clave, por ejemplo
"abcdefghijk..." para cifrar un texto. Si dejas a un usuario
cifrar con ella, podría cifrar el texto "aaaaaaaaaaaaa..." y así
generar la clave +1 para cada caracter.

Siguiendo estos principios se espera que el primer parámetro de la
función sea un array con los decimales del texto a cifrar.
Y que el segundo parámetro sea la contraseña tambien en decimal.
Se pueden pasar strings a la función SMTaD(); para obtener el array
en decimal correspondiente.
Ejemplo:

$Saludo = "Hola";
$Contraseña = "ceaf";

$Saludo = SMTaD($Saludo);
$Contraseña = SMTaD($Contraseña);

$SaludoCifrado = SMDpD($Saludo, $Contraseña);

$SaludoCifrado = SMDaT($SaludoCifrado);

Sucede lo siguiente:
SMTaD($Saludo); devuelve el array 8,16,12,1
SMTaD($Contraseña); devuelve el array 3,5,1,6
SMDpD($Saludo, $Contraseña); devuelve el array 11,21,13,7

La siguiente función SMDaT($array); utiliza un diccionario para
convertir los decimales a texto.

SMDaT($SaludoCifrado); devuelve el array k,t,m,g

Puedes usar:
Un diccionario de 27 caracteres (a-z) y usar el operador módulo
o restar el tamaño del diccionario. En este caso a+z seria 28.
28-27 es 1.
1 es igual a 'a'.

Un diccionario de 54 caracteres (a-Z).
Un diccionario de 64 caracteres (a-9).

O un diccionario que contemple las máximas posibilidades.
En caso de solo operar con minúsuculas tanto en la clave
como en la contraseña (a-z) tendrías un valor máximo posible
de z+z (27+27). Es decir, puedes representar todos
los resultados en un diccionario de 54 caracteres.
Por ejemplo (a-Z). Así z+a = 28. que sería 'A'.
Esta última opción puede ser menos o más segura criptográficamente.
Pero sigue siendo irrompible si se siguen todas las pautas.
Ya que 'A' puede ser a+z, b+y, c+x, d+w, e+v, f+u ...
En conclusión 'A' puede ser cualquier letra del abecedario
en conjunto con otra letra opuesta (en relación con su orden alfabético).
Es difícil analizar que método es mejor.
Teoricamente ninguno da información relevante en el supuesto de
utilizar una clave aleatoria y cifrando por transposición el texto.

Debido a que la seguridad del cifrado se basa en el desconocimiento de la clave
por parte del atacante, recomiendo utilizar la propia clave para aplicar transposición
al texto que se va a cifrar, o al propio resultado del cifrado.
Siguiendo el ejemplo anterior:
texto -> h o l a
contraseña -> c e a f
resultado -> k t m g

Puedes utilizar el propio orden alfabético de la clave para reordenar el resultado:
c e a f -> 1c=2, 2e=3, 3a=1, 4f=4
k t m g -> 1k pos2, 2t pos3, 3m pos1, 4g pos4
resultado -> m k t g

*Esta funcion puede ser utilizada sin modificaciones para cifrar utilizando varios
cifrados comunes.
Por ejemplo para cifrar con cifrado Cesar introduce como clave la letra
que corresponda alfabéticamente al numero de vueltas.
$Saludo = "Hola";
$Contraseña = "bbbb";
$Saludo = SMTaD($Saludo);
$Contraseña = SMTaD($Contraseña);
$SaludoCifrado = SMDpD($Saludo, $Contraseña);
$SaludoCifrado = SMDaT($SaludoCifrado);
El resultado de cifrar hola con bbbb es j q n c.

Para cifrar con vigenere:
$Saludo = "Hola";
$Contraseña = "abcd";
$Saludo = SMTaD($Saludo);
$Contraseña = SMTaD($Contraseña);
$SaludoCifrado = SMDpD($Saludo, $Contraseña);
$SaludoCifrado = SMDaT($SaludoCifrado);
El resultado de cifrar hola con abcd es iqñe
en el diccionario incluí la ñ. Si la eliminas el
resultado seria el mismo que en el diccionario
inglés de uso común resultado -> iqoe.
Se obtiene el mismo resultado que cifrando hola
con vigenere y la clave bcde.
Con un par de modificaciones se puede conseguir el
mismo resultado.

Si quieres imprimir el contenido del array puedes usar:
foreach($SaludoCifrado as $value)
{
echo "$value" ;
}

Y para imprimirlo en un documento de texto:
file_put_contents("carpeta/resultado.txt", $SaludoCifrado, FILE_APPEND);

*/


  function SMDaT($cipherText)
  {
  $cipherTextTam = count($cipherText);

  $diccionario = array('a','b','c','d','e','f','g','h','i','j','k','l',
'm','n','ñ','o','p','q','r','s','t','u','v','w','x','y','z','A','B','C',
'D','E','F','G','H','I','J','K','L','M','N','Ñ','O','P','Q','R','S',
'T','U','V','W','X','Y','Z','1','2','3','4','5','6','7','8','9','0','.',
':','/','-','!','@','^','&','*','+','=','<','>','{','}','[',']','€','%','?','_','|',
'a','b','c','d','e','f','g','h','i','j','k','l','m','n','ñ','o','p','q','r','s',
't','u','v','w','x','y','z','A','B','C','D','E','F','G','H','I','J','K','L',
'M','N','Ñ','O','P','Q','R','S','T','U','V','W','X','Y','Z'); $diccionarioTam = count($diccionario);

  $diccionario2 = array('1','2','3','4','5','6','7','8','9',
'10','11','12','13','14','15','16','17','18','19','20','21',
'22','23','24','25','26','27','28','29','30','31','32','33',
'34','35','36','37','38','39','40','41','42','43','44','45',
'46','47','48','49','50','51','52','53','54','55','56','57',
'58','59','60','61','62','63','64','65','66','67','68','69',
'70','71','72','73','74','75','76','77','78','79','80','81',
'82','83','84','85','86','87','88','89','90','91','92','93',
'94','95','96','97','98','99','100','101','102','103',
'104','105','106','107','108','109','110','111','112',
'113','114','115','116','117','118','119','120','121',
'122','123','124','125','126','127','128','129','130',
'131','132','133','134','135','136','137','138','139');
  $diccionario2Tam = count($diccionario2);

  $cipherTextText = [];

$z=0;
     for($i = 0; $i < $cipherTextTam; ++$i)
     {
        for($x =0; $x<$diccionarioTam; ++$x)
        {
           if($cipherText[$i] == $diccionario2[$x])
           {
           $cipherTextText[$z] = $diccionario[$x];
           ++$z;
           }
        }
     }
  return $cipherTextText;
  }




e

e