Compare commits
No commits in common. "9fe05b28b69019f65fc076950f9a7cfacd7ac704" and "1822fc20cb56d76146b833c5efdee122eaab3e0f" have entirely different histories.
9fe05b28b6
...
1822fc20cb
100
keygen.py
100
keygen.py
@ -1,100 +0,0 @@
|
|||||||
import hmac
|
|
||||||
import hashlib
|
|
||||||
import base64
|
|
||||||
|
|
||||||
salt = "UqEEf08yMDE3qIQodAGAyNQ2EPFhb26f2o85MTIyMeAAkqlANiXcF"
|
|
||||||
|
|
||||||
def generate_secret_key(identifier: str, expiry: str) -> str:
|
|
||||||
"""
|
|
||||||
Generate a secret key with the following structure:
|
|
||||||
- encoded_data: Base64 encoding of (identifier + expiry)
|
|
||||||
- signature: 32 characters from the URL-safe base64 HMAC signature
|
|
||||||
- id_length_hex: 2-character hexadecimal number representing the length of the identifier
|
|
||||||
The HMAC is computed over (encoded_data + id_length_hex).
|
|
||||||
"""
|
|
||||||
global salt
|
|
||||||
if len(identifier) > 255:
|
|
||||||
raise ValueError("Identifier must be at most 255 characters long.")
|
|
||||||
if len(expiry) != 8:
|
|
||||||
raise ValueError("Expiry must be an 8-character string in DDMMYYYY format.")
|
|
||||||
|
|
||||||
# Concatenate identifier and expiry, then base64 encode the result.
|
|
||||||
plain_data = identifier + expiry
|
|
||||||
# Remove padding for compactness; we'll add it back during decoding.
|
|
||||||
encoded_data = base64.urlsafe_b64encode(plain_data.encode()).decode().rstrip("=")
|
|
||||||
|
|
||||||
# Format the length of the identifier as a 2-digit hexadecimal string.
|
|
||||||
id_length_hex = f"{len(identifier):02X}"
|
|
||||||
|
|
||||||
# Build the message for HMAC: encoded_data + id_length_hex.
|
|
||||||
message = encoded_data + id_length_hex
|
|
||||||
|
|
||||||
# Compute HMAC using SHA-256.
|
|
||||||
hmac_digest = hmac.new(salt.encode(), message.encode(), hashlib.sha256).digest()
|
|
||||||
|
|
||||||
# Get a URL-safe base64 representation and take the first 32 characters as the signature.
|
|
||||||
signature = base64.urlsafe_b64encode(hmac_digest).decode()[:32]
|
|
||||||
|
|
||||||
# Construct the final secret key.
|
|
||||||
secret_key = encoded_data + signature + id_length_hex
|
|
||||||
return secret_key
|
|
||||||
|
|
||||||
def decode_secret_key(secret_key: str):
|
|
||||||
"""
|
|
||||||
Decode the secret key and extract the identifier and expiry.
|
|
||||||
|
|
||||||
Token structure:
|
|
||||||
- encoded_data: secret_key[0 : (len(secret_key)-34)] (variable length)
|
|
||||||
- signature: secret_key[-34:-2] (32 characters)
|
|
||||||
- id_length_hex: secret_key[-2:] (2 characters)
|
|
||||||
|
|
||||||
The HMAC is verified over (encoded_data + id_length_hex). Then, encoded_data is base64-decoded
|
|
||||||
(with proper padding added) to yield (identifier + expiry), where the last 8 characters represent expiry.
|
|
||||||
The identifier length is determined from id_length_hex.
|
|
||||||
"""
|
|
||||||
global salt
|
|
||||||
|
|
||||||
if len(secret_key) < 34: # Must at least have signature (32) + id_length_hex (2)
|
|
||||||
raise ValueError("Invalid key length.")
|
|
||||||
|
|
||||||
# Extract the last 2 characters: id_length_hex.
|
|
||||||
id_length_hex = secret_key[-2:]
|
|
||||||
try:
|
|
||||||
id_length = int(id_length_hex, 16)
|
|
||||||
except ValueError:
|
|
||||||
raise ValueError("Invalid identifier length prefix in key.")
|
|
||||||
|
|
||||||
# The signature is the 32 characters preceding the last 2.
|
|
||||||
signature = secret_key[-34:-2]
|
|
||||||
# The remainder is the encoded_data.
|
|
||||||
encoded_data = secret_key[:-34]
|
|
||||||
|
|
||||||
# Verify the signature.
|
|
||||||
message = encoded_data + id_length_hex
|
|
||||||
expected_hmac = hmac.new(salt.encode(), message.encode(), hashlib.sha256).digest()
|
|
||||||
expected_signature = base64.urlsafe_b64encode(expected_hmac).decode()[:32]
|
|
||||||
|
|
||||||
if not hmac.compare_digest(signature, expected_signature):
|
|
||||||
raise ValueError("Invalid key signature.")
|
|
||||||
|
|
||||||
# Base64-decode the encoded_data.
|
|
||||||
# Add back any missing '=' padding.
|
|
||||||
padding = '=' * (-len(encoded_data) % 4)
|
|
||||||
plain_data = base64.urlsafe_b64decode(encoded_data + padding).decode()
|
|
||||||
|
|
||||||
# plain_data should be (identifier + expiry), where expiry is the last 8 characters.
|
|
||||||
if len(plain_data) != id_length + 8:
|
|
||||||
raise ValueError("Decoded data length does not match expected identifier length and expiry.")
|
|
||||||
|
|
||||||
identifier = plain_data[:id_length]
|
|
||||||
expiry = plain_data[id_length:]
|
|
||||||
|
|
||||||
return identifier, expiry
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
key = generate_secret_key("Besondere Gottesdienste", "30042025")
|
|
||||||
print(key)
|
|
||||||
identifier, expiry = decode_secret_key(key)
|
|
||||||
print("identifier:", identifier)
|
|
||||||
print("expiry:", expiry)
|
|
||||||
Loading…
x
Reference in New Issue
Block a user