Skip to content

Instantly share code, notes, and snippets.

@techguy16
Last active March 26, 2024 17:34
Show Gist options
  • Save techguy16/20c8433c69f231877ea8d6ba977fe695 to your computer and use it in GitHub Desktop.
Save techguy16/20c8433c69f231877ea8d6ba977fe695 to your computer and use it in GitHub Desktop.
Windows XP VLK Key Generation in many Languages

XP VLK Key Generator in different programming languages

Available

  • Python
  • HTML + JS
  • C

THIS IS FOR EDUCARIONAL USE ONLY. I WILL NOT BE HELD RESPONSIBLE FOR ANY UNSOLICITED USE OF THIS CODE

#include <stdio.h>
#include <string.h>
#include <openssl/bn.h>
#include <openssl/ec.h>
#include <openssl/sha.h>
#include <assert.h>
#define FIELD_BITS 384
#define FIELD_BYTES 48
uint8_t cset[] = "BCDFGHJKMPQRTVWXY2346789";
static void unpack(uint32_t *pid, uint32_t *hash, uint32_t *sig, uint32_t *raw)
{
// pid = Bit 0..30
pid[0] = raw[0] & 0x7fffffff;
// hash(s) = Bit 31..58
hash[0] = ((raw[0] >> 31) | (raw[1] << 1)) & 0xfffffff;
// sig(e) = bit 58..113
sig[0] = (raw[1] >> 27) | (raw[2] << 5);
sig[1] = (raw[2] >> 27) | (raw[3] << 5);
}
static void pack(uint32_t *raw, uint32_t *pid, uint32_t *hash, uint32_t *sig)
{
raw[0] = pid[0] | ((hash[0] & 1) << 31);
raw[1] = (hash[0] >> 1) | ((sig[0] & 0x1f) << 27);
raw[2] = (sig[0] >> 5) | (sig[1] << 27);
raw[3] = sig[1] >> 5;
}
// Reverse data
static void endian(uint8_t *data, int len)
{
int i;
for (i = 0; i < len/2; i++) {
uint8_t temp;
temp = data[i];
data[i] = data[len-i-1];
data[len-i-1] = temp;
}
}
void unbase24(uint32_t *x, uint8_t *c)
{
memset(x, 0, 16);
int i, n;
BIGNUM *y = BN_new();
BN_zero(y);
for (i = 0; i < 25; i++)
{
BN_mul_word(y, 24);
BN_add_word(y, c[i]);
}
n = BN_num_bytes(y);
BN_bn2bin(y, (uint8_t *)x);
BN_free(y);
endian((uint8_t *)x, n);
}
void base24(uint8_t *c, uint32_t *x)
{
uint8_t y[16];
int i;
BIGNUM *z;
// Convert x to BigNum z
memcpy(y, x, sizeof(y)); // Copy X to Y; Y=X
for (i = 15; y[i] == 0; i--) {} i++; // skip following nulls
endian(y, i); // Reverse y
z = BN_bin2bn(y, i, NULL); // Convert y to BigNum z
// Divide z by 24 and convert remainder with cset to Base24-CDKEY Char
c[25] = 0;
for (i = 24; i >= 0; i--) {
uint8_t t = BN_div_word(z, 24);
c[i] = cset[t];
}
BN_free(z);
}
void print_product_id(uint32_t *pid)
{
char raw[12];
char b[6], c[8];
int i, digit = 0;
// Cut a away last bit of pid and convert it to an accii-number (=raw)
sprintf(raw, "%d", pid[0] >> 1);
// Make b-part {640-....}
strncpy(b, raw, 3);
b[3] = 0;
// Make c-part {...-123456X...}
strcpy(c, raw + 3);
// Make checksum digit-part {...56X-}
assert(strlen(c) == 6);
for (i = 0; i < 6; i++)
digit -= c[i] - '0'; // Sum digits
while (digit < 0)
digit += 7;
c[6] = digit + '0';
c[7] = 0;
printf("Product ID: 55274-%s-%s-23xxx\n", b, c);
}
void print_product_key(uint8_t *pk)
{
int i;
assert(strlen((const char *)pk) == 25);
for (i = 0; i < 25; i++) {
putchar(pk[i]);
if (i != 24 && i % 5 == 4) putchar('-');
}
}
void verify(EC_GROUP *ec, EC_POINT *generator, EC_POINT *public_key, char *cdkey)
{
uint8_t key[25];
int i, j, k;
BN_CTX *ctx = BN_CTX_new();
// remove Dashs from CDKEY
for (i = 0, k = 0; i < strlen(cdkey); i++) {
for (j = 0; j < 24; j++) {
if (cdkey[i] != '-' && cdkey[i] == cset[j]) {
key[k++] = j;
break;
}
assert(j < 24);
}
if (k >= 25) break;
}
// Base24_CDKEY -> Bin_CDKEY
uint32_t bkey[4] = {0};
uint32_t pid[1], hash[1], sig[2];
unbase24(bkey, key);
// Output Bin_CDKEY
printf("%.8x %.8x %.8x %.8x\n", bkey[3], bkey[2], bkey[1], bkey[0]);
// Divide/Extract pid_data, hash, sig from Bin_CDKEY
unpack(pid, hash, sig, bkey);
print_product_id(pid);
printf("PID: %.8x\nHash: %.8x\nSig: %.8x %.8x\n", pid[0], hash[0], sig[1], sig[0]);
BIGNUM *e, *s;
/* e = hash, s = sig */
e = BN_new();
BN_set_word(e, hash[0]);
endian((uint8_t *)sig, sizeof(sig));
s = BN_bin2bn((uint8_t *)sig, sizeof(sig), NULL);
BIGNUM *x = BN_new();
BIGNUM *y = BN_new();
EC_POINT *u = EC_POINT_new(ec);
EC_POINT *v = EC_POINT_new(ec);
/* v = s*generator + e*(-public_key) */
EC_POINT_mul(ec, u, NULL, generator, s, ctx);
EC_POINT_mul(ec, v, NULL, public_key, e, ctx);
EC_POINT_add(ec, v, u, v, ctx);
EC_POINT_get_affine_coordinates_GFp(ec, v, x, y, ctx);
uint8_t buf[FIELD_BYTES], md[20];
uint32_t h;
uint8_t t[4];
SHA_CTX h_ctx;
/* h = (fist 32 bits of SHA1(pid || v.x, v.y)) >> 4 */
SHA1_Init(&h_ctx);
t[0] = pid[0] & 0xff;
t[1] = (pid[0] & 0xff00) >> 8;
t[2] = (pid[0] & 0xff0000) >> 16;
t[3] = (pid[0] & 0xff000000) >> 24;
SHA1_Update(&h_ctx, t, sizeof(t));
memset(buf, 0, sizeof(buf));
BN_bn2bin(x, buf);
endian((uint8_t *)buf, sizeof(buf));
SHA1_Update(&h_ctx, buf, sizeof(buf));
memset(buf, 0, sizeof(buf));
BN_bn2bin(y, buf);
endian((uint8_t *)buf, sizeof(buf));
SHA1_Update(&h_ctx, buf, sizeof(buf));
SHA1_Final(md, &h_ctx);
h = (md[0] | (md[1] << 8) | (md[2] << 16) | (md[3] << 24)) >> 4;
h &= 0xfffffff;
printf("Calculated hash: %.8x\n", h);
if (h == hash[0]) printf("Key valid\n");
else printf("Key invalid\n");
putchar('\n');
BN_free(e);
BN_free(s);
BN_free(x);
BN_free(y);
EC_POINT_free(u);
EC_POINT_free(v);
BN_CTX_free(ctx);
}
void generate(uint8_t *pkey, EC_GROUP *ec, EC_POINT *generator, BIGNUM *order, BIGNUM *priv, uint32_t *pid)
{
BN_CTX *ctx = BN_CTX_new();
BIGNUM *k = BN_new();
BIGNUM *s = BN_new();
BIGNUM *x = BN_new();
BIGNUM *y = BN_new();
EC_POINT *r = EC_POINT_new(ec);
uint32_t bkey[4];
// Loop in case signaturepart will make cdkey(base-24 "digits") longer than 25
do {
BN_pseudo_rand(k, FIELD_BITS, -1, 0);
EC_POINT_mul(ec, r, NULL, generator, k, ctx);
EC_POINT_get_affine_coordinates_GFp(ec, r, x, y, ctx);
SHA_CTX h_ctx;
uint8_t t[4], md[20], buf[FIELD_BYTES];
uint32_t hash[1];
/* h = (fist 32 bits of SHA1(pid || r.x, r.y)) >> 4 */
SHA1_Init(&h_ctx);
t[0] = pid[0] & 0xff;
t[1] = (pid[0] & 0xff00) >> 8;
t[2] = (pid[0] & 0xff0000) >> 16;
t[3] = (pid[0] & 0xff000000) >> 24;
SHA1_Update(&h_ctx, t, sizeof(t));
memset(buf, 0, sizeof(buf));
BN_bn2bin(x, buf);
endian((uint8_t *)buf, sizeof(buf));
SHA1_Update(&h_ctx, buf, sizeof(buf));
memset(buf, 0, sizeof(buf));
BN_bn2bin(y, buf);
endian((uint8_t *)buf, sizeof(buf));
SHA1_Update(&h_ctx, buf, sizeof(buf));
SHA1_Final(md, &h_ctx);
hash[0] = (md[0] | (md[1] << 8) | (md[2] << 16) | (md[3] << 24)) >> 4;
hash[0] &= 0xfffffff;
/* s = priv*h + k */
BN_copy(s, priv);
BN_mul_word(s, hash[0]);
BN_mod_add(s, s, k, order, ctx);
uint32_t sig[2] = {0};
BN_bn2bin(s, (uint8_t *)sig);
endian((uint8_t *)sig, BN_num_bytes(s));
pack(bkey, pid, hash, sig);
printf("PID: %.8x\nHash: %.8x\nSig: %.8x %.8x\n", pid[0], hash[0], sig[1], sig[0]);
} while (bkey[3] >= 0x62a32);
base24(pkey, bkey);
BN_free(k);
BN_free(s);
BN_free(x);
BN_free(y);
EC_POINT_free(r);
BN_CTX_free(ctx);
}
int main()
{
// Init
BIGNUM *a, *b, *p, *gx, *gy, *pubx, *puby, *n, *priv;
BN_CTX *ctx = BN_CTX_new();
// make BigNumbers
a = BN_new();
b = BN_new();
p = BN_new();
gx = BN_new();
gy = BN_new();
pubx = BN_new();
puby = BN_new();
n = BN_new();
priv = BN_new();
// Data from pidgen-Bink-resources
/* Elliptic curve parameters: y^2 = x^3 + ax + b mod p */
BN_hex2bn(&p, "92ddcf14cb9e71f4489a2e9ba350ae29454d98cb93bdbcc07d62b502ea12238ee904a8b20d017197aae0c103b32713a9");
BN_set_word(a, 1);
BN_set_word(b, 0);
/* base point (generator) G */
BN_hex2bn(&gx, "46E3775ECE21B0898D39BEA57050D422A0AF989E497962BAEE2CB17E0A28D5360D5476B8DC966443E37A14F1AEF37742");
BN_hex2bn(&gy, "7C8E741D2C34F4478E325469CD491603D807222C9C4AC09DDB2B31B3CE3F7CC191B3580079932BC6BEF70BE27604F65E");
/* inverse of public key */
BN_hex2bn(&pubx, "5D8DBE75198015EC41C45AAB6143542EB098F6A5CC9CE4178A1B8A1E7ABBB5BC64DF64FAF6177DC1B0988AB00BA94BF8");
BN_hex2bn(&puby, "23A2909A0B4803C89F910C7191758B48746CEA4D5FF07667444ACDB9512080DBCA55E6EBF30433672B894F44ACE92BFA");
// Computed data
/* order of G - computed in 18 hours using a P3-450 */
BN_hex2bn(&n, "DB6B4C58EFBAFD");
/* THE private key - computed in 10 hours using a P3-450 */
BN_hex2bn(&priv, "565B0DFF8496C8");
// Calculation
EC_GROUP *ec = EC_GROUP_new_curve_GFp(p, a, b, ctx);
EC_POINT *g = EC_POINT_new(ec);
EC_POINT_set_affine_coordinates_GFp(ec, g, gx, gy, ctx);
EC_POINT *pub = EC_POINT_new(ec);
EC_POINT_set_affine_coordinates_GFp(ec, pub, pubx, puby, ctx);
uint8_t pkey[26];
uint32_t pid[1];
pid[0] = 640000000 << 1; /* <- change */
// generate a key
generate(pkey, ec, g, n, priv, pid);
print_product_key(pkey); printf("\n\n");
// verify the key
verify(ec, g, pub, (char*)pkey);
// Cleanup
BN_CTX_free(ctx);
return 0;
}
<html>
<script language="JavaScript" type="text/javascript" src="https://techguy16.github.io/xpkeygen-js/jsbn.js"> </script>
<script language="JavaScript" type="text/javascript" src="https://techguy16.github.io/xpkeygen-js/jsbn2.js"> </script>
<script language="JavaScript" type="text/javascript" src="https://techguy16.github.io/xpkeygen-js/sha1.js"> </script>
<script language="JavaScript" type="text/javascript">
function addmod(x, a, b, m) {
a.addTo(b, x);
while (x.compareTo(m) >= 0) { x.subTo(m, x); }
}
function dblmod(x, a, m) {
a.lShiftTo(1, x);
while (x.compareTo(m) >= 0) { x.subTo(m, x); }
}
function submod(x, a, b, m) {
a.subTo(b, x);
while (x.compareTo(BigInteger.ZERO) < 0) { x.addTo(m, x); }
}
function mulmod(x, a, b, barrett) {
barrett.mulTo(a, b, x);
}
function sqrmod(x, a, barrett) {
barrett.sqrTo(a, x);
}
function Point(x, y, inf) {
this.x = x;
this.y = y;
this.inf = inf;
}
function Ecc(a, b, p) {
this.barrett = new Barrett(p);
this.p = p;
this.a = a;
this.b = b;
}
function pinf() { return new Point(0, 0, true); }
Ecc.prototype.add = function(p1, p2) {
if (p1.inf) { return p2; }
if (p2.inf) { return p1; }
var t1 = nbi();
var t2 = nbi();
var l = nbi();
if (p1.x.compareTo(p2.x) != 0) {
submod(t1, p1.y, p2.y, this.p);
submod(t2, p1.x, p2.x, this.p);
} else {
if (p1.y.compareTo(p2.y) != 0 || p2.y.compareTo(BigInteger.ZERO) == 0) { return Ecc.INF; }
sqrmod(t1, p2.x, this.barrett);
dblmod(t2, t1, this.p);
addmod(t1, t2, t1, this.p);
addmod(t1, t1, this.a, this.p);
dblmod(t2, p2.y, this.p);
}
mulmod(l, t1, t2.modInverse(this.p), this.barrett);
var x3 = nbi();
var y3 = nbi();
sqrmod(x3, l, this.barrett);
submod(x3, x3, p1.x, this.p);
submod(x3, x3, p2.x, this.p);
submod(y3, p2.x, x3, this.p);
mulmod(t1, y3, l, this.barrett);
submod(y3, t1, p2.y, this.p);
return new Point(x3, y3, false);
}
Ecc.prototype.neg = function(p) {
var ny = nbi();
this.p.subTo(p.y, ny);
return new Point(p.x, ny, false);
}
Ecc.prototype.mul = function(p1, n) {
var p = pinf();
var i;
var n3 = nbi();
n.addTo(n, n3);
n.addTo(n3, n3);
var np1 = this.neg(p1);
for (i = n3.bitLength() - 1; i >= 1; i--) {
p = this.add(p, p);
if (n3.testBit(i) && !n.testBit(i)) {
p = this.add(p, p1);
} else if (!n3.testBit(i) && n.testBit(i)) {
p = this.add(p, np1);
}
}
return p;
}
var cset = "BCDFGHJKMPQRTVWXY2346789";
var hexc = "0123456789abcdef";
var xpecc = new Ecc(
new BigInteger("1", 16),
new BigInteger("0", 16),
new BigInteger("92ddcf14cb9e71f4489a2e9ba350ae29454d98cb93bdbcc07d62b502ea12238ee904a8b20d017197aae0c103b32713a9", 16)
);
var g = new Point(
new BigInteger("46e3775ece21b0898d39bea57050d422a0af989e497962baee2cb17e0a28d5360d5476b8dc966443e37a14f1aef37742", 16),
new BigInteger("7c8e741d2c34f4478e325469cd491603d807222c9c4ac09ddb2b31b3ce3f7cc191b3580079932bc6bef70be27604f65e", 16),
false
);
// order of g
var order = new BigInteger("db6b4c58efbafd", 16);
// pirvate key
var priv = new BigInteger("565b0dff8496c8", 16);
function hexToByte(s) {
t = "";
var i, j;
if (s.length % 2 != 0) { s = "0" + s; }
for (i = 0; i < s.length; i += 2) {
t += String.fromCharCode(parseInt(s.substr(i, 2), 16));
}
return t;
}
function reverse(s) {
t = "";
var i;
for (i = s.length - 1; i >= 0; i--) {
t += s.charAt(i);
}
return t;
}
function random(n) {
t = "";
var i;
for (i = 0; i < 2*n; i++) {
t += hexc.charAt(Math.floor(Math.random() * 16));
}
k = nbi();
k.fromString(t, 16);
return k;
}
function generate() {
pid = 640000000 << 1;
maxkey = new BigInteger("62A32B15517FFFFFFFFFFFFFFFFFF", 16); // 24^25-1
do {
// calculate the Schnorr signature of pid (http://en.wikipedia.org/wiki/Schnorr_signature)
var k = random(7);
//log("k: " + k.toString(16));
var r = xpecc.mul(g, k);
var x = reverse(hexToByte(r.x.toString(16)));
var y = reverse(hexToByte(r.y.toString(16)));
while (x.length < 48) { x = x + '&#65533;'; }
while (y.length < 48) { y = y + '&#65533;'; }
var h = calcSHA1(
String.fromCharCode(pid & 0xff) +
String.fromCharCode((pid >> 8) & 0xff) +
String.fromCharCode((pid >> 16) & 0xff) +
String.fromCharCode((pid >> 24) & 0xff) +
x + y
);
h = hexToByte(h.substr(0, 8));
h = (h.charCodeAt(0) + (h.charCodeAt(1) << 8) + (h.charCodeAt(2) << 16) + (h.charCodeAt(3) << 24)) >>> 4;
h = new BigInteger(h.toString(16), 16);
var s = nbi();
priv.multiplyTo(h, s);
s = s.mod(order);
// private key is inverted, add instead of subtract
s.addTo(k, s);
while (s.compareTo(order) >= 0) { s.subTo(order, s); }
// key = s (56) || h (28) || pid (31)
var key = new BigInteger(pid.toString(16), 16);
key.addTo(h.shiftLeft(31), key);
key.addTo(s.shiftLeft(59), key);
} while (key.compareTo(maxkey) > 0);
var skey = "";
var t;
var i;
var base = new BigInteger("24", 10);
// skey = base 24 of key
for (i = 0; i < 25; i++) {
t = key.divideAndRemainder(base);
key = t[0];
t = t[1];
skey = cset.charAt(parseInt(t.toString(16), 16)) + skey;
}
t = "";
for (i = 0; i < 25; i++) {
t += skey.charAt(i);
if (i != 24 && i % 5 == 4) { t += "-"; }
}
return t;
}
</script>
<body onload='do_generate()'>
<input type="text" size="30" id="key" />
<script type="text/javascript">
function log(m) {
document.getElementById("msg").innerHTML += m + "<br>"
}
function do_generate() {
var key = document.getElementById("key");
key.value = generate();
}
</script>
</body>
</html>
import hashlib
import random
import secrets
# JSON object for BINK data
key_data = {
"p": 22604814143135632990679956684344311209819952803216271952472204855524756275151440456421260165232069708317717961315241,
"a": 1,
"b": 0,
"g": [
10910744922206512781156913169071750153028386884676208947062808346072531411270489432930252839559606812441712224597826,
19170993669917204517491618000619818679152109690172641868349612889930480365274675096509477191800826190959228181870174
],
"pub": [
14399230353963643339712940015954061581064239835926823517419716769613937039346822269422480779920783799484349086780408,
5484731395987446993229594927733430043632089703338918322171291299699820472711849119800714736923107362018017833200634
],
"n": 61760995553426173,
"priv": 37454031876727861
}
p = key_data["p"]
a = key_data["a"]
b = key_data["b"]
B = tuple(key_data["g"])
K = tuple(key_data["pub"])
order = key_data["n"]
private_key = -key_data["priv"] % order
# PID of product key
pid = 756_696969
# Key alphabet
KCHARS = "BCDFGHJKMPQRTVWXY2346789"
def int_to_bytes(n, l=None):
n = int(n)
if not l:
l = (n.bit_length() + 7) // 8
return n.to_bytes(l, byteorder="little")
def encode_pkey(n):
out = ""
while n > 0:
out = KCHARS[n % 24] + out
n //= 24
out = "-".join([out[i:i+5] for i in range(0, len(out), 5)])
return out
def inverse_mod(k, p):
return pow(k, -1, p)
def add_points(P, Q, p, a):
if P is None:
return Q
if Q is None:
return P
if P[0] == Q[0] and (P[1] + Q[1]) % p == 0:
return None
if P != Q:
lam = ((Q[1] - P[1]) * inverse_mod(Q[0] - P[0], p)) % p
else:
lam = ((3 * P[0] * P[0] + a) * inverse_mod(2 * P[1], p)) % p
x = (lam * lam - P[0] - Q[0]) % p
y = (lam * (P[0] - x) - P[1]) % p
return (x, y)
def scalar_mult(k, P, p, a):
R = None
for i in range(384):
if k & (1 << i):
R = add_points(R, P, p, a)
P = add_points(P, P, p, a)
return R
pid <<= 1
while True:
k = secrets.randbelow(p - 1) + 1 # Generate a random k in the range [1, p-1]
r = scalar_mult(k, B, p, a)
x, y = r
md = hashlib.sha1(int_to_bytes(pid, 4) + int_to_bytes(x, 48) + int_to_bytes(y, 48)).digest()
h = int.from_bytes(md[:4], byteorder="little") >> 4
h &= 0xfffffff
s = int(abs((private_key * h + k) % order))
raw_pkey = s << 59 | h << 31 | pid
if raw_pkey >> 96 < 0x40000:
break
print(encode_pkey(raw_pkey))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment