Failed Conditions
Push — PHPSecLib_Rid ( 1a2f9f...838957 )
by Florent
05:04
created

RSA::_stop_element_handler()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 1 Features 0
Metric Value
c 3
b 1
f 0
dl 0
loc 7
rs 9.4285
cc 2
eloc 4
nc 2
nop 0
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
/**
15
 * Pure-PHP PKCS#1 compliant implementation of RSA.
16
 *
17
 * @author  Jim Wigginton <[email protected]>
18
 */
19
final class RSA
20
{
21
    /**#@+
22
     * @see self::encrypt()
23
     * @see self::decrypt()
24
     */
25
    /**
26
     * Use {@link http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding Optimal Asymmetric Encryption Padding}
27
     * (OAEP) for encryption / decryption.
28
     *
29
     * Uses sha1 by default.
30
     *
31
     * @see self::setHash()
32
     * @see self::setMGFHash()
33
     */
34
    const ENCRYPTION_OAEP = 1;
35
36
    /**
37
     * Use PKCS#1 padding.
38
     *
39
     * Although self::ENCRYPTION_OAEP offers more security, including PKCS#1 padding is necessary for purposes of backwards
40
     * compatibility with protocols (like SSH-1) written before OAEP's introduction.
41
     */
42
    const ENCRYPTION_PKCS1 = 2;
43
44
    /**#@-*/
45
46
    /**#@+
47
     * @see self::sign()
48
     * @see self::verify()
49
     * @see self::setHash()
50
     */
51
    /**
52
     * Use the Probabilistic Signature Scheme for signing.
53
     *
54
     * Uses sha1 by default.
55
     *
56
     * @see self::setSaltLength()
57
     * @see self::setMGFHash()
58
     */
59
    const SIGNATURE_PSS = 1;
60
    /**
61
     * Use the PKCS#1 scheme by default.
62
     *
63
     * Although self::SIGNATURE_PSS offers more security, including PKCS#1 signing is necessary for purposes of backwards
64
     * compatibility with protocols (like SSH-2) written before PSS's introduction.
65
     */
66
    const SIGNATURE_PKCS1 = 2;
67
    /**#@-*/
68
69
    /**#@+
70
     * @see \phpseclib\Crypt\RSA::createKey()
71
     */
72
    /**
73
     * ASN1 Integer.
74
     */
75
    const ASN1_INTEGER = 2;
76
    /**
77
     * ASN1 Bit String.
78
     */
79
    const ASN1_BITSTRING = 3;
80
    /**
81
     * ASN1 Octet String.
82
     */
83
    const ASN1_OCTETSTRING = 4;
84
    /**
85
     * ASN1 Object Identifier.
86
     */
87
    const ASN1_OBJECT = 6;
88
    /**
89
     * ASN1 Sequence (with the constucted bit set).
90
     */
91
    const ASN1_SEQUENCE = 48;
92
    /**#@-*/
93
94
    /**#@+
95
     * @see \phpseclib\Crypt\RSA::__construct()
96
     */
97
    /**
98
     * To use the pure-PHP implementation.
99
     */
100
    const MODE_INTERNAL = 1;
101
    /**
102
     * To use the OpenSSL library.
103
     *
104
     * (if enabled; otherwise, the internal implementation will be used)
105
     */
106
    const MODE_OPENSSL = 2;
107
    /**#@-*/
108
109
    /**#@+
110
     * @see \phpseclib\Crypt\RSA::createKey()
111
     * @see \phpseclib\Crypt\RSA::setPrivateKeyFormat()
112
     */
113
    /**
114
     * PKCS#1 formatted private key.
115
     *
116
     * Used by OpenSSH
117
     */
118
    const PRIVATE_FORMAT_PKCS1 = 0;
119
    /**
120
     * PuTTY formatted private key.
121
     */
122
    const PRIVATE_FORMAT_PUTTY = 1;
123
    /**
124
     * XML formatted private key.
125
     */
126
    const PRIVATE_FORMAT_XML = 2;
127
    /**
128
     * PKCS#8 formatted private key.
129
     */
130
    const PRIVATE_FORMAT_PKCS8 = 8;
131
    /**#@-*/
132
133
    /**#@+
134
     * @see \phpseclib\Crypt\RSA::createKey()
135
     * @see \phpseclib\Crypt\RSA::setPublicKeyFormat()
136
     */
137
    /**
138
     * Raw public key.
139
     *
140
     * An array containing two \Jose\Util\BigInteger objects.
141
     *
142
     * The exponent can be indexed with any of the following:
143
     *
144
     * 0, e, exponent, publicExponent
145
     *
146
     * The modulus can be indexed with any of the following:
147
     *
148
     * 1, n, modulo, modulus
149
     */
150
    const PUBLIC_FORMAT_RAW = 3;
151
    /**
152
     * PKCS#1 formatted public key (raw).
153
     *
154
     * Used by File/X509.php
155
     *
156
     * Has the following header:
157
     *
158
     * -----BEGIN RSA PUBLIC KEY-----
159
     *
160
     * Analogous to ssh-keygen's pem format (as specified by -m)
161
     */
162
    const PUBLIC_FORMAT_PKCS1 = 4;
163
    const PUBLIC_FORMAT_PKCS1_RAW = 4;
164
    /**
165
     * XML formatted public key.
166
     */
167
    const PUBLIC_FORMAT_XML = 5;
168
    /**
169
     * OpenSSH formatted public key.
170
     *
171
     * Place in $HOME/.ssh/authorized_keys
172
     */
173
    const PUBLIC_FORMAT_OPENSSH = 6;
174
    /**
175
     * PKCS#1 formatted public key (encapsulated).
176
     *
177
     * Used by PHP's openssl_public_encrypt() and openssl's rsautl (when -pubin is set)
178
     *
179
     * Has the following header:
180
     *
181
     * -----BEGIN PUBLIC KEY-----
182
     *
183
     * Analogous to ssh-keygen's pkcs8 format (as specified by -m). Although PKCS8
184
     * is specific to private keys it's basically creating a DER-encoded wrapper
185
     * for keys. This just extends that same concept to public keys (much like ssh-keygen)
186
     */
187
    const PUBLIC_FORMAT_PKCS8 = 7;
188
    /**#@-*/
189
190
    /**
191
     * Precomputed Zero.
192
     *
193
     * @var array
194
     */
195
    private $zero;
196
197
    /**
198
     * Precomputed One.
199
     *
200
     * @var array
201
     */
202
    private $one;
203
204
    /**
205
     * Private Key Format.
206
     *
207
     * @var int
208
     */
209
    private $privateKeyFormat = self::PRIVATE_FORMAT_PKCS1;
210
211
    /**
212
     * Public Key Format.
213
     *
214
     * @var int
215
     */
216
    private $publicKeyFormat = self::PUBLIC_FORMAT_PKCS8;
217
218
    /**
219
     * Modulus (ie. n).
220
     *
221
     * @var \Jose\Util\BigInteger
222
     */
223
    private $modulus;
224
225
    /**
226
     * Modulus length.
227
     *
228
     * @var \Jose\Util\BigInteger
229
     */
230
    private $k;
231
232
    /**
233
     * Exponent (ie. e or d).
234
     *
235
     * @var \Jose\Util\BigInteger
236
     */
237
    private $exponent;
238
239
    /**
240
     * Primes for Chinese Remainder Theorem (ie. p and q).
241
     *
242
     * @var array
243
     */
244
    private $primes;
245
246
    /**
247
     * Exponents for Chinese Remainder Theorem (ie. dP and dQ).
248
     *
249
     * @var array
250
     */
251
    private $exponents;
252
253
    /**
254
     * Coefficients for Chinese Remainder Theorem (ie. qInv).
255
     *
256
     * @var array
257
     */
258
    private $coefficients;
259
260
    /**
261
     * Hash name.
262
     *
263
     * @var string
264
     */
265
    private $hashName;
266
267
    /**
268
     * Hash function.
269
     *
270
     * @var \Jose\Util\Hash
271
     */
272
    private $hash;
273
274
    /**
275
     * Length of hash function output.
276
     *
277
     * @var int
278
     */
279
    private $hLen;
280
281
    /**
282
     * Length of salt.
283
     *
284
     * @var int
285
     */
286
    private $sLen;
287
288
    /**
289
     * Hash function for the Mask Generation Function.
290
     *
291
     * @var \Jose\Util\Hash
292
     */
293
    private $mgfHash;
294
295
    /**
296
     * Length of MGF hash function output.
297
     *
298
     * @var int
299
     */
300
    private $mgfHLen;
301
302
    /**
303
     * Encryption mode.
304
     *
305
     * @var int
306
     */
307
    private $encryptionMode = self::ENCRYPTION_OAEP;
308
309
    /**
310
     * Signature mode.
311
     *
312
     * @var int
313
     */
314
    private $signatureMode = self::SIGNATURE_PSS;
315
316
    /**
317
     * Public Exponent.
318
     *
319
     * @var mixed
320
     */
321
    private $publicExponent = false;
322
323
    /**
324
     * Password.
325
     *
326
     * @var string
327
     */
328
    private $password = false;
329
330
    /**
331
     * Components.
332
     *
333
     * For use with parsing XML formatted keys.  PHP's XML Parser functions use utilized - instead of PHP's DOM functions -
334
     * because PHP's XML Parser functions work on PHP4 whereas PHP's DOM functions - although surperior - don't.
335
     *
336
     * @see self::_start_element_handler()
337
     *
338
     * @var array
339
     */
340
    private $components = [];
341
342
    /**
343
     * Current String.
344
     *
345
     * For use with parsing XML formatted keys.
346
     *
347
     * @see self::_character_handler()
348
     * @see self::_stop_element_handler()
349
     *
350
     * @var mixed
351
     */
352
    private $current;
353
354
    /**
355
     * OpenSSL configuration file name.
356
     *
357
     * Set to null to use system configuration file.
358
     *
359
     * @see self::createKey()
360
     *
361
     * @var mixed
362
     * @Access public
363
     */
364
    private $configFile;
365
366
    /**
367
     * Public key comment field.
368
     *
369
     * @var string
370
     */
371
    private $comment = 'phpseclib-generated-key';
372
373
    /**
374
     * The constructor.
375
     *
376
     * If you want to make use of the openssl extension, you'll need to set the mode manually, yourself.  The reason
377
     * \phpseclib\Crypt\RSA doesn't do it is because OpenSSL doesn't fail gracefully.  openssl_pkey_new(), in particular, requires
378
     * openssl.cnf be present somewhere and, unfortunately, the only real way to find out is too late.
379
     */
380
    public function __construct()
381
    {
382
        $this->configFile = dirname(__FILE__).'/../openssl.cnf';
383
384
        if (!defined('CRYPT_RSA_MODE')) {
385
            switch (true) {
386
                // Math/BigInteger's openssl requirements are a little less stringent than Crypt/RSA's. in particular,
387
                // Math/BigInteger doesn't require an openssl.cfg file whereas Crypt/RSA does. so if Math/BigInteger
388
                // can't use OpenSSL it can be pretty trivially assumed, then, that Crypt/RSA can't either.
389
                case defined('MATH_BIGINTEGER_OPENSSL_DISABLE'):
390
                    define('CRYPT_RSA_MODE', self::MODE_INTERNAL);
391
                    break;
392
                case extension_loaded('openssl') && file_exists($this->configFile):
393
                    // some versions of XAMPP have mismatched versions of OpenSSL which causes it not to work
394
                    ob_start();
395
                    @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...
396
                    $content = ob_get_contents();
397
                    ob_end_clean();
398
399
                    preg_match_all('#OpenSSL (Header|Library) Version(.*)#im', $content, $matches);
400
401
                    $versions = [];
402
                    if (!empty($matches[1])) {
403
                        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...
404
                            $fullVersion = trim(str_replace('=>', '', strip_tags($matches[2][$i])));
405
406
                            // Remove letter part in OpenSSL version
407
                            if (!preg_match('/(\d+\.\d+\.\d+)/i', $fullVersion, $m)) {
408
                                $versions[$matches[1][$i]] = $fullVersion;
409
                            } else {
410
                                $versions[$matches[1][$i]] = $m[0];
411
                            }
412
                        }
413
                    }
414
415
                    // it doesn't appear that OpenSSL versions were reported upon until PHP 5.3+
416
                    switch (true) {
417
                        case !isset($versions['Header']):
418
                        case !isset($versions['Library']):
419
                        case $versions['Header'] == $versions['Library']:
420
                            define('CRYPT_RSA_MODE', self::MODE_OPENSSL);
421
                            break;
422
                        default:
423
                            define('CRYPT_RSA_MODE', self::MODE_INTERNAL);
424
                            define('MATH_BIGINTEGER_OPENSSL_DISABLE', true);
425
                    }
426
                    break;
427
                default:
428
                    define('CRYPT_RSA_MODE', self::MODE_INTERNAL);
429
            }
430
        }
431
432
        $this->zero = BigInteger::createFromDecimalString('0');
0 ignored issues
show
Documentation Bug introduced by
It seems like \Jose\Util\BigInteger::c...eFromDecimalString('0') of type object<Jose\Util\BigInteger> is incompatible with the declared type array of property $zero.

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...
433
        $this->one = BigInteger::createFromDecimalString('1');
0 ignored issues
show
Documentation Bug introduced by
It seems like \Jose\Util\BigInteger::c...eFromDecimalString('1') of type object<Jose\Util\BigInteger> is incompatible with the declared type array of property $one.

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...
434
435
        $this->hash = new Hash('sha1');
436
        $this->hLen = 20;
437
        $this->hashName = 'sha1';
438
        $this->mgfHash = new Hash('sha1');
439
        $this->mgfHLen = 20;
440
    }
441
442
    /**
443
     * Break a public or private key down into its constituant components.
444
     *
445
     * @see self::_convertPublicKey()
446
     * @see self::_convertPrivateKey()
447
     *
448
     * @param string $key
449
     * @param int    $type
450
     *
451
     * @return array
452
     */
453
    private function _parseKey($key, $type)
454
    {
455
        if ($type != self::PUBLIC_FORMAT_RAW && !is_string($key)) {
456
            return false;
457
        }
458
459
        switch ($type) {
460
            case self::PUBLIC_FORMAT_RAW:
461
                if (!is_array($key)) {
462
                    return false;
463
                }
464
                $components = [];
465
                switch (true) {
466
                    case isset($key['e']):
467
                        $components['publicExponent'] = $key['e']->copy();
468
                        break;
469
                    case isset($key['exponent']):
470
                        $components['publicExponent'] = $key['exponent']->copy();
471
                        break;
472
                    case isset($key['publicExponent']):
473
                        $components['publicExponent'] = $key['publicExponent']->copy();
474
                        break;
475
                    case isset($key[0]):
476
                        $components['publicExponent'] = $key[0]->copy();
477
                }
478
                switch (true) {
479
                    case isset($key['n']):
480
                        $components['modulus'] = $key['n']->copy();
481
                        break;
482
                    case isset($key['modulo']):
483
                        $components['modulus'] = $key['modulo']->copy();
484
                        break;
485
                    case isset($key['modulus']):
486
                        $components['modulus'] = $key['modulus']->copy();
487
                        break;
488
                    case isset($key[1]):
489
                        $components['modulus'] = $key[1]->copy();
490
                }
491
492
                return isset($components['modulus']) && isset($components['publicExponent']) ? $components : false;
493
            case self::PRIVATE_FORMAT_PKCS1:
494
            case self::PRIVATE_FORMAT_PKCS8:
495
            case self::PUBLIC_FORMAT_PKCS1:
496
                /* Although PKCS#1 proposes a format that public and private keys can use, encrypting them is
497
                   "outside the scope" of PKCS#1.  PKCS#1 then refers you to PKCS#12 and PKCS#15 if you're wanting to
498
                   protect private keys, however, that's not what OpenSSL* does.  OpenSSL protects private keys by adding
499
                   two new "fields" to the key - DEK-Info and Proc-Type.  These fields are discussed here:
500
501
                   http://tools.ietf.org/html/rfc1421#section-4.6.1.1
502
                   http://tools.ietf.org/html/rfc1421#section-4.6.1.3
503
504
                   DES-EDE3-CBC as an algorithm, however, is not discussed anywhere, near as I can tell.
505
                   DES-CBC and DES-EDE are discussed in RFC1423, however, DES-EDE3-CBC isn't, nor is its key derivation
506
                   function.  As is, the definitive authority on this encoding scheme isn't the IETF but rather OpenSSL's
507
                   own implementation.  ie. the implementation *is* the standard and any bugs that may exist in that
508
                   implementation are part of the standard, as well.
509
510
                   * OpenSSL is the de facto standard.  It's utilized by OpenSSH and other projects */
511
                if (preg_match('#DEK-Info: (.+),(.+)#', $key, $matches)) {
512
                    $iv = pack('H*', trim($matches[2]));
513
                    $symkey = pack('H*', md5($this->password.substr($iv, 0, 8))); // symkey is short for symmetric key
514
                    $symkey .= pack('H*', md5($symkey.$this->password.substr($iv, 0, 8)));
515
                    // remove the Proc-Type / DEK-Info sections as they're no longer needed
516
                    $key = preg_replace('#^(?:Proc-Type|DEK-Info): .*#m', '', $key);
517
                    $ciphertext = $this->_extractBER($key);
518
                    if ($ciphertext === false) {
519
                        $ciphertext = $key;
520
                    }
521
                    switch ($matches[1]) {
522
                        case 'AES-256-CBC':
523
                            $crypto = new AES();
524
                            break;
525
                        case 'AES-128-CBC':
526
                            $symkey = substr($symkey, 0, 16);
527
                            $crypto = new AES();
528
                            break;
529
                        case 'DES-EDE3-CFB':
530
                            $crypto = new TripleDES(Base::MODE_CFB);
531
                            break;
532
                        case 'DES-EDE3-CBC':
533
                            $symkey = substr($symkey, 0, 24);
534
                            $crypto = new TripleDES();
535
                            break;
536
                        case 'DES-CBC':
537
                            $crypto = new DES();
538
                            break;
539
                        default:
540
                            return false;
541
                    }
542
                    $crypto->setKey($symkey);
543
                    $crypto->setIV($iv);
544
                    $decoded = $crypto->decrypt($ciphertext);
545
                } else {
546
                    $decoded = $this->_extractBER($key);
547
                }
548
549
                if ($decoded !== false) {
550
                    $key = $decoded;
551
                }
552
553
                $components = [];
554
555
                if (ord($this->_string_shift($key)) != self::ASN1_SEQUENCE) {
556
                    return false;
557
                }
558
                if ($this->_decodeLength($key) != strlen($key)) {
559
                    return false;
560
                }
561
562
                $tag = ord($this->_string_shift($key));
563
                /* intended for keys for which OpenSSL's asn1parse returns the following:
564
565
                    0:d=0  hl=4 l= 631 cons: SEQUENCE
566
                    4:d=1  hl=2 l=   1 prim:  INTEGER           :00
567
                    7:d=1  hl=2 l=  13 cons:  SEQUENCE
568
                    9:d=2  hl=2 l=   9 prim:   OBJECT            :rsaEncryption
569
                   20:d=2  hl=2 l=   0 prim:   NULL
570
                   22:d=1  hl=4 l= 609 prim:  OCTET STRING
571
572
                   ie. PKCS8 keys*/
573
574
                if ($tag == self::ASN1_INTEGER && substr($key, 0, 3) == "\x01\x00\x30") {
575
                    $this->_string_shift($key, 3);
576
                    $tag = self::ASN1_SEQUENCE;
577
                }
578
579
                if ($tag == self::ASN1_SEQUENCE) {
580
                    $temp = $this->_string_shift($key, $this->_decodeLength($key));
581
                    if (ord($this->_string_shift($temp)) != self::ASN1_OBJECT) {
582
                        return false;
583
                    }
584
                    $length = $this->_decodeLength($temp);
585
                    switch ($this->_string_shift($temp, $length)) {
586
                        case "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01": // rsaEncryption
587
                            break;
588
                        case "\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03": // pbeWithMD5AndDES-CBC
589
                            /*
590
                               PBEParameter ::= SEQUENCE {
591
                                   salt OCTET STRING (SIZE(8)),
592
                                   iterationCount INTEGER }
593
                            */
594
                            if (ord($this->_string_shift($temp)) != self::ASN1_SEQUENCE) {
595
                                return false;
596
                            }
597
                            if ($this->_decodeLength($temp) != strlen($temp)) {
598
                                return false;
599
                            }
600
                            $this->_string_shift($temp); // assume it's an octet string
601
                            $salt = $this->_string_shift($temp, $this->_decodeLength($temp));
602
                            if (ord($this->_string_shift($temp)) != self::ASN1_INTEGER) {
603
                                return false;
604
                            }
605
                            $this->_decodeLength($temp);
606
                            list(, $iterationCount) = unpack('N', str_pad($temp, 4, chr(0), STR_PAD_LEFT));
607
                            $this->_string_shift($key); // assume it's an octet string
608
                            $length = $this->_decodeLength($key);
609
                            if (strlen($key) != $length) {
610
                                return false;
611
                            }
612
613
                            $crypto = new DES();
614
                            $crypto->setPassword($this->password, 'pbkdf1', 'md5', $salt, $iterationCount);
615
                            $key = $crypto->decrypt($key);
616
                            if ($key === false) {
617
                                return false;
618
                            }
619
620
                            return $this->_parseKey($key, self::PRIVATE_FORMAT_PKCS1);
621
                        default:
622
                            return false;
623
                    }
624
                    /* intended for keys for which OpenSSL's asn1parse returns the following:
625
626
                        0:d=0  hl=4 l= 290 cons: SEQUENCE
627
                        4:d=1  hl=2 l=  13 cons:  SEQUENCE
628
                        6:d=2  hl=2 l=   9 prim:   OBJECT            :rsaEncryption
629
                       17:d=2  hl=2 l=   0 prim:   NULL
630
                       19:d=1  hl=4 l= 271 prim:  BIT STRING */
631
                    $tag = ord($this->_string_shift($key)); // skip over the BIT STRING / OCTET STRING tag
632
                    $this->_decodeLength($key); // skip over the BIT STRING / OCTET STRING length
633
                    // "The initial octet shall encode, as an unsigned binary integer wtih bit 1 as the least significant bit, the number of
634
                    //  unused bits in the final subsequent octet. The number shall be in the range zero to seven."
635
                    //  -- http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf (section 8.6.2.2)
636
                    if ($tag == self::ASN1_BITSTRING) {
637
                        $this->_string_shift($key);
638
                    }
639
                    if (ord($this->_string_shift($key)) != self::ASN1_SEQUENCE) {
640
                        return false;
641
                    }
642
                    if ($this->_decodeLength($key) != strlen($key)) {
643
                        return false;
644
                    }
645
                    $tag = ord($this->_string_shift($key));
646
                }
647
                if ($tag != self::ASN1_INTEGER) {
648
                    return false;
649
                }
650
651
                $length = $this->_decodeLength($key);
652
                $temp = $this->_string_shift($key, $length);
653
                if (strlen($temp) != 1 || ord($temp) > 2) {
654
                    $components['modulus'] = BigInteger::createFromBinaryString($temp);
655
                    $this->_string_shift($key); // skip over self::ASN1_INTEGER
656
                    $length = $this->_decodeLength($key);
657
                    $components[$type == self::PUBLIC_FORMAT_PKCS1 ? 'publicExponent' : 'privateExponent'] = BigInteger::createFromBinaryString($this->_string_shift($key, $length));
658
659
                    return $components;
660
                }
661
                if (ord($this->_string_shift($key)) != self::ASN1_INTEGER) {
662
                    return false;
663
                }
664
                $length = $this->_decodeLength($key);
665
                $components['modulus'] = BigInteger::createFromBinaryString($this->_string_shift($key, $length));
666
                $this->_string_shift($key);
667
                $length = $this->_decodeLength($key);
668
                $components['publicExponent'] = BigInteger::createFromBinaryString($this->_string_shift($key, $length));
669
                $this->_string_shift($key);
670
                $length = $this->_decodeLength($key);
671
                $components['privateExponent'] = BigInteger::createFromBinaryString($this->_string_shift($key, $length));
672
                $this->_string_shift($key);
673
                $length = $this->_decodeLength($key);
674
                $components['primes'] = [1 => BigInteger::createFromBinaryString($this->_string_shift($key, $length))];
675
                $this->_string_shift($key);
676
                $length = $this->_decodeLength($key);
677
                $components['primes'][] = BigInteger::createFromBinaryString($this->_string_shift($key, $length));
678
                $this->_string_shift($key);
679
                $length = $this->_decodeLength($key);
680
                $components['exponents'] = [1 => BigInteger::createFromBinaryString($this->_string_shift($key, $length))];
681
                $this->_string_shift($key);
682
                $length = $this->_decodeLength($key);
683
                $components['exponents'][] = BigInteger::createFromBinaryString($this->_string_shift($key, $length));
684
                $this->_string_shift($key);
685
                $length = $this->_decodeLength($key);
686
                $components['coefficients'] = [2 => BigInteger::createFromBinaryString($this->_string_shift($key, $length))];
687
688
                if (!empty($key)) {
689
                    if (ord($this->_string_shift($key)) != self::ASN1_SEQUENCE) {
690
                        return false;
691
                    }
692
                    $this->_decodeLength($key);
693
                    while (!empty($key)) {
694
                        if (ord($this->_string_shift($key)) != self::ASN1_SEQUENCE) {
695
                            return false;
696
                        }
697
                        $this->_decodeLength($key);
698
                        $key = substr($key, 1);
699
                        $length = $this->_decodeLength($key);
700
                        $components['primes'][] = BigInteger::createFromBinaryString($this->_string_shift($key, $length));
701
                        $this->_string_shift($key);
702
                        $length = $this->_decodeLength($key);
703
                        $components['exponents'][] = BigInteger::createFromBinaryString($this->_string_shift($key, $length));
704
                        $this->_string_shift($key);
705
                        $length = $this->_decodeLength($key);
706
                        $components['coefficients'][] = BigInteger::createFromBinaryString($this->_string_shift($key, $length));
707
                    }
708
                }
709
710
                return $components;
711
            case self::PUBLIC_FORMAT_OPENSSH:
712
                $parts = explode(' ', $key, 3);
713
714
                $key = isset($parts[1]) ? base64_decode($parts[1]) : false;
715
                if ($key === false) {
716
                    return false;
717
                }
718
719
                $comment = isset($parts[2]) ? $parts[2] : false;
720
721
                $cleanup = substr($key, 0, 11) == "\0\0\0\7ssh-rsa";
722
723
                if (strlen($key) <= 4) {
724
                    return false;
725
                }
726
                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...
727
                $publicExponent = BigInteger::createFromBinaryString($this->_string_shift($key, $length));
728
                if (strlen($key) <= 4) {
729
                    return false;
730
                }
731
                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...
732
                $modulus = BigInteger::createFromBinaryString($this->_string_shift($key, $length));
733
734
                if ($cleanup && strlen($key)) {
735
                    if (strlen($key) <= 4) {
736
                        return false;
737
                    }
738
                    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...
739
                    $realModulus = BigInteger::createFromBinaryString($this->_string_shift($key, $length));
740
741
                    return strlen($key) ? false : [
742
                        'modulus'        => $realModulus,
743
                        'publicExponent' => $modulus,
744
                        'comment'        => $comment,
745
                    ];
746
                } else {
747
                    return strlen($key) ? false : [
748
                        'modulus'        => $modulus,
749
                        'publicExponent' => $publicExponent,
750
                        'comment'        => $comment,
751
                    ];
752
                }
753
            // http://www.w3.org/TR/xmldsig-core/#sec-RSAKeyValue
754
            // http://en.wikipedia.org/wiki/XML_Signature
755
            case self::PRIVATE_FORMAT_XML:
756
            case self::PUBLIC_FORMAT_XML:
757
                $this->components = [];
758
759
                $xml = xml_parser_create('UTF-8');
760
                xml_set_object($xml, $this);
761
                xml_set_element_handler($xml, '_start_element_handler', '_stop_element_handler');
762
                xml_set_character_data_handler($xml, '_data_handler');
763
                // add <xml></xml> to account for "dangling" tags like <BitStrength>...</BitStrength> that are sometimes added
764
                if (!xml_parse($xml, '<xml>'.$key.'</xml>')) {
765
                    return false;
766
                }
767
768
                return isset($this->components['modulus']) && isset($this->components['publicExponent']) ? $this->components : false;
769
            // from PuTTY's SSHPUBK.C
770
            case self::PRIVATE_FORMAT_PUTTY:
771
                $components = [];
772
                $key = preg_split('#\r\n|\r|\n#', $key);
773
                $type = trim(preg_replace('#PuTTY-User-Key-File-2: (.+)#', '$1', $key[0]));
774
                if ($type != 'ssh-rsa') {
775
                    return false;
776
                }
777
                $encryption = trim(preg_replace('#Encryption: (.+)#', '$1', $key[1]));
778
                $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...
779
780
                $publicLength = trim(preg_replace('#Public-Lines: (\d+)#', '$1', $key[3]));
781
                $public = base64_decode(implode('', array_map('trim', array_slice($key, 4, $publicLength))));
782
                $public = substr($public, 11);
783
                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...
784
                $components['publicExponent'] = BigInteger::createFromBinaryString($this->_string_shift($public, $length));
785
                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...
786
                $components['modulus'] = BigInteger::createFromBinaryString($this->_string_shift($public, $length));
787
788
                $privateLength = trim(preg_replace('#Private-Lines: (\d+)#', '$1', $key[$publicLength + 4]));
789
                $private = base64_decode(implode('', array_map('trim', array_slice($key, $publicLength + 5, $privateLength))));
790
791
                switch ($encryption) {
792
                    case 'aes256-cbc':
793
                        $symkey = '';
794
                        $sequence = 0;
795
                        while (strlen($symkey) < 32) {
796
                            $temp = pack('Na*', $sequence++, $this->password);
797
                            $symkey .= pack('H*', sha1($temp));
798
                        }
799
                        $symkey = substr($symkey, 0, 32);
800
                        $crypto = new AES();
801
                }
802
803
                if ($encryption != 'none') {
804
                    $crypto->setKey($symkey);
805
                    $crypto->disablePadding();
806
                    $private = $crypto->decrypt($private);
807
                    if ($private === false) {
808
                        return false;
809
                    }
810
                }
811
812
                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...
813
                if (strlen($private) < $length) {
814
                    return false;
815
                }
816
                $components['privateExponent'] = BigInteger::createFromBinaryString($this->_string_shift($private, $length), true);
817
                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...
818
                if (strlen($private) < $length) {
819
                    return false;
820
                }
821
                $components['primes'] = [1 => BigInteger::createFromBinaryString($this->_string_shift($private, $length), true)];
822
                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...
823
                if (strlen($private) < $length) {
824
                    return false;
825
                }
826
                $components['primes'][] = BigInteger::createFromBinaryString($this->_string_shift($private, $length), true);
827
828
                $temp = $components['primes'][1]->subtract($this->one);
0 ignored issues
show
Documentation introduced by
$this->one is of type array, but the function expects a object<Jose\Util\BigInteger>.

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...
829
                $components['exponents'] = [1 => $components['publicExponent']->modInverse($temp)];
830
                $temp = $components['primes'][2]->subtract($this->one);
831
                $components['exponents'][] = $components['publicExponent']->modInverse($temp);
832
833
                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...
834
                if (strlen($private) < $length) {
835
                    return false;
836
                }
837
                $components['coefficients'] = [2 => BigInteger::createFromBinaryString($this->_string_shift($private, $length), true)];
838
839
                return $components;
840
        }
841
    }
842
843
    /**
844
     * Start Element Handler.
845
     *
846
     * Called by xml_set_element_handler()
847
     *
848
     * @param resource $parser
849
     * @param string   $name
850
     * @param array    $attribs
851
     */
852
    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...
853
    {
854
        //$name = strtoupper($name);
855
        switch ($name) {
856
            case 'MODULUS':
857
                $this->current = &$this->components['modulus'];
858
                break;
859
            case 'EXPONENT':
860
                $this->current = &$this->components['publicExponent'];
861
                break;
862
            case 'P':
863
                $this->current = &$this->components['primes'][1];
864
                break;
865
            case 'Q':
866
                $this->current = &$this->components['primes'][2];
867
                break;
868
            case 'DP':
869
                $this->current = &$this->components['exponents'][1];
870
                break;
871
            case 'DQ':
872
                $this->current = &$this->components['exponents'][2];
873
                break;
874
            case 'INVERSEQ':
875
                $this->current = &$this->components['coefficients'][2];
876
                break;
877
            case 'D':
878
                $this->current = &$this->components['privateExponent'];
879
        }
880
        $this->current = '';
881
    }
882
883
    /**
884
     * Stop Element Handler.
885
     */
886
    private function _stop_element_handler()
887
    {
888
        if (isset($this->current)) {
889
            $this->current = BigInteger::createFromBinaryString(base64_decode($this->current));
890
            unset($this->current);
891
        }
892
    }
