PluginProbe ʕ •ᴥ•ʔ
JetBackup – Backup, Restore & Migrate / 3.1.13.4
JetBackup – Backup, Restore & Migrate v3.1.13.4
3.1.22.4 3.1.22.3 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.4.8 1.4.8.1 1.4.9 1.5.0 1.5.1 1.5.1.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.6.0 1.6.10 1.6.11 1.6.12 1.6.13 1.6.15 1.6.5.1 1.6.8.8 1.6.9 1.6.9.1 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7.5 2.0.8.7 2.0.9.11 2.0.9.14 2.0.9.15 2.0.9.6 2.0.9.7 2.0.9.9 3.1.10.7 3.1.11.1 3.1.12.3 3.1.13.4 3.1.14.17 3.1.15.4 3.1.16.1 3.1.17.5 3.1.18.10 3.1.18.8 3.1.18.9 3.1.19.8 3.1.20.3 3.1.21.3 3.1.7.9 3.1.9.2 trunk 1.1.90 1.1.91 1.2.0 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.3.6 1.3.7 1.3.8 1.3.9 1.4.0 1.4.1 1.4.2
backup / src / JetBackup / 3rdparty / phpseclib3 / Crypt / RSA / PublicKey.php
backup / src / JetBackup / 3rdparty / phpseclib3 / Crypt / RSA Last commit date
Formats 9 months ago .htaccess 9 months ago PrivateKey.php 9 months ago PublicKey.php 9 months ago index.html 9 months ago web.config 9 months ago
PublicKey.php
492 lines
1 <?php
2
3 /**
4 * RSA Public Key
5 *
6 * @author Jim Wigginton <terrafrost@php.net>
7 * @copyright 2015 Jim Wigginton
8 * @license http://www.opensource.org/licenses/mit-license.html MIT License
9 * @link http://phpseclib.sourceforge.net
10 */
11
12 declare(strict_types=1);
13
14 namespace phpseclib3\Crypt\RSA;
15
16 use phpseclib3\Common\Functions\Strings;
17 use phpseclib3\Crypt\Common;
18 use phpseclib3\Crypt\Hash;
19 use phpseclib3\Crypt\Random;
20 use phpseclib3\Crypt\RSA;
21 use phpseclib3\Crypt\RSA\Formats\Keys\PSS;
22 use phpseclib3\Exception\LengthException;
23 use phpseclib3\Exception\OutOfRangeException;
24 use phpseclib3\Exception\UnsupportedAlgorithmException;
25 use phpseclib3\Exception\UnsupportedFormatException;
26 use phpseclib3\File\ASN1;
27 use phpseclib3\File\ASN1\Maps\DigestInfo;
28 use phpseclib3\Math\BigInteger;
29
30 /**
31 * Raw RSA Key Handler
32 *
33 * @author Jim Wigginton <terrafrost@php.net>
34 */
35 final class PublicKey extends RSA implements Common\PublicKey
36 {
37 use Common\Traits\Fingerprint;
38
39 /**
40 * Exponentiate
41 */
42 private function exponentiate(BigInteger $x): BigInteger
43 {
44 return $x->modPow($this->exponent, $this->modulus);
45 }
46
47 /**
48 * RSAVP1
49 *
50 * See {@link http://tools.ietf.org/html/rfc3447#section-5.2.2 RFC3447#section-5.2.2}.
51 *
52 * @return bool|BigInteger
53 */
54 private function rsavp1(BigInteger $s)
55 {
56 if ($s->compare(self::$zero) < 0 || $s->compare($this->modulus) > 0) {
57 return false;
58 }
59 return $this->exponentiate($s);
60 }
61
62 /**
63 * RSASSA-PKCS1-V1_5-VERIFY
64 *
65 * See {@link http://tools.ietf.org/html/rfc3447#section-8.2.2 RFC3447#section-8.2.2}.
66 *
67 * @throws LengthException if the RSA modulus is too short
68 */
69 private function rsassa_pkcs1_v1_5_verify(string $m, string $s): bool
70 {
71 // Length checking
72
73 if (strlen($s) != $this->k) {
74 return false;
75 }
76
77 // RSA verification
78
79 $s = $this->os2ip($s);
80 $m2 = $this->rsavp1($s);
81 if ($m2 === false) {
82 return false;
83 }
84 $em = $this->i2osp($m2, $this->k);
85 if ($em === false) {
86 return false;
87 }
88
89 // EMSA-PKCS1-v1_5 encoding
90
91 $exception = false;
92
93 // If the encoding operation outputs "intended encoded message length too short," output "RSA modulus
94 // too short" and stop.
95 try {
96 $em2 = $this->emsa_pkcs1_v1_5_encode($m, $this->k);
97 $r1 = hash_equals($em, $em2);
98 } catch (\LengthException $e) {
99 $exception = true;
100 }
101
102 try {
103 $em3 = $this->emsa_pkcs1_v1_5_encode_without_null($m, $this->k);
104 $r2 = hash_equals($em, $em3);
105 } catch (\LengthException $e) {
106 $exception = true;
107 } catch (UnsupportedAlgorithmException $e) {
108 $r2 = false;
109 }
110
111 if ($exception) {
112 throw new LengthException('RSA modulus too short');
113 }
114
115 // Compare
116 return $r1 || $r2;
117 }
118
119 /**
120 * RSASSA-PKCS1-V1_5-VERIFY (relaxed matching)
121 *
122 * Per {@link http://tools.ietf.org/html/rfc3447#page-43 RFC3447#page-43} PKCS1 v1.5
123 * specified the use BER encoding rather than DER encoding that PKCS1 v2.0 specified.
124 * This means that under rare conditions you can have a perfectly valid v1.5 signature
125 * that fails to validate with _rsassa_pkcs1_v1_5_verify(). PKCS1 v2.1 also recommends
126 * that if you're going to validate these types of signatures you "should indicate
127 * whether the underlying BER encoding is a DER encoding and hence whether the signature
128 * is valid with respect to the specification given in [PKCS1 v2.0+]". so if you do
129 * $rsa->getLastPadding() and get RSA::PADDING_RELAXED_PKCS1 back instead of
130 * RSA::PADDING_PKCS1... that means BER encoding was used.
131 */
132 private function rsassa_pkcs1_v1_5_relaxed_verify(string $m, string $s): bool
133 {
134 // Length checking
135
136 if (strlen($s) != $this->k) {
137 return false;
138 }
139
140 // RSA verification
141
142 $s = $this->os2ip($s);
143 $m2 = $this->rsavp1($s);
144 if ($m2 === false) {
145 return false;
146 }
147 $em = $this->i2osp($m2, $this->k);
148 if ($em === false) {
149 return false;
150 }
151
152 if (Strings::shift($em, 2) != "\0\1") {
153 return false;
154 }
155
156 $em = ltrim($em, "\xFF");
157 if (Strings::shift($em) != "\0") {
158 return false;
159 }
160
161 $decoded = ASN1::decodeBER($em);
162 if (!is_array($decoded) || empty($decoded[0]) || strlen($em) > $decoded[0]['length']) {
163 return false;
164 }
165
166 static $oids;
167 if (!isset($oids)) {
168 $oids = [
169 'md2' => '1.2.840.113549.2.2',
170 'md4' => '1.2.840.113549.2.4', // from PKCS1 v1.5
171 'md5' => '1.2.840.113549.2.5',
172 'id-sha1' => '1.3.14.3.2.26',
173 'id-sha256' => '2.16.840.1.101.3.4.2.1',
174 'id-sha384' => '2.16.840.1.101.3.4.2.2',
175 'id-sha512' => '2.16.840.1.101.3.4.2.3',
176 // from PKCS1 v2.2
177 'id-sha224' => '2.16.840.1.101.3.4.2.4',
178 'id-sha512/224' => '2.16.840.1.101.3.4.2.5',
179 'id-sha512/256' => '2.16.840.1.101.3.4.2.6',
180 ];
181 ASN1::loadOIDs($oids);
182 }
183
184 $decoded = ASN1::asn1map($decoded[0], DigestInfo::MAP);
185 if (!isset($decoded) || $decoded === false) {
186 return false;
187 }
188
189 if (!isset($oids[$decoded['digestAlgorithm']['algorithm']])) {
190 return false;
191 }
192
193 if (isset($decoded['digestAlgorithm']['parameters']) && $decoded['digestAlgorithm']['parameters'] !== ['null' => '']) {
194 return false;
195 }
196
197 $hash = $decoded['digestAlgorithm']['algorithm'];
198 $hash = substr($hash, 0, 3) == 'id-' ?
199 substr($hash, 3) :
200 $hash;
201 $hash = new Hash($hash);
202 $em = $hash->hash($m);
203 $em2 = $decoded['digest'];
204
205 return hash_equals($em, $em2);
206 }
207
208 /**
209 * EMSA-PSS-VERIFY
210 *
211 * See {@link http://tools.ietf.org/html/rfc3447#section-9.1.2 RFC3447#section-9.1.2}.
212 *
213 * @return string
214 */
215 private function emsa_pss_verify(string $m, string $em, int $emBits)
216 {
217 // if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
218 // be output.
219
220 $emLen = ($emBits + 7) >> 3; // ie. ceil($emBits / 8);
221 $sLen = $this->sLen !== null ? $this->sLen : $this->hLen;
222
223 $mHash = $this->hash->hash($m);
224 if ($emLen < $this->hLen + $sLen + 2) {
225 return false;
226 }
227
228 if ($em[-1] != chr(0xBC)) {
229 return false;
230 }
231
232 $maskedDB = substr($em, 0, -$this->hLen - 1);
233 $h = substr($em, -$this->hLen - 1, $this->hLen);
234 $temp = chr(0xFF << ($emBits & 7));
235 if ((~$maskedDB[0] & $temp) != $temp) {
236 return false;
237 }
238 $dbMask = $this->mgf1($h, $emLen - $this->hLen - 1);
239 $db = $maskedDB ^ $dbMask;
240 $db[0] = ~chr(0xFF << ($emBits & 7)) & $db[0];
241 $temp = $emLen - $this->hLen - $sLen - 2;
242 if (substr($db, 0, $temp) != str_repeat(chr(0), $temp) || ord($db[$temp]) != 1) {
243 return false;
244 }
245 $salt = substr($db, $temp + 1); // should be $sLen long
246 $m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt;
247 $h2 = $this->hash->hash($m2);
248 return hash_equals($h, $h2);
249 }
250
251 /**
252 * RSASSA-PSS-VERIFY
253 *
254 * See {@link http://tools.ietf.org/html/rfc3447#section-8.1.2 RFC3447#section-8.1.2}.
255 *
256 * @return bool|string
257 */
258 private function rsassa_pss_verify(string $m, string $s)
259 {
260 // Length checking
261
262 if (strlen($s) != $this->k) {
263 return false;
264 }
265
266 // RSA verification
267
268 $modBits = strlen($this->modulus->toBits());
269
270 $s2 = $this->os2ip($s);
271 $m2 = $this->rsavp1($s2);
272 $em = $this->i2osp($m2, $this->k);
273 if ($em === false) {
274 return false;
275 }
276
277 // EMSA-PSS verification
278
279 return $this->emsa_pss_verify($m, $em, $modBits - 1);
280 }
281
282 /**
283 * Verifies a signature
284 *
285 * @see self::sign()
286 * @param string $message
287 * @param string $signature
288 * @return bool
289 */
290 public function verify($message, $signature)
291 {
292 switch ($this->signaturePadding) {
293 case self::SIGNATURE_RELAXED_PKCS1:
294 return $this->rsassa_pkcs1_v1_5_relaxed_verify($message, $signature);
295 case self::SIGNATURE_PKCS1:
296 return $this->rsassa_pkcs1_v1_5_verify($message, $signature);
297 //case self::SIGNATURE_PSS:
298 default:
299 return $this->rsassa_pss_verify($message, $signature);
300 }
301 }
302
303 /**
304 * RSAES-PKCS1-V1_5-ENCRYPT
305 *
306 * See {@link http://tools.ietf.org/html/rfc3447#section-7.2.1 RFC3447#section-7.2.1}.
307 *
308 * @param bool $pkcs15_compat optional
309 * @return bool|string
310 * @throws LengthException if strlen($m) > $this->k - 11
311 */
312 private function rsaes_pkcs1_v1_5_encrypt(string $m, bool $pkcs15_compat = false)
313 {
314 $mLen = strlen($m);
315
316 // Length checking
317
318 if ($mLen > $this->k - 11) {
319 throw new LengthException('Message too long');
320 }
321
322 // EME-PKCS1-v1_5 encoding
323
324 $psLen = $this->k - $mLen - 3;
325 $ps = '';
326 while (strlen($ps) != $psLen) {
327 $temp = Random::string($psLen - strlen($ps));
328 $temp = str_replace("\x00", '', $temp);
329 $ps .= $temp;
330 }
331 $type = 2;
332 $em = chr(0) . chr($type) . $ps . chr(0) . $m;
333
334 // RSA encryption
335 $m = $this->os2ip($em);
336 $c = $this->rsaep($m);
337 $c = $this->i2osp($c, $this->k);
338
339 // Output the ciphertext C
340
341 return $c;
342 }
343
344 /**
345 * RSAES-OAEP-ENCRYPT
346 *
347 * See {@link http://tools.ietf.org/html/rfc3447#section-7.1.1 RFC3447#section-7.1.1} and
348 * {http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding OAES}.
349 *
350 * @throws LengthException if strlen($m) > $this->k - 2 * $this->hLen - 2
351 */
352 private function rsaes_oaep_encrypt(string $m): string
353 {
354 $mLen = strlen($m);
355
356 // Length checking
357
358 // if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
359 // be output.
360
361 if ($mLen > $this->k - 2 * $this->hLen - 2) {
362 throw new LengthException('Message too long');
363 }
364
365 // EME-OAEP encoding
366
367 $lHash = $this->hash->hash($this->label);
368 $ps = str_repeat(chr(0), $this->k - $mLen - 2 * $this->hLen - 2);
369 $db = $lHash . $ps . chr(1) . $m;
370 $seed = Random::string($this->hLen);
371 $dbMask = $this->mgf1($seed, $this->k - $this->hLen - 1);
372 $maskedDB = $db ^ $dbMask;
373 $seedMask = $this->mgf1($maskedDB, $this->hLen);
374 $maskedSeed = $seed ^ $seedMask;
375 $em = chr(0) . $maskedSeed . $maskedDB;
376
377 // RSA encryption
378
379 $m = $this->os2ip($em);
380 $c = $this->rsaep($m);
381 $c = $this->i2osp($c, $this->k);
382
383 // Output the ciphertext C
384
385 return $c;
386 }
387
388 /**
389 * RSAEP
390 *
391 * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.1 RFC3447#section-5.1.1}.
392 *
393 * @return bool|BigInteger
394 */
395 private function rsaep(BigInteger $m)
396 {
397 if ($m->compare(self::$zero) < 0 || $m->compare($this->modulus) > 0) {
398 throw new OutOfRangeException('Message representative out of range');
399 }
400 return $this->exponentiate($m);
401 }
402
403 /**
404 * Raw Encryption / Decryption
405 *
406 * Doesn't use padding and is not recommended.
407 *
408 * @return bool|string
409 * @throws LengthException if strlen($m) > $this->k
410 */
411 private function raw_encrypt(string $m)
412 {
413 if (strlen($m) > $this->k) {
414 throw new LengthException('Message too long');
415 }
416
417 $temp = $this->os2ip($m);
418 $temp = $this->rsaep($temp);
419 return $this->i2osp($temp, $this->k);
420 }
421
422 /**
423 * Encryption
424 *
425 * Both self::PADDING_OAEP and self::PADDING_PKCS1 both place limits on how long $plaintext can be.
426 * If $plaintext exceeds those limits it will be broken up so that it does and the resultant ciphertext's will
427 * be concatenated together.
428 *
429 * @return bool|string
430 * @throws LengthException if the RSA modulus is too short
431 * @see self::decrypt()
432 */
433 public function encrypt(string $plaintext)
434 {
435 switch ($this->encryptionPadding) {
436 case self::ENCRYPTION_NONE:
437 return $this->raw_encrypt($plaintext);
438 case self::ENCRYPTION_PKCS1:
439 return $this->rsaes_pkcs1_v1_5_encrypt($plaintext);
440 //case self::ENCRYPTION_OAEP:
441 default:
442 return $this->rsaes_oaep_encrypt($plaintext);
443 }
444 }
445
446 /**
447 * Returns the public key
448 *
449 * The public key is only returned under two circumstances - if the private key had the public key embedded within it
450 * or if the public key was set via setPublicKey(). If the currently loaded key is supposed to be the public key this
451 * function won't return it since this library, for the most part, doesn't distinguish between public and private keys.
452 *
453 * @param array $options optional
454 */
455 public function toString(string $type, array $options = []): string
456 {
457 $type = self::validatePlugin('Keys', $type, 'savePublicKey');
458
459 if ($type == PSS::class) {
460 if ($this->signaturePadding == self::SIGNATURE_PSS) {
461 $options += [
462 'hash' => $this->hash->getHash(),
463 'MGFHash' => $this->mgfHash->getHash(),
464 'saltLength' => $this->getSaltLength(),
465 ];
466 } else {
467 throw new UnsupportedFormatException('The PSS format can only be used when the signature method has been explicitly set to PSS');
468 }
469 }
470
471 return $type::savePublicKey($this->modulus, $this->publicExponent, $options);
472 }
473
474 /**
475 * Converts a public key to a private key
476 */
477 public function asPrivateKey(): RSA
478 {
479 $new = new PrivateKey();
480 $new->exponent = $this->exponent;
481 $new->modulus = $this->modulus;
482 $new->k = $this->k;
483 $new->format = $this->format;
484 return $new
485 ->withHash($this->hash->getHash())
486 ->withMGFHash($this->mgfHash->getHash())
487 ->withSaltLength($this->sLen)
488 ->withLabel($this->label)
489 ->withPadding($this->signaturePadding | $this->encryptionPadding);
490 }
491 }
492