Failed Conditions
Push — PHPSecLib_Rid ( 655cb9...fca9fd )
by Florent
02:40
created

RSA   C

Complexity

Total Complexity 71

Size/Duplication

Total Lines 671
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 14
Bugs 2 Features 0
Metric Value
wmc 71
c 14
b 2
f 0
lcom 1
cbo 4
dl 0
loc 671
rs 5.1868

23 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
C loadKey() 0 31 7
A getKeyComponent() 0 4 1
A convertIntegerToOctetString() 0 9 2
A convertOctetStringToInteger() 0 4 1
C _exponentiate() 0 43 7
A _blind() 0 11 1
A _equals() 0 13 3
A _rsaep() 0 8 3
A _rsadp() 0 8 3
A _rsasp1() 0 8 3
A _rsavp1() 0 8 3
A _mgf1() 0 13 2
B _rsaes_oaep_encrypt() 0 35 2
B _rsaes_oaep_decrypt() 0 43 6
B _emsa_pss_encode() 0 25 2
B _emsa_pss_verify() 0 36 6
A _rsassa_pss_sign() 0 16 1
B _rsassa_pss_verify() 0 26 4
A encrypt() 0 16 3
B decrypt() 0 23 4
A sign() 0 8 3
A verify() 0 8 3

How to fix   Complexity   

Complex Class

Complex classes like RSA often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use RSA, and based on these observations, apply Extract Interface, too.

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
     * Precomputed Zero.
21
     *
22
     * @var \Jose\Util\BigInteger
23
     */
24
    private $zero;
25
26
    /**
27
     * Precomputed One.
28
     *
29
     * @var \Jose\Util\BigInteger
30
     */
31
    private $one;
32
33
    /**
34
     * Modulus (ie. n).
35
     *
36
     * @var \Jose\Util\BigInteger
37
     */
38
    private $modulus;
39
40
    /**
41
     * Modulus length.
42
     *
43
     * @var int
44
     */
45
    private $k;
46
47
    /**
48
     * Exponent (ie. e or d).
49
     *
50
     * @var \Jose\Util\BigInteger
51
     */
52
    private $exponent;
53
54
    /**
55
     * Primes for Chinese Remainder Theorem (ie. p and q).
56
     *
57
     * @var \Jose\Util\BigInteger[]
58
     */
59
    private $primes;
60
61
    /**
62
     * Exponents for Chinese Remainder Theorem (ie. dP and dQ).
63
     *
64
     * @var \Jose\Util\BigInteger[]
65
     */
66
    private $exponents;
67
68
    /**
69
     * Coefficients for Chinese Remainder Theorem (ie. qInv).
70
     *
71
     * @var \Jose\Util\BigInteger[]
72
     */
73
    private $coefficients;
74
75
    /**
76
     * Public Exponent.
77
     *
78
     * @var mixed
79
     */
80
    private $publicExponent = false;
81
82
    /**
83
     * RSA constructor.
84
     */
85
    public function __construct()
86
    {
87
        $this->zero = BigInteger::createFromDecimalString('0');
88
        $this->one = BigInteger::createFromDecimalString('1');
89
    }
90
91
    /**
92
     * Loads a public or private key.
93
     *
94
     * @param \Jose\Object\JWKInterface $key
95
     */
96
    public function loadKey(JWKInterface $key)
97
    {
98
        $this->modulus = $this->getKeyComponent($key, 'n');
99
        $this->k = strlen($this->modulus->toBytes());
100
101
        if ($key->has('d')) {
102
            $this->exponent = $this->getKeyComponent($key, 'd');
103
            $this->publicExponent = $this->getKeyComponent($key, 'e');
104
        } else {
105
            $this->exponent = $this->getKeyComponent($key, 'e');
106
        }
107
108
        if ($key->has('p') && $key->has('q')) {
109
            $this->primes = [
110
                $this->getKeyComponent($key, 'p'),
111
                $this->getKeyComponent($key, 'q'),
112
            ];
113
        } else {
114
            $this->primes = [];
115
        }
116
117
        if ($key->has('dp') && $key->has('dq') && $key->has('qi')) {
118
            $this->coefficients = [
119
                $this->getKeyComponent($key, 'dp'),
120
                $this->getKeyComponent($key, 'dq'),
121
                $this->getKeyComponent($key, 'qi'),
122
            ];
123
        } else {
124
            $this->coefficients = [];
125
        }
126
    }