893
894
    /**
895
     * Data Handler.
896
     *
897
     * Called by xml_set_character_data_handler()
898
     *
899
     * @param resource $parser
900
     * @param string   $data
901
     */
902
    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...
903
    {
904
        if (!isset($this->current) || is_object($this->current)) {
905
            return;
906
        }
907
        $this->current .= trim($data);
908
    }
909
910
    /**
911
     * Loads a public or private key.
912
     *
913
     * Returns true on success and false on failure (ie. an incorrect password was provided or the key was malformed)
914
     *
915
     * @param string $key
916
     * @param int    $type optional
917
     */
918
    public function loadKey($key, $type = false)
919
    {
920
        if ($key instanceof self) {
921
            $this->privateKeyFormat = $key->privateKeyFormat;
922
            $this->publicKeyFormat = $key->publicKeyFormat;
923
            $this->k = $key->k;
924
            $this->hLen = $key->hLen;
925
            $this->sLen = $key->sLen;
926
            $this->mgfHLen = $key->mgfHLen;
927
            $this->encryptionMode = $key->encryptionMode;
928
            $this->signatureMode = $key->signatureMode;
929
            $this->password = $key->password;
930
            $this->configFile = $key->configFile;
931
            $this->comment = $key->comment;
932
933
            if (is_object($key->hash)) {
934
                $this->hash = new Hash($key->hash->getHash());
935
            }
936
            if (is_object($key->mgfHash)) {
937
                $this->mgfHash = new Hash($key->mgfHash->getHash());
938
            }
939
940
            if (is_object($key->modulus)) {
941
                $this->modulus = $key->modulus->copy();
942
            }
943
            if (is_object($key->exponent)) {
944
                $this->exponent = $key->exponent->copy();
945
            }
946
            if (is_object($key->publicExponent)) {
947
                $this->publicExponent = $key->publicExponent->copy();
948
            }
949
950
            $this->primes = [];
951
            $this->exponents = [];
952
            $this->coefficients = [];
953
954
            foreach ($this->primes as $prime) {
955
                $this->primes[] = $prime->copy();
956
            }
957
            foreach ($this->exponents as $exponent) {
958
                $this->exponents[] = $exponent->copy();
959
            }
960
            foreach ($this->coefficients as $coefficient) {
961
                $this->coefficients[] = $coefficient->copy();
962
            }
963
964
            return true;
965
        }
966
967
        if ($type === false) {
968
            $types = [
969
                self::PUBLIC_FORMAT_RAW,
970
                self::PRIVATE_FORMAT_PKCS1,
971
                self::PRIVATE_FORMAT_XML,
972
                self::PRIVATE_FORMAT_PUTTY,
973
                self::PUBLIC_FORMAT_OPENSSH,
974
            ];
975
            foreach ($types as $type) {
976
                $components = $this->_parseKey($key, $type);
977
                if ($components !== false) {
978
                    break;
979
                }
980
            }
981
        } else {
982
            $components = $this->_parseKey($key, $type);
983
        }
984
985
        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...
986
            return false;
987
        }
988
989
        if (isset($components['comment']) && $components['comment'] !== false) {
990
            $this->comment = $components['comment'];
991
        }
992
        $this->modulus = $components['modulus'];
993
        $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...
994
        $this->exponent = isset($components['privateExponent']) ? $components['privateExponent'] : $components['publicExponent'];
995
        if (isset($components['primes'])) {
996
            $this->primes = $components['primes'];
997
            $this->exponents = $components['exponents'];
998
            $this->coefficients = $components['coefficients'];
999
            $this->publicExponent = $components['publicExponent'];
1000
        } else {
1001
            $this->primes = [];
1002
            $this->exponents = [];
1003
            $this->coefficients = [];
1004
            $this->publicExponent = false;
1005
        }
1006
1007
        switch ($type) {
1008
            case self::PUBLIC_FORMAT_OPENSSH:
1009
            case self::PUBLIC_FORMAT_RAW:
1010
                $this->setPublicKey();
1011
                break;
1012
            case self::PRIVATE_FORMAT_PKCS1:
1013
                switch (true) {
1014
                    case strpos($key, '-BEGIN PUBLIC KEY-') !== false:
1015
                    case strpos($key, '-BEGIN RSA PUBLIC KEY-') !== false:
1016
                        $this->setPublicKey();
1017
                }
1018
        }
1019
1020
        return true;
1021
    }
1022
1023
    /**
1024
     * DER-decode the length.
1025
     *
1026
     * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4.  See
1027
     * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information.
1028
     *
1029
     * @param string $string
1030
     *
1031
     * @return int
1032
     */
1033
    private function _decodeLength(&$string)
1034
    {
1035
        $length = ord($this->_string_shift($string));
1036
        if ($length & 0x80) { // definite length, long form
1037
            $length &= 0x7F;
1038
            $temp = $this->_string_shift($string, $length);
1039
            list(, $length) = unpack('N', substr(str_pad($temp, 4, chr(0), STR_PAD_LEFT), -4));
1040
        }
1041
1042
        return $length;
1043
    }
1044
1045
    /**
1046
     * String Shift.
1047
     *
1048
     * Inspired by array_shift
1049
     *
1050
     * @param string $string
1051
     * @param int    $index
1052
     *
1053
     * @return string
1054
     */
1055
    private function _string_shift(&$string, $index = 1)
1056
    {
1057
        $substr = substr($string, 0, $index);
1058
        $string = substr($string, $index);
1059
1060
        return $substr;
1061
    }
1062
1063
    /**
1064
     * Determines which hashing function should be used.
1065
     *
1066
     * Used with signature production / verification and (if the encryption mode is self::ENCRYPTION_OAEP) encryption and
1067
     * decryption.  If $hash isn't supported, sha1 is used.
1068
     *
1069
     * @param string $hash
1070
     */
1071
    public function setHash($hash)
1072
    {
1073
        switch ($hash) {
1074
            case 'sha1':
1075
                $this->hLen = 20;
1076
                break;
1077
            case 'sha256':
1078
                $this->hLen = 32;
1079
                break;
1080
            case 'sha384':
1081
                $this->hLen = 48;
1082
                break;
1083
            case 'sha512':
1084
                $this->hLen = 64;
1085
                break;
1086
            default:
1087
                throw new \InvalidArgumentException('Unsupported hash algorithm.');
1088
        }
1089
        $this->hash = new Hash($hash);
1090
    }
1091
1092
    /**
1093
     * Determines which hashing function should be used for the mask generation function.
1094
     *
1095
     * The mask generation function is used by self::ENCRYPTION_OAEP and self::SIGNATURE_PSS and although it's
1096
     * best if Hash and MGFHash are set to the same thing this is not a requirement.
1097
     *
1098
     * @param string $hash
1099
     */
1100
    public function setMGFHash($hash)
1101
    {
1102
        switch ($hash) {
1103
            case 'sha1':
1104
                $this->mgfHLen = 20;
1105
                break;
1106
            case 'sha256':
1107
                $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...
1108
                break;
1109
            case 'sha384':
1110
                $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...
1111
                break;
1112
            case 'sha512':
1113
                $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...
1114
                break;
1115
            default:
1116
                throw new \InvalidArgumentException('Unsupported hash algorithm.');
1117
        }
1118
        $this->mgfHash = new Hash($hash);
1119
    }
1120
1121
    /**
1122
     * Determines the salt length.
1123
     *
1124
     * To quote from {@link http://tools.ietf.org/html/rfc3447#page-38 RFC3447#page-38}:
1125
     *
1126
     *    Typical salt lengths in octets are hLen (the length of the output
1127
     *    of the hash function Hash) and 0.
1128
     *
1129
     * @param int $format
0 ignored issues
show
Bug introduced by
There is no parameter named $format. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
1130
     */
1131
    public function setSaltLength($sLen)
1132
    {
1133
        $this->sLen = $sLen;
1134
    }
1135
1136
    /**
1137
     * Integer-to-Octet-String primitive.
1138
     *
1139
     * See {@link http://tools.ietf.org/html/rfc3447#section-4.1 RFC3447#section-4.1}.
1140
     *
1141
     * @param \Jose\Util\BigInteger $x
1142
     * @param int                   $xLen
1143
     *
1144
     * @return string
1145
     */
1146
    private function _i2osp($x, $xLen)
1147
    {
1148
        $x = $x->toBytes();
1149
        if (strlen($x) > $xLen) {
1150
            user_error('Integer too large');
1151
1152
            return false;
1153
        }
1154
1155
        return str_pad($x, $xLen, chr(0), STR_PAD_LEFT);
1156
    }
1157
1158
    /**
1159
     * Octet-String-to-Integer primitive.
1160
     *
1161
     * See {@link http://tools.ietf.org/html/rfc3447#section-4.2 RFC3447#section-4.2}.
1162
     *
1163
     * @param string $x
1164
     *
1165
     * @return \Jose\Util\BigInteger
1166
     */
1167
    private function _os2ip($x)
1168
    {
1169
        return BigInteger::createFromBinaryString($x);
1170
    }
1171
1172
    /**
1173
     * Exponentiate with or without Chinese Remainder Theorem.
1174
     *
1175
     * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.1 RFC3447#section-5.1.2}.
1176
     *
1177
     * @param \Jose\Util\BigInteger $x
1178
     *
1179
     * @return \Jose\Util\BigInteger
1180
     */
1181
    private function _exponentiate($x)
1182
    {
1183
        if (empty($this->primes) || empty($this->coefficients) || empty($this->exponents)) {
1184
            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 1184 which is incompatible with the return type documented by Jose\Util\RSA::_exponentiate of type Jose\Util\BigInteger.
Loading history...
1185
        }
1186
1187
        $num_primes = count($this->primes);
1188
1189
        if (defined('CRYPT_RSA_DISABLE_BLINDING')) {
1190
            $m_i = [
1191
                1 => $x->modPow($this->exponents[1], $this->primes[1]),
1192
                2 => $x->modPow($this->exponents[2], $this->primes[2]),
1193
            ];
1194
            $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...
1195
            $h = $h->multiply($this->coefficients[2]);
1196
            list(, $h) = $h->divide($this->primes[1]);
1197
            $m = $m_i[2]->add($h->multiply($this->primes[2]));
1198
1199
            $r = $this->primes[1];
1200
            for ($i = 3; $i <= $num_primes; $i++) {
1201
                $m_i = $x->modPow($this->exponents[$i], $this->primes[$i]);
1202
1203
                $r = $r->multiply($this->primes[$i - 1]);
1204
1205
                $h = $m_i->subtract($m);
1206
                $h = $h->multiply($this->coefficients[$i]);
1207
                list(, $h) = $h->divide($this->primes[$i]);
1208
1209
                $m = $m->add($r->multiply($h));
1210
            }
1211
        } else {
1212
            $smallest = $this->primes[1];
1213
            for ($i = 2; $i <= $num_primes; $i++) {
1214
                if ($smallest->compare($this->primes[$i]) > 0) {
1215
                    $smallest = $this->primes[$i];
1216
                }
1217
            }
1218
1219
            $one = BigInteger::createFromDecimalString('1');
1220
1221
            $r = $one->random($one, $smallest->subtract($one));
1222
1223
            $m_i = [
1224
                1 => $this->_blind($x, $r, 1),
1225
                2 => $this->_blind($x, $r, 2),
1226
            ];
1227
            $h = $m_i[1]->subtract($m_i[2]);
1228
            $h = $h->multiply($this->coefficients[2]);
1229
            list(, $h) = $h->divide($this->primes[1]);
1230
            $m = $m_i[2]->add($h->multiply($this->primes[2]));
1231
1232
            $r = $this->primes[1];
1233
            for ($i = 3; $i <= $num_primes; $i++) {
1234
                $m_i = $this->_blind($x, $r, $i);
1235
1236
                $r = $r->multiply($this->primes[$i - 1]);
1237
1238
                $h = $m_i->subtract($m);
1239
                $h = $h->multiply($this->coefficients[$i]);
1240
                list(, $h) = $h->divide($this->primes[$i]);
1241
1242
                $m = $m->add($r->multiply($h));
1243
            }
1244
        }
1245
1246
        return $m;
1247
    }
1248
1249
    /**
1250
     * Performs RSA Blinding.
1251
     *
1252
     * Protects against timing attacks by employing RSA Blinding.
1253
     * Returns $x->modPow($this->exponents[$i], $this->primes[$i])
1254
     *
1255
     * @param \Jose\Util\BigInteger $x
1256
     * @param \Jose\Util\BigInteger $r
1257
     * @param int                   $i
1258
     *
1259
     * @return \Jose\Util\BigInteger
1260
     */
1261
    private function _blind($x, $r, $i)
1262
    {
1263
        $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...
1264
        $x = $x->modPow($this->exponents[$i], $this->primes[$i]);
1265
1266
        $r = $r->modInverse($this->primes[$i]);
1267
        $x = $x->multiply($r);
0 ignored issues
show
Bug introduced by
It seems like $r defined by $r->modInverse($this->primes[$i]) on line 1266 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...
1268
        list(, $x) = $x->divide($this->primes[$i]);
1269
1270
        return $x;
1271
    }
1272
1273
    /**
1274
     * Performs blinded RSA equality testing.
1275
     *
1276
     * Protects against a particular type of timing attack described.
1277
     *
1278
     * See {@link http://codahale.com/a-lesson-in-timing-attacks/ A Lesson In Timing Attacks (or, Don't use MessageDigest.isEquals)}
1279
     *
1280
     * Thanks for the heads up singpolyma!
1281
     *
1282
     * @param string $x
1283
     * @param string $y
1284
     *
1285
     * @return bool
1286
     */
1287
    private function _equals($x, $y)
1288
    {
1289
        if (strlen($x) != strlen($y)) {
1290
            return false;
1291
        }
1292
1293
        $result = 0;
1294
        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...
1295
            $result |= ord($x[$i]) ^ ord($y[$i]);
1296
        }
1297
1298
        return $result == 0;
1299
    }
1300
1301
    /**
1302
     * RSAEP.
1303
     *
1304
     * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.1 RFC3447#section-5.1.1}.
1305
     *
1306
     * @param \Jose\Util\BigInteger $m
1307
     *
1308
     * @return \Jose\Util\BigInteger
1309
     */
1310
    private function _rsaep($m)
1311
    {
1312
        if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) {
0 ignored issues
show
Documentation introduced by
$this->zero is of type array, but the function expects a object<Jose\Util\BigInteger>.

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...
1313
            user_error('Message representative out of range');
1314
1315
            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...
1316
        }
1317
1318
        return $this->_exponentiate($m);
1319
    }
1320
1321
    /**
1322
     * RSADP.
1323
     *
1324
     * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.2 RFC3447#section-5.1.2}.
1325
     *
1326
     * @param \Jose\Util\BigInteger $c
1327
     *
1328
     * @return \Jose\Util\BigInteger
1329
     */
1330
    private function _rsadp($c)
1331
    {
1332
        if ($c->compare($this->zero) < 0 || $c->compare($this->modulus) > 0) {
0 ignored issues
show
Documentation introduced by
$this->zero is of type array, but the function expects a object<Jose\Util\BigInteger>.

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...
1333
            user_error('Ciphertext representative out of range');
1334
1335
            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...
1336
        }
1337
1338
        return $this->_exponentiate($c);
1339
    }
1340
1341
    /**
1342
     * RSASP1.
1343
     *
1344
     * See {@link http://tools.ietf.org/html/rfc3447#section-5.2.1 RFC3447#section-5.2.1}.
1345
     *
1346
     * @param \Jose\Util\BigInteger $m
1347
     *
1348
     * @return \Jose\Util\BigInteger
1349
     */
1350
    private function _rsasp1($m)
1351
    {
1352
        if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) {
0 ignored issues
show
Documentation introduced by
$this->zero is of type array, but the function expects a object<Jose\Util\BigInteger>.

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...
1353
            user_error('Message representative out of range');
1354
1355
            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...
1356
        }
1357
1358
        return $this->_exponentiate($m);
1359
    }
1360
1361
    /**
1362
     * RSAVP1.
1363
     *
1364
     * See {@link http://tools.ietf.org/html/rfc3447#section-5.2.2 RFC3447#section-5.2.2}.
1365
     *
1366
     * @param \Jose\Util\BigInteger $s
1367
     *
1368
     * @return \Jose\Util\BigInteger
1369
     */
1370
    private function _rsavp1($s)
1371
    {
1372
        if ($s->compare($this->zero) < 0 || $s->compare($this->modulus) > 0) {
0 ignored issues
show
Documentation introduced by
$this->zero is of type array, but the function expects a object<Jose\Util\BigInteger>.

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...
1373
            user_error('Signature representative out of range');
1374
1375
            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...
1376
        }
1377
1378
        return $this->_exponentiate($s);
1379
    }
1380
1381
    /**
1382
     * MGF1.
1383
     *
1384
     * See {@link http://tools.ietf.org/html/rfc3447#appendix-B.2.1 RFC3447#appendix-B.2.1}.
1385
     *
1386
     * @param string $mgfSeed
1387
     * @param int    $mgfLen
0 ignored issues
show
Bug introduced by
There is no parameter named $mgfLen. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
1388
     *
1389
     * @return string
1390
     */
1391
    private function _mgf1($mgfSeed, $maskLen)
1392
    {
1393
        // if $maskLen would yield strings larger than 4GB, PKCS#1 suggests a "Mask too long" error be output.
1394
1395
        $t = '';
1396
        $count = ceil($maskLen / $this->mgfHLen);
1397
        for ($i = 0; $i < $count; $i++) {
1398
            $c = pack('N', $i);
1399
            $t .= $this->mgfHash->hash($mgfSeed.$c);
1400
        }
1401
1402
        return substr($t, 0, $maskLen);
1403
    }
1404
1405
    /**
1406
     * RSAES-OAEP-ENCRYPT.
1407
     *
1408
     * See {@link http://tools.ietf.org/html/rfc3447#section-7.1.1 RFC3447#section-7.1.1} and
1409
     * {http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding OAES}.
1410
     *
1411
     * @param string $m
1412
     * @param string $l
1413
     *
1414
     * @return string
1415
     */
1416
    private function _rsaes_oaep_encrypt($m, $l = '')
1417
    {
1418
        $mLen = strlen($m);
1419
1420
        // Length checking
1421
1422
        // if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
1423
        // be output.
1424
1425
        if ($mLen > $this->k - 2 * $this->hLen - 2) {
1426
            user_error('Message too long');
1427
1428
            return false;
1429
        }
1430
1431
        // EME-OAEP encoding
1432
1433
        $lHash = $this->hash->hash($l);
1434
        $ps = str_repeat(chr(0), $this->k - $mLen - 2 * $this->hLen - 2);
1435
        $db = $lHash.$ps.chr(1).$m;
1436
        $seed = random_bytes($this->hLen);
1437
        $dbMask = $this->_mgf1($seed, $this->k - $this->hLen - 1);
1438
        $maskedDB = $db ^ $dbMask;
1439
        $seedMask = $this->_mgf1($maskedDB, $this->hLen);
1440
        $maskedSeed = $seed ^ $seedMask;
1441
        $em = chr(0).$maskedSeed.$maskedDB;
1442
1443
        // RSA encryption
1444
1445
        $m = $this->_os2ip($em);
1446
        $c = $this->_rsaep($m);
1447
        $c = $this->_i2osp($c, $this->k);
1448
1449
        // Output the ciphertext C
1450
1451
        return $c;
1452
    }
1453
1454
    /**
1455
     * RSAES-OAEP-DECRYPT.
1456
     *
1457
     * See {@link http://tools.ietf.org/html/rfc3447#section-7.1.2 RFC3447#section-7.1.2}.  The fact that the error
1458
     * messages aren't distinguishable from one another hinders debugging, but, to quote from RFC3447#section-7.1.2:
1459
     *
1460
     *    Note.  Care must be taken to ensure that an opponent cannot
1461
     *    distinguish the different error conditions in Step 3.g, whether by
1462
     *    error message or timing, or, more generally, learn partial
1463
     *    information about the encoded message EM.  Otherwise an opponent may
1464
     *    be able to obtain useful information about the decryption of the
1465
     *    ciphertext C, leading to a chosen-ciphertext attack such as the one
1466
     *    observed by Manger [36].
1467
     *
1468
     * As for $l...  to quote from {@link http://tools.ietf.org/html/rfc3447#page-17 RFC3447#page-17}:
1469
     *
1470
     *    Both the encryption and the decryption operations of RSAES-OAEP take
1471
     *    the value of a label L as input.  In this version of PKCS #1, L is
1472
     *    the empty string; other uses of the label are outside the scope of
1473
     *    this document.
1474
     *
1475
     * @param string $c
1476
     * @param string $l
1477
     *
1478
     * @return string
1479
     */
1480
    private function _rsaes_oaep_decrypt($c, $l = '')
1481
    {
1482
        // Length checking
1483
1484
        // if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
1485
        // be output.
1486
1487
        if (strlen($c) != $this->k || $this->k < 2 * $this->hLen + 2) {
1488
            user_error('Decryption error');
1489
1490
            return false;
1491
        }
1492
1493
        // RSA decryption
1494
1495
        $c = $this->_os2ip($c);
1496
        $m = $this->_rsadp($c);
1497
        if ($m === false) {
1498
            user_error('Decryption error');
1499
1500
            return false;
1501
        }
1502
        $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...
1503
1504
        // EME-OAEP decoding
1505
1506
        $lHash = $this->hash->hash($l);
1507
        $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...
1508
        $maskedSeed = substr($em, 1, $this->hLen);
1509
        $maskedDB = substr($em, $this->hLen + 1);
1510
        $seedMask = $this->_mgf1($maskedDB, $this->hLen);
1511
        $seed = $maskedSeed ^ $seedMask;
1512
        $dbMask = $this->_mgf1($seed, $this->k - $this->hLen - 1);
1513
        $db = $maskedDB ^ $dbMask;
1514
        $lHash2 = substr($db, 0, $this->hLen);
1515
        $m = substr($db, $this->hLen);
1516
        if ($lHash != $lHash2) {
1517
            user_error('Decryption error');
1518
1519
            return false;
1520
        }
1521
        $m = ltrim($m, chr(0));
1522
        if (ord($m[0]) != 1) {
1523
            user_error('Decryption error');
1524
1525
            return false;
1526
        }
1527
1528
        // Output the message M
1529
1530
        return substr($m, 1);
1531
    }
1532
1533
    /**
1534
     * RSAES-PKCS1-V1_5-ENCRYPT.
1535
     *
1536
     * See {@link http://tools.ietf.org/html/rfc3447#section-7.2.1 RFC3447#section-7.2.1}.
1537
     *
1538
     * @param string $m
1539
     *
1540
     * @return string
1541
     */
1542
    private function _rsaes_pkcs1_v1_5_encrypt($m)
1543
    {
1544
        $mLen = strlen($m);
1545
1546
        // Length checking
1547
1548
        if ($mLen > $this->k - 11) {
1549
            user_error('Message too long');
1550
1551
            return false;
1552
        }
1553
1554
        // EME-PKCS1-v1_5 encoding
1555
1556
        $psLen = $this->k - $mLen - 3;
1557
        $ps = '';
1558
        while (strlen($ps) != $psLen) {
1559
            $temp = random_bytes($psLen - strlen($ps));
1560
            $temp = str_replace("\x00", '', $temp);
1561
            $ps .= $temp;
1562
        }
1563
        $type = 2;
1564
        // see the comments of _rsaes_pkcs1_v1_5_decrypt() to understand why this is being done
1565
        if (defined('CRYPT_RSA_PKCS15_COMPAT') && (!isset($this->publicExponent) || $this->exponent !== $this->publicExponent)) {
1566
            $type = 1;
1567
            // "The padding string PS shall consist of k-3-||D|| octets. ... for block type 01, they shall have value FF"
1568
            $ps = str_repeat("\xFF", $psLen);
1569
        }
1570
        $em = chr(0).chr($type).$ps.chr(0).$m;
1571
1572
        // RSA encryption
1573
        $m = $this->_os2ip($em);
1574
        $c = $this->_rsaep($m);
1575
        $c = $this->_i2osp($c, $this->k);
1576
1577
        // Output the ciphertext C
1578
1579
        return $c;
1580
    }
1581
1582
    /**
1583
     * RSAES-PKCS1-V1_5-DECRYPT.
1584
     *
1585
     * See {@link http://tools.ietf.org/html/rfc3447#section-7.2.2 RFC3447#section-7.2.2}.
1586
     *
1587
     * For compatibility purposes, this function departs slightly from the description given in RFC3447.
1588
     * The reason being that RFC2313#section-8.1 (PKCS#1 v1.5) states that ciphertext's encrypted by the
1589
     * private key should have the second byte set to either 0 or 1 and that ciphertext's encrypted by the
1590
     * public key should have the second byte set to 2.  In RFC3447 (PKCS#1 v2.1), the second byte is supposed
1591
     * to be 2 regardless of which key is used.  For compatibility purposes, we'll just check to make sure the
1592
     * second byte is 2 or less.  If it is, we'll accept the decrypted string as valid.
1593
     *
1594
     * As a consequence of this, a private key encrypted ciphertext produced with \phpseclib\Crypt\RSA may not decrypt
1595
     * with a strictly PKCS#1 v1.5 compliant RSA implementation.  Public key encrypted ciphertext's should but
1596
     * not private key encrypted ciphertext's.
1597
     *
1598
     * @param string $c
1599
     *
1600
     * @return string
1601
     */
1602
    private function _rsaes_pkcs1_v1_5_decrypt($c)
1603
    {
1604
        // Length checking
1605
1606
        if (strlen($c) != $this->k) { // or if k < 11
1607
            user_error('Decryption error');
1608
1609
            return false;
1610
        }
1611
1612
        // RSA decryption
1613
1614
        $c = $this->_os2ip($c);
1615
        $m = $this->_rsadp($c);
1616
1617
        if ($m === false) {
1618
            user_error('Decryption error');
1619
1620
            return false;
1621
        }
1622
        $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...
1623
1624
        // EME-PKCS1-v1_5 decoding
1625
1626
        if (ord($em[0]) != 0 || ord($em[1]) > 2) {
1627
            user_error('Decryption error');
1628
1629
            return false;
1630
        }
1631
1632
        $ps = substr($em, 2, strpos($em, chr(0), 2) - 2);
1633
        $m = substr($em, strlen($ps) + 3);
1634
1635
        if (strlen($ps) < 8) {
1636
            user_error('Decryption error');
1637
1638
            return false;
1639
        }
1640
1641
        // Output M
1642
1643
        return $m;
1644
    }
1645
1646
    /**
1647
     * EMSA-PSS-ENCODE.
1648
     *
1649
     * See {@link http://tools.ietf.org/html/rfc3447#section-9.1.1 RFC3447#section-9.1.1}.
1650
     *
1651
     * @param string $m
1652
     * @param int    $emBits
1653
     */
1654
    private function _emsa_pss_encode($m, $emBits)
1655
    {
1656
        // if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
1657
        // be output.
1658
1659
        $emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8)
1660
        $sLen = $this->sLen ? $this->sLen : $this->hLen;
1661
1662
        $mHash = $this->hash->hash($m);
1663
        if ($emLen < $this->hLen + $sLen + 2) {
1664
            user_error('Encoding error');
1665
1666
            return false;
1667
        }
1668
1669
        $salt = random_bytes($sLen);
1670
        $m2 = "\0\0\0\0\0\0\0\0".$mHash.$salt;
1671
        $h = $this->hash->hash($m2);
1672
        $ps = str_repeat(chr(0), $emLen - $sLen - $this->hLen - 2);
1673
        $db = $ps.chr(1).$salt;
1674
        $dbMask = $this->_mgf1($h, $emLen - $this->hLen - 1);
1675
        $maskedDB = $db ^ $dbMask;
1676
        $maskedDB[0] = ~chr(0xFF << ($emBits & 7)) & $maskedDB[0];
1677
        $em = $maskedDB.$h.chr(0xBC);
1678
1679
        return $em;
1680
    }
1681
1682
    /**
1683
     * EMSA-PSS-VERIFY.
1684
     *
1685
     * See {@link http://tools.ietf.org/html/rfc3447#section-9.1.2 RFC3447#section-9.1.2}.
1686
     *
1687
     * @param string $m
1688
     * @param string $em
1689
     * @param int    $emBits
1690
     *
1691
     * @return string
1692
     */
1693
    private function _emsa_pss_verify($m, $em, $emBits)
1694
    {
1695
        // if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
1696
        // be output.
1697
1698
        $emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8);
1699
        $sLen = $this->sLen ? $this->sLen : $this->hLen;
1700
1701
        $mHash = $this->hash->hash($m);
1702
        if ($emLen < $this->hLen + $sLen + 2) {
1703
            return false;
1704
        }
1705
1706
        if ($em[strlen($em) - 1] != chr(0xBC)) {
1707
            return false;
1708
        }
1709
1710
        $maskedDB = substr($em, 0, -$this->hLen - 1);
1711
        $h = substr($em, -$this->hLen - 1, $this->hLen);
1712
        $temp = chr(0xFF << ($emBits & 7));
1713
        if ((~$maskedDB[0] & $temp) != $temp) {
1714
            return false;
1715
        }
1716
        $dbMask = $this->_mgf1($h, $emLen - $this->hLen - 1);
1717
        $db = $maskedDB ^ $dbMask;
1718
        $db[0] = ~chr(0xFF << ($emBits & 7)) & $db[0];
1719
        $temp = $emLen - $this->hLen - $sLen - 2;
1720
        if (substr($db, 0, $temp) != str_repeat(chr(0), $temp) || ord($db[$temp]) != 1) {
1721
            return false;
1722
        }
1723
        $salt = substr($db, $temp + 1); // should be $sLen long
1724
        $m2 = "\0\0\0\0\0\0\0\0".$mHash.$salt;
1725
        $h2 = $this->hash->hash($m2);
1726
1727
        return $this->_equals($h, $h2);
1728
    }
1729
1730
    /**
1731
     * RSASSA-PSS-SIGN.
1732
     *
1733
     * See {@link http://tools.ietf.org/html/rfc3447#section-8.1.1 RFC3447#section-8.1.1}.
1734
     *
1735
     * @param string $m
1736
     *
1737
     * @return string
1738
     */
1739
    private function _rsassa_pss_sign($m)
1740
    {
1741
        // EMSA-PSS encoding
1742
1743
        $em = $this->_emsa_pss_encode($m, 8 * $this->k - 1);
1744
1745
        // RSA signature
1746
1747
        $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 1743 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...
1748
        $s = $this->_rsasp1($m);
1749
        $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...
1750
1751
        // Output the signature S
1752
1753
        return $s;
1754
    }
1755
1756
    /**
1757
     * RSASSA-PSS-VERIFY.
1758
     *
1759
     * See {@link http://tools.ietf.org/html/rfc3447#section-8.1.2 RFC3447#section-8.1.2}.
1760
     *
1761
     * @param string $m
1762
     * @param string $s
1763
     *
1764
     * @return string
1765
     */
1766
    private function _rsassa_pss_verify($m, $s)
1767
    {
1768
        // Length checking
1769
1770
        if (strlen($s) != $this->k) {
1771
            user_error('Invalid signature');
1772
1773
            return false;
1774
        }
1775
1776
        // RSA verification
1777
1778
        $modBits = 8 * $this->k;
1779
1780
        $s2 = $this->_os2ip($s);
1781
        $m2 = $this->_rsavp1($s2);
1782
        if ($m2 === false) {
1783
            user_error('Invalid signature');
1784
1785
            return false;
1786
        }
1787
        $em = $this->_i2osp($m2, $modBits >> 3);
1788
        if ($em === false) {
1789
            user_error('Invalid signature');
1790
1791
            return false;
1792
        }
1793
1794
        // EMSA-PSS verification
1795
1796
        return $this->_emsa_pss_verify($m, $em, $modBits - 1);
1797
    }
1798
1799
    /**
1800
     * EMSA-PKCS1-V1_5-ENCODE.
1801
     *
1802
     * See {@link http://tools.ietf.org/html/rfc3447#section-9.2 RFC3447#section-9.2}.
1803
     *
1804
     * @param string $m
1805
     * @param int    $emLen
1806
     *
1807
     * @return string
1808
     */
1809
    private function _emsa_pkcs1_v1_5_encode($m, $emLen)
1810
    {
1811
        $h = $this->hash->hash($m);
1812
        if ($h === false) {
1813
            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...
1814
        }
1815
1816
        // see http://tools.ietf.org/html/rfc3447#page-43
1817
        switch ($this->hashName) {
1818
            case 'md2':
1819
                $t = pack('H*', '3020300c06082a864886f70d020205000410');
1820
                break;
1821
            case 'md5':
1822
                $t = pack('H*', '3020300c06082a864886f70d020505000410');
1823
                break;
1824
            case 'sha1':
1825
                $t = pack('H*', '3021300906052b0e03021a05000414');
1826
                break;
1827
            case 'sha256':
1828
                $t = pack('H*', '3031300d060960864801650304020105000420');
1829
                break;
1830
            case 'sha384':
1831
                $t = pack('H*', '3041300d060960864801650304020205000430');
1832
                break;
1833
            case 'sha512':
1834
                $t = pack('H*', '3051300d060960864801650304020305000440');
1835
        }
1836
        $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...
1837
        $tLen = strlen($t);
1838
1839
        if ($emLen < $tLen + 11) {
1840
            user_error('Intended encoded message length too short');
1841
1842
            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...
1843
        }
1844
1845
        $ps = str_repeat(chr(0xFF), $emLen - $tLen - 3);
1846
1847
        $em = "\0\1$ps\0$t";
1848
1849
        return $em;
1850
    }
1851
1852
    /**
1853
     * RSASSA-PKCS1-V1_5-SIGN.
1854
     *
1855
     * See {@link http://tools.ietf.org/html/rfc3447#section-8.2.1 RFC3447#section-8.2.1}.
1856
     *
1857
     * @param string $m
1858
     *
1859
     * @return string
1860
     */
1861
    private function _rsassa_pkcs1_v1_5_sign($m)
1862
    {
1863
        // EMSA-PKCS1-v1_5 encoding
1864
1865
        $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...
1866
        if ($em === false) {
1867
            user_error('RSA modulus too short');
1868
1869
            return false;
1870
        }
1871
1872
        // RSA signature
1873
1874
        $m = $this->_os2ip($em);
1875
        $s = $this->_rsasp1($m);
1876
        $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...
1877
1878
        // Output the signature S
1879
1880
        return $s;
1881
    }
1882
1883
    /**
1884
     * RSASSA-PKCS1-V1_5-VERIFY.
1885
     *
1886
     * See {@link http://tools.ietf.org/html/rfc3447#section-8.2.2 RFC3447#section-8.2.2}.
1887
     *
1888
     * @param string $m
1889
     *
1890
     * @return string
1891
     */
