Passed
Push — develop ( 754fa3...6370fb )
by nguereza
02:20
created

Webauthn::processAuthentication()   D

Complexity

Conditions 21
Paths 34

Size

Total Lines 118
Code Lines 44

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 21
eloc 44
c 1
b 0
f 0
nc 34
nop 8
dl 0
loc 118
rs 4.1666

How to fix   Long Method    Complexity    Many Parameters   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
/**
4
 * Platine Webauth
5
 *
6
 * Platine Webauthn is the implementation of webauthn specifications
7
 *
8
 * This content is released under the MIT License (MIT)
9
 *
10
 * Copyright (c) 2020 Platine Webauth
11
 * Copyright (c) Jakob Bennemann <[email protected]>
12
 *
13
 * Permission is hereby granted, free of charge, to any person obtaining a copy
14
 * of this software and associated documentation files (the "Software"), to deal
15
 * in the Software without restriction, including without limitation the rights
16
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17
 * copies of the Software, and to permit persons to whom the Software is
18
 * furnished to do so, subject to the following conditions:
19
 *
20
 * The above copyright notice and this permission notice shall be included in all
21
 * copies or substantial portions of the Software.
22
 *
23
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29
 * SOFTWARE.
30
 */
31
32
declare(strict_types=1);
33
34
namespace Platine\Webauthn;
35
36
use Exception;
37
use Platine\Http\Uri;
38
use Platine\Stdlib\Helper\Json;
39
use Platine\Stdlib\Helper\Path;
40
use Platine\Webauthn\Attestation\AttestationData;
41
use Platine\Webauthn\Attestation\AuthenticatorData;
42
use Platine\Webauthn\Entity\AuthenticatorSelection;
43
use Platine\Webauthn\Entity\PublicKey;
44
use Platine\Webauthn\Entity\PublicKeyAuthParam;
45
use Platine\Webauthn\Entity\RelyingParty;
46
use Platine\Webauthn\Entity\UserCredential;
47
use Platine\Webauthn\Entity\UserInfo;
48
use Platine\Webauthn\Enum\AttestationType;
49
use Platine\Webauthn\Enum\KeyFormat;
50
use Platine\Webauthn\Enum\TransportType;
51
use Platine\Webauthn\Exception\WebauthnException;
52
use Platine\Webauthn\Helper\ByteBuffer;
53
54
/**
55
 * @class Webauthn
56
 * @package Platine\Webauthn
57
 */
