One of the first
challenges of any good software company begins with protecting their
investment. Software license management offers a way to control access to
your software while allowing you to license various features without the
costs of re-distribution.
The goal of any good
software license management process is to restrict use of an application and
prevent theft or exploited use as well as meet the following operational
requirements:
- Restriction on use
of an executable (so as to permit LOW RISK evaluations to happen and
encourage these users to UPGRADE to full priced licensed releases).
- Linkage between an
executable, the order (i.e. the customer and $), and some restriction to
prevent sharing of licenses as it gives up the identity of the original
buyer.
- The ability to
simply swap out a license file to increase functionality (no additional
install required).
A fairly easy way to meet
these requirements without paying a fortune in development costs is to use a standalone license file model as described
below. The following model uses order number, encryption, product release
information, and some basic feature keying to accomplish the goals above. A
more comprehensive variant of this method would implement a key value pair
system for keying zero or more product features within the license file
while making this system infinitely more flexible. Too keep things simple,
the following model uses a fixed delimiter as a means of allowing the
license file to key multiple aspects of the application but as just
mentioned this is inferior to using key value pairs.
Following the simple model, the license file MUST be named as follows:
<product name>_<order
number>.lic
- Product name links the file name with the internal name given to
the application it is attempting to license. Note this should have
little (if any) to do with the actual name of the application file
name. Such that if the application file name was renamed, it would
still continue to use the same license file so such renaming would
have no impact on which license file the application looks for.
- The order number links the customer with the product name such
that if the user of a given application shares his license file with
someone their order number goes with it.
- Finally, the ".lic" extension provides an obvious indicator of
what the file does.
Inside the file are a
number of fields - each with a very specific purpose:
<random number>_<product name>:<order
number>|<major version>;<restriction>
For example: xyzapp_2TG82317RK6513331.lic
would contain the following information
430599247240560_ xyzapp:
2TG82317RK6513331|1.x;0
For example:
- Random Number: 430599247240560 (text string less than 20 characters
' allows each
license to be represented by a different string even though they are
equivalent)
- Product Name: xyzapp(text string less than 20 characters)
- Order Number: 2TG82317RK6513331 (text string less than 20 characters)
- Major Version: 1.x
(three character sting that indicates the major version)
- Restriction: 0
(7 digital integers ' 0=no restrictions, -1=prevent all functions, or some
positive number specifying a limitation such as number of clients)
This license file is
created by the website during the sales process. The buyer, must download
and install their license for any applications to work. Installation could
be as easy as moving the file to the same directory as the application or as
sophisticated as loading a registry key or running some application to
download and install the license file to the proper location on the file
system. Each executable or obvious utility should require a license to
operate correctly. The exception might be executables for data
manipulation or some other non-intended use.
How to process the
license file:
- Program will look
for a license a (.lic) file beginning with its same name (e.g. xyzapp will
look for a license file beginning with xyzapp). If a license file DOES NOT
exist, the executable will terminate with: Program Aborted: License
File MISSING ' Error#001'. If multiple xyzapp license files exist, it will
grab the first one it finds (or alternatively the one with the most
recent creation date). Ideally there should not be multiples but in the
case of an upgrade from evaluation to full release, there could be two
files.
- Upon selecting the
file, store the full filename and parse out the program name and the
order number for future validation use.
- Read in the
contents of the file and decrypt using the shared key:
my $cipher_key =
"0123456789ABCDEF"; # Exactly 8 bytes
- Parse out the
contents of the decrypted string using the delimiters described above '
the delimiters are intentionally not all the same but will appear in the
order as described earlier into their associated variables.
Alternatively, if key value pairs were used that would eliminate the
need for delimiters.
- First TEST: check
to see that the program name and order number in the
decrypted string are same as the filename. If they are NOT equal, exit
executable with: 'Program Aborted: License Invalid ' Error#101'. If test
is successful, the license can be interpreted further'
- Second TEST: check
to see if the version of the program is less than or equal to the
version in the license. This requires that the version of the program be
hard coded and available at the time this license test is performed. If
the version of the executable is 1.12 and the license is 1.x, the
license should be interpreted as being valid. If the version of the
executable is 2.11 and the license file version is 1.x then exit
executable with: 'Program Aborted: License Invalid ' Error#201'.
- If the license
file passes both tests, it should be interpreted as being VALID, the last
step is to see what if any restrictions on executable operation to
apply. This field can be applied with varying levels of sophistication
or simplification. Basically there are three different levels:
- "-1" To allow the restriction of operation. Meaning, that if
someone obtains this license, it won't work. As such, if the executable
finds such a file, it will exit the program with the following: 'Program
Aborted: License Invalid ' Error#301'.
- "0" To allow unrestricted use of all the features of an
application.
- "1" or some other number like 50000 To allow some level of
restriction of the executable. How this is implemented may vary. For
example, it could be that any positive integer would limit transactions to
10. Or one could just take the number in the license and set the transaction
limit to that number such as 50000 or the maximum if this number exceeds the
maximum.
Included below is the
code for two perl programs that encrypt or decrypt a file using DES and the
cipher key mentioned above.
[lm-encrypt.pl]
my $cipher_key =
"1F492C7E80AD6B53"; # Exactly 8 bytes
my $filename =
"memu_2TG82317RK6513331";
my $string;
open F,
"$filename\.txt";
my @raw = <F>;
close F;
foreach (@raw) {
chomp;
next if ($_ eq '');
$string = $_;
}
open F,
">$filename\.lic";
print F
&encrypt_payload($string);
close F;
#####################################################################
sub encrypt_payload {
my ($payload) = @_;
my $cypher_str;
my $i;
my $substring;
use Crypt::DES;
my $key =
pack("H16", $prog{'cipher_key'}); # min. 8 bytes
my $cipher = new
Crypt::DES $key;
## cypher only
works on text of exactly 8 bytes (PAD if necessary)
my $len =
length($payload);
my $loop = int
($len/8);
$loop++ if ($len%8
>0);
for ($i=0;
$i<$loop; $i++) {
$substring =
substr($payload, $i*8, 8);
if (($len =
length $substring)!= 8) {
## PAD
string with NULL characters to make it exactly equal 8 bytes
$substring
.= "\0" x (8 - $len);
}
$cypher_str .=
unpack("H16", $cipher->encrypt($substring));
}
return
($cypher_str);
}
[lm-decrypt.pl]
my $cipher_key =
"0123456789ABCDEF"; # Exactly 8 bytes
my $filename =
"memu_2TG82317RK6513331";
my $string;
open F,
"$filename\.lic";
my @enc = <F>;
close F;
foreach (@enc) {
chomp;
next if ($_ eq '');
$string = $_;
}
open F,
">$filename\-original\.txt";
print F
&decrypt_payload($string);
close F;
## business rules for interpreting decrypted file
#####################################################################
sub decrypt_payload {
my ($payload) = @_;
my $decypher_str;
my $i;
use Crypt::DES;
my $key =
pack("H16", $prog{'cipher_key'}); # min. 8 bytes
my $cipher = new
Crypt::DES $key;
## decypher only
works in in reverse of cypher (blocks of 16) but is exact multiples
my $len =
length($payload);
my $loop = $len/16;
if ($len%16 != 0) {
## Encrypted
string has been tampered or encryption scheme has been upgraded
return
("error007-URL is invalid, click the link directly or re-attempt copy and
paste");
}
## check for the
existence of a payload and if it contains all uppercase hex
my $temp_payload =
$payload;
$temp_payload =~
s/[A-F0-9]//g;
#exit(0) if
(($payload eq '')||($temp_payload eq ''));
for ($i=0; $i<$loop;
$i++) {
$decypher_str
.= $cipher->decrypt(pack("H16", substr($payload, $i*16, 16)));
}
## Exit if string
was not first frozen -- hack attack
#exit(0) if ($decypher_str
!~ /^FrT/);
## Remove any
trailing NULL/PAD characters from decrypted string
$decypher_str =~
s/\0*$//;
return ($decypher_str);
}
Can Birds-Eye.Net help you or your Company?
Receive your Birds-Eye.Net articles and white
papers hot off
the presses by adding our RSS feed to your reader.