Failed Conditions
Push — PHPSecLib_Rid ( cdef82...9d7c65 )
by Florent
02:40
created

RSA::_i2osp()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 1 Features 0
Metric Value
c 2
b 1
f 0
dl 0
loc 11
rs 9.4285
cc 2
eloc 6
nc 2
nop 2
1
<?php
2
3
/*
4
 * The MIT License (MIT)
5
 *
6
 * Copyright (c) 2014-2016 Spomky-Labs
7
 *
8
 * This software may be modified and distributed under the terms
9
 * of the MIT license.  See the LICENSE file for details.
10
 */
11
12
namespace Jose\Util;
13
14
final class RSA
15
{
16
    /**
17
     * Optimal Asymmetric Encryption Padding (OAEP).
18
     */
19
    const ENCRYPTION_OAEP = 1;
20
21
    /**
22
     * PKCS#1 padding.
23
     */
24
    const ENCRYPTION_PKCS1 = 2;
25
26
    /**
27
     * Probabilistic Signature Scheme for signing.
28
     */
29
    const SIGNATURE_PSS = 1;
30
    /**
31
     * PKCS#1 scheme.
32
     */
33
    const SIGNATURE_PKCS1 = 2;
34
35
    /**
36
     * ASN1 Integer.
37
     */
38
    const ASN1_INTEGER = 2;
39
40
    /**
41
     * ASN1 Bit String.
42
     */
43
    const ASN1_BITSTRING = 3;
44
45
    /**
46
     * ASN1 Octet String.
47
     */
48
    const ASN1_OCTETSTRING = 4;
49
50
    /**
51
     * ASN1 Object Identifier.
52
     */
53
    const ASN1_OBJECT = 6;
54
55
    /**
56
     * ASN1 Sequence (with the constucted bit set).
57
     */
58
    const ASN1_SEQUENCE = 48;
59
60
    /**
61
     * To use the pure-PHP implementation.
62
     */
63
    const MODE_INTERNAL = 1;
64
65
    /**
66
     * To use the OpenSSL library.
67
     */
68
    const MODE_OPENSSL = 2;
69
70
    /**
71
     * PKCS#1 formatted private key.
72
     */
73
    const PRIVATE_FORMAT_PKCS1 = 0;
74
75
    /**
76
     * PuTTY formatted private key.
77
     */
78
    const PRIVATE_FORMAT_PUTTY = 1;
79
80
    /**
81
     * XML formatted private key.
82
     */
83
    const PRIVATE_FORMAT_XML = 2;
84
85
    /**
86
     * PKCS#8 formatted private key.
87
     */
88
    const PRIVATE_FORMAT_PKCS8 = 8;
89
90
    /**
91
     * Raw public key.
92
     */
93
    const PUBLIC_FORMAT_RAW = 3;
94
95
    /**
96
     * PKCS#1 formatted public key (raw).
97
     */
98
    const PUBLIC_FORMAT_PKCS1 = 4;
99
    const PUBLIC_FORMAT_PKCS1_RAW = 4;
100
101
    /**
102
     * XML formatted public key.
103
     */
104
    const PUBLIC_FORMAT_XML = 5;
105
106
    /**
107
     * OpenSSH formatted public key.
108
     */
109
    const PUBLIC_FORMAT_OPENSSH = 6;
110
111
    /**
112
     * PKCS#1 formatted public key (encapsulated).
113
     */
114
    const PUBLIC_FORMAT_PKCS8 = 7;
115
116
    /**
117
     * Precomputed Zero.
118
     *
119
     * @var \Jose\Util\BigInteger
120
     */
121
    private $zero;
122
123
    /**
124
     * Precomputed One.
125
     *
126
     * @var \Jose\Util\BigInteger
127
     */
128
    private $one;
129
130
    /**
131
     * Private Key Format.
132
     *
133
     * @var int
134
     */
135
    private $privateKeyFormat = self::PRIVATE_FORMAT_PKCS1;
136
137
    /**
138
     * Public Key Format.
139
     *
140
     * @var int
141
     */
142
    private $publicKeyFormat = self::PUBLIC_FORMAT_PKCS8;
143
144
    /**
145
     * Modulus (ie. n).
146
     *
147
     * @var \Jose\Util\BigInteger
148
     */
149
    private $modulus;
150
151
    /**
152
     * Modulus length.
153
     *
154
     * @var \Jose\Util\BigInteger
155
     */
156
    private $k;
157
158
    /**
159
     * Exponent (ie. e or d).
160
     *
161
     * @var \Jose\Util\BigInteger
162
     */
163
    private $exponent;
164
165
    /**
166
     * Primes for Chinese Remainder Theorem (ie. p and q).
167
     *
168
     * @var array
169
     */
170
    private $primes;
171
172
    /**
173
     * Exponents for Chinese Remainder Theorem (ie. dP and dQ).
174
     *
175
     * @var array
176
     */
177
    private $exponents;
178
179
    /**
180
     * Coefficients for Chinese Remainder Theorem (ie. qInv).
181
     *
182
     * @var array
183
     */
184
    private $coefficients;
185
186
    /**
187
     * Hash name.
188
     *
189
     * @var string
190
     */
191
    private $hashName;
192
193
    /**
194
     * Hash function.
195
     *
196
     * @var \Jose\Util\Hash
197
     */
198
    private $hash;
199
200
    /**
201
     * Length of hash function output.
202
     *
203
     * @var int
204
     */
205
    private $hLen;
206
207
    /**
208
     * Length of salt.
209
     *
210
     * @var int
211
     */
212
    private $sLen;
213
214
    /**
215
     * Hash function for the Mask Generation Function.
216
     *
217
     * @var \Jose\Util\Hash
218
     */
219
    private $mgfHash;
220
221
    /**
222
     * Length of MGF hash function output.
223
     *
224
     * @var int
225
     */
226
    private $mgfHLen;
227
228
    /**
229
     * Encryption mode.
230
     *
231
     * @var int
232
     */
233
    private $encryptionMode = self::ENCRYPTION_OAEP;
234
235
    /**
236
     * Signature mode.
237
     *
238
     * @var int
239
     */
240
    private $signatureMode = self::SIGNATURE_PSS;
241
242
    /**
243
     * Public Exponent.
244
     *
245
     * @var mixed
246
     */
247
    private $publicExponent = false;
248
249
    /**
250
     * Password.
251
     *
252
     * @var string
253
     */
254
    private $password = false;
255
256
    /**
257
     * Components.
258
     *
259
     * @var array
260
     */
261
    private $components = [];
262
263
    /**
264
     * Current String.
265
     *
266
     * @var mixed
267
     */
268
    private $current;
269
270
    /**
271
     * OpenSSL configuration file name.
272
     *
273
     * @var mixed
274
     */
275
    private $configFile;
276
277
    /**
278
     * Public key comment field.
279
     *
280
     * @var string
281
     */
282
    private $comment = 'phpseclib-generated-key';
283
284
    /**
285
     * RSA constructor.
286
     */
287
    public function __construct()
288
    {
289
        $this->configFile = dirname(__FILE__).'/../openssl.cnf';
290
291
        if (!defined('CRYPT_RSA_MODE')) {
292
            switch (true) {
293
                // Math/BigInteger's openssl requirements are a little less stringent than Crypt/RSA's. in particular,
294
                // Math/BigInteger doesn't require an openssl.cfg file whereas Crypt/RSA does. so if Math/BigInteger
295
                // can't use OpenSSL it can be pretty trivially assumed, then, that Crypt/RSA can't either.
296
                case defined('MATH_BIGINTEGER_OPENSSL_DISABLE'):
297
                    define('CRYPT_RSA_MODE', self::MODE_INTERNAL);
298
                    break;
299
                case extension_loaded('openssl') && file_exists($this->configFile):
300
                    // some versions of XAMPP have mismatched versions of OpenSSL which causes it not to work
301
                    ob_start();
302
                    @phpinfo();
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
303
                    $content = ob_get_contents();
304
                    ob_end_clean();
305
306
                    preg_match_all('#OpenSSL (Header|Library) Version(.*)#im', $content, $matches);
307
308
                    $versions = [];
309
                    if (!empty($matches[1])) {
310
                        for ($i = 0; $i < count($matches[1]); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
Performance Best Practice introduced by
Consider avoiding function calls on each iteration of the for loop.

If you have a function call in the test part of a for loop, this function is executed on each iteration. Often such a function, can be moved to the initialization part and be cached.

// count() is called on each iteration
for ($i=0; $i < count($collection); $i++) { }

// count() is only called once
for ($i=0, $c=count($collection); $i<$c; $i++) { }
Loading history...
311
                            $fullVersion = trim(str_replace('=>', '', strip_tags($matches[2][$i])));
312
313
                            // Remove letter part in OpenSSL version
314
                            if (!preg_match('/(\d+\.\d+\.\d+)/i', $fullVersion, $m)) {
315
                                $versions[$matches[1][$i]] = $fullVersion;
316
                            } else {
317
                                $versions[$matches[1][$i]] = $m[0];
318
                            }
319
                        }
320
                    }
321
322
                    // it doesn't appear that OpenSSL versions were reported upon until PHP 5.3+
323
                    switch (true) {
324
                        case !isset($versions['Header']):
325
                        case !isset($versions['Library']):
326
                        case $versions['Header'] == $versions['Library']:
327
                            define('CRYPT_RSA_MODE', self::MODE_OPENSSL);
328
                            break;
329
                        default:
330
                            define('CRYPT_RSA_MODE', self::MODE_INTERNAL);
331
                            define('MATH_BIGINTEGER_OPENSSL_DISABLE', true);
332
                    }
333
                    break;
334
                default:
335
                    define('CRYPT_RSA_MODE', self::MODE_INTERNAL);
336
            }
337
        }
338
339
        $this->zero = BigInteger::createFromDecimalString('0');
340
        $this->one = BigInteger::createFromDecimalString('1');
341
342
        $this->hash = new Hash('sha1');
343
        $this->hLen = 20;
344
        $this->hashName = 'sha1';
345
        $this->mgfHash = new Hash('sha1');
346
        $this->mgfHLen = 20;
347
    }
348
349
    /**
350
     * Break a public or private key down into its constituant components.
351
     *
352
     * @param string $key
353
     * @param int    $type
354
     *
355
     * @return array
356
     */
357
    private function _parseKey($key, $type)
358
    {
359
        if ($type != self::PUBLIC_FORMAT_RAW && !is_string($key)) {
360
            return false;
361
        }
362
363
        switch ($type) {
364
            case self::PUBLIC_FORMAT_RAW:
365
                if (!is_array($key)) {
366
                    return false;
367
                }
368
                $components = [];
369
                switch (true) {
370
                    case isset($key['e']):
371
                        $components['publicExponent'] = $key['e']->copy();
372
                        break;
373
                    case isset($key['exponent']):
374
                        $components['publicExponent'] = $key['exponent']->copy();
375
                        break;
376
                    case isset($key['publicExponent']):
377
                        $components['publicExponent'] = $key['publicExponent']->copy();
378
                        break;
379
                    case isset($key[0]):
380
                        $components['publicExponent'] = $key[0]->copy();
381
                }
382
                switch (true) {
383
                    case isset($key['n']):
384
                        $components['modulus'] = $key['n']->copy();
385
                        break;
386
                    case isset($key['modulo']):
387
                        $components['modulus'] = $key['modulo']->copy();
388
                        break;
389
                    case isset($key['modulus']):
390
                        $components['modulus'] = $key['modulus']->copy();
391
                        break;
392
                    case isset($key[1]):
393
                        $components['modulus'] = $key[1]->copy();
394
                }
395
396
                return isset($components['modulus']) && isset($components['publicExponent']) ? $components : false;
397
            case self::PRIVATE_FORMAT_PKCS1:
398
            case self::PRIVATE_FORMAT_PKCS8:
399
            case self::PUBLIC_FORMAT_PKCS1:
400
                $decoded = $this->_extractBER($key);
401
402
                if ($decoded !== false) {
403
                    $key = $decoded;
404
                }
405
406
                $components = [];
407
408
                if (ord($this->_string_shift($key)) != self::ASN1_SEQUENCE) {
409
                    return false;
410
                }
411
                if ($this->_decodeLength($key) != strlen($key)) {
412
                    return false;
413
                }
414
415
                $tag = ord($this->_string_shift($key));
416
417
                if ($tag == self::ASN1_INTEGER && substr($key, 0, 3) == "\x01\x00\x30") {
418
                    $this->_string_shift($key, 3);
419
                    $tag = self::ASN1_SEQUENCE;
420
                }
421
422
                if ($tag == self::ASN1_SEQUENCE) {
423
                    $temp = $this->_string_shift($key, $this->_decodeLength($key));
424
                    if (ord($this->_string_shift($temp)) != self::ASN1_OBJECT) {
425
                        return false;
426
                    }
427
                    $length = $this->_decodeLength($temp);
428
                    switch ($this->_string_shift($temp, $length)) {
429
                        case "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01": // rsaEncryption
430
                            break;
431
                        case "\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03": // pbeWithMD5AndDES-CBC
432
                            if (ord($this->_string_shift($temp)) != self::ASN1_SEQUENCE) {
433
                                return false;
434
                            }
435
                            if ($this->_decodeLength($temp) != strlen($temp)) {
436
                                return false;
437
                            }
438
                            $this->_string_shift($temp); // assume it's an octet string
439
                            $salt = $this->_string_shift($temp, $this->_decodeLength($temp));
440
                            if (ord($this->_string_shift($temp)) != self::ASN1_INTEGER) {
441
                                return false;
442
                            }
443
                            $this->_decodeLength($temp);
444
                            list(, $iterationCount) = unpack('N', str_pad($temp, 4, chr(0), STR_PAD_LEFT));
445
                            $this->_string_shift($key); // assume it's an octet string
446
                            $length = $this->_decodeLength($key);
447
                            if (strlen($key) != $length) {
448
                                return false;
449
                            }
450
451
                            $crypto = new DES();
452
                            $crypto->setPassword($this->password, 'pbkdf1', 'md5', $salt, $iterationCount);
453
                            $key = $crypto->decrypt($key);
454
                            if ($key === false) {
455
                                return false;
456
                            }
457
458
                            return $this->_parseKey($key, self::PRIVATE_FORMAT_PKCS1);
459
                        default:
460
                            return false;
461
                    }
462
                    $tag = ord($this->_string_shift($key)); // skip over the BIT STRING / OCTET STRING tag
463
                    $this->_decodeLength($key); // skip over the BIT STRING / OCTET STRING length
464
                    if ($tag == self::ASN1_BITSTRING) {
465
                        $this->_string_shift($key);
466
                    }
467
                    if (ord($this->_string_shift($key)) != self::ASN1_SEQUENCE) {
468
                        return false;
469
                    }
470
                    if ($this->_decodeLength($key) != strlen($key)) {
471
                        return false;
472
                    }
473
                    $tag = ord($this->_string_shift($key));
474
                }
475
                if ($tag != self::ASN1_INTEGER) {
476
                    return false;
477
                }
478
479
                $length = $this->_decodeLength($key);
480
                $temp = $this->_string_shift($key, $length);
481
                if (strlen($temp) != 1 || ord($temp) > 2) {
482
                    $components['modulus'] = BigInteger::createFromBinaryString($temp);
483
                    $this->_string_shift($key); // skip over self::ASN1_INTEGER
484
                    $length = $this->_decodeLength($key);
485
                    $components[$type == self::PUBLIC_FORMAT_PKCS1 ? 'publicExponent' : 'privateExponent'] = BigInteger::createFromBinaryString($this->_string_shift($key, $length));
486
487
                    return $components;
488
                }
489
                if (ord($this->_string_shift($key)) != self::ASN1_INTEGER) {
490
                    return false;
491
                }
492
                $length = $this->_decodeLength($key);
493
                $components['modulus'] = BigInteger::createFromBinaryString($this->_string_shift($key, $length));
494
                $this->_string_shift($key);
495
                $length = $this->_decodeLength($key);
496
                $components['publicExponent'] = BigInteger::createFromBinaryString($this->_string_shift($key, $length));
497
                $this->_string_shift($key);
498
                $length = $this->_decodeLength($key);
499
                $components['privateExponent'] = BigInteger::createFromBinaryString($this->_string_shift($key, $length));
500
                $this->_string_shift($key);
501
                $length = $this->_decodeLength($key);
502
                $components['primes'] = [1 => BigInteger::createFromBinaryString($this->_string_shift($key, $length))];
503
                $this->_string_shift($key);
504
                $length = $this->_decodeLength($key);
505
                $components['primes'][] = BigInteger::createFromBinaryString($this->_string_shift($key, $length));
506
                $this->_string_shift($key);
507
                $length = $this->_decodeLength($key);
508
                $components['exponents'] = [1 => BigInteger::createFromBinaryString($this->_string_shift($key, $length))];
509
                $this->_string_shift($key);
510
                $length = $this->_decodeLength($key);
511
                $components['exponents'][] = BigInteger::createFromBinaryString($this->_string_shift($key, $length));
512
                $this->_string_shift($key);
513
                $length = $this->_decodeLength($key);
514
                $components['coefficients'] = [2 => BigInteger::createFromBinaryString($this->_string_shift($key, $length))];
515
516
                if (!empty($key)) {
517
                    if (ord($this->_string_shift($key)) != self::ASN1_SEQUENCE) {
518
                        return false;
519
                    }
520
                    $this->_decodeLength($key);
521
                    while (!empty($key)) {
522
                        if (ord($this->_string_shift($key)) != self::ASN1_SEQUENCE) {
523
                            return false;
524
                        }
525
                        $this->_decodeLength($key);
526
                        $key = substr($key, 1);
527
                        $length = $this->_decodeLength($key);
528
                        $components['primes'][] = BigInteger::createFromBinaryString($this->_string_shift($key, $length));
529
                        $this->_string_shift($key);
530
                        $length = $this->_decodeLength($key);
531
                        $components['exponents'][] = BigInteger::createFromBinaryString($this->_string_shift($key, $length));
532
                        $this->_string_shift($key);
533
                        $length = $this->_decodeLength($key);
534
                        $components['coefficients'][] = BigInteger::createFromBinaryString($this->_string_shift($key, $length));
535
                    }
536
                }
537
538
                return $components;
539
            case self::PUBLIC_FORMAT_OPENSSH:
540
                $parts = explode(' ', $key, 3);
541
542
                $key = isset($parts[1]) ? base64_decode($parts[1]) : false;
543
                if ($key === false) {
544
                    return false;
545
                }
546
547
                $comment = isset($parts[2]) ? $parts[2] : false;
548
549
                $cleanup = substr($key, 0, 11) == "\0\0\0\7ssh-rsa";
550
551
                if (strlen($key) <= 4) {
552
                    return false;
553
                }
554
                extract(unpack('Nlength', $this->_string_shift($key, 4)));
0 ignored issues
show
Bug introduced by
unpack('Nlength', $this->_string_shift($key, 4)) cannot be passed to extract() as the parameter $var_array expects a reference.
Loading history...
555
                $publicExponent = BigInteger::createFromBinaryString($this->_string_shift($key, $length));
556
                if (strlen($key) <= 4) {
557
                    return false;
558
                }
559
                extract(unpack('Nlength', $this->_string_shift($key, 4)));
0 ignored issues
show
Bug introduced by
unpack('Nlength', $this->_string_shift($key, 4)) cannot be passed to extract() as the parameter $var_array expects a reference.
Loading history...
560
                $modulus = BigInteger::createFromBinaryString($this->_string_shift($key, $length));
561
562
                if ($cleanup && strlen($key)) {
563
                    if (strlen($key) <= 4) {
564
                        return false;
565
                    }
566
                    extract(unpack('Nlength', $this->_string_shift($key, 4)));
0 ignored issues
show
Bug introduced by
unpack('Nlength', $this->_string_shift($key, 4)) cannot be passed to extract() as the parameter $var_array expects a reference.
Loading history...
567
                    $realModulus = BigInteger::createFromBinaryString($this->_string_shift($key, $length));
568
569
                    return strlen($key) ? false : [
570
                        'modulus'        => $realModulus,
571
                        'publicExponent' => $modulus,
572
                        'comment'        => $comment,
573
                    ];
574
                } else {
575
                    return strlen($key) ? false : [
576
                        'modulus'        => $modulus,
577
                        'publicExponent' => $publicExponent,
578
                        'comment'        => $comment,
579
                    ];
580
                }
581
            case self::PRIVATE_FORMAT_XML:
582
            case self::PUBLIC_FORMAT_XML:
583
                $this->components = [];
584
585
                $xml = xml_parser_create('UTF-8');
586
                xml_set_object($xml, $this);
587
                xml_set_element_handler($xml, '_start_element_handler', '_stop_element_handler');
588
                xml_set_character_data_handler($xml, '_data_handler');
589
                // add <xml></xml> to account for "dangling" tags like <BitStrength>...</BitStrength> that are sometimes added
590
                if (!xml_parse($xml, '<xml>'.$key.'</xml>')) {
591
                    return false;
592
                }
593
594
                return isset($this->components['modulus']) && isset($this->components['publicExponent']) ? $this->components : false;
595
            // from PuTTY's SSHPUBK.C
596
            case self::PRIVATE_FORMAT_PUTTY:
597
                $components = [];
598
                $key = preg_split('#\r\n|\r|\n#', $key);
599
                $type = trim(preg_replace('#PuTTY-User-Key-File-2: (.+)#', '$1', $key[0]));
600
                if ($type != 'ssh-rsa') {
601
                    return false;
602
                }
603
                $encryption = trim(preg_replace('#Encryption: (.+)#', '$1', $key[1]));
604
                $comment = trim(preg_replace('#Comment: (.+)#', '$1', $key[2]));
0 ignored issues
show
Unused Code introduced by
$comment is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
605
606
                $publicLength = trim(preg_replace('#Public-Lines: (\d+)#', '$1', $key[3]));
607
                $public = base64_decode(implode('', array_map('trim', array_slice($key, 4, $publicLength))));
608
                $public = substr($public, 11);
609
                extract(unpack('Nlength', $this->_string_shift($public, 4)));
0 ignored issues
show
Bug introduced by
unpack('Nlength', $this-...ring_shift($public, 4)) cannot be passed to extract() as the parameter $var_array expects a reference.
Loading history...
610
                $components['publicExponent'] = BigInteger::createFromBinaryString($this->_string_shift($public, $length));
611
                extract(unpack('Nlength', $this->_string_shift($public, 4)));
0 ignored issues
show
Bug introduced by
unpack('Nlength', $this-...ring_shift($public, 4)) cannot be passed to extract() as the parameter $var_array expects a reference.
Loading history...
612
                $components['modulus'] = BigInteger::createFromBinaryString($this->_string_shift($public, $length));
613
614
                $privateLength = trim(preg_replace('#Private-Lines: (\d+)#', '$1', $key[$publicLength + 4]));
615
                $private = base64_decode(implode('', array_map('trim', array_slice($key, $publicLength + 5, $privateLength))));
616
617
                switch ($encryption) {
618
                    case 'aes256-cbc':
619
                        $symkey = '';
620
                        $sequence = 0;
621
                        while (strlen($symkey) < 32) {
622
                            $temp = pack('Na*', $sequence++, $this->password);
623
                            $symkey .= pack('H*', sha1($temp));
624
                        }
625
                        $symkey = substr($symkey, 0, 32);
626
                        $crypto = new AES();
627
                }
628
629
                if ($encryption != 'none') {
630
                    $crypto->setKey($symkey);
631
                    $crypto->disablePadding();
632
                    $private = $crypto->decrypt($private);
633
                    if ($private === false) {
634
                        return false;
635
                    }
636
                }
637
638
                extract(unpack('Nlength', $this->_string_shift($private, 4)));
0 ignored issues
show
Bug introduced by
unpack('Nlength', $this-...ing_shift($private, 4)) cannot be passed to extract() as the parameter $var_array expects a reference.
Loading history...
639
                if (strlen($private) < $length) {
640
                    return false;
641
                }
642
                $components['privateExponent'] = BigInteger::createFromBinaryString($this->_string_shift($private, $length), true);
643
                extract(unpack('Nlength', $this->_string_shift($private, 4)));
0 ignored issues
show
Bug introduced by
unpack('Nlength', $this-...ing_shift($private, 4)) cannot be passed to extract() as the parameter $var_array expects a reference.
Loading history...
644
                if (strlen($private) < $length) {
645
                    return false;
646
                }
647
                $components['primes'] = [1 => BigInteger::createFromBinaryString($this->_string_shift($private, $length), true)];
648
                extract(unpack('Nlength', $this->_string_shift($private, 4)));
0 ignored issues
show
Bug introduced by
unpack('Nlength', $this-...ing_shift($private, 4)) cannot be passed to extract() as the parameter $var_array expects a reference.
Loading history...
649
                if (strlen($private) < $length) {
650
                    return false;
651
                }
652
                $components['primes'][] = BigInteger::createFromBinaryString($this->_string_shift($private, $length), true);
653
654
                $temp = $components['primes'][1]->subtract($this->one);
655
                $components['exponents'] = [1 => $components['publicExponent']->modInverse($temp)];
656
                $temp = $components['primes'][2]->subtract($this->one);
657
                $components['exponents'][] = $components['publicExponent']->modInverse($temp);
658
659
                extract(unpack('Nlength', $this->_string_shift($private, 4)));
0 ignored issues
show
Bug introduced by
unpack('Nlength', $this-...ing_shift($private, 4)) cannot be passed to extract() as the parameter $var_array expects a reference.
Loading history...
660
                if (strlen($private) < $length) {
661
                    return false;
662
                }
663
                $components['coefficients'] = [2 => BigInteger::createFromBinaryString($this->_string_shift($private, $length), true)];
664
665
                return $components;
666
        }
667
    }
668
669
    /**
670
     * Start Element Handler.
671
     *
672
     * Called by xml_set_element_handler()
673
     *
674
     * @param resource $parser
675
     * @param string   $name
676
     * @param array    $attribs
677
     */
678
    private function _start_element_handler($parser, $name, $attribs)
0 ignored issues
show
Unused Code introduced by
The parameter $parser is not used and could be removed.

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

Loading history...
Unused Code introduced by
The parameter $attribs is not used and could be removed.

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

Loading history...
679
    {
680
        //$name = strtoupper($name);
681
        switch ($name) {
682
            case 'MODULUS':
683
                $this->current = &$this->components['modulus'];
684
                break;
685
            case 'EXPONENT':
686
                $this->current = &$this->components['publicExponent'];
687
                break;
688
            case 'P':
689
                $this->current = &$this->components['primes'][1];
690
                break;
691
            case 'Q':
692
                $this->current = &$this->components['primes'][2];
693
                break;
694
            case 'DP':
695
                $this->current = &$this->components['exponents'][1];
696
                break;
697
            case 'DQ':
698
                $this->current = &$this->components['exponents'][2];
699
                break;
700
            case 'INVERSEQ':
701
                $this->current = &$this->components['coefficients'][2];
702
                break;
703
            case 'D':
704
                $this->current = &$this->components['privateExponent'];
705
        }
706
        $this->current = '';
707
    }
708
709
    /**
710
     * Stop Element Handler.
711
     */
712
    private function _stop_element_handler()
713
    {
714
        if (isset($this->current)) {
715
            $this->current = BigInteger::createFromBinaryString(base64_decode($this->current));
716
            unset($this->current);
717
        }
718
    }
719
720
    /**
721
     * Data Handler.
722
     *
723
     * Called by xml_set_character_data_handler()
724
     *
725
     * @param resource $parser
726
     * @param string   $data
727
     */
728
    public function _data_handler($parser, $data)
0 ignored issues
show
Unused Code introduced by
The parameter $parser is not used and could be removed.

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

Loading history...
729
    {
730
        if (!isset($this->current) || is_object($this->current)) {
731
            return;
732
        }
733
        $this->current .= trim($data);
734
    }
735
736
    /**
737
     * Loads a public or private key.
738
     *
739
     * @param string $key
740
     * @param bool   $type optional
741
     *
742
     * @return bool
743
     */
744
    public function loadKey($key, $type = false)
745
    {
746
        if ($key instanceof self) {
747
            $this->privateKeyFormat = $key->privateKeyFormat;
748
            $this->publicKeyFormat = $key->publicKeyFormat;
749
            $this->k = $key->k;
750
            $this->hLen = $key->hLen;
751
            $this->sLen = $key->sLen;
752
            $this->mgfHLen = $key->mgfHLen;
753
            $this->encryptionMode = $key->encryptionMode;
754
            $this->signatureMode = $key->signatureMode;
755
            $this->password = $key->password;
756
            $this->configFile = $key->configFile;
757
            $this->comment = $key->comment;
758
759
            if (is_object($key->hash)) {
760
                $this->hash = new Hash($key->hash->getHash());
761
            }
762
            if (is_object($key->mgfHash)) {
763
                $this->mgfHash = new Hash($key->mgfHash->getHash());
764
            }
765
766
            if (is_object($key->modulus)) {
767
                $this->modulus = $key->modulus->copy();
768
            }
769
            if (is_object($key->exponent)) {
770
                $this->exponent = $key->exponent->copy();
771
            }
772
            if (is_object($key->publicExponent)) {
773
                $this->publicExponent = $key->publicExponent->copy();
774
            }
775
776
            $this->primes = [];
777
            $this->exponents = [];
778
            $this->coefficients = [];
779
780
            foreach ($this->primes as $prime) {
781
                $this->primes[] = $prime->copy();
782
            }
783
            foreach ($this->exponents as $exponent) {
784
                $this->exponents[] = $exponent->copy();
785
            }
786
            foreach ($this->coefficients as $coefficient) {
787
                $this->coefficients[] = $coefficient->copy();
788
            }
789
790
            return true;
791
        }
792
793
        if ($type === false) {
794
            $types = [
795
                self::PUBLIC_FORMAT_RAW,
796
                self::PRIVATE_FORMAT_PKCS1,
797
                self::PRIVATE_FORMAT_XML,
798
                self::PRIVATE_FORMAT_PUTTY,
799
                self::PUBLIC_FORMAT_OPENSSH,
800
            ];
801
            foreach ($types as $type) {
802
                $components = $this->_parseKey($key, $type);
803
                if ($components !== false) {
804
                    break;
805
                }
806
            }
807
        } else {
808
            $components = $this->_parseKey($key, $type);
0 ignored issues
show
Documentation introduced by
$type is of type boolean, but the function expects a integer.

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...
809
        }
810
811
        if ($components === false) {
0 ignored issues
show
Bug introduced by
The variable $components does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
812
            return false;
813
        }
814
815
        if (isset($components['comment']) && $components['comment'] !== false) {
816
            $this->comment = $components['comment'];
817
        }
818
        $this->modulus = $components['modulus'];
819
        $this->k = strlen($this->modulus->toBytes());
0 ignored issues
show
Documentation Bug introduced by
It seems like strlen($this->modulus->toBytes()) of type integer is incompatible with the declared type object<Jose\Util\BigInteger> of property $k.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
820
        $this->exponent = isset($components['privateExponent']) ? $components['privateExponent'] : $components['publicExponent'];
821
        if (isset($components['primes'])) {
822
            $this->primes = $components['primes'];
823
            $this->exponents = $components['exponents'];
824
            $this->coefficients = $components['coefficients'];
825
            $this->publicExponent = $components['publicExponent'];
826
        } else {
827
            $this->primes = [];
828
            $this->exponents = [];
829
            $this->coefficients = [];
830
            $this->publicExponent = false;
831
        }
832
833
        switch ($type) {
834
            case self::PUBLIC_FORMAT_OPENSSH:
835
            case self::PUBLIC_FORMAT_RAW:
836
                $this->setPublicKey();
837
                break;
838
            case self::PRIVATE_FORMAT_PKCS1:
839
                switch (true) {
840
                    case strpos($key, '-BEGIN PUBLIC KEY-') !== false:
841
                    case strpos($key, '-BEGIN RSA PUBLIC KEY-') !== false:
842
                        $this->setPublicKey();
843
                }
844
        }
845
846
        return true;
847
    }
848
849
    /**
850
     * DER-decode the length.
851
     *
852
     * @param string $string
853
     *
854
     * @return int
855
     */
856
    private function _decodeLength(&$string)
857
    {
858
        $length = ord($this->_string_shift($string));
859
        if ($length & 0x80) { // definite length, long form
860
            $length &= 0x7F;
861
            $temp = $this->_string_shift($string, $length);
862
            list(, $length) = unpack('N', substr(str_pad($temp, 4, chr(0), STR_PAD_LEFT), -4));
863
        }
864
865
        return $length;
866
    }
867
868
    /**
869
     * String Shift.
870
     *
871
     * @param string $string
872
     * @param int    $index
873
     *
874
     * @return string
875
     */
876
    private function _string_shift(&$string, $index = 1)
877
    {
878
        $substr = substr($string, 0, $index);
879
        $string = substr($string, $index);
880
881
        return $substr;
882
    }
883
884
    /**
885
     * Determines which hashing function should be used.
886
     *
887
     * @param string $hash
888
     */
889
    public function setHash($hash)
890
    {
891
        switch ($hash) {
892
            case 'sha1':
893
                $this->hLen = 20;
894
                break;
895
            case 'sha256':
896
                $this->hLen = 32;
897
                break;
898
            case 'sha384':
899
                $this->hLen = 48;
900
                break;
901
            case 'sha512':
902
                $this->hLen = 64;
903
                break;
904
            default:
905
                throw new \InvalidArgumentException('Unsupported hash algorithm.');
906
        }
907
        $this->hash = new Hash($hash);
908
    }
909
910
    /**
911
     * Determines which hashing function should be used for the mask generation function.
912
     *
913
     * @param string $hash
914
     */
915
    public function setMGFHash($hash)
916
    {
917
        switch ($hash) {
918
            case 'sha1':
919
                $this->mgfHLen = 20;
920
                break;
921
            case 'sha256':
922
                $this->mgfHash = 32;
0 ignored issues
show
Documentation Bug introduced by
It seems like 32 of type integer is incompatible with the declared type object<Jose\Util\Hash> of property $mgfHash.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
923
                break;
924
            case 'sha384':
925
                $this->mgfHash = 48;
0 ignored issues
show
Documentation Bug introduced by
It seems like 48 of type integer is incompatible with the declared type object<Jose\Util\Hash> of property $mgfHash.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
926
                break;
927
            case 'sha512':
928
                $this->mgfHash = 64;
0 ignored issues
show
Documentation Bug introduced by
It seems like 64 of type integer is incompatible with the declared type object<Jose\Util\Hash> of property $mgfHash.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
929
                break;
930
            default:
931
                throw new \InvalidArgumentException('Unsupported hash algorithm.');
932
        }
933
        $this->mgfHash = new Hash($hash);
934
    }
935
936
    /**
937
     * Determines the salt length.
938
     *
939
     * @param int $sLen
940
     */
941
    public function setSaltLength($sLen)
942
    {
943
        $this->sLen = $sLen;
944
    }
945
946
    /**
947
     * Integer-to-Octet-String primitive.
948
     *
949
     * @param \Jose\Util\BigInteger $x
950
     * @param int                   $xLen
951
     *
952
     * @return string
953
     */
954
    private function _i2osp($x, $xLen)
955
    {
956
        $x = $x->toBytes();
957
        if (strlen($x) > $xLen) {
958
            user_error('Integer too large');
959
960
            return false;
961
        }
962
963
        return str_pad($x, $xLen, chr(0), STR_PAD_LEFT);
964
    }
965
966
    /**
967
     * Octet-String-to-Integer primitive.
968
     *
969
     * @param string $x
970
     *
971
     * @return \Jose\Util\BigInteger
972
     */
973
    private function _os2ip($x)
974
    {
975
        return BigInteger::createFromBinaryString($x);
976
    }
977
978
    /**
979
     * Exponentiate with or without Chinese Remainder Theorem.
980
     *
981
     * @param \Jose\Util\BigInteger $x
982
     *
983
     * @return \Jose\Util\BigInteger
984
     */
985
    private function _exponentiate($x)
986
    {
987
        if (empty($this->primes) || empty($this->coefficients) || empty($this->exponents)) {
988
            return $x->modPow($this->exponent, $this->modulus);
0 ignored issues
show
Bug Compatibility introduced by
The expression $x->modPow($this->exponent, $this->modulus); of type Jose\Util\BigInteger|boolean adds the type boolean to the return on line 988 which is incompatible with the return type documented by Jose\Util\RSA::_exponentiate of type Jose\Util\BigInteger.
Loading history...
989
        }
990
991
        $num_primes = count($this->primes);
992
993
        if (defined('CRYPT_RSA_DISABLE_BLINDING')) {
994
            $m_i = [
995
                1 => $x->modPow($this->exponents[1], $this->primes[1]),
996
                2 => $x->modPow($this->exponents[2], $this->primes[2]),
997
            ];
998
            $h = $m_i[1]->subtract($m_i[2]);
0 ignored issues
show
Bug introduced by
It seems like $m_i[2] can also be of type boolean; however, Jose\Util\BigInteger::subtract() does only seem to accept object<Jose\Util\BigInteger>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
999
            $h = $h->multiply($this->coefficients[2]);
1000
            list(, $h) = $h->divide($this->primes[1]);
1001
            $m = $m_i[2]->add($h->multiply($this->primes[2]));
1002
1003
            $r = $this->primes[1];
1004
            for ($i = 3; $i <= $num_primes; $i++) {
1005
                $m_i = $x->modPow($this->exponents[$i], $this->primes[$i]);
1006
1007
                $r = $r->multiply($this->primes[$i - 1]);
1008
1009
                $h = $m_i->subtract($m);
1010
                $h = $h->multiply($this->coefficients[$i]);
1011
                list(, $h) = $h->divide($this->primes[$i]);
1012
1013
                $m = $m->add($r->multiply($h));
1014
            }
1015
        } else {
1016
            $smallest = $this->primes[1];
1017
            for ($i = 2; $i <= $num_primes; $i++) {
1018
                if ($smallest->compare($this->primes[$i]) > 0) {
1019
                    $smallest = $this->primes[$i];
1020
                }
1021
            }
1022
1023
            $one = BigInteger::createFromDecimalString('1');
1024
1025
            $r = $one->random($one, $smallest->subtract($one));
1026
1027
            $m_i = [
1028
                1 => $this->_blind($x, $r, 1),
1029
                2 => $this->_blind($x, $r, 2),
1030
            ];
1031
            $h = $m_i[1]->subtract($m_i[2]);
1032
            $h = $h->multiply($this->coefficients[2]);
1033
            list(, $h) = $h->divide($this->primes[1]);
1034
            $m = $m_i[2]->add($h->multiply($this->primes[2]));
1035
1036
            $r = $this->primes[1];
1037
            for ($i = 3; $i <= $num_primes; $i++) {
1038
                $m_i = $this->_blind($x, $r, $i);
1039
1040
                $r = $r->multiply($this->primes[$i - 1]);
1041
1042
                $h = $m_i->subtract($m);
1043
                $h = $h->multiply($this->coefficients[$i]);
1044
                list(, $h) = $h->divide($this->primes[$i]);
1045
1046
                $m = $m->add($r->multiply($h));
1047
            }
1048
        }
1049
1050
        return $m;
1051
    }
1052
1053
    /**
1054
     * Performs RSA Blinding.
1055
     *
1056
     * @param \Jose\Util\BigInteger $x
1057
     * @param \Jose\Util\BigInteger $r
1058
     * @param int                   $i
1059
     *
1060
     * @return \Jose\Util\BigInteger
1061
     */
1062
    private function _blind($x, $r, $i)
1063
    {
1064
        $x = $x->multiply($r->modPow($this->publicExponent, $this->primes[$i]));
0 ignored issues
show
Bug introduced by
It seems like $r->modPow($this->public...ent, $this->primes[$i]) targeting Jose\Util\BigInteger::modPow() can also be of type boolean; however, Jose\Util\BigInteger::multiply() does only seem to accept object<Jose\Util\BigInteger>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
1065
        $x = $x->modPow($this->exponents[$i], $this->primes[$i]);
1066
1067
        $r = $r->modInverse($this->primes[$i]);
1068
        $x = $x->multiply($r);
0 ignored issues
show
Bug introduced by
It seems like $r defined by $r->modInverse($this->primes[$i]) on line 1067 can also be of type boolean; however, Jose\Util\BigInteger::multiply() does only seem to accept object<Jose\Util\BigInteger>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
1069
        list(, $x) = $x->divide($this->primes[$i]);
1070
1071
        return $x;
1072
    }
1073
1074
    /**
1075
     * Performs blinded RSA equality testing.
1076
     *
1077
     * @param string $x
1078
     * @param string $y
1079
     *
1080
     * @return bool
1081
     */
1082
    private function _equals($x, $y)
1083
    {
1084
        if (strlen($x) != strlen($y)) {
1085
            return false;
1086
        }
1087
1088
        $result = 0;
1089
        for ($i = 0; $i < strlen($x); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
Consider avoiding function calls on each iteration of the for loop.

If you have a function call in the test part of a for loop, this function is executed on each iteration. Often such a function, can be moved to the initialization part and be cached.

// count() is called on each iteration
for ($i=0; $i < count($collection); $i++) { }

// count() is only called once
for ($i=0, $c=count($collection); $i<$c; $i++) { }
Loading history...
1090
            $result |= ord($x[$i]) ^ ord($y[$i]);
1091
        }
1092
1093
        return $result == 0;
1094
    }
1095
1096
    /**
1097
     * RSAEP.
1098
     *
1099
     * @param \Jose\Util\BigInteger $m
1100
     *
1101
     * @return \Jose\Util\BigInteger
1102
     */
1103
    private function _rsaep($m)
1104
    {
1105
        if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) {
1106
            user_error('Message representative out of range');
1107
1108
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by Jose\Util\RSA::_rsaep of type Jose\Util\BigInteger.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
1109
        }
1110
1111
        return $this->_exponentiate($m);
1112
    }
1113
1114
    /**
1115
     * RSADP.
1116
     *
1117
     * @param \Jose\Util\BigInteger $c
1118
     *
1119
     * @return \Jose\Util\BigInteger
1120
     */
1121
    private function _rsadp($c)
1122
    {
1123
        if ($c->compare($this->zero) < 0 || $c->compare($this->modulus) > 0) {
1124
            user_error('Ciphertext representative out of range');
1125
1126
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by Jose\Util\RSA::_rsadp of type Jose\Util\BigInteger.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
1127
        }
1128
1129
        return $this->_exponentiate($c);
1130
    }
1131
1132
    /**
1133
     * RSASP1.
1134
     *
1135
     * @param \Jose\Util\BigInteger $m
1136
     *
1137
     * @return \Jose\Util\BigInteger
1138
     */
1139
    private function _rsasp1($m)
1140
    {
1141
        if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) {
1142
            user_error('Message representative out of range');
1143
1144
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by Jose\Util\RSA::_rsasp1 of type Jose\Util\BigInteger.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
1145
        }
1146
1147
        return $this->_exponentiate($m);
1148
    }
1149
1150
    /**
1151
     * RSAVP1.
1152
     *
1153
     * @param \Jose\Util\BigInteger $s
1154
     *
1155
     * @return \Jose\Util\BigInteger
1156
     */
