build failed with copy protection enabled
-
@Christoph-Hart Trying to get my head in the unlocker and copy protection. It's hard without documentation. I start by enabling
USE_COPY_PROTECTION
andUSE_SCRIPT_COPY_PROTECTION
but I get: -
I see that it dynamic_cast the frontend proc, does it mean it shouldn't be enabled for Hise but only for frontend build?
-
@ustk You don't need to define those preprocessors when building HISE, just put them in your project_info.xml. HISE itself has a dedicated "development" unlocker class.
So basically the procedure is:
- Define
USE_SCRIPT_COPY_PROTECTION
(add it as ExtraDefinition) (you actually don't need to define the other one, it gets defined automatically when you define. - Create a RSA key file (Tools -> Create RSA Key, then Save to file). This is important and the build process will fail without this step. The public key will be embedded into HISE, the private key must lie on your server that generates the key file.
- Use the script unlocker class and its methods (I'll probably need to make a minimal example project here, as there might be a few things that are not self explanatory), but basically you create an instance, then ask
unlocker.isUnlocked()
and if not, call your server and fetch a key file that you then apply withunlocker.writeKeyFile()
.
- Define
-
@christoph-hart Much clearer, thanks! I'll see if I can get on with the productCheckFunction once the first bit works
-
@ustk You don't need to define that function, the default will just compare the product name against the one in your key file (I removed the version number as default check), so unless you want some custom checks (eg. invalidate license key files with version upgrades, etc), leave that one alone :)
-
@christoph-hart Ok so I get weird things... Maybe you could just add a minimal project in the tutorial repo?
-
@Christoph-Hart would be nice indeed ! +1
-
@Christoph-Hart So I finally got the unlocker to work. I'm not yet ready to create the file server side, so for now I fake it in Hise using a dummy license file.
Though something seems weird with the license system, but chances are it is me not understanding.When a license file is created, it is created from the RSA pair (that is the same for all instances/installation).
So what prevent anyone to share the file? I don't imagine it is the non-crypted part, but the crypted part that should contain customer info. But how do you decrypt it? I tried several thing including online tools using the RSA pair with no luck... -
@ustk The license file embeds a machine identification so it can only be used on the system that created the file. Your responsibility on the server side is to write a method that takes this information and returns a key file. We're using it in the current project and the activation function looks more or less like this:
inline function activationAttempt() { local parameters = {}; parameters.email = Content.getComponent("emailInput").get("text"); parameters.machineID = FileSystem.getSystemId(); parameters.version = Engine.getVersion(); parameters.OS = Engine.getOS(); parameters.productID = "Our Funky Product"; parameters.serial = Content.getComponent("serialInput").get("text"); // Your server must implement this function and either return an error string or the encrypted key file using the same RSA key as embedded into the plugin Server.callWithPOST("activate", parameters, onServerActivate); return false; } function onServerActivate(status, obj) { if(status != 200) { // obj is an error string Console.print(obj); } else { // obj is the key file content unlocked = ul.writeKeyFile(obj); if(unlocked) { Console.print("Nice"); } else { Console.print("SUPER CRITICAL ERROR. WHY"); } } }
The beauty of this is you don't need to decrypt anything manually, this is done all by the system (the public RSA key for decrypting the content gets mangled into the plugin binary).
-
@Christoph-Hart Easier than I thought, so it is already working :)
Well, still the server side to to create the license file, which is not a small rock to move... -
@ustk I've posted a PHP snipped on the JUCE forum which you can use as starting point:
<?php /** PHP script that creates an unlock file for the Tracktion Marketplace Status. */ /** Variables */ $PRODUCT_ID = "YOUR PRODUCT"; $EMAIL = "YOUR EMAIL"; $USER = "YOUR NAME"; $DATE = date("j M Y g:i:sa"); $MACHINE = "BYPASS"; $TIME = dec2hex(round(microtime(true)*1000)); $PRIVATE_KEY_PART_1 = "742ccf57e16f84bb"; // These are stupid 64bit keys for developing, but it should run with big keys too... $PRIVATE_KEY_PART_2 = "ae4337057c0e3f6f"; header('Content-Description: File Transfer'); header('Content-Type: text/plain'); header('Content-Disposition: attachment; filename='.$PRODUCT_ID.'.licence'); header('Content-Transfer-Encoding: UTF-8'); header('Expires: 0'); /** Helper Functions */ function dec2hex($number) { $hexvalues = array('0','1','2','3','4','5','6','7', '8','9','a','b','c','d','e','f'); $hexval = ''; while($number != '0') { $hexval = $hexvalues[bcmod($number,'16')].$hexval; $number = bcdiv($number,'16',0); } return $hexval; } include ('Math/BigInteger.php'); // get this from: phpseclib.sourceforge.net function applyToValue ($message, $key_part1, $key_part2) { $result = new Math_BigInteger(); $zero = new Math_BigInteger(); $value = new Math_BigInteger (strrev ($message), 256); $part1 = new Math_BigInteger ($key_part1, 16); $part2 = new Math_BigInteger ($key_part2, 16); while (! $value->equals ($zero)) { $result = $result->multiply ($part2); list ($value, $remainder) = $value->divide ($part2); $result = $result->add ($remainder->modPow ($part1, $part2)); } return $result->toHex(); } // Create the comment section echo "Keyfile for ", $PRODUCT_ID, "\n"; echo "User: ", $USER, "\n"; echo "Email: ", $EMAIL, "\n"; echo "Machine numbers: ", "\n"; echo "Created: ", $DATE, "\n"; echo "\n"; // Create the XML $dom = new DOMDocument("1.0", "utf-8"); $root = $dom->createElement("key"); $dom->appendChild($root); $root->setAttribute("user", $USER); $root->setAttribute("email", $EMAIL); $root->setAttribute("mach", $MACHINE); $root->setAttribute("app", $PRODUCT_ID); $root->setAttribute("date", $TIME); $XML_STRING = $dom->saveXML(); $ENCRYPTED_XML = "#" . applyToValue($XML_STRING, $PRIVATE_KEY_PART_1, $PRIVATE_KEY_PART_2); $XML_DATA = chunk_split($ENCRYPTED_XML, 70); echo $XML_DATA; ?>
But if anyone is motivated to do a .js version (or whatever server language is the cool kid as we speak), it might be better than this ugly hack.
-
@Christoph-Hart Yeah thanks, I remember you posted this on the Juces forum, thanks!
-
Hey, any of you managed to write working code in node?
I'm not good in backend but I made something like this
import {create} from 'xmlbuilder2' import bigInt from 'big-integer' const PRODUCT_ID = "PRODUCT NAME" const EMAIL = "user@example.com" const USER = "user" const MACHINE = "XXXXXXX" class RSAKey { constructor(key) { const keys = key.split(','); this.k1 = this.toHex(keys[0]) this.k2 = this.toHex(keys[1]) } toHex(str) { let result = ''; for (let i = 0; i < str.length; i++) { result += str.charCodeAt(i).toString(16); } return result; } applyToValue(message) { let result = bigInt.zero const zero = bigInt.zero const value = bigInt(message, 256) const key1 = bigInt(this.k1) const key2 = bigInt(this.k2) result = key2.multiply(result); const {quotient, remainder} = value.divmod(key2) result = result.add(remainder.modPow(key1, key2)) return result.toString(16); } } const root = create({version: '1.0'}) .ele('key') .att("user", USER) .att("email", EMAIL) .att("mach", MACHINE) .att("app", PRODUCT_ID) const xml = root.end() const rsa = new RSAKey('2c69e2b65085061fc4ecc0f48523d1cf8ae55e1ea2aa111cef8d766273d5850539914c0e48ce199904611ec7a6eff1bf167f5aa70bf05e03213945a3538b81fd,6f08b6c7c94c8f4f6c4fe2634cd98c86db3d6b4c96a92ac856e1a7f62195cc8e6b17a6c7c50ee2b8bfb59e7e3dffd752917d328e52019f337488beac3f545b5f'); const result = rsa.applyToValue(rsa.toHex(xml)) console.log(result) process.exit(0)
Everything seems be ok and I can generate key but is much shorten than this fake one generated in hise.
-
@parabuh Have you compared the output with the PHP snippet I posted above? I don't know node.js but I think if you just recreate the PHP thingie you should be able to compare and match the outputs of those two.
It obviously depends on the external libraries for big integers and XML creation that you use and how they handle bit order and whatnot.
-
Hello @ustk
How is this whole method works?
Just want to understand the RSA using a dummy License in the plugin.
I'm trying to create just a a demo to get an understanding of how this whole thing works, but there's no documentation and as well as I'm not getting fulfilling inputs here in the forum.
Seems like this method is more effective than the simple copy protection.
Any help is appreciated -