<?php

/****************************************************************************\
/****************************************************************************\
/****************************************************************************\
HCE MD5 Module:

Author : Manon Goo <manon@passionet.de>
Date:	 15. Okt 1999
Version: 0.1
Algorithm: Described in "Applied Cryptography" from Bruce Schneider
	(p. 353  / "Karn" )
	
	


Usage:
	$key = "My Secryt Key";
	$rand = "A Shared Random String ...";
	$abcd = "Pleas Encrypt Me ....";
	
	// These Functions work with mime decoded Data
	
	$cryptomime = new HCEMD5 ($key, $rand);
	$encrypt = $cryptomime->hce_block_encode_mime($abcd);
	$abcd = $cryptomime->hce_block_decode_mime($encrypt);
	
	// These Functions work with binary Data
	
	$cryptobin = new HCEMD5 ($key, $rand);
	$encrypt = $cryptobin->hce_block_encode_mime($abcd);
	$abcd = $cryptobin->hce_block_decode_mime($encrypt);

Bugs: 
	Function binmd5 could be done nicer.
	Function array2string could be done nicer.
		
	
	
Description:

	The MIME Functions are tested and work eitherways with
	the Crypt::HCE_MD5 module (0.45) (whithout teh KEYBUG Flag ..).
	

	Shamelessly stolen from Eric Estabrooks, eric@urbanrage.com
	Crypt::HCE_MD5 module
	
       This module implements a chaining block cipher using a one
       way hash.  This method of encryption is the same that is
       used by radius (RFC2138) and is also described in Applied
       Cryptography.

       Two interfaces are provided in the module.  The first is
       straight block encryption/decryption the second does
       base64 mime encoding/decoding of the encrypted/decrypted
       blocks.

       The idea is the the two sides have a shared secret that
       supplies one of the keys and a randomly generated block of
       bytes provides the second key.  The random key is passed
       in cleartext between the two sides.

       An example client and server are packaged as modules with
       this module.  They are used in the tests.  They can also
       be found in the examples directory.

       The 'KEYBUG' flag is for decrypting data encrypted with an
       older version of Crypt::HCE_MD5.  There was a bug in the
       chaining portion that prevented the full 16 bytes of the
       previous block from being using in creating the chaining
       hash.  Decrypt your old data and re-encrypt it without the
       KEYBUG flag.

       The release after this one will have the KEYBUG feature
       removed, but I'll leave this version on CPAN

       Thanks to Jake Angerman for pointing out the weakness.

 /****************************************************************************\
/****************************************************************************\
/****************************************************************************\
/****************************************************************************/

class HCEMD5 {
	var $hce_md5_sec_key;
	var $hce_md5_rand;
	// Turn on debugging Here
	var $hce_md5_debug = "0";
	
	
	function HCEMD5($key, $rand, $debug="$this->hce_md5_debug") {
	// Initalize The Keys ..
		if ($key && $rand) {
   			$this->hce_md5_sec_key = $key;
   			$this->hce_md5_rand = $rand;
   			// $this->hce_md5_debug = $debug;
   			// if ($this->hce_md5_debug)  print "<h1> xx $this->hce_md5_debug xx </h1>";
   			return true;
   		} else {
   			return false;
   		}
	}
		
       function array2string ($array, $debug="$this->hce_md5_debug"){
       // $var =  join ( "", $var) does not seem to work
       // under php3 If someone knows better please tell me ..
       // 
            $length = count($array);
            if ($length < 1) {
            	return $array;
            }
            $length++;
            $string = $array[1];
            for ( $i=2 ; $i < $length  ; $i++) {
            	$string = $string . $array[$i];
            }
            return $string;
        }

        function array2pack ($array, $debug="$this->hce_md5_debug"){
        // hexadecimal representaion of a string or array ...
            $length = count($array);
            if ($length == 1) {
            	return pack('C*', $array);
            }
            $length++;
            for ( $i=0 ; $i < $length  ; $i++) {
            	$pack[$i] = pack('C*', $array[$i]);
            }
            return $pack;
        }
	
	
	function  _new_key($round, $debug="$this->hce_md5_debug") {
		/* Ok Punch the Keys togeter .*/
                $digest = $this->binmd5($this->hce_md5_sec_key . $round);
                $e_block = unpack('C*', $digest);
            	return $e_block;
        }

