Failed Conditions
Push — PHPSecLib_Rid ( 9d7c65...0687d3 )
by Florent
03:08
created

RSA::sign()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 1 Features 0
Metric Value
c 4
b 1
f 0
dl 0
loc 9
rs 9.6666
cc 3
eloc 4
nc 2
nop 1
1
<?php
2
3
/*
4
 * The MIT License (MIT)
5
 *
6
 * Copyright (c) 2014-2016 Spomky-Labs
7
 *
8
 * This software may be modified and distributed under the terms
9
 * of the MIT license.  See the LICENSE file for details.
10
 */
11
12
namespace Jose\Util;
13
14
final class RSA
15
{
16
    /**
17
     * Optimal Asymmetric Encryption Padding (OAEP).
18
     */
19
    const ENCRYPTION_OAEP = 1;
20
21
    /**
22
     * PKCS#1 padding.
23
     */
24
    const ENCRYPTION_PKCS1 = 2;
25
26
    /**
27
     * Probabilistic Signature Scheme for signing.
28
     */
29
    const SIGNATURE_PSS = 1;
30
    /**
31
     * PKCS#1 scheme.
32
     */
33
    const SIGNATURE_PKCS1 = 2;
34
35
    /**
36
     * ASN1 Integer.
37
     */
38
    const ASN1_INTEGER = 2;
39
40
    /**
41
     * ASN1 Bit String.
42
     */
43
    const ASN1_BITSTRING = 3;
44
45
    /**
46
     * ASN1 Octet String.
47
     */
48
    const ASN1_OCTETSTRING = 4;
49
50
    /**
51
     * ASN1 Object Identifier.
52
     */
53
    const ASN1_OBJECT = 6;
54
55
    /**
56
     * ASN1 Sequence (with the constucted bit set).
57
     */
58
    const ASN1_SEQUENCE = 48;
59
60
    /**
61
     * To use the pure-PHP implementation.
62
     */
63
    const MODE_INTERNAL = 1;
64
65
    /**
66
     * To use the OpenSSL library.
67
     */
68
    const MODE_OPENSSL = 2;
69
70
    /**
71
     * PKCS#1 formatted private key.
72
     */
73
    const PRIVATE_FORMAT_PKCS1 = 0;
74
75
    /**
76
     * PuTTY formatted private key.
77
     */
78
    const PRIVATE_FORMAT_PUTTY = 1;
79
80
    /**
81
     * XML formatted private key.
82
     */
83
    const PRIVATE_FORMAT_XML = 2;
84
85
    /**
86
     * PKCS#8 formatted private key.
87
     */
88
    const PRIVATE_FORMAT_PKCS8 = 8;
89
90
    /**
91
     * Raw public key.
92
     */
93
    const PUBLIC_FORMAT_RAW = 3;
94
95
    /**
96
     * PKCS#1 formatted public key (raw).
97
     */
98
    const PUBLIC_FORMAT_PKCS1 = 4;
99
    const PUBLIC_FORMAT_PKCS1_RAW = 4;
100
101
    /**
102
     * XML formatted public key.
103
     */
104
    const PUBLIC_FORMAT_XML = 5;
105
106
    /**
107
     * OpenSSH formatted public key.
108
     */
109
    const PUBLIC_FORMAT_OPENSSH = 6;
110
111
    /**
112
     * PKCS#1 formatted public key (encapsulated).
113
     */
114
    const PUBLIC_FORMAT_PKCS8 = 7;
115
116
    /**
117
     * Precomputed Zero.
118
     *
119
     * @var \Jose\Util\BigInteger
120
     */
121
    private $zero;
122
123
    /**
124
     * Precomputed One.
125
     *
126
     * @var \Jose\Util\BigInteger
127
     */
128
    private $one;
129
130
    /**
131
     * Private Key Format.
132
     *
133
     * @var int
134
     */
135
    private $privateKeyFormat = self::PRIVATE_FORMAT_PKCS1;
136
137
    /**
138
     * Public Key Format.
139
     *
140
     * @var int
141
     */
142
    private $publicKeyFormat = self::PUBLIC_FORMAT_PKCS8;
143
144
    /**
145
     * Modulus (ie. n).
146
     *
147
     * @var \Jose\Util\BigInteger
148
     */
149
    private $modulus;
150
151
    /**
152
     * Modulus length.
153
     *
154
     * @var int
155
     */
156
    private $k;
157
158
    /**
159
     * Exponent (ie. e or d).
160
     *
161
     * @var \Jose\Util\BigInteger
162
     */
163
    private $exponent;
164
165
    /**
166
     * Primes for Chinese Remainder Theorem (ie. p and q).
167
     *
168
     * @var array
169
     */
170
    private $primes;
171
172
    /**
173
     * Exponents for Chinese Remainder Theorem (ie. dP and dQ).
174
     *
175
     * @var array
176
     */
177
    private $exponents;
178
179
    /**
180
     * Coefficients for Chinese Remainder Theorem (ie. qInv).
181
     *
182
     * @var array
183
     */
184
    private $coefficients;
185
186
    /**
187
     * Hash name.
188
     *
189
     * @var string
190
     */
191
    private $hashName;
192
193
    /**
194
     * Hash function.
195
     *
196
     * @var \Jose\Util\Hash
197
     */
198
    private $hash;
199
200
    /**
201
     * Length of hash function output.
202
     *
203
     * @var int
204
     */
205
    private $hLen;
206
207
    /**
208
     * Length of salt.
209
     *
210
     * @var int
211
     */
212
    private $sLen;
213
214
    /**
215
     * Hash function for the Mask Generation Function.
216
     *
217
     * @var \Jose\Util\Hash
218
     */
219
    private $mgfHash;
220
221
    /**
222
     * Length of MGF hash function output.
223
     *
224
     * @var int
225
     */
226
    private $mgfHLen;
227
228
    /**
229
     * Encryption mode.
230
     *
231
     * @var int
232
     */
233
    private $encryptionMode = self::ENCRYPTION_OAEP;
234
235
    /**
236
     * Signature mode.
237
     *
238
     * @var int
239
     */
240
    private $signatureMode = self::SIGNATURE_PSS;
241
242
    /**
243
     * Public Exponent.
244
     *
245
     * @var mixed
246
     */
247
    private $publicExponent = false;
248
249
    /**
250
     * Password.
251
     *
252
     * @var string
253
     */
254
    private $password = false;
255
256
    /**
257
     * Components.
258
     *
259
     * @var array
260
     */
261
    private $components = [];
0 ignored issues
show
Unused Code introduced by
The property $components is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
262
263
    /**
264
     * Current String.
265
     *
266
     * @var mixed
267
     */
268
    private $current;
269
270
    /**
271
     * OpenSSL configuration file name.
272
     *
273
     * @var mixed
274
     */
275
    private $configFile;
276
277
    /**
278
     * Public key comment field.
279
     *
280
     * @var string
281
     */
282
    private $comment = 'phpseclib-generated-key';
283
284
    /**
285
     * RSA constructor.
286
     */
287
    public function __construct()
288
    {
289
        $this->configFile = dirname(__FILE__).'/../openssl.cnf';
290
291
        if (!defined('CRYPT_RSA_MODE')) {
292
            switch (true) {
293
                // Math/BigInteger's openssl requirements are a little less stringent than Crypt/RSA's. in particular,
294
                // Math/BigInteger doesn't require an openssl.cfg file whereas Crypt/RSA does. so if Math/BigInteger
295
                // can't use OpenSSL it can be pretty trivially assumed, then, that Crypt/RSA can't either.
296
                case defined('MATH_BIGINTEGER_OPENSSL_DISABLE'):
297
                    define('CRYPT_RSA_MODE', self::MODE_INTERNAL);
298
                    break;
299
                case extension_loaded('openssl') && file_exists($this->configFile):
300
                    // some versions of XAMPP have mismatched versions of OpenSSL which causes it not to work
301
                    ob_start();
302
                    @phpinfo();
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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

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

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

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

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

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

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

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

// count() is only called once
for ($i=0, $c=count($collection); $i<$c; $i++) { }
Loading history...
311
                            $fullVersion = trim(str_replace('=>', '', strip_tags($matches[2][$i])));
312
313
                            // Remove letter part in OpenSSL version
314
                            if (!preg_match('/(\d+\.\d+\.\d+)/i', $fullVersion, $m)) {
315
                                $versions[$matches[1][$i]] = $fullVersion;
316
                            } else {
317
                                $versions[$matches[1][$i]] = $m[0];
318
                            }
319
                        }
320
                    }
321
322
                    // it doesn't appear that OpenSSL versions were reported upon until PHP 5.3+
323
                    switch (true) {
324
                        case !isset($versions['Header']):
325
                        case !isset($versions['Library']):
326
                        case $versions['Header'] == $versions['Library']:
327
                            define('CRYPT_RSA_MODE', self::MODE_OPENSSL);
328
                            break;
329
                        default:
330
                            define('CRYPT_RSA_MODE', self::MODE_INTERNAL);
331
                            define('MATH_BIGINTEGER_OPENSSL_DISABLE', true);
332
                    }
333
                    break;
334
                default:
335
                    define('CRYPT_RSA_MODE', self::MODE_INTERNAL);
336
            }
337
        }
338
339
        $this->zero = BigInteger::createFromDecimalString('0');
340
        $this->one = BigInteger::createFromDecimalString('1');
341
342
        $this->hash = new Hash('sha1');
343
        $this->hLen = 20;
344
        $this->hashName = 'sha1';
345
        $this->mgfHash = new Hash('sha1');
346
        $this->mgfHLen = 20;
347
    }
348
349
    /**
350
     * Break a public or private key down into its constituant components.
351
     *
352
     * @param string $key
353
     * @param int    $type
354
     *
355
     * @return array
356
     */
357
    private function _parseKey($key, $type)
358
    {
359
        if ($type != self::PUBLIC_FORMAT_RAW && !is_string($key)) {
360
            return false;
361
        }
362
363
        switch ($type) {
364
            case self::PRIVATE_FORMAT_PKCS1:
365
            case self::PRIVATE_FORMAT_PKCS8:
366
            case self::PUBLIC_FORMAT_PKCS1:
367
                $decoded = $this->_extractBER($key);
368
369
                if ($decoded !== false) {
370
                    $key = $decoded;
371
                }
372
373
                $components = [];
374
375
                if (ord($this->_string_shift($key)) != self::ASN1_SEQUENCE) {
376
                    return false;
377
                }
378
                if ($this->_decodeLength($key) != strlen($key)) {
379
                    return false;
380
                }
381
382
                $tag = ord($this->_string_shift($key));
383
384
                if ($tag == self::ASN1_INTEGER && substr($key, 0, 3) == "\x01\x00\x30") {
385
                    $this->_string_shift($key, 3);
386
                    $tag = self::ASN1_SEQUENCE;
387
                }
388
389
                if ($tag == self::ASN1_SEQUENCE) {
390
                    $temp = $this->_string_shift($key, $this->_decodeLength($key));
391
                    if (ord($this->_string_shift($temp)) != self::ASN1_OBJECT) {
392
                        return false;
393
                    }
394
                    $length = $this->_decodeLength($temp);
395
                    switch ($this->_string_shift($temp, $length)) {
396
                        case "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01": // rsaEncryption
397
                            break;
398
                    }
399
                    $tag = ord($this->_string_shift($key)); // skip over the BIT STRING / OCTET STRING tag
400
                    $this->_decodeLength($key); // skip over the BIT STRING / OCTET STRING length
401
                    if ($tag == self::ASN1_BITSTRING) {
402
                        $this->_string_shift($key);
403
                    }
404
                    if (ord($this->_string_shift($key)) != self::ASN1_SEQUENCE) {
405
                        return false;
406
                    }
407
                    if ($this->_decodeLength($key) != strlen($key)) {
408
                        return false;
409
                    }
410
                    $tag = ord($this->_string_shift($key));
411
                }
412
                if ($tag != self::ASN1_INTEGER) {
413
                    return false;
414
                }
415
416
                $length = $this->_decodeLength($key);
417
                $temp = $this->_string_shift($key, $length);
418
                if (strlen($temp) != 1 || ord($temp) > 2) {
419
                    $components['modulus'] = BigInteger::createFromBinaryString($temp);
420
                    $this->_string_shift($key); // skip over self::ASN1_INTEGER
421
                    $length = $this->_decodeLength($key);
422
                    $components[$type == self::PUBLIC_FORMAT_PKCS1 ? 'publicExponent' : 'privateExponent'] = BigInteger::createFromBinaryString($this->_string_shift($key, $length));
423
424
                    return $components;
425
                }
426
                if (ord($this->_string_shift($key)) != self::ASN1_INTEGER) {
427
                    return false;
428
                }
429
                $length = $this->_decodeLength($key);
430
                $components['modulus'] = BigInteger::createFromBinaryString($this->_string_shift($key, $length));
431
                $this->_string_shift($key);
432
                $length = $this->_decodeLength($key);
433
                $components['publicExponent'] = BigInteger::createFromBinaryString($this->_string_shift($key, $length));
434
                $this->_string_shift($key);
435
                $length = $this->_decodeLength($key);
436
                $components['privateExponent'] = BigInteger::createFromBinaryString($this->_string_shift($key, $length));
437
                $this->_string_shift($key);
438
                $length = $this->_decodeLength($key);
439
                $components['primes'] = [1 => BigInteger::createFromBinaryString($this->_string_shift($key, $length))];
440
                $this->_string_shift($key);
441
                $length = $this->_decodeLength($key);
442
                $components['primes'][] = BigInteger::createFromBinaryString($this->_string_shift($key, $length));
443
                $this->_string_shift($key);
444
                $length = $this->_decodeLength($key);
445
                $components['exponents'] = [1 => BigInteger::createFromBinaryString($this->_string_shift($key, $length))];
446
                $this->_string_shift($key);
447
                $length = $this->_decodeLength($key);
448
                $components['exponents'][] = BigInteger::createFromBinaryString($this->_string_shift($key, $length));
449
                $this->_string_shift($key);
450
                $length = $this->_decodeLength($key);
451
                $components['coefficients'] = [2 => BigInteger::createFromBinaryString($this->_string_shift($key, $length))];
452
453
454
                return $components;
455
        }
456
    }
457
458
    /**
459
     * Data Handler.
460
     *
461
     * Called by xml_set_character_data_handler()
462
     *
463
     * @param resource $parser
464
     * @param string   $data
465
     */
466
    public function _data_handler($parser, $data)
0 ignored issues
show
Unused Code introduced by
The parameter $parser is not used and could be removed.

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

Loading history...
467
    {
468
        if (!isset($this->current) || is_object($this->current)) {
469
            return;
470
        }
471
        $this->current .= trim($data);
472
    }
473
474
    /**
475
     * Loads a public or private key.
476
     *
477
     * @param string $key
478
     * @param bool   $type optional
479
     *
480
     * @return bool
481
     */
482
    public function loadKey($key, $type = false)
483
    {
484
        if ($key instanceof self) {
485
            $this->privateKeyFormat = $key->privateKeyFormat;
486
            $this->publicKeyFormat = $key->publicKeyFormat;
487
            $this->k = $key->k;
488
            $this->hLen = $key->hLen;
489
            $this->sLen = $key->sLen;
490
            $this->mgfHLen = $key->mgfHLen;
491
            $this->encryptionMode = $key->encryptionMode;
492
            $this->signatureMode = $key->signatureMode;
493
            $this->password = $key->password;
494
            $this->configFile = $key->configFile;
495
            $this->comment = $key->comment;
496
497
            if (is_object($key->hash)) {
498
                $this->hash = new Hash($key->hash->getHash());
499
            }
500
            if (is_object($key->mgfHash)) {
501
                $this->mgfHash = new Hash($key->mgfHash->getHash());
502
            }
503
504
            if (is_object($key->modulus)) {
505
                $this->modulus = $key->modulus->copy();
506
            }
507
            if (is_object($key->exponent)) {
508
                $this->exponent = $key->exponent->copy();
509
            }
510
            if (is_object($key->publicExponent)) {
511
                $this->publicExponent = $key->publicExponent->copy();
512
            }
513
514
            $this->primes = [];
515
            $this->exponents = [];
516
            $this->coefficients = [];
517
518
            foreach ($this->primes as $prime) {
519
                $this->primes[] = $prime->copy();
520
            }
521
            foreach ($this->exponents as $exponent) {
522
                $this->exponents[] = $exponent->copy();
523
            }
524
            foreach ($this->coefficients as $coefficient) {
525
                $this->coefficients[] = $coefficient->copy();
526
            }
527
528
            return true;
529
        }
530
531
        if ($type === false) {
532
            $types = [
533
                self::PUBLIC_FORMAT_RAW,
534
                self::PRIVATE_FORMAT_PKCS1,
535
                self::PRIVATE_FORMAT_XML,
536
                self::PRIVATE_FORMAT_PUTTY,
537
                self::PUBLIC_FORMAT_OPENSSH,
538
            ];
539
            foreach ($types as $type) {
540
                $components = $this->_parseKey($key, $type);
541
                if ($components !== false) {
542
                    break;
543
                }
544
            }
545
        } else {
546
            $components = $this->_parseKey($key, $type);
0 ignored issues
show
Documentation introduced by
$type is of type boolean, but the function expects a integer.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
547
        }
548
549
        if ($components === false) {
0 ignored issues
show
Bug introduced by
The variable $components does not seem to be defined for all execution paths leading up to this point.

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

Let’s take a look at an example:

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

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

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

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

Available Fixes

  1. Check for existence of the variable explicitly:

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

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

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
550
            return false;
551
        }
552
553
        if (isset($components['comment']) && $components['comment'] !== false) {
554
            $this->comment = $components['comment'];
555
        }
556
        $this->modulus = $components['modulus'];
557
        $this->k = strlen($this->modulus->toBytes());
558
        $this->exponent = isset($components['privateExponent']) ? $components['privateExponent'] : $components['publicExponent'];
559
        if (isset($components['primes'])) {
560
            $this->primes = $components['primes'];
561
            $this->exponents = $components['exponents'];
562
            $this->coefficients = $components['coefficients'];
563
            $this->publicExponent = $components['publicExponent'];
564
        } else {
565
            $this->primes = [];
566
            $this->exponents = [];
567
            $this->coefficients = [];
568
            $this->publicExponent = false;
569
        }
570
571
        switch ($type) {
572
            case self::PUBLIC_FORMAT_OPENSSH:
573
            case self::PUBLIC_FORMAT_RAW:
574
                $this->setPublicKey();
575
                break;
576
            case self::PRIVATE_FORMAT_PKCS1:
577
                switch (true) {
578
                    case strpos($key, '-BEGIN PUBLIC KEY-') !== false:
579
                    case strpos($key, '-BEGIN RSA PUBLIC KEY-') !== false:
580
                        $this->setPublicKey();
581
                }
582
        }
583
584
        return true;
585
    }
586
587
    /**
588
     * DER-decode the length.
589
     *
590
     * @param string $string
591
     *
592
     * @return int
593
     */
594
    private function _decodeLength(&$string)
595
    {
596
        $length = ord($this->_string_shift($string));
597
        if ($length & 0x80) { // definite length, long form
598
            $length &= 0x7F;
599
            $temp = $this->_string_shift($string, $length);
600
            list(, $length) = unpack('N', substr(str_pad($temp, 4, chr(0), STR_PAD_LEFT), -4));
601
        }
602
603
        return $length;
604
    }
605
606
    /**
607
     * String Shift.
608
     *
609
     * @param string $string
610
     * @param int    $index
611
     *
612
     * @return string
613
     */
614
    private function _string_shift(&$string, $index = 1)
615
    {
616
        $substr = substr($string, 0, $index);
617
        $string = substr($string, $index);
618
619
        return $substr;
620
    }
621
622
    /**
623
     * Determines which hashing function should be used.
624
     *
625
     * @param string $hash
626
     */
627
    public function setHash($hash)
628
    {
629
        switch ($hash) {
630
            case 'sha1':
631
                $this->hLen = 20;
632
                break;
633
            case 'sha256':
634
                $this->hLen = 32;
635
                break;
636
            case 'sha384':
637
                $this->hLen = 48;
638
                break;
639
            case 'sha512':
640
                $this->hLen = 64;
641
                break;
642
            default:
643
                throw new \InvalidArgumentException('Unsupported hash algorithm.');
644
        }
645
        $this->hash = new Hash($hash);
646
    }
647
648
    /**
649
     * Determines which hashing function should be used for the mask generation function.
650
     *
651
     * @param string $hash
652
     */
653
    public function setMGFHash($hash)
654
    {
655
        switch ($hash) {
656
            case 'sha1':
657
                $this->mgfHLen = 20;
658
                break;
659
            case 'sha256':
660
                $this->mgfHash = 32;
0 ignored issues
show
Documentation Bug introduced by
It seems like 32 of type integer is incompatible with the declared type object<Jose\Util\Hash> of property $mgfHash.

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

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

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

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

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

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

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

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

Loading history...
667
                break;
668
            default:
669
                throw new \InvalidArgumentException('Unsupported hash algorithm.');
670
        }
671
        $this->mgfHash = new Hash($hash);
672
    }
673
674
    /**
675
     * Determines the salt length.
676
     *
677
     * @param int $sLen
678
     */
679
    public function setSaltLength($sLen)
680
    {
681
        $this->sLen = $sLen;
682
    }
683
684
    /**
685
     * Integer-to-Octet-String primitive.
686
     *
687
     * @param \Jose\Util\BigInteger $x
688
     * @param int                   $xLen
689
     *
690
     * @return string
691
     */
692
    private function _i2osp($x, $xLen)
693
    {
694
        $x = $x->toBytes();
695
        if (strlen($x) > $xLen) {
696
            user_error('Integer too large');
697
698
            return false;
699
        }
700
701
        return str_pad($x, $xLen, chr(0), STR_PAD_LEFT);
702
    }
