MetadataToc::fromJson()   B
last analyzed

Complexity

Conditions 9
Paths 19

Size

Total Lines 33
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 90

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 9
eloc 21
c 1
b 0
f 0
nc 19
nop 1
dl 0
loc 33
ccs 0
cts 20
cp 0
crap 90
rs 8.0555
1
<?php
2
3
namespace MadWizard\WebAuthn\Metadata\Statement;
4
5
use DateTimeImmutable;
6
use MadWizard\WebAuthn\Attestation\Identifier\Aaguid;
7
use MadWizard\WebAuthn\Attestation\Identifier\Aaid;
8
use MadWizard\WebAuthn\Attestation\Identifier\AttestationKeyIdentifier;
9
use MadWizard\WebAuthn\Attestation\Identifier\IdentifierInterface;
10
use MadWizard\WebAuthn\Exception\DataValidationException;
11
use MadWizard\WebAuthn\Exception\ParseException;
12
use MadWizard\WebAuthn\Exception\VerificationException;
13
use MadWizard\WebAuthn\Format\ByteBuffer;
14
use MadWizard\WebAuthn\Format\DataValidator;
15
use MadWizard\WebAuthn\Format\SerializableTrait;
16
use Serializable;
17
18
class MetadataToc implements Serializable
19
{
20
    use SerializableTrait;
21
22
    /**
23
     * @var DateTimeImmutable
24
     */
25
    private $nextUpdate;
26
27
    /**
28
     * @var array
29
     */
30
    private $index = [];
31
32
    public static function fromJson(array $json): self
33
    {
34
        try {
35
            DataValidator::checkArray($json, [
36
                'nextUpdate' => 'string',
37
                'entries' => 'array',
38
            ], false);
39
        } catch (DataValidationException $e) {
40
            throw new VerificationException(sprintf('Unexpected or missing entries in MDS: %s.', $e->getMessage()), 0, $e);
41
        }
42
43
        $toc = new self();
44
45
        $toc->nextUpdate = new DateTimeImmutable($json['nextUpdate']); // TODO validate date?
46
        foreach ($json['entries'] as $entry) {
47
            if (!is_array($entry)) {
48
                continue;
49
            }
50
            if (is_string($entry['aaguid'] ?? false)) {
51
                $toc->index[Aaguid::TYPE][strtolower($entry['aaguid'])] = $entry;
52
            }
53
            if (is_string($entry['aaid'] ?? false)) {
54
                $toc->index[Aaid::TYPE][strtoupper($entry['aaid'])] = $entry;
55
            }
56
            if (is_array($entry['attestationCertificateKeyIdentifiers'] ?? false)) {
57
                foreach ($entry['attestationCertificateKeyIdentifiers'] as $id) {
58
                    if (is_string($id)) {
59
                        $toc->index[AttestationKeyIdentifier::TYPE][strtolower($id)] = $entry;
60
                    }
61
                }
62
            }
63
        }
64
        return $toc;
65
    }
66
67
    public function getNextUpdate(): DateTimeImmutable
68
    {
69
        return $this->nextUpdate;
70
    }
71
72
    public function findItem(IdentifierInterface $identifier): ?TocItem
73
    {
74
        $entry = $this->index[$identifier->getType()][$identifier->toString()] ?? null;
75
        if ($entry === null) {
76
            return null;
77
        }
78
79
        DataValidator::checkArray($entry, [
80
            'url' => '?string',
81
            'hash' => '?string',
82
            'statusReports' => '?array',
83
        ], false);
84
85
        $statusReports = array_map(function ($item) {
86
            if (!is_array($item)) {
87
                throw new ParseException('Invalid status report.');
88
            }
89
            return StatusReport::fromArray($item);
90
        }, $entry['statusReports']);
91
92
        return new TocItem(
93
            $identifier,
94
            $entry['hash'] === null ? null : ByteBuffer::fromBase64Url($entry['hash']),
95
            $entry['url'],
96
            $statusReports
97
        );
98
    }
99
100
    public function __serialize(): array
101
    {
102
        return [
103
            'nextUpdate' => $this->nextUpdate,
104
            'index' => $this->index,
105
        ];
106
    }
107
108
    public function __unserialize(array $serialized): void
109
    {
110
        $this->nextUpdate = $serialized['nextUpdate'];
111
        $this->index = $serialized['index'];
112
    }
113
}
114