rand.Bytes

2,066 views
Skip to first unread message

Michael Hoisie

unread,
Dec 20, 2009, 2:16:17 AM12/20/09
to golang-nuts
Does go have a way to generate a random byte array? I can't seem to
find it in the rand package. This would be useful for generating
unique identifiers (UUID), or for things like encryption keys or
password salts.

There's some test code in go that uses /dev/urandom, but this doesn't
seem like the best cross-platform approach.

Bob Cunningham

unread,
Dec 20, 2009, 7:39:14 AM12/20/09
to Michael Hoisie, golang-nuts

From my "99 Bottles" code: http://gopaste.org/view/MunWk

// Create a randomized list over the passed range (end-inclusive)
func randList(min, max int) []int {
if max < min {
min, max = max, min
}
length := max - min + 1
rand.Seed(time.Nanoseconds())
list := rand.Perm(length)
for index, _ := range list {
list[index] += min
}
return list
}


While the above is not what you asked for, it should be simple to modify to get other kinds of random content into other numeric types.


-BobC

SnakE

unread,
Dec 20, 2009, 8:07:32 PM12/20/09
to Bob Cunningham, Michael Hoisie, golang-nuts
2009/12/20 Bob Cunningham <Fly...@gmail.com>

On 12/19/2009 11:16 PM, Michael Hoisie wrote:
Does go have a way to generate a random byte array? I can't seem to
find it in the rand package. This would be useful for generating
unique identifiers (UUID), or for things like encryption keys or
password salts.

There's some test code in go that uses /dev/urandom, but this doesn't
seem like the best cross-platform approach.

From my "99 Bottles" code: http://gopaste.org/view/MunWk

