From 82d454d80e075b43054112f9857ef3b84e1124fc Mon Sep 17 00:00:00 2001 From: Mathias Laurin Date: Sat, 23 May 2020 19:13:46 +0200 Subject: [PATCH] lint: Do not use protected methods from random Secrets was calling protected methods from `class Random`. Random is already in a protected namespace so that we can change the access specification of its most useful methods to public. --- src/mbedtls/_random.pyx | 36 +++++++++++++++++++----------------- src/mbedtls/secrets.py | 10 ++-------- tests/test_random.py | 9 --------- tests/test_secrets.py | 6 ++++++ 4 files changed, 27 insertions(+), 34 deletions(-) diff --git a/src/mbedtls/_random.pyx b/src/mbedtls/_random.pyx index 78e27a8f..9e64b63d 100644 --- a/src/mbedtls/_random.pyx +++ b/src/mbedtls/_random.pyx @@ -79,7 +79,16 @@ cdef class Random: def _entropy(self): return self._entropy - def _urandom(self, size_t length): + def _reseed(self, const unsigned char[:] data=None): + """Reseed the RNG.""" + if data is None: + check_error(_rnd.mbedtls_ctr_drbg_reseed(&self._ctx, NULL, 0)) + else: + check_error( + _rnd.mbedtls_ctr_drbg_reseed(&self._ctx, &data[0], data.size) + ) + + def urandom(self, size_t length): """Returns `length` random bytes.""" cdef unsigned char *output = malloc( length * sizeof(unsigned char) @@ -96,24 +105,17 @@ cdef class Random: finally: free(output) - def _reseed(self, const unsigned char[:] data=None): - """Reseed the RNG.""" - if data is None: - check_error(_rnd.mbedtls_ctr_drbg_reseed(&self._ctx, NULL, 0)) - else: - check_error( - _rnd.mbedtls_ctr_drbg_reseed(&self._ctx, &data[0], data.size) - ) - - def _randbelow(self, n): + def randbelow(self, upper_bound): """Return a random int in the range [0, n). - Raises ValueError if n==0. + Raises ValueError if n <= 0. """ - kk = n.bit_length() + if upper_bound <= 0: + raise ValueError("Upper bound must be positive.") + kk = upper_bound.bit_length() rr = self.getrandbits(kk) - while rr >= n: + while rr >= upper_bound: rr = self.getrandbits(kk) return rr @@ -121,7 +123,7 @@ cdef class Random: """Return the next random floating point number.""" # Algorithm taken from Python's secrets and random libraries. return float( - _mpi.MPI.from_bytes(self._urandom(7), "big") >> 3 + _mpi.MPI.from_bytes(self.urandom(7), "big") >> 3 ) * RECIP_BPF def getrandbits(self, k): @@ -132,7 +134,7 @@ cdef class Random: if not isinstance(k, _numbers.Integral): raise TypeError("number of bits should be an integer") numbytes = (k + 7) // 8 - value = _mpi.MPI.from_bytes(self._urandom(numbytes), "big") + value = _mpi.MPI.from_bytes(self.urandom(numbytes), "big") # Trim excess bits: extra_bits = value.bit_length() - k return value >> (0 if extra_bits <= 0 else extra_bits) @@ -140,7 +142,7 @@ cdef class Random: def choice(self, seq): """Return a random element from `seq`.""" try: - ii = self._randbelow(len(seq)) + ii = self.randbelow(len(seq)) except ValueError: raise IndexError("Cannot choose from an empty sequence") return seq[ii] diff --git a/src/mbedtls/secrets.py b/src/mbedtls/secrets.py index 6c8f4202..f9d77f8e 100644 --- a/src/mbedtls/secrets.py +++ b/src/mbedtls/secrets.py @@ -33,13 +33,7 @@ randbits = __rng.getrandbits choice = __rng.choice - - -def randbelow(upper_bound): - """Return a random int in the range [0, n).""" - if upper_bound <= 0: - raise ValueError("Upper bound must be positive.") - return __rng._randbelow(upper_bound) +randbelow = __rng.randbelow def token_bytes(nbytes=None): @@ -50,7 +44,7 @@ def token_bytes(nbytes=None): """ if nbytes is None: nbytes = DEFAULT_ENTROPY - return __rng._urandom(nbytes) + return __rng.urandom(nbytes) def token_hex(nbytes=None): diff --git a/tests/test_random.py b/tests/test_random.py index 84a4a2d4..279ca18f 100644 --- a/tests/test_random.py +++ b/tests/test_random.py @@ -57,15 +57,6 @@ def random(self): def test_reseed(self, random): random._reseed() - @pytest.mark.repeat(10) - @pytest.mark.parametrize("max", range(1, 300)) - def test_randbelow(self, random, max): - assert 0 <= random._randbelow(max) < max - - def test_randbelow_zero_raises_valueerror(self, random): - with pytest.raises(ValueError): - random._randbelow(0) - @pytest.mark.repeat(100) def test_random(self, random): value = random.random() diff --git a/tests/test_secrets.py b/tests/test_secrets.py index 1270b0da..abd35282 100644 --- a/tests/test_secrets.py +++ b/tests/test_secrets.py @@ -50,6 +50,12 @@ def test_randbits(): assert 0 <= secrets.randbits(32) < (1 << 32) +@pytest.mark.parametrize("upper_bound", [0, -1]) +def test_randbelow_zero_raises_valueerror(upper_bound): + with pytest.raises(ValueError): + secrets.randbelow(upper_bound) + + @pytest.mark.repeat(100) @pytest.mark.parametrize("upper_bound", [1, 1 << 32, 1 << 128, 1 << 1024]) def test_randbelow(upper_bound):