Passed
Push — develop ( 8b9d3d...b96d85 )
by nguereza
02:03
created

Webauthn::getSignatureCounter()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
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|array<string> $path
124
     * @return $this
125
     */
126
    public function addRootCertificate($path): self
127
    {
128
        if (is_array($path)) {
129
            foreach ($path as $p) {
130
                $this->certificates[] = Path::realPath($p);
131
            }
132
        } else {
133
            $this->certificates[] = Path::realPath($path);
134
        }
135
136
        return $this;
137
    }
138
139
    /**
140
     * Return the parameters to be used for the registration
141
     * @param string $userId
142
     * @param string $userName
143
     * @param string $userDisplayName
144
     * @param string $userVerificationType
145
     * @param bool $crossPlatformAttachment
146
     * @param array<string> $excludeCredentialIds
147
     * @param bool $withoutAttestation
148
     * @return PublicKey
149
     */
150
    public function getRegistrationParams(
151
        string $userId,
152
        string $userName,
153
        string $userDisplayName,
154
        string $userVerificationType,
155
        bool $crossPlatformAttachment = false,
156
        array $excludeCredentialIds = [],
157
        bool $withoutAttestation = false
158
    ): PublicKey {
159
        $excludeCredentials = [];
160
        foreach ($excludeCredentialIds as $id) {
161
            $hex = hex2bin($id);
162
            if ($hex === false) {
163
                throw new WebauthnException(sprintf('Can not convert credential id [%s] to binary', $id));
164
            }
165
166
            $excludeCredentials[] = new UserCredential(
167
                new ByteBuffer($hex),
168
                array_values(TransportType::all())
169
            );
170
        }
171
172
        $attestation = AttestationType::INDIRECT;
173
        if (count($this->certificates) > 0) {
174
            $attestation = AttestationType::DIRECT;
175
        }
176
177
        if ($withoutAttestation) {
178
            $attestation = AttestationType::NONE;
179
        }
180
181
        $relyingParty = new RelyingParty(
182
            $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

182
            /** @scrutinizer ignore-type */ $this->config->get('relying_party_id'),
Loading history...
183
            $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

183
            /** @scrutinizer ignore-type */ $this->config->get('relying_party_name'),
Loading history...
184
            $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

184
            /** @scrutinizer ignore-type */ $this->config->get('relying_party_logo')
Loading history...
185
        );
186
187
        $userInfo = new UserInfo(
188
            new ByteBuffer($userId),
189
            $userName,
190
            $userDisplayName
191
        );
192
193
        $authenticatorSelection = new AuthenticatorSelection(
194
            $userVerificationType,
195
            false,
196
            $crossPlatformAttachment
197
        );
198
199
        $publicKey = (new PublicKey())
200
                      ->setUserInfo($userInfo)
201
                      ->setRelyingParty($relyingParty)
202
                      ->setAuthenticatorSelection($authenticatorSelection)
203
                      ->setExcludeCredentials($excludeCredentials)
204
                      ->setChallenge($this->createChallenge())
205
                      ->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

205
                      ->setTimeout(/** @scrutinizer ignore-type */ $this->config->get('timeout'))
Loading history...
206
                      ->setExtensions()
207
                      ->addPublicKeys()
208
                      ->setAttestation($attestation);
209
210
        return $publicKey;
211
    }
212
213
    /**
214
     * Return the authentication parameters
215
     * @param string $userVerificationType
216
     * @param array<string> $credentialIds
217
     * @return PublicKey
218
     */
219
    public function getAuthenticationParams(
220
        string $userVerificationType,
221
        array $credentialIds = []
222
    ): PublicKey {
223
        $allowedCredentials = [];
224
        foreach ($credentialIds as $id) {
225
            $hex = hex2bin($id);
226
            if ($hex === false) {
227
                throw new WebauthnException(sprintf('Can not convert credential id [%s] to binary', $id));
228
            }
229
230
            $allowedCredentials[] = new PublicKeyAuthParam(
231
                new ByteBuffer($hex),
232
                $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

232
                /** @scrutinizer ignore-type */ $this->config->get('transport_types')
Loading history...
233
            );
234
        }
235
236
        $publicKey = (new PublicKey())
237
                      ->setRelyingPartyId($this->relyingParty->getId())
238
                      ->setAllowCredentials($allowedCredentials)
239
                      ->setChallenge($this->createChallenge())
240
                      ->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

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

542
            ! in_array($this->relyingParty->getId(), /** @scrutinizer ignore-type */ $this->config->get('ignore_origins')) &&
Loading history...
543
            $url->getScheme() !== 'https'
544
        ) {
545
            return false;
546
        }
547
548
        // The RP ID must be equal to the origin's effective domain, or a registrable
549
        // domain suffix of the origin's effective domain.
550
        return preg_match('/' . preg_quote($this->relyingParty->getId()) . '$/i', $url->getHost()) === 1;
551
    }
552
553
    /**
554
     * Create the challenge if not yet created
555
     * @return ByteBuffer
556
     */
557
    protected function createChallenge(): ByteBuffer
558
    {
559
        if ($this->challenge === null) {
560
            $length = $this->config->get('challenge_length');
561
            $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

561
            $this->challenge = ByteBuffer::randomBuffer(/** @scrutinizer ignore-type */ $length);
Loading history...
562
        }
563
564
        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...
565
    }
566
567
    /**
568
     * Normalize the formats
569
     * @param array<string> $formats
570
     * @return array<string>
571
     */
572
    protected function normalizeFormats(array $formats): array
573
    {
574
        $supportedFormats = KeyFormat::all();
575
        if (count($formats) === 0) {
576
            return $supportedFormats;
577
        }
578
579
        $desiredFormats = array_filter($formats, function ($entry) use ($supportedFormats) {
580
            return in_array($entry, $supportedFormats);
581
        });
582
583
        if (count($desiredFormats) > 0) {
584
            return $desiredFormats;
585
        }
586
587
        return $supportedFormats;
588
    }
589
}
590