Rev 846 | Rev 1042 | 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 | * Pure-PHP implementation of EC. |
||
5 | * |
||
6 | * PHP version 5 |
||
7 | * |
||
8 | * Here's an example of how to create signatures and verify signatures with this library: |
||
9 | * <code> |
||
10 | * <?php |
||
11 | * include 'vendor/autoload.php'; |
||
12 | * |
||
13 | * $private = \phpseclib3\Crypt\EC::createKey('secp256k1'); |
||
14 | * $public = $private->getPublicKey(); |
||
15 | * |
||
16 | * $plaintext = 'terrafrost'; |
||
17 | * |
||
18 | * $signature = $private->sign($plaintext); |
||
19 | * |
||
20 | * echo $public->verify($plaintext, $signature) ? 'verified' : 'unverified'; |
||
21 | * ?> |
||
22 | * </code> |
||
23 | * |
||
874 | daniel-mar | 24 | * @category Crypt |
25 | * @package EC |
||
827 | daniel-mar | 26 | * @author Jim Wigginton <terrafrost@php.net> |
27 | * @copyright 2016 Jim Wigginton |
||
28 | * @license http://www.opensource.org/licenses/mit-license.html MIT License |
||
29 | * @link http://phpseclib.sourceforge.net |
||
30 | */ |
||
31 | |||
32 | namespace phpseclib3\Crypt; |
||
33 | |||
34 | use phpseclib3\Crypt\Common\AsymmetricKey; |
||
35 | use phpseclib3\Crypt\EC\BaseCurves\Montgomery as MontgomeryCurve; |
||
36 | use phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve; |
||
37 | use phpseclib3\Crypt\EC\Curves\Curve25519; |
||
38 | use phpseclib3\Crypt\EC\Curves\Ed25519; |
||
39 | use phpseclib3\Crypt\EC\Curves\Ed448; |
||
40 | use phpseclib3\Crypt\EC\Formats\Keys\PKCS1; |
||
41 | use phpseclib3\Crypt\EC\Parameters; |
||
42 | use phpseclib3\Crypt\EC\PrivateKey; |
||
43 | use phpseclib3\Crypt\EC\PublicKey; |
||
44 | use phpseclib3\Exception\UnsupportedAlgorithmException; |
||
45 | use phpseclib3\Exception\UnsupportedCurveException; |
||
46 | use phpseclib3\Exception\UnsupportedOperationException; |
||
47 | use phpseclib3\File\ASN1; |
||
48 | use phpseclib3\File\ASN1\Maps\ECParameters; |
||
49 | use phpseclib3\Math\BigInteger; |
||
50 | |||
51 | /** |
||
52 | * Pure-PHP implementation of EC. |
||
53 | * |
||
874 | daniel-mar | 54 | * @package EC |
827 | daniel-mar | 55 | * @author Jim Wigginton <terrafrost@php.net> |
874 | daniel-mar | 56 | * @access public |
827 | daniel-mar | 57 | */ |
58 | abstract class EC extends AsymmetricKey |
||
59 | { |
||
60 | /** |
||
61 | * Algorithm Name |
||
62 | * |
||
63 | * @var string |
||
874 | daniel-mar | 64 | * @access private |
827 | daniel-mar | 65 | */ |
66 | const ALGORITHM = 'EC'; |
||
67 | |||
68 | /** |
||
69 | * Public Key QA |
||
70 | * |
||
71 | * @var object[] |
||
72 | */ |
||
73 | protected $QA; |
||
74 | |||
75 | /** |
||
76 | * Curve |
||
77 | * |
||
78 | * @var \phpseclib3\Crypt\EC\BaseCurves\Base |
||
79 | */ |
||
80 | protected $curve; |
||
81 | |||
82 | /** |
||
83 | * Signature Format |
||
84 | * |
||
85 | * @var string |
||
874 | daniel-mar | 86 | * @access private |
827 | daniel-mar | 87 | */ |
88 | protected $format; |
||
89 | |||
90 | /** |
||
91 | * Signature Format (Short) |
||
92 | * |
||
93 | * @var string |
||
874 | daniel-mar | 94 | * @access private |
827 | daniel-mar | 95 | */ |
96 | protected $shortFormat; |
||
97 | |||
98 | /** |
||
99 | * Curve Name |
||
100 | * |
||
101 | * @var string |
||
102 | */ |
||
103 | private $curveName; |
||
104 | |||
105 | /** |
||
106 | * Curve Order |
||
107 | * |
||
108 | * Used for deterministic ECDSA |
||
109 | * |
||
110 | * @var \phpseclib3\Math\BigInteger |
||
111 | */ |
||
112 | protected $q; |
||
113 | |||
114 | /** |
||
115 | * Alias for the private key |
||
116 | * |
||
117 | * Used for deterministic ECDSA. AsymmetricKey expects $x. I don't like x because |
||
118 | * with x you have x * the base point yielding an (x, y)-coordinate that is the |
||
119 | * public key. But the x is different depending on which side of the equal sign |
||
120 | * you're on. It's less ambiguous if you do dA * base point = (x, y)-coordinate. |
||
121 | * |
||
122 | * @var \phpseclib3\Math\BigInteger |
||
123 | */ |
||
124 | protected $x; |
||
125 | |||
126 | /** |
||
127 | * Context |
||
128 | * |
||
129 | * @var string |
||
130 | */ |
||
131 | protected $context; |
||
132 | |||
133 | /** |
||
134 | * Create public / private key pair. |
||
135 | * |
||
874 | daniel-mar | 136 | * @access public |
827 | daniel-mar | 137 | * @param string $curve |
138 | * @return \phpseclib3\Crypt\EC\PrivateKey |
||
139 | */ |
||
140 | public static function createKey($curve) |
||
141 | { |
||
142 | self::initialize_static_variables(); |
||
143 | |||
144 | if (!isset(self::$engines['PHP'])) { |
||
145 | self::useBestEngine(); |
||
146 | } |
||
147 | |||
148 | $curve = strtolower($curve); |
||
149 | if (self::$engines['libsodium'] && $curve == 'ed25519' && function_exists('sodium_crypto_sign_keypair')) { |
||
150 | $kp = sodium_crypto_sign_keypair(); |
||
151 | |||
152 | $privatekey = EC::loadFormat('libsodium', sodium_crypto_sign_secretkey($kp)); |
||
153 | //$publickey = EC::loadFormat('libsodium', sodium_crypto_sign_publickey($kp)); |
||
154 | |||
155 | $privatekey->curveName = 'Ed25519'; |
||
156 | //$publickey->curveName = $curve; |
||
157 | |||
158 | return $privatekey; |
||
159 | } |
||
160 | |||
161 | $privatekey = new PrivateKey(); |
||
162 | |||
163 | $curveName = $curve; |
||
164 | if (preg_match('#(?:^curve|^ed)\d+$#', $curveName)) { |
||
165 | $curveName = ucfirst($curveName); |
||
166 | } elseif (substr($curveName, 0, 10) == 'brainpoolp') { |
||
167 | $curveName = 'brainpoolP' . substr($curveName, 10); |
||
168 | } |
||
169 | $curve = '\phpseclib3\Crypt\EC\Curves\\' . $curveName; |
||
170 | |||
171 | if (!class_exists($curve)) { |
||
172 | throw new UnsupportedCurveException('Named Curve of ' . $curveName . ' is not supported'); |
||
173 | } |
||
174 | |||
175 | $reflect = new \ReflectionClass($curve); |
||
176 | $curveName = $reflect->isFinal() ? |
||
177 | $reflect->getParentClass()->getShortName() : |
||
178 | $reflect->getShortName(); |
||
179 | |||
180 | $curve = new $curve(); |
||
181 | $privatekey->dA = $dA = $curve->createRandomMultiplier(); |
||
182 | if ($curve instanceof Curve25519 && self::$engines['libsodium']) { |
||
183 | //$r = pack('H*', '0900000000000000000000000000000000000000000000000000000000000000'); |
||
184 | //$QA = sodium_crypto_scalarmult($dA->toBytes(), $r); |
||
185 | $QA = sodium_crypto_box_publickey_from_secretkey($dA->toBytes()); |
||
186 | $privatekey->QA = [$curve->convertInteger(new BigInteger(strrev($QA), 256))]; |
||
187 | } else { |
||
188 | $privatekey->QA = $curve->multiplyPoint($curve->getBasePoint(), $dA); |
||
189 | } |
||
190 | $privatekey->curve = $curve; |
||
191 | |||
192 | //$publickey = clone $privatekey; |
||
193 | //unset($publickey->dA); |
||
194 | //unset($publickey->x); |
||
195 | |||
196 | $privatekey->curveName = $curveName; |
||
197 | //$publickey->curveName = $curveName; |
||
198 | |||
199 | if ($privatekey->curve instanceof TwistedEdwardsCurve) { |
||
200 | return $privatekey->withHash($curve::HASH); |
||
201 | } |
||
202 | |||
203 | return $privatekey; |
||
204 | } |
||
205 | |||
206 | /** |
||
207 | * OnLoad Handler |
||
208 | * |
||
209 | * @return bool |
||
874 | daniel-mar | 210 | * @access protected |
827 | daniel-mar | 211 | * @param array $components |
212 | */ |
||
213 | protected static function onLoad($components) |
||
214 | { |
||
215 | if (!isset(self::$engines['PHP'])) { |
||
216 | self::useBestEngine(); |
||
217 | } |
||
218 | |||
219 | if (!isset($components['dA']) && !isset($components['QA'])) { |
||
220 | $new = new Parameters(); |
||
221 | $new->curve = $components['curve']; |
||
222 | return $new; |
||
223 | } |
||
224 | |||
225 | $new = isset($components['dA']) ? |
||
226 | new PrivateKey() : |
||
227 | new PublicKey(); |
||
228 | $new->curve = $components['curve']; |
||
229 | $new->QA = $components['QA']; |
||
230 | |||
231 | if (isset($components['dA'])) { |
||
232 | $new->dA = $components['dA']; |
||
233 | } |
||
234 | |||
235 | if ($new->curve instanceof TwistedEdwardsCurve) { |
||
236 | return $new->withHash($components['curve']::HASH); |
||
237 | } |
||
238 | |||
239 | return $new; |
||
240 | } |
||
241 | |||
242 | /** |
||
243 | * Constructor |
||
244 | * |
||
245 | * PublicKey and PrivateKey objects can only be created from abstract RSA class |
||
246 | */ |
||
247 | protected function __construct() |
||
248 | { |
||
249 | $this->sigFormat = self::validatePlugin('Signature', 'ASN1'); |
||
250 | $this->shortFormat = 'ASN1'; |
||
251 | |||
252 | parent::__construct(); |
||
253 | } |
||
254 | |||
255 | /** |
||
256 | * Returns the curve |
||
257 | * |
||
258 | * Returns a string if it's a named curve, an array if not |
||
259 | * |
||
874 | daniel-mar | 260 | * @access public |
827 | daniel-mar | 261 | * @return string|array |
262 | */ |
||
263 | public function getCurve() |
||
264 | { |
||
265 | if ($this->curveName) { |
||
266 | return $this->curveName; |
||
267 | } |
||
268 | |||
269 | if ($this->curve instanceof MontgomeryCurve) { |
||
270 | $this->curveName = $this->curve instanceof Curve25519 ? 'Curve25519' : 'Curve448'; |
||
271 | return $this->curveName; |
||
272 | } |
||
273 | |||
274 | if ($this->curve instanceof TwistedEdwardsCurve) { |
||
275 | $this->curveName = $this->curve instanceof Ed25519 ? 'Ed25519' : 'Ed448'; |
||
276 | return $this->curveName; |
||
277 | } |
||
278 | |||
279 | $params = $this->getParameters()->toString('PKCS8', ['namedCurve' => true]); |
||
280 | $decoded = ASN1::extractBER($params); |
||
281 | $decoded = ASN1::decodeBER($decoded); |
||
282 | $decoded = ASN1::asn1map($decoded[0], ECParameters::MAP); |
||
283 | if (isset($decoded['namedCurve'])) { |
||
284 | $this->curveName = $decoded['namedCurve']; |
||
285 | return $decoded['namedCurve']; |
||
286 | } |
||
287 | |||
288 | if (!$namedCurves) { |
||
289 | PKCS1::useSpecifiedCurve(); |
||
290 | } |
||
291 | |||
292 | return $decoded; |
||
293 | } |
||
294 | |||
295 | /** |
||
296 | * Returns the key size |
||
297 | * |
||
298 | * Quoting https://tools.ietf.org/html/rfc5656#section-2, |
||
299 | * |
||
300 | * "The size of a set of elliptic curve domain parameters on a prime |
||
301 | * curve is defined as the number of bits in the binary representation |
||
302 | * of the field order, commonly denoted by p. Size on a |
||
303 | * characteristic-2 curve is defined as the number of bits in the binary |
||
304 | * representation of the field, commonly denoted by m. A set of |
||
305 | * elliptic curve domain parameters defines a group of order n generated |
||
306 | * by a base point P" |
||
307 | * |
||
874 | daniel-mar | 308 | * @access public |
827 | daniel-mar | 309 | * @return int |
310 | */ |
||
311 | public function getLength() |
||
312 | { |
||
313 | return $this->curve->getLength(); |
||
314 | } |
||
315 | |||
316 | /** |
||
317 | * Returns the current engine being used |
||
318 | * |
||
319 | * @see self::useInternalEngine() |
||
320 | * @see self::useBestEngine() |
||
874 | daniel-mar | 321 | * @access public |
827 | daniel-mar | 322 | * @return string |
323 | */ |
||
324 | public function getEngine() |
||
325 | { |
||
326 | if (!isset(self::$engines['PHP'])) { |
||
327 | self::useBestEngine(); |
||
328 | } |
||
329 | if ($this->curve instanceof TwistedEdwardsCurve) { |
||
330 | return $this->curve instanceof Ed25519 && self::$engines['libsodium'] && !isset($this->context) ? |
||
331 | 'libsodium' : 'PHP'; |
||
332 | } |
||
333 | |||
334 | return self::$engines['OpenSSL'] && in_array($this->hash->getHash(), openssl_get_md_methods()) ? |
||
335 | 'OpenSSL' : 'PHP'; |
||
336 | } |
||
337 | |||
338 | /** |
||
339 | * Returns the public key coordinates as a string |
||
340 | * |
||
341 | * Used by ECDH |
||
342 | * |
||
343 | * @return string |
||
344 | */ |
||
345 | public function getEncodedCoordinates() |
||
346 | { |
||
347 | if ($this->curve instanceof MontgomeryCurve) { |
||
348 | return strrev($this->QA[0]->toBytes(true)); |
||
349 | } |
||
350 | if ($this->curve instanceof TwistedEdwardsCurve) { |
||
351 | return $this->curve->encodePoint($this->QA); |
||
352 | } |
||
353 | return "\4" . $this->QA[0]->toBytes(true) . $this->QA[1]->toBytes(true); |
||
354 | } |
||
355 | |||
356 | /** |
||
357 | * Returns the parameters |
||
358 | * |
||
359 | * @see self::getPublicKey() |
||
874 | daniel-mar | 360 | * @access public |
827 | daniel-mar | 361 | * @param string $type optional |
362 | * @return mixed |
||
363 | */ |
||
364 | public function getParameters($type = 'PKCS1') |
||
365 | { |
||
366 | $type = self::validatePlugin('Keys', $type, 'saveParameters'); |
||
367 | |||
368 | $key = $type::saveParameters($this->curve); |
||
369 | |||
370 | return EC::load($key, 'PKCS1') |
||
371 | ->withHash($this->hash->getHash()) |
||
372 | ->withSignatureFormat($this->shortFormat); |
||
373 | } |
||
374 | |||
375 | /** |
||
376 | * Determines the signature padding mode |
||
377 | * |
||
378 | * Valid values are: ASN1, SSH2, Raw |
||
379 | * |
||
874 | daniel-mar | 380 | * @access public |
827 | daniel-mar | 381 | * @param string $format |
382 | */ |
||
383 | public function withSignatureFormat($format) |
||
384 | { |
||
385 | if ($this->curve instanceof MontgomeryCurve) { |
||
386 | throw new UnsupportedOperationException('Montgomery Curves cannot be used to create signatures'); |
||
387 | } |
||
388 | |||
389 | $new = clone $this; |
||
390 | $new->shortFormat = $format; |
||
391 | $new->sigFormat = self::validatePlugin('Signature', $format); |
||
392 | return $new; |
||
393 | } |
||
394 | |||
395 | /** |
||
396 | * Returns the signature format currently being used |
||
397 | * |
||
874 | daniel-mar | 398 | * @access public |
827 | daniel-mar | 399 | */ |
400 | public function getSignatureFormat() |
||
401 | { |
||
402 | return $this->shortFormat; |
||
403 | } |
||
404 | |||
405 | /** |
||
406 | * Sets the context |
||
407 | * |
||
408 | * Used by Ed25519 / Ed448. |
||
409 | * |
||
410 | * @see self::sign() |
||
411 | * @see self::verify() |
||
874 | daniel-mar | 412 | * @access public |
827 | daniel-mar | 413 | * @param string $context optional |
414 | */ |
||
415 | public function withContext($context = null) |
||
416 | { |
||
417 | if (!$this->curve instanceof TwistedEdwardsCurve) { |
||
418 | throw new UnsupportedCurveException('Only Ed25519 and Ed448 support contexts'); |
||
419 | } |
||
420 | |||
421 | $new = clone $this; |
||
422 | if (!isset($context)) { |
||
423 | $new->context = null; |
||
424 | return $new; |
||
425 | } |
||
426 | if (!is_string($context)) { |
||
427 | throw new \InvalidArgumentException('setContext expects a string'); |
||
428 | } |
||
429 | if (strlen($context) > 255) { |
||
430 | throw new \LengthException('The context is supposed to be, at most, 255 bytes long'); |
||
431 | } |
||
432 | $new->context = $context; |
||
433 | return $new; |
||
434 | } |
||
435 | |||
436 | /** |
||
437 | * Returns the signature format currently being used |
||
438 | * |
||
874 | daniel-mar | 439 | * @access public |
827 | daniel-mar | 440 | */ |
441 | public function getContext() |
||
442 | { |
||
443 | return $this->context; |
||
444 | } |
||
445 | |||
446 | /** |
||
447 | * Determines which hashing function should be used |
||
448 | * |
||
874 | daniel-mar | 449 | * @access public |
827 | daniel-mar | 450 | * @param string $hash |
451 | */ |
||
452 | public function withHash($hash) |
||
453 | { |
||
454 | if ($this->curve instanceof MontgomeryCurve) { |
||
455 | throw new UnsupportedOperationException('Montgomery Curves cannot be used to create signatures'); |
||
456 | } |
||
457 | if ($this->curve instanceof Ed25519 && $hash != 'sha512') { |
||
458 | throw new UnsupportedAlgorithmException('Ed25519 only supports sha512 as a hash'); |
||
459 | } |
||
460 | if ($this->curve instanceof Ed448 && $hash != 'shake256-912') { |
||
461 | throw new UnsupportedAlgorithmException('Ed448 only supports shake256 with a length of 114 bytes'); |
||
462 | } |
||
463 | |||
464 | return parent::withHash($hash); |
||
465 | } |
||
466 | |||
467 | /** |
||
468 | * __toString() magic method |
||
469 | * |
||
470 | * @return string |
||
471 | */ |
||
472 | public function __toString() |
||
473 | { |
||
474 | if ($this->curve instanceof MontgomeryCurve) { |
||
475 | return ''; |
||
476 | } |
||
477 | |||
478 | return parent::__toString(); |
||
479 | } |
||
480 | } |