PLUSNative: Adding Basic Copy Protection

For a high-level primer on copy-protection, please see our blog post "Product Activation: Fingerprints, Copy Protection, Disconnected Computers".

A major aspect of software licensing is copy protection. This prevents an end-user from activating a license on one system and copying that application to another system to gain additional unauthorized licenses. A machine fingerprint is generated and saved in each license file. This fingerprint consists of several attributes known as system identifiers, which may consist of the network adapter's MAC address, the computer name, or other uniquely identifiable data. Each time an application runs, it generates the fingerprint for the current system and compares it to that stored in the license file. If enough of the individual system identifiers match, it is reasonable to believe it is the same system and the application is authorized to run. Otherwise, the user must reactivate. The system fingerprint is also included in the activation request and sent to SOLO Server. If the product option is configured to "Allow Reactivations on Same Computer" then SOLO Server will look for a previously activated installation with a matching fingerprint, and if found, will allow the system to reactivate.

The following example demonstrates generating the system fingerprint and comparing that to the fingerprint in the license file to determine if the application is authorized to run.

Initializing System Identifiers

There are several built-in system identifier algorithms that can be used to fingerprint a system. The fingerprint is usually only generated on application start-up and gets stored in memory in the API context.

Note

More detailed information on each system identifier can be found in the PLUSNative API documentation under the SK_SystemIdentifierAlgorithm Enumerations.

The following code snippet shows how to initialize the current system's identifiers:

C/C++
//declare variables
SK_ResultCode result = SK_ERROR_NONE;
SK_ApiContext context = NULL;
int count = 0;
int matches = 0;

//initialize API context (usually called during application start-up)
//refer to the Configuring the API Context topic for instructions

//generate the current system identifiers
result = SK_PLUS_SystemIdentifierAlgorithmAddCurrentIdentifiers(context, SK_FLAGS_NONE, SK_SYSTEM_IDENTIFIER_ALGORITHM_NIC, NULL, &count);
result = SK_PLUS_SystemIdentifierAlgorithmAddCurrentIdentifiers(context, SK_FLAGS_NONE, SK_SYSTEM_IDENTIFIER_ALGORITHM_COMPUTER_NAME, NULL, &count);
result = SK_PLUS_SystemIdentifierAlgorithmAddCurrentIdentifiers(context, SK_FLAGS_NONE, SK_SYSTEM_IDENTIFIER_ALGORITHM_HARD_DISK_VOLUME_SERIAL, NULL, &count);

The code snippet above omits many of the parameters necessary to initialize the API context (the context variable). Refer to the Configuring the API Context topic for instructions and a complete example of calling the SK_ApiContextInitialize function.

Basic System Identifier Validation

Once the license file has been loaded, the current system identifiers can be compared to that in the license file.

C/C++
//compare the current system identifiers to that in the license file
result = SK_PLUS_SystemIdentifierCompare(context, SK_FLAGS_NONE, NULL, &count, &matches);

//make sure all system identifiers match
if (matches < count)
{
//the current system is not authorized
...
}

The code above checks to make sure the system's current identifiers are an exact match to the identifiers authorized during activation. If the CurrentIdentifiers do not match the AuthorizedIdentifiers in the license file exactly, then the license is rejected by the example above.

The SK_PLUS_SystemIdentifierCompare function's type argument can be used to filter out the comparison to a certain system identifier algorithm type. This can be useful if you want to implement a more granular system identification and matching algorithm.

Comparing System Identifiers with Fuzzy-Matching

It is possible to customize your validation logic to allow for some amount of acceptable change/variation by comparing the hash values of certain types of CurrentIdentifiers against the hash values of the same type of AuthorizedIdentifiers.

C/C++
//compare the current HardDiskVolumeSerialIdentifiers to that in the license file
result = SK_PLUS_SystemIdentifierCompare(context, SK_FLAGS_NONE, "HardDiskVolumeSerialIdentifier", &count, &matches);

//make sure at least one system identifier matches
if (matches < 1)
{
//the current system is not authorized
...
}

The SK_PLUS_SystemIdentifierCompare function's type argument can be used to filter out the comparison to a certain system identifier algorithm type. This can be useful if you want to implement a more granular system identification and matching algorithm.

Custom System Identifier Algorithms

Depending on the licensing requirements, the built-in system identifier algorithms might not be suitable. For example, you may wish to bind the license to a particular user by implementing a username identifier algorithm.

The following code snippet shows how to initialize a custom system identifier:

C/C++
//declare variables
SK_ResultCode result = SK_ERROR_NONE;
SK_ApiContext context = NULL;
char *value = ...;

//initialize API context (usually called during application start-up)
//refer to the Configuring the API Context topic for instructions

//generate the current system identifiers
result = SK_PLUS_SystemIdentifierAddCurrentIdentifier(context, SK_FLAGS_NONE, "CustomIdentifier1", "CustomIdentifier", value);

The value variable in the above code snippet would be initialized to a plain-text string value consisting of the custom system identifier data. The SK_PLUS_SystemIdentifierAddCurrentIdentifier function will hash the identifier data so that no user-identifiable data is transmitted or stored to avoid privacy concerns.

Important

These basic source code examples omit important and necessary error checking for the sake of clarity. Many of the functions used could fail for one reason or another, and it is important that you make sure each function call succeeds before passing the result from one function to another. Otherwise, you may not be able to tell exactly where a problem originated if and when one occurs.