CryptoCard KT-1 Token Authentication and Resynchronization Demonstration Page (PHP Code as Text)


<?php
//Don't allow any browser to cache these contents, because of the
//dynamic content.
//
header("Cache-Control: no-cache, must-revalidate");
header("Pragma: no-cache");
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
?>

<html>

<head>
<title>CryptoCard KT-1 Token Authentication and Resynchronization Demonstration Page</title>
</head>

<body background="/bkgnds/bk_garlic.jpg">

<font face="arial" size="3">

<p align="center">
<font size="5">
<i>CryptoCard</i> KT-1 Token Authentication and Resynchronization Demonstration Page
</font>
</p>

<hr>

<p>
<b>Instructions:</b>
<ul>
<li>This page demonstrates authentication of <i>CryptoCard</i> KT-1 tokens from PHP.&nbsp; Under the hood,
a compiled C program that interacts with <i>CryptoCard</i>'s <i>AuthEngine SDK</i> is used.</li>
<li>This page will only authenticate KT-1 tokens programmed with the token options described in the
<a href="../compiled_c_prog_design/index.php" target="_blank">compiled C program</a>.</li>
<li>Enter either:</li>
<ul>
<li>The token key, OTP, challenge, and optionally OTP window size (defaults to 3), <i>OR</i></li>
<li>The token key, OTP, resynchronization string, and optionally OTP window size (defaults to 3).</li>
</ul>
<li>Press the <i>Authenticate</i> button.</li>
<ul>
<li>Messages describing the success or failure of the authentication will be provided.</li>
<li>If the authentication is successful, successive clicks of the <i>Authenticate</i> button
    will authenticate successive OTPs.</li>
<li>The OTP window is manipulated automatically.&nbsp; (If some OTP values are skipped, the 
    <i>challenge</i> will be updated automatically to reflect the successful OTP.&nbsp; This
    is the same way tokens would be authenticated in practice.)</li>
</ul>
<li>The PHP source code for this page is available
    <a href="demoauthtextserve.php" target="_blank">here</a>.</li>
</ul>
</p>

<hr>

<?php
//--------------------------------------------------------------------------------
//Returns 1 if the string is purely digits '0'-'9', or 0 otherwise.
//
function STRFUNC_is_pure_digits($arg)
   {
   //Must be a string.
   if (! is_string($arg))
   return(0);

   $len = strlen($arg);

   for ($i=0; $i<$len; $i++)
      {
      $c = SubStr($arg, $i, 1);
      if (strpos("0123456789", $c) === FALSE)
         return(0);
      }

   return(1);
   }
//
//--------------------------------------------------------------------------------
//Returns 1 if each character in the subset is present in the set, i.e.
//if it is a proper or improper subset, or 0 otherwise.
//
function STRFUNC_is_char_subset($conjectured_subset, $reference_set)
   {
   $conjsubsetlen = strlen($conjectured_subset);
   $refsetlen     = strlen($reference_set);
   
   for ($i=0; $i<$conjsubsetlen; $i++)
      {
      $c = SubStr($conjectured_subset, $i, 1);

      if (strpos($reference_set, $c) === FALSE)
	return(0);
      }

   //If we've made it this far, no character has failed to pan out.
   return(1);
   }
//
//--------------------------------------------------------------------------------
//Removes all characters from the input that are not in the set of
//allowed characters.
//
function STRFUNC_force_into_subset($input, $subset)
   {
   $inputlen  = strlen($input);
   $subsetlen = strlen($subset);
   
   $rv = "";
   for ($i=0; $i<$inputlen; $i++)
      {
      $c = SubStr($input, $i, 1);

      if (strpos($subset, $c) === FALSE)
	 {
	 //Character is not in the set.  Do not add it to
	 //the result.
	 }
      else
	 {
	 //Character is in the set.  Add it.
	 $rv .= $c;
	 }
      }

   return($rv);
   }
//
//--------------------------------------------------------------------------------
//Forces the variable to be a string, removes all characters from the input that 
//are not in the set of allowed characters, then truncates the string if it is 
//too long.
//
function STRFUNC_force_stringtype_subset_truncate($input, $subset, $maxlen)
   {
   //Force the type.  Only numerics and strings can reliably be
   //strings.
   //
   if (is_string($input))
      {
      //It is already a string.  Do nothing.
      }
   else if (is_numeric($input))
      {
      //A number can be reliably made to a string.
      $input = (string) $input;
      }
   else
      {
      //We don't know what it is.  Make the empty string out of it.
      $input = (string) "";
      }

   //Force it into the allowed character set.
   $input = STRFUNC_force_into_subset($input, $subset);

   //Take care of the length.
   if (strlen($input) > $maxlen)
      $input = SubStr($input, 0, $maxlen);

   //echo " / " . $input . " / ";

   return($input);
   }
