[ Index ] |
PHP Cross Reference of Joomla 4.2.2 documentation |
[Summary view] [Print] [Text view]
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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Sep 7 05:41:13 2022 | Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer |