PreEmptive Protection - Dotfuscator 4.31
User Guide

Shelf Life Check

A Shelf Life Check is a type of Check that detects if an application is being run after a certain date. This gives the application a shelf life - a limited period of time for which it can be run.

Shelf Life Checks are particularly useful for beta or evaluation software. If the user attempts to run the application beyond the expiration date, a Shelf Life Check can detect this and react by sending incident telemetry, notifying the application, and causing the application to exit. In other words, a Shelf Life Check detects and reacts to unauthorized use of your application based on the current date.

Configuring Shelf Life Checks

To have Dotfuscator inject Shelf Life Checks into your application, you must first obtain a Shelf Life Activation Key from PreEmptive Solutions.

Once you have an Activation Key, enable code injection. Then, configure the Checks via the user interface, or by annotating your source code with ShelfLifeCheckAttribute. Both of these methods allow you to specify various properties that determine how the Check operates; for a full listing, see the ShelfLifeCheckAttribute section of the Check Attributes page.

Activation Key

A Shelf Life Activation Key is a data file that is required to inject Shelf Life Checks (and Sign-of-Life messaging). To obtain an Activation Key, contact PreEmptive Solutions.

Once issued, you must specify the path to the Activation Key in each Shelf Life Check's ActivationKeyFile property. We recommend keeping a copy of the Activation Key on each build machine.

The Activation Key is only needed when Dotfuscator processes the application. The application does not need to access the Activation Key at runtime, so it should not be distributed outside of the development organization.

Tokens

A Shelf Life Token is a file containing information about an application's shelf life, such as the expiration date. Rather than embedding the expiration information directly into the application, Dotfuscator generates a Token containing this information. At runtime, the Shelf Life Check uses the Token to determine if the application has expired.

In Professional Edition, there are two ways of providing the Token to a Shelf Life Check:

  1. Embed the Token with the Check in the application. This is more convenient, but once the application has shipped, the Token (and therefore the expiration date) cannot be changed or customized.

  2. Generate the Token separately, then provide it to the Check at runtime. For instance, if you provide the Token to the application using a web service, you can change the Token without redistributing the application, allowing for expiration date extensions and per-user expiration dates.

In either scenario, you may also ensure the authenticity of a Token by using a public / private key pair. The Token includes the public key; after the Token is generated, it is signed with the private key. At runtime, the Shelf Life Check verifies the signature matches the public key. This ensures that the Token has not been modified, preventing certain kinds of expiration date manipulation.

Embedding a Token with the Check

You can have Dotfuscator automatically generate and embed a Token with a Shelf Life Check. This is the simpler of the two options.

To do this, when configuring the Check, specify values for the Check's ExpirationDate and (optionally) WarningDate properties. If you want the generated token to be signed, also specify values for PrivateKeyFile and, if needed, PrivateKeyFilePassword. For details on these and other properties of Shelf Life Checks, see the ShelfLifeCheckAttribute section of the Check Attributes page.

Generating a Token Separately

You can instead generate the Token manually and have the application itself provide the Token to the Shelf Life Check at runtime. This allows you to, for instance, store the Token in a database or web service that the application accesses. You can change the Token provided without redistributing the application, allowing for expiration date extensions and per-user expiration dates.

To generate a Token manually, see the Generate New Shelf Life Token section for details.

To have the Check retrieve the Token dynamically at runtime, when configuring the Check, specify values for the ShelfLifeTokenSource properties. These values specify a source in the application code that provides the Token as a string. For details on these and other properties of Shelf Life Checks, see the ShelfLifeCheckAttribute section of the Check Attributes page.

Expiration and Warning Dates

Each Shelf Life Token has an associated expiration date. When a Shelf Life Check using the Token runs, it compares the expiration date to the current time (using an algorithm more complex than just checking the system time). If the expiration date is in the past, the Check determines the application to be expired. What happens as a result of the application being expired depends on the application notification properties.

Each Shelf Life Token may optionally specify a warning date. When a Shelf Life Check using the Token runs, if the warning date is in the past, the Check determines the application to be in the warning period. What happens as a result of the application being in the warning period depends on the application notification properties.

For a Token embedded with a Shelf Life Check, the expiration and warning dates are specified by the Check's ExpirationDate and WarningDate properties, respectively. For Tokens provided at runtime, these dates are specified when the Token is generated.

Dates for a Shelf Life Tokens are configured as strings, in one of two formats:

  • An absolute date, specified in the form YYYY-MM-DD.

  • A date relative to the date Dotfuscator generated the Token, specified as an integer indicating the number of days.

For instance, if Dotfuscator generates the Token on August 1, 2017, and the application should expire on August 31, 2017, the expiration date can be written as either 2017-08-31 or 30.

Check Telemetry

Shelf Life Checks can send telemetry when the application is expired or in the warning period. For more information, see the Check Telemetry page.

Application Notification

Application Notification with Shelf Life Checks differs somewhat from that of other Check types.

Notification Kinds

