From 8bd20d815c799a9abe57572560974a96af148bfc Mon Sep 17 00:00:00 2001 From: lelo Date: Mon, 14 Apr 2025 21:29:20 +0200 Subject: [PATCH] automatic keygen for possible future usage --- keygen.py | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 keygen.py diff --git a/keygen.py b/keygen.py new file mode 100644 index 0000000..645162b --- /dev/null +++ b/keygen.py @@ -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)