//
//--------------------------------------------------------------------------------
//For the passed string, will return a string of "&nbsp;"'s that approximately
//equals the length when displayed in a table.
//
function STRFUNC_nbsp_padding($arg)
   {
   $n = (int)(1.95 * strlen($arg));

   $rv = "";

   for ($i = 0; $i < $n; $i++)
      {
      $rv .= "&nbsp;";
      }

   return($rv);
   }
//
//--------------------------------------------------------------------------------
//Outputs the major form of the page.
//
function do_form($messages_in, $token_key_in, $otp_in, $resync_in, $otp_window_in, $challenge_in)
   {
   $col1start = "<td width=\"50%\">\n";
   $col2start = "<td width=\"50%\" align=\"center\">\n";

   echo "<form method=post action=\"demoauth.php\">\n";
   echo "<table border=1>\n";

   if (($messages_in !== FALSE) && (is_array($messages_in)))
      {
      echo "<tr>\n";
      echo "<td colspan=\"2\">\n";
      echo "<b>Authentication Results:</b>\n";
      echo "<ul>\n";
      for ($i=0; $i<count($messages_in); $i++)
	 {
	 echo "<li>";
	 echo $messages_in[$i];
         echo "</li>\n";
	 }
      echo "</ul>\n";
      echo "</td>\n";
      echo "</tr>\n";      
      }

   echo "<tr>\n";
   echo $col1start;
   echo "<b>Token Key:</b><br>\n";
   echo "The token key is the 192-bit (48 hexadecimal digit) \n";
   echo "key programmed into the <i>CryptoCard</i> token.&nbsp; For convenience, it is carried \n";
   echo "through on each click of the <i>Authenticate</i> button.&nbsp; The default value of the \n";
   echo "key is the value included in the <i>CryptoCard</i> example token initialization software.\n";
   echo "</td>\n";
   echo $col2start;
   echo "<input name=\"token_key_in\" value=\"" . $token_key_in . "\" size=\"60\" style=\"text-align: right\">\n";
   echo "</td>\n";
   echo "</tr>\n";
   echo "<tr>\n";
   echo $col1start;
   echo "<b>OTP:</b><br>\n";
   echo "The OTP (one-time password) is the value displayed on the token when either the token button is pressed \n";
   echo "or the token is resynchronized.\n";
   echo "</td>\n";
   echo $col2start;
   echo "<input name=\"otp_in\" value=\"" . $otp_in . "\" size=\"12\" style=\"text-align: right\">\n";
   echo "</td>\n";
   echo "</tr>\n";
   echo "<tr>\n";
   echo $col1start;
   echo "<b>Resynchronization String:</b><br>\n";
   echo "The resynchronization string is the value manually entered into the token.&nbsp; This would normally \n";
   echo "be necessary only if the token were activated but the OTP generated were not used (for example, if \n";
   echo "children were allowed to play with the token).\n";
   echo "</td>\n";
   echo $col2start;
   echo "<input name=\"resync_in\" value=\"" . $resync_in . "\" size=\"12\" style=\"text-align: right\">\n";
   echo "</td>\n";
   echo "</tr>\n";
   echo "<tr>\n";
   echo $col1start;
   echo "<b>OTP Window Size:</b><br>\n";
   echo "The OTP window size represents the number of consecutive expected values of the token OTP that will \n";
   echo "be allowed for authentication.&nbsp; A value greater than 1 is more convenient for the user, as it \n";
   echo "lessens the probability that a resynchronization will be required.&nbsp; This window allows accidental \n";
   echo "activation of the token without using the OTP without forcing resynchronization.\n";
   echo "</td>\n";
   echo $col2start;
   echo "<input name=\"otp_window_in\" value=\"" . $otp_window_in . "\" size=\"10\" style=\"text-align: right\">\n";
   echo "</td>\n";
   echo "</tr>\n";
   echo "</tr>\n";
   echo "<tr>\n";
   echo $col1start;
   echo "<b>Challenge:</b><br>\n";
   echo "The challenge is a 128-bit (32 hexadecimal digit) number that is held as state in the token (separate \n";
   echo "from the key).&nbsp; On each generated OTP, the challenge is incremented.\n";
   echo "</td>\n";
   echo $col2start;
   echo "<input name=\"challenge_in\" value=\"" . $challenge_in . "\" size=\"50\" style=\"text-align: right\">\n";
   echo "</td>\n";
   echo "</tr>\n";
   echo "<tr>\n";
   echo "<td colspan=\"2\" align=\"center\">\n";
   echo "<p><input type=\"submit\" value=\" Authenticate \"></p>\n";
   echo "</td>\n";
   echo "</tr>\n";
   echo "</table>\n";
   echo "</form>\n";
   }
//
//--------------------------------------------------------------------------------
//Runs the external executable to attempt to authenticate.  If something goes
//wrong where either the executable can't be run or where the MAJOR_RESULT
//can't be parsed, a value of -3 is returned for MAJOR_RESULT.
//
//The code below was lifted, plus or minus a little, from the PHP web page
//(www.php.net) under the proc_open() function.  Because I'm using PHP 4.X, I
//had to make a minor modification to the code that reads the stdout pipe from
//the child process.
//
function run_external_executable( $token_key_in,      //Unused inputs should be set to "". 
                                  $otp_in, 
                                  $resync_string_in,
                                  $otp_window_in,
                                  $challenge_in,
				 &$major_result_out, //Always set to an integer.
				 &$challenge_out)    //Set if appropriate, otherwise "".
   {
   $descriptorspec = array(
			  0 => array("pipe", "r"),  // stdin is a pipe that the child will read from
			  1 => array("pipe", "w")   // stdout is a pipe that the child will write to
			  );

   $process = proc_open("/usr/local/bin/cc_kt1_auth_php", 
                        $descriptorspec, 
                        $pipes);

   if (is_resource($process)) 
      {
      // $pipes now looks like this:
      // 0 => writeable handle connected to child stdin
      // 1 => readable handle connected to child stdout

      //Form a string that will be sent to the pipe.  This contains all the input
      //goodies.
      $input_string = "";
      if (strlen($token_key_in))
	 {
	 $input_string .= "TOKEN_KEY: ";
         $input_string .= $token_key_in;
         $input_string .= "\n";
	 }
      if (strlen($otp_in))
	 {
	 $input_string .= "OTP: ";
         $input_string .= $otp_in;
         $input_string .= "\n";
	 }
      if (strlen($resync_string_in))
	 {
	 $input_string .= "RESYNC_STRING: ";
         $input_string .= $resync_string_in;
         $input_string .= "\n";
	 }
      if (strlen($otp_window_in))
	 {
	 $input_string .= "OTP_WINDOW: ";
         $input_string .= $otp_window_in;
         $input_string .= "\n";
	 }
      if (strlen($challenge_in))
	 {
	 $input_string .= "CHALLENGE: ";
         $input_string .= $challenge_in;
         $input_string .= "\n";
	 }

      //Send the input string to the child process.  Dave's comment:  presumably the child process
      //would block when waiting for input.
      //
      fwrite($pipes[0], $input_string);
      fclose($pipes[0]);  //Dave's comment, presumably this triggers EOF in the child, but I'm not sure.

      //Get the full output from the child.  This comes back on the standard output.
      //Dave's comment:  presumably the EOF occurs when the child terminates, but I'm not sure.
      //
      $output_string = "";
      while (!feof($pipes[1]))
	{
	$output_string .= fread($pipes[1], 1);
	}
      fclose($pipes[1]);

      //It is important that you close any pipes before calling
      //proc_close in order to avoid a deadlock.
      //
      $return_value = proc_close($process);

      //Try to parse the output and assign values.  We are looking for
      //MAJOR_RESULT and CHALLENGE.
      //
      //Dave's comment:  there is one version of the CryptoCard library that writes
      //an extraneous line to stdout.  This line does not collide with any of the
      //strings we're looking for.
      //
      //Assign default values for the outputs in case strings not found.
      $major_result_out = -3;
      $challenge_out    = "";
      
      //Convert output to upper-case and strip all possible trash from the output.
      //No trash is expected, but this is part of basic web security.
      //
      $output_string = STRFUNC_force_into_subset(StrToUpper($output_string), ":_-\nABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789");

      //Crack up the string at newlines.
      $output_array = explode("\n", $output_string);

      //For each line, try to parse it out.  On a match, replace the default.
      //
      for ($i=0; $i<count($output_array); $i++)
	 {
	 if (strstr($output_array[$i], "MAJOR_RESULT:") !== FALSE)
	    {
	    $x = SubStr($output_array[$i], strlen("MAJOR_RESULT:"));

            if (is_numeric($x))
	       $x = (int)(float)$x;
            if ($x <= 9999)
	       $major_result_out = $x;
	    }
	 else if (strstr($output_array[$i], "CHALLENGE:") !== FALSE)
	    {
	    $x = SubStr($output_array[$i], strlen("CHALLENGE:"));

            if (strlen($x) == 32)
	       {
	       $challenge_out = $x;
	       }
	    }
	 }
      }
   else
      {
      //The attempt to execute the process did not work.  Fail the
      //authentication.
      //
      $major_result_out = -3;
      $challenge_out = "";
      }
   }
 
