solveChallenge( $challenge['challenge'], $challenge['salt'], Algorithm::SHA256, (int) $challenge['maxnumber'], ); self::assertNotNull($solution, 'challenge must be solvable'); return base64_encode(json_encode([ 'algorithm' => $challenge['algorithm'], 'challenge' => $challenge['challenge'], 'number' => $solution->number, 'salt' => $challenge['salt'], 'signature' => $challenge['signature'], ])); } public function testCreateChallengeShape(): void { $c = $this->service()->createChallenge(); self::assertSame('SHA-256', $c['algorithm']); self::assertArrayHasKey('challenge', $c); self::assertArrayHasKey('salt', $c); self::assertArrayHasKey('signature', $c); self::assertSame(2000, $c['maxnumber']); } public function testValidSolutionVerifies(): void { $svc = $this->service(); $payload = $this->solvedPayload($svc->createChallenge()); self::assertTrue($svc->verifySolution($payload)); } public function testReplayIsRejected(): void { $svc = $this->service(); $payload = $this->solvedPayload($svc->createChallenge()); self::assertTrue($svc->verifySolution($payload), 'first use accepted'); self::assertFalse($svc->verifySolution($payload), 'second use (replay) rejected'); } public function testTamperedSignatureRejected(): void { $svc = $this->service(); $challenge = $svc->createChallenge(); $challenge['signature'] = str_repeat('0', strlen($challenge['signature'])); self::assertFalse($svc->verifySolution($this->solvedPayload($challenge))); } public function testEmptyAndGarbagePayloadsRejected(): void { $svc = $this->service(); self::assertFalse($svc->verifySolution('')); self::assertFalse($svc->verifySolution('not-base64-!@#')); self::assertFalse($svc->verifySolution(base64_encode('{"foo":"bar"}'))); } public function testEnabledFlag(): void { self::assertTrue($this->service(true)->enabled()); self::assertFalse($this->service(false)->enabled()); } }