[Python-ideas] PEP 506 (secrets module) and token functions

Nick Coghlan ncoghlan at gmail.com
Sun Sep 27 15:28:13 CEST 2015

On 26 September 2015 at 23:07, Steven D'Aprano <steve at pearwood.info> wrote:
> I'm looking for guidance and/or consensus on two issues regarding token*
> functions in secrets: output type, and default values.
> The idea is that the module will include a few functions for generating
> tokens, suitable for (say) password recovery, with the
> following signatures:
> def token_bytes(nbytes:int) -> bytes:
>     """Return nbytes random bytes."""
> def token_hex(nbytes:int) -> ???? :
>     """Return nbytes random bytes, encoded to hex"""
> def token_url(nbytes:int) -> ???? :
>     """Return nbytes random bytes, URL-safe base64 encoded."""
> Question one:
> - token_bytes obviously should return bytes. What should the others
>   return, bytes or str?

token_hex and token_url are inspired by Pyramid's and Django's token
generators (albeit with a different implementation technique in the
latter case), so I'd look at what type those return.

The Django token generator is django.utils.crypto.get_random_string,
and returns text.
The Pyramid CSRF token generator in
sessions.BaseCookieSessionFactory.CookieSession.new_csrf_token also
returns text

However, I'm starting to think we should just pick one of the two
algorithms and call it "token_str" (with the shorter output from the
URL-safe base64 with any trailing "=" removed being my preference).
For folks that want or need to use a different token generation
algorithm, we can offer the Pyramid and Django generation algorithms
as recipes in the documentation.

> Question two:
> - Many people will have no idea how many bytes should be used to be
>   confident that it will be hard for an attacker to guess. Earlier, I
>   suggested that the three functions include default values for nbytes,
>   and there were no objections. Do we have consensus on this, and if so,
>   what default value should we use?

32 bytes (256 bits of entropy) seems like a reasonable default to me.

> Question three:
> - If we have default values, do we need some sort of documented
>   exception to the general backwards-compatibility requirement?
> E.g. suppose we release the module in 3.6.0 with defaults of 32 bytes,
> and in 3.6.2 we discover that's too small and we should have used 64
> bytes. Can we change the default in 3.6.3 without notice?

I like Andrew's suggestion of making the default None, and saying that
passing None means we'll choose an appropriate length, which will be
32 bytes for now, but may change in maintenance releases to increase
the length if we decide 256 bits of entropy isn't enough. Changes in
the default length could be indicated through "versionchanged" notes
in the "token_bytes" documentation.


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

More information about the Python-ideas mailing list