703
704
    /**
705
     * Octet-String-to-Integer primitive.
706
     *
707
     * @param string $x
708
     *
709
     * @return \Jose\Util\BigInteger
710
     */
711
    private function _os2ip($x)
712
    {
713
        return BigInteger::createFromBinaryString($x);
714
    }
715
716
    /**
717
     * Exponentiate with or without Chinese Remainder Theorem.
718
     *
719
     * @param \Jose\Util\BigInteger $x
720
     *
721
     * @return \Jose\Util\BigInteger
722
     */
723
    private function _exponentiate($x)
724
    {
725
        if (empty($this->primes) || empty($this->coefficients) || empty($this->exponents)) {
726
            return $x->modPow($this->exponent, $this->modulus);
0 ignored issues
show
Bug Compatibility introduced by
The expression $x->modPow($this->exponent, $this->modulus); of type Jose\Util\BigInteger|boolean adds the type boolean to the return on line 726 which is incompatible with the return type documented by Jose\Util\RSA::_exponentiate of type Jose\Util\BigInteger.
Loading history...
727
        }
728
729
        $num_primes = count($this->primes);
730
731
        if (defined('CRYPT_RSA_DISABLE_BLINDING')) {
732
            $m_i = [
733
                1 => $x->modPow($this->exponents[1], $this->primes[1]),
734
                2 => $x->modPow($this->exponents[2], $this->primes[2]),
735
            ];
736
            $h = $m_i[1]->subtract($m_i[2]);
0 ignored issues
show
Bug introduced by
It seems like $m_i[2] can also be of type boolean; however, Jose\Util\BigInteger::subtract() does only seem to accept object<Jose\Util\BigInteger>, maybe add an additional type check?

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

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

    return array();
}

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

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

Loading history...
737
            $h = $h->multiply($this->coefficients[2]);
738
            list(, $h) = $h->divide($this->primes[1]);
739
            $m = $m_i[2]->add($h->multiply($this->primes[2]));
740
741
            $r = $this->primes[1];
742
            for ($i = 3; $i <= $num_primes; $i++) {
743
                $m_i = $x->modPow($this->exponents[$i], $this->primes[$i]);
744
745
                $r = $r->multiply($this->primes[$i - 1]);
746
747
                $h = $m_i->subtract($m);
748
                $h = $h->multiply($this->coefficients[$i]);
749
                list(, $h) = $h->divide($this->primes[$i]);
750
751
                $m = $m->add($r->multiply($h));
752
            }
753
        } else {
754
            $smallest = $this->primes[1];
755
            for ($i = 2; $i <= $num_primes; $i++) {
756
                if ($smallest->compare($this->primes[$i]) > 0) {
757
                    $smallest = $this->primes[$i];
758
                }
759
            }
760
761
            $one = BigInteger::createFromDecimalString('1');
762
763
            $r = $one->random($one, $smallest->subtract($one));
764
765
            $m_i = [
766
                1 => $this->_blind($x, $r, 1),
767
                2 => $this->_blind($x, $r, 2),
768
            ];
769
            $h = $m_i[1]->subtract($m_i[2]);
770
            $h = $h->multiply($this->coefficients[2]);
771
            list(, $h) = $h->divide($this->primes[1]);
772
            $m = $m_i[2]->add($h->multiply($this->primes[2]));
773
774
            $r = $this->primes[1];
775
            for ($i = 3; $i <= $num_primes; $i++) {
776
                $m_i = $this->_blind($x, $r, $i);
777
778
                $r = $r->multiply($this->primes[$i - 1]);
779
780
                $h = $m_i->subtract($m);
781
                $h = $h->multiply($this->coefficients[$i]);
782
                list(, $h) = $h->divide($this->primes[$i]);
783
784
                $m = $m->add($r->multiply($h));
785
            }
786
        }
787
788
        return $m;
789
    }
790
791
    /**
792
     * Performs RSA Blinding.
793
     *
794
     * @param \Jose\Util\BigInteger $x
795
     * @param \Jose\Util\BigInteger $r
796
     * @param int                   $i
797
     *
798
     * @return \Jose\Util\BigInteger
799
     */
800
    private function _blind($x, $r, $i)
801
    {
802
        $x = $x->multiply($r->modPow($this->publicExponent, $this->primes[$i]));
0 ignored issues
show
Bug introduced by
It seems like $r->modPow($this->public...ent, $this->primes[$i]) targeting Jose\Util\BigInteger::modPow() can also be of type boolean; however, Jose\Util\BigInteger::multiply() does only seem to accept object<Jose\Util\BigInteger>, maybe add an additional type check?

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

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

An additional type check may prevent trouble.

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

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

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

    return array();
}

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

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

Loading history...
807
        list(, $x) = $x->divide($this->primes[$i]);
808
809
        return $x;
810
    }
811
812
    /**
813
     * Performs blinded RSA equality testing.
814
     *
815
     * @param string $x
816
     * @param string $y
817
     *
818
     * @return bool
819
     */
820
    private function _equals($x, $y)
821
    {
822
        if (strlen($x) != strlen($y)) {
823
            return false;
824
        }
825
826
        $result = 0;
827
        for ($i = 0; $i < strlen($x); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
Consider avoiding function calls on each iteration of the for loop.

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

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

// count() is only called once
for ($i=0, $c=count($collection); $i<$c; $i++) { }
Loading history...
828
            $result |= ord($x[$i]) ^ ord($y[$i]);
829
        }
830
831
        return $result == 0;
832
    }
833
834
    /**
835
     * RSAEP.
836
     *
837
     * @param \Jose\Util\BigInteger $m
838
     *
839
     * @return \Jose\Util\BigInteger
840
     */
841
    private function _rsaep($m)
842
    {
843
        if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) {
844
            user_error('Message representative out of range');
845
846
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by Jose\Util\RSA::_rsaep of type Jose\Util\BigInteger.

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

Let’s take a look at an example:

class Author {
    private $name;

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

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

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

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

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

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

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

Loading history...
847
        }
848
849
        return $this->_exponentiate($m);
850
    }
851
852
    /**
853
     * RSADP.
854
     *
855
     * @param \Jose\Util\BigInteger $c
856
     *
857
     * @return \Jose\Util\BigInteger
858
     */
859
    private function _rsadp($c)
860
    {
861
        if ($c->compare($this->zero) < 0 || $c->compare($this->modulus) > 0) {
862
            user_error('Ciphertext representative out of range');
863
864
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by Jose\Util\RSA::_rsadp of type Jose\Util\BigInteger.

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

Let’s take a look at an example:

class Author {
    private $name;

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

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

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

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

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

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

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

Loading history...
865
        }
866
867
        return $this->_exponentiate($c);
868
    }
869
870
    /**
871
     * RSASP1.
872
     *
873
     * @param \Jose\Util\BigInteger $m
874
     *
875
     * @return \Jose\Util\BigInteger
876
     */
877
    private function _rsasp1($m)
878
    {
879
        if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) {
880
            user_error('Message representative out of range');
881
882
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by Jose\Util\RSA::_rsasp1 of type Jose\Util\BigInteger.

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

Let’s take a look at an example:

class Author {
    private $name;

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

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

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

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

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

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

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

Loading history...
883
        }
884
885
        return $this->_exponentiate($m);
886
    }
887
888
    /**
889
     * RSAVP1.
890
     *
891
     * @param \Jose\Util\BigInteger $s
892
     *
893
     * @return \Jose\Util\BigInteger
894
     */
895
    private function _rsavp1($s)
896
    {
897
        if ($s->compare($this->zero) < 0 || $s->compare($this->modulus) > 0) {
898
            user_error('Signature representative out of range');
899
900
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by Jose\Util\RSA::_rsavp1 of type Jose\Util\BigInteger.

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

Let’s take a look at an example:

class Author {
    private $name;

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

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

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

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

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

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

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

Loading history...
901
        }
902
903
        return $this->_exponentiate($s);
904
    }
905
906
    /**
907
     * MGF1.
908
     *
909
     * @param string $mgfSeed
910
     * @param int    $maskLen
911
     *
912
     * @return string
913
     */
914
    private function _mgf1($mgfSeed, $maskLen)
915
    {
916
        // if $maskLen would yield strings larger than 4GB, PKCS#1 suggests a "Mask too long" error be output.
917
918
        $t = '';
919
        $count = ceil($maskLen / $this->mgfHLen);
920
        for ($i = 0; $i < $count; $i++) {
921
            $c = pack('N', $i);
922
            $t .= $this->mgfHash->hash($mgfSeed.$c);
923
        }
924
925
        return substr($t, 0, $maskLen);
926
    }
927
928
    /**
929
     * RSAES-OAEP-ENCRYPT.
930
     *
931
     * @param string $m
932
     * @param string $l
933
     *
934
     * @return string
935
     */
936
    private function _rsaes_oaep_encrypt($m, $l = '')
937
    {
938
        $mLen = strlen($m);
939
940
        // Length checking
941
942
        // if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
943
        // be output.
944
945
        if ($mLen > $this->k - 2 * $this->hLen - 2) {
946
            user_error('Message too long');
947
948
            return false;
949
        }
950
951
        // EME-OAEP encoding
952
953
        $lHash = $this->hash->hash($l);
954
        $ps = str_repeat(chr(0), $this->k - $mLen - 2 * $this->hLen - 2);
955
        $db = $lHash.$ps.chr(1).$m;
956
        $seed = random_bytes($this->hLen);
957
        $dbMask = $this->_mgf1($seed, $this->k - $this->hLen - 1);
958
        $maskedDB = $db ^ $dbMask;
959
        $seedMask = $this->_mgf1($maskedDB, $this->hLen);
960
        $maskedSeed = $seed ^ $seedMask;
961
        $em = chr(0).$maskedSeed.$maskedDB;
962
963
        // RSA encryption
964
965
        $m = $this->_os2ip($em);
966
        $c = $this->_rsaep($m);
967
        $c = $this->_i2osp($c, $this->k);
968
969
        // Output the ciphertext C
970
971
        return $c;
972
    }
973
974
    /**
975
     * RSAES-OAEP-DECRYPT.
976
     *
977
     * @param string $c
978
     * @param string $l
979
     *
980
     * @return string
981
     */
982
    private function _rsaes_oaep_decrypt($c, $l = '')
983
    {
984
        // Length checking
985
986
        // if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
987
        // be output.
988
989
        if (strlen($c) != $this->k || $this->k < 2 * $this->hLen + 2) {
990
            user_error('Decryption error');
991
992
            return false;
993
        }
994
995
        // RSA decryption
996
997
        $c = $this->_os2ip($c);
998
        $m = $this->_rsadp($c);
999
        if ($m === false) {
1000
            user_error('Decryption error');
1001
1002
            return false;
1003
        }
1004
        $em = $this->_i2osp($m, $this->k);
1005
1006
        // EME-OAEP decoding
1007
1008
        $lHash = $this->hash->hash($l);
1009
        $y = ord($em[0]);
0 ignored issues
show
Unused Code introduced by
$y is not used, you could remove the assignment.

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

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

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

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

Loading history...
1010
        $maskedSeed = substr($em, 1, $this->hLen);
1011
        $maskedDB = substr($em, $this->hLen + 1);
1012
        $seedMask = $this->_mgf1($maskedDB, $this->hLen);
1013
        $seed = $maskedSeed ^ $seedMask;
1014
        $dbMask = $this->_mgf1($seed, $this->k - $this->hLen - 1);
1015
        $db = $maskedDB ^ $dbMask;
1016
        $lHash2 = substr($db, 0, $this->hLen);
1017
        $m = substr($db, $this->hLen);
1018
        if ($lHash != $lHash2) {
1019
            user_error('Decryption error');
1020
1021
            return false;
1022
        }
1023
        $m = ltrim($m, chr(0));
1024
        if (ord($m[0]) != 1) {
1025
            user_error('Decryption error');
1026
1027
            return false;
1028
        }
1029
1030
        // Output the message M
1031
1032
        return substr($m, 1);
1033
    }
1034
1035
    /**
1036
     * EMSA-PSS-ENCODE.
1037
     *
1038
     * @param string $m
1039
     * @param int    $emBits
1040
     *
1041
     * @return bool
1042
     */
1043
    private function _emsa_pss_encode($m, $emBits)
1044
    {
1045
        // if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
1046
        // be output.
1047
1048
        $emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8)
1049
        $sLen = $this->sLen ? $this->sLen : $this->hLen;
1050
1051
        $mHash = $this->hash->hash($m);
1052
        if ($emLen < $this->hLen + $sLen + 2) {
1053
            user_error('Encoding error');
1054
1055
            return false;
1056
        }
1057
1058
        $salt = random_bytes($sLen);
1059
        $m2 = "\0\0\0\0\0\0\0\0".$mHash.$salt;
1060
        $h = $this->hash->hash($m2);
1061
        $ps = str_repeat(chr(0), $emLen - $sLen - $this->hLen - 2);
1062
        $db = $ps.chr(1).$salt;
1063
        $dbMask = $this->_mgf1($h, $emLen - $this->hLen - 1);
1064
        $maskedDB = $db ^ $dbMask;
1065
        $maskedDB[0] = ~chr(0xFF << ($emBits & 7)) & $maskedDB[0];
1066
        $em = $maskedDB.$h.chr(0xBC);
1067
1068
        return $em;
1069
    }
1070
1071
    /**
1072
     * EMSA-PSS-VERIFY.
1073
     *
1074
     * @param string $m
1075
     * @param string $em
1076
     * @param int    $emBits
1077
     *
1078
     * @return string
1079
     */
1080
    private function _emsa_pss_verify($m, $em, $emBits)
1081
    {
1082
        // if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
1083
        // be output.
1084
1085
        $emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8);
1086
        $sLen = $this->sLen ? $this->sLen : $this->hLen;
1087
1088
        $mHash = $this->hash->hash($m);
1089
        if ($emLen < $this->hLen + $sLen + 2) {
1090
            return false;
1091
        }
1092
1093
        if ($em[strlen($em) - 1] != chr(0xBC)) {
1094
            return false;
1095
        }
1096
1097
        $maskedDB = substr($em, 0, -$this->hLen - 1);
1098
        $h = substr($em, -$this->hLen - 1, $this->hLen);
1099
        $temp = chr(0xFF << ($emBits & 7));
1100
        if ((~$maskedDB[0] & $temp) != $temp) {
1101
            return false;
1102
        }
1103
        $dbMask = $this->_mgf1($h, $emLen - $this->hLen - 1);
1104
        $db = $maskedDB ^ $dbMask;
1105
        $db[0] = ~chr(0xFF << ($emBits & 7)) & $db[0];
1106
        $temp = $emLen - $this->hLen - $sLen - 2;
1107
        if (substr($db, 0, $temp) != str_repeat(chr(0), $temp) || ord($db[$temp]) != 1) {
1108
            return false;
1109
        }
1110
        $salt = substr($db, $temp + 1); // should be $sLen long
1111
        $m2 = "\0\0\0\0\0\0\0\0".$mHash.$salt;
1112
        $h2 = $this->hash->hash($m2);
1113
1114
        return $this->_equals($h, $h2);
1115
    }
1116
1117
    /**
1118
     * RSASSA-PSS-SIGN.
1119
     *
1120
     * @param string $m
1121
     *
1122
     * @return string
1123
     */
1124
    private function _rsassa_pss_sign($m)
1125
    {
1126
        // EMSA-PSS encoding
1127
1128
        $em = $this->_emsa_pss_encode($m, 8 * $this->k - 1);
1129
1130
        // RSA signature
1131
1132
        $m = $this->_os2ip($em);
0 ignored issues
show
Security Bug introduced by
It seems like $em defined by $this->_emsa_pss_encode($m, 8 * $this->k - 1) on line 1128 can also be of type false; however, Jose\Util\RSA::_os2ip() does only seem to accept string, did you maybe forget to handle an error condition?

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

Consider the follow example

<?php

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

    return false;
}

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

Loading history...
1133
        $s = $this->_rsasp1($m);
1134
        $s = $this->_i2osp($s, $this->k);
1135
1136
        // Output the signature S
1137
1138
        return $s;
1139
    }
1140
1141
    /**
1142
     * RSASSA-PSS-VERIFY.
1143
     *
1144
     * @param string $m
1145
     * @param string $s
1146
     *
1147
     * @return string
1148
     */
1149
    private function _rsassa_pss_verify($m, $s)
1150
    {
1151
        // Length checking
1152
1153
        if (strlen($s) != $this->k) {
1154
            user_error('Invalid signature');
1155
1156
            return false;
1157
        }
1158
1159
        // RSA verification
1160
1161
        $modBits = 8 * $this->k;
1162
1163
        $s2 = $this->_os2ip($s);
1164
        $m2 = $this->_rsavp1($s2);
1165
        if ($m2 === false) {
1166
            user_error('Invalid signature');
1167
1168
            return false;
1169
        }
1170
        $em = $this->_i2osp($m2, $modBits >> 3);
1171
        if ($em === false) {
1172
            user_error('Invalid signature');
1173
1174
            return false;
1175
        }
1176
1177
        // EMSA-PSS verification
1178
1179
        return $this->_emsa_pss_verify($m, $em, $modBits - 1);
1180
    }
1181
1182
    /**
1183
     * Set Encryption Mode.
1184
     *
1185
     * Valid values include self::ENCRYPTION_OAEP and self::ENCRYPTION_PKCS1.
1186
     *
1187
     * @param int $mode
1188
     */
1189
    public function setEncryptionMode($mode)
1190
    {
1191
        $this->encryptionMode = $mode;
1192
    }
1193
1194
    /**
1195
     * Set Signature Mode.
1196
     *
1197
     * Valid values include self::SIGNATURE_PSS and self::SIGNATURE_PKCS1
1198
     *
1199
     * @param int $mode
1200
     */
1201
    public function setSignatureMode($mode)
1202
    {
1203
        $this->signatureMode = $mode;
1204
    }
1205
1206
    /**
1207
     * Encryption.
1208
     *
1209
     * Both self::ENCRYPTION_OAEP and self::ENCRYPTION_PKCS1 both place limits on how long $plaintext can be.
1210
     * If $plaintext exceeds those limits it will be broken up so that it does and the resultant ciphertext's will
1211
     * be concatenated together.
1212
     *
1213
     * @see self::decrypt()
1214
     *
1215
     * @param string $plaintext
1216
     *
1217
     * @return string
1218
     */
1219
    public function encrypt($plaintext)
1220
    {
1221
        $length = $this->k - 2 * $this->hLen - 2;
1222
        if ($length <= 0) {
1223
            return false;
1224
        }
1225
1226
        $plaintext = str_split($plaintext, $length);
1227
        $ciphertext = '';
1228
        foreach ($plaintext as $m) {
1229
            $ciphertext .= $this->_rsaes_oaep_encrypt($m);
1230
        }
1231
1232
        return $ciphertext;
1233
    }
1234
1235
    /**
1236
     * Decryption.
1237
     *
1238
     * @param string $ciphertext
1239
     *
1240
     * @return string
1241
     */
1242
    public function decrypt($ciphertext)
1243
    {
1244
        if ($this->k <= 0) {
1245
            return false;
1246
        }
1247
1248
        $ciphertext = str_split($ciphertext, $this->k);
1249
        $ciphertext[count($ciphertext) - 1] = str_pad($ciphertext[count($ciphertext) - 1], $this->k, chr(0), STR_PAD_LEFT);
1250
1251
        $plaintext = '';
1252
1253
        foreach ($ciphertext as $c) {
1254
            $temp = $this->_rsaes_oaep_decrypt($c);
1255
            if ($temp === false) {
1256
                return false;
1257
            }
1258
            $plaintext .= $temp;
1259
        }
1260
1261
        return $plaintext;
1262
    }
1263
1264
    /**
1265
     * Create a signature.
1266
     *
1267
     * @param string $message
1268
     *
1269
     * @return string
1270
     */
1271
    public function sign($message)
1272
    {
1273
        if (empty($this->modulus) || empty($this->exponent)) {
1274
            return false;
1275
        }
1276
1277
1278
        return $this->_rsassa_pss_sign($message);
1279
    }
1280
1281
    /**
1282
     * Verifies a signature.
1283
     *
1284
     * @param string $message
1285
     * @param string $signature
1286
     *
1287
     * @return bool
1288
     */
1289
    public function verify($message, $signature)
1290
    {
1291
        if (empty($this->modulus) || empty($this->exponent)) {
1292
            return false;
1293
        }
1294
1295
        return $this->_rsassa_pss_verify($message, $signature);
1296
    }
1297
1298
    /**
1299
     * Extract raw BER from Base64 encoding.
1300
     *
1301
     * @param string $str
1302
     *
1303
     * @return string
1304
     */
1305
    private function _extractBER($str)
1306
    {
1307
        $temp = preg_replace('#.*?^-+[^-]+-+[\r\n ]*$#ms', '', $str, 1);
1308
        // remove the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- stuff
1309
        $temp = preg_replace('#-+[^-]+-+#', '', $temp);
1310
        // remove new lines
1311
        $temp = str_replace(["\r", "\n", ' '], '', $temp);
1312
        $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false;
1313
1314
        return $temp != false ? $temp : $str;
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $temp of type string|false against false; this is ambiguous if the string can be empty. Consider using a strict comparison !== instead.
Loading history...
1315
    }
1316
1317
    /**
1318
     * Defines the public key.
1319
     *
1320
     * @param string $key optional
1321
     *
1322
     * @return bool
1323
     */
1324
    private function setPublicKey($key = false)
1325
    {
1326
        if ($key === false && !empty($this->modulus)) {
1327
            $this->publicExponent = $this->exponent;
1328
1329
            return true;
1330
        }
1331
    }
1332
}
1333