Copy protection doubts

Hi folks!

I’m working on a simple copy protection solution for my plugins. I read some posts on the forum but couldn’t find answers to some of my doubts.

I really like the approach used by Valhalla DSP with no internet connection and server required for the process.

The idea is to generate a unique XML key file at checkout (if that’s even possible on Shopify) using the user data and a serial key that follows some criteria. The unique key is then encrypted using a RSA public key and is provided to the user in the XML key file.

At initialization, the plugin checks for the XML key file in a fixed location (somewhere in Application Support for Mac, and the equivalent for Windows). If the XML key file is found, it’s decrypted using the corresponding RSA private key stored in the plugin and, if the serial key follows the aforementioned criteria, hence it’s valid, the plugin is unlocked.

If the XML key file is not found, the user is asked to provide it. If the file is correctly provided, and the serial key follows the correct criteria, the plugin copies the XML key file to the same fixed location for later use and it unlocks itself.

If the process fails, the plugin goes in bypass mode.

I have some doubts on how to implement this:

  1. Where should I call the unlocking function from? I want to avoid the AudioProcessor constructor to not block the DAW when scanning the plugins and I also want to avoid prepareToPlay() for obvious reasons. VST3 has the initialize() method for such functionalities.

  2. Where should the RSA private key be stored in the plugin for it to be safe? BinaryData?

  3. How can I create such pop-up from the AudioProcessor before the plugin UI is opened?

Thank you!

My question is: what would a band of 5 members stop to use all the same XML file on their computers?
And those 5 to share it with their other 5 band mates, if they play in several bands?

IMHO you need to tie the XML content to some property of the machine to recognise it again.
If you want to do it offline, you can add a challenge export putton that returns a hash of e.g. the system ID which is then embedded by the server into the license file. You can now transport those files via USB stick from and to the machine in question.

Thanks for answer!

For the size of the operations, anything more complicated than this is not worth it. I prefer to invest time and resources in building a decent product base for now. Crackers crack PACE, so there is no point in going crazy about copy protection, at least for now.

The plugin UI will show the name, family name and email of the user that registered the product, and unlimited activations are possible.

Other plugin manufacturers built a sustainable business with the same process and I’d like to think an eventual user base won’t be made of assholes, otherwise I wouldn’t even make plugins, if not for personal use.

2 Likes

Fair enough, that is a fine approach. The choice is yours.

About 1: since it is just reading a local file, it shouldn’t be heavy, so it doesn’t mattter too much. I would go for prepareToPlay, as it is not a time critical function. I agree with you not to use the constructor.
An alternative is to use Timer::callAfterDelay in connection with a WeakReference, so it doesn’t crash if the class to be unlocked was deleted in the meantime.

About 2: BinaryData is fine, it is the same as putting it somewhere as string literal. It is just a static array of bytes embedded in the binary.
There is also a tool, I don’t know how effective it is, but mentioning in Projucer the “createObfuscatedString”:

 Projucer --obfuscated-string-code string_to_obfuscate
    Generates a C++ function which returns the given string, but in an obfuscated way.

About 3: I would advice against, since if several plugins do that, even with AlwaysOnTop flag, you create a mess.

2 Likes

Thank you!

  1. It’s also doing the decryption, but I can write some basic logic so that the plugin doesn’t keep trying to unlock itself every time the prepareToPlay is called. So there is no equivalent to the VST3 initialize in JUCE?

  2. Great, I’ll do that.

  3. Yeah, if many plugins do that it might not be optimal, but the pop-up should activate only when the plugin is dropped on the track. So it should be ok. In any case, how would I go about creating that pop-up? Is there a dedicated JUCE class for it?

Please correct me if I’m wrong but I’m pretty sure this is the wrong way round. iirc you can easily derive the public key from the private key but not the other way round, so if you deliver the private key in the plugin it’s game over as then the crackers can just create a keygen rather than trying to find all the places you are doing copy protection checks.

I think in the reverse it’s called signing and verifying instead of encrypting/decrypting, which is what you really want to do; the plugin is verifying that the key was generated by the proper authority.

5 Likes

I just want to add that as you’ll place the public key in the plugin binary, an attacker might simply replace the public key with one whose private key he controls- then he could issue valid XMLs on his own.

Valid point, but something that could either be obfuscated and/or checked that it’s the shipped public key in various different places.

However this is all just cat & mouse games, and it ends up at the age old argument that trying too hard to enforce copy protection is more or less a waste of time that could be better used to improve the products or make new ones.

I went with a very simple scheme that can be easily defeated with a modicum of effort on the behalf of the crackers, but I’m just looking to discourage the casual pirates as well as not make it onerous for legit customers.

1 Like

The same is true if it’s the private key that’s embedded in the plug-in binary: the attacker can replace it with the private key that he owns, and again issue valid XMLs.

I second @asimilon : it’s the public key the one that should be embedded in the plug-in because, well, that’s the one being distributed to the public. Then the private key is kept private.

To mitigate the key replacement problem, the plug-in may include some sort of background validation against a server “at home” if internet is available. You could either check the integrity of the binary as a whole, or of the XML files. If it doesn’t match any of the XML files you generated, it’s a forged one.

So the protection scheme still works without connection, but if there’s internet available you have some more guarantee that users are legit.
For those that replace the key in the binary and keep it always offline, I don’t think there’s much you can do, and in my experience they’re not “lost sales”: if they couldn’t use your product for free, they’d turn to use something else instead.

1 Like

Exactly, I just wanted to make clear what the limitations of the approach are!

