| @@ -1,128 +1,119 @@ | ||
| 1 | -<?php | |
| 2 | - | |
| 3 | -// Do not allow the file to be called directly. | |
| 4 | -if ( ! defined( 'ABSPATH' ) ) { | |
| 5 | - exit; | |
| 6 | -} | |
| 7 | - | |
| 8 | -require_once dirname( __FILE__ ) . '/base32.php'; | |
| 9 | - | |
| 10 | -class TokenAuth6238 { | |
| 11 | - | |
| 12 | - /** | |
| 13 | - * Verify the code & token. | |
| 14 | - * | |
| 15 | - * @param string $secretkey Secret clue (base 32). | |
| 16 | - * @return bool True if success, false if failure | |
| 17 | - */ | |
| 18 | - public static function verify( $secretkey, $code, $rangein30s = 3 ) { | |
| 19 | - $key = Base32Static::decode( $secretkey ); | |
| 20 | - $unixtimestamp = time() / 30; | |
| 21 | - | |
| 22 | - // Without a valid decoded key there is nothing to verify against, and | |
| 23 | - // passing an empty key to hash_hmac() is deprecated on PHP 8.1+. | |
| 24 | - if ( ! is_string( $key ) || $key === '' ) { | |
| 25 | - return false; | |
| 26 | - } | |
| 27 | - | |
| 28 | - for ( $i = -( $rangein30s ); $i <= $rangein30s; $i++ ) { | |
| 29 | - $checktime = (int) ( $unixtimestamp + $i ); | |
| 30 | - $thiskey = self::oath_hotp( $key, $checktime ); | |
| 31 | - | |
| 32 | - // oath_truncate() returns an int, so zero-pad to 6 digits to match the | |
| 33 | - // codes authenticator apps display (e.g. "012345"). | |
| 34 | - $computed = str_pad( (string) self::oath_truncate( $thiskey, 6 ), 6, '0', STR_PAD_LEFT ); | |
| 35 | - if ( self::stringEquals( $computed, (string) $code ) ) { | |
| 36 | - return true; | |
| 37 | - } | |
| 38 | - } | |
| 39 | - | |
| 40 | - return false; | |
| 41 | - } | |
| 42 | - | |
| 43 | - /** | |
| 44 | - * Generate the random clue/key. | |
| 45 | - * | |
| 46 | - * @param integer $length | |
| 47 | - * @return string | |
| 48 | - */ | |
| 49 | - public static function generateRandomClue( $length = 16 ) { | |
| 50 | - if ( function_exists( 'random_bytes' ) ) { | |
| 51 | - return Base32Static::encode( random_bytes( 10 ) ); | |
| 52 | - } | |
| 53 | - | |
| 54 | - require_once dirname( __FILE__ ) . '/polyfill/lib/random.php'; | |
| 55 | - return Base32Static::encode( random_bytes( 10 ) ); | |
| 56 | - } | |
| 57 | - | |
| 58 | - /** | |
| 59 | - * | |
| 60 | - * @param string $key | |
| 61 | - * @param integer $counter | |
| 62 | - * @return string | |
| 63 | - */ | |
| 64 | - private static function oath_hotp( $key, $counter ) { | |
| 65 | - $cur_counter = [ 0, 0, 0, 0, 0, 0, 0, 0 ]; | |
| 66 | - | |
| 67 | - for ( $i = 7; $i >= 0; $i-- ) { // C for unsigned char, * for repeating to the end of the input data | |
| 68 | - $cur_counter[ $i ] = pack( 'C*', $counter ); | |
| 69 | - $counter = $counter >> 8; | |
| 70 | - } | |
| 71 | - | |
| 72 | - $binary = implode( $cur_counter ); | |
| 73 | - | |
| 74 | - // Pad to 8 characters | |
| 75 | - $binary = str_pad( $binary, 8, chr( 0 ), STR_PAD_LEFT ); | |
| 76 | - return hash_hmac( 'sha1', $binary, $key ); | |
| 77 | - } | |
| 78 | - | |
| 79 | - /** | |
| 80 | - * Truncate | |
| 81 | - * | |
| 82 | - * @param string $hash | |
| 83 | - * @param integer $length | |
| 84 | - * @return boolean | |
| 85 | - */ | |
| 86 | - private static function oath_truncate( $hash, $length = 6 ) { | |
| 87 | - $hashcharacters = str_split( $hash, 2 ); | |
| 88 | - | |
| 89 | - for ( $j = 0; $j < count( $hashcharacters ); $j++ ) { | |
| 90 | - $hmac_result[] = hexdec( $hashcharacters[ $j ] ); | |
| 91 | - } | |
| 92 | - | |
| 93 | - $offset = $hmac_result[19] & 0xf; | |
| 94 | - return ( | |
| 95 | - ( ( $hmac_result[ $offset + 0 ] & 0x7f ) << 24 ) | | |
| 96 | - ( ( $hmac_result[ $offset + 1 ] & 0xff ) << 16 ) | | |
| 97 | - ( ( $hmac_result[ $offset + 2 ] & 0xff ) << 8 ) | | |
| 98 | - ( $hmac_result[ $offset + 3 ] & 0xff ) | |
| 99 | - ) % pow( 10, $length ); | |
| 100 | - } | |
| 101 | - | |
| 102 | - /** | |
| 103 | - * Compare 2 strings with each other. | |
| 104 | - * | |
| 105 | - * @param string $own | |
| 106 | - * @param string $user | |
| 107 | - * @return boolean | |
| 108 | - */ | |
| 109 | - private static function stringEquals( $own, $user ) { | |
| 110 | - if ( function_exists( 'hash_equals' ) ) { | |
| 111 | - return hash_equals( $own, $user ); | |
| 112 | - } | |
| 113 | - | |
| 114 | - $safeLen = strlen( $own ); | |
| 115 | - $userLen = strlen( $user ); | |
| 116 | - | |
| 117 | - if ( $userLen != $safeLen ) { | |
| 118 | - return false; | |
| 119 | - } | |
| 120 | - | |
| 121 | - $result = 0; | |
| 122 | - for ( $i = 0; $i < $userLen; $i++ ) { | |
| 123 | - $result |= ( ord( $own[$i] ) ^ ord( $user[$i] ) ); | |
| 124 | - } | |
| 125 | - | |
| 126 | - return $result === 0; | |
| 127 | - } | |
| 128 | -} | |
| 1 | +<?php | |
| 2 | + | |
| 3 | +// Do not allow the file to be called directly. | |
| 4 | +if ( ! defined( 'ABSPATH' ) ) { | |
| 5 | + exit; | |
| 6 | +} | |
| 7 | + | |
| 8 | +require_once dirname( __FILE__ ) . '/base32.php'; | |
| 9 | + | |
| 10 | +class TokenAuth6238 { | |
| 11 | + | |
| 12 | + /** | |
| 13 | + * Verify the code & token. | |
| 14 | + * | |
| 15 | + * @param string $secretkey Secret clue (base 32). | |
| 16 | + * @return bool True if success, false if failure | |
| 17 | + */ | |
| 18 | + public static function verify( $secretkey, $code, $rangein30s = 3 ) { | |
| 19 | + $key = Base32Static::decode( $secretkey ); | |
| 20 | + $unixtimestamp = time() / 30; | |
| 21 | + | |
| 22 | + for ( $i = -( $rangein30s ); $i <= $rangein30s; $i++ ) { | |
| 23 | + $checktime = (int) ( $unixtimestamp + $i ); | |
| 24 | + $thiskey = self::oath_hotp( $key, $checktime ); | |
| 25 | + | |
| 26 | + if ( self::stringEquals( (string) self::oath_truncate( $thiskey, 6 ), (string) $code ) ) { | |
| 27 | + return true; | |
| 28 | + } | |
| 29 | + } | |
| 30 | + | |
| 31 | + return false; | |
| 32 | + } | |
| 33 | + | |
| 34 | + /** | |
| 35 | + * Generate the random clue/key. | |
| 36 | + * | |
| 37 | + * @param integer $length | |
| 38 | + * @return string | |
| 39 | + */ | |
| 40 | + public static function generateRandomClue( $length = 16 ) { | |
| 41 | + if ( function_exists( 'random_bytes' ) ) { | |
| 42 | + return Base32Static::encode( random_bytes( 10 ) ); | |
| 43 | + } | |
| 44 | + | |
| 45 | + require_once dirname( __FILE__ ) . '/polyfill/lib/random.php'; | |
| 46 | + return Base32Static::encode( random_bytes( 10 ) ); | |
| 47 | + } | |
| 48 | + | |
| 49 | + /** | |
| 50 | + * | |
| 51 | + * @param string $key | |
| 52 | + * @param integer $counter | |
| 53 | + * @return string | |
| 54 | + */ | |
| 55 | + private static function oath_hotp( $key, $counter ) { | |
| 56 | + $cur_counter = [ 0, 0, 0, 0, 0, 0, 0, 0 ]; | |
| 57 | + | |
| 58 | + for ( $i = 7; $i >= 0; $i-- ) { // C for unsigned char, * for repeating to the end of the input data | |
| 59 | + $cur_counter[ $i ] = pack( 'C*', $counter ); | |
| 60 | + $counter = $counter >> 8; | |
| 61 | + } | |
| 62 | + | |
| 63 | + $binary = implode( $cur_counter ); | |
| 64 | + | |
| 65 | + // Pad to 8 characters | |
| 66 | + str_pad( $binary, 8, chr( 0 ), STR_PAD_LEFT ); | |
| 67 | + return hash_hmac( 'sha1', $binary, $key ); | |
| 68 | + } | |
| 69 | + | |
| 70 | + /** | |
| 71 | + * Truncate | |
| 72 | + * | |
| 73 | + * @param string $hash | |
| 74 | + * @param integer $length | |
| 75 | + * @return boolean | |
| 76 | + */ | |
| 77 | + private static function oath_truncate( $hash, $length = 6 ) { | |
| 78 | + $hashcharacters = str_split( $hash, 2 ); | |
| 79 | + | |
| 80 | + for ( $j = 0; $j < count( $hashcharacters ); $j++ ) { | |
| 81 | + $hmac_result[] = hexdec( $hashcharacters[ $j ] ); | |
| 82 | + } | |
| 83 | + | |
| 84 | + $offset = $hmac_result[19] & 0xf; | |
| 85 | + return ( | |
| 86 | + ( ( $hmac_result[ $offset + 0 ] & 0x7f ) << 24 ) | | |
| 87 | + ( ( $hmac_result[ $offset + 1 ] & 0xff ) << 16 ) | | |
| 88 | + ( ( $hmac_result[ $offset + 2 ] & 0xff ) << 8 ) | | |
| 89 | + ( $hmac_result[ $offset + 3 ] & 0xff ) | |
| 90 | + ) % pow( 10, $length ); | |
| 91 | + } | |
| 92 | + | |
| 93 | + /** | |
| 94 | + * Compare 2 strings with each other. | |
| 95 | + * | |
| 96 | + * @param string $own | |
| 97 | + * @param string $user | |
| 98 | + * @return boolean | |
| 99 | + */ | |
| 100 | + private static function stringEquals( $own, $user ) { | |
| 101 | + if ( function_exists( 'hash_equals' ) ) { | |
| 102 | + return hash_equals( $own, $user ); | |
| 103 | + } | |
| 104 | + | |
| 105 | + $safeLen = strlen( $own ); | |
| 106 | + $userLen = strlen( $user ); | |
| 107 | + | |
| 108 | + if ( $userLen != $safeLen ) { | |
| 109 | + return false; | |
| 110 | + } | |
| 111 | + | |
| 112 | + $result = 0; | |
| 113 | + for ( $i = 0; $i < $userLen; $i++ ) { | |
| 114 | + $result |= ( ord( $own[$i] ) ^ ord( $user[$i] ) ); | |
| 115 | + } | |
| 116 | + | |
| 117 | + return $result === 0; | |
| 118 | + } | |
| 119 | +} | |