127
128
    /**
129
     * @param \Jose\Object\JWKInterface $jwk
130
     * @param string                    $key
131
     *
132
     * @return \Jose\Util\BigInteger
133
     */
134
    public function getKeyComponent(JWKInterface $jwk, $key)
135
    {
136
        return BigInteger::createFromBinaryString(Base64Url::decode($jwk->get($key)));
137
    }
138
139
140
    /**
141
     * Integer-to-Octet-String primitive.
142
     *
143
     * @param \Jose\Util\BigInteger $x
144
     * @param int                   $xLen
145
     *
146
     * @return string
147
     */
148
    private function convertIntegerToOctetString($x, $xLen)
149
    {
150
        $x = $x->toBytes();
151
        if (strlen($x) > $xLen) {
152
            return false;
153
        }
154
155
        return str_pad($x, $xLen, chr(0), STR_PAD_LEFT);
156
    }
157
158
    /**
159
     * Octet-String-to-Integer primitive.
160
     *
161
     * @param string $x
162
     *
163
     * @return \Jose\Util\BigInteger
164
     */
165
    private function convertOctetStringToInteger($x)
166
    {
167
        return BigInteger::createFromBinaryString($x);
168
    }
169
170
    /**
171
     * Exponentiate with or without Chinese Remainder Theorem.
172
     *
173
     * @param \Jose\Util\BigInteger $x
174
     *
175
     * @return \Jose\Util\BigInteger
176
     */
177
    private function _exponentiate($x)
178
    {
179
        if (empty($this->primes) || empty($this->coefficients) || empty($this->exponents)) {
180
            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 180 which is incompatible with the return type documented by Jose\Util\RSA::_exponentiate of type Jose\Util\BigInteger.
Loading history...
181
        }
182
183
        $num_primes = count($this->primes);
184
185
        $smallest = $this->primes[1];
186
        for ($i = 2; $i <= $num_primes; $i++) {
187
            if ($smallest->compare($this->primes[$i]) > 0) {
188
                $smallest = $this->primes[$i];
189
            }
190
        }
191
192
        $one = BigInteger::createFromDecimalString('1');
193
194
        $r = $one->random($one, $smallest->subtract($one));
195
196
        $m_i = [
197
            1 => $this->_blind($x, $r, 1),
198
            2 => $this->_blind($x, $r, 2),
199
        ];
200
        $h = $m_i[1]->subtract($m_i[2]);
201
        $h = $h->multiply($this->coefficients[2]);
202
        list(, $h) = $h->divide($this->primes[1]);
203
        $m = $m_i[2]->add($h->multiply($this->primes[2]));
204
205
        $r = $this->primes[1];
206
        for ($i = 3; $i <= $num_primes; $i++) {
207
            $m_i = $this->_blind($x, $r, $i);
208
209
            $r = $r->multiply($this->primes[$i - 1]);
210
211
            $h = $m_i->subtract($m);
212
            $h = $h->multiply($this->coefficients[$i]);
213
            list(, $h) = $h->divide($this->primes[$i]);
214
215
            $m = $m->add($r->multiply($h));
216
        }
217
218
        return $m;
219
    }
220
221
    /**
222
     * Performs RSA Blinding.
223
     *
224
     * @param \Jose\Util\BigInteger $x
225
     * @param \Jose\Util\BigInteger $r
226
     * @param int                   $i
227
     *
228
     * @return \Jose\Util\BigInteger
229
     */
230
    private function _blind($x, $r, $i)
231
    {
232
        $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...
233
        $x = $x->modPow($this->exponents[$i], $this->primes[$i]);
234
235
        $r = $r->modInverse($this->primes[$i]);
236
        $x = $x->multiply($r);
0 ignored issues
show
Bug introduced by
It seems like $r defined by $r->modInverse($this->primes[$i]) on line 235 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...
237
        list(, $x) = $x->divide($this->primes[$i]);
238
239
        return $x;
240
    }
241
242
    /**
243
     * Performs blinded RSA equality testing.
244
     *
245
     * @param string $x
246
     * @param string $y
247
     *
248
     * @return bool
249
     */
250
    private function _equals($x, $y)
251
    {
252
        if (strlen($x) != strlen($y)) {
253
            return false;
254
        }
255
256
        $result = 0;
257
        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...
258
            $result |= ord($x[$i]) ^ ord($y[$i]);
259
        }
260
261
        return $result == 0;
262
    }
263
264
    /**
265
     * RSAEP.
266
     *
267
     * @param \Jose\Util\BigInteger $m
268
     *
269
     * @return \Jose\Util\BigInteger|false
270
     */
271
    private function _rsaep($m)
272
    {
273
        if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) {
274
            return false;
275
        }
276
277
        return $this->_exponentiate($m);
278
    }
279
280
    /**
281
     * RSADP.
282
     *
283
     * @param \Jose\Util\BigInteger $c
284
     *
285
     * @return \Jose\Util\BigInteger|false
286
     */
287
    private function _rsadp($c)
288
    {
289
        if ($c->compare($this->zero) < 0 || $c->compare($this->modulus) > 0) {
290
            return false;
291
        }
292
293
        return $this->_exponentiate($c);
294
    }
295
296
    /**
297
     * RSASP1.
298
     *
299
     * @param \Jose\Util\BigInteger $m
300
     *
301
     * @return \Jose\Util\BigInteger|false
302
     */
303
    private function _rsasp1($m)
304
    {
305
        if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) {
306
            return false;
307
        }
308
309
        return $this->_exponentiate($m);
310
    }
311
312
    /**
313
     * RSAVP1.
314
     *
315
     * @param \Jose\Util\BigInteger $s
316
     *
317
     * @return \Jose\Util\BigInteger|false
318
     */
319
    private function _rsavp1($s)
320
    {
321
        if ($s->compare($this->zero) < 0 || $s->compare($this->modulus) > 0) {
322
            return false;
323
        }
324
325
        return $this->_exponentiate($s);
326
    }
327
328
    /**
329
     * MGF1.
330
     *
331
     * @param string          $mgfSeed
332
     * @param int             $maskLen
333
     * @param \Jose\Util\Hash $mgfHash
334
     *
335
     * @return string
336
     */
337
    private function _mgf1($mgfSeed, $maskLen, Hash $mgfHash)
338
    {
339
        // if $maskLen would yield strings larger than 4GB, PKCS#1 suggests a "Mask too long" error be output.
340
341
        $t = '';
342
        $count = ceil($maskLen / $mgfHash->getLength());
343
        for ($i = 0; $i < $count; $i++) {
344
            $c = pack('N', $i);
345
            $t .= $mgfHash->hash($mgfSeed.$c);
346
        }
347
348
        return substr($t, 0, $maskLen);
349
    }
350
351
    /**
352
     * RSAES-OAEP-ENCRYPT.
353
     *
354
     * @param string          $m
355
     * @param \Jose\Util\Hash $hash
356
     *
357
     * @return string
358
     */
359
    private function _rsaes_oaep_encrypt($m, Hash $hash)
360
    {
361
        $mLen = strlen($m);
362
363
        // Length checking
364
365
        // if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
366
        // be output.
367
368
        if ($mLen > $this->k - 2 * $hash->getLength() - 2) {
369
            return false;
370
        }
371
372
        // EME-OAEP encoding
373
374
        $lHash = $hash->hash('');
375
        $ps = str_repeat(chr(0), $this->k - $mLen - 2 * $hash->getLength() - 2);
376
        $db = $lHash.$ps.chr(1).$m;
377
        $seed = random_bytes($hash->getLength());
378
        $dbMask = $this->_mgf1($seed, $this->k - $hash->getLength() - 1, $hash/*MGF*/);
379
        $maskedDB = $db ^ $dbMask;
380
        $seedMask = $this->_mgf1($maskedDB, $hash->getLength(), $hash/*MGF*/);
381
        $maskedSeed = $seed ^ $seedMask;
382
        $em = chr(0).$maskedSeed.$maskedDB;
383
384
        // RSA encryption
385
386
        $m = $this->convertOctetStringToInteger($em);
387
        $c = $this->_rsaep($m);
388
        $c = $this->convertIntegerToOctetString($c, $this->k);
0 ignored issues
show
Security Bug introduced by
It seems like $c defined by $this->convertIntegerToOctetString($c, $this->k) on line 388 can also be of type false; however, Jose\Util\RSA::convertIntegerToOctetString() does only seem to accept object<Jose\Util\BigInteger>, did you maybe forget to handle an error condition?

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

Consider the follow example

<?php

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

    return false;
}

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

Loading history...
389
390
        // Output the ciphertext C
391
392
        return $c;
393
    }
394
395
    /**
396
     * RSAES-OAEP-DECRYPT.
397
     *
398
     * @param string $c
399
     * @param \Jose\Util\Hash $hash
400
     *
401
     * @return string
402
     */
403
    private function _rsaes_oaep_decrypt($c, Hash $hash)
404
    {
405
        // Length checking
406
407
        // if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
408
        // be output.
409
410
        if (strlen($c) != $this->k || $this->k < 2 * $hash->getLength() + 2) {
411
            return false;
412
        }
413
414
        // RSA decryption
415
416
        $c = $this->convertOctetStringToInteger($c);
417
        $m = $this->_rsadp($c);
418
        if ($m === false) {
419
            return false;
420
        }
421
        $em = $this->convertIntegerToOctetString($m, $this->k);
422
423
        // EME-OAEP decoding
424
425
        $lHash = $hash->hash('');
426
        $maskedSeed = substr($em, 1, $hash->getLength());
427
        $maskedDB = substr($em, $hash->getLength() + 1);
428
        $seedMask = $this->_mgf1($maskedDB, $hash->getLength(), $hash/*MGF*/);
429
        $seed = $maskedSeed ^ $seedMask;
430
        $dbMask = $this->_mgf1($seed, $this->k - $hash->getLength() - 1, $hash/*MGF*/);
431
        $db = $maskedDB ^ $dbMask;
432
        $lHash2 = substr($db, 0, $hash->getLength());
433
        $m = substr($db, $hash->getLength());
434
        if ($lHash != $lHash2) {
435
            return false;
436
        }
437
        $m = ltrim($m, chr(0));
438
        if (ord($m[0]) != 1) {
439
            return false;
440
        }
441
442
        // Output the message M
443
444
        return substr($m, 1);
445
    }
446
447
    /**
448
     * EMSA-PSS-ENCODE.
449
     *
450
     * @param string          $m
451
     * @param int             $emBits
452
     * @param \Jose\Util\Hash $hash
453
     *
454
     * @return bool
455
     */
456
    private function _emsa_pss_encode($m, $emBits, Hash $hash)
457
    {
458
        // if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
459
        // be output.
460
461
        $emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8)
462
        $sLen = $hash->getLength();
463
464
        $mHash = $hash->hash($m);
465
        if ($emLen < $hash->getLength() + $sLen + 2) {
466
            return false;
467
        }
468
469
        $salt = random_bytes($sLen);
470
        $m2 = "\0\0\0\0\0\0\0\0".$mHash.$salt;
471
        $h = $hash->hash($m2);
472
        $ps = str_repeat(chr(0), $emLen - $sLen - $hash->getLength() - 2);
473
        $db = $ps.chr(1).$salt;
474
        $dbMask = $this->_mgf1($h, $emLen - $hash->getLength() - 1, $hash/*MGF*/);
475
        $maskedDB = $db ^ $dbMask;
476
        $maskedDB[0] = ~chr(0xFF << ($emBits & 7)) & $maskedDB[0];
477
        $em = $maskedDB.$h.chr(0xBC);
478
479
        return $em;
480
    }
481
482
    /**
483
     * EMSA-PSS-VERIFY.
484
     *
485
     * @param string          $m
486
     * @param string          $em
487
     * @param int             $emBits
488
     * @param \Jose\Util\Hash $hash
489
     *
490
     * @return string
491
     */
492
    private function _emsa_pss_verify($m, $em, $emBits, Hash $hash)
493
    {
494
        // if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
495
        // be output.
496
497
        $emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8);
498
        $sLen = $hash->getLength();
499
500
        $mHash = $hash->hash($m);
501
        if ($emLen < $hash->getLength() + $sLen + 2) {
502
            return false;
503
        }
504
505
        if ($em[strlen($em) - 1] != chr(0xBC)) {
506
            return false;
507
        }
508
509
        $maskedDB = substr($em, 0, -$hash->getLength() - 1);
510
        $h = substr($em, -$hash->getLength() - 1, $hash->getLength());
511
        $temp = chr(0xFF << ($emBits & 7));
512
        if ((~$maskedDB[0] & $temp) != $temp) {
513
            return false;
514
        }
515
        $dbMask = $this->_mgf1($h, $emLen - $hash->getLength() - 1, $hash/*MGF*/);
516
        $db = $maskedDB ^ $dbMask;
517
        $db[0] = ~chr(0xFF << ($emBits & 7)) & $db[0];
518
        $temp = $emLen - $hash->getLength() - $sLen - 2;
519
        if (substr($db, 0, $temp) != str_repeat(chr(0), $temp) || ord($db[$temp]) != 1) {
520
            return false;
521
        }
522
        $salt = substr($db, $temp + 1); // should be $sLen long
523
        $m2 = "\0\0\0\0\0\0\0\0".$mHash.$salt;
524
        $h2 = $hash->hash($m2);
525
526
        return $this->_equals($h, $h2);
527
    }
528
529
    /**
530
     * RSASSA-PSS-SIGN.
531
     *
532
     * @param string          $m
533
     * @param \Jose\Util\Hash $hash
534
     *
535
     * @return string
536
     */
537
    private function _rsassa_pss_sign($m, Hash $hash)
538
    {
539
        // EMSA-PSS encoding
540
541
        $em = $this->_emsa_pss_encode($m, 8 * $this->k - 1, $hash);
542
543
        // RSA signature
544
545
        $m = $this->convertOctetStringToInteger($em);
0 ignored issues
show
Security Bug introduced by
It seems like $em defined by $this->_emsa_pss_encode(... * $this->k - 1, $hash) on line 541 can also be of type false; however, Jose\Util\RSA::convertOctetStringToInteger() 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...
546
        $s = $this->_rsasp1($m);
547
        $s = $this->convertIntegerToOctetString($s, $this->k);
0 ignored issues
show
Security Bug introduced by
It seems like $s defined by $this->convertIntegerToOctetString($s, $this->k) on line 547 can also be of type false; however, Jose\Util\RSA::convertIntegerToOctetString() does only seem to accept object<Jose\Util\BigInteger>, did you maybe forget to handle an error condition?

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

Consider the follow example

<?php

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

    return false;
}

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

Loading history...
548
549
        // Output the signature S
550
551
        return $s;
552
    }
553
554
    /**
555
     * RSASSA-PSS-VERIFY.
556
     *
557
     * @param string          $m
558
     * @param string          $s
559
     * @param \Jose\Util\Hash $hash
560
     *
561
     * @return string
562
     */
563
    private function _rsassa_pss_verify($m, $s, Hash $hash)
564
    {
565
        // Length checking
566
567
        if (strlen($s) != $this->k) {
568
            return false;
569
        }
570
571
        // RSA verification
572
573
        $modBits = 8 * $this->k;
574
575
        $s2 = $this->convertOctetStringToInteger($s);
576
        $m2 = $this->_rsavp1($s2);
577
        if ($m2 === false) {
578
            return false;
579
        }
580
        $em = $this->convertIntegerToOctetString($m2, $modBits >> 3);
581
        if ($em === false) {
582
            return false;
583
        }
584
585
        // EMSA-PSS verification
586
587
        return $this->_emsa_pss_verify($m, $em, $modBits - 1, $hash);
588
    }
589
590
    /**
591
     * Encryption.
592
     *
593
     * Both self::ENCRYPTION_OAEP and self::ENCRYPTION_PKCS1 both place limits on how long $plaintext can be.
594
     * If $plaintext exceeds those limits it will be broken up so that it does and the resultant ciphertext's will
595
     * be concatenated together.
596
     *
597
     * @see self::decrypt()
598
     *
599
     * @param string $plaintext
600
     * @param string $hash_algorithm
601
     *
602
     * @return string
603
     */
604
    public function encrypt($plaintext, $hash_algorithm)
605
    {
606
        $hash = Hash::$hash_algorithm();
607
        $length = $this->k - 2 * $hash->getLength() - 2;
608
        if ($length <= 0) {
609
            return false;
610
        }
611
612
        $plaintext = str_split($plaintext, $length);
613
        $ciphertext = '';
614
        foreach ($plaintext as $m) {
615
            $ciphertext .= $this->_rsaes_oaep_encrypt($m, $hash);
616
        }
617
618
        return $ciphertext;
619
    }
620
621
    /**
622
     * Decryption.
623
     *
624
     * @param string $ciphertext
625
     * @param string $hash_algorithm
626
     *
627
     * @return string
628
     */
629
    public function decrypt($ciphertext, $hash_algorithm)
630
    {
631
        if ($this->k <= 0) {
632
            return false;
633
        }
634
635
        $hash = Hash::$hash_algorithm();
636
637
        $ciphertext = str_split($ciphertext, $this->k);
638
        $ciphertext[count($ciphertext) - 1] = str_pad($ciphertext[count($ciphertext) - 1], $this->k, chr(0), STR_PAD_LEFT);
639
640
        $plaintext = '';
641
642
        foreach ($ciphertext as $c) {
643
            $temp = $this->_rsaes_oaep_decrypt($c, $hash);
644
            if ($temp === false) {
645
                return false;
646
            }
647
            $plaintext .= $temp;
648
        }
649
650
        return $plaintext;
651
    }
652
653
    /**
654
     * Create a signature.
655
     *
656
     * @param string $message
657
     * @param string $hash
658
     *
659
     * @return string
660
     */
661
    public function sign($message, $hash)
662
    {
663
        if (empty($this->modulus) || empty($this->exponent)) {
664
            return false;
665
        }
666
667
        return $this->_rsassa_pss_sign($message, Hash::$hash());
668
    }
669
670
    /**
671
     * Verifies a signature.
672
     *
673
     * @param string $message
674
     * @param string $signature
675
     * @param string $hash
676
     *
677
     * @return bool
678
     */
679
    public function verify($message, $signature, $hash)
680
    {
681
        if (empty($this->modulus) || empty($this->exponent)) {
682
            return false;
683
        }
684
685
        return $this->_rsassa_pss_verify($message, $signature, Hash::$hash());
686
    }
687
}
688