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. 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. (If some OTP values are skipped, the
<i>challenge</i> will be updated automatically to reflect the successful OTP. 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 " "'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 .= " ";
}
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. For convenience, it is carried \n";
echo "through on each click of the <i>Authenticate</i> button. 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. 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. A value greater than 1 is more convenient for the user, as it \n";
echo "lessens the probability that a resynchronization will be required. 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). 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. A token key must always be specified. " .
"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. 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 . ". 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. 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. 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 . ". 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>.
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 11:14:04 pm on May 20, 2012.