'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 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(empty($input)) return; $paddingCharCount = substr_count($input, self::$map[32]); $allowedValues = array(6,4,3,1,0); if(!in_array($paddingCharCount, $allowedValues)) return false; for($i=0; $i<4; $i++){ if($paddingCharCount == $allowedValues[$i] && substr($input, -($allowedValues[$i])) != str_repeat(self::$map[32], $allowedValues[$i])) return false; } $input = str_replace('=','', $input); $input = str_split($input); $binaryString = ""; for($i=0; $i < count($input); $i = $i+8) { $x = ""; if(!in_array($input[$i], self::$map)) return false; for($j=0; $j < 8; $j++) { $x .= str_pad(base_convert(@self::$flippedMap[@$input[$i + $j]], 10, 2), 5, '0', STR_PAD_LEFT); } $eightBits = str_split($x, 8); for($z = 0; $z < count($eightBits); $z++) { $binaryString .= ( ($y = chr(base_convert($eightBits[$z], 2, 10))) || ord($y) == 48 ) ? $y:""; } } 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 = time()/30; for($i=-($rangein30s); $i<=$rangein30s; $i++) { $checktime = (int)($unixtimestamp+$i); $thiskey = self::oath_hotp($key, $checktime); if ((int)$code == self::oath_truncate($thiskey,6)) { return true; } } return false; } public static function getTokenCode($secretkey,$rangein30s = 3) { $result = ""; $key = base32static::decode($secretkey); $unixtimestamp = 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 = 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) { $url = "https://chart.apis.google.com/chart"; $url = $url."?chs=300x300&chld=M|0&cht=qr&chl=otpauth://totp/"; $url = $url.$username . "@" . $domain . "%3Fsecret%3D" . $secretkey . '%26issuer%3D' . rawurlencode($issuer); return $url; } public static function generateRandomClue($length = 16) { $b32 = "234567QWERTYUIOPASDFGHJKLZXCVBNM"; $s = ""; for ($i = 0; $i < $length; $i++) $s .= $b32[rand(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; } }