ga_login_test.module in Google Authenticator login 7
ga_login_test module.
File
tests/ga_login_test/ga_login_test.moduleView source
<?php
/**
* @file
* ga_login_test module.
*/
/**
* Implements hook_boot().
*/
function ga_login_test_boot() {
global $drupal_hash_salt, $databases;
if (empty($drupal_hash_salt)) {
$clone = $databases;
$clone['default']['default']['prefix'] = "";
$drupal_hash_salt = hash('sha256', serialize($clone));
}
}
/**
* Generate the code for a given secret.
*
* @param string $secret
* Secret as generated by the ga4php class.
* @param int $timestamp_offset
* Timestamp offset.
*
* @returns int
* Returns 6 digit code.
*/
function ga_login_test_generate_code($secret, $timestamp_offset = 0) {
$timestamp = Google2FA::getTimestamp();
$timestamp += $timestamp_offset;
$secretkey = Google2FA::base32Decode($secret);
return Google2FA::oathHotp($secretkey, $timestamp);
}
/**
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* PHP Google two-factor authentication module.
*
* @see http://www.idontplaydarts.com/2011/07/google-totp-two-factor-authentication-for-php/
*
* @author Phil
**/
class Google2FA {
// Interval between key regeneration.
const KEYREGENERATION = 30;
// Length of the Token generated.
const OTPLENGTH = 6;
// Lookup needed for Base32 encoding.
private static $lut = array(
"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.
*
* @param int $length
* Length of the key
*
* @return string
* Secret key
*/
public static function generateSecretKey($length = 16) {
$b32 = "234567QWERTYUIOPASDFGHJKLZXCVBNM";
$s = "";
for ($i = 0; $i < $length; $i++) {
$s .= $b32[rand(0, 31)];
}
return $s;
}
/**
* Returns the current ts devided by the KEYREGENERATION period.
*
* @return int
* Timestamp
*/
public static function getTimestamp() {
return floor(microtime(TRUE) / self::KEYREGENERATION);
}
/**
* Decodes a base32 string into a binary string.
*/
public static function base32Decode($b32) {
$b32 = strtoupper($b32);
if (!preg_match('/^[ABCDEFGHIJKLMNOPQRSTUVWXYZ234567]+$/', $b32, $match)) {
throw new Exception('Invalid characters in the base32 string.');
}
$l = strlen($b32);
$n = 0;
$j = 0;
$binary = "";
for ($i = 0; $i < $l; $i++) {
// Move buffer left by 5 to make room.
$n = $n << 5;
// Add value into buffer.
$n = $n + self::$lut[$b32[$i]];
// Keep track of number of bits in buffer.
$j = $j + 5;
if ($j >= 8) {
$j = $j - 8;
$binary .= chr(($n & 0xff << $j) >> $j);
}
}
return $binary;
}
/**
* Takes the secret key and the timestamp and returns the OTP.
*
* @param binary $key
* Secret key in binary form.
* @param int $counter
* Timestamp as returned by getTimestamp.
*
* @return string
* The OTP
*/
public static function oathHotp($key, $counter) {
if (strlen($key) < 8) {
throw new Exception('Secret key is too short. Must be at least 16 base 32 characters');
}
// Counter must be 64-bit int.
$bin_counter = pack('N*', 0) . pack('N*', $counter);
$hash = hash_hmac('sha1', $bin_counter, $key, TRUE);
return str_pad(self::oathTruncate($hash), self::OTPLENGTH, '0', STR_PAD_LEFT);
}
/**
* Verifys a key against the current timestamp.
*
* @param string $b32seed
* Seed to use
* @param string $key
* User specified key
* @param int $window
* How many windows to use.
* @param bool $use_timestamp
* Use the timestamp.
*
* @return bool
* TRUE is key is valid.
*/
public static function verifyKey($b32seed, $key, $window = 4, $use_timestamp = TRUE) {
$timestamp = self::getTimestamp();
if ($use_timestamp !== TRUE) {
$timestamp = (int) $use_timestamp;
}
$binary_seed = self::base32Decode($b32seed);
for ($ts = $timestamp - $window; $ts <= $timestamp + $window; $ts++) {
if (self::oathHotp($binary_seed, $ts) == $key) {
return TRUE;
}
}
return FALSE;
}
/**
* Extracts the OTP from the SHA1 hash.
*
* @param binary $hash
* Hash to extract OTP from.
*
* @return int
* OTP.
*/
public static function oathTruncate($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);
}
}
Functions
Name | Description |
---|---|
ga_login_test_boot | Implements hook_boot(). |
ga_login_test_generate_code | Generate the code for a given secret. |