2020/04/11

Twister del azar

El problema es cómo obtener un buen random para claves temporarias para usuarios o tokens para flujos. Lo típico en una recuperación de contraseñas o cuando un workflow pasa por un mail.

Si uno llama random() no sirve, ya que el (pseudo) azar, da siempre la misma secuencia. Eso en sí no está mal, si uno está haciendo un juego o una simulación y quiere testear componentes que usen azar, lo bueno sería que ese azar sea el mismo en cada ejecución de un mismo unit test.

Luego en producción, hay que darle una semilla para que arranque en algún lugar distinto de la secuencia.

En basic creo recordar (no pienso mirar en google) que era randomize(). En opencobol, que es justo con lo que estaba haciendo las pruebas es random(valor) para cambiar la semilla y random() para obtener el valor al azar.

Esa semilla suele ser tomada de la fecha/hora, lo cual no es muy bueno:

A lo largo de todo el año hay 365x24x60x1000 = 31.536.000.000, ok.

Pero como más o menos se puede saber el día y quizas la hora se reduce a 60x60x1000=3.600.000, no tan ok.

Y si más o menos sabés el minuto 2x60x1000=120.000, mucho menos ok.

Dependiendo del escenario, algunos ataques serían factibles. Por lo general los online no, ya que hay otras medidas de control, por ejemplo no aceptar más que un cierto número de conexiones en un lapso determinado desde una misma IP.

Si a eso se le suma que el token tiene un tiempo de vida y está correctamente calculado según el número de conexiones y el tiempo por IP, está mitigado.

Pero si el ataque es offline o no disponemos de mecanismos adicionales, estamos en el horno.

Podemos agregar un "secreto" a este número y recuperamos la fortaleza inicial, ¿no? Salvo que ese secreto forma parte del código y aunque no compartimos ni descuidamos el código, damos por sentado que está comprometido.



A falta de un mainframe, he aquí una implementación en linux con opencobol que usa los "milisegundos" como semilla.


       IDENTIFICATION DIVISION.
       PROGRAM-ID. principal.
       ENVIRONMENT DIVISION.
       DATA DIVISION.

       WORKING-STORAGE SECTION.
       01  SEED                 PIC 9(08).
       01  WS-TIME.
        03  DUMMY               PIC 9(06).
        03  MILLISECONDS        PIC 99.
       01 IDX                   PIC 9.
       01 RVALUE                PIC 9(09) COMP-3.

       PROCEDURE DIVISION.
       PROGRAM-BEGIN.
         DISPLAY "Generando random sin seed...".
         PERFORM VARYING IDX FROM 1 BY 1 UNTIL IDX = 5
           COMPUTE RVALUE = FUNCTION RANDOM * 1000000000
           DISPLAY RVALUE
         END-PERFORM.

         DISPLAY "Generando random con seed...".
         ACCEPT WS-TIME FROM TIME.
         MOVE MILLISECONDS TO SEED(1:2).
         ACCEPT WS-TIME FROM TIME.
         MOVE MILLISECONDS TO SEED(3:2).
         ACCEPT WS-TIME FROM TIME.
         MOVE MILLISECONDS TO SEED(5:2).
         ACCEPT WS-TIME FROM TIME.
         MOVE MILLISECONDS TO SEED(7:2).
         COMPUTE RVALUE = FUNCTION RANDOM(SEED).

         PERFORM VARYING IDX FROM 1 BY 1 UNTIL IDX = 5
           COMPUTE RVALUE = FUNCTION RANDOM * 1000000000
           DISPLAY RVALUE
         END-PERFORM.

       PROGRAM-DONE.
          STOP RUN.



Ejemplo de ejecuciones sucesivas, obsérvese el patrón de repeticiones en amarillo:


?> make && ./principal
Generando random sin seed...
180428938
846930886
168169277
171463691
Generando random con seed...
146627632
119773096
977866279
695807865

?>./principal
Generando random sin seed...
180428938
846930886
168169277
171463691
Generando random con seed...
169097225
730835526
213552898
116350005

?> ./principal

Generando random sin seed...
180428938
846930886
168169277
171463691
Generando random con seed...
395357036
623986740
139161360
104338109


La mejor solución es un auténtico random, que en cobol en mainframe se obtiene accediendo a la placa criptográfica, si la hay.

Como poder usar un mainframe no suele ser sencillo, ¿qué tal un microcontrolador? Es más barato y entra en una mano.

Hay un juego muy simpático, que consiste en ir poniendo manos y pies en unos círculos de colores, terminando los participantes muy enredados. El problema que tiene es que tiene una ruleta que tiene que manipular una persona externa al juego, que suele ser algún progenitor.

Para evitar mi responsabilidad como progenitor, lo que hice fué este simpático muñeco, que mediante leds de colores indica a los jugadores donde poner sus extremidades, al azar, claro, si no no lo mencionaría en este post.


Frente




Espalda





El código está en github, una versión baremetal sin azar y una arduino con azar, ambas se queman con USBasp.


El código es bastante sencillo:
  • En baremetal main() / arduino setup() se configuran los puertos como salidas. 
  • fullBlink() prende y apaga todo.
  • heartbeat() invierte todo.
  • set() dependiendo de la posición a mostrar, lo mapea a uno de los pines.
  • main() hace un poco de circo de comienzo y luego alterna entre elegir y la extremidad/color.



Lo que importa es obtener el seed, para lo cual tuve horribles sufrimientos.

Primero pensé en utilizar un micrófono, pero mi ignorancia analógica me lo impidió. Luego quise leer un fotorreceptor infrarrojo, pero no sé que falló. Por último decidí medir el tiempo hasta que alguien oprimiera un botón y eventuales rebotes, con el ruido del ADC ni hace falta apretar el botón, que está conectado a A0.

Velo en ejecución:





Ah, me olvidaba, para los tokens, estas son algunas medidas útiles:


ComposiciónRango de valoresValores posibles en cada posiciónPosiciones necesarias segun bits
100128256
Hexadecimal0-910303977
Números0-9A-F16253264
LetrasA-Z26212754
Letras y números0-9A-Z36192550
Todas la letras y números0-9A-Za-z52172143

Los números salen de acá, pegar en una hoja de cálculo como fórmula:

CDEF
3
100128256
410=LOG(POTENCIA(2;D$3);$C4)=LOG(POTENCIA(2;E$3);$C4)=LOG(POTENCIA(2;F$3);$C4)
516=LOG(POTENCIA(2;D$3);$C5)=LOG(POTENCIA(2;E$3);$C5)=LOG(POTENCIA(2;F$3);$C5)
626=LOG(POTENCIA(2;D$3);$C6)=LOG(POTENCIA(2;E$3);$C6)=LOG(POTENCIA(2;F$3);$C6)
736=LOG(POTENCIA(2;D$3);$C7)=LOG(POTENCIA(2;E$3);$C7)=LOG(POTENCIA(2;F$3);$C7)
852=LOG(POTENCIA(2;D$3);$C8)=LOG(POTENCIA(2;E$3);$C8)=LOG(POTENCIA(2;F$3);$C8)

Y acá un caso concreto de wordpress del 2015, lo primero que encontré.

Si notás una cierta falta de ritmo en esta entrada, puede ser debido a que la hice en 2017, no pude hacer funcionar el azar bien rápido y algo me distrajo. Luego, con #quedateencasa me puse a hacer cosas y tuve que programar con USBasp y esté era el único ejemplo que tenía, así que lo reviví y acá está.

No hay comentarios:

Publicar un comentario