Skip to content

Instantly share code, notes, and snippets.

@computerquip
Last active April 19, 2024 07:42
Show Gist options
  • Save computerquip/84ae83f03bee49e3be86ee7bfb33775d to your computer and use it in GitHub Desktop.
Save computerquip/84ae83f03bee49e3be86ee7bfb33775d to your computer and use it in GitHub Desktop.
UT2004 Key Generator Bottle App
from bottle import default_app, route
import hashlib
import ctypes
import secrets
# Copyright (c) 2005-2023, NumPy Developers.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided
# with the distribution.
#
# * Neither the name of the NumPy Developers nor the names of any
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
def base_repr(number, base=2, padding=0):
digits = '0123456789abcdefghijklmnopqrstuvwxyz'
if base > len(digits):
raise ValueError("Bases greater than 36 not handled in base_repr.")
elif base < 2:
raise ValueError("Bases less than 2 not handled in base_repr.")
num = abs(number)
res = []
while num:
res.append(digits[num % base])
num //= base
if padding:
res.append('0' * padding)
if number < 0:
res.append('-')
return ''.join(reversed(res or '0'))
def scramble(str):
result = ''
table = 'ABCDEFGHJLKMNPQRTUVWXYZ2346789'
for ch in str:
c = ord(ch)
if c >= 0x30 and c <= 0x39:
result += table[c - 0x30]
elif c >= 0x41 and c <= 0x5A:
result += table[c - 0x41 + 10]
elif c >= 0x61 and c <= 0x7A:
result += table[c - 0x61 + 10]
else:
result += '\0'
return result
@route('/')
def generate_key():
magic = 'appDebugfNoInit'
seed = secrets.randbits(64)
base = 30
padch = 'A'
seed30 = base_repr(ctypes.c_ulonglong(seed).value, base)
key = scramble(seed30).rjust(14, padch)
checkData = f'{ctypes.c_longlong(seed).value}{magic}'
digest = hashlib.md5(checkData.encode('utf-8')).digest()
# Convert to unsigned 64-bit integer
qdigest = ctypes.c_ulonglong(int.from_bytes(digest[0:8], 'little')).value
digest30 = base_repr(qdigest, base).rjust(6, padch)
check = scramble(digest30)
fullkey = f'{key}{check}'
return f'{fullkey[10:15]}-{fullkey[5:10]}-{fullkey[0:5]}-{fullkey[15:20]}'
application = default_app()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment