Created
March 19, 2026 22:43
-
-
Save TheExpertNoob/dc9d0fc6f11b84b39e86c1f837298ec3 to your computer and use it in GitHub Desktop.
Generate Tinfoil HAUTH
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
| #!/usr/bin/env python3 | |
| """ | |
| Usage: | |
| python3 HAUTHgen.py <seed> <url> | |
| Example: | |
| python3 HAUTHgen.py "Cancel" "https://shop.nlib.cc/" | |
| """ | |
| import sys | |
| import hashlib | |
| from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes | |
| from cryptography.hazmat.backends import default_backend | |
| def aes128_encrypt_block(key: bytes, block: bytes) -> bytes: | |
| c = Cipher(algorithms.AES(key), modes.ECB(), backend=default_backend()) | |
| e = c.encryptor() | |
| return e.update(block) + e.finalize() | |
| def left_shift_one(b: bytearray) -> bytearray: | |
| out = bytearray(16) | |
| carry = 0 | |
| for i in range(15, -1, -1): | |
| next_carry = (b[i] & 0x80) >> 7 | |
| out[i] = ((b[i] << 1) | carry) & 0xFF | |
| carry = next_carry | |
| return out | |
| def derive_subkey(b: bytes) -> bytes: | |
| arr = bytearray(b) | |
| msb_set = (arr[0] & 0x80) != 0 | |
| arr = left_shift_one(arr) | |
| if msb_set: | |
| arr[15] ^= 0x87 | |
| return bytes(arr) | |
| def aes_cmac(key: bytes, msg: bytes) -> bytes: | |
| zero = bytes(16) | |
| L = aes128_encrypt_block(key, zero) | |
| K1 = derive_subkey(L) | |
| K2 = derive_subkey(K1) | |
| block_size = 16 | |
| msg_len = len(msg) | |
| n = max((msg_len + block_size - 1) // block_size, 1) | |
| is_complete = (msg_len != 0) and (msg_len % block_size == 0) | |
| mLast = bytearray(16) | |
| if is_complete: | |
| last = msg[(n - 1) * block_size:] | |
| for i in range(16): | |
| mLast[i] = last[i] ^ K1[i] | |
| else: | |
| rem = msg_len % block_size | |
| last = msg[msg_len - rem:] if rem else b'' | |
| for i in range(rem): | |
| mLast[i] = last[i] | |
| mLast[rem] = 0x80 | |
| for i in range(16): | |
| mLast[i] ^= K2[i] | |
| x = bytearray(16) | |
| for i in range(n - 1): | |
| block = msg[i * block_size:(i + 1) * block_size] | |
| y = bytearray(a ^ b for a, b in zip(x, block)) | |
| x = bytearray(aes128_encrypt_block(key, bytes(y))) | |
| y = bytearray(a ^ b for a, b in zip(x, mLast)) | |
| return aes128_encrypt_block(key, bytes(y)) | |
| def build_host_part(url: str) -> str | None: | |
| if not url: | |
| return None | |
| scheme_sep = url.find("://") | |
| if scheme_sep <= 0: | |
| return None | |
| netloc_start = scheme_sep + 3 | |
| if netloc_start >= len(url): | |
| return None | |
| end = len(url) | |
| for ch in '/?#': | |
| idx = url.find(ch, netloc_start) | |
| if idx != -1: | |
| end = min(end, idx) | |
| netloc = url[netloc_start:end] | |
| if not netloc: | |
| return None | |
| scheme = url[:scheme_sep].lower() | |
| if scheme == 'https' and netloc.endswith(':443'): | |
| netloc = netloc[:-4] | |
| elif scheme == 'http' and netloc.endswith(':80'): | |
| netloc = netloc[:-3] | |
| return f"{scheme}://{netloc}" | |
| def compute_hauth(seed: str, url: str) -> str: | |
| host_part = build_host_part(url) | |
| if not host_part: | |
| return "0" | |
| key = hashlib.sha256(seed.encode('utf-8')).digest()[:16] | |
| msg = host_part.encode('utf-8') + b'\x00' | |
| return aes_cmac(key, msg).hex().upper() | |
| if __name__ == "__main__": | |
| if len(sys.argv) != 3: | |
| print(f"Usage: {sys.argv[0]} <seed> <url>") | |
| sys.exit(1) | |
| seed, url = sys.argv[1], sys.argv[2] | |
| hauth = compute_hauth(seed, url) | |
| print(f"HAUTH: {hauth}") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment