[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/libraries/src/Encrypt/ -> Totp.php (source)

   1  <?php
   2  
   3  /**
   4   * Joomla! Content Management System
   5   *
   6   * @copyright  (C) 2013 Open Source Matters, Inc. <https://www.joomla.org>
   7   * @license    GNU General Public License version 2 or later; see LICENSE.txt
   8   * @note    This file has been modified by the Joomla! Project and no longer reflects the original work of its author.
   9   */
  10  
  11  namespace Joomla\CMS\Encrypt;
  12  
  13  // phpcs:disable PSR1.Files.SideEffects
  14  \defined('JPATH_PLATFORM') or die;
  15  // phpcs:enable PSR1.Files.SideEffects
  16  
  17  /**
  18   * This class provides an RFC6238-compliant Time-based One Time Passwords,
  19   * compatible with Google Authenticator (with PassCodeLength = 6 and TimePeriod = 30).
  20   *
  21   * @since    4.0.0
  22   */
  23  class Totp
  24  {
  25      /**
  26       * Passcode length
  27       *
  28       * @var   integer
  29       */
  30      private $_passCodeLength = 6;
  31  
  32      /**
  33       * Pin modulo
  34       *
  35       * @var   integer
  36       */
  37      private $_pinModulo;
  38  
  39      /**
  40       * The length of the secret in bytes.
  41       * RFC 4226: "The length of the shared secret MUST be at least 128 bits. This document RECOMMENDs a shared secret length of 160 bits."
  42       * The original value was 10 bytes (80 bits) this value has been increased to 20 (160 bits) with Joomla! 3.9.25
  43       *
  44       * @var   integer
  45       */
  46      private $_secretLength = 20;
  47  
  48      /**
  49       * Timestep
  50       *
  51       * @var   integer
  52       */
  53      private $_timeStep = 30;
  54  
  55      /**
  56       * Base32
  57       *
  58       * @var   integer
  59       */
  60      private $_base32 = null;
  61  
  62      /**
  63       * Initialises an RFC6238-compatible TOTP generator. Please note that this
  64       * class does not implement the constraint in the last paragraph of ยง5.2
  65       * of RFC6238. It's up to you to ensure that the same user/device does not
  66       * retry validation within the same Time Step.
  67       *
  68       * @param   int     $timeStep        The Time Step (in seconds). Use 30 to be compatible with Google Authenticator.
  69       * @param   int     $passCodeLength  The generated passcode length. Default: 6 digits.
  70       * @param   int     $secretLength    The length of the secret key. Default: 10 bytes (80 bits).
  71       * @param   Object  $base32          The base32 en/decrypter
  72       */
  73      public function __construct($timeStep = 30, $passCodeLength = 6, $secretLength = 10, $base32 = null)
  74      {
  75          $this->_timeStep       = $timeStep;
  76          $this->_passCodeLength = $passCodeLength;
  77          $this->_secretLength   = $secretLength;
  78          $this->_pinModulo      = pow(10, $this->_passCodeLength);
  79  
  80          if (\is_null($base32)) {
  81              $this->_base32 = new Base32();
  82          } else {
  83              $this->_base32 = $base32;
  84          }
  85      }
  86  
  87      /**
  88       * Get the time period based on the $time timestamp and the Time Step
  89       * defined. If $time is skipped or set to null the current timestamp will
  90       * be used.
  91       *
  92       * @param   int|null  $time  Timestamp
  93       *
  94       * @return  integer  The time period since the UNIX Epoch
  95       */
  96      public function getPeriod($time = null)
  97      {
  98          if (\is_null($time)) {
  99              $time = time();
 100          }
 101  
 102          $period = floor($time / $this->_timeStep);
 103  
 104          return $period;
 105      }
 106  
 107      /**
 108       * Check is the given passcode $code is a valid TOTP generated using secret
 109       * key $secret
 110       *
 111       * @param   string  $secret  The Base32-encoded secret key
 112       * @param   string  $code    The passcode to check
 113       *
 114       * @return boolean True if the code is valid
 115       */
 116      public function checkCode($secret, $code)
 117      {
 118          $time = $this->getPeriod();
 119  
 120          for ($i = -1; $i <= 1; $i++) {
 121              if ($this->getCode($secret, ($time + $i) * $this->_timeStep) == $code) {
 122                  return true;
 123              }
 124          }
 125  
 126          return false;
 127      }
 128  
 129      /**
 130       * Gets the TOTP passcode for a given secret key $secret and a given UNIX
 131       * timestamp $time
 132       *
 133       * @param   string  $secret  The Base32-encoded secret key
 134       * @param   int     $time    UNIX timestamp
 135       *
 136       * @return string
 137       */
 138      public function getCode($secret, $time = null)
 139      {
 140          $period = $this->getPeriod($time);
 141          $secret = $this->_base32->decode($secret);
 142  
 143          $time = pack("N", $period);
 144          $time = str_pad($time, 8, \chr(0), STR_PAD_LEFT);
 145  
 146          $hash = hash_hmac('sha1', $time, $secret, true);
 147          $offset = \ord(substr($hash, -1));
 148          $offset = $offset & 0xF;
 149  
 150          $truncatedHash = $this->hashToInt($hash, $offset) & 0x7FFFFFFF;
 151          $pinValue = str_pad($truncatedHash % $this->_pinModulo, $this->_passCodeLength, "0", STR_PAD_LEFT);
 152  
 153          return $pinValue;
 154      }
 155  
 156      /**
 157       * Extracts a part of a hash as an integer
 158       *
 159       * @param   string  $bytes  The hash
 160       * @param   string  $start  The char to start from (0 = first char)
 161       *
 162       * @return  string
 163       */
 164      protected function hashToInt($bytes, $start)
 165      {
 166          $input = substr($bytes, $start, \strlen($bytes) - $start);
 167          $val2 = unpack("N", substr($input, 0, 4));
 168  
 169          return $val2[1];
 170      }
 171  
 172      /**
 173       * Returns a QR code URL for easy setup of TOTP apps like Google Authenticator
 174       *
 175       * @param   string  $user      User
 176       * @param   string  $hostname  Hostname
 177       * @param   string  $secret    Secret string
 178       *
 179       * @return  string
 180       */
 181      public function getUrl($user, $hostname, $secret)
 182      {
 183          $url = sprintf("otpauth://totp/%s@%s?secret=%s", $user, $hostname, $secret);
 184          $encoder = "https://chart.googleapis.com/chart?chs=200x200&chld=Q|2&cht=qr&chl=";
 185          $encoderURL = $encoder . urlencode($url);
 186  
 187          return $encoderURL;
 188      }
 189  
 190      /**
 191       * Generates a (semi-)random Secret Key for TOTP generation
 192       *
 193       * @return  string
 194       *
 195       * @note Since 3.9.25 we use the secure method "random_bytes" over the original insecure "rand" function.
 196       *       The random_bytes function has been backported to outdated PHP versions by the core shipped library paragonie/random_compat
 197       */
 198      public function generateSecret()
 199      {
 200          $secret = random_bytes($this->_secretLength);
 201  
 202          return $this->_base32->encode($secret);
 203      }
 204  }


Generated: Wed Sep 7 05:41:13 2022 Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer