[Security-sig] PEP 522: Allow BlockingIOError in security sensitive APIs on Linux

Tim Peters tim.peters at gmail.com
Wed Jun 22 23:33:58 EDT 2016


[Nick Coghlan]
> PEP: 522
> Title: Allow BlockingIOError in security sensitive APIs on Linux
> ...
> Other than for ``random.SystemRandom`` (which is a relatively thin
> wrapper around ``os.urandom``), the ``random`` module has never made
> any guarantees that the numbers it generates are suitable for use in
> security sensitive operations,

To the contrary, it explicitly says it "should not be used for
security purposes".


> so the use of the system random number generator to seed the default
> Mersenne Twister instance is mainly beneficial as a harm mitigation
> measure for code that is using the ``random`` module inappropriately.

Except that's largely accidental.  It so happens that using urandom()
left Python immune to the "poor seeding" attacks in the PHP paper
widely discussed when `secrets` was gestating, and it's entirely
accidental that Python 3 (but not Python 2) happens to implement
random.choice(), .randrange(), etc in such a way as to leave it
resistant even to the PHP paper's "deduce MT state from partial
outputs" attacks.  Even wholly naive "generate a password" snippets
using small alphabets with random.choice() are highly resistant to
state-deducing attacks in Python 3.

Those continue to be worth something.

But the _real_ reason MT uses urandom() is that MT has massive
internal state, and initialization wants the best chance it can get at
picking any of the 2**19937-1 possible initial states.  For example,
seeding with time.time() and/or pid can't possibly get at more than an
infinitesimal fraction of those.

This has nothing to do with "security" - it has to do with best
practice for simulations.  Seeding the Twister (any PRNG with massive
state) "fairly" is a puzzle, and seeding from urandom() was the best
that could be done.  Quite possible that, e.g., the system CSPRNG has
only 512 bits of state, but that's still far better than brewing
pseudo-nonsense out of a comparative handful of time.time() (etc)
bits.


> Since a single call to ``os.urandom()`` is cheap once the system random
> number generator has been initialized it makes sense to retain that as the
> default behaviour, but there's no need to issue a warning when falling back to
> a potentially more predictable alternative when necessary (in such cases,
> a warning will typically already have been issued as part of interpreter
> startup, as the only way for the call when importing the random module to
> fail without the implicit call during interpreter startup also failing if for
> the latter to have been skipped by entirely disabling the hash randomization
> mechanism).

Since the set of people who start simulations very early in the boot
sequence is empty, I have no objection to any change here - so long as
MT initialization continues using the OS RNG when possible.


More information about the Security-SIG mailing list