python / cpython

The Python programming language
https://www.python.org
Other
62.37k stars 29.96k forks source link

Improve secrets with attribute randrange(n, m) #101064

Closed NotwhatIwanttotell closed 1 year ago

NotwhatIwanttotell commented 1 year ago

In the original python random (but not secure module) there was an attribute randrange(0, n). Suggest the idea is copyied into secrets as one more attribute; secrets.randrange(n, m)

It would make python programming faster and most likely more crypto secure. secrets.randrange(2, 13) should create a random integrer between 2 and 12. secrets.randrange(0, 101) would give a number between 0 and 100. If someone wants a 5000-10000 range it would be (5000, 10001) and so on. Think you got the picture.... ;-)

Thanks for looking on this suggestion.

stevendaprano commented 1 year ago

I understand what secrets.randrange would do, but I don't understand why you want it and when you would need it. When would you want a random integer between 2 and 12 that was cryptographically strong? Tell us your user story for this feature.

NotwhatIwanttotell commented 1 year ago

Its just an example. Nobody would use a random integer between 2 and 12 for cryptographic purposes.

secrets.randbelow(n) could give a very low number or even a zero. That is pointless for any cryptographic use. If randbelow had two variables (min & max) it would be okay, but it doesn't. It would even make more sense to have a secrets.randabove(n) when discussing cyptographic use.

However, not all randomised integers are used for cyptographic purposes. There are a lot of other situations where a random integer is used.

Finally, secrets is supposed to have a better randomness than the original pseudo-random number generator random. Maybe that's what I'm after.

lschoe commented 1 year ago

Well, uniformly random integers from a given range are used all over the place in cryptography, even with small ranges. And it's very important that the distribution is indeed uniform, otherwise all kinds of attacks may apply.

For example, the nonces used for signature generation in schemes like Schnorr's and (EC)DSA should be numbers like secrets.randbelow(q) or secrets.randrange(1, q), where q is a large prime. Even small biases, small deviations from uniform randomness, open the possibility for Bleichenbacher types of attacks on the nonces.

With Shamir secret sharing, but also commonly in post-quantum cryptosystems, we need such random numbers even for small q. For example, it makes perfect sense to use Shamir secret sharing modulo 11, where the dealer will use calls secrets.randbelow(11) to generate uniformly random coefficients for its secret polynomial.

Shamir secret sharing is information-theoretically secure (unbreakable even with a quantum computer), hence it works just as well with small numbers. We routinely use this to build protocols for secure multiparty computation (MPC). But this all requires that the random numbers are indeed uniformly random in the relevant range.

Given secrets.randbelow(n) which handles the problem of ensuring uniform randomness for the range [0,n)---by generating a random integer r from the required number of random bits and then accepting r only if r<n---the need for extra functions like secrets.randrange is maybe not that strong. Although, it would be convenient for things like secrets.randrange(1, 2**k, 2) to easily generate a uniform random odd number modulo $2^k$.

rhettinger commented 1 year ago

However, not all randomised integers are used for cyptographic purposes. There are a lot of other situations where a random integer is used.

The SystemRandom class in the random module provides what you're looking for:

>>> from random import SystemRandom
>>> SystemRandom().randrange(1000, 2000)
1855

The secrets module has only the limited goal of covering common and basic security tasks. It does not (and should not) aspire to more broadly replicate the functionality already provided by the random module.

I recommend closing this feature request as not needed because 1) the random module already provides the desired service and 2) the request is beyond the intended scope of the secrets module.

NotwhatIwanttotell commented 1 year ago

Okay, looks like this is closed. Which is of course wrong.

The scope of the secrets module is to get the highest standard of randomness. It is supposed to have a "better randomness" than the random module.

So lets give you an example where the highest standard of randomness within an interval is necessary: BIP 39 - Wordlist!

Its 2048 words used to create wallets. It starts with one (1) and not zero (0) so secrets.randbelow(2048) will not work as zero is not an option. The only way to make it work is secrets.randrange(1, 2048).

https://github.com/bitcoin/bips/blob/master/bip-0039/english.txt

stevendaprano commented 1 year ago

The only way to make it work is secrets.randrange(1, 2048).

x = 0
while x == 0:
    x = secrets.randbelow(2048)

I think you will need a better example than this to demonstrate a need for this.

If you are serious about pushing this issue, please take further discussion to the Ideas topic on Discuss to get community feedback. If there is support for this feature with good use-cases then we will reconsider the idea, but in the meantime the issue is closed.

pochmann commented 1 year ago

Its 2048 words used to create wallets. It starts with one (1) and not zero (0)

That doesn't even seem true. There are no numbers in that file. Are you talking about the line numbers that GitHub shows?

The only way to make it work is secrets.randrange(1, 2048).

That would exclude 2048, would need stop value 2049.

And another way is 1 + secrets.randbelow(2048). And another way is 1 + secrets.randbits(11) . And another way is using random.SystemRandom, as already pointed out. And unless you start with 1 instead of the usual 0, you don't need the 1 +. Already so many ways to get it done...