diff --git a/examples/battlenet.php b/examples/battlenet.php deleted file mode 100644 index 7b2a646..0000000 --- a/examples/battlenet.php +++ /dev/null @@ -1,36 +0,0 @@ - - * @copyright 2023 Smiley - * @license MIT - */ - -use chillerlan\Authenticator\{Authenticator, AuthenticatorOptions}; -use chillerlan\Authenticator\Authenticators\AuthenticatorInterface; - -require_once '../vendor/autoload.php'; - -$options = new AuthenticatorOptions([ - // switch mode to BATTLE_NET - 'mode' => AuthenticatorInterface::BATTLE_NET, -]); - -$auth = new Authenticator($options); - -// set a secret - Battle.net secrets come as hex strings (20 byte, 40 chars) -$secret = $auth->setSecret('3132333435363738393031323334353637383930'); -// get a one time code -$code = $auth->code(); -var_dump($code); -// verify the current code -var_dump($auth->verify($code)); // -> true -// previous code -var_dump($auth->verify($code, (time() - $options->period))); // -> true -// 2nd adjacent is invalid -var_dump($auth->verify($code, (time() + 2 * $options->period))); // -> false -// allow 2 adjacent codes -$options->adjacent = 2; -var_dump($auth->verify($code, (time() + 2 * $options->period))); // -> true diff --git a/src/Authenticators/AuthenticatorInterface.php b/src/Authenticators/AuthenticatorInterface.php index b09bb5f..c4d245d 100644 --- a/src/Authenticators/AuthenticatorInterface.php +++ b/src/Authenticators/AuthenticatorInterface.php @@ -22,7 +22,6 @@ interface AuthenticatorInterface{ public const TOTP = 'totp'; public const HOTP = 'hotp'; public const STEAM_GUARD = 'steam'; - public const BATTLE_NET = 'battlenet'; public const ALGO_SHA1 = 'SHA1'; public const ALGO_SHA256 = 'SHA256'; @@ -32,7 +31,6 @@ interface AuthenticatorInterface{ self::HOTP => HOTP::class, self::TOTP => TOTP::class, self::STEAM_GUARD => SteamGuard::class, - self::BATTLE_NET => BattleNet::class, ]; public const HASH_ALGOS = [ diff --git a/src/Authenticators/BattleNet.php b/src/Authenticators/BattleNet.php deleted file mode 100644 index cb1bc42..0000000 --- a/src/Authenticators/BattleNet.php +++ /dev/null @@ -1,87 +0,0 @@ - - * @copyright 2023 smiley - * @license MIT - * - * @noinspection PhpComposerExtensionStubsInspection - */ - -namespace chillerlan\Authenticator\Authenticators; - -use chillerlan\Authenticator\Common\Hex; -use RuntimeException; -use SensitiveParameter; -use function str_pad; -use const STR_PAD_LEFT; - -/** - * @see https://github.com/winauth/winauth/blob/master/Authenticator/BattleNetAuthenticator.cs - * @see https://github.com/krtek4/php-bma - * @see https://github.com/jleclanche/python-bna/issues/38 - */ -final class BattleNet extends TOTP{ - - /** - * @inheritDoc - */ - public function setSecret(string $encodedSecret):AuthenticatorInterface{ - $this->secret = Hex::decode($this->checkEncodedSecret($encodedSecret)); - - return $this; - } - - /** - * @inheritDoc - */ - public function getSecret():string{ - - if($this->secret === null){ - throw new RuntimeException('No secret set'); - } - - return Hex::encode($this->secret); - } - - /** - * @inheritDoc - * @codeCoverageIgnore - */ - public function createSecret(int $length = null):string{ - throw new RuntimeException('Not implemented'); - } - - /** - * @inheritDoc - */ - public function getCounter(int $data = null):int{ - // the period is fixed to 30 seconds for Battle.net - $this->options->period = 30; - - return parent::getCounter($data); - } - - /** - * @inheritDoc - */ - public function getHMAC(int $counter):string{ - // algorithm is fixed to sha1 for Battle.net - $this->options->algorithm = self::ALGO_SHA1; - - return parent::getHMAC($counter); - } - - /** - * @inheritDoc - */ - public function getOTP(#[SensitiveParameter] int $code):string{ - $code %= 100000000; - - // length is fixed to 8 for Battle.net - return str_pad((string)$code, 8, '0', STR_PAD_LEFT); - } - -} diff --git a/tests/Authenticators/BattleNetTest.php b/tests/Authenticators/BattleNetTest.php deleted file mode 100644 index 51f065d..0000000 --- a/tests/Authenticators/BattleNetTest.php +++ /dev/null @@ -1,126 +0,0 @@ - - * @copyright 2023 smiley - * @license MIT - */ -declare(strict_types=1); - -namespace chillerlan\AuthenticatorTest\Authenticators; - -use chillerlan\Authenticator\AuthenticatorOptions; -use chillerlan\Authenticator\Authenticators\{AuthenticatorInterface, BattleNet}; -use chillerlan\Authenticator\Common\Hex; -use Generator; -use function date; -use function dechex; -use function is_int; -use const PHP_INT_SIZE; - -/** - * @property \chillerlan\Authenticator\Authenticators\BattleNet $authenticatorInterface - */ -class BattleNetTest extends AuthenticatorInterfaceTestAbstract{ - - protected const secret = '3132333435363738393031323334353637383930'; - - protected const BattleNetVectors = [ - // timestamps and time slices from RFC 6238, see https://tools.ietf.org/html/rfc6238#page-14 - [ 59, '1', '94287082'], - [ 1111111109, '23523ec', '07081804'], - [ 1111111111, '23523ed', '14050471'], - [ 1234567890, '273ef07', '89005924'], - [ 2000000000, '3f940aa', '69279037'], - // 64bit only - [20000000000, '27bc86aa', '65353130'], - ]; - - protected function getInstance(AuthenticatorOptions $options):AuthenticatorInterface{ - return new BattleNet($options); - } - - public function testSetGetSecret():void{ - $this->authenticatorInterface->setSecret($this::secret); - - $secret = $this->authenticatorInterface->getSecret(); - - $this::assertSame($this::secret, $secret); - $this::assertSame($this::rawsecret, Hex::decode($secret)); - } - - public function testCreateSecretDefaultLength():void{ - $this::markTestSkipped('N/A'); - } - - public function testCreateSecretWithLength():void{ - $this::markTestSkipped('N/A'); - } - - public function testCreateSecretCheckCharacterSet():void{ - $this::markTestSkipped('N/A'); - } - - public function testCreateSecretException():void{ - $this::markTestSkipped('N/A'); - } - - /** - * Timestamps and -slices from the RFC6238 page, codes from a verified implementation - * - * @see https://tools.ietf.org/html/rfc6238#page-14 - */ - public static function battleNetVectors():Generator{ - foreach(self::BattleNetVectors as [$timestamp, $timeslice, $totp]){ - // skip 64bit numbers on 32bit PHP - if(PHP_INT_SIZE < 8 && !is_int($timestamp)){ - continue; - } - - yield date('Y-m-d H:i:s', $timestamp) => [$timestamp, $timeslice, $totp]; - } - } - - /** - * @dataProvider battleNetVectors - */ - public function testIntermediateValues(int $timestamp, string $timeslice, string $totp):void{ - $this->authenticatorInterface->setSecret($this::secret); - - $timeslice_intermediate = $this->authenticatorInterface->getCounter($timestamp); - - $this::assertSame($timeslice, dechex($timeslice_intermediate)); - - $hmac_intermediate = $this->authenticatorInterface->getHMAC($timeslice_intermediate); - $code_intermediate = $this->authenticatorInterface->getCode($hmac_intermediate); - $code_formatted = $this->authenticatorInterface->getOTP($code_intermediate); - - $this::assertSame($totp, $code_formatted); - } - - /** - * @dataProvider battleNetVectors - */ - public function testAdjacent(int $timestamp, string $timeslice, string $totp):void{ - $adjacent = 20; - $limit = (2 * $adjacent); - - $this->authenticatorInterface->setSecret($this::secret); - - $this->options->adjacent = $adjacent; - // phpcs:ignore - for($i = -$limit; $i <= $limit; $i++){ - $this->options->time_offset = ($i * $this->options->period); - - $verify = $this->authenticatorInterface->verify($totp, $timestamp); - - ($i < -$adjacent || $i > $adjacent) - ? $this::assertFalse($verify) - : $this::assertTrue($verify); - } - - } - -}