Completed
Push — master ( 6f0ba5...dd01b0 )
by Florent
03:51
created

AttestationObjectLoader   A

Complexity

Total Complexity 6

Size/Duplication

Total Lines 62
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 41
dl 0
loc 62
rs 10
c 0
b 0
f 0
wmc 6

3 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 3 1
A load() 0 45 4
A getDecoder() 0 3 1
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * The MIT License (MIT)
7
 *
8
 * Copyright (c) 2014-2018 Spomky-Labs
9
 *
10
 * This software may be modified and distributed under the terms
11
 * of the MIT license.  See the LICENSE file for details.
12
 */
13
14
namespace U2FAuthentication\Fido2\AttestationStatement;
15
16
use Base64Url\Base64Url;
17
use CBOR\Decoder;
18
use CBOR\MapObject;
19
use CBOR\StringStream;
20
use U2FAuthentication\Fido2\AttestedCredentialData;
21
use U2FAuthentication\Fido2\AuthenticatorData;
22
23
class AttestationObjectLoader
24
{
25
    private const FLAG_AT = 0b01000000;
26
    private const FLAG_ED = 0b10000000;
27
28
    private $decoder;
29
30
    public function __construct(Decoder $decoder)
31
    {
32
        $this->decoder = $decoder;
33
    }
34
35
    public function getDecoder(): Decoder
36
    {
37
        return $this->decoder;
38
    }
39
40
    public function load(string $data): AttestationObject
41
    {
42
        $decodedData = Base64Url::decode($data);
43
        $stream = new StringStream($decodedData);
44
        $attestationObject = $this->decoder->decode($stream)->getNormalizedData();
45
        $authData = $attestationObject['authData'];
46
47
        $authDataStream = new StringStream($authData);
48
        $rp_id_hash = $authDataStream->read(32);
49
        $flags = $authDataStream->read(1);
50
        $signCount = $authDataStream->read(4);
51
        $signCount = unpack('N', $signCount)[1];
52
53
        if (\ord($flags) & self::FLAG_AT) {
54
            $aaguid = $authDataStream->read(16);
55
            $credentialLength = $authDataStream->read(2);
56
            $credentialLength = unpack('n', $credentialLength)[1];
57
            $credentialId = $authDataStream->read($credentialLength);
58
            $credentialPublicKey = $this->decoder->decode($authDataStream);
59
            if (!$credentialPublicKey instanceof MapObject) {
60
                throw new \InvalidArgumentException('The data does not contain a valid credential public key.');
61
            }
62
            $attestedCredentialData = new AttestedCredentialData($aaguid, $credentialId, (string) $credentialPublicKey);
63
        } else {
64
            $attestedCredentialData = null;
65
        }
66
67
        if (\ord($flags) & self::FLAG_ED) {
68
            $extension = $this->decoder->decode($authDataStream);
69
        } else {
70
            $extension = null;
71
        }
72
        $authenticatorData = new AuthenticatorData(
73
            $authData,
74
            $rp_id_hash,
75
            $flags,
76
            $signCount,
77
            $attestedCredentialData,
78
            $extension
79
        );
80
81
        return new AttestationObject(
82
            $data,
83
            new AttestationStatement($attestationObject['fmt'], $attestationObject['attStmt']),
84
            $authenticatorData
85
        );
86
    }
87
}
88