Coverage for polar/kit/crypto.py: 42%
24 statements
« prev ^ index » next coverage.py v7.10.6, created at 2025-12-05 15:52 +0000
« prev ^ index » next coverage.py v7.10.6, created at 2025-12-05 15:52 +0000
1import hashlib 1ab
2import hmac 1ab
3import secrets 1ab
4import string 1ab
5import zlib 1ab
8def _crc32_to_base62(number: int) -> str: 1ab
9 characters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
10 base = len(characters)
11 encoded = ""
12 while number:
13 number, remainder = divmod(number, base)
14 encoded = characters[remainder] + encoded
15 return encoded.zfill(6) # Ensure the checksum is 6 characters long
18def get_token_hash(token: str, *, secret: str) -> str: 1ab
19 hash = hmac.new(secret.encode("ascii"), token.encode("ascii"), hashlib.sha256) 1c
20 return hash.hexdigest() 1c
23def generate_token(*, prefix: str = "") -> str: 1ab
24 # Generate a high entropy random token
25 token = "".join(
26 secrets.choice(string.ascii_letters + string.digits) for _ in range(37)
27 )
29 # Calculate a 32-bit CRC checksum
30 checksum = zlib.crc32(token.encode("utf-8")) & 0xFFFFFFFF
31 checksum_base62 = _crc32_to_base62(checksum)
33 # Concatenate the prefix, token, and checksum
34 return f"{prefix}{token}{checksum_base62}"
37def generate_token_hash_pair(*, secret: str, prefix: str = "") -> tuple[str, str]: 1ab
38 """
39 Generate a token suitable for sensitive values like API tokens.
41 Returns both the actual value and its HMAC-SHA256 hash.
42 Only the latter shall be stored in database.
43 """
44 token = generate_token(prefix=prefix)
45 return token, get_token_hash(token, secret=secret)