<?php
/**
* Project: Distrubution License Class
* File: class.license.lib.php
*
* Copyright (C) 2005 Oliver Lillie
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* @link http://www.buggedcom.co.uk/
* @link http://www.phpclasses.org/browse/package/2298.html
* @author Oliver Lillie, buggedcom <publicmail at buggedcom dot co dot uk>
* @version 0.1
* @history---------------------------------------------
* see CHANGELOG
*/
class padl {
/**
* hash key 1 used to encrypt the generate key data.
* hash key 2 used to encrypt the request data
* hash key 3 used to encrypt the dial home data
* NOTE1 : there are three different hash keys for the three different operations
* NOTE2 : these hash key's are for use by both mcrypt and alternate cryptions
* and although mcrypts keys are typically short they should be kept long
* for the sake of the other functions
*
* @var string
* @var string
* @var string
*/
var $HASH_KEY1 = 'YmUzYWM2sNGU24NbA363zA7IDSDFGDFGB5aVi35BDFGQ3YNO36ycDFGAATq4sYmSFVDFGDFGps7XDYEzGDDw96OnMW3kjCFJ7M+UV2kHe1WTTEcM09UMHHT';
var $HASH_KEY2 = '80dSbqylf4Cu5e5OYdAoAVkzpRDWAt7J1Vp27sYDU52ZBJprdRL1KE0il8KQXuKCK3sdA51P9w8U60wohX2gdmBu7uVhjxbS8g4y874Ht8L12W54Q6T4R4a';
var $HASH_KEY3 = 'ant9pbc3OK28Li36Mi4d3fsWJ4tQSN4a9Z2qa8W66qR7ctFbljsOc9J4wa2Bh6j8KB3vbEXB18i6gfbE0yHS0ZXQCceIlG7jwzDmN7YT06mVwcM9z0vy62T';
/**
* You may not want to use mcrypt even if your system has it installed
* make this false to use a regular encryption method
*
* @var boolean
*/
var $USE_MCRYPT = true;
/**
* The algorythm to be used by mcrypt
*
* @var string
*/
var $ALGORITHM = 'blowfish';
/**
* use time binding vars inited.
*/
var $USE_TIME;
/**
* time checking start period difference allowance ie if the user has slightly different time
* setting on their server make an allowance for the diff period. carefull to not make it too
* much otherwise they could just reset their server to a time period before the license expires.
*
* @var number (seconds)
*/
var $START_DIF = 129600;
/**
* id 1 used to validate license keys
* id 2 used to validate license key requests
* id 2 used to validate dial home data
*
* @var string
* @var string
* @var string
*/
# id to check for to validate source
var $ID1 = 'nSpkAHRiFfM2hE588eB';
var $ID2 = 'NWCy0s0JpGubCVKlkkK';
var $ID3 = 'G95ZP2uS782cFey9x5A';
/**
* begining and end strings
*
* @var strings
*/
var $BEGIN1 = 'BEGIN LICENSE KEY';
var $END1 = 'END LICENSE KEY';
/**
* wrap key settings
*
* @var number
* @var string
* @var string
*/
var $_WRAPTO = 80;
var $_PAD = "-";
/**
* init the linebreak var
*/
var $_LINEBREAK;
/**
* dial home return query deliminators
*
* @var string
* @var string
*/
var $BEGIN2 = '_DATA{';
var $END2 = '}DATA_';
/**
* init the key data array.
*
* @var array
*/
var $_DATA = array();
/**
* use server binding vars inited.
*/
var $USE_SERVER;
var $_SERV;
var $_MAC;
var $ALLOW_LOCAL;
var $_SERVER_INFO = array();
/**
* this is the number of required server stats for the key generation to be successfull
* if the server can't produce this number of details then the key fails to be generated
* you can set it to however many you wish, the max is 5
*
* @var number
*/
var $REQUIRED_URIS = 2;
/**
* the date string for human readable format
*
* @var string
*/
var $DATE_STRING = 'd/M/Y H:i:s';
/**
* Constructor
*
* @access private
**/
function padl()
{
# check to see if the class has been secured
$this->_check_secure();
}
/**
* init
*
* init the license class
*
* @access public
* @param $use_mcrypt boolean Determines if mcrypt encryption is used or not (defaults to true,
* however if mcrypt is not available, it is set to false)
* @param $use_time boolean Sets if time binding should be used in the key (defaults to true)
* @param $use_server boolean Sets if server binding should be used in the key (defaults to true)
* @param $allow_local boolean Sets if server binding is in use then localhost servers are valid (defaults to false)
**/
function init($use_mcrypt=true, $use_time=true, $use_server=true, $allow_local=false)
{
# check to see if the class has been secured
$this->_check_secure();
$this->USE_MCRYPT = ($use_mcrypt && function_exists('mcrypt_generic'));
$this->USE_TIME = $use_time;
$this->ALLOW_LOCAL = $allow_local;
$this->USE_SERVER = $use_server;
$this->_LINEBREAK = $this->_get_os_linebreak();
}
/**
* _get_os_linebreak
*
* get's the os linebreak
*
* @access private
* @param $true_val boolean If the true value is needed for writing files, make true
* defaults to false
* @return string Returns the os linebreak
**/
function _get_os_linebreak($true_val=false)
{
$os = strtolower(PHP_OS);
switch($os)
{
# not sure if the string is correct for FreeBSD
# not tested
case 'freebsd' :
# not sure if the string is correct for NetBSD
# not tested
case 'netbsd' :
# not sure if the string is correct for Solaris
# not tested
case 'solaris' :
# not sure if the string is correct for SunOS
# not tested
case 'sunos' :
# linux variation
# tested on server
case 'linux' :
$nl = "\n";
break;
# darwin is mac os x
# tested only on the client os
case 'darwin' :
# note os x has \r line returns however it appears that the ifcofig
# file used to source much data uses \n. let me know if this is
# just my setup and i will attempt to fix.
if($true_val) $nl = "\r";
else $nl = "\n";
break;
# defaults to a win system format;
default :
$nl = "\r\n";
}
return $nl;
}
/**
* _post_data
*
* Posts data to and recieves data from dial home server. Returned info
* contains the dial home validation result
*
* @access private
* @param $host string Host name of the server to be contacted
* @param $path string Path of the script for the data to be sent to
* @param $query_array array Array that contains the license key info to be validated
* @param $port number Port Number to send the data through
* @return array Result of the dialhome validation
* @return string - SOCKET_FAILED will be returned if it was not possible to open a socket to the home server
**/
function _post_data($host, $path, $query_array, $port=80)
{
# generate the post query info
$query = 'POSTDATA='.$this->_encrypt($query_array, 'HOMEKEY');
$query .= '&MCRYPT='.$this->USE_MCRYPT;
# init the return string
$return = '';
# generate the post headers
$post = "POST $path HTTP/1.1\r\n";
$post .= "Host: $host\r\n";
$post .= "Content-type: application/x-www-form-urlencoded\r\n";
$post .= "Content-length: ".strlen($query)."\r\n";
$post .= "Connection: close\r\n";
$post .= "\r\n";
$post .= $query;
# open a socket
$header = @fsockopen($host, $port);
if(!$header)
{
# if the socket fails return failed
return array('RESULT'=>'SOCKET_FAILED');
}
@fputs($header, $post);
# read the returned data
while (!@feof($header))
{
$return .= @fgets($header, 1024);
}
fclose($header);
# seperate out the data using the delims
$leftpos = strpos($return, $this->BEGIN2)+strlen($this->BEGIN2);
$rightpos = strpos($return, $this->END2)-$leftpos;
#trace($return);
# decrypt and return the data
return $this->_decrypt(substr($return, $leftpos, $rightpos), 'HOMEKEY');
}
/**
* _compare_domain_ip
*
* uses the supplied domain in the key and runs a check against the collected
* ip addresses. If there are matching ips it returns true as the domain
* and ip address match up
*
* @access private
* @return boolean
**/
function _compare_domain_ip($domain, $ips=false)
{
# if no ips are supplied get the ip addresses for the server
if(!$ips) $ips = $this->_get_ip_address();
# get the domain ip list
$domain_ips = gethostbynamel($domain);
# loop through the collected ip's searching for matches against the domain ips
if(is_array($domain_ips) && count($domain_ips) > 0)
{
foreach($domain_ips as $ip)
{
if(in_array($ip, $ips)) return true;
}
}
return false;
}
/**
* _pad
*
* pad out the begin and end seperators
*
* @access private
* @param $str string The string to be padded
* @return string Returns the padded string
**/
function _pad($str)
{
$str_len = strlen($str);
$spaces = ($this->_WRAPTO-$str_len)/2;
$str1 = '';
for($i=0; $i<$spaces; $i++)
{
$str1 = $str1.$this->_PAD;
}
if($spaces/2 != round($spaces/2))
{
$str = substr($str1, 0, strlen($str1)-1).$str;
}
else
{
$str = $str1.$str;
}
$str = $str.$str1;
return $str;
}
/**
* _get_key
*
* gets the hash key for the current encryption
*
* @access private
* @param $key_type string The license key type being produced
* @return string Returns the hash key
**/
function _get_key($key_type)
{
switch($key_type)
{
case 'KEY' :
return $this->HASH_KEY1;
case 'REQUESTKEY' :
return $this->HASH_KEY2;
case 'HOMEKEY' :
return $this->HASH_KEY3;
default :
}
}
/**
* _get_begin
*
* gets the begining license key seperator text
*
* @access private
* @param $key_type string The license key type being produced
* @return string Returns the begining string
**/
function _get_begin($key_type)
{
switch($key_type)
{
case 'KEY' :
return $this->BEGIN1;
case 'REQUESTKEY' :
return $this->BEGIN2;
case 'HOMEKEY' :
return '';
}
}
/**
* _get_end
*
* gets the ending license key seperator text
*
* @access private
* @param $key_type string The license key type being produced
* @return string Returns the ending string
**/
function _get_end($key_type)
{
switch($key_type)
{
case 'KEY' :
return $this->END1;
case 'REQUESTKEY' :
return $this->_END2;
case 'HOMEKEY' :
return '';
}
}
/**
* _generate_random_string
*
* generates a random string
*
* @access private
* @param $length number The length of the random string
* @param $seeds string The string to pluck the characters from
* @return string Returns random string
**/
function _generate_random_string($length=10, $seeds='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890123456789')
{
$str = '';
$seeds_count = strlen($seeds);
list($usec, $sec) = explode(' ', microtime());
$seed = (float) $sec + ((float) $usec * 100000);
mt_srand($seed);
for ($i = 0; $length > $i; $i++) {
$str .= $seeds{mt_rand(0, $seeds_count - 1)};
}
return $str;
}
/**
* _encrypt
*
* encrypts the key
*
* @access private
* @param $src_array array The data array that contains the key data
* @return string Returns the encrypted string
**/
function _encrypt($src_array, $key_type='KEY')
{
# check to see if the class has been secured
$this->_check_secure();
$rand_add_on = $this->_generate_random_string(3);
# get the key
$key = $this->_get_key($key_type);
$key = $rand_add_on . $key;
# check to see if mycrypt exists
if($this->USE_MCRYPT)
{
# openup mcrypt
$td = mcrypt_module_open($this->ALGORITHM, '', 'ecb', '');
$iv = mcrypt_create_iv (mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
# process the key
$key = substr($key, 0, mcrypt_enc_get_key_size($td));
# init mcrypt
mcrypt_generic_init($td, $key, $iv);
# encrypt data
# double base64 gets makes all the characters alpha numeric
# and gets rig of the special characters
$crypt = mcrypt_generic($td, serialize($src_array));
# shutdown mcrypt
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
}
else
{
# if mcrypt doesn't exist use regular encryption method
# init the vars
$crypt = '';
$str = serialize($src_array);
# loop through the str and encrypt it
for($i=1; $i<=strlen($str); $i++)
{
$char = substr($str, $i-1, 1);
$keychar = substr($key, ($i % strlen($key))-1, 1);
$char = chr(ord($char)+ord($keychar));
$crypt .= $char;
}
}
# return the key
return $rand_add_on.base64_encode(base64_encode(trim($crypt)));
}
/**
* _decrypt
*
* decrypts the key
*
* @access private
* @param $enc_string string The key string that contains the data
* @return array Returns decrypted array
**/
function _decrypt($str, $key_type='KEY')
{
# check to see if the class has been secured
$this->_check_secure();
$rand_add_on = substr($str, 0, 3);
$str = base64_decode(base64_decode(substr($str, 3)));
# get the key
$key = $rand_add_on . $this->_get_key($key_type);
# check to see if mycrypt exists
if($this->USE_MCRYPT)
{
# openup mcrypt
$td = mcrypt_module_open($this->ALGORITHM, '', 'ecb', '');
$iv = mcrypt_create_iv (mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
# process the key
$key = substr($key, 0, mcrypt_enc_get_key_size($td));
# init mcrypt
mcrypt_generic_init($td, $key, $iv);
# decrypt the data and return
$decrypt = mdecrypt_generic($td, $str);
# shutdown mcrypt
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
}
else
{
# if mcrypt doesn't exist use regular decryption method
# init the decrypt vars
$decrypt = '';
# loop through the text and decode the string
for($i=1; $i<=strlen($str); $i++)
{
$char = substr($str, $i-1, 1);
$keychar = substr($key, ($i % strlen($key))-1, 1);
$char = chr(ord($char)-ord($keychar));
$decrypt .= $char;
}
}
# return the key
return unserialize($decrypt);
}
/**
* _wrap_license
*
* wraps up the license key in a nice little package
*
* @access private
* @param $src_array array The array that needs to be turned into a license str
* @param $key_type string The type of key to be wrapped (KEY=license key, REQUESTKEY=license request key)
* @return string Returns encrypted and formatted license key
**/
function _wrap_license($src_array, $key_type='KEY')
{
# sort the variables
$begin = $this->_pad($this->_get_begin($key_type));
$end = $this->_pad($this->_get_end($key_type));
# encrypt the data
$str = $this->_encrypt($src_array, $key_type);
# return the wrap
return $begin.$this->_LINEBREAK.wordwrap($str, $this->_WRAPTO, $this->_LINEBREAK, 1).$this->_LINEBREAK.$end;
}
/**
* _unwrap_license
*
* unwraps license key back into it's data array
*
* @access private
* @param $enc_str string The encrypted license key string that needs to be decrypted
* @param $key_type string The type of key to be unwrapped (KEY=license key, REQUESTKEY=license request key)
* @return array Returns license data array
**/
function _unwrap_license($enc_str, $key_type='KEY')
{
# sort the variables
$begin = $this->_pad($this->_get_begin($key_type));
$end = $this->_pad($this->_get_end($key_type));
# get string without seperators
$str = trim(str_replace(array($begin, $end, "\r", "\n", "\t"), '', $enc_str));
# decrypt and return the key
return $this->_decrypt($str, $key_type);
}
/**
* make_secure
*
* deletes all class values to prevent re-writing of a key;
*
* @access public
**/
function make_secure($report=false)
{
if($report) define('_PADL_REPORT_ABUSE_', true);
# walkthrough and delete the class vars
foreach(array_keys(get_object_vars($this)) as $value)
{
unset($this->$value);
}
# define that class is secure
define('_PADL_SECURE_', 1);
}
/**
* _check_secure
*
* checks to see if the class has been made secure
*
* @access private
**/
function _check_secure()
{
# check to see if padl has been made secure
if(defined('_PADL_SECURE_'))
{
# if(defined('_PADL_REPORT_ABUSE_')) $this->_post_data($this->_HOST, $this->_PATH, array());
# trigger the error because user has attempted to access secured functions
# after the call has been made to 'make_secure'
trigger_error("<br /><br /><span style='color: #F00;font-weight: bold;'>The PHP Application Distribution License System (PADL) has been made secure.<br />You have attempted to use functions that have been protected and this has terminated your script.<br /><br /></span>", E_USER_ERROR);
exit;
}
}
}
/**
* custom functions to aid in debugging
*
* @var mixed
*/
function trace()
{
$message = '';
for ($i=0; $i<func_num_args(); $i++)
{
if(is_array(func_get_arg($i)))
{
trace_r(func_get_arg($i));
}
else
{
$message .= func_get_arg($i);
}
if($i <= func_num_args()-2)
{
$message.=' : ';
}
}
echo "<br><b>\r\r".$message."\r\r</b>";
}
function trace_r($array="array is empty")
{
echo "<pre><b>\r\r";
print_r($array);
echo "\r\r</b></pre>";
}
?> |