// Create a randomized list over the passed range (end-inclusive)
func randList(min, max int) []int {
 if max < min {
   min, max = max, min
 }
 length := max - min + 1
 rand.Seed(time.Nanoseconds())

I think seeding rand every time is a really bad idea.
 

Bob Cunningham

unread,
Dec 21, 2009, 2:32:11 AM12/21/09
to SnakE, Michael Hoisie, golang-nuts
On 12/20/2009 05:07 PM, SnakE wrote:
> 2009/12/20 Bob Cunningham <Fly...@gmail.com <mailto:Fly...@gmail.com>>

>
> On 12/19/2009 11:16 PM, Michael Hoisie wrote:
>
> Does go have a way to generate a random byte array? I can't seem to
> find it in the rand package. This would be useful for generating
> unique identifiers (UUID), or for things like encryption keys or
> password salts.
>
> There's some test code in go that uses /dev/urandom, but this
> doesn't
> seem like the best cross-platform approach.
>
> >From my "99 Bottles" code: http://gopaste.org/view/MunWk
> rand.Seed(time.Nanoseconds())
>
> I think seeding rand every time is a really bad idea.

It is *absolutely* required in order to obtain different behavior on each run. Otherwise, using the same default seed over and over, you get exactly the same random number sequence every time.

I needed this so that I could obtain useful statistics for program performance over many runs. If the runs are all the same (which is what happens if the random number generator is not seeded for each run), then by definition you have no statistics!

After only 12 runs, the expected performance, that randomization produces a number of misses that was expected to be half the worst-case, was proven with 90% confidence.

Failing to properly seed the random number generator before each run is a typical freshman CS error when first working with stochastic processes. If you were to generate crypto keys or salts without reseeding the generator, your crypto system would be fatally weakened.

Here's a simple program that clearly illustrates the issue (rand_test.go):

package main

import (
"fmt"
"rand"
"time"
)

func main() {
fmt.Printf("First value from random number generator = %v\n", rand.Int63())
rand.Seed(time.Nanoseconds())
fmt.Printf("First value from re-seeded generator = %v\n", rand.Int63())
}

Compile this program, then run it several times. On my system, I got the following:

[bobc@snafu Go]$ rand_test.out
First value from random number generator = 5577006791947779410
First value from re-seeded generator = 2561830037514801697

[bobc@snafu Go]$ rand_test.out
First value from random number generator = 5577006791947779410
First value from re-seeded generator = 497439534987103448

[bobc@snafu Go]$ rand_test.out
First value from random number generator = 5577006791947779410
First value from re-seeded generator = 1179316969969921209

[bobc@snafu Go]$ rand_test.out
First value from random number generator = 5577006791947779410
First value from re-seeded generator = 2151590588581465432

Notice that the first number is ALWAYS THE SAME on every run, and the next value, after re-seeding with a different seed every time, is ALWAYS DIFFERENT! Only with proper re-seeding can you get something that appears random.

Which would you want to use for generating crypto keys?

I believe it is a serious mistake for any rand package to have the same default behavior each time it is used, as is the case with the "convenience" generator in Go's rand package. If you *do* want the same sequence of numbers over and over, then it is far better to force the programmer to have to manually initialize the generator using a constant seed.

In the file rand.go (in package rand), Go's "convenience" random number generator is hard-wired with an initialization seed value of 1 (one).

My selection of time.Nanoseconds() as my source of a random-ish seed isn't theoretically ideal, but it is the best source of changing numbers that is trivially easy to access. It is effectively impossible that I will *ever* get the same seed value twice, which is all I really care about for the random number sequences I need.

-BobC

Peter Froehlich

unread,
Dec 21, 2009, 3:53:46 AM12/21/09
to Bob Cunningham, SnakE, Michael Hoisie, golang-nuts
I think he meant that you repeatedly re-seed the RNG. Seeding it once
when your program comes up, sure. Seeding it again and again, maybe
not so good. Depending on where your code came from, it may or may not
be proper to re-seed. But if you're planning to put this in a/the
library, leave global state alone. (Sometimes you want the same
sequence over and over, and if a library routine decides to seed a
global resource when I don't want it to, well...)

--
Peter H. Froehlich <http://www.cs.jhu.edu/~phf/>
Senior Lecturer | Director, Johns Hopkins Gaming Lab

Adam F. Rogoyski

unread,
Dec 21, 2009, 3:11:42 AM12/21/09
to golang-nuts
On Dec 20, 11:32 pm, Bob Cunningham <FlyM...@gmail.com> wrote:
> On 12/20/2009 05:07 PM, SnakE wrote:
>
>
>
>
>
> > 2009/12/20 Bob Cunningham <FlyM...@gmail.com <mailto:FlyM...@gmail.com>>

>
> >     On 12/19/2009 11:16 PM, Michael Hoisie wrote:
>
> >         Does go have a way to generate a random byte array? I can't seem to
> >         find it in the rand package. This would be useful for generating
> >         unique identifiers (UUID), or for things like encryption keys or
> >         password salts.
>
> >         There's some test code in go that uses /dev/urandom, but this
> >         doesn't
> >         seem like the best cross-platform approach.
>
> >      >From my "99 Bottles" code:http://gopaste.org/view/MunWk
> >       rand.Seed(time.Nanoseconds())
>
> > I think seeding rand every time is a really bad idea.
>
> It is *absolutely* required in order to obtain different behavior on each run.  Otherwise, using the same default seed over and over, you get exactly the same random number sequence every time.

He was referring to reseeding with every call to the function instead
of doing it once at startup, or using once.Do(...).

Bob Cunningham

unread,
Dec 21, 2009, 5:56:05 AM12/21/09
to Adam F. Rogoyski, golang-nuts

True!

Since the list function gets called only once, I got lazy and chose to keep all the rand code together (it was added late in development). The code has been updated: http://gopaste.org/view/9X6w7

As a specific risk, it would become detectable only if the function were called many times with a periodicity that is stable to within nanoseconds (a "timing attack").

The defense for a timing attack is classic: Destroy the ability to repeat execution in the time domain by adding a small random delay before the random number generator returns. With such a defensive generator, even my lazy code should be safe.


-BobC

Qtvali

unread,
Dec 21, 2009, 6:22:09 AM12/21/09
to golang-nuts
I did once read a text about how to use noice from microphone input to
generate true random numbers (this works without microphone
connected). I'm not sure if this noise can't have some patterns or
that no OS is going to reduce the noise, but I think that when using
different seeds, one has only integer range of tests to be done to
find your crypto key - that's not so easy to hack into network system
that way, but hacking some e-mail signature etc. would be considered.
You should be aware that if you are going to use 512-bit keys, those
keys could be easily in range of 64-bit of possibilities; also that if
you are using current timestamp as seed, then even with nanoseconds
you restrict it more - knowing that the key was generated on some
given hour, one will get something like 60*60*1,000,000,000
possibilities. With nowadays computer with 50,000,000,000 instructions
per second, it could make your keys crackable in case your program
will execute, generate the key and return. Having a program generate
some unneeded random number each second, running on background and
generating keys when requested will give better results. Just to let
you know that your ranges might lie ;)

Qtvali

unread,
Dec 21, 2009, 6:28:33 AM12/21/09
to golang-nuts

Qtvali

unread,
Dec 21, 2009, 7:06:32 AM12/21/09
to golang-nuts, Peter Froehlich
On Dec 21, 10:53 am, Peter Froehlich <peter.hans.froehl...@gmail.com>
wrote:

> I think he meant that you repeatedly re-seed the RNG. Seeding it once
> when your program comes up, sure. Seeding it again and again, maybe
> not so good. Depending on where your code came from, it may or may not
> be proper to re-seed. But if you're planning to put this in a/the
> library, leave global state alone. (Sometimes you want the same
> sequence over and over, and if a library routine decides to seed a
> global resource when I don't want it to, well...)

You should not rely on that. It's enough for a library to _use_ that
global random generator to disallow you from getting your sequence. To
get the same sequence, you need some local generator anyway.

Rob 'Commander' Pike

unread,
Dec 21, 2009, 7:08:09 AM12/21/09
to Bob Cunningham, Adam F. Rogoyski, golang-nuts

On 21/12/2009, at 9:56 PM, Bob Cunningham wrote:

> As a specific risk, it would become detectable only if the function
> were called many times with a periodicity that is stable to within
> nanoseconds (a "timing attack").

It's worse than that. Seeding a random number generator is very hard
because it's difficult to generate enough noise to fill the generator
given only a few input bits. In my experience, a random number
generator is much worse right after seeding that after it's run a few
million cycles. Also, if you seed it with something predictable like
the time, the risk is obvious. Seeding it on every call is folly.

-rob

baldmountain

unread,
Dec 21, 2009, 8:19:56 AM12/21/09
to golang-nuts

On Dec 21, 7:08 am, "Rob 'Commander' Pike" <r...@google.com> wrote:
> It's worse than that.  Seeding a random number generator is very hard  
> because it's difficult to generate enough noise to fill the generator  
> given only a few input bits.  In my experience, a random number  
> generator is much worse right after seeding that after it's run a few  
> million cycles.

Is this true? I thought an RNG was just a function that generated a,
hopefully, very long sequence of numbers that appeared random. It
didn't matter where in the sequence you started since the sequence was
cyclic. By setting a seed you were specifying where in the sequence
you were starting. This way you could generate the same sequence over
and over. Since it is just a sequence the RNG should behave the same
irregardless of whether you are taking a sample near where it was
seeded or a few million cycles later.

Or am I just behind the times and newer RNGs behave differently?

>  Also, if you seed it with something predictable like  
> the time, the risk is obvious.  Seeding it on every call is folly.

This I agree with.


geoff

Qtvali

unread,
Dec 21, 2009, 8:44:37 AM12/21/09
to golang-nuts
http://www.gnu.org/software/gsl/manual/html_node/Unix-random-number-generators.html
gsl_rng_rand has seed as initial value
gsl_rng_rand48 has seed as upper 32 bits of initial value

http://www.gnu.org/software/gsl/manual/html_node/Other-random-number-generators.html
gsl_rng_ranf - seed is lower 32 bits

Anyway, even if you can use different seed to get the same sequence
that you would have got by just cycling a bit, it's clear that after
those cycles, this new seed is less predictable from initial
conditions - for example, if input is time, then having the seed
directly refer to some moment in time is worse than if it's already
randomized. It means - the operation from seed to 3rd number is not
seed+3, but something you can't predict by such easy arithmetics.
Thus, doing those cycles is worth doing because it gives you more
random seed than the inital value you got from time or somewhere. As
we are talking about pseudorandom numbers, anything you can do to make
it harder to get back to seed is good. Having it tick after each
seconds after getting a time-based seed gives you a rough equivalent
of non-time-based-seed, which is much larger range. Also, if seed only
seeds some 64 bits of value, but the value itself is larger, it's also
a way to get to those other values. So it clearly makes sense to not
rely on this first number, unless it's a true random in itself.

Qtvali

unread,
Dec 21, 2009, 9:18:17 AM12/21/09
to golang-nuts
On Dec 21, 3:19 pm, baldmountain <baldmount...@gmail.com> wrote:
> On Dec 21, 7:08 am, "Rob 'Commander' Pike" <r...@google.com> wrote:
>
> > It's worse than that.  Seeding a random number generator is very hard  
> > because it's difficult to generate enough noise to fill the generator  
> > given only a few input bits.  In my experience, a random number  
> > generator is much worse right after seeding that after it's run a few  
> > million cycles.
>
> Is this true? I thought an RNG was just a function that generated a,
> hopefully, very long sequence of numbers that appeared random. It
> didn't matter where in the sequence you started since the sequence was
> cyclic. By setting a seed you were specifying where in the sequence
> you were starting. This way you could generate the same sequence over
> and over. Since it is just a sequence the RNG should behave the same
> irregardless of whether you are taking a sample near where it was
> seeded or a few million cycles later.
>
> Or am I just behind the times and newer RNGs behave differently?

Just to make the point more clear.
Lets suppose you have the following seed:
const xor time();

Const is some constant known to you, time() is current time. Lets
suppose cracker already knows the const, thus lets see time():
Current time is made from two parts - constant and variable. Variable
is last x bits, constant is the beginning (for example, right now the
first bits, which are the same for 1970.1.1 and today are constant;
the rest is more or less variable).

Thus, you have some not very long variable as seed value.

Lets take an example of 256 possibilities. You have, at first, 4 bits
more or less random as initial condition - this is the time,
simplified - and then you have those other 4 bits. You have an
operation: seed+=16; which moves you to the next seed. You actually
don't have other operation to get access to those other numbers - time
is very limited and as limited are most other things, which give you
some initial seed. They all represent the first 4 bits here. Now, each
time you calculate a random number, you do seed+=16.

When hacker knows the range of initial condition - these 4 bits, he
has to try 16 possibilities. If you have randomly done 0, 1 or 2 times
of getting next number, the range becomes 16*3. If you have randomly
done 5, 7 or 8 times of it, the range becomes yet larger unless
cracker knows that those jumps are 5, 7 or 8. If you simply add 0, 1
or 2 to your seed after getting your first 4 bits, you will make a
range only 18. Given that access to original number needs iterating
over the range and doing some more or less complex operation for each
iteration, your task is to get this range as big as possible. Also,
given that adding 16 is a bit harder to do than adding 1 (you have to
generate a new number in the real operation presented here as +16) it
makes sense over just adding to the seed even if seed is only single
number representing the whole state of a generator at any time.

As 64-bit seed itself has a range of 2**64, doing few million
iterations is meaningful - if x is initial range, then each iteration
adds new x to range. And in this case, even if you do some adding or
multiplying with initial seed, those iterations are still
qualitatively different from those operations.

Qtvali

unread,
Dec 21, 2009, 9:33:21 AM12/21/09
to golang-nuts
Just as an additional detail, consider the following for knowing that
there is time() seed and no additional iterations:

for i := time(); i > 0; i-- {
seed(i);
if correctSolution() { // Uses current seed
break;
}
}

Given that one has possibly done seed += timerange*int, you need more
iterating.

Now, given that up to 1000000 additional iterations are done, consider
the following:

for i := time(); i > 0; i-- {
seed(i);
for j := 0; j < 1000000; j++ {
pushseed();
if correctSolution() {
break;
}
popseed();
random(); // Calculate next number
}
}

You can restructure the latter, but it's clear that it's more complex
in all cases.

Given that one has possibly done seed += timerange*int with the latter
case, you need once again more iterating, but you still need this
iterator, which calculates the next number.

Thus, doing whatever you can is the best ;)

Russ Cox

unread,
Dec 21, 2009, 11:27:53 AM12/21/09
to Michael Hoisie, golang-nuts
On Sat, Dec 19, 2009 at 23:16, Michael Hoisie <hoi...@gmail.com> wrote:
> Does go have a way to generate a random byte array? I can't seem to
> find it in the rand package.

No, not yet.

Russ

Reply all
Reply to author
Forward
0 new messages