guile-user
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Sufficiently safe random information for security-critical Guile applica


From: Christopher Allan Webber
Subject: Sufficiently safe random information for security-critical Guile applications
Date: Fri, 26 Aug 2016 14:59:56 -0500
User-agent: mu4e 0.9.16; emacs 24.5.1

Hello!  So, as some of you know, I'm working on a federation
implementation in Guile.  This needs a few things:

 - Random tokens which won't collide, for various purposes
 - The ability to generate a solid random key, which is used for...
 - The ability to generate an HMAC (for signed cooke based sessions)

To start out with, I've wondered how reliable Guile's RNG is.  From the
Guile docs:

 -- Scheme Procedure: random-state-from-platform
 -- C Function: scm_random_state_from_platform ()
     Construct a new random state seeded from a platform-specific source
     of entropy, appropriate for use in non-security-critical
     applications.  Currently ‘/dev/urandom’ is tried first, or else the
     seed is based on the time, date, process ID, an address from a
     freshly allocated heap cell, an address from the local stack frame,
     and a high-resolution timer if available.

Well okay, if I could be sure that an application is using urandom, then
it seems fine, but that doesn't seem clear.  But here's a question: even
if urandom is used for the initial seed, will that be enough?  Depending
on your RNG, you might be likely to see repeats.  See this horror story:

  https://medium.com/@betable/tifu-by-using-math-random-f1c308c4fd9d#.lybr6y64d

Is Guile's RNG ever good enough for a security sensitive application,
even if a reliably random and secure initial seed is provided?  Or
should we only trust it for things like games?

As for what I'm doing, I've figured, I have libgcrypt, which I'm using
for the HMAC, and so for the random tokens, I could use libgcrypt, which
is probably good-enough.  But getting tokens via gcrypt, while not super
slow, isn't really super fast.

So here's some code I have, GPLv3 or later, yadda yadda:

==================================================
(use-modules (system foreign)
             (rnrs bytevectors)
             (guix gcrypt)
             (guix base64))

(define %gcry-weak-random 0)
(define %gcry-strong-random 1)
(define %gcry-very-strong-random 2)

(define* (gen-random-bv #:optional (bv-length 50)
                        (level %gcry-strong-random))
  (let* ((ptr (libgcrypt-func "gcry_randomize"))
         (proc (pointer->procedure void ptr `(* ,size_t ,int))) ; buffer, 
length, level
         (bv (make-bytevector bv-length))
         (bv-ptr (bytevector->pointer bv)))
    (proc bv-ptr bv-length %gcry-strong-random)
    bv))

(define* (gen-random-token #:optional (bv-length 50)
                           (level %gcry-strong-random))
  (base64-encode (gen-random-bv bv-length level)
                 0 bv-length #f #t base64url-alphabet))
==================================================

So you can use gen-random-token like so:

scheme@(guile-user)> (gen-random-token)
$27 = "XRtMvTmfnQGRCWgA8BqVGFPUB3pOvFn5Us9Qc0JhecL4uxFZkwvb_jeFk8CpPC7TWbw"

Horray, a random token!  It's probably secure, as in, it probably won't
collide with anything, because it's quite large and I suspect that
libgcrypt knows what it's doing about reasonably-secureness in its RNG.
(But what do I know?)

It's not very fast though.  I can generate ~5k tokens per second, which
isn't so bad.

scheme@(guile-user)> ,time (do ((i 1 (1+ i)))
                               ((> i 10000))
                             (gen-random-token))
;; 1.890826s real time, 1.959617s run time.  0.293538s spent in GC.

Much of that is taken up by the base64 encoding... if we tear that out:

scheme@(guile-user)> ,time (do ((i 1 (1+ i)))
                               ((> i 10000))
                             (gen-random-bv))
;; 0.440007s real time, 0.430297s run time.  0.000000s spent in GC.

So that's a bit better.

But it could be a lot faster.  Here's something a lot faster, but it
depends on our RNG being reliable:

  https://github.com/cwebber/pubstrate/blob/master/pubstrate/webapp/auth.scm#L53

(Sorry for the GitHub link, it will be moved shortly.)

That's a lot faster:

scheme@(guile-user)> ,time (do ((i 1 (1+ i)))
                               ((> i 10000))
                             (gen-bearer-token))
;; 0.226487s real time, 0.235521s run time.  0.024290s spent in GC.

... and probably could be even faster if I weren't making a string by
appending a list together.

So I've thought, maybe I could generate an initial seed like so:

==================================================
(define* (big-random-number #:optional (bytes 50))
  "Generate a quite large random number, probably suitable for seeding an RNG"
  ;; @@: Is * a sensible way to combine this?
  (apply * (bytevector->uint-list (gen-random-bv bytes %gcry-very-strong-random)
                                  (native-endianness)
                                  8)))

(set! *random-state* (seed->random-state (big-random-number)))
==================================================

Ie, rely on libgcrypt to provide the initial seed.  I'd assume libgcrypt
would provide reliably pretty good random information, even in the
absence of urandom.

So!  Does someone much better informed than I am have any insights? :)

 - Chris



reply via email to

[Prev in Thread] Current Thread [Next in Thread]