1157
    private function _rsavp1($s)
1158
    {
1159
        if ($s->compare($this->zero) < 0 || $s->compare($this->modulus) > 0) {
1160
            user_error('Signature representative out of range');
1161
1162
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by Jose\Util\RSA::_rsavp1 of type Jose\Util\BigInteger.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
1163
        }
1164
1165
        return $this->_exponentiate($s);
1166
    }
1167
1168
    /**
1169
     * MGF1.
1170
     *
1171
     * @param string $mgfSeed
1172
     * @param int    $maskLen
1173
     *
1174
     * @return string
1175
     */
1176
    private function _mgf1($mgfSeed, $maskLen)
1177
    {
1178
        // if $maskLen would yield strings larger than 4GB, PKCS#1 suggests a "Mask too long" error be output.
1179
1180
        $t = '';
1181
        $count = ceil($maskLen / $this->mgfHLen);
1182
        for ($i = 0; $i < $count; $i++) {
1183
            $c = pack('N', $i);
1184
            $t .= $this->mgfHash->hash($mgfSeed.$c);
1185
        }
1186
1187
        return substr($t, 0, $maskLen);
1188
    }
1189
1190
    /**
1191
     * RSAES-OAEP-ENCRYPT.
1192
     *
1193
     * @param string $m
1194
     * @param string $l
1195
     *
1196
     * @return string
1197
     */
1198
    private function _rsaes_oaep_encrypt($m, $l = '')
1199
    {
1200
        $mLen = strlen($m);
1201
1202
        // Length checking
1203
1204
        // if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
1205
        // be output.
1206
1207
        if ($mLen > $this->k - 2 * $this->hLen - 2) {
1208
            user_error('Message too long');
1209
1210
            return false;
1211
        }
1212
1213
        // EME-OAEP encoding
1214
1215
        $lHash = $this->hash->hash($l);
1216
        $ps = str_repeat(chr(0), $this->k - $mLen - 2 * $this->hLen - 2);
1217
        $db = $lHash.$ps.chr(1).$m;
1218
        $seed = random_bytes($this->hLen);
1219
        $dbMask = $this->_mgf1($seed, $this->k - $this->hLen - 1);
1220
        $maskedDB = $db ^ $dbMask;
1221
        $seedMask = $this->_mgf1($maskedDB, $this->hLen);
1222
        $maskedSeed = $seed ^ $seedMask;
1223
        $em = chr(0).$maskedSeed.$maskedDB;
1224
1225
        // RSA encryption
1226
1227
        $m = $this->_os2ip($em);
1228
        $c = $this->_rsaep($m);
1229
        $c = $this->_i2osp($c, $this->k);
1230
1231
        // Output the ciphertext C
1232
1233
        return $c;
1234
    }
1235
1236
    /**
1237
     * RSAES-OAEP-DECRYPT.
1238
     *
1239
     * @param string $c
1240
     * @param string $l
1241
     *
1242
     * @return string
1243
     */
1244
    private function _rsaes_oaep_decrypt($c, $l = '')
1245
    {
1246
        // Length checking
1247
1248
        // if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
1249
        // be output.
1250
1251
        if (strlen($c) != $this->k || $this->k < 2 * $this->hLen + 2) {
1252
            user_error('Decryption error');
1253
1254
            return false;
1255
        }
1256
1257
        // RSA decryption
1258
1259
        $c = $this->_os2ip($c);
1260
        $m = $this->_rsadp($c);
1261
        if ($m === false) {
1262
            user_error('Decryption error');
1263
1264
            return false;
1265
        }
1266
        $em = $this->_i2osp($m, $this->k);
0 ignored issues
show
Documentation introduced by
$this->k is of type object<Jose\Util\BigInteger>, but the function expects a integer.

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...
1267
1268
        // EME-OAEP decoding
1269
1270
        $lHash = $this->hash->hash($l);
1271
        $y = ord($em[0]);
0 ignored issues
show
Unused Code introduced by
$y is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1272
        $maskedSeed = substr($em, 1, $this->hLen);
1273
        $maskedDB = substr($em, $this->hLen + 1);
1274
        $seedMask = $this->_mgf1($maskedDB, $this->hLen);
1275
        $seed = $maskedSeed ^ $seedMask;
1276
        $dbMask = $this->_mgf1($seed, $this->k - $this->hLen - 1);
1277
        $db = $maskedDB ^ $dbMask;
1278
        $lHash2 = substr($db, 0, $this->hLen);
1279
        $m = substr($db, $this->hLen);
1280
        if ($lHash != $lHash2) {
1281
            user_error('Decryption error');
1282
1283
            return false;
1284
        }
1285
        $m = ltrim($m, chr(0));
1286
        if (ord($m[0]) != 1) {
1287
            user_error('Decryption error');
1288
1289
            return false;
1290
        }
1291
1292
        // Output the message M
1293
1294
        return substr($m, 1);
1295
    }
