<?php
    function HexToBin( $hexa )
    {
      $result = "";
      $len = strlen( $hexa );
      
      $hexa = strtolower($hexa);
      
      $hbArray = array( "0" => "0000",
                        "1" => "0001",
                        "2" => "0010",
                        "3" => "0011",
                        "4" => "0100",
                        "5" => "0101",
                        "6" => "0110",
                        "7" => "0111",
                        "8" => "1000",
                        "9" => "1001",
                        "a" => "1010",
                        "b" => "1011",
                        "c" => "1100",
                        "d" => "1101",
                        "e" => "1110",
                        "f" => "1111" );
      
      for ( $i = 0; $i < $len; $i++ )
        $result .= $hbArray[$hexa[$i]];
      
      $i = 0;
      while ( $result[$i] == "0" )
        $i++;          
      return substr( $result, $i );
    }
    
    function TextToBin( $plainText )
    {
      $bin = "";
      $len = strlen( $plainText );
      
      for ( $i = 0; $i < $len; $i++ )
      {
        $ordValue = ord( $plainText[$i] );        
        $iterator = 128;
        
        while ( $iterator >= 1 )
        {
          if ( $ordValue >= $iterator )
          {
            $bin .= "1";
            $ordValue -= $iterator;
          }
          else
          {
            $bin .= "0";
          } 
          $iterator >>= 1;          
        }
      }
      return $bin;
    }
    
    function BinToText( $bin )
    {
      $result = "";
      $len = (int)(strlen( $bin )/8);
      $lastBlockSize = $len % 8;
      
      if ( $lastBlockSize > 0 )
      {
        $paddingSize = 8 - $lastBlockSize;
        for ( $i = 0; $i < $paddingSize; $i++ )
          $bin .= "0";
          
        $len++;
      }      
      
      for ( $i = 0; $i < $len; $i++ )
      {
        $iterator = 128;
        $j = 0;
        $c = 0;
        
        while ( $iterator >= 1 )
        {
          $c += ($bin[$i*8+$j]*$iterator);
          $iterator >>= 1;
          $j++;
        }
        $result .= chr($c);
      }
      return $result; 
    }
        
    function BinarySubstraction( &$value1, $value2, $len )
    {
      $borrow = false;
      $lenDiff = $len - strlen($value2);
      
      for ( $i = $len-1; $i >= 0; $i-- )
      {
        $v1 = ord($value1[$i])-48;
        $v2 = $i >= $lenDiff ? ord($value2[$i-$lenDiff])-48 : 0;
        
        if ( $borrow && ($v1 == 1) )
        {
          $borrow = false;
          $v1 = 0;
        }
        else if ( $borrow )
        {
          $v1 = 1;
        }
        
        if ( $v2 > $v1 )
        {
          $value1[$i] = "1";
          $borrow = true;
        }
        else
        {
          $value1[$i] = chr($v1-$v2+48);
        }
      }
    }
    
    function BinaryMultiplication( $value1, $value2 )
    {
      $result = "";
      $len1 = strlen($value1);
      $len2 = strlen($value2);
      $sumBits = 0;
      
      for ( $i = 0; $i < $len1+$len2-1; $i++ )
      {
        for ( $j = 0; $j < $len2; $j++ )
        {
          $index = $i - $len2 + $j + 1;
          if ( $index >= 0 && $index < $len1 && $value2[$j] == "1" )
          {
            $sumBits += $value1[$len1-$index-1];
          }
        }
        $result = chr(($sumBits&1) + 48).$result;
        $sumBits >>= 1;
      } 
      return $sumBits == 1 ? "1".$result : $result;
    }

    function BinaryModulation( $divident, $divider )
    {
      $result = "0";
      $dividentLen = strlen($divident);
      $dividerLen = strlen($divider);
      $i = 0;
      
      for ( $i = 0; $i <= $dividentLen - $dividerLen; $i++ )
      {
        $bDividerGreater = false;
        if ($i == 0 || $divident[$i-1] != "1")
        { 
          for ( $j = 0; $j < $dividerLen && !$bDividerGreater; $j++ )
          {
        	  if ( $divident[$i+$j] < $divider[$j] )
            {
              $bDividerGreater = true;
              break;
            }
            else if ( $divident[$i+$j] > $divider[$j] )
            {
              break;
            }
          }
        }

        if ( $bDividerGreater )
        {
          $result .= "0";
        }
        else
        {
          $result .= "1";
          BinarySubstraction( $divident, $divider, $i + $dividerLen );
        }
      }
      return $divident;
    }
    
    function ModExponation( $binValue, $binExp, $binMod )
    {
      $d = "1";      
      $expLen = strlen($binExp);
      $nLen = strlen($binMod);

      for ( $i = 0; $i < $expLen; $i++ )
      {
        $d = BinaryModulation( BinaryMultiplication( $d, $d ), $binMod );
        if ( strlen($d) > $nLen )
          $d = substr( $d, strlen($d) - $nLen, $nLen );
        if ( $binExp[$i] == "1" )
        {
          $d = BinaryModulation( BinaryMultiplication( $d, $binValue ), $binMod );
          if ( strlen($d) > $nLen )
            $d = substr( $d, strlen($d) - $nLen, $nLen );
        }
      }
      $dPadding = $nLen - strlen($d);
      while ( $dPadding > 0 )
      {
        $d = "0".$d;
        $dPadding--;
      }       
      return $d;
    }

    function AsymEncrypt( $plainText, $hexPublicKey, $hexN )
    {
      $binResult = "";
      $bin = TextToBin( $plainText );
      $n = HexToBin($hexN);
      $publicKey = HexToBin($hexPublicKey);
      $result = "";
      $nLen = strlen($n)-1;
      $bLen = strlen($bin);
      $blockCount = (int)($bLen/$nLen);
      $lastBlockSize = $bLen % $nLen;
      
      if ( $lastBlockSize > 0 )
      {
        $paddingSize = $nLen - $lastBlockSize;
        for ( $i = 0; $i < $paddingSize; $i++ )
          $bin .= "0";
          
        $blockCount++;
      }
      
      for ( $blockIndex = 0; $blockIndex < $blockCount; $blockIndex++ )
      {
        $m = substr( $bin, $blockIndex*$nLen, $nLen ); 
        $result = ModExponation( $m, $publicKey, $n );
        $binResult .= $result;
      }
      return BinToText($binResult);    
    }

    function AsymDecrypt( $cryptText, $hexPrivateKey, $hexN )
    {
      $bin = TextToBin( $cryptText );
      $n = HexToBin($hexN);
      $privateKey = HexToBin($hexPrivateKey);
      $nLen = strlen($n);
      $bLen = strlen($bin);
      $blockCount = (int)($bLen/$nLen);
      
      for ( $blockIndex = 0; $blockIndex < $blockCount; $blockIndex++ )
      {
        $m = substr( $bin, $blockIndex*$nLen, $nLen ); 
        $result = ModExponation( $m, $privateKey, $n );
        $binResult .= substr( $result, 1 );
      }
      return BinToText($binResult);      
    }
?>
