Let’s get the bytes
Last week I read about the project Quantum RNG for OpenQu that is a “quantum
random numbers as a service”. The idea is to use it to get some random
numbers in Racket, using the net/hhp-client and json modules.
This function creates a bytestring of length n using
the service.
#lang
racket
(require
net/http-client
json)
(define
(quantum-random-bytes n)
(define-values (status headers content)
(http-sendrecv "random.openqu.org"
(string-append "/api/randint"
"?size=" (number->string n)
"&min=0"
"&max=255")))
(list->bytes (hash-ref (read-json content) 'result)))
(quantum-random-bytes
5) ; ==> #"\2\353<\223\346"
(quantum-random-bytes
5) ; ==> #"\240\200\242\364\25"
(quantum-random-bytes
5) ; ==> #"\201\370\367\32\25"
Other random-bytes functions
For comparison, we can use the crypto-random-bytes function
and we also can write our own random-bytes function
that uses the build-in pseudorandom number generator.
(require
racket/random) ; for crypto-random-bytes
(define
(random-bytes n)
(list->bytes
(for/list ([i (in-range n)])
(random 256))))
(random-bytes
5) ; ==> #"E\31\366\4\333"
(crypto-random-bytes
5) ; ==> #"b\345\207\315\""
(quantum-random-bytes
5) ; ==> #"\30`\325\3377"
(require
file/sha1) ; for bytes->hex-string
(bytes->hex-string
(quantum-random-bytes 5)) ; ==> "00b7c4d6db"
(bytes->hex-string
(crypto-random-bytes 5)) ; ==> "662b108fd2"
(bytes->hex-string
(random-bytes 5)) ; ==> "da25419554"
As expected, the result of all of them look like similar
random nonsense. But it looks nicer as hexadecimal random nonsense.
The first is deterministic, but it uses the initial time of
the program as a seed, so you will get different results in each run. Its’s
good enough to for a game or simulation, and you can use random-seed to
get the same sequence.
The second use the operative system random number generator
and is cryptographically secure. The operative system mix many sources of
entropy, but all (most?) of them are classic source. (It’s like rolling a dice,
it’s difficult to predict but it’s not truly random.)
The third uses a quantum system to produce the random
numbers. These numbers are truly random (if quantum mechanics is correct). But
remember that you can’t use them for a password and other security applications
because the server owner may have a copy (or be lying and sending the digits ofpi). Or someone could be wiretapping your internet connection (this use
http, not https!).
(Note that this are not qbits. The “q” part is killed in
the generation, long before they are sent through the wire.)
I guess you can use it for a nice truly random quantum
choice of numbers, like the starting position of a solitaire game. It’s
“better” that just shuffling the cards, but probably undistinguishable for the
player. For the secure version, you should buy the hardware version (and verify
that it works as intended).
Testing the numbers
In a previous article, I used Random Sanity that is a
service that does a minimal check of the random numbers. It may have false
positives, and it may not detect fake random numbers, so do it’s only useful to
detect very bad implementations of (pseudo) random numbers generators.
Using the functions defined in that article to contact the service, we get:
(test-random-bytes
(random-bytes 64)) ; ==> #t ;probably
(test-random-bytes
(crypto-random-bytes 64)) ; ==> #t
(test-random-bytes
(quantum-random-bytes 64)) ; ==> #t
(with-random-seed
1234567890
(test-random-bytes (random-bytes 64))) ; ==> #f
The first is almost always true, but it may be false if we
all keep trying and sending the numbers created by the pseudorandom number generator.
Using the birthday paradox, with 38 persons starting the program in the same
second, we have a 50% chance to get a collision. (Or melt the server, whatever
happens first.) The last one is false because it is using a fixed seed, and I already
tested it and the server detects the collision now.
Note that now that the bytes have been tested, they are even
less secure because yet another person has a copy.
Some ideas to try
- There
is an API call to get some bytes in the base64
format. I tried to use it, but I get an error message. It may work later. It
may be useful base64-decode.
- Without
any good reason I’m generating a random byte string instead of a single random
number. With small modifications, it’s simple to make function that is a
replacement of (random n). Note that the max value
of the interval in the service may be included in the results, so remember to
subtract 1 before sending the request. I don’t know how
big can be max.
- There
is another API call to get floating point numbers, which can be used to write a
function that is a replacement of (random). Just
note that the roundtrip on the network may take longer than tolerable for an
interactive program.
Bonus: Another similar service
While looking for more info I found a few similar services,
but most of them require some kind of registration. One that is easy to use is
the ANU Quantum Random Numbers Server. Let’s use it too:
(define
(other-quantum-random-bytes n)
(define-values (status headers content)
(http-sendrecv "qrng.anu.edu.au"
(string-append "/API/jsonI.php"
"?length=" (number->string n)
"&type=uint8")))
(list->bytes (hash-ref (read-json content) 'data)))
(other-quantum-random-bytes
5) ; ==> #"\25\n1\236K"
(other-quantum-random-bytes
5) ; ==> #"\271\375'\272\205"
(other-quantum-random-bytes
5) ; ==> #"\23\362\324\271\\"
(bytes->hex-string
(other-quantum-random-bytes 5)) ; ==> "9366b3f8d2"
(test-random-bytes
(other-quantum-random-bytes 64)) ; ==> #t
The link to the API is difficult to find, but this server only support int8, int16 and bytes strings in
some hexadecimal format. On the other hand, it supports https, but that part is
left as an exercise for the reader.
Busquemos los bytes
La semana pasada leí sobre el proyecto QuantumRNG for OpenQu que se autodefine como “números cuánticos aleatorios como servicio”.
La idea es usarlo para obtener algunos números aleatorios en Racket, usando los
módulos net/http-client y json.
Esta función crea una cadena de bytes de
longitud n usando el servicio.
#lang
racket
(require
net/http-client
json)
(define
(quantum-random-bytes n)
(define-values (status headers content)
(http-sendrecv
"random.openqu.org"
(string-append "/api/randint"
"?size=" (number->string n)
"&min=0"
"&max=255")))
(list->bytes (hash-ref (read-json content) 'result)))
(quantum-random-bytes
5) ; ==> #"\2\353<\223\346"
(quantum-random-bytes
5) ; ==> #"\240\200\242\364\25"
(quantum-random-bytes 5) ; ==>
#"\201\370\367\32\25"
Otras funciones como random-bytes
A modo de comparación, podemos usar la función crypto-random-bytes y también podemos
escribir nuestra propia función random-bytes
que usa el generador de números pseudoaleatorios incorporado.
(require
racket/random) ; for crypto-random-bytes
(define
(random-bytes n)
(list->bytes
(for/list ([i (in-range n)])
(random 256))))
(random-bytes
5) ; ==> #"E\31\366\4\333"
(crypto-random-bytes
5) ; ==> #"b\345\207\315\""
(quantum-random-bytes
5) ; ==> #"\30`\325\3377"
(require
file/sha1) ; for bytes->hex-string
(bytes->hex-string
(quantum-random-bytes 5)) ; ==> "00b7c4d6db"
(bytes->hex-string
(crypto-random-bytes 5)) ; ==> "662b108fd2"
(bytes->hex-string
(random-bytes 5)) ; ==> "da25419554"
Como era de esperar, los resultados de todos ellos parecen cosas
aleatorias similares sin sentido. Pero se ve más lindo verlo como cosas hexadecimales
aleatorias sin sentido.
La primera es determinista, pero utiliza el tiempo de inicio
del programa como semilla, por lo que se obtienen resultados diferentes en cada
ejecución. Es lo suficientemente bueno como para un juego o simulación, y se
puede usar random-seed para obtener la misma
secuencia.
La segunda utiliza el generador de números aleatorios del
sistema operativo y es criptográficamente segura. El sistema operativo combina
muchas fuentes de entropía, pero todas (¿la mayoría?) son fuentes clásicas. (Es
como tirar un dado, es difícil de predecir, pero no es realmente aleatorio.)
El tercero usa un sistema cuántico para producir los números
aleatorios. Estos números son verdaderamente aleatorios (si la mecánica
cuántica es correcta). Pero no es buena idea usarlos para una contraseña y
otras aplicaciones de seguridad porque el dueño del servidor puede tener una
copia (o estar mintiendo y enviando los dígitos de pi). O alguien podrían estar
interceptando tu conexión a Internet (¡esto usa http, no https!).
(Notar que esto no son qbits. La “q” se elimina en la
generación mucho antes de que se envíen a través del cable.)
Supongo que se puede usar para una buena
elección de números aleatorios en forma cuántica, como la posición inicial de
un juego de solitario. Es "mejor" que simplemente mezclar las cartas,
pero probablemente sea indistinguible para el jugador. Para una versión segura,
es necesario comprar la versión en hardware (y verificar que funciona según lo declarado).
Testeando los números
En
un artículo anterior, usé Random Sanity que es un servicio que realiza una
verificación mínima de los números aleatorios. Puede tener falsos positivos y
puede que no detecte números aleatorios falsos, por lo que solo es útil para
detectar implementaciones muy malas de generadores de números (pseudo)
aleatorios.
Usando
las funciones definidas en ese artículo para contactar este servicio, obtenemos:
(test-random-bytes
(random-bytes 64)) ; ==> #t ;probably
(test-random-bytes
(crypto-random-bytes 64)) ; ==> #t
(test-random-bytes
(quantum-random-bytes 64)) ; ==> #t
(with-random-seed
1234567890
(test-random-bytes
(random-bytes 64))) ; ==> #f
El primero en general es verdadero, pero puede
ser falso si todos generamos y enviamos los números creados por el generador de
números pseudoaleatorios. Usando la paradoja del cumpleaños, con 38 personas
que comienzan el programa en el mismo segundo, tenemos un 50% de posibilidades
de tener una colisión. (O que se derrita el servidor, lo que pase primero.) El
último es falso porque está usando una semilla fija, y yo ya lo testeé y el
servidor ahora detecta la colisión.
Notar que ahora que los bytes han sido testeados,
son aún menos seguros porque otra persona más tiene una copia.
Algunas ideas para probar
- Hay
una llamada API para obtener algunos bytes en el formato base64.
Probé usarla, pero recibo un mensaje de error. Es posible que funcione más adelante.
Puede ser útil base64-decode.
- Sin
ninguna buena razón, estoy generando una cadena de bytes aleatoria en lugar de
un único número aleatorio. Con algunas modificaciones es fácil hacer una
función que reemplace a (random n).
Notar que el valor max del
intervalo en el servicio está incluido en los resultados, así que hay que
restar 1 antes de enviar la
solicitud. No se que tan grande puede ser max.
- Hay otra llamada API para obtener números de
coma flotante, que se puede usar para escribir una función que reemplace a (random). Pero es importante recordar
que el viaje de ida y vuelta en la red puede llevar más tiempo del tolerable
para un programa interactivo.
Bonus: Otro servicio similar en español
Mientras buscaba más información encontré
varios servicios similares, pero la mayoría de ellos pedían algún tipo de
registración. Uno que es fácil de usar es el ANU Quantum Random Numbers Server.
Probémoslo también:
(define
(other-quantum-random-bytes n)
(define-values (status headers content)
(http-sendrecv "qrng.anu.edu.au"
(string-append "/API/jsonI.php"
"?length=" (number->string n)
"&type=uint8")))
(list->bytes (hash-ref (read-json content) 'data)))
(other-quantum-random-bytes
5) ; ==> #"\25\n1\236K"
(other-quantum-random-bytes
5) ; ==> #"\271\375'\272\205"
(other-quantum-random-bytes
5) ; ==> #"\23\362\324\271\\"
(bytes->hex-string
(other-quantum-random-bytes 5)) ; ==> "9366b3f8d2"
(test-random-bytes
(other-quantum-random-bytes 64)) ; ==> #t
El link a la API es difícil de encontrar, pero este servidos solo soporta int8, int16 y cadenas
de bits en algún tipo de formato hexadecimal. Por otro lado, soporta https,
pero esta parte queda como ejercicio para el lector.