Skip to content

Instantly share code, notes, and snippets.

@WitherOrNot
Last active November 24, 2023 06:43
Show Gist options
  • Save WitherOrNot/7ac1be87811df327d4025a2c2ad5196c to your computer and use it in GitHub Desktop.
Save WitherOrNot/7ac1be87811df327d4025a2c2ad5196c to your computer and use it in GitHub Desktop.
Office 2K3/2K7 confirmation ID generation (by david4599)
import hashlib
def add_pid_cksum(pid):
sumPID = 0
val = pid
while val != 0:
sumPID += val % 10
val //= 10
return pid * 10 + 7 - (sumPID % 7)
def decode_iid_new_version(iid, pid):
buffer = [0] * 5
for i in range(len(buffer)):
buffer[i] = int.from_bytes(iid[i*4:i*4+4], byteorder='little')
# print("buffer[" + str(i) + "] = " + hex(buffer[i])[2:].zfill(8))
v1 = (buffer[3] & 0xFFFFFFF8) | 2 # Not really sure but seems to work
v2 = (buffer[3] & 7) << 29 | buffer[2] >> 3
hardwareId = (v1) << 32 | v2
hardwareId = int(hardwareId).to_bytes(8, byteorder='little')
v3 = (buffer[0] & 0xFFFFFF80) >> 7 & 0xFFFFFFFF
unknown1 = v3 & 0x000007FF
v4 = v3 & 0xFFFFF800
v5 = buffer[1] & 0x7F
v6 = buffer[1] >> 7
v7 = (v5 << 25 | v4) >> 11
productID1 = v7 & 0x000003FF
v8 = v7 & 0xFFFFFC00
v9 = (v6 >> 11) & 0x00001FFF
v10 = v9 & 0x00001C00
v11 = v9 & 0x000003FF
v12 = ((v6 << 21) & 0xFFFFFFFF | v8) >> 10
v13 = (v11 << 22) & 0xFFFFFFFF
v14 = v13 | v12
productID3RandomPart = (v14 & 0x3FF00000) >> 20
productID2NoChecksum = v14 & 0x000FFFFF
v15 = v13 >> 30 # 0x00000003
v16 = v10 >> 8 # 0x0000001C
v17 = (buffer[2] & 7) << 6 # 0x000001C0
v18 = (buffer[4] & 1) << 9 # 0x00000200
authInfo = v18 | v17 | v16 | v15 # Not that important bug: bit 5 is not present
productID0 = pid[0]
productID2 = add_pid_cksum(productID2NoChecksum)
productID3 = (pid[3] // 1000) * 1000 + productID3RandomPart
# Just to remember: public key index I of pid (XXXXX-XXX-XXXXXXX-IIXXX) = BINK ID // 2
# Where is actually located the version number?
# version1 = buffer[0] & 7
# print("Decoded IID Version1?: " + str(version1))
# version2 = (int.from_bytes(iid[8:17], byteorder='little') >> 52) & 7
# print("Decoded IID Version2?: " + str(version2))
# version3 = buffer[3] & 7
# print("Decoded IID Version3?: " + str(version3))
if productID1 != pid[1] or productID2 != pid[2] or pid[3] % 1000 != productID3RandomPart:
print("Error: Product ID not matching!")
return 0, 0, 0
return hardwareId, authInfo, unknown1
# Validate installation ID checksum
def validate_cksum(n):
print("Checksumming installation ID...")
n = n.replace("-", "")
cksum = 0
for i, k in enumerate(map(int, n)):
if (i + 1) % 6 == 0 or i == len(n) - 1:
print("Expected last digit", cksum % 7, "got", k)
if cksum % 7 != k:
return None
cksum = 0
else:
cksum += k * (i % 2 + 1)
parts = [n[i:i+5] for i in range(0, len(n), 6)]
n_out = "".join(parts)
if len(n_out) == 42:
n_out = n_out[:-1]
if len(n_out) != 45 and len(n_out) != 41:
return None
return int(n_out)
# Insert checksum digits into confirmation ID
def add_cksum(n):
cksums = []
n = str(n).zfill(35)
parts = [n[i:i+5] for i in range(0, len(n), 5)]
for p in parts:
cksum = 0
for i, k in enumerate(map(int, p)):
cksum += k * (i % 2 + 1)
cksums.append(str(cksum % 7))
n_out = ""
for i in range(7):
n_out += parts[i] + cksums[i] + ("-" if i != 6 else "")
return n_out
def encrypt(decrypted, key):
size_half = len(decrypted) // 2
size_half_dwords = size_half - (size_half % 4)
last = decrypted[size_half*2:]
decrypted = decrypted[:size_half*2]
for i in range(4):
first = decrypted[:size_half]
second = decrypted[size_half:]
# A magic byte 0x79 is now added at the beginning of the list of bytes to hash
sha1_result = hashlib.sha1(bytearray.fromhex("79") + second + key).digest()
sha1_result = (sha1_result[:size_half_dwords] +
sha1_result[size_half_dwords+4-(size_half%4) : size_half+4-(size_half%4)])
decrypted = second + bytes(x^^y for x,y in zip(first, sha1_result))
return decrypted + last
def decrypt(encrypted, key):
size_half = len(encrypted) // 2
size_half_dwords = size_half - (size_half % 4)
last = encrypted[size_half*2:]
encrypted = encrypted[:size_half*2]
for i in range(4):
first = encrypted[:size_half]
second = encrypted[size_half:]
# A magic byte 0x79 is now added at the beginning of the list of bytes to hash
sha1_result = hashlib.sha1(bytearray.fromhex("79") + first + key).digest()
sha1_result = (sha1_result[:size_half_dwords] +
sha1_result[size_half_dwords+4-(size_half%4) : size_half+4-(size_half%4)])
encrypted = bytes(x^^y for x,y in zip(second, sha1_result)) + first
return encrypted + last
# Find v of divisor (u, v) of curve y^2 = F(x)
def find_v(u):
f = F % u
c2 = u[1]^2 - 4 * u[0]
c1 = 2 * f[0] - f[1] * u[1]
if c2 == 0:
if c1 == 0:
return None
try:
v1 = sqrt(f[1]^2 / (2 * c1))
v1.lift()
except:
return None
else:
try:
d = 2 * sqrt(f[0]^2 + f[1] * (f[1] * u[0] - f[0] * u[1]))
v1_1 = sqrt((c1 - d)/c2)
v1_2 = sqrt((c1 + d)/c2)
except:
return None
try:
v1_1.lift()
v1 = v1_1
except:
try:
v1_2.lift()
v1 = v1_2
except:
return None
v0 = (f[1] + u[1] * v1^2) / (2 * v1)
v = v0 + v1 * x
assert (v^2 - f) % u == 0
return v
# order of field Fp
p = 0x16E48DD18451FE9
# Coefficients of F
coeffs = [0, 0xE5F5ECD95C8FD2, 0xFF28276F11F61, 0xFB2BD9132627E6, 0xE5F5ECD95C8FD2, 1]
# This constant inverts multiplication by 0x10001 in verification
INV = 0x01fb8cf48a70dfefe0302a1f7a5341
# Key to decrypt installation IDs
IID_KEY = b'\x5A\x30\xB9\xF3'
#"""
# minimal quadratic non-residue of p
mqnr = least_quadratic_nonresidue(p)
# Galois field of order p
Fp = GF(p)
# Polynomial field Fp[x] over Fp
Fpx.<x> = Fp[]
# Hyperellptic curve function
F = sum(k*x^i for i, k in enumerate(coeffs))
# Hyperelliptic curve E: y^2 = F(x) over Fp
E = HyperellipticCurve(F)
# The jacobian over E
J = E.jacobian()
# unpack&decrypt installationId
installationId = validate_cksum(input("Installation ID (dashes optional): "))
productId = input("Product ID (with dashes): ").split("-")
pid = [int(x) for x in productId]
# Office 2003 Professional Edition FWYTB-C7PPP-4497G-FV737-2HQWG (UMSKT generated)
# installationId = 020572391118023984229275432949036355811509788 # 020570-239116-180233-984220-927546-329495-036352-581151-097880
# pid = [73931, 746, 6952006, 57345] # 73931-746-6952006-57345
# Office 2007 Enterprise Edition XGQ68-R77XM-FPYFH-B436K-46QDY (UMSKT generated)
# installationId = 032422660398632786377841998280144793681167281 # 032424-266032-986324-786370-784193-982801-144791-368115-672814
# pid = [89388, 864, 6523093, 65443] # 89388-864-6523093-65443
print(installationId)
if not installationId:
raise Exception("Invalid Installation ID (checksum fail)")
installationIdSize = 19 if len(str(installationId)) > 41 else 17 # 17 for XP Gold, 19 for SP1+ (includes 12 bits of sha1(product key))
iid = int(installationId).to_bytes(installationIdSize, byteorder='little')
iid = decrypt(iid, IID_KEY)
hwid, authInfo, unknown1 = decode_iid_new_version(iid, pid)
print("\nDecoded Hardware ID: " + hex(int.from_bytes(hwid, byteorder='big')))
print("Decoded AuthInfo: " + hex(authInfo))
print("Decoded Unknown1: " + hex(unknown1))
assert hwid != 0
key = hwid + int((pid[0] << 41 | pid[1] << 58 | pid[2] << 17 | pid[3]) & ((1 << 64) - 1)).to_bytes(8, byteorder='little')
data = [0x00] * 14
# data = b'\xb9g\xdd\xe1\xb0\xef-\x1e\xbd\x0frE\xd8\xbe'
print("\nConfirmation IDs:")
for i in range(0x81):
data[6] = i # Attempt number was byte 7 in older confirmation ID version but it is now byte 6
# Encrypt conf ID, find u of divisor (u, v)
encrypted = encrypt(bytes(data), key)
encrypted = int.from_bytes(encrypted, byteorder="little")
x1, x2 = Fp(encrypted % p), Fp((encrypted // p) + 1)
u1, u0 = x1 * 2, (x1 ^ 2) - ((x2 ^ 2) * mqnr)
u = x^2 + u1 * x + u0
# Generate original divisor
v = find_v(u)
if not v:
print(v)
continue
d2 = J(u, v)
divisor = d2 * INV
# Get x1 and x2
roots = [x for x, y in divisor[0].roots()]
if len(roots) > 0:
y = [divisor[1](r) for r in roots]
x1 = (-roots[0]).lift()
x2 = (-roots[1]).lift()
if (x1 > x2) or (y[0].lift() % 2 != y[1].lift() % 2):
x1 = (-roots[1]).lift()
x2 = (-roots[0]).lift()
else:
x2 = (divisor[0][1] / 2).lift()
x1 = sqrt((x2^2 - divisor[0][0]) / mqnr).lift() + p
# Win
conf_id = x1 * (p + 1) + x2
conf_id = add_cksum(conf_id)
print(conf_id)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment