Builds both WebAuthn ceremonies on top of the library, with real cryptography proven in tests rather than stubbed: - PasskeyCeremonyStore: server-authoritative, single-use challenge state in the nonceCache pool. The client's challenge copy is never trusted, and consume() deletes before verifying so a replay cannot retry the same challenge. - PasskeyManager: registration and login ceremonies. Library types are confined to this class and PasskeyCeremonyFactory. Failures return null rather than distinguishing unknown-credential from bad-signature, so the endpoint is not an enumeration oracle. - PasskeyTestHelper: builds genuinely valid ceremonies (real P-256 keypair, COSE key, signed authenticatorData, CBOR attestation object). - PasskeyRealCryptoSpikeTest: proves registration and assertion verify, that http:// origins are refused (D4), that challenges and rpIdHash are bound, and that a synchronised passkey with a constant zero counter can log in repeatedly.
45 lines
1.2 KiB
PHP
45 lines
1.2 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Service;
|
|
|
|
/**
|
|
* Server-side storage for in-flight WebAuthn ceremonies.
|
|
*
|
|
* Kept separate from {@see PasskeyManager} so the security-critical properties —
|
|
* the challenge is server-authoritative and single-use — are testable without
|
|
* constructing a real authenticator response.
|
|
*/
|
|
interface PasskeyCeremonyStoreInterface
|
|
{
|
|
public const string TYPE_LOGIN = 'login';
|
|
|
|
public const string TYPE_REGISTER = 'register';
|
|
|
|
/**
|
|
* Start a login (assertion) ceremony.
|
|
*
|
|
* @return array{ceremonyId: string, challenge: string}
|
|
*/
|
|
public function startLogin(): array;
|
|
|
|
/**
|
|
* Start a registration (attestation) ceremony for an already-authenticated
|
|
* identity.
|
|
*
|
|
* @return array{ceremonyId: string, challenge: string, userHandle: string}
|
|
*/
|
|
public function startRegistration(string $identity): array;
|
|
|
|
/**
|
|
* Read and destroy a ceremony.
|
|
*
|
|
* Returns null when the id is unknown, expired, already consumed, or was
|
|
* started for the other ceremony type.
|
|
*
|
|
* @return array{challenge: string, identity?: string, userHandle?: string}|null
|
|
*/
|
|
public function consume(string $ceremonyId, string $expectedType): ?array;
|
|
}
|