Created
March 11, 2025 14:40
-
-
Save AdamQuixote/80907662873375ea4b16fc7a657b3293 to your computer and use it in GitHub Desktop.
Simple python rotational cipher for recovery keys in the format ABCD-1234-XXXX.. (format-independent, only uses A-Z and 0-9)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import sys | |
| import time | |
| def get_shift(pin_char): | |
| """ | |
| Returns the numeric shift value for a given PIN character. | |
| Digits are taken as their integer value. | |
| Letters use their position in the alphabet (A=1, B=2, etc.). | |
| """ | |
| if pin_char.isdigit(): | |
| return int(pin_char) | |
| elif pin_char.isalpha(): | |
| return ord(pin_char.upper()) - ord('A') + 1 | |
| else: | |
| return 0 | |
| def rotate_char(c, shift): | |
| """ | |
| Rotates a single character by the given shift amount. | |
| For digits, rotation is modulo 10. | |
| For letters, rotation is modulo 26 (only uppercase letters). | |
| Non-alphanumeric characters (like '-') are returned unchanged. | |
| """ | |
| if c.isdigit(): | |
| new_digit = (int(c) + shift) % 10 | |
| return str(new_digit) | |
| elif c.isalpha(): | |
| new_index = (ord(c) - ord('A') + shift) % 26 | |
| return chr(new_index + ord('A')) | |
| else: | |
| return c | |
| def animate_rotation(c, shift, delay=0.2): | |
| """ | |
| Animates the rotation of a character in place. | |
| If shift is positive, rotates forward; if negative, rotates backward. | |
| Intermediate rotation steps are shown using backspaces. | |
| """ | |
| if shift == 0: | |
| print(c, end="", flush=True) | |
| elif shift > 0: | |
| for i in range(1, shift + 1): | |
| intermediate = rotate_char(c, i) | |
| print(intermediate, end="", flush=True) | |
| time.sleep(delay) | |
| if i != shift: | |
| print("\b", end="", flush=True) | |
| else: # shift is negative | |
| for i in range(-1, shift - 1, -1): # e.g., if shift is -3, i takes -1, -2, -3. | |
| intermediate = rotate_char(c, i) | |
| print(intermediate, end="", flush=True) | |
| time.sleep(delay) | |
| if i != shift: | |
| print("\b", end="", flush=True) | |
| def main(): | |
| # Get inputs from the user. | |
| recovery_key = input("Enter recovery key (format xxxx-xxxx): ").strip().upper() | |
| pin = input("Enter PIN (digits and/or letters, prefix with '-' for decryption): ").strip() | |
| # Check if PIN starts with '-' indicating decryption mode. | |
| decrypt = False | |
| if pin.startswith("-"): | |
| decrypt = True | |
| pin = pin[1:] # Remove the minus sign for processing. | |
| # Convert the PIN into a list of shift values (skip non-alphanumeric characters) | |
| shifts = [get_shift(c) for c in pin if c.isalnum()] | |
| if not shifts: | |
| print("PIN must contain at least one alphanumeric character.") | |
| sys.exit(1) | |
| mode = "Decrypting" if decrypt else "Encrypting" | |
| print(f"\n{mode} rotational cipher:") | |
| final_key = "" | |
| pin_index = 0 | |
| # Process each character in the recovery key. | |
| for c in recovery_key: | |
| if c.isalnum(): | |
| base_shift = shifts[pin_index % len(shifts)] | |
| # Use negative shift if decrypting. | |
| current_shift = -base_shift if decrypt else base_shift | |
| animate_rotation(c, current_shift) | |
| final_key += rotate_char(c, current_shift) | |
| pin_index += 1 | |
| else: | |
| print(c, end="", flush=True) | |
| final_key += c | |
| print() # Move to a new line after printing the key. | |
| if decrypt: | |
| print("Deciphered key:", final_key) | |
| else: | |
| print("Ciphered key:", final_key) | |
| if __name__ == "__main__": | |
| main() |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Currently, any non a-z, 0-9 special character (including accented letters) is treated as a separator. It would be easy to modify to include special characters by using "elif str(c) in ["$", "^"], adding a gvar or param and using that to check whether an object should be iterated through instead (probably a list) - this would allow custom rotations, and essentially turn the script into a key.
Good as a proof of concept, and probably only usable in online contexts where queries are limited. In an offline context, a true encryption function would be essential.