bpo-40286: Makes simpler the relation between randbytes() and getrandbits() (GH-19574)

This commit is contained in:
Serhiy Storchaka 2020-04-17 23:51:28 +03:00 committed by GitHub
parent 5b1d9184bb
commit 223221b290
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 26 additions and 16 deletions

View File

@ -758,7 +758,7 @@ class MersenneTwister_TestBasicOps(TestBasicOps, unittest.TestCase):
# Mersenne Twister randbytes() is deterministic
# and does not depend on the endian and bitness.
seed = 8675309
expected = b'f\xf9\xa836\xd0\xa4\xf4\x82\x9f\x8f\x19\xf0eo\x02'
expected = b'3\xa8\xf9f\xf4\xa4\xd06\x19\x8f\x9f\x82\x02oe\xf0'
self.gen.seed(seed)
self.assertEqual(self.gen.randbytes(16), expected)
@ -773,19 +773,35 @@ class MersenneTwister_TestBasicOps(TestBasicOps, unittest.TestCase):
self.assertEqual(b''.join([self.gen.randbytes(4) for _ in range(4)]),
expected)
# Each randbytes(2) or randbytes(3) call consumes 4 bytes of entropy
# Each randbytes(1), randbytes(2) or randbytes(3) call consumes
# 4 bytes of entropy
self.gen.seed(seed)
expected2 = b''.join(expected[i:i + 2]
expected1 = expected[3::4]
self.assertEqual(b''.join(self.gen.randbytes(1) for _ in range(4)),
expected1)
self.gen.seed(seed)
expected2 = b''.join(expected[i + 2: i + 4]
for i in range(0, len(expected), 4))
self.assertEqual(b''.join(self.gen.randbytes(2) for _ in range(4)),
expected2)
self.gen.seed(seed)
expected3 = b''.join(expected[i:i + 3]
expected3 = b''.join(expected[i + 1: i + 4]
for i in range(0, len(expected), 4))
self.assertEqual(b''.join(self.gen.randbytes(3) for _ in range(4)),
expected3)
def test_randbytes_getrandbits(self):
# There is a simple relation between randbytes() and getrandbits()
seed = 2849427419
gen2 = random.Random()
self.gen.seed(seed)
gen2.seed(seed)
for n in range(9):
self.assertEqual(self.gen.randbytes(n),
gen2.getrandbits(n * 8).to_bytes(n, 'little'))
def gamma(z, sqrt2pi=(2.0*pi)**0.5):
# Reflection to right half of complex plane

View File

@ -534,31 +534,25 @@ _random_Random_randbytes_impl(RandomObject *self, Py_ssize_t n)
return NULL;
}
if (n == 0) {
/* Don't consume any entropy */
return PyBytes_FromStringAndSize(NULL, 0);
}
PyObject *bytes = PyBytes_FromStringAndSize(NULL, n);
if (bytes == NULL) {
return NULL;
}
uint8_t *ptr = (uint8_t *)PyBytes_AS_STRING(bytes);
do {
for (; n; ptr += 4, n -= 4) {
uint32_t word = genrand_uint32(self);
#if PY_LITTLE_ENDIAN
/* Convert to big endian */
#if PY_BIG_ENDIAN
/* Convert to little endian */
word = _Py_bswap32(word);
#endif
if (n < 4) {
memcpy(ptr, &word, n);
/* Drop least significant bits */
memcpy(ptr, (uint8_t *)&word + (4 - n), n);
break;
}
memcpy(ptr, &word, 4);
ptr += 4;
n -= 4;
} while (n);
}
return bytes;
}