Source for file totp.php

Documentation is available at totp.php

  1. <?php
  2. /**
  3.  * @package    FrameworkOnFramework
  4.  * @subpackage encrypt
  5.  * @copyright  Copyright (C) 2010 - 2012 Akeeba Ltd. All rights reserved.
  6.  * @license    GNU General Public License version 2 or later; see LICENSE.txt
  7.  */
  8. defined('_JEXEC') or die;
  9.  
  10. /**
  11.  * This class provides an RFC6238-compliant Time-based One Time Passwords,
  12.  * compatible with Google Authenticator (with PassCodeLength = 6 and TimePeriod = 30).
  13.  *
  14.  * @package  FrameworkOnFramework
  15.  * @since    1.0
  16.  */
  17. {
  18.     private $_passCodeLength = 6;
  19.  
  20.     private $_pinModulo;
  21.  
  22.     private $_secretLength = 10;
  23.  
  24.     private $_timeStep = 30;
  25.  
  26.     /**
  27.      * Initialises an RFC6238-compatible TOTP generator. Please note that this
  28.      * class does not implement the constraint in the last paragraph of ยง5.2
  29.      * of RFC6238. It's up to you to ensure that the same user/device does not
  30.      * retry validation within the same Time Step.
  31.      *
  32.      * @param   int  $timeStep        The Time Step (in seconds). Use 30 to be compatible with Google Authenticator.
  33.      * @param   int  $passCodeLength  The generated passcode length. Default: 6 digits.
  34.      * @param   int  $secretLength    The length of the secret key. Default: 10 bytes (80 bits).
  35.      */
  36.     public function __construct($timeStep = 30, $passCodeLength = 6, $secretLength = 10)
  37.     {
  38.         $this->_timeStep       = $timeStep;
  39.         $this->_passCodeLength = $passCodeLength;
  40.         $this->_secretLength   = $secretLength;
  41.         $this->_pinModulo      = pow(10, $this->_passCodeLength);
  42.     }
  43.  
  44.     /**
  45.      * Get the time period based on the $time timestamp and the Time Step
  46.      * defined. If $time is skipped or set to null the current timestamp will
  47.      * be used.
  48.      *
  49.      * @param   int|null $time  Timestamp
  50.      *
  51.      * @return  int  The time period since the UNIX Epoch
  52.      */
  53.     public function getPeriod($time = null)
  54.     {
  55.         if (is_null($time))
  56.         {
  57.             $time = time();
  58.         }
  59.  
  60.         $period = floor($time / $this->_timeStep);
  61.  
  62.         return $period;
  63.     }
  64.  
  65.     /**
  66.      * Check is the given passcode $code is a valid TOTP generated using secret
  67.      * key $secret
  68.      *
  69.      * @param   string  $secret  The Base32-encoded secret key
  70.      * @param   string  $code    The passcode to check
  71.      *
  72.      * @return boolean True if the code is valid
  73.      */
  74.     public function checkCode($secret, $code)
  75.     {
  76.         $time = $this->getPeriod();
  77.  
  78.         for ($i = -1; $i <= 1; $i++)
  79.         {
  80.             if ($this->getCode($secret, $time + $i) == $code)
  81.             {
  82.                 return true;
  83.             }
  84.         }
  85.  
  86.         return false;
  87.     }
  88.  
  89.     /**
  90.      * Gets the TOTP passcode for a given secret key $secret and a given UNIX
  91.      * timestamp $time
  92.      *
  93.      * @param   string  $secret  The Base32-encoded secret key
  94.      * @param   int     $time    UNIX timestamp
  95.      *
  96.      * @return string 
  97.      */
  98.     public function getCode($secret, $time = null)
  99.     {
  100.         $period = $this->getPeriod($time);
  101.         $base32 = new FOFEncryptBase32;
  102.         $secret = $base32->decode($secret);
  103.  
  104.         $time = pack("N", $period);
  105.         $time = str_pad($time, 8, chr(0), STR_PAD_LEFT);
  106.  
  107.         $hash = hash_hmac('sha1', $time, $secret, true);
  108.         $offset = ord(substr($hash, -1));
  109.         $offset = $offset & 0xF;
  110.  
  111.         $truncatedHash = $this->hashToInt($hash, $offset) & 0x7FFFFFFF;
  112.         $pinValue = str_pad($truncatedHash % $this->_pinModulo, $this->_passCodeLength, "0", STR_PAD_LEFT);
  113.  
  114.         return $pinValue;
  115.     }
  116.  
  117.     /**
  118.      * Extracts a part of a hash as an integer
  119.      *
  120.      * @param   string  $bytes  The hash
  121.      * @param   string  $start  The char to start from (0 = first char)
  122.      *
  123.      * @return  string 
  124.      */
  125.     protected function hashToInt($bytes, $start)
  126.     {
  127.         $input = substr($bytes, $start, strlen($bytes) - $start);
  128.         $val2 = unpack("N", substr($input, 0, 4));
  129.  
  130.         return $val2[1];
  131.     }
  132.  
  133.     /**
  134.      * Returns a QR code URL for easy setup of TOTP apps like Google Authenticator
  135.      *
  136.      * @param   string  $user      User
  137.      * @param   string  $hostname  Hostname
  138.      * @param   string  $secret    Secret string
  139.      *
  140.      * @return  string 
  141.      */
  142.     public function getUrl($user, $hostname, $secret)
  143.     {
  144.         $url = sprintf("otpauth://totp/%s@%s?secret=%s", $user, $hostname, $secret);
  145.         $encoder = "https://chart.googleapis.com/chart?chs=200x200&chld=Q|2&cht=qr&chl=";
  146.         $encoderURL = $encoder . urlencode($url);
  147.  
  148.         return $encoderURL;
  149.     }
  150.  
  151.     /**
  152.      * Generates a (semi-)random Secret Key for TOTP generation
  153.      *
  154.      * @return  string 
  155.      */
  156.     public function generateSecret()
  157.     {
  158.         $secret = "";
  159.  
  160.         for ($i = 1; $i <= $this->_secretLength; $i++)
  161.         {
  162.             $c = rand(0, 255);
  163.             $secret .= pack("c", $c);
  164.         }
  165.  
  166.         $base32 = new FOFEncryptBase32;
  167.  
  168.         return $base32->encode($secret);
  169.     }
  170. }

Documentation generated on Tue, 19 Nov 2013 15:15:56 +0100 by phpDocumentor 1.4.3