Skip to content

Instantly share code, notes, and snippets.

@nitrix
Created October 29, 2012 04:48
Show Gist options
  • Save nitrix/3971583 to your computer and use it in GitHub Desktop.
Save nitrix/3971583 to your computer and use it in GitHub Desktop.
Google two-way authentification in PHP
<?php
include('g2w.php');
$initkey = 'PEHMPSDNLXIOG65R'; // set the inital key or use generate_secret_key() for a new one
$timestamp = G2W::get_timestamp();
$secretkey = G2W::base32_decode($initkey); // decode it into binary
$otp = G2W::oath_hotp($secretkey, $timestamp); // get current token
echo 'Init key: '.$initkey."\n";
echo 'Timestamp: '.$timestamp."\n";
echo 'One time password: '.$otp."\n";
// --- use this to verify a key as it allows for some time drift ---
$result = G2W::verify_key($initkey, '848534'); // test it here
var_dump($result);
?>
<?php
//Google two-way authentification
class G2W {
const keyRegeneration = 30; // Interval between key regeneration
const otpLength = 6; // Length of the one-time password
private static $lut = array( // Lookup needed for Base32 encoding
'A' => 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
);
// Generates a 16 digit secret key in base32 format
public static function generate_secret_key($length = 16) {
$b32 = '234567QWERTYUIOPASDFGHJKLZXCVBNM';
$s = '';
for ($i = 0; $i < $length; $i++)
$s .= $b32[rand(0,31)];
return $s;
}
// Returns the current Unix Timestamp devided by the keyRegeneration
public static function get_timestamp() {
return floor(microtime(true)/self::keyRegeneration);
}
// Decodes a base32 string into a binary string.
public static function base32_decode($b32) {
$b32 = strtoupper($b32);
if (!preg_match('/^[ABCDEFGHIJKLMNOPQRSTUVWXYZ234567]+$/', $b32, $match))
throw new Exception('Invalid characters in the base32 string.');
$l = strlen($b32);
$n = $j = 0;
$binary = '';
for ($i = 0; $i < $l; $i++) {
$n = $n << 5; // Move buffer left by 5 to make room
$n = $n + self::$lut[$b32[$i]]; // Add value into buffer
$j += 5; // Keep track of number of bits in buffer
if ($j >= 8) {
$j = $j - 8;
$binary .= chr(($n & (0xFF << $j)) >> $j);
}
}
return $binary;
}
// Takes the secret key and the timestamp and returns the one time password.
public static function oath_hotp($key, $counter)
{
if (strlen($key) < 8)
throw new Exception('Secret key is too short. Must be at least 16 base 32 characters');
$bin_counter = pack('N*', 0) . pack('N*', $counter); // Counter must be 64-bit int
$hash = hash_hmac ('sha1', $bin_counter, $key, true);
return str_pad(self::oath_truncate($hash), self::otpLength, '0', STR_PAD_LEFT);
}
// Verifys a user inputted key against the current timestamp.
// Checks $windos keys either side of the timestamp.
public static function verify_key($b32seed, $key, $window = 4, $useTimeStamp = true)
{
$timeStamp = self::get_timestamp();
if ($useTimeStamp !== true)
$timeStamp = (int)$useTimeStamp;
$binarySeed = self::base32_decode($b32seed);
for ($ts = $timeStamp - $window; $ts <= $timeStamp + $window; $ts++)
if (self::oath_hotp($binarySeed, $ts) == $key)
return true;
return false;
}
// Extracts the OTP from the SHA1 hash.
public static function oath_truncate($hash)
{
$offset = ord($hash[19]) & 0xf;
return (
((ord($hash[$offset+0]) & 0x7f) << 24 ) |
((ord($hash[$offset+1]) & 0xff) << 16 ) |
((ord($hash[$offset+2]) & 0xff) << 8 ) |
(ord($hash[$offset+3]) & 0xff)
) % pow(10, self::otpLength);
}
}
?>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment