Failed Conditions
Push — analysis-zYOGLy ( 0d6226 )
by Florent
09:58 queued 05:42
created

RSA::_emsa_pkcs1_v1_5_encode()   D

Complexity

Conditions 9
Paths 15

Size

Total Lines 42
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 1 Features 0
Metric Value
c 2
b 1
f 0
dl 0
loc 42
rs 4.909
cc 9
eloc 30
nc 15
nop 2
1
<?php
2
3
/*
4
 * The MIT License (MIT)
5
 *
6
 * Copyright (c) 2014-2016 Spomky-Labs
7
 *
8
 * This software may be modified and distributed under the terms
9
 * of the MIT license.  See the LICENSE file for details.
10
 */
11
12
namespace Jose\Util;
13
14
/**
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
     * Do not use any padding.
46
     *
47
     * Although this method is not recommended it can none-the-less sometimes be useful if you're trying to decrypt some legacy
48
     * stuff, if you're trying to diagnose why an encrypted message isn't decrypting, etc.
49
     */
50
    const ENCRYPTION_NONE = 3;
51
    /**#@-*/
52
53
    /**#@+
54
     * @see self::sign()
55
     * @see self::verify()
56
     * @see self::setHash()
57
     */
58
    /**
59
     * Use the Probabilistic Signature Scheme for signing.
60
     *
61
     * Uses sha1 by default.
62
     *
63
     * @see self::setSaltLength()
64
     * @see self::setMGFHash()
65
     */
66
    const SIGNATURE_PSS = 1;
67
    /**
68
     * Use the PKCS#1 scheme by default.
69
     *
70
     * Although self::SIGNATURE_PSS offers more security, including PKCS#1 signing is necessary for purposes of backwards
71
     * compatibility with protocols (like SSH-2) written before PSS's introduction.
72
     */
73
    const SIGNATURE_PKCS1 = 2;
74
    /**#@-*/
75
76
    /**#@+
77
     * @see \phpseclib\Crypt\RSA::createKey()
78
     */
79
    /**
80
     * ASN1 Integer.
81
     */
82
    const ASN1_INTEGER = 2;
83
    /**
84
     * ASN1 Bit String.
85
     */
86
    const ASN1_BITSTRING = 3;
87
    /**
88
     * ASN1 Octet String.
89
     */
90
    const ASN1_OCTETSTRING = 4;
91
    /**
92
     * ASN1 Object Identifier.
93
     */
94
    const ASN1_OBJECT = 6;
95
    /**
96
     * ASN1 Sequence (with the constucted bit set).
97
     */
98
    const ASN1_SEQUENCE = 48;
99
    /**#@-*/
100
101
    /**#@+
102
     * @see \phpseclib\Crypt\RSA::__construct()
103
     */
104
    /**
105
     * To use the pure-PHP implementation.
106
     */
107
    const MODE_INTERNAL = 1;
108
    /**
109
     * To use the OpenSSL library.
110
     *
111
     * (if enabled; otherwise, the internal implementation will be used)
112
     */
113
    const MODE_OPENSSL = 2;
114
    /**#@-*/
115
116
    /**#@+
117
     * @see \phpseclib\Crypt\RSA::createKey()
118
     * @see \phpseclib\Crypt\RSA::setPrivateKeyFormat()
119
     */
120
    /**
121
     * PKCS#1 formatted private key.
122
     *
123
     * Used by OpenSSH
124
     */
125
    const PRIVATE_FORMAT_PKCS1 = 0;
126
    /**
127
     * PuTTY formatted private key.
128
     */
129
    const PRIVATE_FORMAT_PUTTY = 1;
130
    /**
131
     * XML formatted private key.
132
     */
133
    const PRIVATE_FORMAT_XML = 2;
134
    /**
135
     * PKCS#8 formatted private key.
136
     */
137
    const PRIVATE_FORMAT_PKCS8 = 8;
138
    /**#@-*/
139
140
    /**#@+
141
     * @see \phpseclib\Crypt\RSA::createKey()
142
     * @see \phpseclib\Crypt\RSA::setPublicKeyFormat()
143
     */
144
    /**
145
     * Raw public key.
146
     *
147
     * An array containing two \Jose\Util\BigInteger objects.
148
     *
149
     * The exponent can be indexed with any of the following:
150
     *
151
     * 0, e, exponent, publicExponent
152
     *
153
     * The modulus can be indexed with any of the following:
154
     *
155
     * 1, n, modulo, modulus
156
     */
157
    const PUBLIC_FORMAT_RAW = 3;
158
    /**
159
     * PKCS#1 formatted public key (raw).
160
     *
161
     * Used by File/X509.php
162
     *
163
     * Has the following header:
164
     *
165
     * -----BEGIN RSA PUBLIC KEY-----
166
     *
167
     * Analogous to ssh-keygen's pem format (as specified by -m)
168
     */
169
    const PUBLIC_FORMAT_PKCS1 = 4;
170
    const PUBLIC_FORMAT_PKCS1_RAW = 4;
171
    /**
172
     * XML formatted public key.
173
     */
174
    const PUBLIC_FORMAT_XML = 5;
175
    /**
176
     * OpenSSH formatted public key.
177
     *
178
     * Place in $HOME/.ssh/authorized_keys
179
     */
180
    const PUBLIC_FORMAT_OPENSSH = 6;
181
    /**
182
     * PKCS#1 formatted public key (encapsulated).
183
     *
184
     * Used by PHP's openssl_public_encrypt() and openssl's rsautl (when -pubin is set)
185
     *
186
     * Has the following header:
187
     *
188
     * -----BEGIN PUBLIC KEY-----
189
     *
190
     * Analogous to ssh-keygen's pkcs8 format (as specified by -m). Although PKCS8
191
     * is specific to private keys it's basically creating a DER-encoded wrapper
192
     * for keys. This just extends that same concept to public keys (much like ssh-keygen)
193
     */
194
    const PUBLIC_FORMAT_PKCS8 = 7;
195
    /**#@-*/
196
197
    /**
198
     * Precomputed Zero.
199
     *
200
     * @var array
201
     */
202
    private $zero;
203
204
    /**
205
     * Precomputed One.
206
     *
207
     * @var array
208
     */
209
    private $one;
210
211
    /**
212
     * Private Key Format.
213
     *
214
     * @var int
215
     */
216
    private $privateKeyFormat = self::PRIVATE_FORMAT_PKCS1;
217
218
    /**
219
     * Public Key Format.
220
     *
221
     * @var int
222
     */
223
    private $publicKeyFormat = self::PUBLIC_FORMAT_PKCS8;
224
225
    /**
226
     * Modulus (ie. n).
227
     *
228
     * @var \Jose\Util\BigInteger
229
     */
230
    private $modulus;
231
232
    /**
233
     * Modulus length.
234
     *
235
     * @var \Jose\Util\BigInteger
236
     */
237
    private $k;
238
239
    /**
240
     * Exponent (ie. e or d).
241
     *
242
     * @var \Jose\Util\BigInteger
243
     */
244
    private $exponent;
245
246
    /**
247
     * Primes for Chinese Remainder Theorem (ie. p and q).
248
     *
249
     * @var array
250
     */
251
    private $primes;
252
253
    /**
254
     * Exponents for Chinese Remainder Theorem (ie. dP and dQ).
255
     *
256
     * @var array
257
     */
258
    private $exponents;
259
260
    /**
261
     * Coefficients for Chinese Remainder Theorem (ie. qInv).
262
     *
263
     * @var array
264
     */
265
    private $coefficients;
266
267
    /**
268
     * Hash name.
269
     *
270
     * @var string
271
     */
272
    private $hashName;
273
274
    /**
275
     * Hash function.
276
     *
277
     * @var \phpseclib\Crypt\Hash
278
     */
279
    private $hash;
280
281
    /**
282
     * Length of hash function output.
283
     *
284
     * @var int
285
     */
286
    private $hLen;
287
288
    /**
289
     * Length of salt.
290
     *
291
     * @var int
292
     */
293
    private $sLen;
294
295
    /**
296
     * Hash function for the Mask Generation Function.
297
     *
298
     * @var \phpseclib\Crypt\Hash
299
     */
300
    private $mgfHash;
301
302
    /**
303
     * Length of MGF hash function output.
304
     *
305
     * @var int
306
     */
307
    private $mgfHLen;
308
309
    /**
310
     * Encryption mode.
311
     *
312
     * @var int
313
     */
314
    private $encryptionMode = self::ENCRYPTION_OAEP;
315
316
    /**
317
     * Signature mode.
318
     *
319
     * @var int
320
     */
321
    private $signatureMode = self::SIGNATURE_PSS;
322
323
    /**
324
     * Public Exponent.
325
     *
326
     * @var mixed
327
     */
328
    private $publicExponent = false;
329
330
    /**
331
     * Password.
332
     *
333
     * @var string
334
     */
335
    private $password = false;
336
337
    /**
338
     * Components.
339
     *
340
     * For use with parsing XML formatted keys.  PHP's XML Parser functions use utilized - instead of PHP's DOM functions -
341
     * because PHP's XML Parser functions work on PHP4 whereas PHP's DOM functions - although surperior - don't.
342
     *
343
     * @see self::_start_element_handler()
344
     *
345
     * @var array
346
     */
347
    private $components = [];
348
349
    /**
350
     * Current String.
351
     *
352
     * For use with parsing XML formatted keys.
353
     *
354
     * @see self::_character_handler()
355
     * @see self::_stop_element_handler()
356
     *
357
     * @var mixed
358
     */
359
    private $current;
360
361
    /**
362
     * OpenSSL configuration file name.
363
     *
364
     * Set to null to use system configuration file.
365
     *
366
     * @see self::createKey()
367
     *
368
     * @var mixed
369
     * @Access public
370
     */
371
    private $configFile;
372
373
    /**
374
     * Public key comment field.
375
     *
376
     * @var string
377
     */
378
    private $comment = 'phpseclib-generated-key';
379
380
    /**
381
     * The constructor.
382
     *
383
     * If you want to make use of the openssl extension, you'll need to set the mode manually, yourself.  The reason
384
     * \phpseclib\Crypt\RSA doesn't do it is because OpenSSL doesn't fail gracefully.  openssl_pkey_new(), in particular, requires
385
     * openssl.cnf be present somewhere and, unfortunately, the only real way to find out is too late.
386
     *
387
     * @return \phpseclib\Crypt\RSA
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
388
     */
389
    public function __construct()
390
    {
391
        $this->configFile = dirname(__FILE__).'/../openssl.cnf';
392
393
        if (!defined('CRYPT_RSA_MODE')) {
394
            switch (true) {
395
                // Math/BigInteger's openssl requirements are a little less stringent than Crypt/RSA's. in particular,
396
                // Math/BigInteger doesn't require an openssl.cfg file whereas Crypt/RSA does. so if Math/BigInteger
397
                // can't use OpenSSL it can be pretty trivially assumed, then, that Crypt/RSA can't either.
398
                case defined('MATH_BIGINTEGER_OPENSSL_DISABLE'):
399
                    define('CRYPT_RSA_MODE', self::MODE_INTERNAL);
400
                    break;
401
                case extension_loaded('openssl') && file_exists($this->configFile):
402
                    // some versions of XAMPP have mismatched versions of OpenSSL which causes it not to work
403
                    ob_start();
404
                    @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...
405
                    $content = ob_get_contents();
406
                    ob_end_clean();
407
408
                    preg_match_all('#OpenSSL (Header|Library) Version(.*)#im', $content, $matches);
409
410
                    $versions = [];
411
                    if (!empty($matches[1])) {
412
                        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...
413
                            $fullVersion = trim(str_replace('=>', '', strip_tags($matches[2][$i])));
414
415
                            // Remove letter part in OpenSSL version
416
                            if (!preg_match('/(\d+\.\d+\.\d+)/i', $fullVersion, $m)) {
417
                                $versions[$matches[1][$i]] = $fullVersion;
418
                            } else {
419
                                $versions[$matches[1][$i]] = $m[0];
420
                            }
421
                        }
422
                    }
423
424
                    // it doesn't appear that OpenSSL versions were reported upon until PHP 5.3+
425
                    switch (true) {
426
                        case !isset($versions['Header']):
427
                        case !isset($versions['Library']):
428
                        case $versions['Header'] == $versions['Library']:
429
                            define('CRYPT_RSA_MODE', self::MODE_OPENSSL);
430
                            break;
431
                        default:
432
                            define('CRYPT_RSA_MODE', self::MODE_INTERNAL);
433
                            define('MATH_BIGINTEGER_OPENSSL_DISABLE', true);
434
                    }
435
                    break;
436
                default:
437
                    define('CRYPT_RSA_MODE', self::MODE_INTERNAL);
438
            }
439
        }
440
441
        $this->zero = new BigInteger();
0 ignored issues
show
Documentation Bug introduced by
It seems like new \Jose\Util\BigInteger() 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...
442
        $this->one = new BigInteger(1);
0 ignored issues
show
Documentation Bug introduced by
It seems like new \Jose\Util\BigInteger(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...
443
444
        $this->hash = new Hash('sha1');
0 ignored issues
show
Documentation Bug introduced by
It seems like new \Jose\Util\Hash('sha1') of type object<Jose\Util\Hash> is incompatible with the declared type object<phpseclib\Crypt\Hash> of property $hash.

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

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...
949
            }
950
            if (is_object($key->mgfHash)) {
951
                $this->mgfHash = new Hash($key->mgfHash->getHash());
0 ignored issues
show
Documentation Bug introduced by
It seems like new \Jose\Util\Hash($key->mgfHash->getHash()) of type object<Jose\Util\Hash> is incompatible with the declared type object<phpseclib\Crypt\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...
952
            }
953
954
            if (is_object($key->modulus)) {
955
                $this->modulus = $key->modulus->copy();
956
            }
957
            if (is_object($key->exponent)) {
958
                $this->exponent = $key->exponent->copy();
959
            }
960
            if (is_object($key->publicExponent)) {
961
                $this->publicExponent = $key->publicExponent->copy();
962
            }
963
964
            $this->primes = [];
965
            $this->exponents = [];
966
            $this->coefficients = [];
967
968
            foreach ($this->primes as $prime) {
969
                $this->primes[] = $prime->copy();
970
            }
971
            foreach ($this->exponents as $exponent) {
972
                $this->exponents[] = $exponent->copy();
973
            }
974
            foreach ($this->coefficients as $coefficient) {
975
                $this->coefficients[] = $coefficient->copy();
976
            }
977
978
            return true;
979
        }
980
981
        if ($type === false) {
982
            $types = [
983
                self::PUBLIC_FORMAT_RAW,
984
                self::PRIVATE_FORMAT_PKCS1,
985
                self::PRIVATE_FORMAT_XML,
986
                self::PRIVATE_FORMAT_PUTTY,
987
                self::PUBLIC_FORMAT_OPENSSH,
988
            ];
989
            foreach ($types as $type) {
990
                $components = $this->_parseKey($key, $type);
991
                if ($components !== false) {
992
                    break;
993
                }
994
            }
995
        } else {
996
            $components = $this->_parseKey($key, $type);
997
        }
998
999
        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...
1000
            return false;
1001
        }
1002
1003
        if (isset($components['comment']) && $components['comment'] !== false) {
1004
            $this->comment = $components['comment'];
1005
        }
1006
        $this->modulus = $components['modulus'];
1007
        $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...
1008
        $this->exponent = isset($components['privateExponent']) ? $components['privateExponent'] : $components['publicExponent'];
1009
        if (isset($components['primes'])) {
1010
            $this->primes = $components['primes'];
1011
            $this->exponents = $components['exponents'];
1012
            $this->coefficients = $components['coefficients'];
1013
            $this->publicExponent = $components['publicExponent'];
1014
        } else {
1015
            $this->primes = [];
1016
            $this->exponents = [];
1017
            $this->coefficients = [];
1018
            $this->publicExponent = false;
1019
        }
1020
1021
        switch ($type) {
1022
            case self::PUBLIC_FORMAT_OPENSSH:
1023
            case self::PUBLIC_FORMAT_RAW:
1024
                $this->setPublicKey();
1025
                break;
1026
            case self::PRIVATE_FORMAT_PKCS1:
1027
                switch (true) {
1028
                    case strpos($key, '-BEGIN PUBLIC KEY-') !== false:
1029
                    case strpos($key, '-BEGIN RSA PUBLIC KEY-') !== false:
1030
                        $this->setPublicKey();
1031
                }
1032
        }
1033
1034
        return true;
1035
    }
1036
1037
    /**
1038
     * Generates the smallest and largest numbers requiring $bits bits.
1039
     *
1040
     * @param int $bits
1041
     *
1042
     * @return array
1043
     */
1044
    private function _generateMinMax($bits)
1045
    {
1046
        $bytes = $bits >> 3;
1047
        $min = str_repeat(chr(0), $bytes);
1048
        $max = str_repeat(chr(0xFF), $bytes);
1049
        $msb = $bits & 7;
1050
        if ($msb) {
1051
            $min = chr(1 << ($msb - 1)).$min;
1052
            $max = chr((1 << $msb) - 1).$max;
1053
        } else {
1054
            $min[0] = chr(0x80);
1055
        }
1056
1057
        return [
1058
            'min' => new BigInteger($min, 256),
1059
            'max' => new BigInteger($max, 256),
1060
        ];
1061
    }
1062
1063
    /**
1064
     * DER-decode the length.
1065
     *
1066
     * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4.  See
1067
     * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information.
1068
     *
1069
     * @param string $string
1070
     *
1071
     * @return int
1072
     */
1073
    private function _decodeLength(&$string)
1074
    {
1075
        $length = ord($this->_string_shift($string));
1076
        if ($length & 0x80) { // definite length, long form
1077
            $length &= 0x7F;
1078
            $temp = $this->_string_shift($string, $length);
1079
            list(, $length) = unpack('N', substr(str_pad($temp, 4, chr(0), STR_PAD_LEFT), -4));
1080
        }
1081
1082
        return $length;
1083
    }
1084
1085
    /**
1086
     * DER-encode the length.
1087
     *
1088
     * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4.  See
1089
     * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information.
1090
     *
1091
     * @param int $length
1092
     *
1093
     * @return string
1094
     */
1095
    private function _encodeLength($length)
1096
    {
1097
        if ($length <= 0x7F) {
1098
            return chr($length);
1099
        }
1100
1101
        $temp = ltrim(pack('N', $length), chr(0));
1102
1103
        return pack('Ca*', 0x80 | strlen($temp), $temp);
1104
    }
1105
1106
    /**
1107
     * String Shift.
1108
     *
1109
     * Inspired by array_shift
1110
     *
1111
     * @param string $string
1112
     * @param int    $index
1113
     *
1114
     * @return string
1115
     */
1116
    private function _string_shift(&$string, $index = 1)
1117
    {
1118
        $substr = substr($string, 0, $index);
1119
        $string = substr($string, $index);
1120
1121
        return $substr;
1122
    }
1123
1124
    /**
1125
     * Determines which hashing function should be used.
1126
     *
1127
     * Used with signature production / verification and (if the encryption mode is self::ENCRYPTION_OAEP) encryption and
1128
     * decryption.  If $hash isn't supported, sha1 is used.
1129
     *
1130
     * @param string $hash
1131
     */
1132
    public function setHash($hash)
1133
    {
1134
        switch ($hash) {
1135
            case 'sha1':
1136
                $this->hLen = 20;
1137
                break;
1138
            case 'sha256':
1139
                $this->hLen = 32;
1140
                break;
1141
            case 'sha384':
1142
                $this->hLen = 48;
1143
                break;
1144
            case 'sha512':
1145
                $this->hLen = 64;
1146
                break;
1147
            default:
1148
                throw new \InvalidArgumentException('Unsupported hash algorithm.');
1149
        }
1150
        $this->hash = new Hash($hash);
0 ignored issues
show
Documentation Bug introduced by
It seems like new \Jose\Util\Hash($hash) of type object<Jose\Util\Hash> is incompatible with the declared type object<phpseclib\Crypt\Hash> of property $hash.

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...
1151
    }
1152
1153
    /**
1154
     * Determines which hashing function should be used for the mask generation function.
1155
     *
1156
     * The mask generation function is used by self::ENCRYPTION_OAEP and self::SIGNATURE_PSS and although it's
1157
     * best if Hash and MGFHash are set to the same thing this is not a requirement.
1158
     *
1159
     * @param string $hash
1160
     */
1161
    public function setMGFHash($hash)
1162
    {
1163
        switch ($hash) {
1164
            case 'sha1':
1165
                $this->mgfHLen = 20;
1166
                break;
1167
            case 'sha256':
1168
                $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<phpseclib\Crypt\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...
1169
                break;
1170
            case 'sha384':
1171
                $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<phpseclib\Crypt\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...
1172
                break;
1173
            case 'sha512':
1174
                $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<phpseclib\Crypt\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...
1175
                break;
1176
            default:
1177
                throw new \InvalidArgumentException('Unsupported hash algorithm.');
1178
        }
1179
        $this->mgfHash = new Hash($hash);
0 ignored issues
show
Documentation Bug introduced by
It seems like new \Jose\Util\Hash($hash) of type object<Jose\Util\Hash> is incompatible with the declared type object<phpseclib\Crypt\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...
1180
    }
1181
1182
    /**
1183
     * Determines the salt length.
1184
     *
1185
     * To quote from {@link http://tools.ietf.org/html/rfc3447#page-38 RFC3447#page-38}:
1186
     *
1187
     *    Typical salt lengths in octets are hLen (the length of the output
1188
     *    of the hash function Hash) and 0.
1189
     *
1190
     * @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...
1191
     */
1192
    public function setSaltLength($sLen)
1193
    {
1194
        $this->sLen = $sLen;
1195
    }
1196
1197
    /**
1198
     * Integer-to-Octet-String primitive.
1199
     *
1200
     * See {@link http://tools.ietf.org/html/rfc3447#section-4.1 RFC3447#section-4.1}.
1201
     *
1202
     * @param \Jose\Util\BigInteger $x
1203
     * @param int                   $xLen
1204
     *
1205
     * @return string
1206
     */
1207
    private function _i2osp($x, $xLen)
1208
    {
1209
        $x = $x->toBytes();
1210
        if (strlen($x) > $xLen) {
1211
            user_error('Integer too large');
1212
1213
            return false;
1214
        }
1215
1216
        return str_pad($x, $xLen, chr(0), STR_PAD_LEFT);
1217
    }
1218
1219
    /**
1220
     * Octet-String-to-Integer primitive.
1221
     *
1222
     * See {@link http://tools.ietf.org/html/rfc3447#section-4.2 RFC3447#section-4.2}.
1223
     *
1224
     * @param string $x
1225
     *
1226
     * @return \Jose\Util\BigInteger
1227
     */
1228
    private function _os2ip($x)
1229
    {
1230
        return new BigInteger($x, 256);
1231
    }
1232
1233
    /**
1234
     * Exponentiate with or without Chinese Remainder Theorem.
1235
     *
1236
     * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.1 RFC3447#section-5.1.2}.
1237
     *
1238
     * @param \Jose\Util\BigInteger $x
1239
     *
1240
     * @return \Jose\Util\BigInteger
1241
     */
1242
    private function _exponentiate($x)
1243
    {
1244
        if (empty($this->primes) || empty($this->coefficients) || empty($this->exponents)) {
1245
            return $x->modPow($this->exponent, $this->modulus);
1246
        }
1247
1248
        $num_primes = count($this->primes);
1249
1250
        if (defined('CRYPT_RSA_DISABLE_BLINDING')) {
1251
            $m_i = [
1252
                1 => $x->modPow($this->exponents[1], $this->primes[1]),
1253
                2 => $x->modPow($this->exponents[2], $this->primes[2]),
1254
            ];
1255
            $h = $m_i[1]->subtract($m_i[2]);
1256
            $h = $h->multiply($this->coefficients[2]);
1257
            list(, $h) = $h->divide($this->primes[1]);
1258
            $m = $m_i[2]->add($h->multiply($this->primes[2]));
1259
1260
            $r = $this->primes[1];
1261
            for ($i = 3; $i <= $num_primes; $i++) {
1262
                $m_i = $x->modPow($this->exponents[$i], $this->primes[$i]);
1263
1264
                $r = $r->multiply($this->primes[$i - 1]);
1265
1266
                $h = $m_i->subtract($m);
1267
                $h = $h->multiply($this->coefficients[$i]);
1268
                list(, $h) = $h->divide($this->primes[$i]);
1269
1270
                $m = $m->add($r->multiply($h));
1271
            }
1272
        } else {
1273
            $smallest = $this->primes[1];
1274
            for ($i = 2; $i <= $num_primes; $i++) {
1275
                if ($smallest->compare($this->primes[$i]) > 0) {
1276
                    $smallest = $this->primes[$i];
1277
                }
1278
            }
1279
1280
            $one = new BigInteger(1);
1281
1282
            $r = $one->random($one, $smallest->subtract($one));
1283
1284
            $m_i = [
1285
                1 => $this->_blind($x, $r, 1),
1286
                2 => $this->_blind($x, $r, 2),
1287
            ];
1288
            $h = $m_i[1]->subtract($m_i[2]);
1289
            $h = $h->multiply($this->coefficients[2]);
1290
            list(, $h) = $h->divide($this->primes[1]);
1291
            $m = $m_i[2]->add($h->multiply($this->primes[2]));
1292
1293
            $r = $this->primes[1];
1294
            for ($i = 3; $i <= $num_primes; $i++) {
1295
                $m_i = $this->_blind($x, $r, $i);
1296
1297
                $r = $r->multiply($this->primes[$i - 1]);
1298
1299
                $h = $m_i->subtract($m);
1300
                $h = $h->multiply($this->coefficients[$i]);
1301
                list(, $h) = $h->divide($this->primes[$i]);
1302
1303
                $m = $m->add($r->multiply($h));
1304
            }
1305
        }
1306
1307
        return $m;
1308
    }
1309
1310
    /**
1311
     * Performs RSA Blinding.
1312
     *
1313
     * Protects against timing attacks by employing RSA Blinding.
1314
     * Returns $x->modPow($this->exponents[$i], $this->primes[$i])
1315
     *
1316
     * @param \Jose\Util\BigInteger $x
1317
     * @param \Jose\Util\BigInteger $r
1318
     * @param int                   $i
1319
     *
1320
     * @return \Jose\Util\BigInteger
1321
     */
1322
    private function _blind($x, $r, $i)
1323
    {
1324
        $x = $x->multiply($r->modPow($this->publicExponent, $this->primes[$i]));
1325
        $x = $x->modPow($this->exponents[$i], $this->primes[$i]);
1326
1327
        $r = $r->modInverse($this->primes[$i]);
1328
        $x = $x->multiply($r);
0 ignored issues
show
Security Bug introduced by
It seems like $r defined by $r->modInverse($this->primes[$i]) on line 1327 can also be of type false; however, Jose\Util\BigInteger::multiply() does only seem to accept object<Jose\Util\BigInteger>, 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...
1329
        list(, $x) = $x->divide($this->primes[$i]);
1330
1331
        return $x;
1332
    }
1333
1334
    /**
1335
     * Performs blinded RSA equality testing.
1336
     *
1337
     * Protects against a particular type of timing attack described.
1338
     *
1339
     * See {@link http://codahale.com/a-lesson-in-timing-attacks/ A Lesson In Timing Attacks (or, Don't use MessageDigest.isEquals)}
1340
     *
1341
     * Thanks for the heads up singpolyma!
1342
     *
1343
     * @param string $x
1344
     * @param string $y
1345
     *
1346
     * @return bool
1347
     */
1348
    private function _equals($x, $y)
1349
    {
1350
        if (strlen($x) != strlen($y)) {
1351
            return false;
1352
        }
1353
1354
        $result = 0;
1355
        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...
1356
            $result |= ord($x[$i]) ^ ord($y[$i]);
1357
        }
1358
1359
        return $result == 0;
1360
    }
1361
1362
    /**
1363
     * RSAEP.
1364
     *
1365
     * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.1 RFC3447#section-5.1.1}.
1366
     *
1367
     * @param \Jose\Util\BigInteger $m
1368
     *
1369
     * @return \Jose\Util\BigInteger
1370
     */
1371
    private function _rsaep($m)
1372
    {
1373
        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...
1374
            user_error('Message representative out of range');
1375
1376
            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...
1377
        }
1378
1379
        return $this->_exponentiate($m);
1380
    }
1381
1382
    /**
1383
     * RSADP.
1384
     *
1385
     * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.2 RFC3447#section-5.1.2}.
1386
     *
1387
     * @param \Jose\Util\BigInteger $c
1388
     *
1389
     * @return \Jose\Util\BigInteger
1390
     */
1391
    private function _rsadp($c)
1392
    {
1393
        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...
1394
            user_error('Ciphertext representative out of range');
1395
1396
            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...
1397
        }
1398
1399
        return $this->_exponentiate($c);
1400
    }
1401
1402
    /**
1403
     * RSASP1.
1404
     *
1405
     * See {@link http://tools.ietf.org/html/rfc3447#section-5.2.1 RFC3447#section-5.2.1}.
1406
     *
1407
     * @param \Jose\Util\BigInteger $m
1408
     *
1409
     * @return \Jose\Util\BigInteger
1410
     */
1411
    private function _rsasp1($m)
1412
    {
1413
        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...
1414
            user_error('Message representative out of range');
1415
1416
            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...
1417
        }
1418
1419
        return $this->_exponentiate($m);
1420
    }
1421
1422
    /**
1423
     * RSAVP1.
1424
     *
1425
     * See {@link http://tools.ietf.org/html/rfc3447#section-5.2.2 RFC3447#section-5.2.2}.
1426
     *
1427
     * @param \Jose\Util\BigInteger $s
1428
     *
1429
     * @return \Jose\Util\BigInteger
1430
     */
1431
    private function _rsavp1($s)
1432
    {
1433
        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...
1434
            user_error('Signature representative out of range');
1435
1436
            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...
1437
        }
1438
1439
        return $this->_exponentiate($s);
