Rev 874 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
827 | daniel-mar | 1 | <?php |
2 | |||
3 | /** |
||
4 | * RSA Private 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 | namespace phpseclib3\Crypt\RSA; |
||
13 | |||
14 | use phpseclib3\Crypt\Common; |
||
15 | use phpseclib3\Crypt\Random; |
||
16 | use phpseclib3\Crypt\RSA; |
||
17 | use phpseclib3\Crypt\RSA\Formats\Keys\PSS; |
||
18 | use phpseclib3\Exception\UnsupportedFormatException; |
||
19 | use phpseclib3\Math\BigInteger; |
||
20 | |||
21 | /** |
||
22 | * Raw RSA Key Handler |
||
23 | * |
||
24 | * @author Jim Wigginton <terrafrost@php.net> |
||
25 | */ |
||
26 | class PrivateKey extends RSA implements Common\PrivateKey |
||
27 | { |
||
28 | use Common\Traits\PasswordProtected; |
||
29 | |||
30 | /** |
||
31 | * Primes for Chinese Remainder Theorem (ie. p and q) |
||
32 | * |
||
33 | * @var array |
||
34 | */ |
||
35 | protected $primes; |
||
36 | |||
37 | /** |
||
38 | * Exponents for Chinese Remainder Theorem (ie. dP and dQ) |
||
39 | * |
||
40 | * @var array |
||
41 | */ |
||
42 | protected $exponents; |
||
43 | |||
44 | /** |
||
45 | * Coefficients for Chinese Remainder Theorem (ie. qInv) |
||
46 | * |
||
47 | * @var array |
||
48 | */ |
||
49 | protected $coefficients; |
||
50 | |||
51 | /** |
||
1042 | daniel-mar | 52 | * Private Exponent |
827 | daniel-mar | 53 | * |
1042 | daniel-mar | 54 | * @var \phpseclib3\Math\BigInteger |
827 | daniel-mar | 55 | */ |
1042 | daniel-mar | 56 | protected $privateExponent; |
827 | daniel-mar | 57 | |
58 | /** |
||
59 | * RSADP |
||
60 | * |
||
61 | * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.2 RFC3447#section-5.1.2}. |
||
62 | * |
||
63 | * @return bool|\phpseclib3\Math\BigInteger |
||
64 | */ |
||
1042 | daniel-mar | 65 | private function rsadp(BigInteger $c) |
827 | daniel-mar | 66 | { |
67 | if ($c->compare(self::$zero) < 0 || $c->compare($this->modulus) > 0) { |
||
68 | throw new \OutOfRangeException('Ciphertext representative out of range'); |
||
69 | } |
||
70 | return $this->exponentiate($c); |
||
71 | } |
||
72 | |||
73 | /** |
||
74 | * RSASP1 |
||
75 | * |
||
76 | * See {@link http://tools.ietf.org/html/rfc3447#section-5.2.1 RFC3447#section-5.2.1}. |
||
77 | * |
||
78 | * @return bool|\phpseclib3\Math\BigInteger |
||
79 | */ |
||
1042 | daniel-mar | 80 | private function rsasp1(BigInteger $m) |
827 | daniel-mar | 81 | { |
82 | if ($m->compare(self::$zero) < 0 || $m->compare($this->modulus) > 0) { |
||
83 | throw new \OutOfRangeException('Signature representative out of range'); |
||
84 | } |
||
85 | return $this->exponentiate($m); |
||
86 | } |
||
87 | |||
88 | /** |
||
89 | * Exponentiate |
||
90 | * |
||
91 | * @param \phpseclib3\Math\BigInteger $x |
||
92 | * @return \phpseclib3\Math\BigInteger |
||
93 | */ |
||
94 | protected function exponentiate(BigInteger $x) |
||
95 | { |
||
96 | switch (true) { |
||
97 | case empty($this->primes): |
||
98 | case $this->primes[1]->equals(self::$zero): |
||
99 | case empty($this->coefficients): |
||
100 | case $this->coefficients[2]->equals(self::$zero): |
||
101 | case empty($this->exponents): |
||
102 | case $this->exponents[1]->equals(self::$zero): |
||
103 | return $x->modPow($this->exponent, $this->modulus); |
||
104 | } |
||
105 | |||
106 | $num_primes = count($this->primes); |
||
107 | |||
108 | if (!static::$enableBlinding) { |
||
109 | $m_i = [ |
||
110 | 1 => $x->modPow($this->exponents[1], $this->primes[1]), |
||
111 | 2 => $x->modPow($this->exponents[2], $this->primes[2]) |
||
112 | ]; |
||
113 | $h = $m_i[1]->subtract($m_i[2]); |
||
114 | $h = $h->multiply($this->coefficients[2]); |
||
115 | list(, $h) = $h->divide($this->primes[1]); |
||
116 | $m = $m_i[2]->add($h->multiply($this->primes[2])); |
||
117 | |||
118 | $r = $this->primes[1]; |
||
119 | for ($i = 3; $i <= $num_primes; $i++) { |
||
120 | $m_i = $x->modPow($this->exponents[$i], $this->primes[$i]); |
||
121 | |||
122 | $r = $r->multiply($this->primes[$i - 1]); |
||
123 | |||
124 | $h = $m_i->subtract($m); |
||
125 | $h = $h->multiply($this->coefficients[$i]); |
||
126 | list(, $h) = $h->divide($this->primes[$i]); |
||
127 | |||
128 | $m = $m->add($r->multiply($h)); |
||
129 | } |
||
130 | } else { |
||
131 | $smallest = $this->primes[1]; |
||
132 | for ($i = 2; $i <= $num_primes; $i++) { |
||
133 | if ($smallest->compare($this->primes[$i]) > 0) { |
||
134 | $smallest = $this->primes[$i]; |
||
135 | } |
||
136 | } |
||
137 | |||
138 | $r = BigInteger::randomRange(self::$one, $smallest->subtract(self::$one)); |
||
139 | |||
140 | $m_i = [ |
||
141 | 1 => $this->blind($x, $r, 1), |
||
142 | 2 => $this->blind($x, $r, 2) |
||
143 | ]; |
||
144 | $h = $m_i[1]->subtract($m_i[2]); |
||
145 | $h = $h->multiply($this->coefficients[2]); |
||
146 | list(, $h) = $h->divide($this->primes[1]); |
||
147 | $m = $m_i[2]->add($h->multiply($this->primes[2])); |
||
148 | |||
149 | $r = $this->primes[1]; |
||
150 | for ($i = 3; $i <= $num_primes; $i++) { |
||
151 | $m_i = $this->blind($x, $r, $i); |
||
152 | |||
153 | $r = $r->multiply($this->primes[$i - 1]); |
||
154 | |||
155 | $h = $m_i->subtract($m); |
||
156 | $h = $h->multiply($this->coefficients[$i]); |
||
157 | list(, $h) = $h->divide($this->primes[$i]); |
||
158 | |||
159 | $m = $m->add($r->multiply($h)); |
||
160 | } |
||
161 | } |
||
162 | |||
163 | return $m; |
||
164 | } |
||
165 | |||
166 | /** |
||
167 | * Performs RSA Blinding |
||
168 | * |
||
169 | * Protects against timing attacks by employing RSA Blinding. |
||
170 | * Returns $x->modPow($this->exponents[$i], $this->primes[$i]) |
||
171 | * |
||
172 | * @param \phpseclib3\Math\BigInteger $x |
||
173 | * @param \phpseclib3\Math\BigInteger $r |
||
174 | * @param int $i |
||
175 | * @return \phpseclib3\Math\BigInteger |
||
176 | */ |
||
1042 | daniel-mar | 177 | private function blind(BigInteger $x, BigInteger $r, $i) |
827 | daniel-mar | 178 | { |
179 | $x = $x->multiply($r->modPow($this->publicExponent, $this->primes[$i])); |
||
180 | $x = $x->modPow($this->exponents[$i], $this->primes[$i]); |
||
181 | |||
182 | $r = $r->modInverse($this->primes[$i]); |
||
183 | $x = $x->multiply($r); |
||
184 | list(, $x) = $x->divide($this->primes[$i]); |
||
185 | |||
186 | return $x; |
||
187 | } |
||
188 | |||
189 | /** |
||
190 | * EMSA-PSS-ENCODE |
||
191 | * |
||
192 | * See {@link http://tools.ietf.org/html/rfc3447#section-9.1.1 RFC3447#section-9.1.1}. |
||
193 | * |
||
194 | * @return string |
||
195 | * @param string $m |
||
196 | * @throws \RuntimeException on encoding error |
||
197 | * @param int $emBits |
||
198 | */ |
||
199 | private function emsa_pss_encode($m, $emBits) |
||
200 | { |
||
201 | // if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error |
||
202 | // be output. |
||
203 | |||
204 | $emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8) |
||
205 | $sLen = $this->sLen !== null ? $this->sLen : $this->hLen; |
||
206 | |||
207 | $mHash = $this->hash->hash($m); |
||
208 | if ($emLen < $this->hLen + $sLen + 2) { |
||
209 | throw new \LengthException('RSA modulus too short'); |
||
210 | } |
||
211 | |||
212 | $salt = Random::string($sLen); |
||
213 | $m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt; |
||
214 | $h = $this->hash->hash($m2); |
||
215 | $ps = str_repeat(chr(0), $emLen - $sLen - $this->hLen - 2); |
||
216 | $db = $ps . chr(1) . $salt; |
||
217 | $dbMask = $this->mgf1($h, $emLen - $this->hLen - 1); // ie. stlren($db) |
||
218 | $maskedDB = $db ^ $dbMask; |
||
219 | $maskedDB[0] = ~chr(0xFF << ($emBits & 7)) & $maskedDB[0]; |
||
220 | $em = $maskedDB . $h . chr(0xBC); |
||
221 | |||
222 | return $em; |
||
223 | } |
||
224 | |||
225 | /** |
||
226 | * RSASSA-PSS-SIGN |
||
227 | * |
||
228 | * See {@link http://tools.ietf.org/html/rfc3447#section-8.1.1 RFC3447#section-8.1.1}. |
||
229 | * |
||
230 | * @param string $m |
||
231 | * @return bool|string |
||
232 | */ |
||
233 | private function rsassa_pss_sign($m) |
||
234 | { |
||
235 | // EMSA-PSS encoding |
||
236 | |||
237 | $em = $this->emsa_pss_encode($m, 8 * $this->k - 1); |
||
238 | |||
239 | // RSA signature |
||
240 | |||
241 | $m = $this->os2ip($em); |
||
242 | $s = $this->rsasp1($m); |
||
243 | $s = $this->i2osp($s, $this->k); |
||
244 | |||
245 | // Output the signature S |
||
246 | |||
247 | return $s; |
||
248 | } |
||
249 | |||
250 | /** |
||
251 | * RSASSA-PKCS1-V1_5-SIGN |
||
252 | * |
||
253 | * See {@link http://tools.ietf.org/html/rfc3447#section-8.2.1 RFC3447#section-8.2.1}. |
||
254 | * |
||
255 | * @param string $m |
||
256 | * @throws \LengthException if the RSA modulus is too short |
||
257 | * @return bool|string |
||
258 | */ |
||
259 | private function rsassa_pkcs1_v1_5_sign($m) |
||
260 | { |
||
261 | // EMSA-PKCS1-v1_5 encoding |
||
262 | |||
263 | // If the encoding operation outputs "intended encoded message length too short," output "RSA modulus |
||
264 | // too short" and stop. |
||
265 | try { |
||
266 | $em = $this->emsa_pkcs1_v1_5_encode($m, $this->k); |
||
267 | } catch (\LengthException $e) { |
||
268 | throw new \LengthException('RSA modulus too short'); |
||
269 | } |
||
270 | |||
271 | // RSA signature |
||
272 | |||
273 | $m = $this->os2ip($em); |
||
274 | $s = $this->rsasp1($m); |
||
275 | $s = $this->i2osp($s, $this->k); |
||
276 | |||
277 | // Output the signature S |
||
278 | |||
279 | return $s; |
||
280 | } |
||
281 | |||
282 | /** |
||
283 | * Create a signature |
||
284 | * |
||
285 | * @see self::verify() |
||
286 | * @param string $message |
||
287 | * @return string |
||
288 | */ |
||
289 | public function sign($message) |
||
290 | { |
||
291 | switch ($this->signaturePadding) { |
||
292 | case self::SIGNATURE_PKCS1: |
||
293 | case self::SIGNATURE_RELAXED_PKCS1: |
||
294 | return $this->rsassa_pkcs1_v1_5_sign($message); |
||
295 | //case self::SIGNATURE_PSS: |
||
296 | default: |
||
297 | return $this->rsassa_pss_sign($message); |
||
298 | } |
||
299 | } |
||
300 | |||
301 | /** |
||
302 | * RSAES-PKCS1-V1_5-DECRYPT |
||
303 | * |
||
304 | * See {@link http://tools.ietf.org/html/rfc3447#section-7.2.2 RFC3447#section-7.2.2}. |
||
305 | * |
||
306 | * @param string $c |
||
307 | * @return bool|string |
||
308 | */ |
||
309 | private function rsaes_pkcs1_v1_5_decrypt($c) |
||
310 | { |
||
311 | // Length checking |
||
312 | |||
313 | if (strlen($c) != $this->k) { // or if k < 11 |
||
314 | throw new \LengthException('Ciphertext representative too long'); |
||
315 | } |
||
316 | |||
317 | // RSA decryption |
||
318 | |||
319 | $c = $this->os2ip($c); |
||
320 | $m = $this->rsadp($c); |
||
321 | $em = $this->i2osp($m, $this->k); |
||
322 | |||
323 | // EME-PKCS1-v1_5 decoding |
||
324 | |||
325 | if (ord($em[0]) != 0 || ord($em[1]) > 2) { |
||
326 | throw new \RuntimeException('Decryption error'); |
||
327 | } |
||
328 | |||
329 | $ps = substr($em, 2, strpos($em, chr(0), 2) - 2); |
||
330 | $m = substr($em, strlen($ps) + 3); |
||
331 | |||
332 | if (strlen($ps) < 8) { |
||
333 | throw new \RuntimeException('Decryption error'); |
||
334 | } |
||
335 | |||
336 | // Output M |
||
337 | |||
338 | return $m; |
||
339 | } |
||
340 | |||
341 | /** |
||
342 | * RSAES-OAEP-DECRYPT |
||
343 | * |
||
344 | * See {@link http://tools.ietf.org/html/rfc3447#section-7.1.2 RFC3447#section-7.1.2}. The fact that the error |
||
345 | * messages aren't distinguishable from one another hinders debugging, but, to quote from RFC3447#section-7.1.2: |
||
346 | * |
||
347 | * Note. Care must be taken to ensure that an opponent cannot |
||
348 | * distinguish the different error conditions in Step 3.g, whether by |
||
349 | * error message or timing, or, more generally, learn partial |
||
350 | * information about the encoded message EM. Otherwise an opponent may |
||
351 | * be able to obtain useful information about the decryption of the |
||
352 | * ciphertext C, leading to a chosen-ciphertext attack such as the one |
||
353 | * observed by Manger [36]. |
||
354 | * |
||
355 | * @param string $c |
||
356 | * @return bool|string |
||
357 | */ |
||
358 | private function rsaes_oaep_decrypt($c) |
||
359 | { |
||
360 | // Length checking |
||
361 | |||
362 | // if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error |
||
363 | // be output. |
||
364 | |||
365 | if (strlen($c) != $this->k || $this->k < 2 * $this->hLen + 2) { |
||
366 | throw new \LengthException('Ciphertext representative too long'); |
||
367 | } |
||
368 | |||
369 | // RSA decryption |
||
370 | |||
371 | $c = $this->os2ip($c); |
||
372 | $m = $this->rsadp($c); |
||
373 | $em = $this->i2osp($m, $this->k); |
||
374 | |||
375 | // EME-OAEP decoding |
||
376 | |||
377 | $lHash = $this->hash->hash($this->label); |
||
378 | $y = ord($em[0]); |
||
379 | $maskedSeed = substr($em, 1, $this->hLen); |
||
380 | $maskedDB = substr($em, $this->hLen + 1); |
||
381 | $seedMask = $this->mgf1($maskedDB, $this->hLen); |
||
382 | $seed = $maskedSeed ^ $seedMask; |
||
383 | $dbMask = $this->mgf1($seed, $this->k - $this->hLen - 1); |
||
384 | $db = $maskedDB ^ $dbMask; |
||
385 | $lHash2 = substr($db, 0, $this->hLen); |
||
386 | $m = substr($db, $this->hLen); |
||
387 | $hashesMatch = hash_equals($lHash, $lHash2); |
||
388 | $leadingZeros = 1; |
||
389 | $patternMatch = 0; |
||
390 | $offset = 0; |
||
391 | for ($i = 0; $i < strlen($m); $i++) { |
||
392 | $patternMatch |= $leadingZeros & ($m[$i] === "\1"); |
||
393 | $leadingZeros &= $m[$i] === "\0"; |
||
394 | $offset += $patternMatch ? 0 : 1; |
||
395 | } |
||
396 | |||
397 | // we do | instead of || to avoid https://en.wikipedia.org/wiki/Short-circuit_evaluation |
||
398 | // to protect against timing attacks |
||
399 | if (!$hashesMatch | !$patternMatch) { |
||
400 | throw new \RuntimeException('Decryption error'); |
||
401 | } |
||
402 | |||
403 | // Output the message M |
||
404 | |||
405 | return substr($m, $offset + 1); |
||
406 | } |
||
407 | |||
408 | /** |
||
409 | * Raw Encryption / Decryption |
||
410 | * |
||
411 | * Doesn't use padding and is not recommended. |
||
412 | * |
||
413 | * @param string $m |
||
414 | * @return bool|string |
||
415 | * @throws \LengthException if strlen($m) > $this->k |
||
416 | */ |
||
417 | private function raw_encrypt($m) |
||
418 | { |
||
419 | if (strlen($m) > $this->k) { |
||
420 | throw new \LengthException('Ciphertext representative too long'); |
||
421 | } |
||
422 | |||
423 | $temp = $this->os2ip($m); |
||
424 | $temp = $this->rsadp($temp); |
||
425 | return $this->i2osp($temp, $this->k); |
||
426 | } |
||
427 | |||
428 | /** |
||
429 | * Decryption |
||
430 | * |
||
431 | * @see self::encrypt() |
||
432 | * @param string $ciphertext |
||
433 | * @return bool|string |
||
434 | */ |
||
435 | public function decrypt($ciphertext) |
||
436 | { |
||
437 | switch ($this->encryptionPadding) { |
||
438 | case self::ENCRYPTION_NONE: |
||
439 | return $this->raw_encrypt($ciphertext); |
||
440 | case self::ENCRYPTION_PKCS1: |
||
441 | return $this->rsaes_pkcs1_v1_5_decrypt($ciphertext); |
||
442 | //case self::ENCRYPTION_OAEP: |
||
443 | default: |
||
444 | return $this->rsaes_oaep_decrypt($ciphertext); |
||
445 | } |
||
446 | } |
||
447 | |||
448 | /** |
||
449 | * Returns the public key |
||
450 | * |
||
451 | * @return mixed |
||
452 | */ |
||
453 | public function getPublicKey() |
||
454 | { |
||
455 | $type = self::validatePlugin('Keys', 'PKCS8', 'savePublicKey'); |
||
456 | if (empty($this->modulus) || empty($this->publicExponent)) { |
||
457 | throw new \RuntimeException('Public key components not found'); |
||
458 | } |
||
459 | |||
460 | $key = $type::savePublicKey($this->modulus, $this->publicExponent); |
||
461 | return RSA::loadFormat('PKCS8', $key) |
||
462 | ->withHash($this->hash->getHash()) |
||
463 | ->withMGFHash($this->mgfHash->getHash()) |
||
464 | ->withSaltLength($this->sLen) |
||
465 | ->withLabel($this->label) |
||
466 | ->withPadding($this->signaturePadding | $this->encryptionPadding); |
||
467 | } |
||
468 | |||
469 | /** |
||
470 | * Returns the private key |
||
471 | * |
||
472 | * @param string $type |
||
473 | * @param array $options optional |
||
474 | * @return string |
||
475 | */ |
||
476 | public function toString($type, array $options = []) |
||
477 | { |
||
478 | $type = self::validatePlugin( |
||
479 | 'Keys', |
||
480 | $type, |
||
481 | empty($this->primes) ? 'savePublicKey' : 'savePrivateKey' |
||
482 | ); |
||
483 | |||
484 | if ($type == PSS::class) { |
||
485 | if ($this->signaturePadding == self::SIGNATURE_PSS) { |
||
486 | $options += [ |
||
487 | 'hash' => $this->hash->getHash(), |
||
488 | 'MGFHash' => $this->mgfHash->getHash(), |
||
489 | 'saltLength' => $this->getSaltLength() |
||
490 | ]; |
||
491 | } else { |
||
492 | throw new UnsupportedFormatException('The PSS format can only be used when the signature method has been explicitly set to PSS'); |
||
493 | } |
||
494 | } |
||
495 | |||
496 | if (empty($this->primes)) { |
||
497 | return $type::savePublicKey($this->modulus, $this->exponent, $options); |
||
498 | } |
||
499 | |||
500 | return $type::savePrivateKey($this->modulus, $this->publicExponent, $this->exponent, $this->primes, $this->exponents, $this->coefficients, $this->password, $options); |
||
501 | |||
502 | /* |
||
503 | $key = $type::savePrivateKey($this->modulus, $this->publicExponent, $this->exponent, $this->primes, $this->exponents, $this->coefficients, $this->password, $options); |
||
504 | if ($key !== false || count($this->primes) == 2) { |
||
505 | return $key; |
||
506 | } |
||
507 | |||
508 | $nSize = $this->getSize() >> 1; |
||
509 | |||
510 | $primes = [1 => clone self::$one, clone self::$one]; |
||
511 | $i = 1; |
||
512 | foreach ($this->primes as $prime) { |
||
513 | $primes[$i] = $primes[$i]->multiply($prime); |
||
514 | if ($primes[$i]->getLength() >= $nSize) { |
||
515 | $i++; |
||
516 | } |
||
517 | } |
||
518 | |||
519 | $exponents = []; |
||
520 | $coefficients = [2 => $primes[2]->modInverse($primes[1])]; |
||
521 | |||
522 | foreach ($primes as $i => $prime) { |
||
523 | $temp = $prime->subtract(self::$one); |
||
524 | $exponents[$i] = $this->modulus->modInverse($temp); |
||
525 | } |
||
526 | |||
527 | return $type::savePrivateKey($this->modulus, $this->publicExponent, $this->exponent, $primes, $exponents, $coefficients, $this->password, $options); |
||
528 | */ |
||
529 | } |
||
530 | } |