'0', 'B'=>'1', 'C'=>'2', 'D'=>'3', 'E'=>'4', 'F'=>'5', 'G'=>'6', 'H'=>'7',
'I'=>'8', 'J'=>'9', 'K'=>'10', 'L'=>'11', 'M'=>'12', 'N'=>'13', 'O'=>'14', 'P'=>'15',
'Q'=>'16', 'R'=>'17', 'S'=>'18', 'T'=>'19', 'U'=>'20', 'V'=>'21', 'W'=>'22', 'X'=>'23',
'Y'=>'24', 'Z'=>'25', '2'=>'26', '3'=>'27', '4'=>'28', '5'=>'29', '6'=>'30', '7'=>'31'
);
/**
* Use padding false when encoding for urls
*
* @return string base32 encoded string
* @author Bryan Ruiz
**/
public static function encode($input, $padding = true) {
if(empty($input)) return "";
$input = str_split($input);
$binaryString = "";
for($i = 0; $i < count($input); $i++) {
$binaryString .= str_pad(base_convert(ord($input[$i]), 10, 2), 8, '0', STR_PAD_LEFT);
}
$fiveBitBinaryArray = str_split($binaryString, 5);
$base32 = "";
$i=0;
while($i < count($fiveBitBinaryArray)) {
$base32 .= self::$map[base_convert(str_pad($fiveBitBinaryArray[$i], 5,'0'), 2, 10)];
$i++;
}
if($padding && ($x = strlen($binaryString) % 40) != 0) {
if($x == 8) $base32 .= str_repeat(self::$map[32], 6);
else if($x == 16) $base32 .= str_repeat(self::$map[32], 4);
else if($x == 24) $base32 .= str_repeat(self::$map[32], 3);
else if($x == 32) $base32 .= self::$map[32];
}
return $base32;
}
public static function decode($input) {
if (!is_string($input) || $input === '') return false;
$input = strtoupper($input);
if (!preg_match('/^[A-Z2-7]+={0,6}$/', $input)) return false;
$paddingCharCount = substr_count($input, self::$map[32]);
if (!in_array($paddingCharCount, array(6, 4, 3, 1, 0), true)) return false;
if ($paddingCharCount > 0 && strlen($input) % 8 !== 0) return false;
$input = rtrim($input, self::$map[32]);
if (!in_array(strlen($input) % 8, array(0, 2, 4, 5, 7), true)) return false;
$binaryString = '';
$buffer = 0;
$bufferBits = 0;
foreach (str_split($input) as $character) {
$buffer = ($buffer << 5) | (int)self::$flippedMap[$character];
$bufferBits += 5;
if ($bufferBits >= 8) {
$bufferBits -= 8;
$binaryString .= chr(($buffer >> $bufferBits) & 0xff);
$buffer &= $bufferBits > 0 ? (1 << $bufferBits) - 1 : 0;
}
}
return $binaryString;
}
}
// http://www.faqs.org/rfcs/rfc6238.html
// https://github.com/Voronenko/PHPOTP/blob/08cda9cb9c30b7242cf0b3a9100a6244a2874927/code/rfc6238.php
// Local changes: http -> https, consistent indentation, 200x200 -> 300x300 QR image size, PHP end tag
class TokenAuth6238 {
/**
* verify
*
* @param string $secretkey Secret clue (base 32).
* @return bool True if success, false if failure
*/
public static function verify($secretkey, $code, $rangein30s = 3) {
$key = Base32Static::decode($secretkey);
$unixtimestamp = intdiv(time(), 30);
for($i=-($rangein30s); $i<=$rangein30s; $i++) {
$checktime = (int)($unixtimestamp+$i);
$thiskey = self::oath_hotp($key, $checktime);
if (hash_equals(
str_pad((string)$code, 6, '0', STR_PAD_LEFT),
str_pad((string)self::oath_truncate($thiskey, 6), 6, '0', STR_PAD_LEFT)
)) {
return true;
}
}
return false;
}
public static function getTokenCode($secretkey,$rangein30s = 3) {
$result = "";
$key = Base32Static::decode($secretkey);
$unixtimestamp = intdiv(time(), 30);
for($i=-($rangein30s); $i<=$rangein30s; $i++) {
$checktime = (int)($unixtimestamp+$i);
$thiskey = self::oath_hotp($key, $checktime);
$result = $result." # ".self::oath_truncate($thiskey,6);
}
return $result;
}
public static function getTokenCodeDebug($secretkey,$rangein30s = 3) {
$result = "";
print "
SecretKey: $secretkey
";
$key = Base32Static::decode($secretkey);
print "Key(base 32 decode): $key
";
$unixtimestamp = intdiv(time(), 30);
print "UnixTimeStamp (time()/30): $unixtimestamp
";
for($i=-($rangein30s); $i<=$rangein30s; $i++) {
$checktime = (int)($unixtimestamp+$i);
print "Calculating oath_hotp from (int)(unixtimestamp +- 30sec offset): $checktime basing on secret key
";
$thiskey = self::oath_hotp($key, $checktime, true);
print "======================================================
";
print "CheckTime: $checktime oath_hotp:".$thiskey."
";
$result = $result." # ".self::oath_truncate($thiskey,6,true);
}
return $result;
}
public static function getBarCodeUrl($username, $domain, $secretkey, $issuer) {
$label = rawurlencode($username . '@' . $domain);
$issuer = rawurlencode($issuer);
$otpauth = "otpauth://totp/{$label}?secret={$secretkey}&issuer={$issuer}&algorithm=SHA1&digits=6&period=30";
return 'https://api.qrserver.com/v1/create-qr-code/?' . http_build_query([
'size' => '300x300',
'data' => $otpauth
]);
}
public static function generateRandomClue($length = 16) {
$b32 = "234567QWERTYUIOPASDFGHJKLZXCVBNM";
$s = "";
for ($i = 0; $i < $length; $i++) {
$s .= $b32[random_int(0, 31)];
}
return $s;
}
private static function hotp_tobytestream($key) {
$result = array();
$last = strlen($key);
for ($i = 0; $i < $last; $i = $i + 2) {
$x = $key[$i] + $key[$i + 1];
$x = strtoupper($x);
$x = hexdec($x);
$result = $result.chr($x);
}
return $result;
}
private static function oath_hotp ($key, $counter, $debug=false) {
$result = "";
$orgcounter = $counter;
$cur_counter = array(0,0,0,0,0,0,0,0);
if ($debug) {
print "Packing counter $counter (".dechex($counter).")into binary string - pay attention to hex representation of key and binary representation
";
}
for($i=7;$i>=0;$i--) { // C for unsigned char, * for repeating to the end of the input data
$cur_counter[$i] = pack ('C*', $counter);
if ($debug) {
print $cur_counter[$i]."(".dechex(ord($cur_counter[$i])).")"." from $counter
";
}
$counter = $counter >> 8;
}
if ($debug) {
foreach ($cur_counter as $char) {
print ord($char) . " ";
}
print "
";
}
$binary = implode($cur_counter);
// Pad to 8 characters
str_pad($binary, 8, chr(0), STR_PAD_LEFT);
if ($debug) {
print "Prior to HMAC calculation pad with zero on the left until 8 characters.
";
print "Calculate sha1 HMAC(Hash-based Message Authentication Code http://en.wikipedia.org/wiki/HMAC).
";
print "hash_hmac ('sha1', $binary, $key)
";
}
$result = hash_hmac ('sha1', $binary, $key);
if ($debug) {
print "Result: $result
";
}
return $result;
}
private static function oath_truncate($hash, $length = 6, $debug=false) {
$result="";
// Convert to dec
if($debug) {
print "converting hex hash into characters
";
}
$hashcharacters = str_split($hash,2);
if($debug) {
print_r($hashcharacters);
print "
and convert to decimals:
";
}
for ($j=0; $j";
print "offset:".$offset;
}
$result = (
(($hmac_result[$offset+0] & 0x7f) << 24 ) |
(($hmac_result[$offset+1] & 0xff) << 16 ) |
(($hmac_result[$offset+2] & 0xff) << 8 ) |
($hmac_result[$offset+3] & 0xff)
) % pow(10,$length);
return $result;
}
}
?>