| 1 |
<?php |
| 2 |
|
| 3 |
if (!defined('WEBTOTEM_INIT') || WEBTOTEM_INIT !== true) { |
| 4 |
if (!headers_sent()) { |
| 5 |
/* Report invalid access if possible. */ |
| 6 |
header('HTTP/1.1 403 Forbidden'); |
| 7 |
} |
| 8 |
exit(1); |
| 9 |
} |
| 10 |
|
| 11 |
// Licensed under the Apache License, Version 2.0 (the "License"); |
| 12 |
// you may not use this file except in compliance with the License. |
| 13 |
// You may obtain a copy of the License at |
| 14 |
// |
| 15 |
// http://www.apache.org/licenses/LICENSE-2.0 |
| 16 |
// |
| 17 |
// Unless required by applicable law or agreed to in writing, software |
| 18 |
// distributed under the License is distributed on an "AS IS" BASIS, |
| 19 |
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 20 |
// See the License for the specific language governing permissions and |
| 21 |
// limitations under the License. |
| 22 |
|
| 23 |
|
| 24 |
include_once("FixedByteNotation.php"); |
| 25 |
|
| 26 |
|
| 27 |
class WebTotemGoogleAuthenticator { |
| 28 |
static $PASS_CODE_LENGTH = 6; |
| 29 |
static $PIN_MODULO; |
| 30 |
static $SECRET_LENGTH = 10; |
| 31 |
|
| 32 |
public function __construct() { |
| 33 |
self::$PIN_MODULO = pow(10, self::$PASS_CODE_LENGTH); |
| 34 |
} |
| 35 |
|
| 36 |
public function checkCode($secret, $code) { |
| 37 |
$time = floor(time() / 30); |
| 38 |
for ( $i = -1; $i <= 1; $i++) { |
| 39 |
|
| 40 |
if ($this->getCode($secret,$time + $i) == $code) { |
| 41 |
return true; |
| 42 |
} |
| 43 |
} |
| 44 |
|
| 45 |
return false; |
| 46 |
|
| 47 |
} |
| 48 |
|
| 49 |
public function getCode($secret, $time = null) { |
| 50 |
|
| 51 |
if (!$time) { |
| 52 |
$time = floor(time() / 30); |
| 53 |
} |
| 54 |
$base32 = new WebTotemFixedBitNotation(5, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567', TRUE, TRUE); |
| 55 |
$secret = $base32->decode($secret); |
| 56 |
|
| 57 |
$time = pack("N", $time); |
| 58 |
$time = str_pad($time,8, chr(0), STR_PAD_LEFT); |
| 59 |
|
| 60 |
$hash = hash_hmac('sha1',$time,$secret,true); |
| 61 |
$offset = ord(substr($hash,-1)); |
| 62 |
$offset = $offset & 0xF; |
| 63 |
|
| 64 |
$truncatedHash = self::hashToInt($hash, $offset) & 0x7FFFFFFF; |
| 65 |
$pinValue = str_pad($truncatedHash % self::$PIN_MODULO,6,"0",STR_PAD_LEFT);; |
| 66 |
return $pinValue; |
| 67 |
} |
| 68 |
|
| 69 |
protected function hashToInt($bytes, $start) { |
| 70 |
$input = substr($bytes, $start, strlen($bytes) - $start); |
| 71 |
$val2 = unpack("N",substr($input,0,4)); |
| 72 |
return $val2[1]; |
| 73 |
} |
| 74 |
|
| 75 |
public function getUrl($user, $hostname, $secret) { |
| 76 |
// $url = sprintf("otpauth://totp/%s@%s?secret=%s", $user, $hostname, $secret); |
| 77 |
// $encoder = "https://www.google.com/chart?chs=200x200&chld=M|0&cht=qr&chl="; |
| 78 |
// $encoderURL = sprintf( "%sotpauth://totp/%s@%s&secret=%s",$encoder, $user, $hostname, $secret); |
| 79 |
|
| 80 |
$url = "otpauth://totp/" . rawurlencode($hostname . ' (' . $user . ')') . '?secret=' . $secret . '&algorithm=SHA1&digits=6&period=30&issuer=WebTotem'; |
| 81 |
|
| 82 |
return $url; |
| 83 |
} |
| 84 |
|
| 85 |
public function generateSecret() { |
| 86 |
$secret = ""; |
| 87 |
for($i = 1; $i<= self::$SECRET_LENGTH; $i++) { |
| 88 |
$c = rand(0,255); |
| 89 |
$secret .= pack("c",$c); |
| 90 |
} |
| 91 |
$base32 = new WebTotemFixedBitNotation(5, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567', TRUE, TRUE); |
| 92 |
return $base32->encode($secret); |
| 93 |
} |
| 94 |
|
| 95 |
} |
| 96 |
|
| 97 |
|