Skip to content

Instantly share code, notes, and snippets.

@rwaddin
Last active January 11, 2022 03:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rwaddin/b20204e372038393db00cb7ac57b34bf to your computer and use it in GitHub Desktop.
Save rwaddin/b20204e372038393db00cb7ac57b34bf to your computer and use it in GitHub Desktop.
this script for generate code to tblclients field authdata. for verify token 2 FA whmcs. This is not clean code just example base on sistem verify whmcs.
<?php
$kd_manual = createBase32Key();
$tokenkey = helperb322hex($kd_manual);
$qrcode = helperhex2b32($tokenkey);
$backupcode = "123456789012345";
$init = [
"tokenkey" => $tokenkey,
"tokentype" => "TOTP",
"tokentimer" => 30,
"tokencounter" => 1,
"tokenalgorithm" => "SHA1",
"user" => ""
];
$step2 = serialize($init);
$step3 = base64_encode($step2);
$step4 = [
"tokendata" => $step3,
"backupcode" => sha1($backupcode)
];
$step5 = serialize($step4);
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Generate manual 2FA WHMCS</title>
<!-- <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css"> -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css">
<style>
.card-header{
color: white;
font-weight: bold;
}
</style>
</head>
<body>
<div class="container" style="margin-top: 4%;">
<table class="table table-striped">
<tr>
<td>Kode jika tambah manual tidak scan qrcode </td>
<td>:</td>
<td><?php echo $kd_manual ?></td>
</tr>
<tr>
<td>Token key for the database</td>
<td>:</td>
<td><?php echo $tokenkey ?></td>
</tr>
<tr>
<td>qrcode</td>
<td>:</td>
<td>otpauth://totp/companyName:user@mail.com?secret=<?php echo $qrcode ?></td>
</tr>
<tr>
<td>backupcode</td>
<td>:</td>
<td><?php echo $backupcode ?></td>
</tr>
</table>
<h1 class="text-center alert alert-info">Generate code : </h1>
<div class="row">
<div class="col-sm-4">
<div class="card shadow">
<div class="card-header bg-success">
1 : init
</div>
<div class="card-body">
<?php print_r($init) ?>
</div>
</div>
</div>
<div class="col-sm-4">
<div class="card shadow">
<div class="card-header bg-warning">2 : serialize</div>
<div class="card-body">
<p style="word-break: break-word;">
<?= $step2 ?>
</p>
</div>
</div>
</div>
<div class="col-sm-4">
<div class="card shadow">
<div class="card-header bg-info">3 base 64encode</div>
<div class="card-body">
<div style="word-break: break-word;">
<?= $step3 ?>
</div>
</div>
</div>
</div>
<div class="col-sm-4">
<div class="card shadow">
<div class="card-header bg-danger">4 merge</div>
<div class="card-body">
<div style="word-break: break-word;">
<?php print_r($step4) ?>
</div>
</div>
</div>
</div>
<div class="col-sm-4">
<div class="card shadow">
<div class="card-header bg-primary">5 finish</div>
<div class="card-body">
<p class="badge badge-info">Copy below to authdata field</p>
<div style="word-break: break-word;">
<?= $step5 ?>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
<?php
function createBase32Key()
{
$alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
$key = "";
for ($i = 0; $i < 16; $i++) {
$offset = rand(0, strlen($alphabet) - 1);
$key .= $alphabet[$offset];
}
return $key;
}
function helperb322hex($b32)
{
$alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
$out = "";
$dous = "";
for ($i = 0; $i < strlen($b32); $i++) {
$in = strrpos($alphabet, $b32[$i]);
$b = str_pad(base_convert($in, 10, 2), 5, "0", STR_PAD_LEFT);
$out .= $b;
$dous .= $b . ".";
}
$ar = str_split($out, 20);
$out2 = "";
foreach ($ar as $val) {
$rv = str_pad(base_convert($val, 2, 16), 5, "0", STR_PAD_LEFT);
$out2 .= $rv;
}
return $out2;
}
function helperhex2b32($hex)
{
$alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
$ar = str_split($hex, 5);
$out = "";
foreach ($ar as $var) {
$bc = base_convert($var, 16, 2);
$bin = str_pad($bc, 20, "0", STR_PAD_LEFT);
$out .= $bin;
}
$out2 = "";
$ar2 = str_split($out, 5);
foreach ($ar2 as $var2) {
$bc = base_convert($var2, 2, 10);
$out2 .= $alphabet[$bc];
}
return $out2;
}
?>
<?php
function backupCode($userid = false)
{
$backupCode = sha1("companyname" . $userid . time());
$backupCode = substr($backupCode, 0, 16);
$pisah = substr($backupCode, 0, 4) . " " . substr($backupCode, 4, 4) . " " . substr($backupCode, 8, 4) . " " . substr($backupCode, 12, 4);
return [
"encrypt" => sha1($backupCode),
"plain" => $pisah
];
}
<?php
$key = "028988"; # key show in google authenticator or other app
$username = "companyname";
$username = "WHMCS:" . $username;
# in tblconfiguration setting='TOTPUsedOTPs'
# for check history code
$usedotps = [];
$hash = md5($username . $key);
if (array_key_exists($hash, $usedotps)) {
echo "exist : cannot use this key";
}else{
echo "insert to db";
}
echo "<br>";
echo $hash;
echo "<br>";
$auth = authenticateUser($username, $key);
if ($auth) {
echo "verified";
}else{
echo "unverified";
}
function authenticateUser($username, $code)
{
if (preg_match("/[0-9][0-9][0-9][0-9][0-9][0-9]/", $code) < 1) {
return false;
}
$ttype = "TOTP";
$tlid = 1;
$tkey = "a19b90d4e0e19713274e";
$ttimer = 30;
$totpSkew = 1;
switch ($ttype) {
case "TOTP":
$t_now = time();
$t_ear = $t_now - $totpSkew * $ttimer;
$t_lat = $t_now + $totpSkew * $ttimer;
$t_st = (int) ($t_ear / $ttimer);
$t_en = (int) ($t_lat / $ttimer);
for ($i = $t_st; $i <= $t_en; $i++) {
$stest = oath_hotp($tkey, $i);
if ($code == $stest) {
return true;
}
}
break;
default:
return false;
}
return false;
}
function oath_hotp($key, $counter)
{
$key = pack("H*", $key);
$cur_counter = array(0, 0, 0, 0, 0, 0, 0, 0);
for ($i = 7; 0 <= $i; $i--) {
$cur_counter[$i] = pack("C*", $counter);
$counter = $counter >> 8;
}
$bin_counter = implode($cur_counter);
if (strlen($bin_counter) < 8) {
$bin_counter = str_repeat(chr(0), 8 - strlen($bin_counter)) . $bin_counter;
}
$hash = hash_hmac("sha1", $bin_counter, $key);
return str_pad(oath_truncate($hash), 6, "0", STR_PAD_LEFT);
}
function oath_truncate($hash, $length = 6)
{
foreach (str_split($hash, 2) as $hex) {
$hmac_result[] = hexdec($hex);
}
$offset = $hmac_result[19] & 15;
return (($hmac_result[$offset + 0] & 127) << 24 | ($hmac_result[$offset + 1] & 255) << 16 | ($hmac_result[$offset + 2] & 255) << 8 | $hmac_result[$offset + 3] & 255) % pow(10, $length);
}
@rwaddin
Copy link
Author

rwaddin commented Jan 10, 2022

has been try in whmcs 7.7.1-release.1 and work well

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment