mirror of
https://github.com/Piwigo/Piwigo.git
synced 2026-03-28 17:42:57 +01:00
* Introduces a verification code step before generating password reset links. * New configuration "password_reset_code_duration". * Adds Base32, TOTP and PHPQRCode classes . * New section is required in password.tpl: code verification won't work on themes not updated yet. * 5 new language strings were added.
116 lines
3.3 KiB
PHP
116 lines
3.3 KiB
PHP
<?php
|
|
defined('PHPWG_ROOT_PATH') or die('Hacking attempt!');
|
|
|
|
require_once(PHPWG_ROOT_PATH . 'include/base32.class.php');
|
|
|
|
class PwgTOTP
|
|
{
|
|
|
|
/**
|
|
* Generate a Base32 secret for TOTP
|
|
*
|
|
* @param string $secret Base32-encoded secret
|
|
* @param int $timestamp 30s intervasl since 1970
|
|
* @return string TOTP Code
|
|
*/
|
|
private static function generateCodeFromTimestamp($secret, $timestamp)
|
|
{
|
|
$key = PwgBase32::decode($secret);
|
|
|
|
$msg = pack('N*', 0) . pack('N*', $timestamp); // hash_hmac need this form
|
|
$hash = hash_hmac('sha1', $msg, $key, true);
|
|
|
|
// RFC 4226, section 5.3
|
|
$offset = ord(substr($hash, -1)) & 0x0F;
|
|
$part = substr($hash, $offset, 4);
|
|
$number = unpack('N', $part)[1] & 0x7FFFFFFF;
|
|
|
|
$code = $number % 1000000; // code 6 digits $number % 10^6
|
|
return str_pad((string)$code, 6, '0', STR_PAD_LEFT); // 123 become 000123
|
|
}
|
|
|
|
/**
|
|
* Generate a Base32 secret for TOTP
|
|
*
|
|
* @param int $length Length in bytes (default: 20)
|
|
* @return string Base32-encoded secret
|
|
*/
|
|
public static function generateSecret($length = 20)
|
|
{
|
|
$random = random_bytes($length);
|
|
return PwgBase32::encode($random, false);
|
|
}
|
|
|
|
/**
|
|
* Get Otp auth url
|
|
*
|
|
* @param string $secret Encoded base32 secret
|
|
* @return string otpauth://totp/ url
|
|
*/
|
|
public static function getOtpAuthUrl($secret)
|
|
{
|
|
global $user;
|
|
$url = substr(get_absolute_root_url(), 0, -1);
|
|
return 'otpauth://totp/'.$user['username'].':'.$url.'?secret='.$secret.'&issuer=Piwigo&algorithm=sha1&digits=6&period=30';
|
|
}
|
|
|
|
/**
|
|
* Get Qr Code
|
|
*
|
|
* @param string $secret Encoded base32 secret
|
|
* @return string data:image/png;base64..
|
|
*/
|
|
public static function getQrCode($secret)
|
|
{
|
|
// require_once(TF_REALPATH . 'include/phpqrcode.php');
|
|
// $otp_url = self::getOtpAuthUrl($secret);
|
|
|
|
// ob_start();
|
|
// QRcode::png($otp_url);
|
|
// $qrcode_image = ob_get_clean();
|
|
// $base64_qrcode = base64_encode($qrcode_image);
|
|
// return 'data:image/png;base64,' . $base64_qrcode;
|
|
}
|
|
|
|
/**
|
|
* Generate a TOTP Code
|
|
*
|
|
* @param string $secret Encoded base32 secret
|
|
* @param int $timestamp timestamp used in second (default: 30)
|
|
* @return string 6 digits TOTP code
|
|
*/
|
|
public static function generateCode($secret, $timestamp = 30)
|
|
{
|
|
$timestamp = floor(time() / $timestamp); // e.g 58338889 > 30-second intervals since 1970 at the moment T
|
|
return self::generateCodeFromTimestamp($secret, $timestamp);
|
|
}
|
|
|
|
/**
|
|
* Verify TOTP Code
|
|
*
|
|
* @param string $code Digits 6 TOTP Code
|
|
* @param string $secret Encoded base32 secret
|
|
* @param int $timestamp timestamp used in second (default: 30)
|
|
* @param int $check_interval Number of 30s steps to check before/after current (default: 1)
|
|
* @return bool
|
|
*/
|
|
public static function verifyCode($code, $secret, $timestamp = 30, $check_interval = 1)
|
|
{
|
|
$timestamp = floor(time() / $timestamp);
|
|
|
|
// generate a totp code for 30s intervals
|
|
// following or preceding the current one and check it
|
|
for ($i=-$check_interval; $i <= $check_interval; $i++)
|
|
{
|
|
$interval_timestamp = $timestamp + $i;
|
|
$generated_code = self::generateCodeFromTimestamp($secret, $interval_timestamp);
|
|
if (hash_equals($generated_code, $code))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|