From 5adfd9d30071219912a16943e2f1206d7328499b Mon Sep 17 00:00:00 2001 From: "A. B. M. Mahmudul Hasan" Date: Tue, 5 May 2026 22:25:12 +0600 Subject: [PATCH] updated ci --- .github/workflows/security-standards.yml | 2 + captainhook.json | 2 +- src/AbstractOtpAuthenticator.php | 100 +++++++++++++++++++++++ src/HOTP.php | 32 ++------ src/TOTP.php | 33 +++----- 5 files changed, 122 insertions(+), 47 deletions(-) create mode 100644 src/AbstractOtpAuthenticator.php diff --git a/.github/workflows/security-standards.yml b/.github/workflows/security-standards.yml index bf15ab3..500e447 100644 --- a/.github/workflows/security-standards.yml +++ b/.github/workflows/security-standards.yml @@ -24,3 +24,5 @@ jobs: phpstan_memory_limit: "1G" psalm_threads: "1" run_analysis: true + run_svg_report: true + artifact_retention_days: 61 diff --git a/captainhook.json b/captainhook.json index c52f64a..782a292 100644 --- a/captainhook.json +++ b/captainhook.json @@ -23,7 +23,7 @@ "options": [] }, { - "action": "composer ic:tests", + "action": "composer ic:ci", "options": [] } ] diff --git a/src/AbstractOtpAuthenticator.php b/src/AbstractOtpAuthenticator.php new file mode 100644 index 0000000..e20c8f7 --- /dev/null +++ b/src/AbstractOtpAuthenticator.php @@ -0,0 +1,100 @@ + $include + * @param array $additionalParameters + */ + final protected function buildEnrollmentPayload( + string $otpType, + string $secret, + string $label, + string $issuer, + array $include, + array $additionalParameters, + string $algorithm, + int $digitCount, + ?int $period, + ?int $counter, + bool $withQrSvg, + int $imageSize, + string $uri, + ): EnrollmentPayload { + return ProvisioningUriBuilder::enrollmentPayload( + $otpType, + $secret, + $label, + $issuer, + $this->includeFlags($include), + $additionalParameters, + $algorithm, + $digitCount, + $period, + $counter, + null, + $withQrSvg ? SvgQrRenderer::render($uri, $imageSize) : null, + ); + } + + /** + * @param array $include + * @param array $additionalParameters + */ + final protected function buildProvisioningUri( + string $otpType, + string $secret, + string $label, + string $issuer, + array $include, + array $additionalParameters, + string $algorithm, + int $digitCount, + ?int $period, + ?int $counter, + ): string { + return ProvisioningUriBuilder::build( + $otpType, + $secret, + $label, + $issuer, + $this->includeFlags($include), + $additionalParameters, + $algorithm, + $digitCount, + $period, + $counter, + ); + } + + /** + * @param array $include + * @return array + */ + final protected function includeFlags(array $include): array + { + return array_fill_keys($include, true); + } +} diff --git a/src/HOTP.php b/src/HOTP.php index 892a0a2..3211ebf 100644 --- a/src/HOTP.php +++ b/src/HOTP.php @@ -9,15 +9,12 @@ use Infocyph\OTP\Result\VerificationResult; use Infocyph\OTP\Support\AlgorithmValidator; use Infocyph\OTP\Support\OtpMath; -use Infocyph\OTP\Support\ProvisioningUriBuilder; -use Infocyph\OTP\Support\ProvisioningUriParser; use Infocyph\OTP\Support\SecretUtility; use Infocyph\OTP\Support\SvgQrRenderer; use Infocyph\OTP\ValueObjects\EnrollmentPayload; -use Infocyph\OTP\ValueObjects\ParsedOtpAuthUri; use Infocyph\OTP\ValueObjects\SecretRotation; -final class HOTP +final class HOTP extends AbstractOtpAuthenticator { private readonly string $secret; @@ -44,11 +41,6 @@ public static function generateSecret(int $bytes = 64): string return SecretUtility::generate($bytes); } - public static function parseProvisioningUri(string $uri): ParsedOtpAuthUri - { - return ProvisioningUriParser::parse($uri); - } - /** * @param array $include * @param array $additionalParameters @@ -63,19 +55,20 @@ public function getEnrollmentPayload( ): EnrollmentPayload { $uri = $this->getProvisioningUri($label, $issuer, $include, $additionalParameters); - return ProvisioningUriBuilder::enrollmentPayload( + return $this->buildEnrollmentPayload( 'hotp', $this->secret, $label, $issuer, - array_fill_keys($include, true), + $include, $additionalParameters, $this->algorithm, $this->digitCount, null, $this->counter, - null, - $withQrSvg ? SvgQrRenderer::render($uri, $imageSize) : null, + $withQrSvg, + $imageSize, + $uri, ); } @@ -94,12 +87,12 @@ public function getProvisioningUri( array $include = ['algorithm', 'digits', 'counter'], array $additionalParameters = [], ): string { - return ProvisioningUriBuilder::build( + return $this->buildProvisioningUri( 'hotp', $this->secret, $label, $issuer, - array_fill_keys($include, true), + $include, $additionalParameters, $this->algorithm, $this->digitCount, @@ -187,7 +180,7 @@ public function verifyWithResult( ?ReplayStoreInterface $replayStore = null, ?string $binding = null, ): VerificationResult { - $this->assertOtp($otp); + $this->assertOtp($otp, $this->digitCount); if ($counter < 0 || $lookAhead < 0) { throw new \InvalidArgumentException('Counter and look-ahead window must be non-negative.'); } @@ -217,11 +210,4 @@ public function verifyWithResult( return new VerificationResult(false, 'mismatch'); } - - private function assertOtp(string $otp): void - { - if (!preg_match('/^\d+$/', $otp) || strlen($otp) !== $this->digitCount) { - throw new \InvalidArgumentException('OTP must be a numeric string matching the configured digit count.'); - } - } } diff --git a/src/TOTP.php b/src/TOTP.php index 2d46381..b438df7 100644 --- a/src/TOTP.php +++ b/src/TOTP.php @@ -9,16 +9,13 @@ use Infocyph\OTP\Result\VerificationResult; use Infocyph\OTP\Support\AlgorithmValidator; use Infocyph\OTP\Support\OtpMath; -use Infocyph\OTP\Support\ProvisioningUriBuilder; -use Infocyph\OTP\Support\ProvisioningUriParser; use Infocyph\OTP\Support\SecretUtility; use Infocyph\OTP\Support\SvgQrRenderer; use Infocyph\OTP\ValueObjects\EnrollmentPayload; -use Infocyph\OTP\ValueObjects\ParsedOtpAuthUri; use Infocyph\OTP\ValueObjects\SecretRotation; use Infocyph\OTP\ValueObjects\VerificationWindow; -final class TOTP +final class TOTP extends AbstractOtpAuthenticator { private readonly string $secret; @@ -47,11 +44,6 @@ public static function generateSecret(int $bytes = 64): string return SecretUtility::generate($bytes); } - public static function parseProvisioningUri(string $uri): ParsedOtpAuthUri - { - return ProvisioningUriParser::parse($uri); - } - public function getCurrentTimeStep(?int $timestamp = null): int { return $this->getTimeStepFromTimestamp($timestamp ?? time()); @@ -71,19 +63,20 @@ public function getEnrollmentPayload( ): EnrollmentPayload { $uri = $this->getProvisioningUri($label, $issuer, $include, $additionalParameters); - return ProvisioningUriBuilder::enrollmentPayload( + return $this->buildEnrollmentPayload( 'totp', $this->secret, $label, $issuer, - array_fill_keys($include, true), + $include, $additionalParameters, $this->algorithm, $this->digitCount, $this->period, null, - null, - $withQrSvg ? SvgQrRenderer::render($uri, $imageSize) : null, + $withQrSvg, + $imageSize, + $uri, ); } @@ -107,16 +100,17 @@ public function getProvisioningUri( array $include = ['algorithm', 'digits', 'period'], array $additionalParameters = [], ): string { - return ProvisioningUriBuilder::build( + return $this->buildProvisioningUri( 'totp', $this->secret, $label, $issuer, - array_fill_keys($include, true), + $include, $additionalParameters, $this->algorithm, $this->digitCount, $this->period, + null, ); } @@ -229,7 +223,7 @@ public function verifyWithWindow( ?string $binding = null, bool $singleUse = true, ): VerificationResult { - $this->assertOtp($otp); + $this->assertOtp($otp, $this->digitCount); $window ??= new VerificationWindow(); if ($window->past < 0 || $window->future < 0) { throw new \InvalidArgumentException('Verification windows must be non-negative.'); @@ -267,11 +261,4 @@ public function verifyWithWindow( return new VerificationResult(false, 'mismatch'); } - - private function assertOtp(string $otp): void - { - if (!preg_match('/^\d+$/', $otp) || strlen($otp) !== $this->digitCount) { - throw new \InvalidArgumentException('OTP must be a numeric string matching the configured digit count.'); - } - } }