Foros:
Por tokamak
Bueno, pues en este reto tendréis que romper la clave de un criptosistema basado en un generador de números aleatorios. Estos generadores suelen estar basados en funciones recurrentes del tipo:
f x(n+1)=(ax(n) + b) mod m
Las series generadas por estas funciones siempre caen en ciclos, pero es posible ajustar los parámetros para que estos ciclos sean de amplitud máxima. Podéis consultar Wikipedia para ver los detalles. Por ejemplo con a=3, b=5 y m=32 se obtiene un ciclo de amplitud máxima, como máximo m, sin repetirse ningún valor.
Lo interesante son las congruencias con ciclos amplísimos, de las que he encontrado algunos ejemplos aquí o aquí
Al final me he quedado con la siguiente:
a = 2862933555777941757 b = 3037000493 m = 2^64
Esa función tiene un período de 2^64, lo que nos servirá para implementar nuestro algoritmo de esta forma:
El alfabeto base es el estándar de Kriptópolis, con valores entre 0 y 31:
ABCDEFGHIJKLMNÑOPQRSTUVWXYZ_.,:;
0-) Ponemos la clave en el "bloque anterior"
1-) Tomamos un bloque de 8 bytes del texto claro.
2-) Tomamos el bloque anterior, del texto en claro
2-) Convertimos el bloque anterior en una cadena de números, con los valores numéricos de sus carácteres.
3-) Utilizamos ese valor para generar un número "aleatorio" con la función propuesta
4-) Tomamos los 16 valores más significativos de ese número. Si hay menos, anteponemos ceros hasta completar 16 bytes
4-) Repasamos ese número, cogiendo cifras de dos en dos y haciendo módulo 32
5-) Hacemos Xor con esos valores y con cada uno de los valores del bloque actual y generamos el bloque cifrado
6-) Así hasta el final del cifrado
Os pongo un ejemplo
Clave
SANCHO_P
Claro
EN_UN_LUGAR_DE_LA_MANCHA
Traza
Bloque anterior Bloque actual Cadena numérica Aleatorio Aleatorio mod 32 Cifrado SANCHO_P EN_UN_LU 1900130207152716 8144192868236447 1712192804230015 UBIJJMLZ EN_UN_LU GAR_DE_L 0413272113271121 1284083070662115 1220083006022115 UBIJJMLZKTZFFGÑE GAR_DE_L A_MANCHA 0600182703042711 9560860986001016 3128220922001016 UBIJJMLZKTZFFGÑE;HZJ_CNP
Ya se ve que que es un proceso sencillísimo, poco más que un Vigenère con autoclave; además como es difícil que cada secuencia de 8 bytes se repita en el texto y como cada una de ellas nos genera un número distinto que sumar al bloque, resulta que estamos incrementando mucho el nivel de dificultad. Sobre todo teniendo en cuenta que no es fácil compilar una lista exhaustiva de las permutaciones posibles de cada bloque, a causa del gran número (32^8) de ellas, lista con la que podríamos conocer las asignaciones "aleatorias" que corresponderían a cada bloque.
La única complicación técnica de este proceso es la dificultad para hacer cálculos con precisión de 16 dígitos pero, mira por donde, ya había salido el tema en otro post, y yo había localizado unas funciones muy útiles para ese propósito en esta monografía. Sqrmatryx también nos contó como se puee hacer en Java, por lo que creo que no va a ser ningún problema.
Lo he llamado EL GRIAL, por la maravillosa simplicidad de los algoritmos para cifrados basados en generadores aleatorios, que se acercan a mi ideal de criptosistema. Ahora bien: seguro que existe una metodología para atacar estos cifrados, porque si no...apaga y vámonos. Además, finalmente lo he hecho más simple de lo que inicialmente había pergueñado, para dar alguna oportunidad. Por ejemplo no hago un segundo Xor con la clave anterior, lo que ofuscaría más el texto. Hay una vulnerabilidad evidente que podéis explotar.
Bueno que lo disfrutéis, el reto está en un fichero que se puede bajar de aquí
Y este es el código fuente, utilizable como siempre desde Excel.
El cifrado lo componen sólo las 4 primeras funciones, un código minimalista. El resto es la "biblioteca" para cálculos largos.
Public Function CoD(ByRef mensaje As Variant, clave As Variant, cd As Variant) As String Dim bloque_ant, bloque_8, letra, letra_clave, semilla, nuevo, suma Dim i, j, cifra, k, l CoD = "" bloque_ant = clave For i = 1 To Len(mensaje) Step 8 bloque_8 = Mid$(mensaje, i, 8) semilla = "" For j = 1 To 8 letra_clave = Mid(bloque_ant, j, 1) semilla = semilla & Format$(inv_caracter(letra_clave), "00") Next j nuevo = aleatorio(semilla) 'Convierte bloque anterior en nº "aleatorio" aux = "": k = 0 For j = 1 To 16 Step 2 k = k + 1 cifra = CInt(Mid(nuevo, j, 2)) Mod 32 ' nº aleatorio letra bloque cifra = cifra Xor inv_caracter(Mid(bloque_8, k, 1)) CoD = CoD & caracter(cifra) aux = aux & caracter(cifra) Next j If cd = "C" Then bloque_ant = bloque_8 Else bloque_ant = aux Next i End Function Public Function inv_caracter(caracter) Dim i, caracteres caracteres = "ABCDEFGHIJKLMNÑOPQRSTUVWXYZ_.,:;" i = InStr(caracteres, caracter) inv_caracter = i - 1 End Function Public Function caracter(numero) Dim caracteres() caracteres = 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", "_", ".", ",", ":", ";") caracter = caracteres(numero) End Function Public Function aleatorio(semilla) As String Dim x(2) As String Dim y(2) As String Dim z(2) As String Dim CR() As String 'X = (2862933555777941757 * X + 3037000493) mod 2^64 Dim n As Integer, test As Double Dim Potencia, producto, suma As String n = Len(test) - 1 Potencia = "18446744073709551616" ' 2^64 y(1) = semilla y(2) = "2862933555777941757" producto = Multiplicar(y(), n) x(1) = producto x(2) = "3037000493" suma = Sumar(x(), n) z(1) = suma z(2) = Potencia CR() = DivisionEuclidea(z(), n) aleatorio = CR(2) If Len(aleatorio) > 16 Then aleatorio = Left(aleatorio, 16) diferencia = 16 - Len(aleatorio) If diferencia > 0 Then aleatorio = String(diferencia, "0") & aleatorio End Function
Aquí se puede enlazar al texto completo. Funciones de cálculos largos incluídas
Descargar código completo con biblioteca de cálculos largos
Post scríptum
EL GRIAL (FORTE)
Existe la posibilidad de incrementar poderosamente la seguridad de este cifrado, añadiendo un generador adicional pseudoaleatorio que inicializaremos también con el valor de clave, y que se irá alimentando recursivamente con los valores que vaya produciendo. Cada valor generado lo Xoreamos con el bloque actual, y después seguimos el mismo procedimiento que con EL GRIAL.
Como generador adicional podemos usar, por ejemplo:
a = 6364136223846793005
b = 1442695040888963407
m = 2^64
Y ésta sería la traza para cifrar el mismo texto:
Cadena numérica Aleatorio Aleatorio mod 32 Semilla adicional Aleatorio adicional Adicional mod 32 Bloque cifra 1900130207152716 8144192868236447 1712192804230015 1900130207152716 1427708576476394 1427062112153130 _ZÑ.FDTE 0413272113271121 1284083070662115 1220083006022115 1427708576476394 1781493185490403 1717173121170403 _FLZPWKH 0600182703042711 9560860986001016 3128220922001016 1781493185490403 1813710054170517 1813070022170517 NK,JNSIB
No voy a publicar el fuente para no interferir con el reto. Quien quiera el código sólo tiene que pedirmelo.

