[Python-ideas] Globally configurable random number generation

Nick Coghlan ncoghlan at gmail.com
Tue Sep 15 04:07:38 CEST 2015


On 15 September 2015 at 09:10, Andrew Barnert <abarnert at yahoo.com> wrote:
> On Sep 14, 2015, at 06:32, Nick Coghlan <ncoghlan at gmail.com> wrote:
>>
>> This is an expansion of the random module enhancement idea I
>> previously posted to Donald's thread:
>> https://mail.python.org/pipermail/python-ideas/2015-September/035969.html
>
> Since I suggested the set_default_instance and the singleton instances that can be imported in place of the module, I'm obviously happy with those parts.
>
> However, I think you still haven't solved the problem with my proposal that you set out to solve.
>
> The main difference is that I wanted to deprecate (and eventually make it an error) to use the top-level functions without calling set_default_instance, while you want to allow them and gradually shift the semantics from using the seeded to the seedless PRNG.
>
> As I understand it, the reason for this is that you want to make it possible for someone to write "from random import choice", and not get a warning or error telling them they need to call set_default_instance or import one of the singletons instead.
>
> But then you're encouraging people to write code that's broken in 3.6 and earlier--and that's also potentially broken in 3.7 if used together with any code that calls set_default_instance (because that can't retroactive fix anything from-imported before the call). So, it takes 18 more months to provide any benefit, and it adds an extra cost.

This entire problem is one that I put in the "fix it eventually"
category, rather than "fix it urgently" - folks really are better off
learning to use things like cryptography.io for security sensitive
software, so this change is just about harm mitigation given that it's
inevitable that a non-trivial proportion of the millions of current
and future Python developers won't do that.

Since there's really only one transition I want to enable (seedable ->
seedless as the default RNG), I now think the "switch implicitly as
needed" is a better idea than a permanent support API for switching
the default instance - I'd just add a deprecation warning to that
behaviour, with the intent of removing it some time after 2.7 goes
EOL.

I also realised based on Paul's comments that we really do want
"random.seedable" and "random.seedless" submodules, since that allows
proper interaction with the import system in constructs like "from
random.seedable import randint"

That would make the proposed change for Python 3.6:

* add a random.SeedlessRandom API that omits the seed(), getstate()
and setstate() methods and uses a cryptographically secure PRNG
internally (such as the ChaCha20 algorithm implemented by OpenBSD)
* deprecate the seed(), getstate() and setstate() methods on SystemRandom
* convert random to a pseudo-module with "seedless", "seedable" and
"system" submodules (keeping most code in __init__ for easy pickle
compatibility)
* these would each work like the current top-level random module - a
default instance, with bound methods as module level callables
* random._inst would be an alias for random.seedless._inst by default
* the top level random functions would change to be functions lazily
looking up methods on random._inst, rather than bound methods
* if you call the module level seed(), getstate(), or setstate()
methods, and random._inst is set to random.seedless._inst, it will
issue a deprecation warning recommending the direct use of
"random.seedable" and switch random._inst to refer to
random.seedable._inst instead

Compared to my original proposal, the seedable MT RNG retains the
random.Random name, so any code already using explicit instances is
entirely unaffected by the proposed change. This means the only code
that will receive a deprecation warning is code calling the module
level seed(), getstate() and setstate() functions, and that warning
will just recommend importing "random.seedable" rather than importing
"random".

The API used to replace the default instance at runtime for backwards
compatibility purposes becomes private rather than public, so we only
need to support our specific reasons for doing that, rather than
supporting it as a general feature.

Future security audits would focus on the use of the module "seed()",
"getstate()" and "setstate()" functions (since they'd trigger the
deterministic RNG process wide), and it would also still be encouraged
to use random.SystemRandom() or os.urandom() for security sensitive
use cases (since that's both version independent, and immune to other
modules modifying the active default RNG).

Regards,
Nick.

-- 
Nick Coghlan   |   ncoghlan at gmail.com   |   Brisbane, Australia


More information about the Python-ideas mailing list