diff --git a/doc/bash.1 b/doc/bash.1
index 9a7a384a..4c4d3882 100644
--- a/doc/bash.1
+++ b/doc/bash.1
@@ -1825,11 +1825,16 @@ generated. The sequence of random numbers may be initialized by assigning
a value to
.SM
.BR RANDOM .
+The value can be a string.
If
.SM
.B RANDOM
is unset, it loses its special properties, even if it is
subsequently reset.
+.SM
+.B RANDOM
+uses Salsa20 for generating the integers, which as of 2015 has
+no published cryptanalysis attacks.
.TP
.B READLINE_LINE
The contents of the
diff --git a/variables.c b/variables.c
index be2446e0..a4a7af52 100644
--- a/variables.c
+++ b/variables.c
@@ -18,6 +18,7 @@
along with Bash. If not, see .
*/
+#include
#include "config.h"
#include "bashtypes.h"
@@ -1295,53 +1296,125 @@ init_seconds_var ()
return v;
}
-/* The random number seed. You can change this by setting RANDOM. */
-static unsigned long rseed = 1;
-static int last_random_value;
static int seeded_subshell = 0;
-/* A linear congruential random number generator based on the example
- one in the ANSI C standard. This one isn't very good, but a more
- complicated one is overkill. */
+/* Salsa20 Cryptographically secure pseudorandom number generator from
+ https://en.wikipedia.org/wiki/Salsa20 */
+
+#define ROTL(a,b) (((a) << (b)) | ((a) >> (32 - (b))))
+#define QR(a, b, c, d)( \
+ b ^= ROTL(a + d, 7), \
+ c ^= ROTL(b + a, 9), \
+ d ^= ROTL(c + b,13), \
+ a ^= ROTL(d + c,18))
+#define ROUNDS 20
+
+uint32_t salsastate[16] = {
+ /* Start value: "expand 32-byte k" */
+ 'e', 'x', 'p', 'a', 'n', 'd', ' ', '3', '2', '-', 'b', 'y', 't', 'e', ' ', 'k'
+};
+
+void
+salsa20_block ()
+{
+ int i;
+ uint32_t x[16];
+
+ for (i = 0; i < 16; ++i)
+ x[i] = salsastate[i];
+ // 10 loops × 2 rounds/loop = 20 rounds
+ for (i = 0; i < ROUNDS; i += 2) {
+ // Odd round
+ QR(x[ 0], x[ 4], x[ 8], x[12]); // column 1
+ QR(x[ 5], x[ 9], x[13], x[ 1]); // column 2
+ QR(x[10], x[14], x[ 2], x[ 6]); // column 3
+ QR(x[15], x[ 3], x[ 7], x[11]); // column 4
+ // Even round
+ QR(x[ 0], x[ 1], x[ 2], x[ 3]); // row 1
+ QR(x[ 5], x[ 6], x[ 7], x[ 4]); // row 2
+ QR(x[10], x[11], x[ 8], x[ 9]); // row 3
+ QR(x[15], x[12], x[13], x[14]); // row 4
+ }
+ for (i = 0; i < 16; ++i)
+ salsastate[i] += x[i];
+}
+
+int state_idx = 0;
-/* Returns a pseudo-random number between 0 and 32767. */
static int
brand ()
{
- /* From "Random number generators: good ones are hard to find",
- Park and Miller, Communications of the ACM, vol. 31, no. 10,
- October 1988, p. 1195. filtered through FreeBSD */
- long h, l;
-
- /* Can't seed with 0. */
- if (rseed == 0)
- rseed = 123459876;
- h = rseed / 127773;
- l = rseed % 127773;
- rseed = 16807 * l - 2836 * h;
-#if 0
- if (rseed < 0)
- rseed += 0x7fffffff;
-#endif
- return ((unsigned int)(rseed & 32767)); /* was % 32768 */
+ /* salsa20_block generates 16 values */
+ /* use them all before calling salsa20_block again */
+ if(0 == state_idx) {
+ salsa20_block ();
+ }
+ state_idx++;
+ /* Keep 0 <= state_idx < 16 */
+ state_idx &= 0xF;
+ return ((unsigned int)(salsastate[state_idx] & 32767));
}
-/* Set the random number generator seed to SEED. */
+/* set salsastate to 0 */
+static void
+reset_salsastate ()
+{
+ int i;
+ for (i = 0; i < 16; ++i)
+ salsastate[i] = 0;
+}
+
+/* backwards compatible 32-bit seeder */
static void
sbrand (seed)
unsigned long seed;
{
- rseed = seed;
- last_random_value = 0;
+ int i;
+ reset_salsastate ();
+ salsastate[0] = seed & 0xFFFFFFFF;
+ state_idx = 0;
+}
+
+/* add seed with any binary input e.g. a string */
+/* similar to adding entropy to /dev/random */
+static void
+stringaddseedrand (seed,len)
+ uint8_t *seed;
+ int len;
+{
+ int i;
+ for (i = 0; i < len; ++i) {
+ salsastate[i%16] ^= seed[i];
+ if (i%16 == 15)
+ /* shake the bits before adding more */
+ /* otherwise 16 bytes repeated twice will cancel out */
+ salsa20_block ();
+ }
+ salsa20_block ();
+}
+
+/* seed with binary input of any size e.g. a string */
+static void
+stringseedrand (seed,len)
+ uint8_t *seed;
+ int len;
+{
+ reset_salsastate ();
+ stringaddseedrand (seed,len);
+ state_idx = 0;
}
static void
seedrand ()
{
+ /* add some semi-random seed */
+ /* look at https://github.com/jirka-h/haveged for better input */
struct timeval tv;
-
+ uint32_t pid;
gettimeofday (&tv, NULL);
- sbrand (tv.tv_sec ^ tv.tv_usec ^ getpid ());
+ stringaddseedrand (&tv,sizeof(tv));
+ pid = getpid ();
+ stringaddseedrand (&pid,sizeof(pid));
}
static SHELL_VAR *
@@ -1351,7 +1424,7 @@ assign_random (self, value, unused, key)
arrayind_t unused;
char *key;
{
- sbrand (strtoul (value, (char **)NULL, 10));
+ stringseedrand (value,strlen(value));
if (subshell_environment)
seeded_subshell = getpid ();
return (self);
@@ -1370,9 +1443,7 @@ get_random_number ()
seeded_subshell = pid;
}
- do
- rv = brand ();
- while (rv == last_random_value);
+ rv = brand ();
return rv;
}
@@ -1384,7 +1455,6 @@ get_random (var)
char *p;
rv = get_random_number ();
- last_random_value = rv;
p = itos (rv);
FREE (value_cell (var));