Proposed copy protection method


#1

Hey folks,

Could you have a quick peek at my proposed method for doing copy protection on my plugin? I wanted something relatively secure without hassling the user too much. It's based on a serial key and was inspired by the various posts found on these forums. If someone could just double check that there's no glaring mistakes that would be awesome.

Below is a link to document describing it.

https://docs.google.com/presentation/d/1-q1ZY4yMvmTU74k-zyibUu1y_4wYT2ERgBnvCOV-gjU/edit?usp=sharing

 

Thanks,

Rob


Uniqueness of SystemStats::getDeviceIdentifiers() for copy protection?
#2

What about MAC spoofing?


#3

I agree with Timur that the MAC address can be hit and miss. On Windows, for example, VirtualBox, VMWare or Cisco's VPN client and others install virtual ethernet drivers with their own associated random MAC addresses.  If your customer installs one of those after they installed your plug-in, the plug-in may think that the MAC address has changed. On Windows it's quite difficult to detect weather the network adapter is real or virtual. OS X's IOKit at least has a "built-in" flag for every network adapter which is false for every adapter except the one on the main board.

 I think the hard disk serial numbers are generally better for copy protection.


#4

A common strategy is to collect a set of different parameters about the machine that are unlikely to change (hard disk serial, CPU clock speed, number of cores, Mac or PC ...) and then to combine them into some kind of custom "Machine ID" string that you can use (instead of your MAC address).


#5

Ok brilliant, thanks for the tips. But apart from that it seems relatively secure? It's not for some super expensive bit of software like photoshop that needs to be encased in a digital fortress. Just enough to make the buyers feel like they bought something and the piraters feel like they pirated something. We can track serial authentications and block ones that have clearly been pirated.


#6

Sounds like a reasonable approach to me.

You may also want to think about whether your licence files will be per-user or for the whole machine, and about your long-term support strategy: for example, what happens if one year later, you find a bug in your authentication code and sometimes it generates the wrong IDs or something. How would you roll out a fix to affected customers without breaking other people's licences?

Also, think about what to do with those people who don't have internet (some recording studios insist on having their production computers offline without internet access, they'll need some authentication method, too, probably involving another computer that does have internet).


#7

Hi Rob,

have you already taken a look at http://klangfreund.github.io/jucedoc/doc/classOnlineUnlockStatus.html ?

It's the copy protection mechanism provided by Juce. It's close to your proposal, but it also incorporates unique machine properties as mentioned above.

 

Here's some additional advice (I learned the hard way):

- Machine ID: Don't use MAC adresses (or if you do, do it really carefully). They come and go, especially with virtual network cards by VirtualBox, VMWare and alike.

- Machine ID: If you use file ID numbers (also done by OnlineUnlockStatus) be aware that e.g. GarageBand uses sandboxing and only provides limited access to the real files (So in GarageBand a plugin might report another ID then in every other host). On Windows, 32bit and 64bit applications also do not always get the same IDs from Windows for the exact same file, depending on its path. Test these cases to be sure that the same user with the same computer does not get different hardware identification tokens in your application.

- Don't put the public key as a plain string in your code (split it and store parts of it in different places, xor it, stuff like these). Otherwise it can quite easy be found via a hex editor in your final binary. A cracker will then replace it with another public key and will provide a custom keygenerator based on the matching private key.

- If you compile for OS X, the symbols are still in the binary, making it much easier for crackers to get along. To remove the symbols use e.g. 'strip -x -S'. There is one backdraw: Stripping the symbols will render the OS X crash report quite useless. Decide for yourself if you want to keep them or not.


#8

One possible obfuscation strategy to get around the OSX symbols issue without stripping the symbols is to rename critical functions to some misleading stuff using macros.


#9

If I understand correctly, getLocalMachineIDs() is not reliable in its current state for challenge/response purposes. Any chance that it is fixed in the future ? Samuel would you be ok to share your implementation ?
Cheers


#10

Actually I don’t like to share my code entirely.

In the attached implementation I have replaced my choice with the home directory of the user. I assume this will work, but please test it yourself in Garage Band and in 32 and 64 bit under Windows. Bear in mind that choosing the home directory gives each user on the same computer a different machine ID. So your software only gets unlocked for the user who unlocks it. It wont be unlocked for all users of the same system.

StringArray CopyProtection::FullProtectionStatus::getLocalMachineIDs()
{
    // Based on TracktionMarketplaceStatus::getLocalMachineIDs()
    
    // First choice for an ID number is a filesystem ID for the user's home
    // directory.
    //
    // A filesystem ID is preferred because the number of network adapters can
    // change (and with this the first MAC address) when a virtualization 
    // application like VirtualBox is used.
    
    StringArray nums;
    
#if JUCE_WINDOWS
    // This file identifier stays the same for 32 and 64 bit applications.
    uint64 num = File::getSpecialLocation (File::userHomeDirectory).getFileIdentifier();
#elif JUCE_MAC
    // The Music folder has been chosen because it is also available when the
    // plugin gets sandboxed - like in GarageBand.
    uint64 num = File ("~").getFileIdentifier();
#elif JUCE_LINUX
    uint64 num = File ("~").getFileIdentifier();
#endif

    if (num != 0)
    {
        nums.add (getEncodedIDString (String::toHexString ((int64) num)));
        return nums;
    }
    
    // ..if that fails, use the MAC addresses..
    Array<MACAddress> addresses;
    MACAddress::findAllAddresses (addresses);
    
    for (int i = 0; i < addresses.size(); ++i)
        nums.add (getEncodedIDString (addresses[i].toString()));
 
    jassert (nums.size() > 0); // failed to create any IDs!
    return nums;
}

#11

Thanks a lot for sharing Samuel !


#12

ok, I read the presentation. Here are some important notes:

One issue is with communication to the server. How does the server know it’s talking to your plugin and not a key generator? Here’s the situation: the code that validates a serial key is sitting on the pirate’s computer. Once you have the code that checks keys it’s simple to randomly generate a set of valid keys and DoS the server looking for valid/non-blacklisted keys. I mean it would be suspicious if you get 1000 different key requests with the same MAC address, but if you’re only blacklisting instead of whitelisting then it would only take 2-3 attempts.

My 2 cents:

~Twitter Rant~
write an app that can read a twitter stream (just one stream): dev.twitter.com/oauth/application-only This is to understand the process of generating an application authentication key. A key that you can REVOKE. The key is used to request a bearer token from the server. A Bearer Token is an access [state] key that expires after 15 minutes. See Section 4.4 of OAuth 2 for Client Credentials Grant

Because you can REVOKE the application key, you can require that users upgrade to the latest version when attempting to validate the purchase. If the application can’t successfully request a bearer token, it can’t communicate with the server license file generator.
~~~~~End Twitter Rant ~~~~~

The Plugin also needs an RSA generated private key to encrypt the communication to the server. This is so the server knows that it’s communicating with a plugin and not with a key generator. Yeah, pirates can crack the application’s private key, but when that happens you simply throw away the public key for that pair and build new app versions with new keys… Just like the Twitter Application Key!

The server should also send a SALT/(NONCE) to the client that should be in all future communications that the client sends the server… Just like a bearer token!

Have the user enter their email address as well as serial number. This effectively switches your server from blacklisting to whitelisting.