1892
    private function _rsassa_pkcs1_v1_5_verify($m, $s)
1893
    {
1894
        // Length checking
1895
1896
        if (strlen($s) != $this->k) {
1897
            user_error('Invalid signature');
1898
1899
            return false;
1900
        }
1901
1902
        // RSA verification
1903
1904
        $s = $this->_os2ip($s);
1905
        $m2 = $this->_rsavp1($s);
1906
        if ($m2 === false) {
1907
            user_error('Invalid signature');
1908
1909
            return false;
1910
        }
1911
        $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...
1912
        if ($em === false) {
1913
            user_error('Invalid signature');
1914
1915
            return false;
1916
        }
1917
1918
        // EMSA-PKCS1-v1_5 encoding
1919
1920
        $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...
1921
        if ($em2 === false) {
1922
            user_error('RSA modulus too short');
1923
1924
            return false;
1925
        }
1926
1927
        // Compare
1928
        return $this->_equals($em, $em2);
1929
    }
1930
1931
    /**
1932
     * Set Encryption Mode.
1933
     *
1934
     * Valid values include self::ENCRYPTION_OAEP and self::ENCRYPTION_PKCS1.
1935
     *
1936
     * @param int $mode
1937
     */
1938
    public function setEncryptionMode($mode)
1939
    {
1940
        $this->encryptionMode = $mode;
1941
    }
1942
1943
    /**
1944
     * Set Signature Mode.
1945
     *
1946
     * Valid values include self::SIGNATURE_PSS and self::SIGNATURE_PKCS1
1947
     *
1948
     * @param int $mode
1949
     */
1950
    public function setSignatureMode($mode)
1951
    {
1952
        $this->signatureMode = $mode;
1953
    }
1954
1955
    /**
1956
     * Encryption.
1957
     *
1958
     * Both self::ENCRYPTION_OAEP and self::ENCRYPTION_PKCS1 both place limits on how long $plaintext can be.
1959
     * If $plaintext exceeds those limits it will be broken up so that it does and the resultant ciphertext's will
1960
     * be concatenated together.
1961
     *
1962
     * @see self::decrypt()
1963
     *
1964
     * @param string $plaintext
1965
     *
1966
     * @return string
1967
     */
1968
    public function encrypt($plaintext)
1969
    {
1970
        switch ($this->encryptionMode) {
1971
            case self::ENCRYPTION_PKCS1:
1972
                $length = $this->k - 11;
1973
                if ($length <= 0) {
1974
                    return false;
1975
                }
1976
1977
                $plaintext = str_split($plaintext, $length);
1978
                $ciphertext = '';
1979
                foreach ($plaintext as $m) {
1980
                    $ciphertext .= $this->_rsaes_pkcs1_v1_5_encrypt($m);
1981
                }
1982
1983
                return $ciphertext;
1984
            case self::ENCRYPTION_OAEP:
1985
            default:
1986
                $length = $this->k - 2 * $this->hLen - 2;
1987
                if ($length <= 0) {
1988
                    return false;
1989
                }
1990
1991
                $plaintext = str_split($plaintext, $length);
1992
                $ciphertext = '';
1993
                foreach ($plaintext as $m) {
1994
                    $ciphertext .= $this->_rsaes_oaep_encrypt($m);
1995
                }
1996
1997
                return $ciphertext;
1998
        }
1999
    }
2000
2001
    /**
2002
     * Decryption.
2003
     *
2004
     * @see self::encrypt()
2005
     *
2006
     * @param string $plaintext
0 ignored issues
show
Bug introduced by
There is no parameter named $plaintext. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
2007
     *
2008
     * @return string
2009
     */
2010
    public function decrypt($ciphertext)
2011
    {
2012
        if ($this->k <= 0) {
2013
            return false;
2014
        }
2015
2016
        $ciphertext = str_split($ciphertext, $this->k);
2017
        $ciphertext[count($ciphertext) - 1] = str_pad($ciphertext[count($ciphertext) - 1], $this->k, chr(0), STR_PAD_LEFT);
2018
2019
        $plaintext = '';
2020
2021
        switch ($this->encryptionMode) {
2022
            case self::ENCRYPTION_PKCS1:
2023
                $decrypt = '_rsaes_pkcs1_v1_5_decrypt';
2024
                break;
2025
            case self::ENCRYPTION_OAEP:
2026
            default:
2027
                $decrypt = '_rsaes_oaep_decrypt';
2028
        }
2029
2030
        foreach ($ciphertext as $c) {
2031
            $temp = $this->$decrypt($c);
2032
            if ($temp === false) {
2033
                return false;
2034
            }
2035
            $plaintext .= $temp;
2036
        }
2037
2038
        return $plaintext;
2039
    }
2040
2041
    /**
2042
     * Create a signature.
2043
     *
2044
     * @see self::verify()
2045
     *
2046
     * @param string $message
2047
     *
2048
     * @return string
2049
     */
2050
    public function sign($message)
2051
    {
2052
        if (empty($this->modulus) || empty($this->exponent)) {
2053
            return false;
2054
        }
2055
2056
        switch ($this->signatureMode) {
2057
            case self::SIGNATURE_PKCS1:
2058
                return $this->_rsassa_pkcs1_v1_5_sign($message);
2059
            case self::SIGNATURE_PSS:
2060
            default:
2061
                return $this->_rsassa_pss_sign($message);
2062
        }
2063
    }
2064
2065
    /**
2066
     * Verifies a signature.
2067
     *
2068
     * @see self::sign()
2069
     *
2070
     * @param string $message
2071
     * @param string $signature
2072
     *
2073
     * @return bool
2074
     */
2075
    public function verify($message, $signature)
2076
    {
2077
        if (empty($this->modulus) || empty($this->exponent)) {
2078
            return false;
2079
        }
2080
2081
        switch ($this->signatureMode) {
2082
            case self::SIGNATURE_PKCS1:
2083
                return $this->_rsassa_pkcs1_v1_5_verify($message, $signature);
2084
            case self::SIGNATURE_PSS:
2085
            default:
2086
                return $this->_rsassa_pss_verify($message, $signature);
2087
        }
2088
    }
2089
2090
    /**
2091
     * Extract raw BER from Base64 encoding.
2092
     *
2093
     * @param string $str
2094
     *
2095
     * @return string
2096
     */
2097
    private function _extractBER($str)
2098
    {
2099
        /* X.509 certs are assumed to be base64 encoded but sometimes they'll have additional things in them
2100
         * above and beyond the ceritificate.
2101
         * ie. some may have the following preceding the -----BEGIN CERTIFICATE----- line:
2102
         *
2103
         * Bag Attributes
2104
         *     localKeyID: 01 00 00 00
2105
         * subject=/O=organization/OU=org unit/CN=common name
2106
         * issuer=/O=organization/CN=common name
2107
         */
2108
        $temp = preg_replace('#.*?^-+[^-]+-+[\r\n ]*$#ms', '', $str, 1);
2109
        // remove the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- stuff
2110
        $temp = preg_replace('#-+[^-]+-+#', '', $temp);
2111
        // remove new lines
2112
        $temp = str_replace(["\r", "\n", ' '], '', $temp);
2113
        $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false;
2114
2115
        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...
2116
    }
2117
2118
    /**
2119
     * Defines the public key.
2120
     *
2121
     * Some private key formats define the public exponent and some don't.  Those that don't define it are problematic when
2122
     * used in certain contexts.  For example, in SSH-2, RSA authentication works by sending the public key along with a
2123
     * message signed by the private key to the server.  The SSH-2 server looks the public key up in an index of public keys
2124
     * and if it's present then proceeds to verify the signature.  Problem is, if your private key doesn't include the public
2125
     * exponent this won't work unless you manually add the public exponent. phpseclib tries to guess if the key being used
2126
     * is the public key but in the event that it guesses incorrectly you might still want to explicitly set the key as being
2127
     * public.
2128
     *
2129
     * Do note that when a new key is loaded the index will be cleared.
2130
     *
2131
     * Returns true on success, false on failure
2132
     *
2133
     * @see self::getPublicKey()
2134
     *
2135
     * @param string $key  optional
2136
     * @param int    $type optional
2137
     *
2138
     * @return bool
2139
     */
2140
    private function setPublicKey($key = false, $type = false)
2141
    {
2142
        // if a public key has already been loaded return false
2143
        if (!empty($this->publicExponent)) {
2144
            return false;
2145
        }
2146
2147
        if ($key === false && !empty($this->modulus)) {
2148
            $this->publicExponent = $this->exponent;
2149
2150
            return true;
2151
        }
2152
2153
        if ($type === false) {
2154
            $types = [
2155
                self::PUBLIC_FORMAT_RAW,
2156
                self::PUBLIC_FORMAT_PKCS1,
2157
                self::PUBLIC_FORMAT_XML,
2158
                self::PUBLIC_FORMAT_OPENSSH,
2159
            ];
2160
            foreach ($types as $type) {
2161
                $components = $this->_parseKey($key, $type);
0 ignored issues
show
Bug introduced by
It seems like $key defined by parameter $key on line 2140 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...
2162
                if ($components !== false) {
2163
                    break;
2164
                }
2165
            }
2166
        } else {
2167
            $components = $this->_parseKey($key, $type);
0 ignored issues
show
Bug introduced by
It seems like $key defined by parameter $key on line 2140 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...
2168
        }
2169
2170
        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...
2171
            return false;
2172
        }
2173
2174
        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...
2175
            $this->modulus = $components['modulus'];
2176
            $this->exponent = $this->publicExponent = $components['publicExponent'];
2177
2178
            return true;
2179
        }
2180
2181
        $this->publicExponent = $components['publicExponent'];
2182
2183
        return true;
2184
    }
2185
}
2186