Created
March 1, 2019 18:04
-
-
Save undavide/0a23024ac85fa560593597b26166b81b to your computer and use it in GitHub Desktop.
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
/** | |
* Generates a Key Byte | |
* @param {32bit integer} seed e.g. 0xA2791717 | |
* @param {8bit integer} a | |
* @param {8bit integer} b | |
* @param {8bit integer} c | |
* @return {8bit hex string} | |
*/ | |
function PKV_GetKeyByte(seed, a, b, c) { | |
var result; | |
a = a%25; | |
b = b%3; | |
if (a%2 == 0) { | |
result = ((seed >> a) & 0x000000FF) ^ ((seed >> b) | c); | |
} else { | |
result = ((seed >> a) & 0x000000FF) ^ ((seed >> b) & c); | |
} | |
result = result & 0xFF; /* mask 255 values! */ | |
return result.toString(16).toUpperCase(); | |
} | |
/** | |
* Generates the checksum | |
* @param {string} s Seed + Keys | |
* @return {16bit hex string} | |
*/ | |
function PKV_GetChecksum(s) { | |
var left, /* unsigned 16bit integer */ | |
right, /* unsigned 16bit integer */ | |
sum; /* unsigned 16bit integer */ | |
left = 0x0056; /* 101 */ | |
right = 0x00AF; /* 175 */ | |
if (s.length) { | |
for (var i = 0; i < s.length; i++) { | |
right += s.charCodeAt(i); | |
if (right > 0x00FF) { | |
right -= 0x00FF; | |
} | |
left += right; | |
if (left > 0x00FF) { | |
left -= 0x00FF; | |
} | |
}; | |
} | |
sum = (left << 8) + right; | |
return sum.toString(16); | |
} | |
/** | |
* Generates a serial number | |
* @param {32bit integer} seed | |
* @return {20 chars String} | |
*/ | |
function PKV_MakeKey(seed) { | |
var keyBytes = [], | |
result = "", | |
serial; | |
/* Fill keyBytes with values derived from Seed. | |
The parameters used here must be extactly the same | |
as the ones used in the PKV_CheckKey function. | |
A real key system should use more than four bytes. */ | |
keyBytes[0] = PKV_GetKeyByte(seed, 24, 3, 200); | |
keyBytes[1] = PKV_GetKeyByte(seed, 10, 0, 56); | |
keyBytes[2] = PKV_GetKeyByte(seed, 1, 2, 91); | |
keyBytes[3] = PKV_GetKeyByte(seed, 7, 1, 100); | |
/* the key string begins with a hexidecimal string of the seed */ | |
result += seed.toString(16).toUpperCase(); | |
/* then is followed by hexidecimal strings of each byte in the key */ | |
for (var i = 0; i < keyBytes.length; i++) { | |
result += keyBytes[i].toUpperCase(); | |
}; | |
/* Add checksum to key string */ | |
result += PKV_GetChecksum(result).toUpperCase(); | |
/* Add some hyphens to make it easier to type */ | |
serial = result.split(""); | |
var j = serial.length - 4; | |
while (j > 1) { | |
serial.splice(j, 0, "-"); | |
j = j -4; | |
} | |
return serial.join(""); | |
} | |
var KEY_GOOD = 0, | |
KEY_INVALID = 1, | |
KEY_BLACKLISTED = 2, | |
KEY_PHONY = 3, | |
BL = [ | |
"11111111" | |
]; | |
/** | |
* Checks if the checksum in the serial is correct | |
* @param {String} key The 20 chars serial | |
* @return {boolean} | |
*/ | |
function PKV_CheckKeyChecksum(key) { | |
var s, c, | |
result = false; | |
s = key.replace(/-/g,"").toUpperCase(); | |
if (s.length !== 20) { return result } | |
c = s.slice(s.length - 4); | |
s = s.slice(0, 16); | |
result = ( c === PKV_GetChecksum(s).toUpperCase()); | |
return result; | |
} | |
/** | |
* Check if the Serial is valid | |
* @param {String} s 20 chars serial | |
* @return {integer} | |
* KEY_GOOD = 0, | |
* KEY_INVALID = 1, | |
* KEY_BLACKLISTED = 2, | |
* KEY_PHONY = 3, | |
*/ | |
function PKV_CheckKey(s){ | |
var key, /* String */ | |
kb, /* String */ | |
seed, /* Int32 */ | |
b, /* Byte */ | |
result = KEY_INVALID; | |
if (!PKV_CheckKeyChecksum(s)) { | |
/* bad checksum or wrong number of characters */ | |
return result; | |
} | |
/* remove cosmetic hypens and normalize case */ | |
key = s.replace(/-/g,"").toUpperCase(); | |
/* test against blacklist */ | |
var bl = BL.length | |
if (bl) { | |
for (var i = 0; i < bl; i++) { | |
if( key.indexOf(BL[i].toUpperCase()) > -1 ) { | |
result = KEY_BLACKLISTED; | |
return result; | |
} | |
} | |
} | |
/* At this point, the key is either valid or forged, | |
* because a forged key can have a valid checksum. | |
* We now test the "bytes" of the key to determine if it is | |
* actually valid. | |
* When building your release application, use conditional defines | |
* or comment out most of the byte checks! This is the heart | |
* of the partial key verification system. By not compiling in | |
* each check, there is no way for someone to build a keygen that | |
* will produce valid keys. If an invalid keygen is released, you | |
* simply change which byte checks are compiled in, and any serial | |
* number built with the fake keygen no longer works. | |
* Note that the parameters used for PKV_GetKeyByte calls MUST | |
* MATCH the values that PKV_MakeKey uses to make the key in the | |
* first place! | |
*/ | |
result = KEY_PHONY; | |
/* extract the Seed from the supplied key string */ | |
seed = key.substr(0,8); | |
/* test whether the seed is a valid HEX */ | |
if (seed.match(/[A-F0-9]{8}/) === null) { return result; } | |
/* Keys test - never test them all! */ | |
/* Testing K1 */ | |
kb = key.substr(8,2); | |
b = PKV_GetKeyByte(parseInt(seed, 16), 24, 3, 200); | |
if (kb !== b.toUpperCase()) { return result; } | |
/* Testing K2 */ | |
kb = key.substr(10,2); | |
b = PKV_GetKeyByte(parseInt(seed, 16), 10, 0, 56); | |
if (kb !== b.toUpperCase()) { return result; } | |
/* Testing K3 */ | |
kb = key.substr(12,2); | |
b = PKV_GetKeyByte(parseInt(seed, 16), 1, 2, 91); | |
if (kb !== b.toUpperCase()) { return result; } | |
/* Testing K4 */ | |
kb = key.substr(14,2); | |
b = PKV_GetKeyByte(parseInt(seed, 16), 7, 1, 100); | |
if (kb !== b.toUpperCase()) { return result; } | |
/* If we get this far, then it means the key is either good, or was made | |
* with a keygen derived from "this" release. */ | |
result = KEY_GOOD; | |
return result; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment