-1

According to the documentation of Random library, assuming the developer does not provide any seed(None) the function uses the system current time as a seed(time.time()).

Unfortunately, it seems the output doe not reflects the documentation

print(random.randrange(1,10))
a=time.time()
.
.
.
.
random.seed(a)
print(random.randrange(1,10))

The code above produces 2 different outputs so assuming I use windows 10(for those who might think in the direction of urandom supplier) and Python 3 my questions are:

1.Why the above code does not produces the same output

2.How canI make it produces the same output

3.When I tried to find the seed assignment part in Random.py I couldn't find anywhere time.time() assignment to Random.seed so If someone can refers to that part I will be grateful

TheAsker
  • 23
  • 2
  • 4
    Well of course they're different, `time.time()` will not be the same for two consecutive calls... – Chrispresso Oct 21 '20 at 18:56
  • how do you expect to produce the same random values? the time at the first instruction is different at the time at the second instruction – Nikaido Oct 21 '20 at 18:56
  • time has elapsed between your first two lines of code... – acushner Oct 21 '20 at 18:56
  • The time flies by :) The clock at the time of the execution of `random.randrange(1,10)` is not the same as the time at the execution of `time.time()`. – DYZ Oct 21 '20 at 18:56
  • _"How can I make it produces the same output"_: Capture the time _first_, and use _that time_ to seed both times. – Pranav Hosangadi Oct 21 '20 at 18:58
  • I run time.time() before and after print(random.randrange(1,10)) and it produced the same time so I assume saving the time.time() right after the random call is enough.But don't let that bothers you, I also tried it with saving the time before the random call – TheAsker Oct 21 '20 at 18:59
  • The documentation says "None or no argument seeds from current time," not from `time.time()`. `time.time()` does not report the current time. It reports its approximation. – DYZ Oct 21 '20 at 19:01
  • So you imply that they used different function? Because I saw in multiple sources similiar implementation with time.time() – TheAsker Oct 21 '20 at 19:03
  • You cannot assume that `random.random()` seeds from `time.time()` because the documentation does not mention `time.time()`. Apparently, `random.random()` seeds from a high-resolution timer. – DYZ Oct 21 '20 at 19:04

1 Answers1

0

This is actually an interesting question.

First, you can potentially get the same result from consecutive calls to time.time() but mainly due to precision.

In [36]: a=time.time(); b=time.time()

In [37]: b-a
Out[37]: 0.0

Now let's jump into the questions:

  1. It does not produce the same output because of how the initial seed is generated. If you look at the random.py source code for seed() you'll see it specifies
def seed(self, a=None, version=2):
   """Initialize internal state from a seed.
   The only supported seed types are None, int, float,
   str, bytes, and bytearray.
   None or no argument seeds from current time or from an operating
   system specific randomness source if available.

Because there is no reference to time.time() you can't assume it uses it. In fact, you can look at the source for the CPython implementation (if you understand C). One of the ways it helps guarantee a random seed if necessary is this:

static void
random_seed_time_pid(RandomObject *self)
{
    _PyTime_t now;
    uint32_t key[5];

    now = _PyTime_GetSystemClock();
    key[0] = (uint32_t)(now & 0xffffffffU);
    key[1] = (uint32_t)(now >> 32);

    key[2] = (uint32_t)getpid();

    now = _PyTime_GetMonotonicClock();
    key[3] = (uint32_t)(now & 0xffffffffU);
    key[4] = (uint32_t)(now >> 32);

    init_by_array(self, key, Py_ARRAY_LENGTH(key));
}

There are multiple calls to different clocks AND a process ID. Nothing about time.time(). And because of how the seed is generated there, it's pretty impossible for two consecutive seeds to be the same.

  1. If you want something to produce the same output, you need to seed it the same.
In [42]: import random

In [43]: a = time.time()

In [44]: random.seed(a)

In [45]: random.randrange(100)
Out[45]: 98

In [46]: random.randrange(100)
Out[46]: 94

In [47]: random.seed(a)  # Reset

In [48]: random.randrange(100)
Out[48]: 98

It doesn't need to be a number, though. You can seed with a lot of different options.

  1. Hopefully the source code provided above address that.
Chrispresso
  • 3,660
  • 2
  • 19
  • 31
  • First of all thank you for your detailed answer.So If the seed generated by two different clocks which I can hardly predict and PID which I can't predict at all then Random library is perfectly secure.This conclusion contradicts previous research I made where the Random library is not safe for cryptography purposes and can be exploited – TheAsker Oct 21 '20 at 19:51
  • It is not cryptographically secure. It implements Mersenne Twister. See http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/efaq.html#:~:text=Mersenne%20Twister%20is%20not%20cryptographically,based%20on%20a%20linear%20recursion.&text=To%20make%20it%20secure%2C%20you,8%20of%20the%20original%20one). – Chrispresso Oct 21 '20 at 19:56
  • I should clarify: The seeding has nothing to do with it being cryptographically secure. The MT algorithm itself is not secure since the output can be predictable. – Chrispresso Oct 21 '20 at 20:19