<?
require_once 'yubiserve-config.php';
require_once 'yubiserve-utils.php';
if (!isset($_REQUEST["otp"]))
die ("ERR Missing OTP\n");
$otp = trim($_REQUEST["otp"]);
if ((strlen($otp)<32) || (strlen($otp)>48)) {
syslog(LOG_INFO, "Malformed OTP: $otp");
die ("ERR Malformed OTP\n");
}
if (!preg_match("/^([cbdefghijklnrtuv]{0,16})([cbdefghijklnrtuv]{32})$/", $otp, $matches)) {
syslog(LOG_INFO, "Invalid OTP format: $otp");
die("ERR Invalid OTP format\n");
}
$id = $matches[1];
$modhex_ciphertext = $matches[2];
$results = @$db->query("SELECT aeskey, internalname FROM yubikeys WHERE publicname = '$id' AND active = 'true'");
if ($results === false) {
syslog(LOG_INFO, "Unknown yubikey (internalname: $id)");
die("ERR Unknown yubikey\n");
}
$row = $results->fetchAll(SQLITE_ASSOC);
if (count($row)>1) {
syslog(LOG_INFO, "Multiple keys returned! OTP: $otp");
die("ERR Malformed OTP");
} elseif (count($row)<1) {
syslog(LOG_INFO, "Unknown yubikey (internalname: $id)");
die("ERR Unknown yubikey\n");
}
$aeskey = $row[0]['aeskey'];
$internalname = $row[0]['internalname'];
$ciphertext = modhex2hex($modhex_ciphertext);
$plaintext = aes128ecb_decrypt($aeskey, $ciphertext);
$uid = substr($plaintext, 0, 12);
if (strcmp($uid, $internalname) != 0) {
syslog(LOG_ERR, "UID error: $otp $plaintext: $uid vs $internalname");
die("ERR Corrupt OTP\n");
}
if (!crc_is_good($plaintext)) {
syslog(LOG_ERR, "CRC error: $otp: $plaintext");
die("ERR Corrupt OTP\n");
}
$counter = substr($plaintext, 14, 2) . substr($plaintext, 12, 2);
$low = substr($plaintext, 18, 2) . substr($plaintext, 16, 2);
$high = substr($plaintext, 20, 2);
$use = substr($plaintext, 22, 2);
$timestamp = hexdec($high . $low);
$internalcounter = hexdec($counter . $use);
$results = @$db->query("SELECT counter, time FROM yubikeys WHERE publicname = '$id' AND active = 'true' AND counter < $internalcounter");
$row = @$results->fetchAll(SQLITE_ASSOC);
if (count($row)!=1) {
syslog(LOG_ERR, "REPLAYED OTP for yubikey $id (same counter)");
die("ERR Replayed OTP");
} elseif ((($row[0] >> 8) == ($internalcounter >> 8)) && ($row[1] <= $timestamp)) {
syslog(LOG_ERR, "REPLAYED OTP for yubikey $id (same timestamp)");
die("ERR Replayed OTP");
}
print "OK Authentication success";
$results = $db->query("UPDATE yubikeys SET counter = $internalcounter, time = $timestamp WHERE publicname = '$id'");
unset($db);
?>