mirror of
https://git.savannah.gnu.org/git/guile.git
synced 2025-05-20 11:40:18 +02:00
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.
This commit is contained in:
parent
925172cf52
commit
d47db067b6
3 changed files with 117 additions and 19 deletions
|
@ -1865,6 +1865,16 @@ Return a datum representation of @var{state} that may be written out and
|
||||||
read back with the Scheme reader.
|
read back with the Scheme reader.
|
||||||
@end deffn
|
@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*
|
@defvar *random-state*
|
||||||
The global random state used by the above functions when the
|
The global random state used by the above functions when the
|
||||||
@var{state} parameter is not given.
|
@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)
|
(0 1 1 2 2 2 1 2 6 7 10 0 5 3 12 5 5 12)
|
||||||
@end lisp
|
@end lisp
|
||||||
|
|
||||||
To use the time of day as the random seed, you can use code like this:
|
To seed the random state in a sensible way for non-security-critical
|
||||||
|
applications, do this during initialization of your program:
|
||||||
|
|
||||||
@lisp
|
@lisp
|
||||||
(let ((time (gettimeofday)))
|
(set! *random-state* (random-state-from-platform))
|
||||||
(set! *random-state*
|
|
||||||
(seed->random-state (+ (car time)
|
|
||||||
(cdr time)))))
|
|
||||||
@end lisp
|
@end lisp
|
||||||
|
|
||||||
@noindent
|
|
||||||
And then (depending on the time of day, of course):
|
|
||||||
|
|
||||||
@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)
|
|
||||||
@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
|
@node Characters
|
||||||
@subsection Characters
|
@subsection Characters
|
||||||
|
|
|
@ -653,6 +653,107 @@ SCM_DEFINE (scm_random_exp, "random:exp", 0, 1, 0,
|
||||||
}
|
}
|
||||||
#undef FUNC_NAME
|
#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
|
void
|
||||||
scm_init_random ()
|
scm_init_random ()
|
||||||
{
|
{
|
||||||
|
|
|
@ -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_seed_to_random_state (SCM seed);
|
||||||
SCM_API SCM scm_datum_to_random_state (SCM datum);
|
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_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_uniform (SCM state);
|
||||||
SCM_API SCM scm_random_solid_sphere_x (SCM v, 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);
|
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_API SCM scm_random_exp (SCM state);
|
||||||
SCM_INTERNAL void scm_init_random (void);
|
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 */
|
#endif /* SCM_RANDOM_H */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue