Issues (365)

src/Library/libraries/phpseclib/Crypt/RSA.php (31 issues)

1
<?php
2
3
/**
4
 * Pure-PHP PKCS#1 (v2.1) compliant implementation of RSA.
5
 *
6
 * PHP version 5
7
 *
8
 * Here's an example of how to encrypt and decrypt text with this library:
9
 * <code>
10
 * <?php
11
 *    include 'vendor/autoload.php';
12
 *
13
 *    $rsa = new \phpseclib\Crypt\RSA();
14
 *    extract($rsa->createKey());
15
 *
16
 *    $plaintext = 'terrafrost';
17
 *
18
 *    $rsa->loadKey($privatekey);
19
 *    $ciphertext = $rsa->encrypt($plaintext);
20
 *
21
 *    $rsa->loadKey($publickey);
22
 *    echo $rsa->decrypt($ciphertext);
23
 * ?>
24
 * </code>
25
 *
26
 * Here's an example of how to create signatures and verify signatures with this library:
27
 * <code>
28
 * <?php
29
 *    include 'vendor/autoload.php';
30
 *
31
 *    $rsa = new \phpseclib\Crypt\RSA();
32
 *    extract($rsa->createKey());
33
 *
34
 *    $plaintext = 'terrafrost';
35
 *
36
 *    $rsa->loadKey($privatekey);
37
 *    $signature = $rsa->sign($plaintext);
38
 *
39
 *    $rsa->loadKey($publickey);
40
 *    echo $rsa->verify($plaintext, $signature) ? 'verified' : 'unverified';
41
 * ?>
42
 * </code>
43
 *
44
 * @category  Crypt
45
 * @package   RSA
46
 * @author    Jim Wigginton <[email protected]>
47
 * @copyright 2009 Jim Wigginton
48
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
49
 * @link      http://phpseclib.sourceforge.net
50
 */
51
52
namespace phpseclib\Crypt;
53
54
use phpseclib\Math\BigInteger;
55
56
/**
57
 * Pure-PHP PKCS#1 compliant implementation of RSA.
58
 *
59
 * @package RSA
60
 * @author  Jim Wigginton <[email protected]>
61
 * @access  public
62
 */
63
class RSA
64
{
65
    /**#@+
66
     * @access public
67
     * @see self::encrypt()
68
     * @see self::decrypt()
69
     */
70
    /**
71
     * Use {@link http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding Optimal Asymmetric Encryption Padding}
72
     * (OAEP) for encryption / decryption.
73
     *
74
     * Uses sha256 by default.
75
     *
76
     * @see self::setHash()
77
     * @see self::setMGFHash()
78
     */
79
    public const ENCRYPTION_OAEP = 1;
80
    /**
81
     * Use PKCS#1 padding.
82
     *
83
     * Although self::ENCRYPTION_OAEP offers more security, including PKCS#1 padding is necessary for purposes of backwards
84
     * compatibility with protocols (like SSH-1) written before OAEP's introduction.
85
     */
86
    public const ENCRYPTION_PKCS1 = 2;
87
    /**
88
     * Do not use any padding
89
     *
90
     * Although this method is not recommended it can none-the-less sometimes be useful if you're trying to decrypt some legacy
91
     * stuff, if you're trying to diagnose why an encrypted message isn't decrypting, etc.
92
     */
93
    public const ENCRYPTION_NONE = 3;
94
    /**#@-*/
95
96
    /**#@+
97
     * @access public
98
     * @see self::sign()
99
     * @see self::verify()
100
     * @see self::setHash()
101
    */
102
    /**
103
     * Use the Probabilistic Signature Scheme for signing
104
     *
105
     * Uses sha256 by default.
106
     *
107
     * @see self::setSaltLength()
108
     * @see self::setMGFHash()
109
     */
110
    public const SIGNATURE_PSS = 1;
111
    /**
112
     * Use the PKCS#1 scheme by default.
113
     *
114
     * Although self::SIGNATURE_PSS offers more security, including PKCS#1 signing is necessary for purposes of backwards
115
     * compatibility with protocols (like SSH-2) written before PSS's introduction.
116
     */
117
    public const SIGNATURE_PKCS1 = 2;
118
    /**#@-*/
119
120
    /**#@+
121
     * @access private
122
     * @see \phpseclib\Crypt\RSA::createKey()
123
    */
124
    /**
125
     * ASN1 Integer
126
     */
127
    public const ASN1_INTEGER = 2;
128
    /**
129
     * ASN1 Bit String
130
     */
131
    public const ASN1_BITSTRING = 3;
132
    /**
133
     * ASN1 Octet String
134
     */
135
    public const ASN1_OCTETSTRING = 4;
136
    /**
137
     * ASN1 Object Identifier
138
     */
139
    public const ASN1_OBJECT = 6;
140
    /**
141
     * ASN1 Sequence (with the constucted bit set)
142
     */
143
    public const ASN1_SEQUENCE = 48;
144
    /**#@-*/
145
146
    /**#@+
147
     * @access private
148
     * @see \phpseclib\Crypt\RSA::__construct()
149
    */
150
    /**
151
     * To use the pure-PHP implementation
152
     */
153
    public const MODE_INTERNAL = 1;
154
    /**
155
     * To use the OpenSSL library
156
     *
157
     * (if enabled; otherwise, the internal implementation will be used)
158
     */
159
    public const MODE_OPENSSL = 2;
160
    /**#@-*/
161
162
    /**#@+
163
     * @access public
164
     * @see \phpseclib\Crypt\RSA::createKey()
165
     * @see \phpseclib\Crypt\RSA::setPrivateKeyFormat()
166
    */
167
    /**
168
     * PKCS#1 formatted private key
169
     *
170
     * Used by OpenSSH
171
     */
172
    public const PRIVATE_FORMAT_PKCS1 = 0;
173
    /**
174
     * PuTTY formatted private key
175
     */
176
    public const PRIVATE_FORMAT_PUTTY = 1;
177
    /**
178
     * XML formatted private key
179
     */
180
    public const PRIVATE_FORMAT_XML = 2;
181
    /**
182
     * PKCS#8 formatted private key
183
     */
184
    public const PRIVATE_FORMAT_PKCS8 = 8;
185
    /**#@-*/
186
187
    /**#@+
188
     * @access public
189
     * @see \phpseclib\Crypt\RSA::createKey()
190
     * @see \phpseclib\Crypt\RSA::setPublicKeyFormat()
191
    */
192
    /**
193
     * Raw public key
194
     *
195
     * An array containing two \phpseclib\Math\BigInteger objects.
196
     *
197
     * The exponent can be indexed with any of the following:
198
     *
199
     * 0, e, exponent, publicExponent
200
     *
201
     * The modulus can be indexed with any of the following:
202
     *
203
     * 1, n, modulo, modulus
204
     */
205
    public const PUBLIC_FORMAT_RAW = 3;
206
    /**
207
     * PKCS#1 formatted public key (raw)
208
     *
209
     * Used by File/X509.php
210
     *
211
     * Has the following header:
212
     *
213
     * -----BEGIN RSA PUBLIC KEY-----
214
     *
215
     * Analogous to ssh-keygen's pem format (as specified by -m)
216
     */
217
    public const PUBLIC_FORMAT_PKCS1 = 4;
218
    public const PUBLIC_FORMAT_PKCS1_RAW = 4;
219
    /**
220
     * XML formatted public key
221
     */
222
    public const PUBLIC_FORMAT_XML = 5;
223
    /**
224
     * OpenSSH formatted public key
225
     *
226
     * Place in $HOME/.ssh/authorized_keys
227
     */
228
    public const PUBLIC_FORMAT_OPENSSH = 6;
229
    /**
230
     * PKCS#1 formatted public key (encapsulated)
231
     *
232
     * Used by PHP's openssl_public_encrypt() and openssl's rsautl (when -pubin is set)
233
     *
234
     * Has the following header:
235
     *
236
     * -----BEGIN PUBLIC KEY-----
237
     *
238
     * Analogous to ssh-keygen's pkcs8 format (as specified by -m). Although PKCS8
239
     * is specific to private keys it's basically creating a DER-encoded wrapper
240
     * for keys. This just extends that same concept to public keys (much like ssh-keygen)
241
     */
242
    public const PUBLIC_FORMAT_PKCS8 = 7;
243
    /**#@-*/
244
245
    /**
246
     * Precomputed Zero
247
     *
248
     * @var \phpseclib\Math\BigInteger
249
     * @access private
250
     */
251
    public $zero;
252
253
    /**
254
     * Precomputed One
255
     *
256
     * @var \phpseclib\Math\BigInteger
257
     * @access private
258
     */
259
    public $one;
260
261
    /**
262
     * Private Key Format
263
     *
264
     * @var int
265
     * @access private
266
     */
267
    public $privateKeyFormat = self::PRIVATE_FORMAT_PKCS1;
268
269
    /**
270
     * Public Key Format
271
     *
272
     * @var int
273
     * @access public
274
     */
275
    public $publicKeyFormat = self::PUBLIC_FORMAT_PKCS8;
276
277
    /**
278
     * Modulus (ie. n)
279
     *
280
     * @var \phpseclib\Math\BigInteger
281
     * @access private
282
     */
283
    public $modulus;
284
285
    /**
286
     * Modulus length
287
     *
288
     * @var \phpseclib\Math\BigInteger
289
     * @access private
290
     */
291
    public $k;
292
293
    /**
294
     * Exponent (ie. e or d)
295
     *
296
     * @var \phpseclib\Math\BigInteger
297
     * @access private
298
     */
299
    public $exponent;
300
301
    /**
302
     * Primes for Chinese Remainder Theorem (ie. p and q)
303
     *
304
     * @var array
305
     * @access private
306
     */
307
    public $primes;
308
309
    /**
310
     * Exponents for Chinese Remainder Theorem (ie. dP and dQ)
311
     *
312
     * @var array
313
     * @access private
314
     */
315
    public $exponents;
316
317
    /**
318
     * Coefficients for Chinese Remainder Theorem (ie. qInv)
319
     *
320
     * @var array
321
     * @access private
322
     */
323
    public $coefficients;
324
325
    /**
326
     * Hash name
327
     *
328
     * @var string
329
     * @access private
330
     */
331
    public $hashName;
332
333
    /**
334
     * Hash function
335
     *
336
     * @var \phpseclib\Crypt\Hash
337
     * @access private
338
     */
339
    public $hash;
340
341
    /**
342
     * Length of hash function output
343
     *
344
     * @var int
345
     * @access private
346
     */
347
    public $hLen;
348
349
    /**
350
     * Length of salt
351
     *
352
     * @var int
353
     * @access private
354
     */
355
    public $sLen;
356
357
    /**
358
     * Hash function for the Mask Generation Function
359
     *
360
     * @var \phpseclib\Crypt\Hash
361
     * @access private
362
     */
363
    public $mgfHash;
364
365
    /**
366
     * Length of MGF hash function output
367
     *
368
     * @var int
369
     * @access private
370
     */
371
    public $mgfHLen;
372
373
    /**
374
     * Encryption mode
375
     *
376
     * @var int
377
     * @access private
378
     */
379
    public $encryptionMode = self::ENCRYPTION_OAEP;
380
381
    /**
382
     * Signature mode
383
     *
384
     * @var int
385
     * @access private
386
     */
387
    public $signatureMode = self::SIGNATURE_PSS;
388
389
    /**
390
     * Public Exponent
391
     *
392
     * @var mixed
393
     * @access private
394
     */
395
    public $publicExponent = false;
396
397
    /**
398
     * Password
399
     *
400
     * @var string
401
     * @access private
402
     */
403
    public $password = false;
404
405
    /**
406
     * Components
407
     *
408
     * For use with parsing XML formatted keys.  PHP's XML Parser functions use utilized - instead of PHP's DOM functions -
409
     * because PHP's XML Parser functions work on PHP4 whereas PHP's DOM functions - although surperior - don't.
410
     *
411
     * @see self::_start_element_handler()
412
     * @var array
413
     * @access private
414
     */
415
    public $components = array();
416
417
    /**
418
     * Current String
419
     *
420
     * For use with parsing XML formatted keys.
421
     *
422
     * @see self::_character_handler()
423
     * @see self::_stop_element_handler()
424
     * @var mixed
425
     * @access private
426
     */
427
    public $current;
428
429
    /**
430
     * OpenSSL configuration file name.
431
     *
432
     * Set to null to use system configuration file.
433
     * @see self::createKey()
434
     * @var mixed
435
     * @Access public
436
     */
437
    public $configFile;
438
439
    /**
440
     * Public key comment field.
441
     *
442
     * @var string
443
     * @access private
444
     */
445
    public $comment = 'phpseclib-generated-key';
446
447
    /**
448
     * The constructor
449
     *
450
     * If you want to make use of the openssl extension, you'll need to set the mode manually, yourself.  The reason
451
     * \phpseclib\Crypt\RSA doesn't do it is because OpenSSL doesn't fail gracefully.  openssl_pkey_new(), in particular, requires
452
     * openssl.cnf be present somewhere and, unfortunately, the only real way to find out is too late.
453
     *
454
     * @return \phpseclib\Crypt\RSA
455
     * @access public
456
     */
457
    public function __construct()
458
    {
459
        $this->configFile = dirname(__FILE__) . '/../openssl.cnf';
460
461
        if (!defined('CRYPT_RSA_MODE')) {
462
            switch (true) {
463
                // Math/BigInteger's openssl requirements are a little less stringent than Crypt/RSA's. in particular,
464
                // Math/BigInteger doesn't require an openssl.cfg file whereas Crypt/RSA does. so if Math/BigInteger
465
                // can't use OpenSSL it can be pretty trivially assumed, then, that Crypt/RSA can't either.
466
                case defined('MATH_BIGINTEGER_OPENSSL_DISABLE'):
467
                    define('CRYPT_RSA_MODE', self::MODE_INTERNAL);
468
                    break;
469
                case extension_loaded('openssl') && file_exists($this->configFile):
470
                    // some versions of XAMPP have mismatched versions of OpenSSL which causes it not to work
471
                    define('CRYPT_RSA_MODE', self::MODE_OPENSSL);
472
                    break;
473
                default:
474
                    define('CRYPT_RSA_MODE', self::MODE_INTERNAL);
475
            }
476
        }
477
478
        $this->zero = new BigInteger();
479
        $this->one = new BigInteger(1);
480
481
        $this->hash = new Hash('sha256');
482
        $this->hLen = $this->hash->getLength();
483
        $this->hashName = 'sha256';
484
        $this->mgfHash = new Hash('sha256');
485
        $this->mgfHLen = $this->mgfHash->getLength();
486
    }
487
488
    /**
489
     * Create public / private key pair
490
     *
491
     * Returns an array with the following three elements:
492
     *  - 'privatekey': The private key.
493
     *  - 'publickey':  The public key.
494
     *  - 'partialkey': A partially computed key (if the execution time exceeded $timeout).
495
     *                  Will need to be passed back to \phpseclib\Crypt\RSA::createKey() as the third parameter for further processing.
496
     *
497
     * @access public
498
     * @param int $bits
499
     * @param int $timeout
500
     * @param array $p
501
     */
502
    public function createKey($bits = 1024, $timeout = false, $partial = array())
503
    {
504
        if (!defined('CRYPT_RSA_EXPONENT')) {
505
            // http://en.wikipedia.org/wiki/65537_%28number%29
506
            define('CRYPT_RSA_EXPONENT', '65537');
507
        }
508
        // per <http://cseweb.ucsd.edu/~hovav/dist/survey.pdf#page=5>, this number ought not result in primes smaller
509
        // than 256 bits. as a consequence if the key you're trying to create is 1024 bits and you've set CRYPT_RSA_SMALLEST_PRIME
510
        // to 384 bits then you're going to get a 384 bit prime and a 640 bit prime (384 + 1024 % 384). at least if
511
        // CRYPT_RSA_MODE is set to self::MODE_INTERNAL. if CRYPT_RSA_MODE is set to self::MODE_OPENSSL then
512
        // CRYPT_RSA_SMALLEST_PRIME is ignored (ie. multi-prime RSA support is more intended as a way to speed up RSA key
513
        // generation when there's a chance neither gmp nor OpenSSL are installed)
514
        if (!defined('CRYPT_RSA_SMALLEST_PRIME')) {
515
            define('CRYPT_RSA_SMALLEST_PRIME', 4096);
516
        }
517
518
        // OpenSSL uses 65537 as the exponent and requires RSA keys be 384 bits minimum
519
        if (CRYPT_RSA_MODE == self::MODE_OPENSSL && $bits >= 384 && CRYPT_RSA_EXPONENT == 65537) {
520
            $config = array();
521
            if (isset($this->configFile)) {
522
                $config['config'] = $this->configFile;
523
            }
524
            $rsa = openssl_pkey_new(array('private_key_bits' => $bits) + $config);
525
            openssl_pkey_export($rsa, $privatekey, null, $config);
526
            $publickey = openssl_pkey_get_details($rsa);
527
            $publickey = $publickey['key'];
528
529
            $privatekey = call_user_func_array(array($this, '_convertPrivateKey'), array_values($this->_parseKey($privatekey, self::PRIVATE_FORMAT_PKCS1)));
530
            $publickey = call_user_func_array(array($this, '_convertPublicKey'), array_values($this->_parseKey($publickey, self::PUBLIC_FORMAT_PKCS1)));
531
532
            // clear the buffer of error strings stemming from a minimalistic openssl.cnf
533
            while (openssl_error_string() !== false) {
534
            }
535
536
            return array(
537
                'privatekey' => $privatekey,
538
                'publickey' => $publickey,
539
                'partialkey' => false
540
            );
541
        }
542
543
        static $e;
544
        if (!isset($e)) {
545
            $e = new BigInteger(CRYPT_RSA_EXPONENT);
546
        }
547
548
        extract($this->_generateMinMax($bits));
549
        $absoluteMin = $min;
550
        $temp = $bits >> 1; // divide by two to see how many bits P and Q would be
551
        if ($temp > CRYPT_RSA_SMALLEST_PRIME) {
552
            $num_primes = floor($bits / CRYPT_RSA_SMALLEST_PRIME);
553
            $temp = CRYPT_RSA_SMALLEST_PRIME;
554
        } else {
555
            $num_primes = 2;
556
        }
557
        extract($this->_generateMinMax($temp + $bits % $temp));
558
        $finalMax = $max;
559
        extract($this->_generateMinMax($temp));
560
561
        $generator = new BigInteger();
562
563
        $n = $this->one->copy();
564
        if (!empty($partial)) {
565
            extract(unserialize($partial));
566
        } else {
567
            $exponents = $coefficients = $primes = array();
568
            $lcm = array(
569
                'top' => $this->one->copy(),
570
                'bottom' => false
571
            );
572
        }
573
574
        $start = time();
575
        $i0 = count($primes) + 1;
576
577
        do {
578
            for ($i = $i0; $i <= $num_primes; $i++) {
579
                if ($timeout !== false) {
580
                    $timeout-= time() - $start;
581
                    $start = time();
582
                    if ($timeout <= 0) {
583
                        return array(
584
                            'privatekey' => '',
585
                            'publickey'  => '',
586
                            'partialkey' => serialize(array(
587
                                'primes' => $primes,
588
                                'coefficients' => $coefficients,
589
                                'lcm' => $lcm,
590
                                'exponents' => $exponents
591
                            ))
592
                        );
593
                    }
594
                }
595
596
                if ($i == $num_primes) {
597
                    list($min, $temp) = $absoluteMin->divide($n);
598
                    if (!$temp->equals($this->zero)) {
599
                        $min = $min->add($this->one); // ie. ceil()
600
                    }
601
                    $primes[$i] = $generator->randomPrime($min, $finalMax, $timeout);
0 ignored issues
show
It seems like $timeout can also be of type false; however, parameter $timeout of phpseclib\Math\BigInteger::randomPrime() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

601
                    $primes[$i] = $generator->randomPrime($min, $finalMax, /** @scrutinizer ignore-type */ $timeout);
Loading history...
602
                } else {
603
                    $primes[$i] = $generator->randomPrime($min, $max, $timeout);
604
                }
605
606
                if ($primes[$i] === false) { // if we've reached the timeout
607
                    if (count($primes) > 1) {
608
                        $partialkey = '';
609
                    } else {
610
                        array_pop($primes);
611
                        $partialkey = serialize(array(
612
                            'primes' => $primes,
613
                            'coefficients' => $coefficients,
614
                            'lcm' => $lcm,
615
                            'exponents' => $exponents
616
                        ));
617
                    }
618
619
                    return array(
620
                        'privatekey' => '',
621
                        'publickey'  => '',
622
                        'partialkey' => $partialkey
623
                    );
624
                }
625
626
                // the first coefficient is calculated differently from the rest
627
                // ie. instead of being $primes[1]->modInverse($primes[2]), it's $primes[2]->modInverse($primes[1])
628
                if ($i > 2) {
629
                    $coefficients[$i] = $n->modInverse($primes[$i]);
630
                }
631
632
                $n = $n->multiply($primes[$i]);
633
634
                $temp = $primes[$i]->subtract($this->one);
635
636
                // textbook RSA implementations use Euler's totient function instead of the least common multiple.
637
                // see http://en.wikipedia.org/wiki/Euler%27s_totient_function
638
                $lcm['top'] = $lcm['top']->multiply($temp);
639
                $lcm['bottom'] = $lcm['bottom'] === false ? $temp : $lcm['bottom']->gcd($temp);
640
641
                $exponents[$i] = $e->modInverse($temp);
642
            }
643
644
            list($temp) = $lcm['top']->divide($lcm['bottom']);
645
            $gcd = $temp->gcd($e);
646
            $i0 = 1;
647
        } while (!$gcd->equals($this->one));
648
649
        $d = $e->modInverse($temp);
650
651
        $coefficients[2] = $primes[2]->modInverse($primes[1]);
652
653
        // from <http://tools.ietf.org/html/rfc3447#appendix-A.1.2>:
654
        // RSAPrivateKey ::= SEQUENCE {
655
        //     version           Version,
656
        //     modulus           INTEGER,  -- n
657
        //     publicExponent    INTEGER,  -- e
658
        //     privateExponent   INTEGER,  -- d
659
        //     prime1            INTEGER,  -- p
660
        //     prime2            INTEGER,  -- q
661
        //     exponent1         INTEGER,  -- d mod (p-1)
662
        //     exponent2         INTEGER,  -- d mod (q-1)
663
        //     coefficient       INTEGER,  -- (inverse of q) mod p
664
        //     otherPrimeInfos   OtherPrimeInfos OPTIONAL
665
        // }
666
667
        return array(
668
            'privatekey' => $this->_convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients),
669
            'publickey'  => $this->_convertPublicKey($n, $e),
670
            'partialkey' => false
671
        );
672
    }
673
674
    /**
675
     * Convert a private key to the appropriate format.
676
     *
677
     * @access private
678
     * @see self::setPrivateKeyFormat()
679
     * @param string $RSAPrivateKey
680
     * @return string
681
     */
682
    public function _convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients)
683
    {
684
        $signed = $this->privateKeyFormat != self::PRIVATE_FORMAT_XML;
685
        $num_primes = count($primes);
686
        $raw = array(
687
            'version' => $num_primes == 2 ? chr(0) : chr(1), // two-prime vs. multi
688
            'modulus' => $n->toBytes($signed),
689
            'publicExponent' => $e->toBytes($signed),
690
            'privateExponent' => $d->toBytes($signed),
691
            'prime1' => $primes[1]->toBytes($signed),
692
            'prime2' => $primes[2]->toBytes($signed),
693
            'exponent1' => $exponents[1]->toBytes($signed),
694
            'exponent2' => $exponents[2]->toBytes($signed),
695
            'coefficient' => $coefficients[2]->toBytes($signed)
696
        );
697
698
        // if the format in question does not support multi-prime rsa and multi-prime rsa was used,
699
        // call _convertPublicKey() instead.
700
        switch ($this->privateKeyFormat) {
701
            case self::PRIVATE_FORMAT_XML:
702
                if ($num_primes != 2) {
703
                    return false;
704
                }
705
                return "<RSAKeyValue>\r\n" .
706
                       '  <Modulus>' . base64_encode($raw['modulus']) . "</Modulus>\r\n" .
707
                       '  <Exponent>' . base64_encode($raw['publicExponent']) . "</Exponent>\r\n" .
708
                       '  <P>' . base64_encode($raw['prime1']) . "</P>\r\n" .
709
                       '  <Q>' . base64_encode($raw['prime2']) . "</Q>\r\n" .
710
                       '  <DP>' . base64_encode($raw['exponent1']) . "</DP>\r\n" .
711
                       '  <DQ>' . base64_encode($raw['exponent2']) . "</DQ>\r\n" .
712
                       '  <InverseQ>' . base64_encode($raw['coefficient']) . "</InverseQ>\r\n" .
713
                       '  <D>' . base64_encode($raw['privateExponent']) . "</D>\r\n" .
714
                       '</RSAKeyValue>';
715
                break;
0 ignored issues
show
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
716
            case self::PRIVATE_FORMAT_PUTTY:
717
                if ($num_primes != 2) {
718
                    return false;
719
                }
720
                $key = "PuTTY-User-Key-File-2: ssh-rsa\r\nEncryption: ";
721
                $encryption = (!empty($this->password) || is_string($this->password)) ? 'aes256-cbc' : 'none';
722
                $key.= $encryption;
723
                $key.= "\r\nComment: " . $this->comment . "\r\n";
724
                $public = pack(
725
                    'Na*Na*Na*',
726
                    strlen('ssh-rsa'),
727
                    'ssh-rsa',
728
                    strlen($raw['publicExponent']),
729
                    $raw['publicExponent'],
730
                    strlen($raw['modulus']),
731
                    $raw['modulus']
732
                );
733
                $source = pack(
734
                    'Na*Na*Na*Na*',
735
                    strlen('ssh-rsa'),
736
                    'ssh-rsa',
737
                    strlen($encryption),
738
                    $encryption,
739
                    strlen($this->comment),
740
                    $this->comment,
741
                    strlen($public),
742
                    $public
743
                );
744
                $public = base64_encode($public);
745
                $key.= "Public-Lines: " . ((strlen($public) + 63) >> 6) . "\r\n";
746
                $key.= chunk_split($public, 64);
747
                $private = pack(
748
                    'Na*Na*Na*Na*',
749
                    strlen($raw['privateExponent']),
750
                    $raw['privateExponent'],
751
                    strlen($raw['prime1']),
752
                    $raw['prime1'],
753
                    strlen($raw['prime2']),
754
                    $raw['prime2'],
755
                    strlen($raw['coefficient']),
756
                    $raw['coefficient']
757
                );
758
                if (empty($this->password) && !is_string($this->password)) {
759
                    $source.= pack('Na*', strlen($private), $private);
760
                    $hashkey = 'putty-private-key-file-mac-key';
761
                } else {
762
                    $private.= Random::string(16 - (strlen($private) & 15));
763
                    $source.= pack('Na*', strlen($private), $private);
764
                    $sequence = 0;
765
                    $symkey = '';
766
                    while (strlen($symkey) < 32) {
767
                        $temp = pack('Na*', $sequence++, $this->password);
768
                        $symkey.= pack('H*', hash("sha256", $temp));
769
                    }
770
                    $symkey = substr($symkey, 0, 32);
771
                    $crypto = new AES();
772
773
                    $crypto->setKey($symkey);
774
                    $crypto->disablePadding();
775
                    $private = $crypto->encrypt($private);
776
                    $hashkey = 'putty-private-key-file-mac-key' . $this->password;
777
                }
778
779
                $private = base64_encode($private);
780
                $key.= 'Private-Lines: ' . ((strlen($private) + 63) >> 6) . "\r\n";
781
                $key.= chunk_split($private, 64);
782
                $hash = new Hash('sha256');
783
                $hash->setKey(pack('H*', hash("sha256", $hashkey)));
784
                $key.= 'Private-MAC: ' . bin2hex($hash->hash($source)) . "\r\n";
785
786
                return $key;
787
            default: // eg. self::PRIVATE_FORMAT_PKCS1
788
                $components = array();
789
                foreach ($raw as $name => $value) {
790
                    $components[$name] = pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($value)), $value);
791
                }
792
793
                $RSAPrivateKey = implode('', $components);
794
795
                if ($num_primes > 2) {
796
                    $OtherPrimeInfos = '';
797
                    for ($i = 3; $i <= $num_primes; $i++) {
798
                        // OtherPrimeInfos ::= SEQUENCE SIZE(1..MAX) OF OtherPrimeInfo
799
                        //
800
                        // OtherPrimeInfo ::= SEQUENCE {
801
                        //     prime             INTEGER,  -- ri
802
                        //     exponent          INTEGER,  -- di
803
                        //     coefficient       INTEGER   -- ti
804
                        // }
805
                        $OtherPrimeInfo = pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($primes[$i]->toBytes(true))), $primes[$i]->toBytes(true));
806
                        $OtherPrimeInfo.= pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($exponents[$i]->toBytes(true))), $exponents[$i]->toBytes(true));
807
                        $OtherPrimeInfo.= pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($coefficients[$i]->toBytes(true))), $coefficients[$i]->toBytes(true));
808
                        $OtherPrimeInfos.= pack('Ca*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($OtherPrimeInfo)), $OtherPrimeInfo);
809
                    }
810
                    $RSAPrivateKey.= pack('Ca*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($OtherPrimeInfos)), $OtherPrimeInfos);
811
                }
