<?php
/*
* Copyright (c) 2011 Le Lag
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
namespace OTPHP {
/**
* One Time Password Generator
*
* The OTP class allow the generation of one-time
* password that is described in rfc 4xxx.
*
* This is class is meant to be compatible with
* Google Authenticator.
*
* This class was originally ported from the rotp
* ruby library available at https://github.com/mdp/rotp
*/
class OTP {
/**
* The base32 encoded secret key
* @var string
*/
public $secret;
/**
* The algorithm used for the hmac hash function
* @var string
*/
public $digest;
/**
* The number of digits in the one-time password
* @var integer
*/
public $digits;
/**
* Constructor for the OTP class
* @param string $secret the secret key
* @param array $opt options array can contain the
* following keys :
* @param integer digits : the number of digits in the one time password
* Currently Google Authenticator only support 6. Defaults to 6.
* @param string digest : the algorithm used for the hmac hash function
* Google Authenticator only support sha1. Defaults to sha1
*
* @return new OTP class.
*/
public function __construct($secret, $opt = Array()) {
$this->digits = isset($opt['digits']) ? $opt['digits'] : 6;
$this->digest = isset($opt['digest']) ? $opt['digest'] : 'sha1';
$this->secret = $secret;
}
/**
* Generate a one-time password
*
* @param integer $input : number used to seed the hmac hash function.
* This number is usually a counter (HOTP) or calculated based on the current
* timestamp (see TOTP class).
* @return integer the one-time password
*/
public function generateOTP($input) {
$hash = hash_hmac($this->digest, $this->intToBytestring($input), $this->byteSecret());
foreach(str_split($hash, 2) as $hex) { // stupid PHP has bin2hex but no hex2bin WTF
$hmac[] = hexdec($hex);
}
$offset = $hmac[19] & 0xf;
$code = ($hmac[$offset+0] & 0x7F) << 24 |
($hmac[$offset + 1] & 0xFF) << 16 |
($hmac[$offset + 2] & 0xFF) << 8 |
($hmac[$offset + 3] & 0xFF);
return $code % pow(10, $this->digits);
}
/**
* Returns the binary value of the base32 encoded secret
* @access private
* This method should be private but was left public for
* phpunit tests to work.
* @return binary secret key
*/
public function byteSecret() {
return \Base32::decode($this->secret);
}
/**
* Turns an integer in a OATH bytestring
* @param integer $int
* @access private
* @return string bytestring
*/
public function intToBytestring($int) {
$result = Array();
while($int != 0) {
$result[] = chr($int & 0xFF);
$int >>= 8;
}
return str_pad(join(array_reverse($result)), 8, "\000", STR_PAD_LEFT);
}
}
}