1296
1297
    /**
1298
     * RSAES-PKCS1-V1_5-ENCRYPT.
1299
     *
1300
     * @param string $m
1301
     *
1302
     * @return string
1303
     */
1304
    private function _rsaes_pkcs1_v1_5_encrypt($m)
1305
    {
1306
        $mLen = strlen($m);
1307
1308
        // Length checking
1309
1310
        if ($mLen > $this->k - 11) {
1311
            user_error('Message too long');
1312
1313
            return false;
1314
        }
1315
1316
        // EME-PKCS1-v1_5 encoding
1317
1318
        $psLen = $this->k - $mLen - 3;
1319
        $ps = '';
1320
        while (strlen($ps) != $psLen) {
1321
            $temp = random_bytes($psLen - strlen($ps));
1322
            $temp = str_replace("\x00", '', $temp);
1323
            $ps .= $temp;
1324
        }
1325
        $type = 2;
1326
        // see the comments of _rsaes_pkcs1_v1_5_decrypt() to understand why this is being done
1327
        if (defined('CRYPT_RSA_PKCS15_COMPAT') && (!isset($this->publicExponent) || $this->exponent !== $this->publicExponent)) {
1328
            $type = 1;
1329
            // "The padding string PS shall consist of k-3-||D|| octets. ... for block type 01, they shall have value FF"
1330
            $ps = str_repeat("\xFF", $psLen);
1331
        }
1332
        $em = chr(0).chr($type).$ps.chr(0).$m;
1333
1334
        // RSA encryption
1335
        $m = $this->_os2ip($em);
1336
        $c = $this->_rsaep($m);
1337
        $c = $this->_i2osp($c, $this->k);
1338
1339
        // Output the ciphertext C
1340
1341
        return $c;
1342
    }
1343
1344
    /**
1345
     * RSAES-PKCS1-V1_5-DECRYPT.
1346
     *
1347
     * @param string $c
1348
     *
1349
     * @return string
1350
     */
1351
    private function _rsaes_pkcs1_v1_5_decrypt($c)
1352
    {
1353
        // Length checking
1354
1355
        if (strlen($c) != $this->k) { // or if k < 11
1356
            user_error('Decryption error');
1357
1358
            return false;
1359
        }
1360
1361
        // RSA decryption
1362
1363
        $c = $this->_os2ip($c);
1364
        $m = $this->_rsadp($c);
1365
1366
        if ($m === false) {
1367
            user_error('Decryption error');
1368
1369
            return false;
1370
        }
1371
        $em = $this->_i2osp($m, $this->k);
0 ignored issues
show
Documentation introduced by
$this->k is of type object<Jose\Util\BigInteger>, but the function expects a integer.

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...
1372
1373
        // EME-PKCS1-v1_5 decoding
1374
1375
        if (ord($em[0]) != 0 || ord($em[1]) > 2) {
1376
            user_error('Decryption error');
1377
1378
            return false;
1379
        }
1380
1381
        $ps = substr($em, 2, strpos($em, chr(0), 2) - 2);
1382
        $m = substr($em, strlen($ps) + 3);
1383
1384
        if (strlen($ps) < 8) {
1385
            user_error('Decryption error');
1386
1387
            return false;
1388
        }
1389
1390
        // Output M
1391
1392
        return $m;
1393
    }
1394
1395
    /**
1396
     * EMSA-PSS-ENCODE.
1397
     *
1398
     * @param string $m
1399
     * @param int    $emBits
1400
     *
1401
     * @return bool
1402
     */
1403
    private function _emsa_pss_encode($m, $emBits)