        function binmd5($string, $debug="$this->hce_md5_debug") {
       		// We have mhash ..... 
       		// return mhash("MHASH_MD5", $string);
       		// We have md5  ...
       		
       		if ($debug) { 
       			print "<br>Tohash $string  <br>BinTohex: ";
       			print bin2hex($string);
       		}
       		// return pack('H*',md5($string));
       		// seems as thisone is fixed
       		// Someplace here I get my arays terminated with 0x0
       		// after th first round any help to eliminate this bug would 
       		// be great
       		return pack('H*',md5(pack('H*', ereg_replace( "00$", "", bin2hex($string)))));
        }

        function hce_block_decrypt($data,$debug="$this->hce_md5_debug") {
	    $data = unpack('C*', $data);
            unset($ans);
            unset($ans1);
            $ans = array();
            $ans1 = array(); 
            $eblock = 1;
            $e_block = $this->_new_key($this->hce_md5_rand);
            $data_size = count($data);
            // $data_size++;
            for($i=0; $i < $data_size; $i++) {
            	$mod = $i % 16;
                /* print "hr $mod";  */
                if(($mod == 0) && ($i > 15)) {
       			$tmparr = array($data[$i - 15],  $data[$i - 14], $data[$i - 13], $data[$i - 12], $data[$i - 11], $data[$i - 10], $data[$i - 9], $data[$i - 8], $data[$i - 7], $data[$i - 6], $data[$i - 5], $data[$i - 4], $data[$i - 3], $data[$i - 2], $data[$i - 1], $data[$i] );
                       	if ($debug) {
                       		print "<br> New round ... Tmparr: $eblock --\n";
                       		print join(" ", $tmparr);
                       	}
                       	$tmparr = $this->array2pack($tmparr);
                       	$tmparr = $this->array2string($tmparr);
                       	$e_block = $this->_new_key($tmparr);
                       	if ($debug) {
                       	 	print "<br> New round ... E-block $eblock --\n";
                       		print join(" ", $e_block);
                       		$eblock++;
                       	}
                }
                $mod++;
                $i++;
                $ans[$i] = $e_block[$mod] ^  $data[$i];
                $ans1[$i] = pack('C*', $ans[$i ]);
		if ($debug) {
			
			print "<br> Decrypt: round: $i e_block: $e_block[$mod] Data: $data[$i] Ans: $ans[$i] Ansunp: $ans1[$i] ";
		}
		$i--;
            }
	    return $this->array2string($ans1);
        }

        function hce_block_encrypt($data,$debug="$this->hce_md5_debug") {
	    $data = unpack('C*', $data);
            unset($ans);
            unset($ans1);
            $ans = array();
            $ans1 = array(); 
            $eblock = 1;
            $e_block = $this->_new_key($this->hce_md5_rand);
            $data_size = count($data);
            // $data_size++;
            for($i=0; $i < $data_size; $i++) {
            	$mod = $i % 16;
                /* print "hr $mod";  */
                if(($mod == 0) && ($i > 15)) {
       			$tmparr = array($ans[$i - 15],  $ans[$i - 14], $ans[$i - 13], $ans[$i - 12], $ans[$i - 11], $ans[$i - 10], $ans[$i - 9], $ans[$i - 8], $ans[$i - 7], $ans[$i - 6], $ans[$i - 5], $ans[$i - 4], $ans[$i - 3], $ans[$i - 2], $ans[$i - 1], $ans[$i] );
                       	if ($debug) {
                       		print "<br> New round ... Tmparr: $eblock --\n";
                       		print join(" ", $tmparr);
			}
			$tmparr = $this->array2pack($tmparr);
                       	$tmparr = $this->array2string($tmparr);
                       	$e_block = $this->_new_key($tmparr);
                       	if($debug) {
                       		print "<br> New round ... E-block: $eblock --\n";
                       		print join(" ", $e_block);
                       		$eblock++;
                       	}	
                }
                $mod++;
                $i++;
                $ans[$i] = $e_block[$mod] ^  $data[$i];
                $ans1[$i] = pack('C*', $ans[$i]);
		if ($debug) {
			
			print "<br> Encrypt: round: $i e_block: $e_block[$mod] Data: $data[$i] Ans: $ans[$i] Ansunp: $ans1[$i] ";
			
		}
		$i--;
		$mod--;
            }
	    return $this->array2string($ans1);
        }

        function hce_block_decode_mime ($data,$debug="$this->hce_md5_debug") {
		$data = base64_decode($data);
        	return $this->hce_block_decrypt($data,$debug);
        }

        function hce_block_encode_mime ($data,$debug="$this->hce_md5_debug") {
		$data = $this->hce_block_encrypt($data,$debug);
        	return base64_encode($data);
        }

}

?>