58
class Webauthn
59
{
60
    /**
61
     * The attestation data formats
62
     * @var array<string>
63
     */
64
    protected array $formats = [];
65
66
    /**
67
     * The challenge to use
68
     * @var ByteBuffer|null
69
     */
70
    protected ?ByteBuffer $challenge = null;
71
72
    /**
73
     * The signature counter
74
     * @var int
75
     */
76
    protected int $signatureCounter = 0;
77
78
    /**
79
     * The relying party entity
80
     * @var RelyingParty
81
     */
82
    protected RelyingParty $relyingParty;
83
84
    /**
85
     * The certificates files path
86
     * @var array<string>
87
     */
88
    protected array $certificates = [];
89
90
    /**
91
     * The configuration instance
92
     * @var WebauthnConfiguration
93
     */
94
    protected WebauthnConfiguration $config;
95
96
    /**
97
     * Create new instance
98
     * @param WebauthnConfiguration $config
99
     * @param array<string> $allowedFormats
100
     */
101
    public function __construct(WebauthnConfiguration $config, array $allowedFormats = [])
102
    {
103
        if (! function_exists('openssl_open')) {
104
            throw new WebauthnException('OpenSSL module not installed in this platform');
105
        }
106
107
        if (! in_array('SHA256', array_map('strtoupper', openssl_get_md_methods()))) {
108
            throw new WebauthnException('SHA256 is not supported by this OpenSSL installation');
109
        }
110
111
        $this->config = $config;
112
        $this->formats = $this->normalizeFormats($allowedFormats);
113
114
        $this->relyingParty = new RelyingParty(
115
            $config->get('relying_party_id'),
0 ignored issues
show
Bug introduced by
It seems like $config->get('relying_party_id') can also be of type null; however, parameter $id of Platine\Webauthn\Entity\...ingParty::__construct() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

115
            /** @scrutinizer ignore-type */ $config->get('relying_party_id'),
Loading history...
116
            $config->get('relying_party_name'),
0 ignored issues
show
Bug introduced by
It seems like $config->get('relying_party_name') can also be of type null; however, parameter $name of Platine\Webauthn\Entity\...ingParty::__construct() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

116
            /** @scrutinizer ignore-type */ $config->get('relying_party_name'),
Loading history...
117
            $config->get('relying_party_logo')
0 ignored issues
show
Bug introduced by
It seems like $config->get('relying_party_logo') can also be of type null; however, parameter $logo of Platine\Webauthn\Entity\...ingParty::__construct() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

117
            /** @scrutinizer ignore-type */ $config->get('relying_party_logo')
Loading history...
118
        );
119
    }
120
121
    /**
122
     * Add a root certificate to verify new registrations
123
     * @param string $path
124
     * @return $this
125
     */
126
    public function addRootCertificate(string $path): self
127
    {
128
        $this->certificates[] = Path::realPath($path);
129
130
        return $this;
131
    }
132
133
    /**
134
     * Return the parameters to be used for the registration
135
     * @param string $userId
136
     * @param string $userName
137
     * @param string $userDisplayName
138
     * @param string $userVerificationType
139
     * @param bool $crossPlatformAttachment
140
     * @param array<string> $excludeCredentialIds
141
     * @param bool $withoutAttestation
142
     * @return PublicKey
143
     */
144
    public function getRegistrationParams(
145
        string $userId,
146
        string $userName,
147
        string $userDisplayName,
148
        string $userVerificationType,
149
        bool $crossPlatformAttachment = false,
150
        array $excludeCredentialIds = [],
151
        bool $withoutAttestation = false
152
    ): PublicKey {
153
        $excludeCredentials = [];
154
        foreach ($excludeCredentialIds as $id) {
155
            $hex = hex2bin($id);
156
            if ($hex === false) {
157
                throw new WebauthnException(sprintf('Can not convert credential id [%s] to binary', $id));
158
            }
159
160
            $excludeCredentials[] = new UserCredential(
161
                new ByteBuffer($hex),
162
                array_values(TransportType::all())
163
            );
164
        }
165
166
        $attestation = AttestationType::INDIRECT;
167
        if (count($this->certificates) > 0) {
168
            $attestation = AttestationType::DIRECT;
169
        }
170
171
        if ($withoutAttestation) {
172
            $attestation = AttestationType::NONE;
173
        }
174
175
        $relyingParty = new RelyingParty(
176
            $this->config->get('relying_party_id'),
0 ignored issues
show
Bug introduced by
It seems like $this->config->get('relying_party_id') can also be of type null; however, parameter $id of Platine\Webauthn\Entity\...ingParty::__construct() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

176
            /** @scrutinizer ignore-type */ $this->config->get('relying_party_id'),
Loading history...
177
            $this->config->get('relying_party_name'),
0 ignored issues
show
Bug introduced by
It seems like $this->config->get('relying_party_name') can also be of type null; however, parameter $name of Platine\Webauthn\Entity\...ingParty::__construct() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

177
            /** @scrutinizer ignore-type */ $this->config->get('relying_party_name'),
Loading history...
178
            $this->config->get('relying_party_logo')
0 ignored issues
show
Bug introduced by
It seems like $this->config->get('relying_party_logo') can also be of type null; however, parameter $logo of Platine\Webauthn\Entity\...ingParty::__construct() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

178
            /** @scrutinizer ignore-type */ $this->config->get('relying_party_logo')
Loading history...
179
        );
180
181
        $userInfo = new UserInfo(
182
            new ByteBuffer($userId),
183
            $userName,
184
            $userDisplayName
185
        );
186
187
        $authenticatorSelection = new AuthenticatorSelection(
188
            $userVerificationType,
189
            false,
190
            $crossPlatformAttachment
191
        );
192
193
        $publicKey = (new PublicKey())
194
                      ->setUserInfo($userInfo)
195
                      ->setRelyingParty($relyingParty)
196
                      ->setAuthenticatorSelection($authenticatorSelection)
197
                      ->setExcludeCredentials($excludeCredentials)
198
                      ->setChallenge($this->createChallenge())
199
                      ->setTimeout($this->config->get('timeout'))
0 ignored issues
show
Bug introduced by
It seems like $this->config->get('timeout') can also be of type null; however, parameter $timeout of Platine\Webauthn\Entity\PublicKey::setTimeout() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

199
                      ->setTimeout(/** @scrutinizer ignore-type */ $this->config->get('timeout'))
Loading history...
200
                      ->setExtensions()
201
                      ->addPublicKeys()
202
                      ->setAttestation($attestation);
203
204
        return $publicKey;
205
    }
206
207
    /**
208
     * Return the authentication parameters
209
     * @param string $userVerificationType
210
     * @param array<string> $credentialIds
211
     * @return PublicKey
212
     */
213
    public function getAuthenticationParams(
214
        string $userVerificationType,
215
        array $credentialIds = []
216
    ): PublicKey {
217
        $allowedCredentials = [];
218
        foreach ($credentialIds as $id) {
219
            $hex = hex2bin($id);
220
            if ($hex === false) {
221
                throw new WebauthnException(sprintf('Can not convert credential id [%s] to binary', $id));
222
            }
223
224
            $allowedCredentials[] = new PublicKeyAuthParam(
225
                new ByteBuffer($hex),
226
                $this->config->get('transport_types')
0 ignored issues
show
Bug introduced by
It seems like $this->config->get('transport_types') can also be of type null; however, parameter $transports of Platine\Webauthn\Entity\...uthParam::__construct() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

226
                /** @scrutinizer ignore-type */ $this->config->get('transport_types')
Loading history...
227
            );
228
        }
229
230
        $publicKey = (new PublicKey())
231
                      ->setRelyingPartyId($this->relyingParty->getId())
232
                      ->setAllowCredentials($allowedCredentials)
233
                      ->setChallenge($this->createChallenge())
234
                      ->setTimeout($this->config->get('timeout'))
0 ignored issues
show
Bug introduced by
It seems like $this->config->get('timeout') can also be of type null; however, parameter $timeout of Platine\Webauthn\Entity\PublicKey::setTimeout() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

234
                      ->setTimeout(/** @scrutinizer ignore-type */ $this->config->get('timeout'))
Loading history...
235
                      ->setUserVerificationType($userVerificationType);
236
237
        return $publicKey;
238
    }
239
240
    /**
241
     * Process the user registration
242
     * @param string $clientDataJson
243
     * @param string $attestationObject
244
     * @param string|ByteBuffer $challenge
245
     * @param bool $requireUserVerification
246
     * @param bool $requireUserPresent
247
     * @param bool $failIfRootCertificateMismatch
248
     * @return array<string, mixed>
249
     */
250
    public function processRegistration(
251
        string $clientDataJson,
252
        string $attestationObject,
253
        $challenge,
254
        bool $requireUserVerification = false,
255
        bool $requireUserPresent = true,
256
        bool $failIfRootCertificateMismatch = true
257
    ): array {
258
        $clientDataHash = hash('sha256', $clientDataJson, true);
259
        if (is_string($challenge)) {
260
            $challenge =  new ByteBuffer($challenge);
261
        }
262
263
        // security: https://www.w3.org/TR/webauthn/#registering-a-new-credential
264
        try {
265
            // 2. Let C, the client data claimed as collected during the credential creation,
266
            // be the result of running an implementation-specific JSON parser on JSONtext.
267
            $clientData = Json::decode($clientDataJson);
268
        } catch (Exception $ex) {
269
            throw new WebauthnException(sprintf('Invalid client data provided, [%s]', $ex->getMessage()));
270
        }
271
272
        // 3. Verify that the value of C.type is webauthn.create.
273
        if (! isset($clientData->type) || $clientData->type !== 'webauthn.create') {
274
            throw new WebauthnException('Invalid client type provided');
275
        }
276
277
        // 4. Verify that the value of C.challenge matches the challenge that was
278
        // sent to the authenticator in the create() call.
279
        if (
280
            ! isset($clientData->challenge) ||
281
            ByteBuffer::fromBase64Url($clientData->challenge)->getBinaryString() !== $challenge->getBinaryString()
282
        ) {
283
            throw new WebauthnException('Invalid challenge provided');
284
        }
285
286
        // 5. Verify that the value of C.origin matches the Relying Party's origin.
287
        if (! isset($clientData->origin) || $this->checkOrigin($clientData->origin) === false) {
288
            throw new WebauthnException('Invalid origin provided');
289
        }
290
291
        $attestation = new AttestationData($attestationObject, $this->formats);
292
293
        // 9. Verify that the RP ID hash in authData is indeed the SHA-256
294
        // hash of the RP ID expected by the RP.
295
        if ($attestation->validateRelyingPartyIdHash($this->relyingParty->getHashId()) === false) {
296
            throw new WebauthnException('Invalid relying party id hash provided');
297
        }
298
299
        // 14. Verify that attStmt is a correct attestation statement, conveying
300
        // a valid attestation signature
301
        if ($attestation->validateAttestation($clientDataHash) === false) {
302
            throw new WebauthnException('Invalid certificate signature');
303
        }
304
305
        // 15. If validation is successful, obtain a list of acceptable trust anchors
306
        $isRootValid = count($this->certificates) > 0
307
                ? $attestation->validateRootCertificate($this->certificates)
308
                : false;
309
310
        if ($failIfRootCertificateMismatch && count($this->certificates) > 0 && $isRootValid === false) {
311
            throw new WebauthnException('Invalid root certificate');
312
        }
313
314
        // 10. Verify that the User Present bit of the flags in authData is set.
315
        $userPresent = $attestation->getAuthenticatorData()->isUserPresent();
316
        if ($requireUserPresent && $userPresent === false) {
317
            throw new WebauthnException('User is not present during authentication');
318
        }
319
320
        // 11. If user verification is required for this registration, verify
321
        // that the User Verified bit of the flags in authData is set.
322
        $userVerified = $attestation->getAuthenticatorData()->isUserVerified();
323
        if ($requireUserVerification && $userVerified === false) {
324
            throw new WebauthnException('User is not verified during authentication');
325
        }
326
327
        $signCount = $attestation->getAuthenticatorData()->getSignatureCount();
328
        if ($signCount > 0) {
329
            $this->signatureCounter = $signCount;
330
        }
331
332
        // prepare data to store for future logins
333
        $data = [
334
            'rp_id' => $this->relyingParty->getId(),
335
            'attestation_format' => $attestation->getFormatName(),
336
            'credential_id' => bin2hex($attestation->getAuthenticatorData()->getCredentialId()),
337
            'credential_public_key' => $attestation->getAuthenticatorData()->getPublicKeyPEM(),
338
            'certificate_chain' => $attestation->getCertificateChain(),
339
            'certificate' => $attestation->getCertificatePem(),
340
            'certificate_issuer' => $attestation->getCertificateIssuer(),
341
            'certificate_subject' => $attestation->getCertificateSubject(),
342
            'root_certificate_valid' => $isRootValid,
343
            'signature_counter' => $this->signatureCounter,
344
            'aaguid' => bin2hex($attestation->getAuthenticatorData()->getAaguid()),
345
            'user_present' => $userPresent,
346
            'user_verified' => $userVerified,
347
        ];
348
349
350
        return $data;
351
    }
352
353
    /**
354
     * Process the user authentication
355
     * @param string $clientDataJson
356
     * @param string $authenticatorData
357
     * @param string $signature
358
     * @param string $credentialPublicKey
359
     * @param ByteBuffer|string $challenge
360
     * @param int|null $previousSignatureCount
361
     * @param bool $requireUserVerification
362
     * @param bool $requireUserPresent
363
     * @return bool
364
     */
365
    public function processAuthentication(
366
        string $clientDataJson,
367
        string $authenticatorData,
368
        string $signature,
369
        string $credentialPublicKey,
370
        $challenge,
371
        ?int $previousSignatureCount = null,
372
        bool $requireUserVerification = false,
373
        bool $requireUserPresent = true
374
    ): bool {
375
        if (is_string($challenge)) {
376
            $challenge =  new ByteBuffer($challenge);
377
        }
378
        $clientDataHash = hash('sha256', $clientDataJson, true);
379
        $authenticator = new AuthenticatorData($authenticatorData);
380
        try {
381
            // 5. Let JSON text be the result of running UTF-8 decode on the value of cData.
382
            $clientData = Json::decode($clientDataJson);
383
        } catch (Exception $ex) {
384
            throw new WebauthnException(sprintf('Invalid client data provided, [%s]', $ex->getMessage()));
385
        }
386
387
        // https://www.w3.org/TR/webauthn/#verifying-assertion
388
389
        // 1. If the allowCredentials option was given when this authentication ceremony was initiated,
390
        //    verify that credential.id identifies one of the public key credentials
391
        //    that were listed in allowCredentials.
392
        //    -> TO BE VERIFIED BY IMPLEMENTATION
393
394
        // 2. If credential.response.userHandle is present, verify that the user identified
395
        //    by this value is the owner of the public key credential identified by credential.id.
396
        //    -> TO BE VERIFIED BY IMPLEMENTATION
397
398
        // 3. Using credential’s id attribute (or the corresponding rawId, if base64url encoding is
399
        //    inappropriate for your use case),
400
        //    look up the corresponding credential public key.
401
        //    -> TO BE LOOKED UP BY IMPLEMENTATION
402
403
        // 7. Verify that the value of C.type is the string webauthn.get.
404
        if (! isset($clientData->type) || $clientData->type !== 'webauthn.get') {
405
            throw new WebauthnException('Invalid client type provided');
406
        }
407
408
        // 8. Verify that the value of C.challenge matches the challenge that was sent to the
409
        //    authenticator in the PublicKeyCredentialRequestOptions passed to the get() call.
410
        if (
411
            ! isset($clientData->challenge) ||
412
            ByteBuffer::fromBase64Url($clientData->challenge)->getBinaryString() !== $challenge->getBinaryString()
413
        ) {
414
            throw new WebauthnException('Invalid challenge provided');
415
        }
416
417
        // 9. Verify that the value of C.origin matches the Relying Party's origin.
418
        if (! isset($clientData->origin) || $this->checkOrigin($clientData->origin) === false) {
419
            throw new WebauthnException('Invalid origin provided');
420
        }
421
422
        // 11. Verify that the rpIdHash in authData is the SHA-256 hash
423
        // of the RP ID expected by the Relying Party.
424
        if ($authenticator->getRelyingPartyIdHash() !== $this->relyingParty->getHashId()) {
425
            throw new WebauthnException('Invalid relying party id hash provided');
426
        }
427
428
        // 12. Verify that the User Present bit of the flags in authData is set
429
        if ($requireUserPresent && $authenticator->isUserPresent() === false) {
430
            throw new WebauthnException('User is not present during authentication');
431
        }
432
433
        // 13. If user verification is required for this assertion, verify that
434
        // the User Verified bit of the flags in authData is set.
435
        if ($requireUserVerification && $authenticator->isUserVerified() === false) {
436
            throw new WebauthnException('User is not verified during authentication');
437
        }
438
439
        // 14. Verify the values of the client extension outputs
440
        // TODO    (extensions not implemented)
441
442
        // 16. Using the credential public key looked up in step 3, verify
443
        // that sig is a valid signature over the binary
444
        //  concatenation of authData and hash.
445
        $dataToVerify = '';
446
        $dataToVerify .= $authenticatorData;
447
        $dataToVerify .= $clientDataHash;
448
449
        $publicKey = openssl_pkey_get_public($credentialPublicKey);
450
        if ($publicKey === false) {
451
            throw new WebauthnException('Invalid public key provided');
452
        }
453
454
        if (
455
            openssl_verify(
456
                $dataToVerify,
457
                $signature,
458
                $publicKey,
459
                OPENSSL_ALGO_SHA256
460
            ) !== 1
461
        ) {
462
            throw new WebauthnException('Invalid signature provided');
463
        }
464
465
        $signatureCount = $authenticator->getSignatureCount();
466
        if ($signatureCount !== 0) {
467
            $this->signatureCounter = $signatureCount;
468
        }
469
470
        // 17. If either of the signature counter value authData.signCount or
471
        //     previous signature count is non-zero, and if authData.signCount
472
        //     less than or equal to previous signature count, it's a signal
473
        //     that the authenticator may be cloned
474
        if ($previousSignatureCount !== null) {
475
            if ($signatureCount !== 0 || $previousSignatureCount !== 0) {
476
                if ($previousSignatureCount >= $signatureCount) {
477
                    throw new WebauthnException('Invalid signature counter provided');
478
                }
479
            }
480
        }
481
482
        return true;
483
    }
484
485
    /**
486
     * Return the challenge
487
     * @return ByteBuffer|null
488
     */
489
    public function getChallenge(): ?ByteBuffer
490
    {
491
        return $this->challenge;
492
    }
493
494
495
    /**
496
     * Check the given origin
497
     * @param string $origin
498
     * @return bool
499
     */
500
    protected function checkOrigin(string $origin): bool
501
    {
502
        // https://www.w3.org/TR/webauthn/#rp-id
503
504
        // The origin's scheme must be https and not be ignored/whitelisted
505
        $url = new Uri($origin);
506
        if (
507
            ! in_array($this->relyingParty->getId(), $this->config->get('ignore_origins')) &&
0 ignored issues
show
Bug introduced by
It seems like $this->config->get('ignore_origins') can also be of type null; however, parameter $haystack of in_array() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

507
            ! in_array($this->relyingParty->getId(), /** @scrutinizer ignore-type */ $this->config->get('ignore_origins')) &&
Loading history...
508
            $url->getScheme() !== 'https'
509
        ) {
510
            return false;
511
        }
512
513
        // The RP ID must be equal to the origin's effective domain, or a registrable
514
        // domain suffix of the origin's effective domain.
515
        return preg_match('/' . preg_quote($this->relyingParty->getId()) . '$/i', $url->getHost()) === 1;
516
    }
517
518
    /**
519
     * Create the challenge if not yet created
520
     * @return ByteBuffer
521
     */
522
    protected function createChallenge(): ByteBuffer
523
    {
524
        if ($this->challenge === null) {
525
            $length = $this->config->get('challenge_length');
526
            $this->challenge = ByteBuffer::randomBuffer($length);
0 ignored issues
show
Bug introduced by
It seems like $length can also be of type null; however, parameter $length of Platine\Webauthn\Helper\ByteBuffer::randomBuffer() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

526
            $this->challenge = ByteBuffer::randomBuffer(/** @scrutinizer ignore-type */ $length);
Loading history...
527
        }
528
529
        return $this->challenge;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->challenge could return the type null which is incompatible with the type-hinted return Platine\Webauthn\Helper\ByteBuffer. Consider adding an additional type-check to rule them out.
Loading history...
530
    }
531
532
    /**
533
     * Normalize the formats
534
     * @param array<string> $formats
535
     * @return array<string>
536
     */
537
    protected function normalizeFormats(array $formats): array
538
    {
539
        $supportedFormats = KeyFormat::all();
540
        if (count($formats) === 0) {
541
            return $supportedFormats;
542
        }
543
544
        $desiredFormats = array_filter($formats, function ($entry) use ($supportedFormats) {
545
            return in_array($entry, $supportedFormats);
546
        });
547
548
        if (count($desiredFormats) > 0) {
549
            return $desiredFormats;
550
        }
551
552
        return $supportedFormats;
553
    }
554
}
555