Failed Conditions
Push — master ( d2ad41...5d048e )
by Florent
21:38 queued 15:06
created

Component/KeyManagement/KeyConverter/RSAKey.php (1 issue)

mismatching argument types.

Documentation Minor

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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 Jose\Component\KeyManagement\KeyConverter;
15
16
use Base64Url\Base64Url;
17
use Jose\Component\Core\JWK;
18
use Jose\Component\Core\Util\BigInteger;
19
20
/**
21
 * @internal
22
 */
23
class RSAKey
24
{
25
    /**
26
     * @var array
27
     */
28
    private $values = [];
29
30
    /**
31
     * RSAKey constructor.
32
     *
33
     * @param array $data
34
     */
35
    private function __construct(array $data)
36
    {
37
        $this->loadJWK($data);
38
    }
39
40
    /**
41
     * @param string $pem
42
     *
43
     * @return RSAKey
44
     */
45
    public static function createFromPEM(string $pem): self
46
    {
47
        $data = self::loadPEM($pem);
48
49
        return new self($data);
50
    }
51
52
    /**
53
     * @param JWK $jwk
54
     *
55
     * @return RSAKey
56
     */
57
    public static function createFromJWK(JWK $jwk): self
58
    {
59
        return new self($jwk->all());
60
    }
61
62
    /**
63
     * @param string $data
64
     *
65
     * @return array
66
     */
67
    private static function loadPEM(string $data): array
68
    {
69
        $res = openssl_pkey_get_private($data);
70
        if (false === $res) {
71
            $res = openssl_pkey_get_public($data);
72
        }
73
        if (false === $res) {
74
            throw new \InvalidArgumentException('Unable to load the key.');
75
        }
76
77
        $details = openssl_pkey_get_details($res);
78
        if (!array_key_exists('rsa', $details)) {
79
            throw new \InvalidArgumentException('Unable to load the key.');
80
        }
81
82
        $values = ['kty' => 'RSA'];
83
        $keys = [
84
            'n'  => 'n',
85
            'e'  => 'e',
86
            'd'  => 'd',
87
            'p'  => 'p',
88
            'q'  => 'q',
89
            'dp' => 'dmp1',
90
            'dq' => 'dmq1',
91
            'qi' => 'iqmp',
92
        ];
93
        foreach ($details['rsa'] as $key => $value) {
94
            if (in_array($key, $keys)) {
95
                $value = Base64Url::encode($value);
96
                $values[array_search($key, $keys)] = $value;
97
            }
98
        }
99
100
        return $values;
101
    }
102
103
    /**
104
     * @return bool
105
     */
106
    public function isPublic(): bool
107
    {
108
        return !array_key_exists('d', $this->values);
109
    }
110
111
    /**
112
     * @param RSAKey $private
113
     *
114
     * @return RSAKey
115
     */
116
    public static function toPublic(self $private): self
117
    {
118
        $data = $private->toArray();
119
        $keys = ['p', 'd', 'q', 'dp', 'dq', 'qi'];
120
        foreach ($keys as $key) {
121
            if (array_key_exists($key, $data)) {
122
                unset($data[$key]);
123
            }
124
        }
125
126
        return new self($data);
127
    }
128
129
    /**
130
     * @return array
131
     */
132
    public function toArray(): array
133
    {
134
        return $this->values;
135
    }
136
137
    /**
138
     * @param array $jwk
139
     */
140
    private function loadJWK(array $jwk)
141
    {
142
        if (!array_key_exists('kty', $jwk)) {
143
            throw new \InvalidArgumentException('The key parameter "kty" is missing.');
144
        }
145
        if ('RSA' !== $jwk['kty']) {
146
            throw new \InvalidArgumentException('The JWK is not a RSA key.');
147
        }
148
149
        $this->values = $jwk;
150
    }
151
152
    /**
153
     * @return JWK
154
     */
155
    public function toJwk(): JWK
156
    {
157
        return JWK::create($this->values);
158
    }
159
160
    /**
161
     * This method will try to add Chinese Remainder Theorem (CRT) parameters.
162
     * With those primes, the decryption process is really fast.
163
     */
164
    public function optimize()
165
    {
166
        if (array_key_exists('d', $this->values)) {
167
            $this->populateCRT();
168
        }
169
    }
170
171
    /**
172
     * This method adds Chinese Remainder Theorem (CRT) parameters if primes 'p' and 'q' are available.
173
     */
174
    private function populateCRT()
175
    {
176
        if (!array_key_exists('p', $this->values) && !array_key_exists('q', $this->values)) {
177
            $d = BigInteger::createFromBinaryString(Base64Url::decode($this->values['d']));
178
            $e = BigInteger::createFromBinaryString(Base64Url::decode($this->values['e']));
179
            $n = BigInteger::createFromBinaryString(Base64Url::decode($this->values['n']));
180
181
            list($p, $q) = $this->findPrimeFactors($d, $e, $n);
182
            $this->values['p'] = Base64Url::encode($p->toBytes());
183
            $this->values['q'] = Base64Url::encode($q->toBytes());
184
        }
185
186
        if (array_key_exists('dp', $this->values) && array_key_exists('dq', $this->values) && array_key_exists('qi', $this->values)) {
187
            return;
188
        }
189
190
        $one = BigInteger::createFromDecimal(1);
191
        $d = BigInteger::createFromBinaryString(Base64Url::decode($this->values['d']));
192
        $p = BigInteger::createFromBinaryString(Base64Url::decode($this->values['p']));
193
        $q = BigInteger::createFromBinaryString(Base64Url::decode($this->values['q']));
194
195
        $this->values['dp'] = Base64Url::encode($d->mod($p->subtract($one))->toBytes());
196
        $this->values['dq'] = Base64Url::encode($d->mod($q->subtract($one))->toBytes());
197
        $this->values['qi'] = Base64Url::encode($q->modInverse($p)->toBytes());
198
    }
199
200
    /**
201
     * @param BigInteger $d
202
     * @param BigInteger $e
203
     * @param BigInteger $n
204
     *
205
     * @return BigInteger[]
206
     */
207
    private function findPrimeFactors(BigInteger $d, BigInteger $e, BigInteger $n): array
208
    {
209
        $zero = BigInteger::createFromDecimal(0);
210
        $one = BigInteger::createFromDecimal(1);
211
        $two = BigInteger::createFromDecimal(2);
212
213
        $k = $d->multiply($e)->subtract($one);
0 ignored issues
show
$e is of type object<Jose\Component\Core\Util\BigInteger>, but the function expects a object<self>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
214
215
        if ($k->isEven()) {
216
            $r = $k;
217
            $t = $zero;
218
219
            do {
220
                $r = $r->divide($two);
221
                $t = $t->add($one);
222
            } while ($r->isEven());
223
224
            $found = false;
225
            $y = null;
226
227
            for ($i = 1; $i <= 100; $i++) {
228
                $g = BigInteger::random($n->subtract($one));
229
                $y = $g->modPow($r, $n);
230
231
                if ($y->equals($one) || $y->equals($n->subtract($one))) {
232
                    continue;
233
                }
234
235
                for ($j = $one; $j->lowerThan($t->subtract($one)); $j = $j->add($one)) {
236
                    $x = $y->modPow($two, $n);
237
238
                    if ($x->equals($one)) {
239
                        $found = true;
240
241
                        break;
242
                    }
243
244
                    if ($x->equals($n->subtract($one))) {
245
                        continue;
246
                    }
247
248
                    $y = $x;
249
                }
250
251
                $x = $y->modPow($two, $n);
252
                if ($x->equals($one)) {
253
                    $found = true;
254
255
                    break;
256
                }
257
            }
258
259
            if (true === $found) {
260
                $p = $y->subtract($one)->gcd($n);
261
                $q = $n->divide($p);
262
263
                return [$p, $q];
264
            }
265
        }
266
267
        throw new \InvalidArgumentException('Unable to find prime factors.');
268
    }
269
}
270