Passed
Push — develop ( c53a49...51ae01 )
by nguereza
01:52
created

CredentialPublicKey::create()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 9
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 7
c 0
b 0
f 0
nc 3
nop 1
dl 0
loc 9
rs 10
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\Entity;
35
36
use JsonSerializable;
37
use Platine\Webauthn\Attestation\AuthenticatorData;
38
use Platine\Webauthn\Exception\WebauthnException;
39
use Platine\Webauthn\Helper\ByteBuffer;
40
use Platine\Webauthn\Helper\CborDecoder;
41
42
/**
43
 * @class CredentialPublicKey
44
 * @package Platine\Webauthn\Entity
45
 */
46
class CredentialPublicKey implements JsonSerializable
47
{
48
    /*
49
     * Cose encoded keys
50
     */
51
    public const COSE_KTY = 1;
52
    public const COSE_ALG = 3;
53
54
    public const EC2_ES256 = -7;
55
    public const EC2_P256 = 1;
56
    public const RSA_RS256 = -257;
57
58
    /*
59
     * Cose EC2 ES256 P-256 curve
60
     */
61
    public const COSE_CRV = -1;
62
    public const COSE_X = -2;
63
    public const COSE_Y = -3;
64
65
    /*
66
     * Cose RSA PS256
67
     */
68
    public const COSE_N = -1;
69
    public const COSE_E = -2;
70
71
72
    /**
73
     * The algorithm
74
     * @var int
75
     */
76
    protected int $alg;
77
78
    /**
79
     * The family of cryptographic algorithms used with the key.
80
     * @var int
81
     */
82
    protected int $kty;
83
84
    /**
85
     * The curve P-256
86
     * @var int|null
87
     */
88
    protected ?int $crv = null;
89
90
    /**
91
     * The x coordinate
92
     * @var string|null
93
     */
94
    protected ?string $x = null;
95
96
    /**
97
     * The y coordinate
98
     * @var string|null
99
     */
100
    protected ?string $y = null;
101
102
    /**
103
     * The RSA modulus
104
     * @var string|null
105
     */
106
    protected ?string $n = null;
107
108
    /**
109
     * The RSA public exponent
110
     * @var string|null
111
     */
112
    protected ?string $e = null;
113
114
    /**
115
     * Create new instance
116
     * @param string $binaryData
117
     * @param int $offset
118
     * @param int $endOffset
119
     */
120
    public function __construct(string $binaryData, int $offset, int &$endOffset)
121
    {
122
        $enc = CborDecoder::decodeInPlace($binaryData, $offset, $endOffset);
123
124
        // COSE key-encoded elliptic curve public key in EC2 format
125
        $this->kty = $enc[self::COSE_KTY];
126
        $this->alg = $enc[self::COSE_ALG];
127
128
        // Update properties
129
        $this->create($enc);
130
    }
131
132
    /**
133
     *
134
     * @return int
135
     */
136
    public function getAlg(): int
137
    {
138
        return $this->alg;
139
    }
140
141
    /**
142
     *
143
     * @return int
144
     */
145
    public function getKty(): int
146
    {
147
        return $this->kty;
148
    }
149
150
    /**
151
     *
152
     * @return int|null
153
     */
154
    public function getCrv(): ?int
155
    {
156
        return $this->crv;
157
    }
158
159
    /**
160
     *
161
     * @return string|null
162
     */
163
    public function getX(): ?string
164
    {
165
        return $this->x;
166
    }
167
168
    /**
169
     *
170
     * @return string|null
171
     */
172
    public function getY(): ?string
173
    {
174
        return $this->y;
175
    }
176
177
    /**
178
     *
179
     * @return string|null
180
     */
181
    public function getN(): ?string
182
    {
183
        return $this->n;
184
    }
185
186
    /**
187
     *
188
     * @return string|null
189
     */
190
    public function getE(): ?string
191
    {
192
        return $this->e;
193
    }
194
195
    /**
196
    * {@inheritdoc}
197
    * @return mixed
198
    */
199
    public function jsonSerialize()
200
    {
201
        return get_object_vars($this);
202
    }
203
204
    /**
205
     * Update properties based on the given data received
206
     * @param array<string, mixed> $enc
207
     * @return void
208
     */
209
    protected function create(array $enc): void
210
    {
211
        switch ($this->alg) {
212
            case self::EC2_ES256:
213
                $this->createES256($enc);
214
                break;
215
            case self::RSA_RS256:
216
                $this->createRSA256($enc);
217
                break;
218
        }
219
    }
220
221
    /**
222
     * Create for ES256
223
     * @param array<string|int, mixed> $enc
224
     * @return void
225
     */
226
    protected function createES256(array $enc): void
227
    {
228
        $this->crv = $enc[self::COSE_CRV];
229
        $this->x = $enc[self::COSE_X] instanceof ByteBuffer ? $enc[self::COSE_X]->getBinaryString() : null;
230
        $this->y = $enc[self::COSE_Y] instanceof ByteBuffer ? $enc[self::COSE_Y]->getBinaryString() : null;
231
232
        // remove encoded data
233
        unset($enc);
234
235
        // Validation
236
        if ($this->kty !== AuthenticatorData::EC2_TYPE) {
237
            throw new WebauthnException('Public key not in EC2 format');
238
        }
239
240
        if ($this->alg !== self::EC2_ES256) {
241
            throw new WebauthnException('The signature algorithm is not ES256');
242
        }
243
244
        if ($this->crv !== self::EC2_P256) {
245
            throw new WebauthnException('The curve is not P-256');
246
        }
247
248
        if (strlen((string) $this->x) !== 32) {
249
            throw new WebauthnException('Invalid X-coordinate provided');
250
        }
251
252
        if (strlen((string) $this->y) !== 32) {
253
            throw new WebauthnException('Invalid Y-coordinate provided');
254
        }
255
    }
256
257
    /**
258
     * Create for RSA256
259
     * @param array<string|int, mixed> $enc
260
     * @return void
261
     */
262
    protected function createRSA256(array $enc): void
263
    {
264
        $this->n = $enc[self::COSE_N] instanceof ByteBuffer ? $enc[self::COSE_N]->getBinaryString() : null;
265
        $this->e = $enc[self::COSE_E] instanceof ByteBuffer ? $enc[self::COSE_E]->getBinaryString() : null;
266
267
        // remove encoded data
268
        unset($enc);
269
270
        // Validation
271
        if ($this->kty !== AuthenticatorData::RSA_TYPE) {
272
            throw new WebauthnException('Public key not in RSA format');
273
        }
274
275
        if ($this->alg !== self::RSA_RS256) {
276
            throw new WebauthnException('The signature algorithm is not RS256');
277
        }
278
279
        if (strlen((string) $this->n) !== 256) {
280
            throw new WebauthnException('Invalid RSA modulus provided');
281
        }
282
283
        if (strlen((string) $this->e) !== 3) {
284
            throw new WebauthnException('Invalid RSA public exponent provided');
285
        }
286
    }
287
}
288