//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
//Obtain the token_key parameter from the POST namespace, if it exists.
//If it doesn't, assign the default token key.  The default is the one included
//in the CryptoCard example program.
//
if (isset($_POST["token_key_in"]))
   {
   $token_key_in = STRFUNC_force_into_subset(StrToUpper($_POST["token_key_in"]), "0123456789ABCDEF");
   }
else
   {
   $token_key_in = StrToUpper("050417f05cad9a55050417f05cad9a55050417f05cad9a55");
   }
//
//Further process the token key.  It must be 48 hexadecimal digits.  If it
//isn't, use the empty string.
//
if (strlen($token_key_in) != 48)
   {
   $token_key_in = "";
   }
//
//
//Obtain the OTP value specified from the POST namespace.  The OTP can contain
//all letters and numbers except I, O, Q, and Z.
//
if (isset($_POST["otp_in"]))
   {
   $otp_in = STRFUNC_force_into_subset(StrToUpper($_POST["otp_in"]), "0123456789ABCDEFGHJKLMNPRSTUVWXY");
   }
else
   {
   $otp_in = "";
   }
//
//Further format the OTP.
if ((strlen($otp_in) != 0) && (strlen($otp_in) != 8))
   {
   $otp_in = StrToUpper("Ill-Formatted (Must be 8 Characters, Letters and Numbers Except I, O, Q, and Z)");
   }
//
//
//Obtain the challenge parameter from the POST namespace, if it exists.  At the end of this
//cycle, the challenge will be either a 32-character hexadecimal string or else the
//empty string.
//
if (isset($_POST["challenge_in"]))
   {
   $challenge_in = STRFUNC_force_into_subset(StrToUpper($_POST["challenge_in"]), "0123456789ABCDEF");
   }
else
   {
   $challenge_in = "";
   }
//
//Further process the challenge in.  It must be 32 hexadecimal digits.  
//If it isn't, need to replace it with the empty string.
//
if ((strlen($challenge_in) != 0) && (strlen($challenge_in) != 32))
   {
   $challenge_in = "";
   }
//
//
//Obtain the resync_in parameter from the POST list.  This is the string that was supplied to the token
//to resynchronize it.  It must be 8 digits.
//
if (isset($_POST["resync_in"]))
   {
   $resync_in = STRFUNC_force_into_subset(StrToUpper($_POST["resync_in"]), "0123456789");
   }
else
   {
   $resync_in = "";
   }
//
//Further process the resynchronization string in.  It must be 8 decimal digits.  
//If it isn't, need to replace it with the empty string.
//
if (strlen($resync_in) != 8)
   {
   $resync_in = "";
   }
//
//Obtain the otp_window_in parameter from the POST list.  The OTP window is the number
//of sequential eligible values that will authenticate.
//
if (isset($_POST["otp_window_in"]))
   {
   $otp_window_in = STRFUNC_force_into_subset(StrToUpper($_POST["otp_window_in"]), "0123456789");
   }
else
   {
   $otp_window_in = "";
   }
//
//Try to convert the OTP window in string to an integer.  If that fails, assign the default
//value of 3.
//
if (is_numeric($otp_window_in))
   {
   $otp_window_in = (float) $otp_window_in;
   $otp_window_in = (int) $otp_window_in;

   if (($otp_window_in < 1) || ($otp_window_in > 9999))
      $otp_window_in = 3;
   }
else
   {
   $otp_window_in = 3;
   }
