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
function SMTaD($cipherText) { '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','.', ':','/',' '); $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) { '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'); $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) { '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); '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'); $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; }