While other Checks provide only one notification, saying whether the Check found an unauthorized application state or not, Shelf Life Checks can have two notifications:

  • Expiration Notification notifies the application code whether or not the application has expired (is being run after its expiration date).

    • This notification will always be used when a Check runs, including if the application is expired, is in the warning period, or is neither.

    • The following properties of a Shelf Life Check determine how that Check notifies the application whether or not it is expired:

      • ExpirationNotificationSinkElement
      • ExpirationNotificationSinkName
      • ExpirationNotificationSinkOwner
    • These properties specify a sink in the application code that receives a bool value: true if the application is expired, and false otherwise.

  • Warning Notification notifies the application code whether or not the application is in the warning period (is being run after its warning date).

    • This notification is skipped if no warning date is specified for the Check, but otherwise will always be used when a Check runs, just like the expiration notification.

    • The following properties of a Shelf Life Check determine how that Check notifies the application whether or not it is in the warning period:

      • WarningNotificationSinkElement
      • WarningNotificationSinkName
      • WarningNotificationSinkOwner
    • These properties specify a sink in the application code that receives a bool value: true if the application is in the warning state, and false otherwise.

Using a String Sink

As an alternative to the bool sinks specified in the previous subsection, either notification may instead use a Method, Delegate, or MethodArgument sink where the method or delegate has the signature void(string, string). In this case, the Shelf Life Check calls the method or delegate with the following two parameters:

  • Warning Date (or null if no warning date was specified)
  • Expiration Date

This allows the application code to be more specific in its reaction, such as by displaying the expiration date to the user.

Like a bool sink, a string sink is called even if the application is not in the warning period nor has expired. However, a sink for a warning notification will never be called if no warning date is specified.

Note that both expiration and warning notifications call string sinks with the same arguments, so specifying both as string sinks is redundant. You can use one of the two notifications with a string sink, and the other with a bool sink.

For example, consider an evaluation software with the following Shelf Life Check and code snippet:

internal class ShelfLifeApplicationNotificationExample
{
    private bool applicationHasExpired; // set by Shelf Life Check

    internal void SetupApplication()
    {
        LoadDataFiles();
        EnableFreeFeatures();

        if (!applicationHasExpired)
        {
            EnablePaidFeatures();
        }
    }

    private void LoadDataFiles() // location of Shelf Life Check
    {
        Console.WriteLine("Simulating startup logic...");
    }

    // EnableFreeFeatures() and EnablePaidFeatures() omitted for brevity

    // called by Shelf Life Check
    private void LogEvaluationNotice(string warnDateString, string expireDateString)
    {
        // Use arguments directly as strings...
        Console.WriteLine($"This evaluation software has an expiration date of {expireDateString}.");
        Console.WriteLine("To purchase the full version, please contact <sales@example.com>.");

        // ...or parse to DateTime objects for calculations.
        DateTime warnDate = DateTime.Parse(warnDateString);
        DateTime expireDate = DateTime.Parse(expireDateString);

        int daysUntilWarn = warnDate.Subtract(DateTime.Today).Days;
        int daysUntilExpire = expireDate.Subtract(DateTime.Today).Days;

        if (daysUntilExpire <= 0)
        {
            Console.WriteLine("WARNING: The software has expired. Paid features are no longer available.");
        }
        else if (daysUntilWarn <= 0)
        {
            Console.WriteLine($"WARNING: The software will expire in {daysUntilExpire} days.");
        }
    }
}

After Dotfuscator's injection, when other application code calls SetupApplication():

  1. SetupApplication() calls LoadDataFiles().

  2. Before LoadDataFiles() executes, the Shelf Life Check runs:

    • The Check determines if the application is expired, in the warning period, or neither.

    • As its warning notification, the Check calls the LogEvaluationNotice(string, string) method, providing the warning date and expiration date as strings.

    • The LogEvaluationNotice(string, string) writes information about the evaluation software to the console, then returns control to the Check.

    • As its expiration notification, the Check sets the applicationHasExpired field to true if the application has expired, and false otherwise.

    • The Check finishes running and returns control to the LoadDataFiles() method.

  3. The LoadDataFiles() method runs, then returns control to SetupApplication().

  4. SetupApplication() calls EnableFreeFeatures(), which then returns control to SetupApplication().

  5. If applicationHasExpired is false (i.e., the evaluation has not expired), SetupApplication() calls EnablePaidFeatures(), which then returns control to SetupApplication().

  6. SetupApplication() returns control to its caller.

Exit Behavior

Shelf Life Checks do not support Check Actions. A configuration equivalent to the use of the "Exit" Action can be done by setting the ExpirationNotificationSinkElement property of the Check to DefaultAction.

When a Shelf Life Check with this configuration detects expired application usage, it will cause the application to exit immediately with exit code 0.

Supported Application Types

Dotfuscator can inject Shelf Life Checks into all .NET assemblies except for the following:

  • Managed C++ assemblies containing native and managed code
  • Multi-module assemblies
  • Silverlight assemblies
  • Windows Phone assemblies
  • WinRT assemblies
  • Xamarin assemblies
  • Unity assemblies

Testing

To test how the Shelf Life Checks injected into your application react to expired application usage or usage during the warning period, the simplest method is to temporarily configure the Checks with the expiration dates and warning dates in the past. Be sure to exercise all locations of your Shelf Life Checks. Once you have tested that the application acts as it should when it is expired or in the warning period, you will need to change the dates back to their intended values before building the project for distribution.

Dotfuscator Version 4.31.0.6091. Copyright © 2017 PreEmptive Solutions, LLC