BaseFormat::createX5cChainFile()   B
last analyzed

Complexity

Conditions 11
Paths 6

Size

Total Lines 38
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 11
eloc 21
c 1
b 0
f 0
nc 6
nop 0
dl 0
loc 38
rs 7.3166

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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\Attestation\Format;
35
36
use JsonSerializable;
37
use Platine\Webauthn\Attestation\AuthenticatorData;
38
39
/**
40
 * @class BaseFormat
41
 * @package Platine\Webauthn\Attestation\Format
42
 */
43
abstract class BaseFormat implements JsonSerializable
44
{
45
    /**
46
     * The X5C Chain data
47
     * @var array<string>
48
     */
49
    protected array $x5cChain = [];
50
51
    /**
52
     * The X5C temporary file
53
     * @var string|null
54
     */
55
    protected ?string $x5cTempFile = null;
56
57
    /**
58
     * Create new instance
59
     * @param array<string|int, mixed> $attestationData
60
     * @param AuthenticatorData $authenticatorData
61
     */
62
    public function __construct(
63
        protected array $attestationData,
64
        protected AuthenticatorData $authenticatorData
65
    ) {
66
    }
67
68
    /**
69
     * Destructor
70
     */
71
    public function __destruct()
72
    {
73
        // delete X.509 chain certificate file after use
74
        if ($this->x5cTempFile !== null && is_file($this->x5cTempFile)) {
75
            unlink($this->x5cTempFile);
76
        }
77
    }
78
79
    /**
80
     * Return the certificate chain
81
     * @return string|null
82
     */
83
    public function getCertificateChain(): ?string
84
    {
85
        if ($this->x5cTempFile !== null && is_file($this->x5cTempFile)) {
86
            return (string) file_get_contents($this->x5cTempFile);
87
        }
88
89
        return null;
90
    }
91
92
    /**
93
     * Return the certificate with PEM format
94
     * @return string|null
95
     */
96
    public function getCertificatePem(): ?string
97
    {
98
        // Child classes need overwrite it
99
        return null;
100
    }
101
102
    /**
103
     * Check the validity of the signature
104
     * @param string $clientData
105
     * @return bool
106
     */
107
    public function validateAttestation(string $clientData): bool
0 ignored issues
show
Unused Code introduced by
The parameter $clientData is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

107
    public function validateAttestation(/** @scrutinizer ignore-unused */ string $clientData): bool

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
108
    {
109
        // Child classes need overwrite it
110
111
        return false;
112
    }
113
114
    /**
115
     * Validate the certificate against root certificates
116
     * @param array<string> $rootCertificates
117
     * @return bool
118
     */
119
    public function validateRootCertificate(array $rootCertificates): bool
0 ignored issues
show
Unused Code introduced by
The parameter $rootCertificates is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

119
    public function validateRootCertificate(/** @scrutinizer ignore-unused */ array $rootCertificates): bool

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
120
    {
121
        // Child classes need overwrite it
122
123
        return false;
124
    }
125
126
    /**
127
    * {@inheritdoc}
128
    * @return mixed
129
    */
130
    public function jsonSerialize(): mixed
131
    {
132
        return get_object_vars($this);
133
    }
134
135
    /**
136
     * Create the certificate PEM format
137
     * @param string $x5c
138
     * @return string
139
     */
140
    protected function createCertificatePem(string $x5c): string
141
    {
142
        $pem = '-----BEGIN CERTIFICATE-----' . "\n";
143
        $pem .= chunk_split(base64_encode($x5c), 64, "\n");
144
        $pem .= '-----END CERTIFICATE-----' . "\n";
145
146
        return $pem;
147
    }
148
149
    /**
150
     * Create the X5C chain file
151
     * @return string|null the PEM file path if success
152
     */
153
    protected function createX5cChainFile(): ?string
154
    {
155
        $content = '';
156
        if (count($this->x5cChain) > 0) {
157
            foreach ($this->x5cChain as $x5c) {
158
                $pem = $this->createCertificatePem($x5c);
159
                $certInfo = openssl_x509_parse($pem);
160
161
                // check if issuer = subject (self signed)
162
                if (is_array($certInfo) && is_array($certInfo['issuer']) && is_array($certInfo['subject'])) {
163
                    $selfSigned = true;
164
                    foreach ($certInfo['issuer'] as $key => $value) {
165
                        if ($certInfo['subject'][$key] !== $value) {
166
                            $selfSigned = false;
167
                            break;
168
                        }
169
                    }
170
171
                    if ($selfSigned === false) {
172
                        $content .= "\n" . $pem . "\n";
173
                    }
174
                }
175
            }
176
        }
177
178
        if (!empty($content)) {
179
            $this->x5cTempFile = sprintf(
180
                '%s/x5c_chain_%s.pem',
181
                sys_get_temp_dir(),
182
                base_convert((string) rand(), 10, 36)
183
            );
184
185
            if (file_put_contents($this->x5cTempFile, $content) !== false) {
186
                return $this->x5cTempFile;
187
            }
188
        }
189
190
        return null;
191
    }
192
193
    /**
194
     * Return the COSE algorithm based on the given number
195
     * @param int $coseNumber
196
     * @return array<string, mixed>|null
197
     */
198
    protected function getCoseAlgorithm(int $coseNumber): ?array
199
    {
200
        // https://www.iana.org/assignments/cose/cose.xhtml#algorithms
201
        $maps = [
202
            [
203
                'hash' => 'SHA1',
204
                'openssl' => OPENSSL_ALGO_SHA1,
205
                'cose' => [
206
                    -65535,  // RS1
207
                ],
208
            ],
209
            [
210
                'hash' => 'SHA256',
211
                'openssl' => OPENSSL_ALGO_SHA256,
212
                'cose' => [
213
                    -257, // RS256
214
                    -37,  // PS256
215
                    -7,   // ES256
216
                    5,     // HMAC256
217
                ],
218
            ],
219
            [
220
                'hash' => 'SHA384',
221
                'openssl' => OPENSSL_ALGO_SHA384,
222
                'cose' => [
223
                    -258, // RS384
224
                    -38,  // PS384
225
                    -35,  // ES384
226
                    6,     // HMAC384
227
                ],
228
            ],
229
            [
230
                'hash' => 'SHA512',
231
                'openssl' => OPENSSL_ALGO_SHA512,
232
                'cose' => [
233
                    -259, // RS512
234
                    -39,  // PS512
235
                    -36,  // ES512
236
                    7,     // HMAC512
237
                ],
238
            ],
239
        ];
240
241
        foreach ($maps as $map) {
242
            if (in_array($coseNumber, $map['cose'], true)) {
243
                return [
244
                    'hash' => $map['hash'],
245
                    'openssl' => $map['openssl'],
246
                ];
247
            }
248
        }
249
250
        return null;
251
    }
252
}
253