As to offline-support: We are using a hybrid approach that needs in internet connection only once for activation. When activating the plugin a machine id (see the recent JUCE commits) is generated and sent to the server, and will be embedded in the licence file that is generated and signed by the server.
This way the plugin can be used offline after activation, but the licence is also restricted to one machine.

Edit: Also, as others have pointed out, keep your private key private at all times!

2 Likes

Please correct me if I’m wrong but I’m pretty sure this is the wrong way round.

I second @asimilon : it’s the public key the one that should be embedded in the plug-in because, well, that’s the one being distributed to the public. Then the private key is kept private.

Edit: Also, as others have pointed out, keep your private key private at all times!

I agree it might seem backwards, but in RSA the private key is used to de-cypher, and the public key to encrypt a message, right? The other way around won’t be effective since anyone could de-cypher the message using the public key.

In my case, the RSA is only used to encrypt the unique serial key needed to unlock the plugin. Even if an attacker manages to de-cypher the message, he still needs to figure out the criteria I’m using to generate new valid unique serial keys. (I guess it can still be done with some reverse engineering of the plugin, but at this point the attacker deserves to have the copy for free haha).

The criteria I mentioned to validate a unique serial key might also be RSA used in the sign/verification configuration. I haven’t decided yet.

I just want something simple that’s not a pain for the user and doesn’t require a server. I’m not even sure I’ll make any profit out of this, I can’t afford to keep up a server for a couple of users at a pure loss.

Said so, anyone has any idea on how to create the pop-up in point 3.?

Do not embed the private key in a binary or ever disclose it to anyone. Period. If you do that then you may as well not encrypt anything at all because with the private key the attacker can generate new ciphertext (extracting the key from a binary is pretty trivial). That means a key-gen will exist pretty quickly, and a cracker can download and use your legitimate binary (the worst kind of attack because then they don’t even have to have any doubt about whether a virus is being delivered along with their software installer or wonder if the cracker broke something / missed a check).

It is only ‘safe’ to embed the public key, and then use that to verify the authenticity of a signed message (i.e. a license file generated on your server signed with the private key). An attacker cannot forge anything with the public key. This is how all symmetric encryption should be used - you only ever disclose a public key or whatever you’re doing is fairly pointless. Private keys should be kept private otherwise the whole security model it affords is broken.

Although the entire conversation is kind of irrelevant, because an attacker can just change the public key in your binary and distribute a license signed with their own private key and your binary will end up with their associated public key in it. Unless you fancy the cat and mouse game of verifying full or partial binaries, or other associated games which ultimately fail as crackers are constantly under-estimated, which you can play until you’re blue in the face.

Use iLok, or just employ a simple key signature check as whatever you do is going to be cracked as soon as you end up on any competent team’s radar and spend your time making a better product. It will take them far longer to crack an iLok protected binary unless you’re very unlucky and release a product just as a few generation Pace hack lands dragging you into the net.

4 Likes

If all you need is an OK/Cancel dialog box, you can conjure one up using NativeMessageBox.

There are other fancier options, which would allow you to style the window however you want, but that is probably the quickest way to try out the idea.

This.

You should really listen to this. The scheme you use doesn’t make sense. You use it like it’s some form of end to end encryption. e.g. Sender (anyone) encrypts with the public key of receiver (authority).
The receiver (authority) can then decrypt with its private key.

This is not the case here. You probably want to use a signature scheme. Then make the signature check bullet proof.

In any case you should at least make it necessary that an attacker has to tamper with your plugin binary. So at least include a unique device identifier and integrity check. Otherwise it’s as easy as copying a file to a directory.

Also be aware! - Runtime debuggers and program flow analyzers exist. Monitoring file IO will quickly identify when your license file is loaded and where it is in memory. So make any checks non trivial and don’t use fixed memory locations. Another encryption on top (flow protection) is much more complicated to crack than any “code obfuscation”.

Got it! As I mentioned, I was thinking of using RSA as you suggested (or another verification scheme) to validate the unique key/confirm its authenticity. It’s what I described as following some criteria. The use of RSA I mentioned was just an additional security step, added simply to encrypt the whole unique key. It could be a hash instead. Just something to not have the unique key in plain text in the XML key file stored in Application Support. So that there is a step more, albeit an easy one to crack, before a keygen is generated.

Use iLok, or just employ a simple key signature check as whatever you do is going to be cracked as soon as you end up on any competent team’s radar and spend your time making a better product

I’ll skip iLok, but I totally agree on the second point. No need to go crazy about it, there is no point to it.
One could argue that iLok, even if harder to crack, is out there and much more worth it. Certainly more effort is spent cracking it, and, when it happens, a patch could take long to come out.

Anyway, thanks for the answer. It was very insightful!

2 Likes

Yeah, nothing crazy. I just need to be able to load a file selector from it when clicking on OK. For now, I don’t care about its look.

Thank you!

In any case you should at least make it necessary that an attacker has to tamper with your plugin binary. So at least include a unique device identifier and integrity check. Otherwise it’s as easy as copying a file to a directory.

I’m not sure how to achieve this. Do you have any example? I’m not interested in limiting the license to the machine the plugin it’s activated on, so I’m ok with the copy and paste if the user who shared the license is ok with having his name, family name and email on other users machines.

Another encryption on top (flow protection) is much more complicated to crack than any “code obfuscation”.

I’ll look into this, thank you!

Is this an assumption of yours, or do you have serious proof that PACE gets cracked?
This may be interesting “news” for paying PACE customers

I know nothing more than a quick Google search can reveal. I wouldn’t consider it serious proof.

I know nothing more than a quick Google search can reveal. I wouldn’t consider it serious proof.