Merge branch 'development' of gitea.centx.de:lelo/bethaus-app into development
This commit is contained in:
commit
e176172e9b
100
keygen.py
Normal file
100
keygen.py
Normal file
@ -0,0 +1,100 @@
|
||||
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