Use Python's secrets module instead of random (#9984)

Functionally identical, but more obviously cryptographically secure.
...Explicit is better than implicit?

Avoids needing to know that SystemRandom() implies a CSPRNG, and
complies with the big scary red box on the documentation for random:

> Warning:
>   The pseudo-random generators of this module should not be used for
>   security purposes. For security or cryptographic uses, see the
>   secrets module.

https://docs.python.org/3/library/random.html

Signed-off-by: Dan Callahan <danc@element.io>
This commit is contained in:
Dan Callahan 2021-05-14 10:58:46 +01:00 committed by GitHub
parent c14f99be46
commit 498084228b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 12 additions and 8 deletions

1
changelog.d/9984.misc Normal file
View File

@ -0,0 +1 @@
Simplify a few helper functions.

View File

@ -13,8 +13,8 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
import itertools import itertools
import random
import re import re
import secrets
import string import string
from collections.abc import Iterable from collections.abc import Iterable
from typing import Optional, Tuple from typing import Optional, Tuple
@ -35,18 +35,21 @@ CLIENT_SECRET_REGEX = re.compile(r"^[0-9a-zA-Z\.=_\-]+$")
# #
MXC_REGEX = re.compile("^mxc://([^/]+)/([^/#?]+)$") MXC_REGEX = re.compile("^mxc://([^/]+)/([^/#?]+)$")
# random_string and random_string_with_symbols are used for a range of things,
# some cryptographically important, some less so. We use SystemRandom to make sure
# we get cryptographically-secure randoms.
rand = random.SystemRandom()
def random_string(length: int) -> str: def random_string(length: int) -> str:
return "".join(rand.choice(string.ascii_letters) for _ in range(length)) """Generate a cryptographically secure string of random letters.
Drawn from the characters: `a-z` and `A-Z`
"""
return "".join(secrets.choice(string.ascii_letters) for _ in range(length))
def random_string_with_symbols(length: int) -> str: def random_string_with_symbols(length: int) -> str:
return "".join(rand.choice(_string_with_symbols) for _ in range(length)) """Generate a cryptographically secure string of random letters/numbers/symbols.
Drawn from the characters: `a-z`, `A-Z`, `0-9`, and `.,;:^&*-_+=#~@`
"""
return "".join(secrets.choice(_string_with_symbols) for _ in range(length))
def is_ascii(s: bytes) -> bool: def is_ascii(s: bytes) -> bool: