JwtValidator::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 0

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 0
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 2
ccs 1
cts 1
cp 1
crap 1
rs 10
1
<?php
2
3
namespace MadWizard\WebAuthn\Pki\Jwt;
4
5
use MadWizard\WebAuthn\Crypto\Der;
6
use MadWizard\WebAuthn\Exception\ParseException;
7
use MadWizard\WebAuthn\Exception\VerificationException;
8
use MadWizard\WebAuthn\Format\ByteBuffer;
9
10
final class JwtValidator implements JwtValidatorInterface
11
{
12
    private const ALG_INFO =
13
        [
14
            'ES256' => ['convert' => true, 'sigComponentLen' => 32],
15
            'ES384' => ['convert' => true, 'sigComponentLen' => 48],
16
            'ES512' => ['convert' => true, 'sigComponentLen' => 66],
17
            'RS256' => ['convert' => false],
18
            'RS384' => ['convert' => false],
19
            'RS512' => ['convert' => false],
20
        ];
21
22 7
    public function __construct()
23
    {
24 7
    }
25
26 5
    public function validate(JwtInterface $token, ValidationContext $context): array
27
    {
28
        // TODO: validate other header items
29 5
        $header = $token->getHeader();
30 5
        $alg = $this->validateAlgorithm($header, $context);
31
32 4
        $asn1Sig = $this->convertSignature($token->getSignature(), $alg);
33 4
        if (!$context->getKey()->verifySignature($token->getSignedData(), $asn1Sig)) {
34 2
            throw new VerificationException('Invalid signature.');
35
        }
36
        /* TODO
37
                $now = $context->getReferenceUnixTime();
38
39
                $exp = $header['exp'] ?? null;
40
                if ($exp !== null) {
41
                    if (!is_int($exp)) {
42
                        throw new VerificationException('Invalid "exp" header value.');
43
                    }
44
                }
45
        */
46 2
        return $token->getBody();
47
    }
48
49 4
    private function convertSignature(ByteBuffer $signature, string $algorithm): ByteBuffer
50
    {
51 4
        $algInfo = self::ALG_INFO[$algorithm];
52 4
        if (!$algInfo['convert']) {
53 2
            return $signature;
54
        }
55 2
        $componentLen = $algInfo['sigComponentLen'];
56 2
        if ($signature->getLength() !== ($componentLen * 2)) {
57
            throw new ParseException(sprintf('Invalid signature length %d.', $signature->getLength()));
58
        }
59 2
        $r = $signature->getBytes(0, $componentLen);
60 2
        $s = $signature->getBytes($componentLen, $componentLen);
61 2
        return new ByteBuffer(Der::sequence(Der::unsignedInteger($r) . Der::unsignedInteger($s)));
62
    }
63
64 5
    private function validateAlgorithm(array $header, ValidationContext $ctx): string
65
    {
66 5
        $alg = $header['alg'] ?? null;
67 5
        if (in_array($alg, $ctx->getAllowedAlgorithms(), true)) {
68 4
            return $alg;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $alg could return the type null which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
69
        }
70 1
        throw new VerificationException('Algorithm not allowed.');
71
    }
72
}
73