4 de febrero de 2020

Quantumly Generated Random Bytes in Racket

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.