812
813
                $RSAPrivateKey = pack('Ca*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey);
814
815
                if ($this->privateKeyFormat == self::PRIVATE_FORMAT_PKCS8) {
816
                    $rsaOID = pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA
817
                    $RSAPrivateKey = pack(
818
                        'Ca*a*Ca*a*',
819
                        self::ASN1_INTEGER,
820
                        "\01\00",
821
                        $rsaOID,
822
                        4,
823
                        $this->_encodeLength(strlen($RSAPrivateKey)),
824
                        $RSAPrivateKey
825
                    );
826
                    $RSAPrivateKey = pack('Ca*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey);
827
                    if (!empty($this->password) || is_string($this->password)) {
828
                        $salt = Random::string(8);
829
                        $iterationCount = 2048;
830
831
                        $crypto = new DES();
832
                        $crypto->setPassword($this->password, 'pbkdf1', 'sha256', $salt, $iterationCount);
833
                        $RSAPrivateKey = $crypto->encrypt($RSAPrivateKey);
834
835
                        $parameters = pack(
836
                            'Ca*a*Ca*N',
837
                            self::ASN1_OCTETSTRING,
838
                            $this->_encodeLength(strlen($salt)),
839
                            $salt,
840
                            self::ASN1_INTEGER,
841
                            $this->_encodeLength(4),
842
                            $iterationCount
843
                        );
844
                        $pbeWithMD5AndDES_CBC = "\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03";
845
846
                        $encryptionAlgorithm = pack(
847
                            'Ca*a*Ca*a*',
848
                            self::ASN1_OBJECT,
849
                            $this->_encodeLength(strlen($pbeWithMD5AndDES_CBC)),
850
                            $pbeWithMD5AndDES_CBC,
851
                            self::ASN1_SEQUENCE,
852
                            $this->_encodeLength(strlen($parameters)),
853
                            $parameters
854
                        );
855
856
                        $RSAPrivateKey = pack(
857
                            'Ca*a*Ca*a*',
858
                            self::ASN1_SEQUENCE,
859
                            $this->_encodeLength(strlen($encryptionAlgorithm)),
860
                            $encryptionAlgorithm,
861
                            self::ASN1_OCTETSTRING,
862
                            $this->_encodeLength(strlen($RSAPrivateKey)),
863
                            $RSAPrivateKey
864
                        );
865
866
                        $RSAPrivateKey = pack('Ca*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey);
867
868
                        $RSAPrivateKey = "-----BEGIN ENCRYPTED PRIVATE KEY-----\r\n" .
869
                                         chunk_split(base64_encode($RSAPrivateKey), 64) .
870
                                         '-----END ENCRYPTED PRIVATE KEY-----';
871
                    } else {
872
                        $RSAPrivateKey = "-----BEGIN PRIVATE KEY-----\r\n" .
873
                                         chunk_split(base64_encode($RSAPrivateKey), 64) .
874
                                         '-----END PRIVATE KEY-----';
875
                    }
876
                    return $RSAPrivateKey;
877
                }
878
879
                if (!empty($this->password) || is_string($this->password)) {
880
                    $iv = Random::string(8);
881
                    $symkey = pack('H*', hash("sha256", $this->password . $iv)); // symkey is short for symmetric key
882
                    $symkey.= substr(pack('H*', hash("sha256", $symkey . $this->password . $iv)), 0, 8);
883
                    $des = new TripleDES();
884
                    $des->setKey($symkey);
885
                    $des->setIV($iv);
886
                    $iv = strtoupper(bin2hex($iv));
887
                    $RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" .
888
                                     "Proc-Type: 4,ENCRYPTED\r\n" .
889
                                     "DEK-Info: DES-EDE3-CBC,$iv\r\n" .
890
                                     "\r\n" .
891
                                     chunk_split(base64_encode($des->encrypt($RSAPrivateKey)), 64) .
892
                                     '-----END RSA PRIVATE KEY-----';
893
                } else {
894
                    $RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" .
895
                                     chunk_split(base64_encode($RSAPrivateKey), 64) .
896
                                     '-----END RSA PRIVATE KEY-----';
897
                }
898
899
                return $RSAPrivateKey;
900
        }
901
    }
902
903
    /**
904
     * Convert a public key to the appropriate format
905
     *
906
     * @access private
907
     * @see self::setPublicKeyFormat()
908
     * @param string $RSAPrivateKey
909
     * @return string
910
     */
911
    public function _convertPublicKey($n, $e)
912
    {
913
        $signed = $this->publicKeyFormat != self::PUBLIC_FORMAT_XML;
914
915
        $modulus = $n->toBytes($signed);
916
        $publicExponent = $e->toBytes($signed);
917
918
        switch ($this->publicKeyFormat) {
919
            case self::PUBLIC_FORMAT_RAW:
920
                return array('e' => $e->copy(), 'n' => $n->copy());
921
            case self::PUBLIC_FORMAT_XML:
922
                return "<RSAKeyValue>\r\n" .
923
                       '  <Modulus>' . base64_encode($modulus) . "</Modulus>\r\n" .
924
                       '  <Exponent>' . base64_encode($publicExponent) . "</Exponent>\r\n" .
925
                       '</RSAKeyValue>';
926
                break;
0 ignored issues
show
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
927
            case self::PUBLIC_FORMAT_OPENSSH:
928
                // from <http://tools.ietf.org/html/rfc4253#page-15>:
929
                // string    "ssh-rsa"
930
                // mpint     e
931
                // mpint     n
932
                $RSAPublicKey = pack('Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($publicExponent), $publicExponent, strlen($modulus), $modulus);
933
                $RSAPublicKey = 'ssh-rsa ' . base64_encode($RSAPublicKey) . ' ' . $this->comment;
934
935
                return $RSAPublicKey;
936
            default: // eg. self::PUBLIC_FORMAT_PKCS1_RAW or self::PUBLIC_FORMAT_PKCS1
937
                // from <http://tools.ietf.org/html/rfc3447#appendix-A.1.1>:
938
                // RSAPublicKey ::= SEQUENCE {
939
                //     modulus           INTEGER,  -- n
940
                //     publicExponent    INTEGER   -- e
941
                // }
942
                $components = array(
943
                    'modulus' => pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($modulus)), $modulus),
944
                    'publicExponent' => pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($publicExponent)), $publicExponent)
945
                );
946
947
                $RSAPublicKey = pack(
948
                    'Ca*a*a*',
949
                    self::ASN1_SEQUENCE,
950
                    $this->_encodeLength(strlen($components['modulus']) + strlen($components['publicExponent'])),
951
                    $components['modulus'],
952
                    $components['publicExponent']
953
                );
954
955
                if ($this->publicKeyFormat == self::PUBLIC_FORMAT_PKCS1_RAW) {
956
                    $RSAPublicKey = "-----BEGIN RSA PUBLIC KEY-----\r\n" .
957
                                    chunk_split(base64_encode($RSAPublicKey), 64) .
958
                                    '-----END RSA PUBLIC KEY-----';
959
                } else {
960
                    // sequence(oid(1.2.840.113549.1.1.1), null)) = rsaEncryption.
961
                    $rsaOID = pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA
962
                    $RSAPublicKey = chr(0) . $RSAPublicKey;
963
                    $RSAPublicKey = chr(3) . $this->_encodeLength(strlen($RSAPublicKey)) . $RSAPublicKey;
964
965
                    $RSAPublicKey = pack(
966
                        'Ca*a*',
967
                        self::ASN1_SEQUENCE,
968
                        $this->_encodeLength(strlen($rsaOID . $RSAPublicKey)),
969
                        $rsaOID . $RSAPublicKey
970
                    );
971
972
                    $RSAPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" .
973
                                     chunk_split(base64_encode($RSAPublicKey), 64) .
974
                                     '-----END PUBLIC KEY-----';
975
                }
976
977
                return $RSAPublicKey;
978
        }
979
    }
980
981
    /**
982
     * Break a public or private key down into its constituant components
983
     *
984
     * @access private
985
     * @see self::_convertPublicKey()
986
     * @see self::_convertPrivateKey()
987
     * @param string $key
988
     * @param int $type
989
     * @return array
990
     */
991
    public function _parseKey($key, $type)
