AbstractVerifier::verifyUser()   A
last analyzed

Complexity

Conditions 4
Paths 3

Size

Total Lines 20
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 4.5923

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 5
c 1
b 0
f 0
nc 3
nop 2
dl 0
loc 20
ccs 4
cts 6
cp 0.6667
crap 4.5923
rs 10
1
<?php
2
3
namespace MadWizard\WebAuthn\Server;
4
5
use MadWizard\WebAuthn\Attestation\AuthenticatorData;
6
use MadWizard\WebAuthn\Dom\AuthenticatorResponseInterface;
7
use MadWizard\WebAuthn\Dom\PublicKeyCredentialInterface;
8
use MadWizard\WebAuthn\Exception\ParseException;
9
use MadWizard\WebAuthn\Exception\VerificationException;
10
use MadWizard\WebAuthn\Extension\ExtensionProcessingContext;
11
use MadWizard\WebAuthn\Extension\ExtensionRegistryInterface;
12
use MadWizard\WebAuthn\Extension\ExtensionResponse;
13
use MadWizard\WebAuthn\Format\CborMap;
14
use MadWizard\WebAuthn\Web\Origin;
15
16
abstract class AbstractVerifier
17
{
18
    /**
19
     * @var ExtensionRegistryInterface
20
     */
21
    protected $extensionRegistry;
22
23 12
    public function __construct(ExtensionRegistryInterface $extensionRegistry)
24
    {
25 12
        $this->extensionRegistry = $extensionRegistry;
26 12
    }
27
28
    // TODO: move?
29 8
    protected function verifyOrigin(string $origin, Origin $rpOrigin): bool
30
    {
31
        try {
32 8
            $clientOrigin = Origin::parse($origin);
33
        } catch (ParseException $e) {
34
            throw new VerificationException('Client has specified an invalid origin.', 0, $e);
35
        }
36
37 8
        return $clientOrigin->equals($rpOrigin);
38
    }
39
40 6
    protected function verifyRpIdHash(AuthenticatorData $authData, AbstractContext $context, ExtensionProcessingContext $extensionContext): bool
41
    {
42 6
        $effectiveRpId = $context->getRpId();
43 6
        $overruledRpId = $extensionContext->getOverruledRpId();
44 6
        if ($overruledRpId !== null) {
45
            $effectiveRpId = $overruledRpId;
46
        }
47 6
        $validHash = hash('sha256', $effectiveRpId, true);
48 6
        return hash_equals($validHash, $authData->getRpIdHash()->getBinaryString());
49
    }
50
51 5
    protected function verifyUser(AuthenticatorData $authData, AbstractContext $context): bool
52
    {
53
        // Reg 10/11, Auth 12/13
54
55
        // Reg 7.1 #10 Verify that the User Present bit of the flags in authData is set.
56
        // Note: isUserPresenceRequired is true by default to conform to the WebAuthn spec.
57
        // It can be set to false manually when required to pass full FIDO2 compliance, which conflicts the
58
        // WebAuthn spec.
59
        // @see https://github.com/fido-alliance/conformance-tools-issues/issues/434
60 5
        if (!$authData->isUserPresent() && $context->isUserPresenceRequired()) {
61
            return false;
62
        }
63
64 5
        if ($context->isUserVerificationRequired()) {
65
            // Reg 7.1 #11 If user verification is required for this registration, verify that the User Verified bit of the
66
            // flags in authData is set.
67
            return $authData->isUserVerified();
68
        }
69
70 5
        return true;
71
    }
72
73 1
    protected function getClientDataHash(AuthenticatorResponseInterface $response): string
74
    {
75 1
        return hash('sha256', $response->getClientDataJson(), true);
76
    }
77
78 12
    protected function processExtensions(PublicKeyCredentialInterface $credential, AuthenticatorData $authData, AbstractContext $operationContext, string $operation): ExtensionProcessingContext
79
    {
80 12
        $authExtensionOutputs = $authData->hasExtensionData() ? $authData->getExtensionData() : new CborMap();
81
82
        // TODO: check for unwanted $authExtensionOutputs
83 12
        $extensionContext = new ExtensionProcessingContext($operation);
84
85 12
        $results = $credential->getClientExtensionResults();
86 12
        $inputs = [];
87 12
        foreach ($operationContext->getExtensionInputs() as $input) {
88
            $inputs[$input->getIdentifier()] = $input;
89
        }
90
91 12
        foreach ($results as $id => $result) {
92
            $input = $inputs[$id] ?? null;
93
            if ($input === null) {
94
                throw new VerificationException(sprintf('Extension "%s" is present in clientExtensionResults but was not used in the input.', $id));
95
            }
96
            $extension = $this->extensionRegistry->getExtension($id);
97
98
            $extensionResponse = new ExtensionResponse($id);
99
            $extensionResponse->setClientExtensionOutput($result);
100
            if ($authExtensionOutputs->has($id)) {
101
                $extensionResponse->setAuthenticatorExtensionOutput($authExtensionOutputs->get($id));
102
            }
103
            $output = $extension->parseResponse($extensionResponse);
104
            $extensionContext->addOutput($output);
105
            $extension->processExtension($input, $output, $extensionContext);
106
        }
107
108 12
        return $extensionContext;
109
    }
110
}
111