1404
    {
1405
        // if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
1406
        // be output.
1407
1408
        $emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8)
1409
        $sLen = $this->sLen ? $this->sLen : $this->hLen;
1410
1411
        $mHash = $this->hash->hash($m);
1412
        if ($emLen < $this->hLen + $sLen + 2) {
1413
            user_error('Encoding error');
1414
1415
            return false;
1416
        }
1417
1418
        $salt = random_bytes($sLen);
1419
        $m2 = "\0\0\0\0\0\0\0\0".$mHash.$salt;
1420
        $h = $this->hash->hash($m2);
1421
        $ps = str_repeat(chr(0), $emLen - $sLen - $this->hLen - 2);
1422
        $db = $ps.chr(1).$salt;
1423
        $dbMask = $this->_mgf1($h, $emLen - $this->hLen - 1);
1424
        $maskedDB = $db ^ $dbMask;
1425
        $maskedDB[0] = ~chr(0xFF << ($emBits & 7)) & $maskedDB[0];
1426
        $em = $maskedDB.$h.chr(0xBC);
1427
1428
        return $em;
1429
    }
1430
1431
    /**
1432
     * EMSA-PSS-VERIFY.
1433
     *
1434
     * @param string $m
1435
     * @param string $em
1436
     * @param int    $emBits
1437
     *
1438
     * @return string
1439
     */
1440
    private function _emsa_pss_verify($m, $em, $emBits)
1441
    {
1442
        // if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
1443
        // be output.
1444
1445
        $emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8);
1446
        $sLen = $this->sLen ? $this->sLen : $this->hLen;
1447
1448
        $mHash = $this->hash->hash($m);
1449
        if ($emLen < $this->hLen + $sLen + 2) {
1450
            return false;
1451
        }
1452
1453
        if ($em[strlen($em) - 1] != chr(0xBC)) {
1454
            return false;
1455
        }
1456
1457
        $maskedDB = substr($em, 0, -$this->hLen - 1);
1458
        $h = substr($em, -$this->hLen - 1, $this->hLen);
1459
        $temp = chr(0xFF << ($emBits & 7));
1460
        if ((~$maskedDB[0] & $temp) != $temp) {
1461
            return false;
1462
        }
1463
        $dbMask = $this->_mgf1($h, $emLen - $this->hLen - 1);
1464
        $db = $maskedDB ^ $dbMask;
1465
        $db[0] = ~chr(0xFF << ($emBits & 7)) & $db[0];
1466
        $temp = $emLen - $this->hLen - $sLen - 2;
1467
        if (substr($db, 0, $temp) != str_repeat(chr(0), $temp) || ord($db[$temp]) != 1) {
1468
            return false;
1469
        }
1470
        $salt = substr($db, $temp + 1); // should be $sLen long
1471
        $m2 = "\0\0\0\0\0\0\0\0".$mHash.$salt;
1472
        $h2 = $this->hash->hash($m2);
1473
1474
        return $this->_equals($h, $h2);
1475
    }
1476
1477
    /**
1478
     * RSASSA-PSS-SIGN.
1479
     *
1480
     * @param string $m
1481
     *
1482
     * @return string
1483
     */
1484
    private function _rsassa_pss_sign($m)
1485
    {
1486
        // EMSA-PSS encoding
1487
1488
        $em = $this->_emsa_pss_encode($m, 8 * $this->k - 1);
1489
1490
        // RSA signature
1491
1492
        $m = $this->_os2ip($em);
0 ignored issues
show
Security Bug introduced by
It seems like $em defined by $this->_emsa_pss_encode($m, 8 * $this->k - 1) on line 1488 can also be of type false; however, Jose\Util\RSA::_os2ip() does only seem to accept string, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
1493
        $s = $this->_rsasp1($m);
1494
        $s = $this->_i2osp($s, $this->k);
0 ignored issues
show
Documentation introduced by
$this->k is of type object<Jose\Util\BigInteger>, but the function expects a integer.

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...
1495
1496
        // Output the signature S
1497
1498
        return $s;
1499
    }
1500
1501
    /**
1502
     * RSASSA-PSS-VERIFY.
1503
     *
1504
     * @param string $m
1505
     * @param string $s
1506
     *
1507
     * @return string
1508
     */
1509
    private function _rsassa_pss_verify($m, $s)
1510
    {
1511
        // Length checking
1512
1513
        if (strlen($s) != $this->k) {
1514
            user_error('Invalid signature');
1515
1516
            return false;
1517
        }
1518
1519
        // RSA verification
1520
1521
        $modBits = 8 * $this->k;
1522
1523
        $s2 = $this->_os2ip($s);
1524
        $m2 = $this->_rsavp1($s2);
1525
        if ($m2 === false) {
1526
            user_error('Invalid signature');
1527
1528
            return false;
1529
        }
1530
        $em = $this->_i2osp($m2, $modBits >> 3);
1531
        if ($em === false) {
1532
            user_error('Invalid signature');
1533
1534
            return false;
1535
        }
1536
1537
        // EMSA-PSS verification
1538
1539
        return $this->_emsa_pss_verify($m, $em, $modBits - 1);
1540
    }
1541
1542
    /**
1543
     * EMSA-PKCS1-V1_5-ENCODE.
1544
     *
1545
     * @param string $m
1546
     * @param int    $emLen
1547
     *
1548
     * @return string
1549
     */
1550
    private function _emsa_pkcs1_v1_5_encode($m, $emLen)
1551
    {
1552
        $h = $this->hash->hash($m);
1553
        if ($h === false) {
1554
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by Jose\Util\RSA::_emsa_pkcs1_v1_5_encode of type string.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
1555
        }
1556
1557
        // see http://tools.ietf.org/html/rfc3447#page-43
1558
        switch ($this->hashName) {
1559
            case 'md2':
1560
                $t = pack('H*', '3020300c06082a864886f70d020205000410');
1561
                break;
1562
            case 'md5':
1563
                $t = pack('H*', '3020300c06082a864886f70d020505000410');
1564
                break;
1565
            case 'sha1':
1566
                $t = pack('H*', '3021300906052b0e03021a05000414');
1567
                break;
1568
            case 'sha256':
1569
                $t = pack('H*', '3031300d060960864801650304020105000420');
1570
                break;
1571
            case 'sha384':
1572
                $t = pack('H*', '3041300d060960864801650304020205000430');
1573
                break;
1574
            case 'sha512':
1575
                $t = pack('H*', '3051300d060960864801650304020305000440');
1576
        }
1577
        $t .= $h;
0 ignored issues
show
Bug introduced by
The variable $t does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1578
        $tLen = strlen($t);
1579
1580
        if ($emLen < $tLen + 11) {
1581
            user_error('Intended encoded message length too short');
1582
1583
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by Jose\Util\RSA::_emsa_pkcs1_v1_5_encode of type string.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
1584
        }
1585
1586
        $ps = str_repeat(chr(0xFF), $emLen - $tLen - 3);
1587
1588
        $em = "\0\1$ps\0$t";
1589
1590
        return $em;
1591
    }
1592
1593
    /**
1594
     * RSASSA-PKCS1-V1_5-SIGN.
1595
     *
1596
     * @param string $m
1597
     *
1598
     * @return string
1599
     */
1600
    private function _rsassa_pkcs1_v1_5_sign($m)
1601
    {
1602
        // EMSA-PKCS1-v1_5 encoding
1603
1604
        $em = $this->_emsa_pkcs1_v1_5_encode($m, $this->k);
0 ignored issues
show
Documentation introduced by
$this->k is of type object<Jose\Util\BigInteger>, but the function expects a integer.

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...
1605
        if ($em === false) {
1606
            user_error('RSA modulus too short');
1607
1608
            return false;
1609
        }
1610
1611
        // RSA signature
1612
1613
        $m = $this->_os2ip($em);
1614
        $s = $this->_rsasp1($m);
1615
        $s = $this->_i2osp($s, $this->k);
0 ignored issues
show
Documentation introduced by
$this->k is of type object<Jose\Util\BigInteger>, but the function expects a integer.

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...
1616
1617
        // Output the signature S
1618
1619
        return $s;
1620
    }
1621
1622
    /**
1623
     * RSASSA-PKCS1-V1_5-VERIFY.
1624
     *
1625
     * @param string $m
1626
     *
1627
     * @return string
1628
     */
1629
    private function _rsassa_pkcs1_v1_5_verify($m, $s)