992
    {
993
        if ($type != self::PUBLIC_FORMAT_RAW && !is_string($key)) {
994
            return false;
995
        }
996
997
        switch ($type) {
998
            case self::PUBLIC_FORMAT_RAW:
999
                if (!is_array($key)) {
1000
                    return false;
1001
                }
1002
                $components = array();
1003
                switch (true) {
1004
                    case isset($key['e']):
1005
                        $components['publicExponent'] = $key['e']->copy();
1006
                        break;
1007
                    case isset($key['exponent']):
1008
                        $components['publicExponent'] = $key['exponent']->copy();
1009
                        break;
1010
                    case isset($key['publicExponent']):
1011
                        $components['publicExponent'] = $key['publicExponent']->copy();
1012
                        break;
1013
                    case isset($key[0]):
1014
                        $components['publicExponent'] = $key[0]->copy();
1015
                }
1016
                switch (true) {
1017
                    case isset($key['n']):
1018
                        $components['modulus'] = $key['n']->copy();
1019
                        break;
1020
                    case isset($key['modulo']):
1021
                        $components['modulus'] = $key['modulo']->copy();
1022
                        break;
1023
                    case isset($key['modulus']):
1024
                        $components['modulus'] = $key['modulus']->copy();
1025
                        break;
1026
                    case isset($key[1]):
1027
                        $components['modulus'] = $key[1]->copy();
1028
                }
1029
                return isset($components['modulus']) && isset($components['publicExponent']) ? $components : false;
1030
            case self::PRIVATE_FORMAT_PKCS1:
1031
            case self::PRIVATE_FORMAT_PKCS8:
1032
            case self::PUBLIC_FORMAT_PKCS1:
1033
                /* Although PKCS#1 proposes a format that public and private keys can use, encrypting them is
1034
                   "outside the scope" of PKCS#1.  PKCS#1 then refers you to PKCS#12 and PKCS#15 if you're wanting to
1035
                   protect private keys, however, that's not what OpenSSL* does.  OpenSSL protects private keys by adding
1036
                   two new "fields" to the key - DEK-Info and Proc-Type.  These fields are discussed here:
1037
1038
                   http://tools.ietf.org/html/rfc1421#section-4.6.1.1
1039
                   http://tools.ietf.org/html/rfc1421#section-4.6.1.3
1040
1041
                   DES-EDE3-CBC as an algorithm, however, is not discussed anywhere, near as I can tell.
1042
                   DES-CBC and DES-EDE are discussed in RFC1423, however, DES-EDE3-CBC isn't, nor is its key derivation
1043
                   function.  As is, the definitive authority on this encoding scheme isn't the IETF but rather OpenSSL's
1044
                   own implementation.  ie. the implementation *is* the standard and any bugs that may exist in that
1045
                   implementation are part of the standard, as well.
1046
1047
                   * OpenSSL is the de facto standard.  It's utilized by OpenSSH and other projects */
1048
                if (preg_match('#DEK-Info: (.+),(.+)#', $key, $matches)) {
1049
                    $iv = pack('H*', trim($matches[2]));
1050
                    $symkey = pack('H*', hash("sha256", $this->password . substr($iv, 0, 8))); // symkey is short for symmetric key
1051
                    $symkey.= pack('H*', hash("sha256", $symkey . $this->password . substr($iv, 0, 8)));
1052
                    // remove the Proc-Type / DEK-Info sections as they're no longer needed
1053
                    $key = preg_replace('#^(?:Proc-Type|DEK-Info): .*#m', '', $key);
1054
                    $ciphertext = $this->_extractBER($key);
1055
                    if ($ciphertext === false) {
1056
                        $ciphertext = $key;
1057
                    }
1058
                    switch ($matches[1]) {
1059
                        case 'AES-256-CBC':
1060
                            $crypto = new AES();
1061
                            break;
1062
                        case 'AES-128-CBC':
1063
                            $symkey = substr($symkey, 0, 16);
1064
                            $crypto = new AES();
1065
                            break;
1066
                        case 'DES-EDE3-CFB':
1067
                            $crypto = new TripleDES(Base::MODE_CFB);
1068
                            break;
1069
                        case 'DES-EDE3-CBC':
1070
                            $symkey = substr($symkey, 0, 24);
1071
                            $crypto = new TripleDES();
1072
                            break;
1073
                        case 'DES-CBC':
1074
                            $crypto = new DES();
1075
                            break;
1076
                        default:
1077
                            return false;
1078
                    }
1079
                    $crypto->setKey($symkey);
1080
                    $crypto->setIV($iv);
1081
                    $decoded = $crypto->decrypt($ciphertext);
1082
                } else {
1083
                    $decoded = $this->_extractBER($key);
1084
                }
1085
1086
                if ($decoded !== false) {
1087
                    $key = $decoded;
1088
                }
1089
1090
                $components = array();
1091
1092
                if (ord($this->_string_shift($key)) != self::ASN1_SEQUENCE) {
1093
                    return false;
1094
                }
1095
                if ($this->_decodeLength($key) != strlen($key)) {
1096
                    return false;
1097
                }
1098
1099
                $tag = ord($this->_string_shift($key));
1100
                /* intended for keys for which OpenSSL's asn1parse returns the following:
1101
1102
                    0:d=0  hl=4 l= 631 cons: SEQUENCE
1103
                    4:d=1  hl=2 l=   1 prim:  INTEGER           :00
1104
                    7:d=1  hl=2 l=  13 cons:  SEQUENCE
1105
                    9:d=2  hl=2 l=   9 prim:   OBJECT            :rsaEncryption
1106
                   20:d=2  hl=2 l=   0 prim:   NULL
1107
                   22:d=1  hl=4 l= 609 prim:  OCTET STRING
1108
1109
                   ie. PKCS8 keys*/
1110
1111
                if ($tag == self::ASN1_INTEGER && substr($key, 0, 3) == "\x01\x00\x30") {
1112
                    $this->_string_shift($key, 3);
1113
                    $tag = self::ASN1_SEQUENCE;
1114
                }
1115
1116
                if ($tag == self::ASN1_SEQUENCE) {
1117
                    $temp = $this->_string_shift($key, $this->_decodeLength($key));
1118
                    if (ord($this->_string_shift($temp)) != self::ASN1_OBJECT) {
1119
                        return false;
1120
                    }
1121
                    $length = $this->_decodeLength($temp);
1122
                    switch ($this->_string_shift($temp, $length)) {
1123
                        case "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01": // rsaEncryption
1124
                            break;
1125
                        case "\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03": // pbeWithMD5AndDES-CBC
1126
                            /*
1127
                               PBEParameter ::= SEQUENCE {
1128
                                   salt OCTET STRING (SIZE(8)),
1129
                                   iterationCount INTEGER }
1130
                            */
1131
                            if (ord($this->_string_shift($temp)) != self::ASN1_SEQUENCE) {
1132
                                return false;
1133
                            }
1134
                            if ($this->_decodeLength($temp) != strlen($temp)) {
1135
                                return false;
1136
                            }
1137
                            $this->_string_shift($temp); // assume it's an octet string
1138
                            $salt = $this->_string_shift($temp, $this->_decodeLength($temp));
1139
                            if (ord($this->_string_shift($temp)) != self::ASN1_INTEGER) {
1140
                                return false;
1141
                            }
1142
                            $this->_decodeLength($temp);
1143
                            list(, $iterationCount) = unpack('N', str_pad($temp, 4, chr(0), STR_PAD_LEFT));
1144
                            $this->_string_shift($key); // assume it's an octet string
1145
                            $length = $this->_decodeLength($key);
1146
                            if (strlen($key) != $length) {
1147
                                return false;
1148
                            }
1149
1150
                            $crypto = new DES();
1151
                            $crypto->setPassword($this->password, 'pbkdf1', 'sha256', $salt, $iterationCount);
1152
                            $key = $crypto->decrypt($key);
1153
                            if ($key === false) {
1154
                                return false;
1155
                            }
1156
                            return $this->_parseKey($key, self::PRIVATE_FORMAT_PKCS1);
1157
                        default:
1158
                            return false;
1159
                    }
1160
                    /* intended for keys for which OpenSSL's asn1parse returns the following:
1161
1162
                        0:d=0  hl=4 l= 290 cons: SEQUENCE
1163
                        4:d=1  hl=2 l=  13 cons:  SEQUENCE
1164
                        6:d=2  hl=2 l=   9 prim:   OBJECT            :rsaEncryption
1165
                       17:d=2  hl=2 l=   0 prim:   NULL
1166
                       19:d=1  hl=4 l= 271 prim:  BIT STRING */
1167
                    $tag = ord($this->_string_shift($key)); // skip over the BIT STRING / OCTET STRING tag
1168
                    $this->_decodeLength($key); // skip over the BIT STRING / OCTET STRING length
1169
                    // "The initial octet shall encode, as an unsigned binary integer wtih bit 1 as the least significant bit, the number of
1170
                    //  unused bits in the final subsequent octet. The number shall be in the range zero to seven."
1171
                    //  -- http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf (section 8.6.2.2)
1172
                    if ($tag == self::ASN1_BITSTRING) {
1173
                        $this->_string_shift($key);
1174
                    }
1175
                    if (ord($this->_string_shift($key)) != self::ASN1_SEQUENCE) {
1176
                        return false;
1177
                    }
1178
                    if ($this->_decodeLength($key) != strlen($key)) {
1179
                        return false;
1180
                    }
1181
                    $tag = ord($this->_string_shift($key));
1182
                }
1183
                if ($tag != self::ASN1_INTEGER) {
1184
                    return false;
1185
                }
1186
1187
                $length = $this->_decodeLength($key);
1188
                $temp = $this->_string_shift($key, $length);
1189
                if (strlen($temp) != 1 || ord($temp) > 2) {
1190
                    $components['modulus'] = new BigInteger($temp, 256);
1191
                    $this->_string_shift($key); // skip over self::ASN1_INTEGER
1192
                    $length = $this->_decodeLength($key);
1193
                    $components[$type == self::PUBLIC_FORMAT_PKCS1 ? 'publicExponent' : 'privateExponent'] = new BigInteger($this->_string_shift($key, $length), 256);
1194
1195
                    return $components;
1196
                }
1197
                if (ord($this->_string_shift($key)) != self::ASN1_INTEGER) {
1198
                    return false;
1199
                }
1200
                $length = $this->_decodeLength($key);
1201
                $components['modulus'] = new BigInteger($this->_string_shift($key, $length), 256);
1202
                $this->_string_shift($key);
1203
                $length = $this->_decodeLength($key);
1204
                $components['publicExponent'] = new BigInteger($this->_string_shift($key, $length), 256);
1205
                $this->_string_shift($key);
1206
                $length = $this->_decodeLength($key);
1207
                $components['privateExponent'] = new BigInteger($this->_string_shift($key, $length), 256);
1208
                $this->_string_shift($key);
1209
                $length = $this->_decodeLength($key);
1210
                $components['primes'] = array(1 => new BigInteger($this->_string_shift($key, $length), 256));
1211
                $this->_string_shift($key);
1212
                $length = $this->_decodeLength($key);
1213
                $components['primes'][] = new BigInteger($this->_string_shift($key, $length), 256);
1214
                $this->_string_shift($key);
1215
                $length = $this->_decodeLength($key);
1216
                $components['exponents'] = array(1 => new BigInteger($this->_string_shift($key, $length), 256));
1217
                $this->_string_shift($key);
1218
                $length = $this->_decodeLength($key);
1219
                $components['exponents'][] = new BigInteger($this->_string_shift($key, $length), 256);
1220
                $this->_string_shift($key);
1221
                $length = $this->_decodeLength($key);
1222
                $components['coefficients'] = array(2 => new BigInteger($this->_string_shift($key, $length), 256));
1223
1224
                if (!empty($key)) {
1225
                    if (ord($this->_string_shift($key)) != self::ASN1_SEQUENCE) {
1226
                        return false;
1227
                    }
1228
                    $this->_decodeLength($key);
1229
                    while (!empty($key)) {
1230
                        if (ord($this->_string_shift($key)) != self::ASN1_SEQUENCE) {
1231
                            return false;
1232
                        }
1233
                        $this->_decodeLength($key);
1234
                        $key = substr($key, 1);
1235
                        $length = $this->_decodeLength($key);
1236
                        $components['primes'][] = new BigInteger($this->_string_shift($key, $length), 256);
1237
                        $this->_string_shift($key);
1238
                        $length = $this->_decodeLength($key);
1239
                        $components['exponents'][] = new BigInteger($this->_string_shift($key, $length), 256);
1240
                        $this->_string_shift($key);
1241
                        $length = $this->_decodeLength($key);
1242
                        $components['coefficients'][] = new BigInteger($this->_string_shift($key, $length), 256);
1243
                    }
1244
                }
1245
1246
                return $components;
1247
            case self::PUBLIC_FORMAT_OPENSSH:
1248
                $parts = explode(' ', $key, 3);
1249
1250
                $key = isset($parts[1]) ? base64_decode($parts[1]) : false;
1251
                if ($key === false) {
1252
                    return false;
1253
                }
1254
1255
                $comment = isset($parts[2]) ? $parts[2] : false;
1256
1257
                $cleanup = substr($key, 0, 11) == "\0\0\0\7ssh-rsa";
1258
1259
                if (strlen($key) <= 4) {
1260
                    return false;
1261
                }
1262
                extract(unpack('Nlength', $this->_string_shift($key, 4)));
0 ignored issues
show
It seems like unpack('Nlength', $this->_string_shift($key, 4)) can also be of type false; however, parameter $var_array of extract() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1262
                extract(/** @scrutinizer ignore-type */ unpack('Nlength', $this->_string_shift($key, 4)));
Loading history...
1263
                $publicExponent = new BigInteger($this->_string_shift($key, $length), -256);
1264
                if (strlen($key) <= 4) {
1265
                    return false;
1266
                }
1267
                extract(unpack('Nlength', $this->_string_shift($key, 4)));
1268
                $modulus = new BigInteger($this->_string_shift($key, $length), -256);
1269
1270
                if ($cleanup && strlen($key)) {
1271
                    if (strlen($key) <= 4) {
1272
                        return false;
1273
                    }
1274
                    extract(unpack('Nlength', $this->_string_shift($key, 4)));
1275
                    $realModulus = new BigInteger($this->_string_shift($key, $length), -256);
1276
                    return strlen($key) ? false : array(
1277
                        'modulus' => $realModulus,
1278
                        'publicExponent' => $modulus,
1279
                        'comment' => $comment
1280
                    );
1281
                } else {
1282
                    return strlen($key) ? false : array(
1283
                        'modulus' => $modulus,
1284
                        'publicExponent' => $publicExponent,
1285
                        'comment' => $comment
1286
                    );
1287
                }
1288
            // http://www.w3.org/TR/xmldsig-core/#sec-RSAKeyValue
1289
            // http://en.wikipedia.org/wiki/XML_Signature
1290
            // no break
1291
            case self::PRIVATE_FORMAT_XML:
1292
            case self::PUBLIC_FORMAT_XML:
1293
                $this->components = array();
1294
1295
                $xml = xml_parser_create('UTF-8');
1296
                xml_set_object($xml, $this);
1297
                xml_set_element_handler($xml, '_start_element_handler', '_stop_element_handler');
1298
                xml_set_character_data_handler($xml, '_data_handler');
1299
                // add <xml></xml> to account for "dangling" tags like <BitStrength>...</BitStrength> that are sometimes added
1300
                if (!xml_parse($xml, '<xml>' . $key . '</xml>')) {
1301
                    return false;
1302
                }
1303
1304
                return isset($this->components['modulus']) && isset($this->components['publicExponent']) ? $this->components : false;
1305
            // from PuTTY's SSHPUBK.C
1306
            case self::PRIVATE_FORMAT_PUTTY:
1307
                $components = array();
1308
                $key = preg_split('#\r\n|\r|\n#', $key);
1309
                $type = trim(preg_replace('#PuTTY-User-Key-File-2: (.+)#', '$1', $key[0]));
1310
                if ($type != 'ssh-rsa') {
1311
                    return false;
1312
                }
1313
                $encryption = trim(preg_replace('#Encryption: (.+)#', '$1', $key[1]));
1314
                $comment = trim(preg_replace('#Comment: (.+)#', '$1', $key[2]));
0 ignored issues
show
The assignment to $comment is dead and can be removed.
Loading history...
1315
1316
                $publicLength = trim(preg_replace('#Public-Lines: (\d+)#', '$1', $key[3]));
1317
                $public = base64_decode(implode('', array_map('trim', array_slice($key, 4, $publicLength))));
0 ignored issues
show
It seems like $key can also be of type false; however, parameter $array of array_slice() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1317
                $public = base64_decode(implode('', array_map('trim', array_slice(/** @scrutinizer ignore-type */ $key, 4, $publicLength))));
Loading history...
$publicLength of type string is incompatible with the type integer expected by parameter $length of array_slice(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1317
                $public = base64_decode(implode('', array_map('trim', array_slice($key, 4, /** @scrutinizer ignore-type */ $publicLength))));
Loading history...
1318
                $public = substr($public, 11);
1319
                extract(unpack('Nlength', $this->_string_shift($public, 4)));
1320
                $components['publicExponent'] = new BigInteger($this->_string_shift($public, $length), -256);
1321
                extract(unpack('Nlength', $this->_string_shift($public, 4)));
1322
                $components['modulus'] = new BigInteger($this->_string_shift($public, $length), -256);
1323
1324
                $privateLength = trim(preg_replace('#Private-Lines: (\d+)#', '$1', $key[$publicLength + 4]));
1325
                $private = base64_decode(implode('', array_map('trim', array_slice($key, $publicLength + 5, $privateLength))));
1326
1327
                switch ($encryption) {
1328
                    case 'aes256-cbc':
1329
                        $symkey = '';
1330
                        $sequence = 0;
1331
                        while (strlen($symkey) < 32) {
1332
                            $temp = pack('Na*', $sequence++, $this->password);
1333
                            $symkey.= pack('H*', hash("sha256", $temp));
1334
                        }
1335
                        $symkey = substr($symkey, 0, 32);
1336
                        $crypto = new AES();
1337
                }
1338
1339
                if ($encryption != 'none') {
1340
                    $crypto->setKey($symkey);
1341
                    $crypto->disablePadding();
1342
                    $private = $crypto->decrypt($private);
1343
                    if ($private === false) {
1344
                        return false;
1345
                    }
1346
                }
1347
1348
                extract(unpack('Nlength', $this->_string_shift($private, 4)));
1349
                if (strlen($private) < $length) {
1350
                    return false;
1351
                }
1352
                $components['privateExponent'] = new BigInteger($this->_string_shift($private, $length), -256);
1353
                extract(unpack('Nlength', $this->_string_shift($private, 4)));
1354
                if (strlen($private) < $length) {
1355
                    return false;
1356
                }
1357
                $components['primes'] = array(1 => new BigInteger($this->_string_shift($private, $length), -256));
1358
                extract(unpack('Nlength', $this->_string_shift($private, 4)));
1359
                if (strlen($private) < $length) {
1360
                    return false;
1361
                }
1362
                $components['primes'][] = new BigInteger($this->_string_shift($private, $length), -256);
1363
1364
                $temp = $components['primes'][1]->subtract($this->one);
1365
                $components['exponents'] = array(1 => $components['publicExponent']->modInverse($temp));
1366
                $temp = $components['primes'][2]->subtract($this->one);
1367
                $components['exponents'][] = $components['publicExponent']->modInverse($temp);
1368
1369
                extract(unpack('Nlength', $this->_string_shift($private, 4)));
1370
                if (strlen($private) < $length) {
1371
                    return false;
1372
                }
1373
                $components['coefficients'] = array(2 => new BigInteger($this->_string_shift($private, $length), -256));
1374
1375
                return $components;
1376
        }
1377
    }
1378
1379
    /**
1380
     * Returns the key size
1381
     *
1382
     * More specifically, this returns the size of the modulo in bits.
1383
     *
1384
     * @access public
1385
     * @return int
1386
     */
1387
    public function getSize()
1388
    {
1389
        return !isset($this->modulus) ? 0 : strlen($this->modulus->toBits());
1390
    }
1391
1392
    /**
1393
     * Start Element Handler
1394
     *
1395
     * Called by xml_set_element_handler()
1396
     *
1397
     * @access private
1398
     * @param resource $parser
1399
     * @param string $name
1400
     * @param array $attribs
1401
     */
1402
    public function _start_element_handler($parser, $name, $attribs)
1403
    {
1404
        //$name = strtoupper($name);
1405
        switch ($name) {
1406
            case 'MODULUS':
1407
                $this->current = &$this->components['modulus'];
1408
                break;
1409
            case 'EXPONENT':
1410
                $this->current = &$this->components['publicExponent'];
1411
                break;
1412
            case 'P':
1413
                $this->current = &$this->components['primes'][1];
1414
                break;
1415
            case 'Q':
1416
                $this->current = &$this->components['primes'][2];
1417
                break;
1418
            case 'DP':
1419
                $this->current = &$this->components['exponents'][1];
1420
                break;
1421
            case 'DQ':
1422
                $this->current = &$this->components['exponents'][2];
1423
                break;
1424
            case 'INVERSEQ':
1425
                $this->current = &$this->components['coefficients'][2];
1426
                break;
1427
            case 'D':
1428
                $this->current = &$this->components['privateExponent'];
1429
        }
1430
        $this->current = '';
1431
    }
1432
1433
    /**
1434
     * Stop Element Handler
1435
     *
1436
     * Called by xml_set_element_handler()
1437
     *
1438
     * @access private
1439
     * @param resource $parser
1440
     * @param string $name
1441
     */
1442
    public function _stop_element_handler($parser, $name)
1443
    {
1444
        if (isset($this->current)) {
1445
            $this->current = new BigInteger(base64_decode($this->current), 256);
1446
            unset($this->current);
1447
        }
1448
    }
1449
1450
    /**
1451
     * Data Handler
1452
     *
1453
     * Called by xml_set_character_data_handler()
1454
     *
1455
     * @access private
1456
     * @param resource $parser
1457
     * @param string $data
1458
     */
1459
    public function _data_handler($parser, $data)
1460
    {
1461
        if (!isset($this->current) || is_object($this->current)) {
1462
            return;
1463
        }
1464
        $this->current.= trim($data);
1465
    }
1466
1467
    /**
1468
     * Loads a public or private key
1469
     *
1470
     * Returns true on success and false on failure (ie. an incorrect password was provided or the key was malformed)
1471
     *
1472
     * @access public
1473
     * @param string $key
1474
     * @param int $type optional
1475
     */
1476
    public function loadKey($key, $type = false)
1477
    {
1478
        if ($key instanceof RSA) {
1479
            $this->privateKeyFormat = $key->privateKeyFormat;
1480
            $this->publicKeyFormat = $key->publicKeyFormat;
1481
            $this->k = $key->k;
1482
            $this->hLen = $key->hLen;
1483
            $this->sLen = $key->sLen;
1484
            $this->mgfHLen = $key->mgfHLen;
1485
            $this->encryptionMode = $key->encryptionMode;
1486
            $this->signatureMode = $key->signatureMode;
1487
            $this->password = $key->password;
1488
            $this->configFile = $key->configFile;
1489
            $this->comment = $key->comment;
1490
1491
            if (is_object($key->hash)) {
1492
                $this->hash = new Hash($key->hash->getHash());
1493
            }
1494
            if (is_object($key->mgfHash)) {
1495
                $this->mgfHash = new Hash($key->mgfHash->getHash());
1496
            }
1497
1498
            if (is_object($key->modulus)) {
1499
                $this->modulus = $key->modulus->copy();
1500
            }
1501
            if (is_object($key->exponent)) {
1502
                $this->exponent = $key->exponent->copy();
1503
            }
1504
            if (is_object($key->publicExponent)) {
1505
                $this->publicExponent = $key->publicExponent->copy();
1506
            }
1507
1508
            $this->primes = array();
1509
            $this->exponents = array();
1510
            $this->coefficients = array();
1511
1512
            foreach ($this->primes as $prime) {
1513
                $this->primes[] = $prime->copy();
1514
            }
1515
            foreach ($this->exponents as $exponent) {
1516
                $this->exponents[] = $exponent->copy();
1517
            }
1518
            foreach ($this->coefficients as $coefficient) {
1519
                $this->coefficients[] = $coefficient->copy();
1520
            }
1521
1522
            return true;
1523
        }
1524
1525
        if ($type === false) {
1526
            $types = array(
1527
                self::PUBLIC_FORMAT_RAW,
1528
                self::PRIVATE_FORMAT_PKCS1,
1529
                self::PRIVATE_FORMAT_XML,
1530
                self::PRIVATE_FORMAT_PUTTY,
1531
                self::PUBLIC_FORMAT_OPENSSH
1532
            );
1533
            foreach ($types as $type) {
1534
                $components = $this->_parseKey($key, $type);
1535
                if ($components !== false) {
1536
                    break;
1537
                }
1538
            }
1539
        } else {
1540
            $components = $this->_parseKey($key, $type);
1541
        }
1542
1543
        if ($components === false) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $components does not seem to be defined for all execution paths leading up to this point.
Loading history...
1544
            return false;
1545
        }
1546
1547
        if (isset($components['comment']) && $components['comment'] !== false) {
1548
            $this->comment = $components['comment'];
1549
        }
1550
        $this->modulus = $components['modulus'];
1551
        $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 phpseclib\Math\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...
1552
        $this->exponent = isset($components['privateExponent']) ? $components['privateExponent'] : $components['publicExponent'];
1553
        if (isset($components['primes'])) {
1554
            $this->primes = $components['primes'];
1555
            $this->exponents = $components['exponents'];
1556
            $this->coefficients = $components['coefficients'];
1557
            $this->publicExponent = $components['publicExponent'];
1558
        } else {
1559
            $this->primes = array();
1560
            $this->exponents = array();
1561
            $this->coefficients = array();
1562
            $this->publicExponent = false;
1563
        }
1564
1565
        switch ($type) {
1566
            case self::PUBLIC_FORMAT_OPENSSH:
1567
            case self::PUBLIC_FORMAT_RAW:
1568
                $this->setPublicKey();
1569
                break;
1570
            case self::PRIVATE_FORMAT_PKCS1:
1571
                switch (true) {
1572
                    case strpos($key, '-BEGIN PUBLIC KEY-') !== false:
1573
                    case strpos($key, '-BEGIN RSA PUBLIC KEY-') !== false:
1574
                        $this->setPublicKey();
1575
                }
1576
        }
1577
1578
        return true;
1579
    }
1580
1581
    /**
1582
     * Sets the password
1583
     *
1584
     * Private keys can be encrypted with a password.  To unset the password, pass in the empty string or false.
1585
     * Or rather, pass in $password such that empty($password) && !is_string($password) is true.
1586
     *
1587
     * @see self::createKey()
1588
     * @see self::loadKey()
1589
     * @access public
1590
     * @param string $password
1591
     */
1592
    public function setPassword($password = false)
1593
    {
1594
        $this->password = $password;
0 ignored issues
show
Documentation Bug introduced by
It seems like $password can also be of type false. However, the property $password is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

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

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
1595
    }
1596
1597
    /**
1598
     * Defines the public key
1599
     *
1600
     * Some private key formats define the public exponent and some don't.  Those that don't define it are problematic when
1601
     * used in certain contexts.  For example, in SSH-2, RSA authentication works by sending the public key along with a
1602
     * message signed by the private key to the server.  The SSH-2 server looks the public key up in an index of public keys
1603
     * and if it's present then proceeds to verify the signature.  Problem is, if your private key doesn't include the public
1604
     * exponent this won't work unless you manually add the public exponent. phpseclib tries to guess if the key being used
1605
     * is the public key but in the event that it guesses incorrectly you might still want to explicitly set the key as being
1606
     * public.
1607
     *
1608
     * Do note that when a new key is loaded the index will be cleared.
1609
     *
1610
     * Returns true on success, false on failure
1611
     *
1612
     * @see self::getPublicKey()
1613
     * @access public
1614
     * @param string $key optional
1615
     * @param int $type optional
1616
     * @return bool
1617
     */
1618
    public function setPublicKey($key = false, $type = false)
1619
    {
1620
        // if a public key has already been loaded return false
1621
        if (!empty($this->publicExponent)) {
1622
            return false;
1623
        }
1624
1625
        if ($key === false && !empty($this->modulus)) {
1626
            $this->publicExponent = $this->exponent;
1627
            return true;
1628
        }
1629
1630
        if ($type === false) {
1631
            $types = array(
1632
                self::PUBLIC_FORMAT_RAW,
1633
                self::PUBLIC_FORMAT_PKCS1,
1634
                self::PUBLIC_FORMAT_XML,
1635
                self::PUBLIC_FORMAT_OPENSSH
1636
            );
1637
            foreach ($types as $type) {
1638
                $components = $this->_parseKey($key, $type);
0 ignored issues
show
It seems like $key can also be of type false; however, parameter $key of phpseclib\Crypt\RSA::_parseKey() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1638
                $components = $this->_parseKey(/** @scrutinizer ignore-type */ $key, $type);
Loading history...
1639
                if ($components !== false) {
1640
                    break;
1641
                }
1642
            }
1643
        } else {
1644
            $components = $this->_parseKey($key, $type);
1645
        }
1646
1647
        if ($components === false) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $components does not seem to be defined for all execution paths leading up to this point.
Loading history...
1648
            return false;
1649
        }
1650
1651
        if (empty($this->modulus) || !$this->modulus->equals($components['modulus'])) {
1652
            $this->modulus = $components['modulus'];
1653
            $this->exponent = $this->publicExponent = $components['publicExponent'];
1654
            return true;
1655
        }
1656
1657
        $this->publicExponent = $components['publicExponent'];
1658
1659
        return true;
1660
    }
1661
1662
    /**
1663
     * Defines the private key
1664
     *
1665
     * If phpseclib guessed a private key was a public key and loaded it as such it might be desirable to force
1666
     * phpseclib to treat the key as a private key. This function will do that.
1667
     *
1668
     * Do note that when a new key is loaded the index will be cleared.
1669
     *
1670
     * Returns true on success, false on failure
1671
     *
1672
     * @see self::getPublicKey()
1673
     * @access public
1674
     * @param string $key optional
1675
     * @param int $type optional
1676
     * @return bool
1677
     */
1678
    public function setPrivateKey($key = false, $type = false)
1679
    {
1680
        if ($key === false && !empty($this->publicExponent)) {
1681
            $this->publicExponent = false;
1682
            return true;
1683
        }
1684
1685
        $rsa = new RSA();
1686
        if (!$rsa->loadKey($key, $type)) {
0 ignored issues
show
It seems like $key can also be of type false; however, parameter $key of phpseclib\Crypt\RSA::loadKey() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1686
        if (!$rsa->loadKey(/** @scrutinizer ignore-type */ $key, $type)) {
Loading history...
It seems like $type can also be of type false; however, parameter $type of phpseclib\Crypt\RSA::loadKey() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1686
        if (!$rsa->loadKey($key, /** @scrutinizer ignore-type */ $type)) {
Loading history...
1687
            return false;
1688
        }
1689
        $rsa->publicExponent = false;
1690
1691
        // don't overwrite the old key if the new key is invalid
1692
        $this->loadKey($rsa);
1693
        return true;
1694
    }
1695
1696
    /**
1697
     * Returns the public key
1698
     *
1699
     * The public key is only returned under two circumstances - if the private key had the public key embedded within it
1700
     * or if the public key was set via setPublicKey().  If the currently loaded key is supposed to be the public key this
1701
     * function won't return it since this library, for the most part, doesn't distinguish between public and private keys.
1702
     *
1703
     * @see self::getPublicKey()
1704
     * @access public
1705
     * @param string $key
1706
     * @param int $type optional
1707
     */
1708
    public function getPublicKey($type = self::PUBLIC_FORMAT_PKCS8)
1709
    {
1710
        if (empty($this->modulus) || empty($this->publicExponent)) {
1711
            return false;
1712
        }
1713
1714
        $oldFormat = $this->publicKeyFormat;
1715
        $this->publicKeyFormat = $type;
1716
        $temp = $this->_convertPublicKey($this->modulus, $this->publicExponent);
1717
        $this->publicKeyFormat = $oldFormat;
1718
        return $temp;
1719
    }
1720
1721
    /**
1722
     * Returns the public key's fingerprint
1723
     *
1724
     * The public key's fingerprint is returned, which is equivalent to running `ssh-keygen -lf rsa.pub`. If there is
1725
     * no public key currently loaded, false is returned.
1726
     * Example output (md5): "c1:b1:30:29:d7:b8:de:6c:97:77:10:d7:46:41:63:87" (as specified by RFC 4716)
1727
     *
1728
     * @access public
1729
     * @param string $algorithm The hashing algorithm to be used. Valid options are 'md5' and 'sha256'. False is returned
1730
     * for invalid values.
1731
     * @return mixed
1732
     */
1733
    public function getPublicKeyFingerprint($algorithm = 'sha256')
1734
    {
1735
        if (empty($this->modulus) || empty($this->publicExponent)) {
1736
            return false;
1737
        }
1738
1739
        $modulus = $this->modulus->toBytes(true);
1740
        $publicExponent = $this->publicExponent->toBytes(true);
1741
1742
        $RSAPublicKey = pack('Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($publicExponent), $publicExponent, strlen($modulus), $modulus);
1743
1744
        switch ($algorithm) {
1745
            case 'sha256':
1746
                $hash = new Hash('sha256');
1747
                $base = base64_encode($hash->hash($RSAPublicKey));
1748
                return substr($base, 0, strlen($base) - 1);
1749
            default:
1750
                return false;
1751
        }
1752
    }
1753
1754
    /**
1755
     * Returns the private key
1756
     *
1757
     * The private key is only returned if the currently loaded key contains the constituent prime numbers.
1758
     *
1759
     * @see self::getPublicKey()
1760
     * @access public
1761
     * @param string $key
1762
     * @param int $type optional
1763
     * @return mixed
1764
     */
1765
    public function getPrivateKey($type = self::PUBLIC_FORMAT_PKCS1)
1766
    {
1767
        if (empty($this->primes)) {
1768
            return false;
1769
        }
1770
1771
        $oldFormat = $this->privateKeyFormat;
1772
        $this->privateKeyFormat = $type;
1773
        $temp = $this->_convertPrivateKey($this->modulus, $this->publicExponent, $this->exponent, $this->primes, $this->exponents, $this->coefficients);
1774
        $this->privateKeyFormat = $oldFormat;
1775
        return $temp;
1776
    }
1777
1778
    /**
1779
     * Returns a minimalistic private key
1780
     *
1781
     * Returns the private key without the prime number constituants.  Structurally identical to a public key that
1782
     * hasn't been set as the public key
1783
     *
1784
     * @see self::getPrivateKey()
1785
     * @access private
1786
     * @param string $key
1787
     * @param int $type optional
1788
     */
1789
    public function _getPrivatePublicKey($mode = self::PUBLIC_FORMAT_PKCS8)
1790
    {
1791
        if (empty($this->modulus) || empty($this->exponent)) {
1792
            return false;
1793
        }
1794
1795
        $oldFormat = $this->publicKeyFormat;
1796
        $this->publicKeyFormat = $mode;
1797
        $temp = $this->_convertPublicKey($this->modulus, $this->exponent);
1798
        $this->publicKeyFormat = $oldFormat;
1799
        return $temp;
1800
    }
1801
1802
    /**
1803
     *  __toString() magic method
1804
     *
1805
     * @access public
1806
     * @return string
1807
     */
1808
    public function __toString()
1809
    {
1810
        $key = $this->getPrivateKey($this->privateKeyFormat);
1811
        if ($key !== false) {
1812
            return $key;
1813
        }
1814
        $key = $this->_getPrivatePublicKey($this->publicKeyFormat);
1815
        return $key !== false ? $key : '';
1816
    }
1817
1818
    /**
1819
     *  __clone() magic method
1820
     *
1821
     * @access public
1822
     * @return Crypt_RSA
1823
     */
1824
    public function __clone()
1825
    {
1826
        $key = new RSA();
1827
        $key->loadKey($this);
1828
        return $key;
1829
    }
1830
1831
    /**
1832
     * Generates the smallest and largest numbers requiring $bits bits
1833
     *
1834
     * @access private
1835
     * @param int $bits
1836
     * @return array
1837
     */
1838
    public function _generateMinMax($bits)
1839
    {
1840
        $bytes = $bits >> 3;
1841
        $min = str_repeat(chr(0), $bytes);
1842
        $max = str_repeat(chr(0xFF), $bytes);
1843
        $msb = $bits & 7;
1844
        if ($msb) {
1845
            $min = chr(1 << ($msb - 1)) . $min;
1846
            $max = chr((1 << $msb) - 1) . $max;
1847
        } else {
1848
            $min[0] = chr(0x80);
1849
        }
1850
1851
        return array(
1852
            'min' => new BigInteger($min, 256),
1853
            'max' => new BigInteger($max, 256)
1854
        );
1855
    }
1856
1857
    /**
1858
     * DER-decode the length
1859
     *
1860
     * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4.  See
1861
     * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information.
1862
     *
1863
     * @access private
1864
     * @param string $string
1865
     * @return int
1866
     */
1867
    public function _decodeLength(&$string)
1868
    {
1869
        $length = ord($this->_string_shift($string));
1870
        if ($length & 0x80) { // definite length, long form
1871
            $length&= 0x7F;
1872
            $temp = $this->_string_shift($string, $length);
1873
            list(, $length) = unpack('N', substr(str_pad($temp, 4, chr(0), STR_PAD_LEFT), -4));
1874
        }
1875
        return $length;
1876
    }
1877
1878
    /**
1879
     * DER-encode the length
1880
     *
1881
     * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4.  See
1882
     * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information.
1883
     *
1884
     * @access private
1885
     * @param int $length
1886
     * @return string
1887
     */
1888
    public function _encodeLength($length)
1889
    {
1890
        if ($length <= 0x7F) {
1891
            return chr($length);
1892
        }
1893
1894
        $temp = ltrim(pack('N', $length), chr(0));
1895
        return pack('Ca*', 0x80 | strlen($temp), $temp);
1896
    }
1897
1898
    /**
1899
     * String Shift
1900
     *
1901
     * Inspired by array_shift
1902
     *
1903
     * @param string $string
1904
     * @param int $index
1905
     * @return string
1906
     * @access private
1907
     */
1908
    public function _string_shift(&$string, $index = 1)
1909
    {
1910
        $substr = substr($string, 0, $index);
1911
        $string = substr($string, $index);
1912
        return $substr;
1913
    }
1914
1915
    /**
1916
     * Determines the private key format
1917
     *
1918
     * @see self::createKey()
1919
     * @access public
1920
     * @param int $format
1921
     */
1922
    public function setPrivateKeyFormat($format)
1923
    {
1924
        $this->privateKeyFormat = $format;
1925
    }
1926
1927
    /**
1928
     * Determines the public key format
1929
     *
1930
     * @see self::createKey()
1931
     * @access public
1932
     * @param int $format
1933
     */
1934
    public function setPublicKeyFormat($format)
1935
    {
1936
        $this->publicKeyFormat = $format;
1937
    }
1938
1939
    /**
1940
     * Determines which hashing function should be used
1941
     *
1942
     * Used with signature production / verification and (if the encryption mode is self::ENCRYPTION_OAEP) encryption and
1943
     * decryption.  If $hash isn't supported, sha256 is used.
1944
     *
1945
     * @access public
1946
     * @param string $hash
1947
     */
1948
    public function setHash($hash)
1949
    {
1950
        // \phpseclib\Crypt\Hash supports algorithms that PKCS#1 doesn't support.  md5-96 and sha256-96, for example.
1951
        switch ($hash) {
1952
            case 'sha256':
1953
            case 'sha384':
1954
            case 'sha512':
1955
                $this->hash = new Hash($hash);
1956
                $this->hashName = $hash;
1957
                break;
1958
            default:
1959
                $this->hash = new Hash('sha256');
1960
                $this->hashName = 'sha256';
1961
        }
1962
        $this->hLen = $this->hash->getLength();
1963
    }
1964
1965
    /**
1966
     * Determines which hashing function should be used for the mask generation function
1967
     *
1968
     * The mask generation function is used by self::ENCRYPTION_OAEP and self::SIGNATURE_PSS and although it's
1969
     * best if Hash and MGFHash are set to the same thing this is not a requirement.
1970
     *
1971
     * @access public
1972
     * @param string $hash
1973
     */
1974
    public function setMGFHash($hash)
1975
    {
1976
        // \phpseclib\Crypt\Hash supports algorithms that PKCS#1 doesn't support.  md5-96 and sha1-96, for example.
1977
        switch ($hash) {
1978
            case 'sha256':
1979
            case 'sha384':
1980
            case 'sha512':
1981
                $this->mgfHash = new Hash($hash);
1982
                break;
1983
            default:
1984
                $this->mgfHash = new Hash('sha256');
1985
        }
1986
        $this->mgfHLen = $this->mgfHash->getLength();
1987
    }
1988
1989
    /**
1990
     * Determines the salt length
1991
     *
1992
     * To quote from {@link http://tools.ietf.org/html/rfc3447#page-38 RFC3447#page-38}:
1993
     *
1994
     *    Typical salt lengths in octets are hLen (the length of the output
1995
     *    of the hash function Hash) and 0.
1996
     *
1997
     * @access public
1998
     * @param int $format
1999
     */
2000
    public function setSaltLength($sLen)
2001
    {
2002
        $this->sLen = $sLen;
2003
    }
2004
2005
    /**
2006
     * Integer-to-Octet-String primitive
2007
     *
2008
     * See {@link http://tools.ietf.org/html/rfc3447#section-4.1 RFC3447#section-4.1}.
2009
     *
2010
     * @access private
2011
     * @param \phpseclib\Math\BigInteger $x
2012
     * @param int $xLen
2013
     * @return string
2014
     */
2015
    public function _i2osp($x, $xLen)
2016
    {
2017
        $x = $x->toBytes();
2018
        if (strlen($x) > $xLen) {
2019
            user_error('Integer too large');
2020
            return false;
2021
        }
2022
        return str_pad($x, $xLen, chr(0), STR_PAD_LEFT);
2023
    }
2024
2025
    /**
2026
     * Octet-String-to-Integer primitive
2027
     *
2028
     * See {@link http://tools.ietf.org/html/rfc3447#section-4.2 RFC3447#section-4.2}.
2029
     *
2030
     * @access private
2031
     * @param string $x
2032
     * @return \phpseclib\Math\BigInteger
2033
     */
2034
    public function _os2ip($x)
2035
    {
2036
        return new BigInteger($x, 256);
2037
    }
2038
2039
    /**
2040
     * Exponentiate with or without Chinese Remainder Theorem
2041
     *
2042
     * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.1 RFC3447#section-5.1.2}.
2043
     *
2044
     * @access private
2045
     * @param \phpseclib\Math\BigInteger $x
2046
     * @return \phpseclib\Math\BigInteger
2047
     */
2048
    public function _exponentiate($x)
2049
    {
2050
        switch (true) {
2051
            case empty($this->primes):
2052
            case $this->primes[1]->equals($this->zero):
2053
            case empty($this->coefficients):
2054
            case $this->coefficients[2]->equals($this->zero):
2055
            case empty($this->exponents):
2056
            case $this->exponents[1]->equals($this->zero):
2057
                return $x->modPow($this->exponent, $this->modulus);
2058
        }
2059
2060
        $num_primes = count($this->primes);
2061
2062
        if (defined('CRYPT_RSA_DISABLE_BLINDING')) {
2063
            $m_i = array(
2064
                1 => $x->modPow($this->exponents[1], $this->primes[1]),
2065
                2 => $x->modPow($this->exponents[2], $this->primes[2])
2066
            );
2067
            $h = $m_i[1]->subtract($m_i[2]);
2068
            $h = $h->multiply($this->coefficients[2]);
2069
            list(, $h) = $h->divide($this->primes[1]);
2070
            $m = $m_i[2]->add($h->multiply($this->primes[2]));
2071
2072
            $r = $this->primes[1];
2073
            for ($i = 3; $i <= $num_primes; $i++) {
2074
                $m_i = $x->modPow($this->exponents[$i], $this->primes[$i]);
2075
2076
                $r = $r->multiply($this->primes[$i - 1]);
2077
2078
                $h = $m_i->subtract($m);
2079
                $h = $h->multiply($this->coefficients[$i]);
2080
                list(, $h) = $h->divide($this->primes[$i]);
2081
2082
                $m = $m->add($r->multiply($h));
2083
            }
2084
        } else {
2085
            $smallest = $this->primes[1];
2086
            for ($i = 2; $i <= $num_primes; $i++) {
2087
                if ($smallest->compare($this->primes[$i]) > 0) {
2088
                    $smallest = $this->primes[$i];
2089
                }
2090
            }
2091
2092
            $one = new BigInteger(1);
2093
2094
            $r = $one->random($one, $smallest->subtract($one));
2095
2096
            $m_i = array(
2097
                1 => $this->_blind($x, $r, 1),
2098
                2 => $this->_blind($x, $r, 2)
2099
            );
2100
            $h = $m_i[1]->subtract($m_i[2]);
2101
            $h = $h->multiply($this->coefficients[2]);
2102
            list(, $h) = $h->divide($this->primes[1]);
2103
            $m = $m_i[2]->add($h->multiply($this->primes[2]));
2104
2105
            $r = $this->primes[1];
2106
            for ($i = 3; $i <= $num_primes; $i++) {
2107
                $m_i = $this->_blind($x, $r, $i);
2108
2109
                $r = $r->multiply($this->primes[$i - 1]);
2110
2111
                $h = $m_i->subtract($m);
2112
                $h = $h->multiply($this->coefficients[$i]);
2113
                list(, $h) = $h->divide($this->primes[$i]);
2114
2115
                $m = $m->add($r->multiply($h));
2116
            }
2117
        }
2118
2119
        return $m;
2120
    }
2121
2122
    /**
2123
     * Performs RSA Blinding
2124
     *
2125
     * Protects against timing attacks by employing RSA Blinding.
2126
     * Returns $x->modPow($this->exponents[$i], $this->primes[$i])
2127
     *
2128
     * @access private
2129
     * @param \phpseclib\Math\BigInteger $x
2130
     * @param \phpseclib\Math\BigInteger $r
2131
     * @param int $i
2132
     * @return \phpseclib\Math\BigInteger
2133
     */
2134
    public function _blind($x, $r, $i)
2135
    {
2136
        $x = $x->multiply($r->modPow($this->publicExponent, $this->primes[$i]));
2137
        $x = $x->modPow($this->exponents[$i], $this->primes[$i]);
2138
2139
        $r = $r->modInverse($this->primes[$i]);
2140
        $x = $x->multiply($r);
0 ignored issues
show
It seems like $r can also be of type false; however, parameter $x of phpseclib\Math\BigInteger::multiply() does only seem to accept phpseclib\Math\BigInteger, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

2140
        $x = $x->multiply(/** @scrutinizer ignore-type */ $r);
Loading history...
2141
        list(, $x) = $x->divide($this->primes[$i]);
2142
2143
        return $x;
2144
    }
2145
2146
    /**
2147
     * Performs blinded RSA equality testing
2148
     *
2149
     * Protects against a particular type of timing attack described.
2150
     *
2151
     * See {@link http://codahale.com/a-lesson-in-timing-attacks/ A Lesson In Timing Attacks (or, Don't use MessageDigest.isEquals)}
2152
     *
2153
     * Thanks for the heads up singpolyma!
2154
     *
2155
     * @access private
2156
     * @param string $x
2157
     * @param string $y
2158
     * @return bool
2159
     */
2160
    public function _equals($x, $y)
2161
    {
2162
        if (strlen($x) != strlen($y)) {
2163
            return false;
2164
        }
2165
2166
        $result = 0;
2167
        for ($i = 0; $i < strlen($x); $i++) {
2168
            $result |= ord($x[$i]) ^ ord($y[$i]);
2169
        }
2170
2171
        return $result == 0;
2172
    }
2173
2174
    /**
2175
     * RSAEP
2176
     *
2177
     * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.1 RFC3447#section-5.1.1}.
2178
     *
2179
     * @access private
2180
     * @param \phpseclib\Math\BigInteger $m
2181
     * @return \phpseclib\Math\BigInteger
2182
     */
2183
    public function _rsaep($m)
2184
    {
2185
        if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) {
2186
            user_error('Message representative out of range');
2187
            return false;
2188
        }
2189
        return $this->_exponentiate($m);
2190
    }
2191
2192
    /**
2193
     * RSADP
2194
     *
2195
     * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.2 RFC3447#section-5.1.2}.
2196
     *
2197
     * @access private
2198
     * @param \phpseclib\Math\BigInteger $c
2199
     * @return \phpseclib\Math\BigInteger
2200
     */
2201
    public function _rsadp($c)
2202
    {
2203
        if ($c->compare($this->zero) < 0 || $c->compare($this->modulus) > 0) {
2204
            user_error('Ciphertext representative out of range');
2205
            return false;
2206
        }
2207
        return $this->_exponentiate($c);
2208
    }
2209
2210
    /**
2211
     * RSASP1
2212
     *
2213
     * See {@link http://tools.ietf.org/html/rfc3447#section-5.2.1 RFC3447#section-5.2.1}.
2214
     *
2215
     * @access private
2216
     * @param \phpseclib\Math\BigInteger $m
2217
     * @return \phpseclib\Math\BigInteger
2218
     */
2219
    public function _rsasp1($m)
2220
    {
2221
        if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) {
2222
            user_error('Message representative out of range');
2223
            return false;
2224
        }
2225
        return $this->_exponentiate($m);
2226
    }
2227
2228
    /**
2229
     * RSAVP1
2230
     *
2231
     * See {@link http://tools.ietf.org/html/rfc3447#section-5.2.2 RFC3447#section-5.2.2}.
2232
     *
2233
     * @access private
2234
     * @param \phpseclib\Math\BigInteger $s
2235
     * @return \phpseclib\Math\BigInteger
2236
     */
2237
    public function _rsavp1($s)
2238
    {
2239
        if ($s->compare($this->zero) < 0 || $s->compare($this->modulus) > 0) {
2240
            user_error('Signature representative out of range');
2241
            return false;
2242
        }
2243
        return $this->_exponentiate($s);
2244
    }
2245
2246
    /**
2247
     * MGF1
2248
     *
2249
     * See {@link http://tools.ietf.org/html/rfc3447#appendix-B.2.1 RFC3447#appendix-B.2.1}.
2250
     *
2251
     * @access private
2252
     * @param string $mgfSeed
2253
     * @param int $mgfLen
2254
     * @return string
2255
     */
2256
    public function _mgf1($mgfSeed, $maskLen)
2257
    {
2258
        // if $maskLen would yield strings larger than 4GB, PKCS#1 suggests a "Mask too long" error be output.
2259
2260
        $t = '';
2261
        $count = ceil($maskLen / $this->mgfHLen);
2262
        for ($i = 0; $i < $count; $i++) {
2263
            $c = pack('N', $i);
2264
            $t.= $this->mgfHash->hash($mgfSeed . $c);
2265
        }
2266
2267
        return substr($t, 0, $maskLen);
2268
    }
2269
2270
    /**
2271
     * RSAES-OAEP-ENCRYPT
2272
     *
2273
     * See {@link http://tools.ietf.org/html/rfc3447#section-7.1.1 RFC3447#section-7.1.1} and
2274
     * {http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding OAES}.
2275
     *
2276
     * @access private
2277
     * @param string $m
2278
     * @param string $l
2279
     * @return string
2280
     */
2281
    public function _rsaes_oaep_encrypt($m, $l = '')
2282
    {
2283
        $mLen = strlen($m);
2284
2285
        // Length checking
2286
2287
        // if $l is larger than two million terrabytes and you're using sha256, PKCS#1 suggests a "Label too long" error
2288
        // be output.
2289
2290
        if ($mLen > $this->k - 2 * $this->hLen - 2) {
2291
            user_error('Message too long');
2292
            return false;
2293
        }
2294
2295
        // EME-OAEP encoding
2296
2297
        $lHash = $this->hash->hash($l);
2298
        $ps = str_repeat(chr(0), $this->k - $mLen - 2 * $this->hLen - 2);
2299
        $db = $lHash . $ps . chr(1) . $m;
2300
        $seed = Random::string($this->hLen);
2301
        $dbMask = $this->_mgf1($seed, $this->k - $this->hLen - 1);
2302
        $maskedDB = $db ^ $dbMask;
2303
        $seedMask = $this->_mgf1($maskedDB, $this->hLen);
2304
        $maskedSeed = $seed ^ $seedMask;
2305
        $em = chr(0) . $maskedSeed . $maskedDB;
2306
2307
        // RSA encryption
2308
2309
        $m = $this->_os2ip($em);
2310
        $c = $this->_rsaep($m);
2311
        $c = $this->_i2osp($c, $this->k);
0 ignored issues
show
$this->k of type phpseclib\Math\BigInteger is incompatible with the type integer expected by parameter $xLen of phpseclib\Crypt\RSA::_i2osp(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

2311
        $c = $this->_i2osp($c, /** @scrutinizer ignore-type */ $this->k);
Loading history...
2312
2313
        // Output the ciphertext C
2314
2315
        return $c;
2316
    }
2317
2318
    /**
2319
     * RSAES-OAEP-DECRYPT
2320
     *
2321
     * See {@link http://tools.ietf.org/html/rfc3447#section-7.1.2 RFC3447#section-7.1.2}.  The fact that the error
2322
     * messages aren't distinguishable from one another hinders debugging, but, to quote from RFC3447#section-7.1.2:
2323
     *
2324
     *    Note.  Care must be taken to ensure that an opponent cannot
2325
     *    distinguish the different error conditions in Step 3.g, whether by
2326
     *    error message or timing, or, more generally, learn partial
2327
     *    information about the encoded message EM.  Otherwise an opponent may
2328
     *    be able to obtain useful information about the decryption of the
2329
     *    ciphertext C, leading to a chosen-ciphertext attack such as the one
2330
     *    observed by Manger [36].
2331
     *
2332
     * As for $l...  to quote from {@link http://tools.ietf.org/html/rfc3447#page-17 RFC3447#page-17}:
2333
     *
2334
     *    Both the encryption and the decryption operations of RSAES-OAEP take
2335
     *    the value of a label L as input.  In this version of PKCS #1, L is
2336
     *    the empty string; other uses of the label are outside the scope of
2337
     *    this document.
2338
     *
2339
     * @access private
2340
     * @param string $c
2341
     * @param string $l
2342
     * @return string
2343
     */
2344
    public function _rsaes_oaep_decrypt($c, $l = '')
2345
    {
2346
        // Length checking
2347
2348
        // if $l is larger than two million terrabytes and you're using sha256, PKCS#1 suggests a "Label too long" error
2349
        // be output.
2350
2351
        if (strlen($c) != $this->k || $this->k < 2 * $this->hLen + 2) {
2352
            user_error('Decryption error');
2353
            return false;
2354
        }
2355
2356
        // RSA decryption
2357
2358
        $c = $this->_os2ip($c);
2359
        $m = $this->_rsadp($c);
2360
        if ($m === false) {
2361
            user_error('Decryption error');
2362
            return false;
2363
        }
2364
        $em = $this->_i2osp($m, $this->k);
0 ignored issues
show
$this->k of type phpseclib\Math\BigInteger is incompatible with the type integer expected by parameter $xLen of phpseclib\Crypt\RSA::_i2osp(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

2364
        $em = $this->_i2osp($m, /** @scrutinizer ignore-type */ $this->k);
Loading history...
2365
2366
        // EME-OAEP decoding
2367
2368
        $lHash = $this->hash->hash($l);
2369
        $y = ord($em[0]);
0 ignored issues
show
The assignment to $y is dead and can be removed.
Loading history...
2370
        $maskedSeed = substr($em, 1, $this->hLen);
2371
        $maskedDB = substr($em, $this->hLen + 1);
2372
        $seedMask = $this->_mgf1($maskedDB, $this->hLen);
2373
        $seed = $maskedSeed ^ $seedMask;
2374
        $dbMask = $this->_mgf1($seed, $this->k - $this->hLen - 1);
2375
        $db = $maskedDB ^ $dbMask;
2376
        $lHash2 = substr($db, 0, $this->hLen);
2377
        $m = substr($db, $this->hLen);
2378
        if ($lHash != $lHash2) {
2379
            user_error('Decryption error');
2380
            return false;
2381
        }
2382
        $m = ltrim($m, chr(0));
2383
        if (ord($m[0]) != 1) {
2384
            user_error('Decryption error');
2385
            return false;
2386
        }
2387
2388
        // Output the message M
2389
2390
        return substr($m, 1);
2391
    }
2392
2393
    /**
2394
     * Raw Encryption / Decryption
2395
     *
2396
     * Doesn't use padding and is not recommended.
2397
     *
2398
     * @access private
2399
     * @param string $m
2400
     * @return string
2401
     */
2402
    public function _raw_encrypt($m)
2403
    {
2404
        $temp = $this->_os2ip($m);
2405
        $temp = $this->_rsaep($temp);
2406
        return  $this->_i2osp($temp, $this->k);
0 ignored issues
show
$this->k of type phpseclib\Math\BigInteger is incompatible with the type integer expected by parameter $xLen of phpseclib\Crypt\RSA::_i2osp(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

2406
        return  $this->_i2osp($temp, /** @scrutinizer ignore-type */ $this->k);
Loading history...
2407
    }
2408
2409
    /**
2410
     * RSAES-PKCS1-V1_5-ENCRYPT
2411
     *
2412
     * See {@link http://tools.ietf.org/html/rfc3447#section-7.2.1 RFC3447#section-7.2.1}.
2413
     *
2414
     * @access private
2415
     * @param string $m
2416
     * @return string
2417
     */
2418
    public function _rsaes_pkcs1_v1_5_encrypt($m)
2419
    {
2420
        $mLen = strlen($m);
2421
2422
        // Length checking
2423
2424
        if ($mLen > $this->k - 11) {
2425
            user_error('Message too long');
2426
            return false;
2427
        }
2428
2429
        // EME-PKCS1-v1_5 encoding
2430
2431
        $psLen = $this->k - $mLen - 3;
2432
        $ps = '';
2433
        while (strlen($ps) != $psLen) {
2434
            $temp = Random::string($psLen - strlen($ps));
2435
            $temp = str_replace("\x00", '', $temp);
2436
            $ps.= $temp;
2437
        }
2438
        $type = 2;
2439
        // see the comments of _rsaes_pkcs1_v1_5_decrypt() to understand why this is being done
2440
        if (defined('CRYPT_RSA_PKCS15_COMPAT') && (!isset($this->publicExponent) || $this->exponent !== $this->publicExponent)) {
2441
            $type = 1;
2442
            // "The padding string PS shall consist of k-3-||D|| octets. ... for block type 01, they shall have value FF"
2443
            $ps = str_repeat("\xFF", $psLen);
2444
        }
2445
        $em = chr(0) . chr($type) . $ps . chr(0) . $m;
2446
2447
        // RSA encryption
2448
        $m = $this->_os2ip($em);
2449
        $c = $this->_rsaep($m);
2450
        $c = $this->_i2osp($c, $this->k);
0 ignored issues
show
$this->k of type phpseclib\Math\BigInteger is incompatible with the type integer expected by parameter $xLen of phpseclib\Crypt\RSA::_i2osp(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

2450
        $c = $this->_i2osp($c, /** @scrutinizer ignore-type */ $this->k);
Loading history...
2451
2452
        // Output the ciphertext C
2453
2454
        return $c;
2455
    }
2456
2457
    /**
2458
     * RSAES-PKCS1-V1_5-DECRYPT
2459
     *
2460
     * See {@link http://tools.ietf.org/html/rfc3447#section-7.2.2 RFC3447#section-7.2.2}.
2461
     *
2462
     * For compatibility purposes, this function departs slightly from the description given in RFC3447.
2463
     * The reason being that RFC2313#section-8.1 (PKCS#1 v1.5) states that ciphertext's encrypted by the
2464
     * private key should have the second byte set to either 0 or 1 and that ciphertext's encrypted by the
2465
     * public key should have the second byte set to 2.  In RFC3447 (PKCS#1 v2.1), the second byte is supposed
2466
     * to be 2 regardless of which key is used.  For compatibility purposes, we'll just check to make sure the
2467
     * second byte is 2 or less.  If it is, we'll accept the decrypted string as valid.
2468
     *
2469
     * As a consequence of this, a private key encrypted ciphertext produced with \phpseclib\Crypt\RSA may not decrypt
2470
     * with a strictly PKCS#1 v1.5 compliant RSA implementation.  Public key encrypted ciphertext's should but
2471
     * not private key encrypted ciphertext's.
2472
     *
2473
     * @access private
2474
     * @param string $c
2475
     * @return string
2476
     */
2477
    public function _rsaes_pkcs1_v1_5_decrypt($c)
2478
    {
2479
        // Length checking
2480
2481
        if (strlen($c) != $this->k) { // or if k < 11
2482
            user_error('Decryption error');
2483
            return false;
2484
        }
2485
2486
        // RSA decryption
2487
2488
        $c = $this->_os2ip($c);
2489
        $m = $this->_rsadp($c);
2490
2491
        if ($m === false) {
2492
            user_error('Decryption error');
2493
            return false;
2494
        }
2495
        $em = $this->_i2osp($m, $this->k);
0 ignored issues
show
$this->k of type phpseclib\Math\BigInteger is incompatible with the type integer expected by parameter $xLen of phpseclib\Crypt\RSA::_i2osp(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

2495
        $em = $this->_i2osp($m, /** @scrutinizer ignore-type */ $this->k);
Loading history...
2496
2497
        // EME-PKCS1-v1_5 decoding
2498
2499
        if (ord($em[0]) != 0 || ord($em[1]) > 2) {
2500
            user_error('Decryption error');
2501
            return false;
2502
        }
2503
2504
        $ps = substr($em, 2, strpos($em, chr(0), 2) - 2);
2505
        $m = substr($em, strlen($ps) + 3);
2506
2507
        if (strlen($ps) < 8) {
2508
            user_error('Decryption error');
2509
            return false;
2510
        }
2511
2512
        // Output M
2513
2514
        return $m;
2515
    }
2516
2517
    /**
2518
     * EMSA-PSS-ENCODE
2519
     *
2520
     * See {@link http://tools.ietf.org/html/rfc3447#section-9.1.1 RFC3447#section-9.1.1}.
2521
     *
2522
     * @access private
2523
     * @param string $m
2524
     * @param int $emBits
2525
     */
2526
    public function _emsa_pss_encode($m, $emBits)
2527
    {
2528
        // if $m is larger than two million terrabytes and you're using sha256, PKCS#1 suggests a "Label too long" error
2529
        // be output.
2530
2531
        $emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8)
2532
        $sLen = $this->sLen !== null ? $this->sLen : $this->hLen;
2533
2534
        $mHash = $this->hash->hash($m);
2535
        if ($emLen < $this->hLen + $sLen + 2) {
2536
            user_error('Encoding error');
2537
            return false;
2538
        }
2539
2540
        $salt = Random::string($sLen);
2541
        $m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt;
2542
        $h = $this->hash->hash($m2);
2543
        $ps = str_repeat(chr(0), $emLen - $sLen - $this->hLen - 2);
2544
        $db = $ps . chr(1) . $salt;
2545
        $dbMask = $this->_mgf1($h, $emLen - $this->hLen - 1);
2546
        $maskedDB = $db ^ $dbMask;
2547
        $maskedDB[0] = ~chr(0xFF << ($emBits & 7)) & $maskedDB[0];
2548
        $em = $maskedDB . $h . chr(0xBC);
2549
2550
        return $em;
2551
    }
2552
2553
    /**
2554
     * EMSA-PSS-VERIFY
2555
     *
2556
     * See {@link http://tools.ietf.org/html/rfc3447#section-9.1.2 RFC3447#section-9.1.2}.
2557
     *
2558
     * @access private
2559
     * @param string $m
2560
     * @param string $em
2561
     * @param int $emBits
2562
     * @return string
2563
     */
2564
    public function _emsa_pss_verify($m, $em, $emBits)
2565
    {
2566
        // if $m is larger than two million terrabytes and you're using sha256, PKCS#1 suggests a "Label too long" error
2567
        // be output.
2568
2569
        $emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8);
2570
        $sLen = $this->sLen !== null ? $this->sLen : $this->hLen;
2571
2572
        $mHash = $this->hash->hash($m);
2573
        if ($emLen < $this->hLen + $sLen + 2) {
2574
            return false;
2575
        }
2576
2577
        if ($em[strlen($em) - 1] != chr(0xBC)) {
2578
            return false;
2579
        }
2580
2581
        $maskedDB = substr($em, 0, -$this->hLen - 1);
2582
        $h = substr($em, -$this->hLen - 1, $this->hLen);
2583
        $temp = chr(0xFF << ($emBits & 7));
2584
        if ((~$maskedDB[0] & $temp) != $temp) {
2585
            return false;
2586
        }
2587
        $dbMask = $this->_mgf1($h, $emLen - $this->hLen - 1);
2588
        $db = $maskedDB ^ $dbMask;
2589
        $db[0] = ~chr(0xFF << ($emBits & 7)) & $db[0];
2590
        $temp = $emLen - $this->hLen - $sLen - 2;
2591
        if (substr($db, 0, $temp) != str_repeat(chr(0), $temp) || ord($db[$temp]) != 1) {
2592
            return false;
2593
        }
2594
        $salt = substr($db, $temp + 1); // should be $sLen long
2595
        $m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt;
2596
        $h2 = $this->hash->hash($m2);
2597
        return $this->_equals($h, $h2);
2598
    }
2599
2600
    /**
2601
     * RSASSA-PSS-SIGN
2602
     *
2603
     * See {@link http://tools.ietf.org/html/rfc3447#section-8.1.1 RFC3447#section-8.1.1}.
2604
     *
2605
     * @access private
2606
     * @param string $m
2607
     * @return string
2608
     */
2609
    public function _rsassa_pss_sign($m)
2610
    {
2611
        // EMSA-PSS encoding
2612
2613
        $em = $this->_emsa_pss_encode($m, 8 * $this->k - 1);
2614
2615
        // RSA signature
2616
2617
        $m = $this->_os2ip($em);
0 ignored issues
show
It seems like $em can also be of type false; however, parameter $x of phpseclib\Crypt\RSA::_os2ip() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

2617
        $m = $this->_os2ip(/** @scrutinizer ignore-type */ $em);
Loading history...
2618
        $s = $this->_rsasp1($m);
2619
        $s = $this->_i2osp($s, $this->k);
0 ignored issues
show
$this->k of type phpseclib\Math\BigInteger is incompatible with the type integer expected by parameter $xLen of phpseclib\Crypt\RSA::_i2osp(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

2619
        $s = $this->_i2osp($s, /** @scrutinizer ignore-type */ $this->k);
Loading history...
2620
2621
        // Output the signature S
2622
2623
        return $s;
2624
    }
2625
2626
    /**
2627
     * RSASSA-PSS-VERIFY
2628
     *
2629
     * See {@link http://tools.ietf.org/html/rfc3447#section-8.1.2 RFC3447#section-8.1.2}.
2630
     *
2631
     * @access private
2632
     * @param string $m
2633
     * @param string $s
2634
     * @return string
2635
     */
2636
    public function _rsassa_pss_verify($m, $s)
2637
    {
2638
        // Length checking
2639
2640
        if (strlen($s) != $this->k) {
2641
            user_error('Invalid signature');
2642
            return false;
2643
        }
2644
2645
        // RSA verification
2646
2647
        $modBits = 8 * $this->k;
2648
2649
        $s2 = $this->_os2ip($s);
2650
        $m2 = $this->_rsavp1($s2);
2651
        if ($m2 === false) {
2652
            user_error('Invalid signature');
2653
            return false;
2654
        }
2655
        $em = $this->_i2osp($m2, $modBits >> 3);
2656
        if ($em === false) {
2657
            user_error('Invalid signature');
2658
            return false;
2659
        }
2660
2661
        // EMSA-PSS verification
2662
2663
        return $this->_emsa_pss_verify($m, $em, $modBits - 1);
2664
    }
2665
2666
    /**
2667
     * EMSA-PKCS1-V1_5-ENCODE
2668
     *
2669
     * See {@link http://tools.ietf.org/html/rfc3447#section-9.2 RFC3447#section-9.2}.
2670
     *
2671
     * @access private
2672
     * @param string $m
2673
     * @param int $emLen
2674
     * @return string
2675
     */
2676
    public function _emsa_pkcs1_v1_5_encode($m, $emLen)
2677
    {
2678
        $h = $this->hash->hash($m);
2679
        if ($h === false) {
2680
            return false;
2681
        }
2682
2683
        // see http://tools.ietf.org/html/rfc3447#page-43
2684
        switch ($this->hashName) {
2685
            case 'sha256':
2686
                $t = pack('H*', '3031300d060960864801650304020105000420');
2687
                break;
2688
            case 'sha384':
2689
                $t = pack('H*', '3041300d060960864801650304020205000430');
2690
                break;
2691
            case 'sha512':
2692
                $t = pack('H*', '3051300d060960864801650304020305000440');
2693
        }
2694
        $t.= $h;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $t does not seem to be defined for all execution paths leading up to this point.
Loading history...
2695
        $tLen = strlen($t);
2696
2697
        if ($emLen < $tLen + 11) {
2698
            user_error('Intended encoded message length too short');
2699
            return false;
2700
        }
2701
2702
        $ps = str_repeat(chr(0xFF), $emLen - $tLen - 3);
2703
2704
        $em = "\0\1$ps\0$t";
2705
2706
        return $em;
2707
    }
2708
2709
    /**
2710
     * RSASSA-PKCS1-V1_5-SIGN
2711
     *
2712
     * See {@link http://tools.ietf.org/html/rfc3447#section-8.2.1 RFC3447#section-8.2.1}.
2713
     *
2714
     * @access private
2715
     * @param string $m
2716
     * @return string
2717
     */
2718
    public function _rsassa_pkcs1_v1_5_sign($m)
2719
    {
2720
        // EMSA-PKCS1-v1_5 encoding
2721
2722
        $em = $this->_emsa_pkcs1_v1_5_encode($m, $this->k);
0 ignored issues
show
$this->k of type phpseclib\Math\BigInteger is incompatible with the type integer expected by parameter $emLen of phpseclib\Crypt\RSA::_emsa_pkcs1_v1_5_encode(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

2722
        $em = $this->_emsa_pkcs1_v1_5_encode($m, /** @scrutinizer ignore-type */ $this->k);
Loading history...
2723
        if ($em === false) {
2724
            user_error('RSA modulus too short');
2725
            return false;
2726
        }
2727
2728
        // RSA signature
2729
2730
        $m = $this->_os2ip($em);
2731
        $s = $this->_rsasp1($m);
2732
        $s = $this->_i2osp($s, $this->k);
0 ignored issues
show
$this->k of type phpseclib\Math\BigInteger is incompatible with the type integer expected by parameter $xLen of phpseclib\Crypt\RSA::_i2osp(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

2732
        $s = $this->_i2osp($s, /** @scrutinizer ignore-type */ $this->k);
Loading history...
2733
2734
        // Output the signature S
2735
2736
        return $s;
2737
    }
2738
2739
    /**
2740
     * RSASSA-PKCS1-V1_5-VERIFY
2741
     *
2742
     * See {@link http://tools.ietf.org/html/rfc3447#section-8.2.2 RFC3447#section-8.2.2}.
2743
     *
2744
     * @access private
2745
     * @param string $m
2746
     * @return string
2747
     */
2748
    public function _rsassa_pkcs1_v1_5_verify($m, $s)
2749
    {
2750
        // Length checking
2751
2752
        if (strlen($s) != $this->k) {
2753
            user_error('Invalid signature');
2754
            return false;
2755
        }
2756
2757
        // RSA verification
2758
2759
        $s = $this->_os2ip($s);
2760
        $m2 = $this->_rsavp1($s);
2761
        if ($m2 === false) {
2762
            user_error('Invalid signature');
2763
            return false;
2764
        }
2765
        $em = $this->_i2osp($m2, $this->k);
0 ignored issues
show
$this->k of type phpseclib\Math\BigInteger is incompatible with the type integer expected by parameter $xLen of phpseclib\Crypt\RSA::_i2osp(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

2765
        $em = $this->_i2osp($m2, /** @scrutinizer ignore-type */ $this->k);
Loading history...
2766
        if ($em === false) {
2767
            user_error('Invalid signature');
2768
            return false;
2769
        }
2770
2771
        // EMSA-PKCS1-v1_5 encoding
2772
2773
        $em2 = $this->_emsa_pkcs1_v1_5_encode($m, $this->k);
0 ignored issues
show
$this->k of type phpseclib\Math\BigInteger is incompatible with the type integer expected by parameter $emLen of phpseclib\Crypt\RSA::_emsa_pkcs1_v1_5_encode(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

2773
        $em2 = $this->_emsa_pkcs1_v1_5_encode($m, /** @scrutinizer ignore-type */ $this->k);
Loading history...
2774
        if ($em2 === false) {
2775
            user_error('RSA modulus too short');
2776
            return false;
2777
        }
2778
2779
        // Compare
2780
        return $this->_equals($em, $em2);
2781
    }
2782
2783
    /**
2784
     * Set Encryption Mode
2785
     *
2786
     * Valid values include self::ENCRYPTION_OAEP and self::ENCRYPTION_PKCS1.
2787
     *
2788
     * @access public
2789
     * @param int $mode
2790
     */
2791
    public function setEncryptionMode($mode)
2792
    {
2793
        $this->encryptionMode = $mode;
2794
    }
2795
2796
    /**
2797
     * Set Signature Mode
2798
     *
2799
     * Valid values include self::SIGNATURE_PSS and self::SIGNATURE_PKCS1
2800
     *
2801
     * @access public
2802
     * @param int $mode
2803
     */
2804
    public function setSignatureMode($mode)
2805
    {
2806
        $this->signatureMode = $mode;
2807
    }
2808
2809
    /**
2810
     * Set public key comment.
2811
     *
2812
     * @access public
2813
     * @param string $comment
2814
     */
2815
    public function setComment($comment)
2816
    {
2817
        $this->comment = $comment;
2818
    }
2819
2820
    /**
2821
     * Get public key comment.
2822
     *
2823
     * @access public
2824
     * @return string
2825
     */
2826
    public function getComment()
2827
    {
2828
        return $this->comment;
2829
    }
2830
2831
    /**
2832
     * Encryption
2833
     *
2834
     * Both self::ENCRYPTION_OAEP and self::ENCRYPTION_PKCS1 both place limits on how long $plaintext can be.
2835
     * If $plaintext exceeds those limits it will be broken up so that it does and the resultant ciphertext's will
2836
     * be concatenated together.
2837
     *
2838
     * @see self::decrypt()
2839
     * @access public
2840
     * @param string $plaintext
2841
     * @return string
2842
     */
2843
    public function encrypt($plaintext)
2844
    {
2845
        switch ($this->encryptionMode) {
2846
            case self::ENCRYPTION_NONE:
2847
                $plaintext = str_split($plaintext, $this->k);
0 ignored issues
show
$this->k of type phpseclib\Math\BigInteger is incompatible with the type integer expected by parameter $split_length of str_split(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

2847
                $plaintext = str_split($plaintext, /** @scrutinizer ignore-type */ $this->k);
Loading history...
2848
                $ciphertext = '';
2849
                foreach ($plaintext as $m) {
2850
                    $ciphertext.= $this->_raw_encrypt($m);
2851
                }
2852
                return $ciphertext;
2853
            case self::ENCRYPTION_PKCS1:
2854
                $length = $this->k - 11;
2855
                if ($length <= 0) {
2856
                    return false;
2857
                }
2858
2859
                $plaintext = str_split($plaintext, $length);
2860
                $ciphertext = '';
2861
                foreach ($plaintext as $m) {
2862
                    $ciphertext.= $this->_rsaes_pkcs1_v1_5_encrypt($m);
2863
                }
2864
                return $ciphertext;
2865
            //case self::ENCRYPTION_OAEP:
2866
            default:
2867
                $length = $this->k - 2 * $this->hLen - 2;
2868
                if ($length <= 0) {
2869
                    return false;
2870
                }
2871
2872
                $plaintext = str_split($plaintext, $length);
2873
                $ciphertext = '';
2874
                foreach ($plaintext as $m) {
2875
                    $ciphertext.= $this->_rsaes_oaep_encrypt($m);
2876
                }
2877
                return $ciphertext;
2878
        }
2879
    }
2880
2881
    /**
2882
     * Decryption
2883
     *
2884
     * @see self::encrypt()
2885
     * @access public
2886
     * @param string $plaintext
2887
     * @return string
2888
     */
2889
    public function decrypt($ciphertext)
2890
    {
2891
        if ($this->k <= 0) {
2892
            return false;
2893
        }
2894
2895
        $ciphertext = str_split($ciphertext, $this->k);
0 ignored issues
show
$this->k of type phpseclib\Math\BigInteger is incompatible with the type integer expected by parameter $split_length of str_split(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

2895
        $ciphertext = str_split($ciphertext, /** @scrutinizer ignore-type */ $this->k);
Loading history...
2896
        $ciphertext[count($ciphertext) - 1] = str_pad($ciphertext[count($ciphertext) - 1], $this->k, chr(0), STR_PAD_LEFT);
0 ignored issues
show
$this->k of type phpseclib\Math\BigInteger is incompatible with the type integer expected by parameter $pad_length of str_pad(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

2896
        $ciphertext[count($ciphertext) - 1] = str_pad($ciphertext[count($ciphertext) - 1], /** @scrutinizer ignore-type */ $this->k, chr(0), STR_PAD_LEFT);
Loading history...
2897
2898
        $plaintext = '';
2899
2900
        switch ($this->encryptionMode) {
2901
            case self::ENCRYPTION_NONE:
2902
                $decrypt = '_raw_encrypt';
2903
                break;
2904
            case self::ENCRYPTION_PKCS1:
2905
                $decrypt = '_rsaes_pkcs1_v1_5_decrypt';
2906
                break;
2907
            //case self::ENCRYPTION_OAEP:
2908
            default:
2909
                $decrypt = '_rsaes_oaep_decrypt';
2910
        }
2911
2912
        foreach ($ciphertext as $c) {
2913
            $temp = $this->$decrypt($c);
2914
            if ($temp === false) {
2915
                return false;
2916
            }
2917
            $plaintext.= $temp;
2918
        }
2919
2920
        return $plaintext;
2921
    }
2922
2923
    /**
2924
     * Create a signature
2925
     *
2926
     * @see self::verify()
2927
     * @access public
2928
     * @param string $message
2929
     * @return string
2930
     */
2931
    public function sign($message)
2932
    {
2933
        if (empty($this->modulus) || empty($this->exponent)) {
2934
            return false;
2935
        }
2936
2937
        switch ($this->signatureMode) {
2938
            case self::SIGNATURE_PKCS1:
2939
                return $this->_rsassa_pkcs1_v1_5_sign($message);
2940
            //case self::SIGNATURE_PSS:
2941
            default:
2942
                return $this->_rsassa_pss_sign($message);
2943
        }
2944
    }
2945
2946
    /**
2947
     * Verifies a signature
2948
     *
2949
     * @see self::sign()
2950
     * @access public
2951
     * @param string $message
2952
     * @param string $signature
2953
     * @return bool
2954
     */
2955
    public function verify($message, $signature)
2956
    {
2957
        if (empty($this->modulus) || empty($this->exponent)) {
2958
            return false;
2959
        }
2960
2961
        switch ($this->signatureMode) {
2962
            case self::SIGNATURE_PKCS1:
2963
                return $this->_rsassa_pkcs1_v1_5_verify($message, $signature);
2964
            //case self::SIGNATURE_PSS:
2965
            default:
2966
                return $this->_rsassa_pss_verify($message, $signature);
2967
        }
2968
    }
2969
2970
    /**
2971
     * Extract raw BER from Base64 encoding
2972
     *
2973
     * @access private
2974
     * @param string $str
2975
     * @return string
2976
     */
2977
    public function _extractBER($str)
2978
    {
2979
        /* X.509 certs are assumed to be base64 encoded but sometimes they'll have additional things in them
2980
         * above and beyond the ceritificate.
2981
         * ie. some may have the following preceding the -----BEGIN CERTIFICATE----- line:
2982
         *
2983
         * Bag Attributes
2984
         *     localKeyID: 01 00 00 00
2985
         * subject=/O=organization/OU=org unit/CN=common name
2986
         * issuer=/O=organization/CN=common name
2987
         */
2988
        $temp = preg_replace('#.*?^-+[^-]+-+[\r\n ]*$#ms', '', $str, 1);
2989
        // remove the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- stuff
2990
        $temp = preg_replace('#-+[^-]+-+#', '', $temp);
2991
        // remove new lines
2992
        $temp = str_replace(array("\r", "\n", ' '), '', $temp);
2993
        $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false;
2994
        return $temp != false ? $temp : $str;
2995
    }
2996
}
2997