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.
|
||||
@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:
|
||||
To seed the random state in a sensible way for non-security-critical
|
||||
applications, do this during initialization of your program:
|
||||
|
||||
@lisp
|
||||
(let ((time (gettimeofday)))
|
||||
(set! *random-state*
|
||||
(seed->random-state (+ (car time)
|
||||
(cdr time)))))
|
||||
(set! *random-state* (random-state-from-platform))
|
||||
@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
|
||||
@subsection Characters
|
||||
|
|
|
@ -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 ()
|
||||
{
|
||||
|
|
|
@ -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 */
|
||||
|
||||
/*
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue