From 5068968581202530d4aa4b973347b33a660bd40c Mon Sep 17 00:00:00 2001 From: "eapl.mx" Date: Thu, 23 Nov 2023 15:56:48 -0600 Subject: [PATCH] Implement support for TOTP Add TOTP.php and allow password or TOTP in session.php --- libs/TOTP.php | 166 ++++++++++++++++++++++++++++++++++++++++++++++ libs/session.php | 22 ++++-- partials/base.php | 4 +- views/login.php | 18 +++-- 4 files changed, 193 insertions(+), 17 deletions(-) create mode 100644 libs/TOTP.php diff --git a/libs/TOTP.php b/libs/TOTP.php new file mode 100644 index 0000000..1ef0173 --- /dev/null +++ b/libs/TOTP.php @@ -0,0 +1,166 @@ +\n"; + + for ($currentWindow = -$window; $currentWindow <= $window; $currentWindow++) { + $time = $timeSlice + $currentWindow; + $generatedCode = generateTOTP($secret, $digits, $time); + #echo "$time $generatedCode
\n"; + + if ($generatedCode === $enteredCodeInt) { + return true; // Code is valid within the window + } + } + + return false; // Code is not valid +} + +function generateTOTP($secret, $digits = 6, $timeSlice = null) { + if ($timeSlice === null) { + $timeSlice = floor(time() / 30); + } + + $secret = base32Decode($secret); + $timeSlice = pack('N*', 0) . pack('N*', $timeSlice); + + $hash = hash_hmac('sha1', $timeSlice, $secret, true); + $offset = ord($hash[19]) & 0xf; + + $otp = ( + (ord($hash[$offset + 0]) & 0x7f) << 24 | + (ord($hash[$offset + 1]) & 0xff) << 16 | + (ord($hash[$offset + 2]) & 0xff) << 8 | + (ord($hash[$offset + 3]) & 0xff) + ); + + #var_dump($otp); + #echo "
\n"; + + // When digits is 10, the padding was giving a wrong OTP + // Just return the calculated value + if ($digits === 10) { + return $otp; + } + + $otp = $otp % pow(10, $digits); + return str_pad($otp, $digits, '0', STR_PAD_LEFT); +} + +function base32Decode($base32) { + $base32chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'; + $base32charsFlipped = array_flip(str_split($base32chars)); + $paddingChar = '='; + $paddingCharCount = 0; + + if (strpos($base32, $paddingChar) !== false) { + $paddingCharCount = substr_count($base32, $paddingChar); + } + + $allowedValues = array(6, 4, 3, 1, 0); + + if (!in_array($paddingCharCount, $allowedValues)) { + return false; + } + + for ($i = 0; $i < 4; ++$i) { + if ($paddingCharCount == $allowedValues[$i] && + substr($base32, -$allowedValues[$i]) != str_repeat($paddingChar, $allowedValues[$i]) + ) { + return false; + } + } + + $base32 = str_replace($paddingChar, '', $base32); + $base32 = str_split($base32); + $binaryString = ''; + + foreach ($base32 as $char) { + if (!isset($base32charsFlipped[$char])) { + return false; // Invalid character found + } + $binaryString .= sprintf('%05b', $base32charsFlipped[$char]); + } + + $length = strlen($binaryString); + $offset = 0; + $binaryData = ''; + + while ($offset < $length) { + $binaryChunk = substr($binaryString, $offset, 8); + + if (strlen($binaryChunk) < 8) { + $binaryChunk = str_pad($binaryChunk, 8, '0', STR_PAD_RIGHT); + } + + $decimalChunk = bindec($binaryChunk); + $binaryData .= pack('C', $decimalChunk); + $offset += 8; + } + + return $binaryData; +} + +function base32Encode($input) { + $base32Chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'; + $base32String = ''; + $position = 0; + $carry = 0; + foreach (str_split($input) as $byte) { + $carry |= ord($byte) << $position; + $position += 8; + while ($position >= 5) { + $base32String .= $base32Chars[$carry & 31]; + $carry >>= 5; + $position -= 5; + } + } + if ($position > 0) { + if ($carry & 31) { + $base32String .= $base32Chars[$carry & 31]; + } + } + return $base32String; +} + +function generateRandomSecret($length = 32) { + // https://medium.com/@nicola88/two-factor-authentication-with-totp-ccc5f828b6df + $bytes = random_bytes($length); + $base32 = base32Encode($bytes); + + return substr($base32, 0, $length); +} + +# Examples of usage (To move somewhere else) +/* +$randomSecret = generateRandomSecret(); +echo "Random Secret Key: $randomSecret
\n"; + +$secret = 'K3OBZ7XPR6T4PTNXSNCQ'; +$enteredCode = '123456'; +$digits = 6; +if (isset($_GET['c']) && isset($_GET['s'])) { + $enteredCode = $_GET['c']; + $secret = $_GET['s']; + $isCodeValid = verifyTOTP($secret, $digits, $enteredCode); + + if ($isCodeValid) { + echo "Code $enteredCode is valid!
"; + } else { + echo "Code $enteredCode is invalid!
"; + } +} + +$code = generateTOTP($secret); +echo "TOTP code: $code
\n"; + +$code = generateTOTP($secret, 8); +echo "TOTP code: $code
\n"; +*/ diff --git a/libs/session.php b/libs/session.php index 28ffa4a..bb9f9fc 100644 --- a/libs/session.php +++ b/libs/session.php @@ -1,18 +1,30 @@ - +
-

Enter password:

+

Enter password or TOTP

-
+