Failed Conditions
Push — PHPSecLib_Rid ( fbc2c9...ddade2 )
by Florent
02:46
created

RSA::loadKey()   C

Complexity

Conditions 7
Paths 8

Size

Total Lines 31
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 7
Bugs 1 Features 0
Metric Value
c 7
b 1
f 0
dl 0
loc 31
rs 6.7272
cc 7
eloc 21
nc 8
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
use Base64Url\Base64Url;
15
use Jose\Object\JWKInterface;
16
17
final class RSA
18
{
19
    /**
20
     * ASN1 Integer.
21
     */
22
    const ASN1_INTEGER = 2;
23
24
    /**
25
     * ASN1 Bit String.
26
     */
27
    const ASN1_BITSTRING = 3;
28
29
    /**
30
     * ASN1 Octet String.
31
     */
32
    const ASN1_OCTETSTRING = 4;
33
34
    /**
35
     * ASN1 Object Identifier.
36
     */
37
    const ASN1_OBJECT = 6;
38
39
    /**
40
     * ASN1 Sequence (with the constucted bit set).
41
     */
42
    const ASN1_SEQUENCE = 48;
43
44
    /**
45
     * To use the pure-PHP implementation.
46
     */
47
    const MODE_INTERNAL = 1;
48
49
    /**
50
     * To use the OpenSSL library.
51
     */
52
    const MODE_OPENSSL = 2;
53
54
    /**
55
     * PKCS#1 formatted private key.
56
     */
57
    const PRIVATE_FORMAT_PKCS1 = 0;
58
59
    /**
60
     * PuTTY formatted private key.
61
     */
62
    const PRIVATE_FORMAT_PUTTY = 1;
63
64
    /**
65
     * XML formatted private key.
66
     */
67
    const PRIVATE_FORMAT_XML = 2;
68
69
    /**
70
     * PKCS#8 formatted private key.
71
     */
72
    const PRIVATE_FORMAT_PKCS8 = 8;
73
74
    /**
75
     * Raw public key.
76
     */
77
    const PUBLIC_FORMAT_RAW = 3;
78
79
    /**
80
     * PKCS#1 formatted public key (raw).
81
     */
82
    const PUBLIC_FORMAT_PKCS1 = 4;
83
84
    /**
85
     * Precomputed Zero.
86
     *
87
     * @var \Jose\Util\BigInteger
88
     */
89
    private $zero;
90
91
    /**
92
     * Precomputed One.
93
     *
94
     * @var \Jose\Util\BigInteger
95
     */
96
    private $one;
97
98
    /**
99
     * Modulus (ie. n).
100
     *
101
     * @var \Jose\Util\BigInteger
102
     */
103
    private $modulus;
104
105
    /**
106
     * Modulus length.
107
     *
108
     * @var int
109
     */
110
    private $k;
111
112
    /**
113
     * Exponent (ie. e or d).
114
     *
115
     * @var \Jose\Util\BigInteger
116
     */
117
    private $exponent;
118
119
    /**
120
     * Primes for Chinese Remainder Theorem (ie. p and q).
121
     *
122
     * @var \Jose\Util\BigInteger[]
123
     */
124
    private $primes;
125
126
    /**
127
     * Exponents for Chinese Remainder Theorem (ie. dP and dQ).
128
     *
129
     * @var \Jose\Util\BigInteger[]
130
     */
131
    private $exponents;
132
133
    /**
134
     * Coefficients for Chinese Remainder Theorem (ie. qInv).
135
     *
136
     * @var \Jose\Util\BigInteger[]
137
     */
138
    private $coefficients;
139
140
    /**
141
     * Hash function.
142
     *
143
     * @var \Jose\Util\Hash
144
     */
145
    private $hash;
146
147
    /**
148
     * Length of salt.
149
     *
150
     * @var int
151
     */
152
    private $sLen;
153
154
    /**
155
     * Hash function for the Mask Generation Function.
156
     *
157
     * @var \Jose\Util\Hash
158
     */
159
    private $mgfHash;
160
161
    /**
162
     * Public Exponent.
163
     *
164
     * @var mixed
165
     */
166
    private $publicExponent = false;
167
168
    /**
169
     * RSA constructor.
170
     */
171
    public function __construct()
172
    {
173
        $this->zero = BigInteger::createFromDecimalString('0');
174
        $this->one = BigInteger::createFromDecimalString('1');
175
176
        $this->hash = Hash::sha1();
177
        $this->mgfHash = Hash::sha1();
178
    }
179
180
    /**
181
     * Loads a public or private key.
182
     *
183
     * @param \Jose\Object\JWKInterface $key
184
     */
185
    public function loadKey(JWKInterface $key)
186
    {
187
        $this->modulus = BigInteger::createFromBinaryString(Base64Url::decode($key->get('n')));
188
        $this->k = strlen($this->modulus->toBytes());
189
190
        if ($key->has('d')) {
191
            $this->exponent = BigInteger::createFromBinaryString(Base64Url::decode($key->get('d')));
192
            $this->publicExponent = BigInteger::createFromBinaryString(Base64Url::decode($key->get('e')));
193
        } else {
194
            $this->exponent = BigInteger::createFromBinaryString(Base64Url::decode($key->get('e')));
195
        }
196
197
        if ($key->has('p') && $key->has('q')) {
198
            $this->primes = [
199
                BigInteger::createFromBinaryString(Base64Url::decode($key->get('p'))),
200
                BigInteger::createFromBinaryString(Base64Url::decode($key->get('q'))),
201
            ];
202
        } else {
203
            $this->primes = [];
204
        }
205
206
        if ($key->has('dp') && $key->has('dq') && $key->has('qi')) {
207
            $this->coefficients = [
208
                BigInteger::createFromBinaryString(Base64Url::decode($key->get('dp'))),
209
                BigInteger::createFromBinaryString(Base64Url::decode($key->get('dq'))),
210
                BigInteger::createFromBinaryString(Base64Url::decode($key->get('qi'))),
211
            ];
212
        } else {
213
            $this->coefficients = [];
214
        }
215
    }
216
217
    /**
218
     * Determines which hashing function should be used.
219
     *
220
     * @param string $hash
221
     */
222
    public function setHash($hash)
223
    {
224
        $this->hash = Hash::$hash();
225
    }
226
227
    /**
228
     * Determines which hashing function should be used for the mask generation function.
229
     *
230
     * @param string $hash
231
     */
232
    public function setMGFHash($hash)
233
    {
234
        $this->mgfHash = Hash::$hash();
235
    }
236
237
    /**
238
     * Determines the salt length.
239
     *
240
     * @param int $sLen
241
     */
242
    public function setSaltLength($sLen)
243
    {
244
        $this->sLen = $sLen;
245
    }
246
247
    /**
248
     * Integer-to-Octet-String primitive.
249
     *
250
     * @param \Jose\Util\BigInteger $x
251
     * @param int                   $xLen
252
     *
253
     * @return string
254
     */
255
    private function _i2osp($x, $xLen)
256
    {
257
        $x = $x->toBytes();
258
        if (strlen($x) > $xLen) {
259
260
            return false;
261
        }
262
263
        return str_pad($x, $xLen, chr(0), STR_PAD_LEFT);
264
    }
265
266
    /**
267
     * Octet-String-to-Integer primitive.
268
     *
269
     * @param string $x
270
     *
271
     * @return \Jose\Util\BigInteger
272
     */
273
    private function _os2ip($x)
274
    {
275
        return BigInteger::createFromBinaryString($x);
276
    }
277
278
    /**
279
     * Exponentiate with or without Chinese Remainder Theorem.
280
     *
281
     * @param \Jose\Util\BigInteger $x
282
     *
283
     * @return \Jose\Util\BigInteger
284
     */
285
    private function _exponentiate($x)
286
    {
287
        if (empty($this->primes) || empty($this->coefficients) || empty($this->exponents)) {
288
            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 288 which is incompatible with the return type documented by Jose\Util\RSA::_exponentiate of type Jose\Util\BigInteger.
Loading history...
289
        }
290
291
        $num_primes = count($this->primes);
292
293
        $smallest = $this->primes[1];
294
        for ($i = 2; $i <= $num_primes; $i++) {
295
            if ($smallest->compare($this->primes[$i]) > 0) {
296
                $smallest = $this->primes[$i];
297
            }
298
        }
299
300
        $one = BigInteger::createFromDecimalString('1');
301
302
        $r = $one->random($one, $smallest->subtract($one));
303
304
        $m_i = [
305
            1 => $this->_blind($x, $r, 1),
306
            2 => $this->_blind($x, $r, 2),
307
        ];
308
        $h = $m_i[1]->subtract($m_i[2]);
309
        $h = $h->multiply($this->coefficients[2]);
310
        list(, $h) = $h->divide($this->primes[1]);
311
        $m = $m_i[2]->add($h->multiply($this->primes[2]));
312
313
        $r = $this->primes[1];
314
        for ($i = 3; $i <= $num_primes; $i++) {
315
            $m_i = $this->_blind($x, $r, $i);
316
317
            $r = $r->multiply($this->primes[$i - 1]);
318
319
            $h = $m_i->subtract($m);
320
            $h = $h->multiply($this->coefficients[$i]);
321
            list(, $h) = $h->divide($this->primes[$i]);
322
323
            $m = $m->add($r->multiply($h));
324
        }
325
326
        return $m;
327
    }
328
329
    /**
330
     * Performs RSA Blinding.
331
     *
332
     * @param \Jose\Util\BigInteger $x
333
     * @param \Jose\Util\BigInteger $r
334
     * @param int                   $i
335
     *
336
     * @return \Jose\Util\BigInteger
337
     */
338
    private function _blind($x, $r, $i)
339
    {
340
        $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...
341
        $x = $x->modPow($this->exponents[$i], $this->primes[$i]);
342
343
        $r = $r->modInverse($this->primes[$i]);
344
        $x = $x->multiply($r);
0 ignored issues
show
Bug introduced by
It seems like $r defined by $r->modInverse($this->primes[$i]) on line 343 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...
345
        list(, $x) = $x->divide($this->primes[$i]);
346
347
        return $x;
348
    }
349
350
    /**
351
     * Performs blinded RSA equality testing.
352
     *
353
     * @param string $x
354
     * @param string $y
355
     *
356
     * @return bool
357
     */
358
    private function _equals($x, $y)
359
    {
360
        if (strlen($x) != strlen($y)) {
361
            return false;
362
        }
363
364
        $result = 0;
365
        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...
366
            $result |= ord($x[$i]) ^ ord($y[$i]);
367
        }
368
369
        return $result == 0;
370
    }
371
372
    /**
373
     * RSAEP.
374
     *
375
     * @param \Jose\Util\BigInteger $m
376
     *
377
     * @return \Jose\Util\BigInteger
378
     */
379
    private function _rsaep($m)
380
    {
381
        if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) {
382
383
            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...
384
        }
385
386
        return $this->_exponentiate($m);
387
    }
388
389
    /**
390
     * RSADP.
391
     *
392
     * @param \Jose\Util\BigInteger $c
393
     *
394
     * @return \Jose\Util\BigInteger
395
     */
396
    private function _rsadp($c)
397
    {
398
        if ($c->compare($this->zero) < 0 || $c->compare($this->modulus) > 0) {
399
400
            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...
401
        }
402
403
        return $this->_exponentiate($c);
404
    }
405
406
    /**
407
     * RSASP1.
408
     *
409
     * @param \Jose\Util\BigInteger $m
410
     *
411
     * @return \Jose\Util\BigInteger
412
     */
413
    private function _rsasp1($m)
414
    {
415
        if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) {
416
417
            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...
418
        }
419
420
        return $this->_exponentiate($m);
421
    }
422
423
    /**
424
     * RSAVP1.
425
     *
426
     * @param \Jose\Util\BigInteger $s
427
     *
428
     * @return \Jose\Util\BigInteger
429
     */
430
    private function _rsavp1($s)
431
    {
432
        if ($s->compare($this->zero) < 0 || $s->compare($this->modulus) > 0) {
433
434
            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...
435
        }
436
437
        return $this->_exponentiate($s);
438
    }
439
440
    /**
441
     * MGF1.
442
     *
443
     * @param string $mgfSeed
444
     * @param int    $maskLen
445
     *
446
     * @return string
447
     */
448
    private function _mgf1($mgfSeed, $maskLen)
449
    {
450
        // if $maskLen would yield strings larger than 4GB, PKCS#1 suggests a "Mask too long" error be output.
451
452
        $t = '';
453
        $count = ceil($maskLen / $this->mgfHash->getLength());
454
        for ($i = 0; $i < $count; $i++) {
455
            $c = pack('N', $i);
456
            $t .= $this->mgfHash->hash($mgfSeed.$c);
457
        }
458
459
        return substr($t, 0, $maskLen);
460
    }
461
462
    /**
463
     * RSAES-OAEP-ENCRYPT.
464
     *
465
     * @param string $m
466
     * @param string $l
467
     *
468
     * @return string
469
     */
470
    private function _rsaes_oaep_encrypt($m, $l = '')
471
    {
472
        $mLen = strlen($m);
473
474
        // Length checking
475
476
        // if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
477
        // be output.
478
479
        if ($mLen > $this->k - 2 * $this->hash->getLength() - 2) {
480
481
            return false;
482
        }
483
484
        // EME-OAEP encoding
485
486
        $lHash = $this->hash->hash($l);
487
        $ps = str_repeat(chr(0), $this->k - $mLen - 2 * $this->hash->getLength() - 2);
488
        $db = $lHash.$ps.chr(1).$m;
489
        $seed = random_bytes($this->hash->getLength());
490
        $dbMask = $this->_mgf1($seed, $this->k - $this->hash->getLength() - 1);
491
        $maskedDB = $db ^ $dbMask;
492
        $seedMask = $this->_mgf1($maskedDB, $this->hash->getLength());
493
        $maskedSeed = $seed ^ $seedMask;
494
        $em = chr(0).$maskedSeed.$maskedDB;
495
496
        // RSA encryption
497
498
        $m = $this->_os2ip($em);
499
        $c = $this->_rsaep($m);
500
        $c = $this->_i2osp($c, $this->k);
501
502
        // Output the ciphertext C
503
504
        return $c;
505
    }
506
507
    /**
508
     * RSAES-OAEP-DECRYPT.
509
     *
510
     * @param string $c
511
     * @param string $l
512
     *
513
     * @return string
514
     */
515
    private function _rsaes_oaep_decrypt($c, $l = '')
516
    {
517
        // Length checking
518
519
        // if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
520
        // be output.
521
522
        if (strlen($c) != $this->k || $this->k < 2 * $this->hash->getLength() + 2) {
523
524
            return false;
525
        }
526
527
        // RSA decryption
528
529
        $c = $this->_os2ip($c);
530
        $m = $this->_rsadp($c);
531
        if ($m === false) {
532
533
            return false;
534
        }
535
        $em = $this->_i2osp($m, $this->k);
536
537
        // EME-OAEP decoding
538
539
        $lHash = $this->hash->hash($l);
540
        $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...
541
        $maskedSeed = substr($em, 1, $this->hash->getLength());
542
        $maskedDB = substr($em, $this->hash->getLength() + 1);
543
        $seedMask = $this->_mgf1($maskedDB, $this->hash->getLength());
544
        $seed = $maskedSeed ^ $seedMask;
545
        $dbMask = $this->_mgf1($seed, $this->k - $this->hash->getLength() - 1);
546
        $db = $maskedDB ^ $dbMask;
547
        $lHash2 = substr($db, 0, $this->hash->getLength());
548
        $m = substr($db, $this->hash->getLength());
549
        if ($lHash != $lHash2) {
550
551
            return false;
552
        }
553
        $m = ltrim($m, chr(0));
554
        if (ord($m[0]) != 1) {
555
556
            return false;
557
        }
558
559
        // Output the message M
560
561
        return substr($m, 1);
562
    }
563
564
    /**
565
     * EMSA-PSS-ENCODE.
566
     *
567
     * @param string $m
568
     * @param int    $emBits
569
     *
570
     * @return bool
571
     */
572
    private function _emsa_pss_encode($m, $emBits)
573
    {
574
        // if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
575
        // be output.
576
577
        $emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8)
578
        $sLen = $this->sLen ? $this->sLen : $this->hash->getLength();
579
580
        $mHash = $this->hash->hash($m);
581
        if ($emLen < $this->hash->getLength() + $sLen + 2) {
582
583
            return false;
584
        }
585
586
        $salt = random_bytes($sLen);
587
        $m2 = "\0\0\0\0\0\0\0\0".$mHash.$salt;
588
        $h = $this->hash->hash($m2);
589
        $ps = str_repeat(chr(0), $emLen - $sLen - $this->hash->getLength() - 2);
590
        $db = $ps.chr(1).$salt;
591
        $dbMask = $this->_mgf1($h, $emLen - $this->hash->getLength() - 1);
592
        $maskedDB = $db ^ $dbMask;
593
        $maskedDB[0] = ~chr(0xFF << ($emBits & 7)) & $maskedDB[0];
594
        $em = $maskedDB.$h.chr(0xBC);
595
596
        return $em;
597
    }
598
599
    /**
600
     * EMSA-PSS-VERIFY.
601
     *
602
     * @param string $m
603
     * @param string $em
604
     * @param int    $emBits
605
     *
606
     * @return string
607
     */
608
    private function _emsa_pss_verify($m, $em, $emBits)
609
    {
610
        // if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
611
        // be output.
612
613
        $emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8);
614
        $sLen = $this->sLen ? $this->sLen : $this->hash->getLength();
615
616
        $mHash = $this->hash->hash($m);
617
        if ($emLen < $this->hash->getLength() + $sLen + 2) {
618
            return false;
619
        }
620
621
        if ($em[strlen($em) - 1] != chr(0xBC)) {
622
            return false;
623
        }
624
625
        $maskedDB = substr($em, 0, -$this->hash->getLength() - 1);
626
        $h = substr($em, -$this->hash->getLength() - 1, $this->hash->getLength());
627
        $temp = chr(0xFF << ($emBits & 7));
628
        if ((~$maskedDB[0] & $temp) != $temp) {
629
            return false;
630
        }
631
        $dbMask = $this->_mgf1($h, $emLen - $this->hash->getLength() - 1);
632
        $db = $maskedDB ^ $dbMask;
633
        $db[0] = ~chr(0xFF << ($emBits & 7)) & $db[0];
634
        $temp = $emLen - $this->hash->getLength() - $sLen - 2;
635
        if (substr($db, 0, $temp) != str_repeat(chr(0), $temp) || ord($db[$temp]) != 1) {
636
            return false;
637
        }
638
        $salt = substr($db, $temp + 1); // should be $sLen long
639
        $m2 = "\0\0\0\0\0\0\0\0".$mHash.$salt;
640
        $h2 = $this->hash->hash($m2);
641
642
        return $this->_equals($h, $h2);
643
    }
644
645
    /**
646
     * RSASSA-PSS-SIGN.
647
     *
648
     * @param string $m
649
     *
650
     * @return string
651
     */
652
    private function _rsassa_pss_sign($m)
653
    {
654
        // EMSA-PSS encoding
655
656
        $em = $this->_emsa_pss_encode($m, 8 * $this->k - 1);
657
658
        // RSA signature
659
660
        $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 656 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...
661
        $s = $this->_rsasp1($m);
662
        $s = $this->_i2osp($s, $this->k);
663
664
        // Output the signature S
665
666
        return $s;
667
    }
668
669
    /**
670
     * RSASSA-PSS-VERIFY.
671
     *
672
     * @param string $m
673
     * @param string $s
674
     *
675
     * @return string
676
     */
677
    private function _rsassa_pss_verify($m, $s)
678
    {
679
        // Length checking
680
681
        if (strlen($s) != $this->k) {
682
683
            return false;
684
        }
685
686
        // RSA verification
687
688
        $modBits = 8 * $this->k;
689
690
        $s2 = $this->_os2ip($s);
691
        $m2 = $this->_rsavp1($s2);
692
        if ($m2 === false) {
693
694
            return false;
695
        }
696
        $em = $this->_i2osp($m2, $modBits >> 3);
697
        if ($em === false) {
698
699
            return false;
700
        }
701
702
        // EMSA-PSS verification
703
704
        return $this->_emsa_pss_verify($m, $em, $modBits - 1);
705
    }
706
707
    /**
708
     * Encryption.
709
     *
710
     * Both self::ENCRYPTION_OAEP and self::ENCRYPTION_PKCS1 both place limits on how long $plaintext can be.
711
     * If $plaintext exceeds those limits it will be broken up so that it does and the resultant ciphertext's will
712
     * be concatenated together.
713
     *
714
     * @see self::decrypt()
715
     *
716
     * @param string $plaintext
717
     *
718
     * @return string
719
     */
720
    public function encrypt($plaintext)
721
    {
722
        $length = $this->k - 2 * $this->hash->getLength() - 2;
723
        if ($length <= 0) {
724
            return false;
725
        }
726
727
        $plaintext = str_split($plaintext, $length);
728
        $ciphertext = '';
729
        foreach ($plaintext as $m) {
730
            $ciphertext .= $this->_rsaes_oaep_encrypt($m);
731
        }
732
733
        return $ciphertext;
734
    }
735
736
    /**
737
     * Decryption.
738
     *
739
     * @param string $ciphertext
740
     *
741
     * @return string
742
     */
743
    public function decrypt($ciphertext)
744
    {
745
        if ($this->k <= 0) {
746
            return false;
747
        }
748
749
        $ciphertext = str_split($ciphertext, $this->k);
750
        $ciphertext[count($ciphertext) - 1] = str_pad($ciphertext[count($ciphertext) - 1], $this->k, chr(0), STR_PAD_LEFT);
751
752
        $plaintext = '';
753
754
        foreach ($ciphertext as $c) {
755
            $temp = $this->_rsaes_oaep_decrypt($c);
756
            if ($temp === false) {
757
                return false;
758
            }
759
            $plaintext .= $temp;
760
        }
761
762
        return $plaintext;
763
    }
764
765
    /**
766
     * Create a signature.
767
     *
768
     * @param string $message
769
     *
770
     * @return string
771
     */
772
    public function sign($message)
773
    {
774
        if (empty($this->modulus) || empty($this->exponent)) {
775
            return false;
776
        }
777
778
779
        return $this->_rsassa_pss_sign($message);
780
    }
781
782
    /**
783
     * Verifies a signature.
784
     *
785
     * @param string $message
786
     * @param string $signature
787
     *
788
     * @return bool
789
     */
790
    public function verify($message, $signature)
791
    {
792
        if (empty($this->modulus) || empty($this->exponent)) {
793
            return false;
794
        }
795
796
        return $this->_rsassa_pss_verify($message, $signature);
797
    }
798
}
799