Phase 6: AIOS security plugin with conservative login lockdown config (10 attempts)

This commit is contained in:
Hanson.xyz Dev
2025-11-28 17:19:54 -06:00
parent 78a744ef06
commit abbd3502e8
430 changed files with 137111 additions and 7 deletions
@@ -0,0 +1,225 @@
<?php
//namespace Base32;
/**
* Base32 encoder and decoder
*
* Last update: 2012-06-20
*
* RFC 4648 compliant
* @link http://www.ietf.org/rfc/rfc4648.txt
*
* Some groundwork based on this class
* https://github.com/NTICompass/PHP-Base32
*
* @author Christian Riesen <chris.riesen@gmail.com>
* @link http://christianriesen.com
* @license MIT License see LICENSE file
*/
class Base32
{
/**
* Table for encoding base32
*
* @var array
*/
private static $encode = array(
0 => 'A',
1 => 'B',
2 => 'C',
3 => 'D',
4 => 'E',
5 => 'F',
6 => 'G',
7 => 'H',
8 => 'I',
9 => 'J',
10 => 'K',
11 => 'L',
12 => 'M',
13 => 'N',
14 => 'O',
15 => 'P',
16 => 'Q',
17 => 'R',
18 => 'S',
19 => 'T',
20 => 'U',
21 => 'V',
22 => 'W',
23 => 'X',
24 => 'Y',
25 => 'Z',
26 => 2,
27 => 3,
28 => 4,
29 => 5,
30 => 6,
31 => 7,
32 => '=',
);
/**
* Table for decoding base32
*
* @var array
*/
private static $decode = array(
'A' => 0,
'B' => 1,
'C' => 2,
'D' => 3,
'E' => 4,
'F' => 5,
'G' => 6,
'H' => 7,
'I' => 8,
'J' => 9,
'K' => 10,
'L' => 11,
'M' => 12,
'N' => 13,
'O' => 14,
'P' => 15,
'Q' => 16,
'R' => 17,
'S' => 18,
'T' => 19,
'U' => 20,
'V' => 21,
'W' => 22,
'X' => 23,
'Y' => 24,
'Z' => 25,
2 => 26,
3 => 27,
4 => 28,
5 => 29,
6 => 30,
7 => 31,
'=' => 32,
);
/**
* Creates an array from a binary string into a given chunk size
*
* @param string $binaryString String to chunk
* @param integer $bits Number of bits per chunk
* @return array
*/
private static function chunk($binaryString, $bits)
{
$binaryString = chunk_split($binaryString, $bits, ' ');
if (substr($binaryString, (strlen($binaryString)) - 1) == ' ') {
$binaryString = substr($binaryString, 0, strlen($binaryString)-1);
}
return explode(' ', $binaryString);
}
/**
* Encodes into base32
*
* @param string $string Clear text string
* @return string Base32 encoded string
*/
public static function encode($string)
{
if (strlen($string) == 0) {
// Gives an empty string
return '';
}
// Convert string to binary
$binaryString = '';
foreach (str_split($string) as $s) {
// Return each character as an 8-bit binary string
$s = decbin(ord($s));
$binaryString .= str_pad($s, 8, 0, STR_PAD_LEFT);
}
// Break into 5-bit chunks, then break that into an array
$binaryArray = self::chunk($binaryString, 5);
// Pad array to be divisible by 8
while (count($binaryArray) % 8 !== 0) {
$binaryArray[] = null;
}
$base32String = '';
// Encode in base32
foreach ($binaryArray as $bin) {
$char = 32;
if (!is_null($bin)) {
// Pad the binary strings
$bin = str_pad($bin, 5, 0, STR_PAD_RIGHT);
$char = bindec($bin);
}
// Base32 character
$base32String .= self::$encode[$char];
}
return $base32String;
}
/**
* Decodes base32
*
* @param string $base32String Base32 encoded string
* @return string Clear text string
*/
public static function decode($base32String)
{
if (strlen($base32String) == 0) {
// Gives an empty string
return '';
}
// Only work in upper cases
$base32String = strtoupper($base32String);
// Remove anything that is not base32 alphabet
$pattern = '/[^A-Z2-7]/';
$replacement = '';
$base32String = preg_replace($pattern, '', $base32String);
$base32Array = str_split($base32String);
$binaryArray = array();
$string = '';
foreach ($base32Array as $str) {
$char = self::$decode[$str];
// Ignore the padding character
if ($char !== 32) {
$char = decbin($char);
$string .= str_pad($char, 5, 0, STR_PAD_LEFT);
}
}
while (strlen($string) %8 !== 0) {
$string = substr($string, 0, strlen($string)-1);
}
$binaryArray = self::chunk($string, 8);
$realString = '';
foreach ($binaryArray as $bin) {
// Pad each value to 8 bits
$bin = str_pad($bin, 8, 0, STR_PAD_RIGHT);
// Convert binary strings to ascii
$realString .= chr(bindec($bin));
}
return $realString;
}
}
@@ -0,0 +1,24 @@
Copyright (c) 2008, Jakob Heuser
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of HOTP-PHP nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY Jakob Heuser ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <copyright holder> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,27 @@
HOTP - PHP Based HMAC One Time Passwords
========================================
**What is HOTP**:
HOTP is a class that simplifies One Time Password systems for PHP Authentication. The HOTP/TOTP Algorithms have been around for a bit, so this is a straightforward class to meet the test vector requirements.
**What works with HOTP/TOTP**:
It's been tested to the test vectors, and I've verified the time-sync hashes against the following:
* Android: Mobile-OTP
* iPhone: OATH Token
**Why would I use this**:
Who wouldn't love a simple drop-in class for HMAC Based One Time Passwords? It's a great extra layer of security (creating two-factor auth) and it's pretty darn zippy.
**Okay you sold me. Give me some docs**:
* $result = HOTP::generateByCounter($key, $counter); // event based
* $result = HOTP::generateByTime($key, $window); // time based within a "window" of time
* $result = HOTP::generateByTimeWindow($key, $window, $min, $max); // same as generateByTime, but for $min windows before and $max windows after
with $result, you can do all sorts of neat things...
* $result->toString();
* $result->toHex();
* $result->doDec();
* $result->toHotp($length); // how many digits in your OTP?
@@ -0,0 +1,160 @@
<?php
/*
HOTP Example File
*/
require_once 'hotp.php';
$key = '12345678901234567890';
$table = array(
'HOTP' => array(
0 => array(
'HMAC' => 'cc93cf18508d94934c64b65d8ba7667fb7cde4b0',
'hex' => '4c93cf18',
'dec' => '1284755224',
'hotp' => '755224',
),
1 => array(
'HMAC' => '75a48a19d4cbe100644e8ac1397eea747a2d33ab',
'hex' => '41397eea',
'dec' => '1094287082',
'hotp' => '287082',
),
2 => array(
'HMAC' => '0bacb7fa082fef30782211938bc1c5e70416ff44',
'hex' => '82fef30',
'dec' => '137359152',
'hotp' => '359152',
),
3 => array(
'HMAC' => '66c28227d03a2d5529262ff016a1e6ef76557ece',
'hex' => '66ef7655',
'dec' => '1726969429',
'hotp' => '969429',
),
4 => array(
'HMAC' => 'a904c900a64b35909874b33e61c5938a8e15ed1c',
'hex' => '61c5938a',
'dec' => '1640338314',
'hotp' => '338314',
),
5 => array(
'HMAC' => 'a37e783d7b7233c083d4f62926c7a25f238d0316',
'hex' => '33c083d4',
'dec' => '868254676',
'hotp' => '254676',
),
6 => array(
'HMAC' => 'bc9cd28561042c83f219324d3c607256c03272ae',
'hex' => '7256c032',
'dec' => '1918287922',
'hotp' => '287922',
),
7 => array(
'HMAC' => 'a4fb960c0bc06e1eabb804e5b397cdc4b45596fa',
'hex' => '4e5b397',
'dec' => '82162583',
'hotp' => '162583',
),
8 => array(
'HMAC' => '1b3c89f65e6c9e883012052823443f048b4332db',
'hex' => '2823443f',
'dec' => '673399871',
'hotp' => '399871',
),
9 => array(
'HMAC' => '1637409809a679dc698207310c8c7fc07290d9e5',
'hex' => '2679dc69',
'dec' => '645520489',
'hotp' => '520489',
),
),
'TOTP' => array(
'59' => array(
'totp' => '94287082',
),
'1111111109' => array(
'totp' => '07081804',
),
'1111111111' => array(
'totp' => '14050471',
),
'1234567890' => array(
'totp' => '89005924',
),
'2000000000' => array(
'totp' => '69279037',
),
),
);
echo <<<DOCBLOCK
<!DOCTYPE><html><head></head><body><pre>
http://www.ietf.org/rfc/rfc4226.txt
http://tools.ietf.org/html/draft-mraihi-totp-timebased-06
TEST VECTOR VERIFICATION
HOTP Tests:
DOCBLOCK;
echo "Count Method Value Pass/Fail\n";
echo "----------------------------------------------------------------------\n";
// loop over all HOTP table results, and calculate the matching value
foreach ($table['HOTP'] as $seed => $results) {
$hotp = HOTP::generateByCounter($key, $seed);
$first = true;
foreach ($results as $type => $calc) {
if ($first) {
echo str_pad($seed, 4, ' ', STR_PAD_LEFT);
$first = false;
}
else {
echo ' ';
}
echo ' ';
echo str_pad($type, 5, ' ', STR_PAD_RIGHT);
echo ' ';
echo str_pad($calc, 47, ' ', STR_PAD_RIGHT);
echo ' ';
$method = 'to'.(ucfirst(str_replace('HMAC', 'string', $type)));
echo str_pad(($calc == $hotp->$method(6)) ? '[OK]' : '[FAIL]', 9, ' ', STR_PAD_LEFT);
echo "\n";
}
}
echo <<<DOCBLOCK
TOTP Tests:
DOCBLOCK;
echo "Time (sec) Value Pass/Fail\n";
echo "----------------------------------------------------------------------\n";
// now echo over the TOTP table
foreach ($table['TOTP'] as $seed => $results) {
$totp = HOTP::generateByTime($key, 30, $seed);
$first = true;
foreach ($results as $type => $calc) {
if ($first) {
echo str_pad($seed, 10, ' ', STR_PAD_LEFT);
$first = false;
}
else {
echo ' ';
}
echo ' ';
echo str_pad($calc, 47, ' ', STR_PAD_RIGHT);
echo ' ';
$method = 'to'.(ucfirst(str_replace('totp', 'hotp', $type)));
echo str_pad(($calc == $totp->$method(8)) ? '[OK]' : '[FAIL]', 9, ' ', STR_PAD_LEFT);
echo "\n";
}
}
echo '</pre></body></html>';
@@ -0,0 +1,174 @@
<?php
/**
* HOTP Class
* Based on the work of OAuth, and the sample implementation of HMAC OTP
* http://tools.ietf.org/html/draft-mraihi-oath-hmac-otp-04#appendix-D
* @author Jakob Heuser (firstname)@felocity.com
* @copyright 2011
* @license BSD
* @version 1.0
*/
class HOTP {
/**
* Generate a HOTP key based on a counter value (event based HOTP)
* @param string $key the key to use for hashing
* @param int $counter the number of attempts represented in this hashing
* @return HOTPResult a HOTP Result which can be truncated or output
*/
public static function generateByCounter($key, $counter) {
// the counter value can be more than one byte long,
// so we need to pack it down properly.
$cur_counter = array(0, 0, 0, 0, 0, 0, 0, 0);
for($i = 7; $i >= 0; $i--) {
$cur_counter[$i] = pack ('C*', $counter);
$counter = $counter >> 8;
}
$bin_counter = implode($cur_counter);
// Pad to 8 chars
if (strlen($bin_counter) < 8) {
$bin_counter = str_repeat (chr(0), 8 - strlen ($bin_counter)) . $bin_counter;
}
// HMAC
$hash = hash_hmac('sha1', $bin_counter, $key);
return new HOTPResult($hash);
}
/**
* Generate a HOTP key based on a timestamp and window size
* @param string $key the key to use for hashing
* @param int $window the size of the window a key is valid for in seconds
* @param int $timestamp a timestamp to calculate for, defaults to time()
* @return HOTPResult a HOTP Result which can be truncated or output
*/
public static function generateByTime($key, $window, $timestamp = false) {
if (!$timestamp && $timestamp !== 0) {
$timestamp = HOTP::getTime();
}
$counter = intval($timestamp / $window);
return HOTP::generateByCounter($key, $counter);
}
/**
* Generate a HOTP key collection based on a timestamp and window size
* all keys that could exist between a start and end time will be included
* in the returned array
* @param string $key the key to use for hashing
* @param int $window the size of the window a key is valid for in seconds
* @param int $min the minimum window to accept before $timestamp
* @param int $max the maximum window to accept after $timestamp
* @param int $timestamp a timestamp to calculate for, defaults to time()
* @return array of HOTPResult
*/
public static function generateByTimeWindow($key, $window, $min = -1, $max = 1, $timestamp = false) {
if (!$timestamp && $timestamp !== 0) {
$timestamp = HOTP::getTime();
}
$counter = intval($timestamp / $window);
$window = range($min, $max);
$out = array();
for ($i = 0; $i < count($window); $i++) {
$shift_counter = $window[$i];
$out[$shift_counter] = HOTP::generateByCounter($key, $counter + $shift_counter);
}
return $out;
}
/**
* Gets the current time
* Ensures we are operating in UTC for the entire framework
* Restores the timezone on exit.
* @return int the current time
*/
public static function getTime() {
return time(); // PHP's time is always UTC
}
}
/**
* The HOTPResult Class converts an HOTP item to various forms
* Supported formats include hex, decimal, string, and HOTP
* @author Jakob Heuser (firstname)@felocity.com
*/
class HOTPResult {
protected $hash;
protected $binary;
protected $decimal;
/**
* Build an HOTP Result
* @param string $value the value to construct with
*/
public function __construct($value) {
// store raw
$this->hash = $value;
}
/**
* Returns the string version of the HOTP
* @return string
*/
public function toString() {
return $this->hash;
}
/**
* Returns the hex version of the HOTP
* @return string
*/
public function toHex() {
if( !$this->hex )
{
$this->hex = dechex($this->toDec());
}
return $this->hex;
}
/**
* Returns the decimal version of the HOTP
* @return int
*/
public function toDec() {
if( !$this->decimal )
{
// store calculate decimal
$hmac_result = array();
// Convert to decimal
foreach(str_split($this->hash,2) as $hex)
{
$hmac_result[] = hexdec($hex);
}
$offset = $hmac_result[19] & 0xf;
$this->decimal = (
(($hmac_result[$offset+0] & 0x7f) << 24 ) |
(($hmac_result[$offset+1] & 0xff) << 16 ) |
(($hmac_result[$offset+2] & 0xff) << 8 ) |
($hmac_result[$offset+3] & 0xff)
);
}
return $this->decimal;
}
/**
* Returns the truncated decimal form of the HOTP
* @param int $length the length of the HOTP to return
* @return string
*/
public function toHOTP($length) {
$str = str_pad($this->toDec(), $length, "0", STR_PAD_LEFT);
$str = substr($str, (-1 * $length));
return $str;
}
}