//
//
//Break into cases based on which parameters are supplied or not supplied.
//
if (
      (! isset($_POST["token_key_in"])) 
      && 
      (! isset($_POST["otp_in"])) 
      && 
      (! isset($_POST["challenge_in"])) 
      && 
      (! isset($_POST["otp_window_in"])) 
      && 
      (! isset($_POST["resync_in"]))
   )
   {
   //Nothing is specified.  This is a fresh form.
   do_form(FALSE, $token_key_in, $otp_in, $resync_in, 3, $challenge_in);
   }
else if ((strlen($token_key_in) == 0) || (strlen($otp_in) == 0))
   {
   //Token key or OTP isn't specified or is bad.  Can do nothing.
   $messages[] = "Unable to attempt to authenticate.&nbsp; A token key must always be specified.&nbsp; " .
                 "Either an OTP and challenge or an " .
                 "OTP and resynchronization string must be specified."; 

   do_form($messages, $token_key_in, $otp_in, $resync_in, $otp_window_in, $challenge_in);
   }
else if (strlen($resync_in) > 0)
   {
   //If we have a token key, an OTP, and a resynchronization string, we
   //can resync.  Challenge is irrelevant.
   //
   run_external_executable( $token_key_in,      //Unused inputs should be set to "". 
                            $otp_in, 
                            $resync_in,
                            $otp_window_in,
                            "",
			    $major_result_out,  //Always set to an integer.
			    $challenge_out);    //Set if appropriate, otherwise "".

   $messages[] = "The major result returned was from the spawned executable " . $major_result_out . ".";

   if ($major_result_out < 0)
      $messages[] = "A major result of " . $major_result_out . " means that the OTP did not authenticate.";
   else
      $messages[] = "A major result of 0 or greater means that the OTP did authenticate.&nbsp; The " .
                    "index returned indicates where in the authentication window the authentication occurred (<i>0</i> " .
                    "means the expected next OTP value, <i>1</i> means one past the expected " .
                    "next OTP value, etc.).";
   if (strlen($challenge_out))
      $messages[] = "The challenge returned from the spawned executable was " .
                    $challenge_out . ".&nbsp; In the case of a " .
                    "successful authentication, the challenge reflects the position in the OTP " .
                    "window where successful authentication occurred.";

   if (strlen($challenge_out))
     $form_challenge = $challenge_out;
   else
     $form_challenge = $challenge_in;

   do_form($messages, $token_key_in, $otp_in, "", $otp_window_in, $form_challenge);
   }
else if (strlen($challenge_in) == 0)
   {
   //Without the challenge, can't do much.
   $messages[] = "Unable to attempt to authenticate.&nbsp; Either a challenge " .
                 "or a resynchronization string is required.";

   do_form($messages, $token_key_in, $otp_in, $resync_in, $otp_window_in, "");
   }
else
   {
   //Normal authentication.
   run_external_executable( $token_key_in,      //Unused inputs should be set to "". 
                            $otp_in, 
                            $resync_in,
                            $otp_window_in,
                            $challenge_in,
			    $major_result_out, //Always set to an integer.
			    $challenge_out);    //Set if appropriate, otherwise "".

   $messages[] = "The major result returned was from the spawned executable " . $major_result_out . ".";

   if ($major_result_out < 0)
      $messages[] = "A major result of " . $major_result_out . " means that the OTP did not authenticate.";
   else
      $messages[] = "A major result of 0 or greater means that the OTP did authenticate.&nbsp; The " .
                    "index returned indicates where in the authentication window the authentication occurred (<i>0</i> " .
                    "means the expected next OTP value, <i>1</i> means one past the expected " .
                    "next OTP value, etc.).";
   if (strlen($challenge_out))
      $messages[] = "The challenge returned from the spawned executable was " . 
                    $challenge_out . ".&nbsp; In the case of a " .
                    "successful authentication, the challenge reflects the position in the OTP " .
                    "window where successful authentication occurred.";

   if (strlen($challenge_out))
     $form_challenge = $challenge_out;
   else
     $form_challenge = $challenge_in;

   do_form($messages, $token_key_in, $otp_in, $resync_in, $otp_window_in, $form_challenge);
   }
//
?>

<hr>
<p align="center" style="margin-top: -3; margin-bottom: -1"><font size="2">This
web page is maintained by <a href="mailto:dta@e3ft.com">David T. Ashley</a>.&nbsp; 
Local 
time on this server (at the time the page was served)
is <? $today = date("g:i:s a \o\\n F j, Y."); echo $today; ?></p>
<hr noshade size="5">

</font>
</body>

</html>

This web page is maintained by David T. Ashley.  Local time on this server (at the time the page was served) is