Skip to content

Instantly share code, notes, and snippets.

@quangnhut123
Last active September 8, 2023 15:57
Show Gist options
  • Save quangnhut123/43fc43bf84b84b45ce027a5a2226e608 to your computer and use it in GitHub Desktop.
Save quangnhut123/43fc43bf84b84b45ce027a5a2226e608 to your computer and use it in GitHub Desktop.
Charles KeyGen
import random
import struct
class SimpleRC5:
P32 = 0xB7E15163
Q32 = 0x9E3779B9
R = 12
def __init__(self, key=None):
self.S = [0] * (2 * (self.R + 1))
if key is not None:
self.set_key(key)
def set_key(self, key):
t = 2 * (self.R + 1)
c = 2
L = [0] * c
L[0] = int(key & 0xFFFFFFFF)
L[1] = int((key >> 32) & 0xFFFFFFFF)
self.S[0] = self.P32
for i in range(1, t):
self.S[i] = (self.S[i - 1] + self.Q32) & 0xFFFFFFFF
i, j = 0, 0
A, B = 0, 0
for k in range(3 * t):
A = self.S[i] = self.rotate_left(self.S[i] + A + B, 3)
B = L[j] = self.rotate_left(L[j] + A + B, A + B)
i = (i + 1) % t
j = (j + 1) % c
def encrypt(self, in_val):
A = (int(in_val & 0xFFFFFFFF) + self.S[0]) & 0xFFFFFFFF
B = (int((in_val >> 32) & 0xFFFFFFFF) + self.S[1]) & 0xFFFFFFFF
for i in range(1, self.R + 1):
A = (self.rotate_left(A ^ B, B) + self.S[2 * i]) & 0xFFFFFFFF
B = (self.rotate_left(B ^ A, A) + self.S[2 * i + 1]) & 0xFFFFFFFF
return self.as_long(A, B)
def decrypt(self, in_val):
A = int(in_val & 0xFFFFFFFF)
B = int((in_val >> 32) & 0xFFFFFFFF)
for i in range(self.R, 0, -1):
B = self.rotate_right(B - self.S[2 * i + 1], A) ^ A
A = self.rotate_right(A - self.S[2 * i], B) ^ B
B = (B - self.S[1]) & 0xFFFFFFFF
A = (A - self.S[0]) & 0xFFFFFFFF
return self.as_long(A, B)
@staticmethod
def rotate_left(x, y):
return ((x << (y & 31)) | (x >> (32 - (y & 31)))) & 0xFFFFFFFF
@staticmethod
def rotate_right(x, y):
return ((x >> (y & 31)) | (x << (32 - (y & 31)))) & 0xFFFFFFFF
@staticmethod
def as_long(a, b):
return (a & 0xFFFFFFFF) | (b << 32)
class CharlesKeygen:
RANDOM = random.SystemRandom()
RC5KEY_NAME = 0x7A21C951691CD470
RC5KEY_KEY = 0xB4F0E0CCEC0EAFAD
NAME_PREFIX = 0x54882F8A
@classmethod
def calc_prefix(cls, name):
name = name.replace(" ", " ")
name_bytes = name.encode("utf-8")
length = len(name_bytes) + 4
padded = (-length & 7) + length
input_data = struct.pack(f">I{len(name_bytes)}s", len(name_bytes), name_bytes)
input_data = input_data.ljust(padded, b"\0")
rc5 = SimpleRC5(cls.RC5KEY_NAME)
output_data = bytearray()
for i in range(0, len(input_data), 8):
segment = struct.unpack(">Q", input_data[i : i + 8])[0]
encrypted = rc5.encrypt(segment)
output_data.extend(struct.pack(">Q", encrypted))
n = 0
for b in output_data:
n = rc5.rotate_left(n ^ b, 0x3)
return n & 0xFFFFFFFF
@staticmethod
def xor(n):
n2 = 0
for i in range(56, -1, -8):
n2 ^= (n >> i) & 0xFF
return abs(n2 & 0xFF)
@classmethod
def key(cls, prefix, suffix):
in_val = (prefix << 32) & 0xFFFFFFFFFFFFFFFF
if (suffix >> 16) in [0x0401, 0x0402, 0x0403]:
in_val |= suffix
else:
in_val |= 0x01000000 | (suffix & 0xFFFFFF)
out = SimpleRC5(cls.RC5KEY_KEY).decrypt(in_val)
return f"{cls.xor(in_val):02x}{out:016x}"
@classmethod
def keygen(cls, name, suffix=None):
if suffix is None:
suffix = cls.RANDOM.randint(0, 0xFFFFFFFF)
prefix = cls.calc_prefix(name) ^ cls.NAME_PREFIX
return cls.key(prefix, suffix)
def main(names):
if len(names) == 0:
print("Usage: python charles_keygen.py name [,...]")
else:
for name in names:
print(f"Name: {name}, Key: {CharlesKeygen.keygen(name)}")
if __name__ == "__main__":
import sys
main(sys.argv[1:])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment