Test Failed
Pull Request — master (#21)
by
unknown
07:48
created

AuthenticatorData::isBackupEligable()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
1
<?php
2
3
namespace MadWizard\WebAuthn\Attestation;
4
5
use MadWizard\WebAuthn\Attestation\Identifier\Aaguid;
6
use MadWizard\WebAuthn\Crypto\CoseKey;
7
use MadWizard\WebAuthn\Crypto\CoseKeyInterface;
8
use MadWizard\WebAuthn\Exception\ByteBufferException;
9
use MadWizard\WebAuthn\Exception\CborException;
10
use MadWizard\WebAuthn\Exception\DataValidationException;
11
use MadWizard\WebAuthn\Exception\NotAvailableException;
12
use MadWizard\WebAuthn\Exception\ParseException;
13
use MadWizard\WebAuthn\Format\ByteBuffer;
14
use MadWizard\WebAuthn\Format\CborDecoder;
15
use MadWizard\WebAuthn\Format\CborMap;
16
17
final class AuthenticatorData
18
{
19
    /**
20
     * User present (UP).
21
     */
22
    private const FLAG_UP = 1 << 0;
23
24
    /**
25
     * User verified (UV).
26
     */
27
    private const FLAG_UV = 1 << 2;
28
29
    /**
30
     * Attested credential data included (AT).
31
     */
32
    private const FLAG_AT = 1 << 6;
33
34
    /**
35
     * Extension data included (ED).
36
     */
37
    private const FLAG_ED = 1 << 7;
38
    
39
     /**
40
     * Backup Eligibility (BE).
41
     */
42
    private const FLAG_BE = 1 << 3;
43
    
44
     /**
45
     * Backup State (BS).
46
     */
47
    private const FLAG_ED = 1 << 4;
48
49
    /**
50
     * SHA-256 hash of the RP ID associated with the credential.
51
     *
52
     * @var ByteBuffer
53
     */
54
    private $rpIdHash;
55
56
    /**
57
     * @var int FLAG_* flags
58
     */
59
    private $flags;
60
61
    /**
62
     * @var int
63
     */
64
    private $signCount;
65
66
    /**
67
     * @var CoseKeyInterface|null
68
     */
69
    private $key;
70
71
    /**
72
     * @var ByteBuffer|null
73
     */
74
    private $credentialId;
75
76
    /**
77
     * @var ByteBuffer|null
78
     */
79
    private $aaguid;
80
81
    /**
82
     * @var ByteBuffer
83
     */
84
    private $raw;
85
86
    /**
87
     * @var CborMap|null Authenticator extension output
88
     */
89
    private $extensionData;
90
91 27
    private const LENGTH_RP_ID_HASH = 32;
92
93 27
    private const LENGTH_AAGUID = 16;
94 27
95
    /**
96
     * AuthenticatorData constructor.
97 27
     *
98 27
     * @throws ParseException
99
     * @throws DataValidationException
100 27
     */
101 27
    public function __construct(ByteBuffer $data)
102 27
    {
103 27
        $this->raw = $data;
104
        $offset = 0;
105 27
106 12
        try {
107 12
            $this->rpIdHash = new ByteBuffer($data->getBytes(0, self::LENGTH_RP_ID_HASH));
108 12
            $offset += self::LENGTH_RP_ID_HASH;
109 12
110 12
            $this->flags = $data->getByteVal($offset);
111 12
            $offset++;
112 12
            $this->signCount = $data->getUint32Val($offset);
113 12
            $offset += 4;
114
115
            if ($this->hasAttestedCredentialData()) {
116 27
                $this->aaguid = new ByteBuffer($data->getBytes($offset, self::LENGTH_AAGUID));
117
                $offset += self::LENGTH_AAGUID;
118
                $credentialIdLength = $data->getUint16Val($offset);
119
                $offset += 2;
120
                $this->credentialId = new ByteBuffer($data->getBytes($offset, $credentialIdLength));
121
                $offset += $credentialIdLength;
122
                $this->key = CoseKey::parseCbor($data, $offset, $endOffset);
123
                $offset = $endOffset;
124 27
            }
125 27
126
            if ($this->hasExtensionData()) {
127
                $extensionData = CborDecoder::decodeInPlace($data, $offset, $endOffset);
128
                $offset = $endOffset;
129
                if (!$extensionData instanceof CborMap) {
130
                    throw new ParseException('Expected CBOR map for extension data in authenticator data.');
131
                }
132 27
                $this->extensionData = $extensionData;
133
            }
134 10
            if ($offset !== $data->getLength()) {
135
                throw new ParseException('Unexpected bytes at end of AuthenticatorData.');
136 10
            }
137
        } catch (ByteBufferException $e) {
138
            throw new ParseException('Failed to parse authenticator data buffer.', 0, $e);
139 5
        } catch (CborException $e) {
140
            throw new ParseException('Failed to parse CBOR authenticator data.', 0, $e);
141 5
        }
142
    }
143
144 6
    public function getRpIdHash(): ByteBuffer
145
    {
146 6
        return $this->rpIdHash;
147
    }
148
149 1
    public function getSignCount(): int
150
    {
151 1
        return $this->signCount;
152
    }
153
154 27
    public function isUserPresent(): bool
155
    {
156 27
        return ($this->flags & self::FLAG_UP) !== 0;
157
    }
158
159 27
    public function isUserVerified(): bool
160
    {
161 27
        return ($this->flags & self::FLAG_UV) !== 0;
162
    }
163
    
164 5
    public function isBackupEligable(): bool
165
    {
166 5
        return ($this->flags & self::FLAG_BE) !== 0;
167
    }
168
169
    public function isBackedUp(): bool
170
    {
171
        return ($this->flags & self::FLAG_BS) !== 0;
0 ignored issues
show
Bug introduced by
The constant MadWizard\WebAuthn\Attes...henticatorData::FLAG_BS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
172
    }
173
174 9
    public function hasAttestedCredentialData(): bool
175
    {
176 9
        return ($this->flags & self::FLAG_AT) !== 0;
177
    }
178
179 9
    public function hasExtensionData(): bool
180
    {
181
        return ($this->flags & self::FLAG_ED) !== 0;
182 2
    }
183
184 2
    public function getCredentialId(): ?ByteBuffer
185
    {
186
        return $this->credentialId;
187
    }
188
189
    /**
190
     * @throws NotAvailableException when authenticator data does not contain a key.
191
     *
192 6
     * @see hasKey
193
     */
194 6
    public function getKey(): CoseKeyInterface
195
    {
196
        if ($this->key === null) {
197 6
            throw new NotAvailableException('AuthenticatorData does not contain a key.');
198
        }
199
        return $this->key;
200
    }
201
202
    public function hasKey(): bool
203
    {
204
        return $this->key !== null;
205
    }
206
207
    public function hasAaguid(): bool
208
    {
209
        return $this->aaguid !== null;
210
    }
211
212 7
    public function getAaguid(): Aaguid
213
    {
214 7
        if ($this->aaguid === null) {
215
            throw new NotAvailableException('AuthenticatorData does not contain an AAGUID.');
216
        }
217
        return new Aaguid($this->aaguid);
218
    }
219
220
    /**
221
     * Returns the authenticator extension data output.
222
     */
223
    public function getExtensionData(): CborMap
224
    {
225
        if ($this->extensionData === null) {
226
            throw new NotAvailableException('AuthenticatorData does not contain extension data.');
227
        }
228
229
        return $this->extensionData;
230
    }
231
232
    public function getRaw(): ByteBuffer
233
    {
234
        return $this->raw;
235
    }
236
}
237