1630
    {
1631
        // Length checking
1632
1633
        if (strlen($s) != $this->k) {
1634
            user_error('Invalid signature');
1635
1636
            return false;
1637
        }
1638
1639
        // RSA verification
1640
1641
        $s = $this->_os2ip($s);
1642
        $m2 = $this->_rsavp1($s);
1643
        if ($m2 === false) {
1644
            user_error('Invalid signature');
1645
1646
            return false;
1647
        }
1648
        $em = $this->_i2osp($m2, $this->k);
0 ignored issues
show
Documentation introduced by
$this->k is of type object<Jose\Util\BigInteger>, but the function expects a integer.

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...
1649
        if ($em === false) {
1650
            user_error('Invalid signature');
1651
1652
            return false;
1653
        }
1654
1655
        // EMSA-PKCS1-v1_5 encoding
1656
1657
        $em2 = $this->_emsa_pkcs1_v1_5_encode($m, $this->k);
0 ignored issues
show
Documentation introduced by
$this->k is of type object<Jose\Util\BigInteger>, but the function expects a integer.

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...
1658
        if ($em2 === false) {
1659
            user_error('RSA modulus too short');
1660
1661
            return false;
1662
        }
1663
1664
        // Compare
1665
        return $this->_equals($em, $em2);
1666
    }
1667
1668
    /**
1669
     * Set Encryption Mode.
1670
     *
1671
     * Valid values include self::ENCRYPTION_OAEP and self::ENCRYPTION_PKCS1.
1672
     *
1673
     * @param int $mode
1674
     */
1675
    public function setEncryptionMode($mode)
1676
    {
1677
        $this->encryptionMode = $mode;
1678
    }
1679
1680
    /**
1681
     * Set Signature Mode.
1682
     *
1683
     * Valid values include self::SIGNATURE_PSS and self::SIGNATURE_PKCS1
1684
     *
1685
     * @param int $mode
1686
     */
1687
    public function setSignatureMode($mode)
1688
    {
1689
        $this->signatureMode = $mode;
1690
    }
1691
1692
    /**
1693
     * Encryption.
1694
     *
1695
     * Both self::ENCRYPTION_OAEP and self::ENCRYPTION_PKCS1 both place limits on how long $plaintext can be.
1696
     * If $plaintext exceeds those limits it will be broken up so that it does and the resultant ciphertext's will
1697
     * be concatenated together.
1698
     *
1699
     * @see self::decrypt()
1700
     *
1701
     * @param string $plaintext
1702
     *
1703
     * @return string
1704
     */
1705
    public function encrypt($plaintext)
1706
    {
1707
        switch ($this->encryptionMode) {
1708
            case self::ENCRYPTION_PKCS1:
1709
                $length = $this->k - 11;
1710
                if ($length <= 0) {
1711
                    return false;
1712
                }
1713
1714
                $plaintext = str_split($plaintext, $length);
1715
                $ciphertext = '';
1716
                foreach ($plaintext as $m) {
1717
                    $ciphertext .= $this->_rsaes_pkcs1_v1_5_encrypt($m);
1718
                }
1719
1720
                return $ciphertext;
1721
            case self::ENCRYPTION_OAEP:
1722
            default:
1723
                $length = $this->k - 2 * $this->hLen - 2;
1724
                if ($length <= 0) {
1725
                    return false;
1726
                }
1727
1728
                $plaintext = str_split($plaintext, $length);
1729
                $ciphertext = '';
1730
                foreach ($plaintext as $m) {
1731
                    $ciphertext .= $this->_rsaes_oaep_encrypt($m);
1732
                }
1733
1734
                return $ciphertext;
1735
        }
1736
    }
1737
1738
    /**
1739
     * Decryption.
1740
     *
1741
     * @param string $ciphertext
1742
     *
1743
     * @return string
1744
     */
1745
    public function decrypt($ciphertext)
1746
    {
1747
        if ($this->k <= 0) {
1748
            return false;
1749
        }
1750
1751
        $ciphertext = str_split($ciphertext, $this->k);
1752
        $ciphertext[count($ciphertext) - 1] = str_pad($ciphertext[count($ciphertext) - 1], $this->k, chr(0), STR_PAD_LEFT);
1753
1754
        $plaintext = '';
1755
1756
        switch ($this->encryptionMode) {
1757
            case self::ENCRYPTION_PKCS1:
1758
                $decrypt = '_rsaes_pkcs1_v1_5_decrypt';
1759
                break;
1760
            case self::ENCRYPTION_OAEP:
1761
            default:
1762
                $decrypt = '_rsaes_oaep_decrypt';
1763
        }
1764
1765
        foreach ($ciphertext as $c) {
1766
            $temp = $this->$decrypt($c);
1767
            if ($temp === false) {
1768
                return false;
1769
            }
1770
            $plaintext .= $temp;
1771
        }
1772
1773
        return $plaintext;
1774
    }
1775
1776
    /**
1777
     * Create a signature.
1778
     *
1779
     * @param string $message
1780
     *
1781
     * @return string
1782
     */
1783
    public function sign($message)
1784
    {
1785
        if (empty($this->modulus) || empty($this->exponent)) {
1786
            return false;
1787
        }
1788
1789
        switch ($this->signatureMode) {
1790
            case self::SIGNATURE_PKCS1:
1791
                return $this->_rsassa_pkcs1_v1_5_sign($message);
1792
            case self::SIGNATURE_PSS:
1793
            default:
1794
                return $this->_rsassa_pss_sign($message);
1795
        }
1796
    }
1797
1798
    /**
1799
     * Verifies a signature.
1800
     *
1801
     * @param string $message
1802
     * @param string $signature
1803
     *
1804
     * @return bool
1805
     */
1806
    public function verify($message, $signature)
1807
    {
1808
        if (empty($this->modulus) || empty($this->exponent)) {
1809
            return false;
1810
        }
1811
1812
        switch ($this->signatureMode) {
1813
            case self::SIGNATURE_PKCS1:
1814
                return $this->_rsassa_pkcs1_v1_5_verify($message, $signature);
1815
            case self::SIGNATURE_PSS:
1816
            default:
1817
                return $this->_rsassa_pss_verify($message, $signature);
1818
        }
1819
    }
1820
1821
    /**
1822
     * Extract raw BER from Base64 encoding.
1823
     *
1824
     * @param string $str
1825
     *
1826
     * @return string
1827
     */
1828
    private function _extractBER($str)
1829
    {
1830
        $temp = preg_replace('#.*?^-+[^-]+-+[\r\n ]*$#ms', '', $str, 1);
1831
        // remove the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- stuff
1832
        $temp = preg_replace('#-+[^-]+-+#', '', $temp);
1833
        // remove new lines
1834
        $temp = str_replace(["\r", "\n", ' '], '', $temp);
1835
        $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false;
1836
1837
        return $temp != false ? $temp : $str;
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $temp of type string|false against false; this is ambiguous if the string can be empty. Consider using a strict comparison !== instead.
Loading history...
1838
    }
1839
1840
    /**
1841
     * Defines the public key.
1842
     *
1843
     * @param string $key  optional
1844
     * @param int    $type optional
1845
     *
1846
     * @return bool
1847
     */
1848
    private function setPublicKey($key = false, $type = false)
1849
    {
1850
        // if a public key has already been loaded return false
1851
        if (!empty($this->publicExponent)) {
1852
            return false;
1853
        }
1854
1855
        if ($key === false && !empty($this->modulus)) {
1856
            $this->publicExponent = $this->exponent;
1857
1858
            return true;
1859
        }
1860
1861
        if ($type === false) {
1862
            $types = [
1863
                self::PUBLIC_FORMAT_RAW,
1864
                self::PUBLIC_FORMAT_PKCS1,
1865
                self::PUBLIC_FORMAT_XML,
1866
                self::PUBLIC_FORMAT_OPENSSH,
1867
            ];
1868
            foreach ($types as $type) {
1869
                $components = $this->_parseKey($key, $type);
0 ignored issues
show
Bug introduced by
It seems like $key defined by parameter $key on line 1848 can also be of type false; however, Jose\Util\RSA::_parseKey() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
1870
                if ($components !== false) {
1871
                    break;
1872
                }
1873
            }
1874
        } else {
1875
            $components = $this->_parseKey($key, $type);
0 ignored issues
show
Bug introduced by
It seems like $key defined by parameter $key on line 1848 can also be of type false; however, Jose\Util\RSA::_parseKey() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
1876
        }
1877
1878
        if ($components === false) {
0 ignored issues
show
Bug introduced by
The variable $components does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1879
            return false;
1880
        }
1881
1882
        if (empty($this->modulus) || !$this->modulus->equals($components['modulus'])) {
0 ignored issues
show
Bug introduced by
The method equals() does not seem to exist on object<Jose\Util\BigInteger>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
1883
            $this->modulus = $components['modulus'];
1884
            $this->exponent = $this->publicExponent = $components['publicExponent'];
1885
1886
            return true;
1887
        }
1888
1889
        $this->publicExponent = $components['publicExponent'];
1890
1891
        return true;
1892
    }
1893
}
1894