summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark H Weaver <mhw@netris.org>2012-01-18 02:36:17 -0500
committerMark H Weaver <mhw@netris.org>2012-01-21 03:22:57 -0500
commitd47db067b63089880e2e202d5a33fd7fe5e41dbe (patch)
tree4942484bcfdc3a877cca2ea9e294d399ed6fc2fc
parent925172cf529a89f0c5fd81720718971f9059fcfd (diff)
downloadguile-d47db067b63089880e2e202d5a33fd7fe5e41dbe.tar.gz
Add `random-state-from-platform' and `scm_i_random_bytes_from_platform'
* libguile/random.c (scm_random_state_from_platform): New procedure. (scm_i_random_bytes_from_platform): New internal function. * libguile/random.h (scm_random_state_from_platform, scm_i_random_bytes_from_platform): Add prototypes. * doc/ref/api-data.texi (Random): Add documentation.
-rw-r--r--doc/ref/api-data.texi32
-rw-r--r--libguile/random.c101
-rw-r--r--libguile/random.h3
3 files changed, 117 insertions, 19 deletions
diff --git a/doc/ref/api-data.texi b/doc/ref/api-data.texi
index 60a214872..bcfbae365 100644
--- a/doc/ref/api-data.texi
+++ b/doc/ref/api-data.texi
@@ -1865,6 +1865,16 @@ Return a datum representation of @var{state} that may be written out and
read back with the Scheme reader.
@end deffn
+@deffn {Scheme Procedure} random-state-from-platform
+@deffnx {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 @file{/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.
+@end deffn
+
@defvar *random-state*
The global random state used by the above functions when the
@var{state} parameter is not given.
@@ -1887,29 +1897,13 @@ Guile started up, will always give:
(0 1 1 2 2 2 1 2 6 7 10 0 5 3 12 5 5 12)
@end lisp
-To use the time of day as the random seed, you can use code like this:
-
-@lisp
-(let ((time (gettimeofday)))
- (set! *random-state*
- (seed->random-state (+ (car time)
- (cdr time)))))
-@end lisp
-
-@noindent
-And then (depending on the time of day, of course):
+To seed the random state in a sensible way for non-security-critical
+applications, do this during initialization of your program:
@lisp
-(map random (cdr (iota 19)))
-@result{}
-(0 0 1 0 2 4 5 4 5 5 9 3 10 1 8 3 14 17)
+(set! *random-state* (random-state-from-platform))
@end lisp
-For security applications, such as password generation, you should use
-more bits of seed. Otherwise an open source password generator could
-be attacked by guessing the seed@dots{} but that's a subject for
-another manual.
-
@node Characters
@subsection Characters
diff --git a/libguile/random.c b/libguile/random.c
index 8bc0d8764..2db19f729 100644
--- a/libguile/random.c
+++ b/libguile/random.c
@@ -653,6 +653,107 @@ SCM_DEFINE (scm_random_exp, "random:exp", 0, 1, 0,
}
#undef FUNC_NAME
+/* Return a new random-state seeded from 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. This is only
+ to be used as a last resort, when no better source of entropy is
+ available. */
+static SCM
+random_state_of_last_resort (void)
+{
+ SCM state;
+ SCM time_of_day = scm_gettimeofday ();
+ SCM sources = scm_list_n
+ (scm_from_unsigned_integer (SCM_UNPACK (time_of_day)), /* heap addr */
+ scm_getpid (), /* process ID */
+ scm_get_internal_real_time (), /* high-resolution process timer */
+ scm_from_unsigned_integer ((scm_t_bits) &time_of_day), /* stack addr */
+ scm_car (time_of_day), /* seconds since midnight 1970-01-01 UTC */
+ scm_cdr (time_of_day), /* microsecond component of the above clock */
+ SCM_UNDEFINED);
+
+ /* Concatenate the sources bitwise to form the seed */
+ SCM seed = SCM_INUM0;
+ while (scm_is_pair (sources))
+ {
+ seed = scm_logxor (seed, scm_ash (scm_car (sources),
+ scm_integer_length (seed)));
+ sources = scm_cdr (sources);
+ }
+
+ /* FIXME The following code belongs in `scm_seed_to_random_state',
+ and here we should simply do:
+
+ return scm_seed_to_random_state (seed);
+
+ Unfortunately, `scm_seed_to_random_state' only preserves around 32
+ bits of entropy from the provided seed. I don't know if it's okay
+ to fix that in 2.0, so for now we have this workaround. */
+ {
+ int i, len;
+ unsigned char *buf;
+ len = scm_to_int (scm_ceiling_quotient (scm_integer_length (seed),
+ SCM_I_MAKINUM (8)));
+ buf = (unsigned char *) malloc (len);
+ for (i = len-1; i >= 0; --i)
+ {
+ buf[i] = scm_to_int (scm_logand (seed, SCM_I_MAKINUM (255)));
+ seed = scm_ash (seed, SCM_I_MAKINUM (-8));
+ }
+ state = make_rstate (scm_c_make_rstate ((char *) buf, len));
+ free (buf);
+ }
+ return state;
+}
+
+/* Attempt to fill buffer with random bytes from /dev/urandom.
+ Return 1 if successful, else return 0. */
+static int
+read_dev_urandom (unsigned char *buf, size_t len)
+{
+ size_t res = 0;
+ FILE *f = fopen ("/dev/urandom", "r");
+ if (f)
+ {
+ res = fread(buf, 1, len, f);
+ fclose (f);
+ }
+ return (res == len);
+}
+
+/* Fill a buffer with random bytes seeded from a platform-specific
+ source of entropy. /dev/urandom is used if available. Note that
+ this function provides no guarantees about the amount of entropy
+ present in the returned bytes. */
+void
+scm_i_random_bytes_from_platform (unsigned char *buf, size_t len)
+{
+ if (read_dev_urandom (buf, len))
+ return;
+ else /* FIXME: support other platform sources */
+ {
+ /* When all else fails, use this (rather weak) fallback */
+ SCM random_state = random_state_of_last_resort ();
+ int i;
+ for (i = len-1; i >= 0; --i)
+ buf[i] = scm_to_int (scm_random (SCM_I_MAKINUM (256), random_state));
+ }
+}
+
+SCM_DEFINE (scm_random_state_from_platform, "random-state-from-platform", 0, 0, 0,
+ (void),
+ "Construct a new random state seeded from a platform-specific\n\
+source of entropy, appropriate for use in non-security-critical applications.")
+#define FUNC_NAME s_scm_random_state_from_platform
+{
+ unsigned char buf[32];
+ if (read_dev_urandom (buf, sizeof(buf)))
+ return make_rstate (scm_c_make_rstate ((char *) buf, sizeof(buf)));
+ else
+ return random_state_of_last_resort ();
+}
+#undef FUNC_NAME
+
void
scm_init_random ()
{
diff --git a/libguile/random.h b/libguile/random.h
index 2f1f0f69a..109969e01 100644
--- a/libguile/random.h
+++ b/libguile/random.h
@@ -86,6 +86,7 @@ SCM_API SCM scm_copy_random_state (SCM state);
SCM_API SCM scm_seed_to_random_state (SCM seed);
SCM_API SCM scm_datum_to_random_state (SCM datum);
SCM_API SCM scm_random_state_to_datum (SCM state);
+SCM_API SCM scm_random_state_from_platform (void);
SCM_API SCM scm_random_uniform (SCM state);
SCM_API SCM scm_random_solid_sphere_x (SCM v, SCM state);
SCM_API SCM scm_random_hollow_sphere_x (SCM v, SCM state);
@@ -94,6 +95,8 @@ SCM_API SCM scm_random_normal_vector_x (SCM v, SCM state);
SCM_API SCM scm_random_exp (SCM state);
SCM_INTERNAL void scm_init_random (void);
+SCM_INTERNAL void scm_i_random_bytes_from_platform (unsigned char *buf, size_t len);
+
#endif /* SCM_RANDOM_H */
/*