1440
    }
1441
1442
    /**
1443
     * MGF1.
1444
     *
1445
     * See {@link http://tools.ietf.org/html/rfc3447#appendix-B.2.1 RFC3447#appendix-B.2.1}.
1446
     *
1447
     * @param string $mgfSeed
1448
     * @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...
1449
     *
1450
     * @return string
1451
     */
1452
    private function _mgf1($mgfSeed, $maskLen)
1453
    {
1454
        // if $maskLen would yield strings larger than 4GB, PKCS#1 suggests a "Mask too long" error be output.
1455
1456
        $t = '';
1457
        $count = ceil($maskLen / $this->mgfHLen);
1458
        for ($i = 0; $i < $count; $i++) {
1459
            $c = pack('N', $i);
1460
            $t .= $this->mgfHash->hash($mgfSeed.$c);
1461
        }
1462
1463
        return substr($t, 0, $maskLen);
1464
    }
1465
1466
    /**
1467
     * RSAES-OAEP-ENCRYPT.
1468
     *
1469
     * See {@link http://tools.ietf.org/html/rfc3447#section-7.1.1 RFC3447#section-7.1.1} and
1470
     * {http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding OAES}.
1471
     *
1472
     * @param string $m
1473
     * @param string $l
1474
     *
1475
     * @return string
1476
     */
1477
    private function _rsaes_oaep_encrypt($m, $l = '')
1478
    {
1479
        $mLen = strlen($m);
1480
1481
        // Length checking
1482
1483
        // if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
1484
        // be output.
1485
1486
        if ($mLen > $this->k - 2 * $this->hLen - 2) {
1487
            user_error('Message too long');
1488
1489
            return false;
1490
        }
1491
1492
        // EME-OAEP encoding
1493
1494
        $lHash = $this->hash->hash($l);
1495
        $ps = str_repeat(chr(0), $this->k - $mLen - 2 * $this->hLen - 2);
1496
        $db = $lHash.$ps.chr(1).$m;
1497
        $seed = random_bytes($this->hLen);
1498
        $dbMask = $this->_mgf1($seed, $this->k - $this->hLen - 1);
1499
        $maskedDB = $db ^ $dbMask;
1500
        $seedMask = $this->_mgf1($maskedDB, $this->hLen);
1501
        $maskedSeed = $seed ^ $seedMask;
1502
        $em = chr(0).$maskedSeed.$maskedDB;
1503
1504
        // RSA encryption
1505
1506
        $m = $this->_os2ip($em);
1507
        $c = $this->_rsaep($m);
1508
        $c = $this->_i2osp($c, $this->k);
1509
1510
        // Output the ciphertext C
1511
1512
        return $c;
1513
    }
1514
1515
    /**
1516
     * RSAES-OAEP-DECRYPT.
1517
     *
1518
     * See {@link http://tools.ietf.org/html/rfc3447#section-7.1.2 RFC3447#section-7.1.2}.  The fact that the error
1519
     * messages aren't distinguishable from one another hinders debugging, but, to quote from RFC3447#section-7.1.2:
1520
     *
1521
     *    Note.  Care must be taken to ensure that an opponent cannot
1522
     *    distinguish the different error conditions in Step 3.g, whether by
1523
     *    error message or timing, or, more generally, learn partial
1524
     *    information about the encoded message EM.  Otherwise an opponent may
1525
     *    be able to obtain useful information about the decryption of the
1526
     *    ciphertext C, leading to a chosen-ciphertext attack such as the one
1527
     *    observed by Manger [36].
1528
     *
1529
     * As for $l...  to quote from {@link http://tools.ietf.org/html/rfc3447#page-17 RFC3447#page-17}:
1530
     *
1531
     *    Both the encryption and the decryption operations of RSAES-OAEP take
1532
     *    the value of a label L as input.  In this version of PKCS #1, L is
1533
     *    the empty string; other uses of the label are outside the scope of
1534
     *    this document.
1535
     *
1536
     * @param string $c
1537
     * @param string $l
1538
     *
1539
     * @return string
1540
     */
1541
    private function _rsaes_oaep_decrypt($c, $l = '')
1542
    {
1543
        // Length checking
1544
1545
        // if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
1546
        // be output.
1547
1548
        if (strlen($c) != $this->k || $this->k < 2 * $this->hLen + 2) {
1549
            user_error('Decryption error');
1550
1551
            return false;
1552
        }
1553
1554
        // RSA decryption
1555
1556
        $c = $this->_os2ip($c);
1557
        $m = $this->_rsadp($c);
1558
        if ($m === false) {
1559
            user_error('Decryption error');
1560
1561
            return false;
1562
        }
1563
        $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...
1564
1565
        // EME-OAEP decoding
1566
1567
        $lHash = $this->hash->hash($l);
1568
        $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...
1569
        $maskedSeed = substr($em, 1, $this->hLen);
1570
        $maskedDB = substr($em, $this->hLen + 1);
1571
        $seedMask = $this->_mgf1($maskedDB, $this->hLen);
1572
        $seed = $maskedSeed ^ $seedMask;
1573
        $dbMask = $this->_mgf1($seed, $this->k - $this->hLen - 1);
1574
        $db = $maskedDB ^ $dbMask;
1575
        $lHash2 = substr($db, 0, $this->hLen);
1576
        $m = substr($db, $this->hLen);
1577
        if ($lHash != $lHash2) {
1578
            user_error('Decryption error');
1579
1580
            return false;
1581
        }
1582
        $m = ltrim($m, chr(0));
1583
        if (ord($m[0]) != 1) {
1584
            user_error('Decryption error');
1585
1586
            return false;
1587
        }
1588
1589
        // Output the message M
1590
1591
        return substr($m, 1);
1592
    }
1593
1594
    /**
1595
     * Raw Encryption / Decryption.
1596
     *
1597
     * Doesn't use padding and is not recommended.
1598
     *
1599
     * @param string $m
1600
     *
1601
     * @return string
1602
     */
1603
    private function _raw_encrypt($m)
1604
    {
1605
        $temp = $this->_os2ip($m);
1606
        $temp = $this->_rsaep($temp);
1607
1608
        return  $this->_i2osp($temp, $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...
1609
    }
1610
1611
    /**
1612
     * RSAES-PKCS1-V1_5-ENCRYPT.
1613
     *
1614
     * See {@link http://tools.ietf.org/html/rfc3447#section-7.2.1 RFC3447#section-7.2.1}.
1615
     *
1616
     * @param string $m
1617
     *
1618
     * @return string
1619
     */
1620
    private function _rsaes_pkcs1_v1_5_encrypt($m)
1621
    {
1622
        $mLen = strlen($m);
1623
1624
        // Length checking
1625
1626
        if ($mLen > $this->k - 11) {
1627
            user_error('Message too long');
1628
1629
            return false;
1630
        }
1631
1632
        // EME-PKCS1-v1_5 encoding
1633
1634
        $psLen = $this->k - $mLen - 3;
1635
        $ps = '';
1636
        while (strlen($ps) != $psLen) {
1637
            $temp = random_bytes($psLen - strlen($ps));
1638
            $temp = str_replace("\x00", '', $temp);
1639
            $ps .= $temp;
1640
        }
1641
        $type = 2;
1642
        // see the comments of _rsaes_pkcs1_v1_5_decrypt() to understand why this is being done
1643
        if (defined('CRYPT_RSA_PKCS15_COMPAT') && (!isset($this->publicExponent) || $this->exponent !== $this->publicExponent)) {
1644
            $type = 1;
1645
            // "The padding string PS shall consist of k-3-||D|| octets. ... for block type 01, they shall have value FF"
1646
            $ps = str_repeat("\xFF", $psLen);
1647
        }
1648
        $em = chr(0).chr($type).$ps.chr(0).$m;
1649
1650
        // RSA encryption
1651
        $m = $this->_os2ip($em);
1652
        $c = $this->_rsaep($m);
1653
        $c = $this->_i2osp($c, $this->k);
1654
1655
        // Output the ciphertext C
1656
1657
        return $c;
1658
    }
1659
1660
    /**
1661
     * RSAES-PKCS1-V1_5-DECRYPT.
1662
     *
1663
     * See {@link http://tools.ietf.org/html/rfc3447#section-7.2.2 RFC3447#section-7.2.2}.
1664
     *
1665
     * For compatibility purposes, this function departs slightly from the description given in RFC3447.
1666
     * The reason being that RFC2313#section-8.1 (PKCS#1 v1.5) states that ciphertext's encrypted by the
1667
     * private key should have the second byte set to either 0 or 1 and that ciphertext's encrypted by the
1668
     * public key should have the second byte set to 2.  In RFC3447 (PKCS#1 v2.1), the second byte is supposed
1669
     * to be 2 regardless of which key is used.  For compatibility purposes, we'll just check to make sure the
1670
     * second byte is 2 or less.  If it is, we'll accept the decrypted string as valid.
1671
     *
1672
     * As a consequence of this, a private key encrypted ciphertext produced with \phpseclib\Crypt\RSA may not decrypt
1673
     * with a strictly PKCS#1 v1.5 compliant RSA implementation.  Public key encrypted ciphertext's should but
1674
     * not private key encrypted ciphertext's.
1675
     *
1676
     * @param string $c
1677
     *
1678
     * @return string
1679
     */
1680
    private function _rsaes_pkcs1_v1_5_decrypt($c)
1681
    {
1682
        // Length checking
1683
1684
        if (strlen($c) != $this->k) { // or if k < 11
1685
            user_error('Decryption error');
1686
1687
            return false;
1688
        }
1689
1690
        // RSA decryption
1691
1692
        $c = $this->_os2ip($c);
1693
        $m = $this->_rsadp($c);
1694
1695
        if ($m === false) {
1696
            user_error('Decryption error');
1697
1698
            return false;
1699
        }
1700
        $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...
1701
1702
        // EME-PKCS1-v1_5 decoding
1703
1704
        if (ord($em[0]) != 0 || ord($em[1]) > 2) {
1705
            user_error('Decryption error');
1706
1707
            return false;
1708
        }
1709
1710
        $ps = substr($em, 2, strpos($em, chr(0), 2) - 2);
1711
        $m = substr($em, strlen($ps) + 3);
1712
1713
        if (strlen($ps) < 8) {
1714
            user_error('Decryption error');
1715
1716
            return false;
1717
        }
1718
1719
        // Output M
1720
1721
        return $m;
1722
    }
1723
1724
    /**
1725
     * EMSA-PSS-ENCODE.
1726
     *
1727
     * See {@link http://tools.ietf.org/html/rfc3447#section-9.1.1 RFC3447#section-9.1.1}.
1728
     *
1729
     * @param string $m
1730
     * @param int    $emBits
1731
     */
1732
    private function _emsa_pss_encode($m, $emBits)
1733
    {
1734
        // if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
1735
        // be output.
1736
1737
        $emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8)
1738
        $sLen = $this->sLen ? $this->sLen : $this->hLen;
1739
1740
        $mHash = $this->hash->hash($m);
1741
        if ($emLen < $this->hLen + $sLen + 2) {
1742
            user_error('Encoding error');
1743
1744
            return false;
1745
        }
1746
1747
        $salt = random_bytes($sLen);
1748
        $m2 = "\0\0\0\0\0\0\0\0".$mHash.$salt;
1749
        $h = $this->hash->hash($m2);
1750
        $ps = str_repeat(chr(0), $emLen - $sLen - $this->hLen - 2);
1751
        $db = $ps.chr(1).$salt;
1752
        $dbMask = $this->_mgf1($h, $emLen - $this->hLen - 1);
1753
        $maskedDB = $db ^ $dbMask;
1754
        $maskedDB[0] = ~chr(0xFF << ($emBits & 7)) & $maskedDB[0];
1755
        $em = $maskedDB.$h.chr(0xBC);
1756
1757
        return $em;
1758
    }
1759
1760
    /**
1761
     * EMSA-PSS-VERIFY.
1762
     *
1763
     * See {@link http://tools.ietf.org/html/rfc3447#section-9.1.2 RFC3447#section-9.1.2}.
1764
     *
1765
     * @param string $m
1766
     * @param string $em
1767
     * @param int    $emBits
1768
     *
1769
     * @return string
1770
     */
1771
    private function _emsa_pss_verify($m, $em, $emBits)
1772
    {
1773
        // if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
1774
        // be output.
1775
1776
        $emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8);
1777
        $sLen = $this->sLen ? $this->sLen : $this->hLen;
1778
1779
        $mHash = $this->hash->hash($m);
1780
        if ($emLen < $this->hLen + $sLen + 2) {
1781
            return false;
1782
        }
1783
1784
        if ($em[strlen($em) - 1] != chr(0xBC)) {
1785
            return false;
1786
        }
1787
1788
        $maskedDB = substr($em, 0, -$this->hLen - 1);
1789
        $h = substr($em, -$this->hLen - 1, $this->hLen);
1790
        $temp = chr(0xFF << ($emBits & 7));
1791
        if ((~$maskedDB[0] & $temp) != $temp) {
1792
            return false;
1793
        }
1794
        $dbMask = $this->_mgf1($h, $emLen - $this->hLen - 1);
1795
        $db = $maskedDB ^ $dbMask;
1796
        $db[0] = ~chr(0xFF << ($emBits & 7)) & $db[0];
1797
        $temp = $emLen - $this->hLen - $sLen - 2;
1798
        if (substr($db, 0, $temp) != str_repeat(chr(0), $temp) || ord($db[$temp]) != 1) {
1799
            return false;
1800
        }
1801
        $salt = substr($db, $temp + 1); // should be $sLen long
1802
        $m2 = "\0\0\0\0\0\0\0\0".$mHash.$salt;
1803
        $h2 = $this->hash->hash($m2);
1804
1805
        return $this->_equals($h, $h2);
1806
    }
1807
1808
    /**
1809
     * RSASSA-PSS-SIGN.
1810
     *
1811
     * See {@link http://tools.ietf.org/html/rfc3447#section-8.1.1 RFC3447#section-8.1.1}.
1812
     *
1813
     * @param string $m
1814
     *
1815
     * @return string
1816
     */
1817
    private function _rsassa_pss_sign($m)
1818
    {
1819
        // EMSA-PSS encoding
1820
1821
        $em = $this->_emsa_pss_encode($m, 8 * $this->k - 1);
1822
1823
        // RSA signature
1824
1825
        $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 1821 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...
1826
        $s = $this->_rsasp1($m);
1827
        $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...
1828
1829
        // Output the signature S
1830
1831
        return $s;
1832
    }
1833
1834
    /**
1835
     * RSASSA-PSS-VERIFY.
1836
     *
1837
     * See {@link http://tools.ietf.org/html/rfc3447#section-8.1.2 RFC3447#section-8.1.2}.
1838
     *
1839
     * @param string $m
1840
     * @param string $s
1841
     *
1842
     * @return string
1843
     */
1844
    private function _rsassa_pss_verify($m, $s)
1845
    {
1846
        // Length checking
1847
1848
        if (strlen($s) != $this->k) {
1849
            user_error('Invalid signature');
1850
1851
            return false;
1852
        }
1853
1854
        // RSA verification
1855
1856
        $modBits = 8 * $this->k;
1857
1858
        $s2 = $this->_os2ip($s);
1859
        $m2 = $this->_rsavp1($s2);
1860
        if ($m2 === false) {
1861
            user_error('Invalid signature');
1862
1863
            return false;
1864
        }
1865
        $em = $this->_i2osp($m2, $modBits >> 3);
1866
        if ($em === false) {
1867
            user_error('Invalid signature');
1868
1869
            return false;
1870
        }
1871
1872
        // EMSA-PSS verification
1873
1874
        return $this->_emsa_pss_verify($m, $em, $modBits - 1);
1875
    }
1876
1877
    /**
1878
     * EMSA-PKCS1-V1_5-ENCODE.
1879
     *
1880
     * See {@link http://tools.ietf.org/html/rfc3447#section-9.2 RFC3447#section-9.2}.
1881
     *
1882
     * @param string $m
1883
     * @param int    $emLen
1884
     *
1885
     * @return string
1886
     */
1887
    private function _emsa_pkcs1_v1_5_encode($m, $emLen)
1888
    {
1889
        $h = $this->hash->hash($m);
1890
        if ($h === false) {
1891
            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...
1892
        }
1893
1894
        // see http://tools.ietf.org/html/rfc3447#page-43
1895
        switch ($this->hashName) {
1896
            case 'md2':
1897
                $t = pack('H*', '3020300c06082a864886f70d020205000410');
1898
                break;
1899
            case 'md5':
1900
                $t = pack('H*', '3020300c06082a864886f70d020505000410');
1901
                break;
1902
            case 'sha1':
1903
                $t = pack('H*', '3021300906052b0e03021a05000414');
1904
                break;
1905
            case 'sha256':
1906
                $t = pack('H*', '3031300d060960864801650304020105000420');
1907
                break;
1908
            case 'sha384':
1909
                $t = pack('H*', '3041300d060960864801650304020205000430');
1910
                break;
1911
            case 'sha512':
1912
                $t = pack('H*', '3051300d060960864801650304020305000440');
1913
        }
1914
        $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...
1915
        $tLen = strlen($t);
1916
1917
        if ($emLen < $tLen + 11) {
1918
            user_error('Intended encoded message length too short');
1919
1920
            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...
1921
        }
1922
1923
        $ps = str_repeat(chr(0xFF), $emLen - $tLen - 3);
1924
1925
        $em = "\0\1$ps\0$t";
1926
1927
        return $em;
1928
    }
1929
1930
    /**
1931
     * RSASSA-PKCS1-V1_5-SIGN.
1932
     *
1933
     * See {@link http://tools.ietf.org/html/rfc3447#section-8.2.1 RFC3447#section-8.2.1}.
1934
     *
1935
     * @param string $m
1936
     *
1937
     * @return string
1938
     */
1939
    private function _rsassa_pkcs1_v1_5_sign($m)
1940
    {
1941
        // EMSA-PKCS1-v1_5 encoding
1942
1943
        $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...
1944
        if ($em === false) {
1945
            user_error('RSA modulus too short');
1946
1947
            return false;
1948
        }
1949
1950
        // RSA signature
1951
1952
        $m = $this->_os2ip($em);
1953
        $s = $this->_rsasp1($m);
1954
        $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...
1955
1956
        // Output the signature S
1957
1958
        return $s;
1959
    }
1960
1961
    /**
1962
     * RSASSA-PKCS1-V1_5-VERIFY.
1963
     *
1964
     * See {@link http://tools.ietf.org/html/rfc3447#section-8.2.2 RFC3447#section-8.2.2}.
1965
     *
1966
     * @param string $m
1967
     *
1968
     * @return string
1969
     */
1970
    private function _rsassa_pkcs1_v1_5_verify($m, $s)
1971
    {
1972
        // Length checking
1973
1974
        if (strlen($s) != $this->k) {
1975
            user_error('Invalid signature');
1976
1977
            return false;
1978
        }
1979
1980
        // RSA verification
1981
1982
        $s = $this->_os2ip($s);
1983
        $m2 = $this->_rsavp1($s);
1984
        if ($m2 === false) {
1985
            user_error('Invalid signature');
1986
1987
            return false;
1988
        }
1989
        $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...
1990
        if ($em === false) {
1991
            user_error('Invalid signature');
1992
1993
            return false;
1994
        }
1995
1996
        // EMSA-PKCS1-v1_5 encoding
1997
1998
        $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...
1999
        if ($em2 === false) {
2000
            user_error('RSA modulus too short');
2001
2002
            return false;
2003
        }
2004
2005
        // Compare
2006
        return $this->_equals($em, $em2);
2007
    }
2008
2009
    /**
2010
     * Set Encryption Mode.
2011
     *
2012
     * Valid values include self::ENCRYPTION_OAEP and self::ENCRYPTION_PKCS1.
2013
     *
2014
     * @param int $mode
2015
     */
2016
    public function setEncryptionMode($mode)
2017
    {
2018
        $this->encryptionMode = $mode;
2019
    }
2020
2021
    /**
2022
     * Set Signature Mode.
2023
     *
2024
     * Valid values include self::SIGNATURE_PSS and self::SIGNATURE_PKCS1
2025
     *
2026
     * @param int $mode
2027
     */
2028
    public function setSignatureMode($mode)
2029
    {
2030
        $this->signatureMode = $mode;
2031
    }
2032
2033
    /**
2034
     * Encryption.
2035
     *
2036
     * Both self::ENCRYPTION_OAEP and self::ENCRYPTION_PKCS1 both place limits on how long $plaintext can be.
2037
     * If $plaintext exceeds those limits it will be broken up so that it does and the resultant ciphertext's will
2038
     * be concatenated together.
2039
     *
2040
     * @see self::decrypt()
2041
     *
2042
     * @param string $plaintext
2043
     *
2044
     * @return string
2045
     */
2046
    public function encrypt($plaintext)
2047
    {
2048
        switch ($this->encryptionMode) {
2049
            case self::ENCRYPTION_NONE:
2050
                $plaintext = str_split($plaintext, $this->k);
2051
                $ciphertext = '';
2052
                foreach ($plaintext as $m) {
2053
                    $ciphertext .= $this->_raw_encrypt($m);
2054
                }
2055
2056
                return $ciphertext;
2057
            case self::ENCRYPTION_PKCS1:
2058
                $length = $this->k - 11;
2059
                if ($length <= 0) {
2060
                    return false;
2061
                }
2062
2063
                $plaintext = str_split($plaintext, $length);
2064
                $ciphertext = '';
2065
                foreach ($plaintext as $m) {
2066
                    $ciphertext .= $this->_rsaes_pkcs1_v1_5_encrypt($m);
2067
                }
2068
2069
                return $ciphertext;
2070
            //case self::ENCRYPTION_OAEP:
2071
            default:
2072
                $length = $this->k - 2 * $this->hLen - 2;
2073
                if ($length <= 0) {
2074
                    return false;
2075
                }
2076
2077
                $plaintext = str_split($plaintext, $length);
2078
                $ciphertext = '';
2079
                foreach ($plaintext as $m) {
2080
                    $ciphertext .= $this->_rsaes_oaep_encrypt($m);
2081
                }
2082
2083
                return $ciphertext;
2084
        }
2085
    }
2086
2087
    /**
2088
     * Decryption.
2089
     *
2090
     * @see self::encrypt()
2091
     *
2092
     * @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...
2093
     *
2094
     * @return string
2095
     */
2096
    public function decrypt($ciphertext)
2097
    {
2098
        if ($this->k <= 0) {
2099
            return false;
2100
        }
2101
2102
        $ciphertext = str_split($ciphertext, $this->k);
2103
        $ciphertext[count($ciphertext) - 1] = str_pad($ciphertext[count($ciphertext) - 1], $this->k, chr(0), STR_PAD_LEFT);
2104
2105
        $plaintext = '';
2106
2107
        switch ($this->encryptionMode) {
2108
            case self::ENCRYPTION_NONE:
2109
                $decrypt = '_raw_encrypt';
2110
                break;
2111
            case self::ENCRYPTION_PKCS1:
2112
                $decrypt = '_rsaes_pkcs1_v1_5_decrypt';
2113
                break;
2114
            //case self::ENCRYPTION_OAEP:
2115
            default:
2116
                $decrypt = '_rsaes_oaep_decrypt';
2117
        }
2118
2119
        foreach ($ciphertext as $c) {
2120
            $temp = $this->$decrypt($c);
2121
            if ($temp === false) {
2122
                return false;
2123
            }
2124
            $plaintext .= $temp;
2125
        }
2126
2127
        return $plaintext;
2128
    }
2129
2130
    /**
2131
     * Create a signature.
2132
     *
2133
     * @see self::verify()
2134
     *
2135
     * @param string $message
2136
     *
2137
     * @return string
2138
     */
2139
    public function sign($message)
2140
    {
2141
        if (empty($this->modulus) || empty($this->exponent)) {
2142
            return false;
2143
        }
2144
2145
        switch ($this->signatureMode) {
2146
            case self::SIGNATURE_PKCS1:
2147
                return $this->_rsassa_pkcs1_v1_5_sign($message);
2148
            //case self::SIGNATURE_PSS:
2149
            default:
2150
                return $this->_rsassa_pss_sign($message);
2151
        }
2152
    }
2153
2154
    /**
2155
     * Verifies a signature.
2156
     *
2157
     * @see self::sign()
2158
     *
2159
     * @param string $message
2160
     * @param string $signature
2161
     *
2162
     * @return bool
2163
     */
2164
    public function verify($message, $signature)
2165
    {
2166
        if (empty($this->modulus) || empty($this->exponent)) {
2167
            return false;
2168
        }
2169
2170
        switch ($this->signatureMode) {
2171
            case self::SIGNATURE_PKCS1:
2172
                return $this->_rsassa_pkcs1_v1_5_verify($message, $signature);
2173
            //case self::SIGNATURE_PSS:
2174
            default:
2175
                return $this->_rsassa_pss_verify($message, $signature);
2176
        }
2177
    }
2178
2179
    /**
2180
     * Extract raw BER from Base64 encoding.
2181
     *
2182
     * @param string $str
2183
     *
2184
     * @return string
2185
     */
2186
    private function _extractBER($str)
2187
    {
2188
        /* X.509 certs are assumed to be base64 encoded but sometimes they'll have additional things in them
2189
         * above and beyond the ceritificate.
2190
         * ie. some may have the following preceding the -----BEGIN CERTIFICATE----- line:
2191
         *
2192
         * Bag Attributes
2193
         *     localKeyID: 01 00 00 00
2194
         * subject=/O=organization/OU=org unit/CN=common name
2195
         * issuer=/O=organization/CN=common name
2196
         */
2197
        $temp = preg_replace('#.*?^-+[^-]+-+[\r\n ]*$#ms', '', $str, 1);
2198
        // remove the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- stuff
2199
        $temp = preg_replace('#-+[^-]+-+#', '', $temp);
2200
        // remove new lines
2201
        $temp = str_replace(["\r", "\n", ' '], '', $temp);
2202
        $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false;
2203
2204
        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...
2205
    }
2206
2207
    /**
2208
     * Defines the public key.
2209
     *
2210
     * Some private key formats define the public exponent and some don't.  Those that don't define it are problematic when
2211
     * used in certain contexts.  For example, in SSH-2, RSA authentication works by sending the public key along with a
2212
     * message signed by the private key to the server.  The SSH-2 server looks the public key up in an index of public keys
2213
     * and if it's present then proceeds to verify the signature.  Problem is, if your private key doesn't include the public
2214
     * exponent this won't work unless you manually add the public exponent. phpseclib tries to guess if the key being used
2215
     * is the public key but in the event that it guesses incorrectly you might still want to explicitly set the key as being
2216
     * public.
2217
     *
2218
     * Do note that when a new key is loaded the index will be cleared.
2219
     *
2220
     * Returns true on success, false on failure
2221
     *
2222
     * @see self::getPublicKey()
2223
     *
2224
     * @param string $key  optional
2225
     * @param int    $type optional
2226
     *
2227
     * @return bool
2228
     */
2229
    private function setPublicKey($key = false, $type = false)
2230
    {
2231
        // if a public key has already been loaded return false
2232
        if (!empty($this->publicExponent)) {
2233
            return false;
2234
        }
2235
2236
        if ($key === false && !empty($this->modulus)) {
2237
            $this->publicExponent = $this->exponent;
2238
2239
            return true;
2240
        }
2241
2242
        if ($type === false) {
2243
            $types = [
2244
                self::PUBLIC_FORMAT_RAW,
2245
                self::PUBLIC_FORMAT_PKCS1,
2246
                self::PUBLIC_FORMAT_XML,
2247
                self::PUBLIC_FORMAT_OPENSSH,
2248
            ];
2249
            foreach ($types as $type) {
2250
                $components = $this->_parseKey($key, $type);
0 ignored issues
show
Bug introduced by
It seems like $key defined by parameter $key on line 2229 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...
2251
                if ($components !== false) {
2252
                    break;
2253
                }
2254
            }
2255
        } else {
2256
            $components = $this->_parseKey($key, $type);
0 ignored issues
show
Bug introduced by
It seems like $key defined by parameter $key on line 2229 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...
2257
        }
2258
2259
        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...
2260
            return false;
2261
        }
2262
2263
        if (empty($this->modulus) || !$this->modulus->equals($components['modulus'])) {
2264
            $this->modulus = $components['modulus'];
2265
            $this->exponent = $this->publicExponent = $components['publicExponent'];
2266
2267
            return true;
2268
        }
2269
2270
        $this->publicExponent = $components['publicExponent'];
2271
2272
        return true;
2273
    }
2274
2275
    /**
2276
     * Defines the private key.
2277
     *
2278
     * If phpseclib guessed a private key was a public key and loaded it as such it might be desirable to force
2279
     * phpseclib to treat the key as a private key. This function will do that.
2280
     *
2281
     * Do note that when a new key is loaded the index will be cleared.
2282
     *
2283
     * Returns true on success, false on failure
2284
     *
2285
     * @see self::getPublicKey()
2286
     *
2287
     * @param string $key  optional
2288
     * @param int    $type optional
2289
     *
2290
     * @return bool
2291
     */
2292
    private function setPrivateKey($key = false, $type = false)
2293
    {
2294
        if ($key === false && !empty($this->publicExponent)) {
2295
            $this->publicExponent = false;
2296
2297
            return true;
2298
        }
2299
2300
        $rsa = new self();
2301
        if (!$rsa->loadKey($key, $type)) {
0 ignored issues
show
Bug introduced by
It seems like $key defined by parameter $key on line 2292 can also be of type false; however, Jose\Util\RSA::loadKey() 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...
2302
            return false;
2303
        }
2304
        $rsa->publicExponent = false;
2305
2306
        // don't overwrite the old key if the new key is invalid
2307
        $this->loadKey($rsa);
0 ignored issues
show
Documentation introduced by
$rsa is of type object<Jose\Util\RSA>, but the function expects a string.

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...
2308
2309
        return true;
2310
    }
2311
}
2312