Calificación A
Yo le adjudico desde ya la calificación A. Si el PRNG no presenta repeticiones cíclicas, se trata de un Fake-OTP con autoclave. Quizás LlamameX haga una de las suyas, atacándolo por donde no está previsto. El problema del autoclave es el posible ataque sobre texto conocido, cuando hay alguna pista.
No obstante, no acabo de ver claro el número-aleatorio-mod-32. Sólo tiene 14 dígitos
17121928423015 = 17 12 19 28 42 30 15
y sale un valor 42 que no puede asignarse a ningún carácter del alfabeto.
Desde luego se podría complicar más, desordenando el alfabeto y también el bloque en claro, pero comprendo que no quieras que nos matemos.
SUGERENCIA:
El larguísimo código se podría poner enlazado a un fichero, para no dificultar la lectura del hilo.
Problema
Oooops!!! bug habemus. Un momento que lo miro...
No hay problema
No, es simplemente problema de la edición del resultado, que cuando el resultado mod 32 de la paraeja es menor de diez, pone unsólo dígito
Esta sería la tabla buena, hasta que Admin me deje editar mi reto ¡que no puedo!
Sustituido en el post original
...mientras me miro lo de los permisos de edición
Ya está
Listo, gracias Admin!, También lleve a cabo la sugerencia de Agustín de enlazar la mayor parte del código.
Simple y elegante, como tiene
Simple y elegante, como tiene que ser un cifrario. Si no le hubieras quitado los 16 dígitos menos significativos a los resultados del generador... :)
Reedición:
---------------------
Ahora que me doy cuenta, sólo participan 2^32 valores de los posibles 2^64, lo que significa que puede ocurrir que dos claves distintas acaben generando, a partir de cierto punto, la misma secuencia, porque de los posibles 2^64 valores que puede devolver el generador, estás recortándolos a 2^32
..Pero sólo tengo 32^8
..Pero sólo tengo 32^8 valores de clave posibles para cada bloque, con valores entre 0000000000000000 y 3131313131313131 por lo que no podemos contar con que se de ninguna repetición, salvo las generadas por bloques idénticos.
******** Reedición *****
Y efectivamente en el texto cifrado, todas las claves diferentes generaron números distintos.
Uys, es verdad...
...no me dí cuenta de que se hacía módulo 32.
A ver si lo he entendido...
Corrígeme si me equivoco, porque antes lo había entendido mal. Entiendo que el bloque anterior se utiliza para obtener un número aleatorio, que se utilizará para hacer XOR con el siguiente bloque. De ser así, si tenemos dos bloques repetidos en diferentes puntos del criptograma, ambos generarán la misma clave para el siguiente bloque. Y si resulta que tenemos dos bloques consecutivos duplicados en el texto en claro (es decir, bloque A seguido de bloque B, y este par de bloques consecutivos repetido en otro lugar del texto claro), cuando se codifiquen, el segundo bloque de ambos duplicados (B) se codificará de la misma manera. En caso de encontrarnos dos bloques idénticos en el criptograma, tendremos bastante certeza de que el texto original en ambos será el mismo, que el bloque anterior en ambos casos tendrá el mismo texto, aunque puede estar codificado de diferente manera, y que el siguiente bloque estará en ambos casos codificado con la misma clave. Si no me he equivocado en lo expuesto, esto puede ser una vulnerabilidad.
Efectivamente es así,
Efectivamente es así, Sqrmatrix, y esa es exactamente la vulnerabilidad de la que hablaba, que puede ayudar a descriptar el texto:
Además, finalmente lo he hecho más simple de lo que inicialmente había pergueñado, para dar alguna oportunidad. Por ejemplo no hago un segundo Xor con la clave anterior, lo que ofuscaría más el texto. Hay una vulnerabilidad evidente que podéis explotar.
Bueno, entonces creo que lo
Bueno, entonces creo que lo voy entendiendo. Es que todavía no lo he implementado, y si no implemento un criptosistema, me cuesta más entenderlo. De todas formas, a primera vista parece complicado resolverlo. A ver cómo avanza la cosa.
Bloques obtenidos
Éstos son los bloques repetidos que he obtenido, y su posición en el criptograma, junto con el bloque anterior y el posterior. Hay altas probabilidades de que los bloques repetidos tengan el mismo texto y la misma clave, que los bloque primeros tengan el mismo texto, pero no la misma clave, y que los bloques últimos tengan la misma clave, pero no el mismo texto.
Vas muy bien, te confirmo que
Vas muy bien, te confirmo que hay numerosas coincidencias en las posiciones que reseñas. Casi puedes pasar ya a la fase de explotación.
(si sé que ibas a tardar tan poco meto los ciclos solapados de dos generadores aleatorios...)
Joer que estress!
Es que no se puede despistar uno. No se si meterme en esto pues antes que le de un tiento ya lo habrá masticado y deglutido Sqrmatrix.
Lo de los 2 (o n) generadores con ciclos distintos te lo iba a comentar como medio fácil de multiplicar por muchísimo la longitud del tuyo.
Tranquilidad, que ahora falta
Tranquilidad, que ahora falta lo más difícil, y me da que va a ser muy difícil. De momento no se me ocurre cómo utilizar estos datos para romper el cifrado. Lo que sí observo, que no me dí cuenta antes, es que los últimos bloques a veces tienen sus primeras letras coincidentes, y es lógico, ya que el texto repetido puede continuar algunas letras en estos bloques, y como están codificados con la misma clave, su codificación coincidirá, lo que nos da una pista de hasta dónde llega el texto. Por otro lado, los primeros bloques tienen el texto idéntico, y posiblemente en algún caso el bloque anterior tenga parte de ese texto coincidente, pero seguramente no coincidan en nada así cifrados y no podamos extraer nada de ellos
Sabemos el XOR de los carácteres en claro
Es evidente que los bloques posteriores a un bloque común difieren por que los carácteres en claro lo hacen. Xoreados entre ellos eliminamos la parte que corresponde al elemento aleatorio que coincidirá y obtenemos el XOR de los carácteres en claro.
Dado que sólo hay que encontrar grupos de texto castellano que xoreados nos den ese resultado para poder descifrar desde ahí, no debería ser demasiado dificil encontrarlos.
O estoy pecando de optimista (siempre lo hago al principio de un ataque)
O sea, haciendo Xor entre las
O sea, haciendo Xor entre las colas de los bloques comunes obtenemos, por fin, un Xor entre dos textos diferentes en castellano. ¿Y dices que no sería difícil descriptarlo? ¡Pero si eso ya es una Fucked-Otp! ....a no ser que se repita un poco....
Esto lo tengo que ver, porque como funcione la mandíbula me va rebotar en los pinreles...
Es muy diferente del FOTP
De entrada no hay alfabeto derivado con lo que podemos operar con los caracteres y después es texto estructurado castellano. Creo que debería caer con mi test de trigramas puesto que ambos bloques, el que se pruebe y el que se obtenga por XOR habrán de cumplirlos. Además hay una segunda verificación posible y es descifrar desde un bloque coincidente hasta completar los dos elementos iguales (la siguiente pareja de bloques iguales) siguientes y comprobar si coinciden.
Una vez descubierto el texto
Una vez descubierto el texto de uno de los bloques, se pueden descifrar todos los que haya después de él aplicando el algoritmo del cifrario. El problema es descifrar los que hay antes
En la vida real, eso sería un
En la vida real, eso sería un problema, pero en este reto si descifras a partir de un punto, el resto lo sacarás también por el contexto, puesto que que el claro es algo conocido.
He añadido al artículo unas líneas sobre un método de fortalecerlo (que no aplicaré en este reto, of course) ¿qué os parece?
Con eso eliminas la vulnerabilidad
Al evitar la repetición de n-gramas, muerto el perro se acabó el perro.
Otros temas que resuelves
Es que al usar sólo la clave para el cifrado del primer bloque (los demás los cifras con el texto claro), aparecen los siguientes problemas:
- Dos cifrados distintos, aún con claves distintas, si tienen un bloque en claro en común podrán usarse entre ellos para un descifrado parcial de ambos textos.
- Un recifrado del mismo texto en claro con otra clave coincidirá en todo con el primer cifrado a partir del tercer bloque haciéndolo identificable y tremendamente atacable pudiendo ir a buscar directamente la clave.
- Juntando ambos casos, si dos textos en claro, cifrados con claves distintas, aunque no coincidan totalmente, empiezan por los mismos bloques estamos fritos.
Bueno...es que es no es mucho
Bueno...es que es no es mucho más que un auto-Vigenère, algún grano le había de salir. Pero para cosas más serias ya está GRIAL FORTE, sólo tres o cuatro inctrucciones más complejo. Por ejemplo para encriptado del tráfico de redes wireless creo que iría razonablemente bien.
Muy, muy difícil
A partir del bloque recien descifrado obtenemos por XOR la modificación (16 dígitos significativos módulo 32) de la parte "aleatoria" que la cifró. Es en teoría posible aunque complicado encontrar los posibles elementos de la secuencia que pudieran generar ese valor y, desde él, los generadores que podrían corresponder al bloque anterior. El proceso habría que repetirlo hacia atrás para cada bloque que se averigüe.
Casi va a ser más fácil atacar por fuerza bruta la clave (que es cortita) directamente que eso. De momento habrá que conformarse con el descifrado parcial.
Sí, creo que tienes razón, se
Sí, creo que tienes razón, se puede ir haciendo una comprobación incremental, ¿cuándo haces una prueba?
Primero tenía que implementarlo
Y ya lo he hecho, podeis probarlo, como siempre aquí
OJO. Parece que la libreria Biginteger que he usado no va bien con algunas versiones de explorer.
Qué rápido lo has
Qué rápido lo has implementado, pero parece que no funciona bien, tú cifras el exto como
y yo como
Algo no va bien, voy a mirar tu código Java, que ahora sé un poquitín,gracias a Rubik2...
Lo haces con explorer 8?
Miralo con chrome o ff. Mientras busco una que vaya mejor con todos
COMPROBADO: CON FIREFOX
COMPROBADO: CON FIREFOX FUNCIONA BIEN
Yo creo que puedes dejarlo así, sabiendo con qué navegadores funciona, vale bien.
Vale
Pero pongo un aviso
El PRNG y la periodicidad
La periodicidad de los PRNGs aparece antes o después cuando el resultado de una generación se toma como semilla para la siguiente. Pero en el caso que nos ocupa, si lo he entendido bien, la semilla se forma con cada bloque de texto (la primera vez con la clave), de manera que quizá no haría falta utilizar un número tan grande (2**64) para hacer la congruencia, lo que eliminaría la necesidad de herramientas que manejen enteros largos, y aumentaría la eficiencia. Corregidme si me equivoco.
Más probable es que encontremos bloques dobles repetidos en el texto, como bien señala sqrmatrix
Sí, pero la periodicidad de
Sí, pero la periodicidad de los dos generadores que os presento, es de 2^64, que es enorme. Y Cuanto más mejor je je..
Los bloques dobles no son consecuencia de la periodicidad, sino de las semillas, los bloques del texto que uso para alimentar el bicho.
Bueno ¿os gustó ésta pequeña novedad? Soy relativamente nuevo en Kriptópolis, pero creo que no se había mostrado antes un cifrado con generador aleatorio. Bueno, vale, Acynos tenía uno, pero baja periodicidad.....
Y se me están ocurriendo muuuuuchas cosas más jejejeje
Una posible mejora
Es desligar la clave del tamaño del bloque o incrementar éste. Para ello habría que cambiar la conversión a numérico del bloque. En lugar de concatenar cifras (otra vulnerabilidad puesto que los dígitos pares -desde 0- serán siempre sólo 0,1,2 o 3) puede usarse fórmulas como Suma((K[i]+1)*10**i)=K[0]+1 + 10*(K[1]+1) + 100*(K[2]+1) +...+ 10**L*(K[L]+1), que nos permitan claves (y por tanto bloques) arbitrariamente más largos.
...o podemos emplear un
...o podemos emplear un sistema parecido al que Agustín nos contó que utiliza RSA para pasar a números el texto:
Para empezar, se codifican los caracteres del mensaje en claro mediante una tabla como ésta:
* 0 1 2 3 4 5 6 7 8 9 0 XX XX XX XX XX XX XX XX XX XX 1 SP A B C D E F G H I 2 J K L M N O P Q R S 3 T U V W X Y Z a b c 4 d e f g h i j k l m 5 n o p q r s t u v w 6 x y z . , : ; ' " ` 7 ! @ # $ % ^ & * - + 8 ( ) [ ] { } ? / < > 9 0 1 2 3 4 5 6 7 8 9en la que cada carácter se representa por el par de números de su fila y su columna. Por ejemplo, la palabra KRIPTOPOLIS se codificaría por el número
Ay, que es lo mismo: 2, 2, 1, 2, 3....
Muy astuto, LlámameX, muy astuto....
opinar