comparison vendor/pear/crypt_gpg/Crypt/GPG/KeyGenerator.php @ 0:1e000243b222

vanilla 1.3.3 distro, I hope
author Charlie Root
date Thu, 04 Jan 2018 15:50:29 -0500
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:1e000243b222
1 <?php
2
3 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
4
5 /**
6 * Crypt_GPG is a package to use GPG from PHP
7 *
8 * This file contains an object that handles GnuPG key generation.
9 *
10 * PHP version 5
11 *
12 * LICENSE:
13 *
14 * This library is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License as
16 * published by the Free Software Foundation; either version 2.1 of the
17 * License, or (at your option) any later version.
18 *
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
23 *
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, see
26 * <http://www.gnu.org/licenses/>
27 *
28 * @category Encryption
29 * @package Crypt_GPG
30 * @author Michael Gauthier <mike@silverorange.com>
31 * @copyright 2011-2013 silverorange
32 * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
33 * @link http://pear.php.net/package/Crypt_GPG
34 * @link http://www.gnupg.org/
35 */
36
37 /**
38 * Base class for GPG methods
39 */
40 require_once 'Crypt/GPGAbstract.php';
41
42 // {{{ class Crypt_GPG_KeyGenerator
43
44 /**
45 * GnuPG key generator
46 *
47 * This class provides an object oriented interface for generating keys with
48 * the GNU Privacy Guard (GPG).
49 *
50 * Secure key generation requires true random numbers, and as such can be slow.
51 * If the operating system runs out of entropy, key generation will block until
52 * more entropy is available.
53 *
54 * If quick key generation is important, a hardware entropy generator, or an
55 * entropy gathering daemon may be installed. For example, administrators of
56 * Debian systems may want to install the 'randomsound' package.
57 *
58 * This class uses the experimental automated key generation support available
59 * in GnuPG. See <b>doc/DETAILS</b> in the
60 * {@link http://www.gnupg.org/download/ GPG distribution} for detailed
61 * information on the key generation format.
62 *
63 * @category Encryption
64 * @package Crypt_GPG
65 * @author Nathan Fredrickson <nathan@silverorange.com>
66 * @author Michael Gauthier <mike@silverorange.com>
67 * @copyright 2005-2013 silverorange
68 * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
69 * @link http://pear.php.net/package/Crypt_GPG
70 * @link http://www.gnupg.org/
71 */
72 class Crypt_GPG_KeyGenerator extends Crypt_GPGAbstract
73 {
74 // {{{ protected properties
75
76 /**
77 * The expiration date of generated keys
78 *
79 * @var integer
80 *
81 * @see Crypt_GPG_KeyGenerator::setExpirationDate()
82 */
83 protected $expirationDate = 0;
84
85 /**
86 * The passphrase of generated keys
87 *
88 * @var string
89 *
90 * @see Crypt_GPG_KeyGenerator::setPassphrase()
91 */
92 protected $passphrase = '';
93
94 /**
95 * The algorithm for generated primary keys
96 *
97 * @var integer
98 *
99 * @see Crypt_GPG_KeyGenerator::setKeyParams()
100 */
101 protected $keyAlgorithm = Crypt_GPG_SubKey::ALGORITHM_DSA;
102
103 /**
104 * The size of generated primary keys
105 *
106 * @var integer
107 *
108 * @see Crypt_GPG_KeyGenerator::setKeyParams()
109 */
110 protected $keySize = 1024;
111
112 /**
113 * The usages of generated primary keys
114 *
115 * This is a bitwise combination of the usage constants in
116 * {@link Crypt_GPG_SubKey}.
117 *
118 * @var integer
119 *
120 * @see Crypt_GPG_KeyGenerator::setKeyParams()
121 */
122 protected $keyUsage = 6; // USAGE_SIGN | USAGE_CERTIFY
123
124 /**
125 * The algorithm for generated sub-keys
126 *
127 * @var integer
128 *
129 * @see Crypt_GPG_KeyGenerator::setSubKeyParams()
130 */
131 protected $subKeyAlgorithm = Crypt_GPG_SubKey::ALGORITHM_ELGAMAL_ENC;
132
133 /**
134 * The size of generated sub-keys
135 *
136 * @var integer
137 *
138 * @see Crypt_GPG_KeyGenerator::setSubKeyParams()
139 */
140 protected $subKeySize = 2048;
141
142 /**
143 * The usages of generated sub-keys
144 *
145 * This is a bitwise combination of the usage constants in
146 * {@link Crypt_GPG_SubKey}.
147 *
148 * @var integer
149 *
150 * @see Crypt_GPG_KeyGenerator::setSubKeyParams()
151 */
152 protected $subKeyUsage = Crypt_GPG_SubKey::USAGE_ENCRYPT;
153
154 // }}}
155 // {{{ __construct()
156
157 /**
158 * Creates a new GnuPG key generator
159 *
160 * Available options are:
161 *
162 * - <kbd>string homedir</kbd> - the directory where the GPG
163 * keyring files are stored. If not
164 * specified, Crypt_GPG uses the
165 * default of <kbd>~/.gnupg</kbd>.
166 * - <kbd>string publicKeyring</kbd> - the file path of the public
167 * keyring. Use this if the public
168 * keyring is not in the homedir, or
169 * if the keyring is in a directory
170 * not writable by the process
171 * invoking GPG (like Apache). Then
172 * you can specify the path to the
173 * keyring with this option
174 * (/foo/bar/pubring.gpg), and specify
175 * a writable directory (like /tmp)
176 * using the <i>homedir</i> option.
177 * - <kbd>string privateKeyring</kbd> - the file path of the private
178 * keyring. Use this if the private
179 * keyring is not in the homedir, or
180 * if the keyring is in a directory
181 * not writable by the process
182 * invoking GPG (like Apache). Then
183 * you can specify the path to the
184 * keyring with this option
185 * (/foo/bar/secring.gpg), and specify
186 * a writable directory (like /tmp)
187 * using the <i>homedir</i> option.
188 * - <kbd>string trustDb</kbd> - the file path of the web-of-trust
189 * database. Use this if the trust
190 * database is not in the homedir, or
191 * if the database is in a directory
192 * not writable by the process
193 * invoking GPG (like Apache). Then
194 * you can specify the path to the
195 * trust database with this option
196 * (/foo/bar/trustdb.gpg), and specify
197 * a writable directory (like /tmp)
198 * using the <i>homedir</i> option.
199 * - <kbd>string binary</kbd> - the location of the GPG binary. If
200 * not specified, the driver attempts
201 * to auto-detect the GPG binary
202 * location using a list of known
203 * default locations for the current
204 * operating system. The option
205 * <kbd>gpgBinary</kbd> is a
206 * deprecated alias for this option.
207 * - <kbd>string agent</kbd> - the location of the GnuPG agent
208 * binary. The gpg-agent is only
209 * used for GnuPG 2.x. If not
210 * specified, the engine attempts
211 * to auto-detect the gpg-agent
212 * binary location using a list of
213 * know default locations for the
214 * current operating system.
215 * - <kbd>mixed debug</kbd> - whether or not to use debug mode.
216 * When debug mode is on, all
217 * communication to and from the GPG
218 * subprocess is logged. This can be
219 *
220 * @param array $options optional. An array of options used to create the
221 * GPG object. All options are optional and are
222 * represented as key-value pairs.
223 *
224 * @throws Crypt_GPG_FileException if the <kbd>homedir</kbd> does not exist
225 * and cannot be created. This can happen if <kbd>homedir</kbd> is
226 * not specified, Crypt_GPG is run as the web user, and the web
227 * user has no home directory. This exception is also thrown if any
228 * of the options <kbd>publicKeyring</kbd>,
229 * <kbd>privateKeyring</kbd> or <kbd>trustDb</kbd> options are
230 * specified but the files do not exist or are are not readable.
231 * This can happen if the user running the Crypt_GPG process (for
232 * example, the Apache user) does not have permission to read the
233 * files.
234 *
235 * @throws PEAR_Exception if the provided <kbd>binary</kbd> is invalid, or
236 * if no <kbd>binary</kbd> is provided and no suitable binary could
237 * be found.
238 *
239 * @throws PEAR_Exception if the provided <kbd>agent</kbd> is invalid, or
240 * if no <kbd>agent</kbd> is provided and no suitable gpg-agent
241 * cound be found.
242 */
243 public function __construct(array $options = array())
244 {
245 parent::__construct($options);
246 }
247
248 // }}}
249 // {{{ setExpirationDate()
250
251 /**
252 * Sets the expiration date of generated keys
253 *
254 * @param string|integer $date either a string that may be parsed by
255 * PHP's strtotime() function, or an integer
256 * timestamp representing the number of seconds
257 * since the UNIX epoch. This date must be at
258 * least one date in the future. Keys that
259 * expire in the past may not be generated. Use
260 * an expiration date of 0 for keys that do not
261 * expire.
262 *
263 * @throws InvalidArgumentException if the date is not a valid format, or
264 * if the date is not at least one day in
265 * the future, or if the date is greater
266 * than 2038-01-19T03:14:07.
267 *
268 * @return Crypt_GPG_KeyGenerator the current object, for fluent interface.
269 */
270 public function setExpirationDate($date)
271 {
272 if (is_int($date) || ctype_digit(strval($date))) {
273 $expirationDate = intval($date);
274 } else {
275 $expirationDate = strtotime($date);
276 }
277
278 if ($expirationDate === false) {
279 throw new InvalidArgumentException(
280 sprintf(
281 'Invalid expiration date format: "%s". Please use a ' .
282 'format compatible with PHP\'s strtotime().',
283 $date
284 )
285 );
286 }
287
288 if ($expirationDate !== 0 && $expirationDate < time() + 86400) {
289 throw new InvalidArgumentException(
290 'Expiration date must be at least a day in the future.'
291 );
292 }
293
294 // GnuPG suffers from the 2038 bug
295 if ($expirationDate > 2147483647) {
296 throw new InvalidArgumentException(
297 'Expiration date must not be greater than 2038-01-19T03:14:07.'
298 );
299 }
300
301 $this->expirationDate = $expirationDate;
302
303 return $this;
304 }
305
306 // }}}
307 // {{{ setPassphrase()
308
309 /**
310 * Sets the passphrase of generated keys
311 *
312 * @param string $passphrase the passphrase to use for generated keys. Use
313 * null or an empty string for no passphrase.
314 *
315 * @return Crypt_GPG_KeyGenerator the current object, for fluent interface.
316 */
317 public function setPassphrase($passphrase)
318 {
319 $this->passphrase = strval($passphrase);
320 return $this;
321 }
322
323 // }}}
324 // {{{ setKeyParams()
325
326 /**
327 * Sets the parameters for the primary key of generated key-pairs
328 *
329 * @param integer $algorithm the algorithm used by the key. This should be
330 * one of the Crypt_GPG_SubKey::ALGORITHM_*
331 * constants.
332 * @param integer $size optional. The size of the key. Different
333 * algorithms have different size requirements.
334 * If not specified, the default size for the
335 * specified algorithm will be used. If an
336 * invalid key size is used, GnuPG will do its
337 * best to round it to a valid size.
338 * @param integer $usage optional. A bitwise combination of key usages.
339 * If not specified, the primary key will be used
340 * only to sign and certify. This is the default
341 * behavior of GnuPG in interactive mode. Use
342 * the Crypt_GPG_SubKey::USAGE_* constants here.
343 * The primary key may be used to certify even
344 * if the certify usage is not specified.
345 *
346 * @return Crypt_GPG_KeyGenerator the current object, for fluent interface.
347 */
348 public function setKeyParams($algorithm, $size = 0, $usage = 0)
349 {
350 $algorithm = intval($algorithm);
351
352 if ($algorithm === Crypt_GPG_SubKey::ALGORITHM_ELGAMAL_ENC) {
353 throw new Crypt_GPG_InvalidKeyParamsException(
354 'Primary key algorithm must be capable of signing. The ' .
355 'Elgamal algorithm can only encrypt.',
356 0,
357 $algorithm,
358 $size,
359 $usage
360 );
361 }
362
363 if ($size != 0) {
364 $size = intval($size);
365 }
366
367 if ($usage != 0) {
368 $usage = intval($usage);
369 }
370
371 $usageEncrypt = Crypt_GPG_SubKey::USAGE_ENCRYPT;
372
373 if ($algorithm === Crypt_GPG_SubKey::ALGORITHM_DSA
374 && ($usage & $usageEncrypt) === $usageEncrypt
375 ) {
376 throw new Crypt_GPG_InvalidKeyParamsException(
377 'The DSA algorithm is not capable of encrypting. Please ' .
378 'specify a different algorithm or do not include encryption ' .
379 'as a usage for the primary key.',
380 0,
381 $algorithm,
382 $size,
383 $usage
384 );
385 }
386
387 $this->keyAlgorithm = $algorithm;
388
389 if ($size != 0) {
390 $this->keySize = $size;
391 }
392
393 if ($usage != 0) {
394 $this->keyUsage = $usage;
395 }
396
397 return $this;
398 }
399
400 // }}}
401 // {{{ setSubKeyParams()
402
403 /**
404 * Sets the parameters for the sub-key of generated key-pairs
405 *
406 * @param integer $algorithm the algorithm used by the key. This should be
407 * one of the Crypt_GPG_SubKey::ALGORITHM_*
408 * constants.
409 * @param integer $size optional. The size of the key. Different
410 * algorithms have different size requirements.
411 * If not specified, the default size for the
412 * specified algorithm will be used. If an
413 * invalid key size is used, GnuPG will do its
414 * best to round it to a valid size.
415 * @param integer $usage optional. A bitwise combination of key usages.
416 * If not specified, the sub-key will be used
417 * only to encrypt. This is the default behavior
418 * of GnuPG in interactive mode. Use the
419 * Crypt_GPG_SubKey::USAGE_* constants here.
420 *
421 * @return Crypt_GPG_KeyGenerator the current object, for fluent interface.
422 */
423 public function setSubKeyParams($algorithm, $size = '', $usage = 0)
424 {
425 $algorithm = intval($algorithm);
426
427 if ($size != 0) {
428 $size = intval($size);
429 }
430
431 if ($usage != 0) {
432 $usage = intval($usage);
433 }
434
435 $usageSign = Crypt_GPG_SubKey::USAGE_SIGN;
436
437 if ($algorithm === Crypt_GPG_SubKey::ALGORITHM_ELGAMAL_ENC
438 && ($usage & $usageSign) === $usageSign
439 ) {
440 throw new Crypt_GPG_InvalidKeyParamsException(
441 'The Elgamal algorithm is not capable of signing. Please ' .
442 'specify a different algorithm or do not include signing ' .
443 'as a usage for the sub-key.',
444 0,
445 $algorithm,
446 $size,
447 $usage
448 );
449 }
450
451 $usageEncrypt = Crypt_GPG_SubKey::USAGE_ENCRYPT;
452
453 if ($algorithm === Crypt_GPG_SubKey::ALGORITHM_DSA
454 && ($usage & $usageEncrypt) === $usageEncrypt
455 ) {
456 throw new Crypt_GPG_InvalidKeyParamsException(
457 'The DSA algorithm is not capable of encrypting. Please ' .
458 'specify a different algorithm or do not include encryption ' .
459 'as a usage for the sub-key.',
460 0,
461 $algorithm,
462 $size,
463 $usage
464 );
465 }
466
467 $this->subKeyAlgorithm = $algorithm;
468
469 if ($size != 0) {
470 $this->subKeySize = $size;
471 }
472
473 if ($usage != 0) {
474 $this->subKeyUsage = $usage;
475 }
476
477 return $this;
478 }
479
480 // }}}
481 // {{{ generateKey()
482
483 /**
484 * Generates a new key-pair in the current keyring
485 *
486 * Secure key generation requires true random numbers, and as such can be
487 * solw. If the operating system runs out of entropy, key generation will
488 * block until more entropy is available.
489 *
490 * If quick key generation is important, a hardware entropy generator, or
491 * an entropy gathering daemon may be installed. For example,
492 * administrators of Debian systems may want to install the 'randomsound'
493 * package.
494 *
495 * @param string|Crypt_GPG_UserId $name either a {@link Crypt_GPG_UserId}
496 * object, or a string containing
497 * the name of the user id.
498 * @param string $email optional. If <i>$name</i> is
499 * specified as a string, this is
500 * the email address of the user id.
501 * @param string $comment optional. If <i>$name</i> is
502 * specified as a string, this is
503 * the comment of the user id.
504 *
505 * @return Crypt_GPG_Key the newly generated key.
506 *
507 * @throws Crypt_GPG_KeyNotCreatedException if the key parameters are
508 * incorrect, if an unknown error occurs during key generation, or
509 * if the newly generated key is not found in the keyring.
510 *
511 * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs.
512 * Use the <kbd>debug</kbd> option and file a bug report if these
513 * exceptions occur.
514 */
515 public function generateKey($name, $email = '', $comment = '')
516 {
517 $handle = uniqid('key', true);
518
519 $userId = $this->getUserId($name, $email, $comment);
520
521 $keyParams = array(
522 'Key-Type' => $this->keyAlgorithm,
523 'Key-Length' => $this->keySize,
524 'Key-Usage' => $this->getUsage($this->keyUsage),
525 'Subkey-Type' => $this->subKeyAlgorithm,
526 'Subkey-Length' => $this->subKeySize,
527 'Subkey-Usage' => $this->getUsage($this->subKeyUsage),
528 'Name-Real' => $userId->getName(),
529 'Handle' => $handle,
530 );
531
532 if ($this->expirationDate != 0) {
533 // GnuPG only accepts granularity of days
534 $expirationDate = date('Y-m-d', $this->expirationDate);
535 $keyParams['Expire-Date'] = $expirationDate;
536 }
537
538 if (strlen($this->passphrase)) {
539 $keyParams['Passphrase'] = $this->passphrase;
540 }
541
542 if ($userId->getEmail() != '') {
543 $keyParams['Name-Email'] = $userId->getEmail();
544 }
545
546 if ($userId->getComment() != '') {
547 $keyParams['Name-Comment'] = $userId->getComment();
548 }
549
550 $keyParamsFormatted = array();
551 foreach ($keyParams as $name => $value) {
552 $keyParamsFormatted[] = $name . ': ' . $value;
553 }
554
555 // This is required in GnuPG 2.1
556 if (!strlen($this->passphrase)) {
557 $keyParamsFormatted[] = '%no-protection';
558 }
559
560 $input = implode("\n", $keyParamsFormatted) . "\n%commit\n";
561
562 $this->engine->reset();
563 $this->engine->setProcessData('Handle', $handle);
564 $this->engine->setInput($input);
565 $this->engine->setOutput($output);
566 $this->engine->setOperation('--gen-key', array('--batch'));
567
568 try {
569 $this->engine->run();
570 } catch (Crypt_GPG_InvalidKeyParamsException $e) {
571 switch ($this->engine->getProcessData('LineNumber')) {
572 case 1:
573 throw new Crypt_GPG_InvalidKeyParamsException(
574 'Invalid primary key algorithm specified.',
575 0,
576 $this->keyAlgorithm,
577 $this->keySize,
578 $this->keyUsage
579 );
580 case 4:
581 throw new Crypt_GPG_InvalidKeyParamsException(
582 'Invalid sub-key algorithm specified.',
583 0,
584 $this->subKeyAlgorithm,
585 $this->subKeySize,
586 $this->subKeyUsage
587 );
588 default:
589 throw $e;
590 }
591 }
592
593 $fingerprint = $this->engine->getProcessData('KeyCreated');
594 $keys = $this->_getKeys($fingerprint);
595
596 if (count($keys) === 0) {
597 throw new Crypt_GPG_KeyNotCreatedException(
598 sprintf(
599 'Newly created key "%s" not found in keyring.',
600 $fingerprint
601 )
602 );
603 }
604
605 return $keys[0];
606 }
607
608 // }}}
609 // {{{ getUsage()
610
611 /**
612 * Builds a GnuPG key usage string suitable for key generation
613 *
614 * See <b>doc/DETAILS</b> in the
615 * {@link http://www.gnupg.org/download/ GPG distribution} for detailed
616 * information on the key usage format.
617 *
618 * @param integer $usage a bitwise combination of the key usages. This is
619 * a combination of the Crypt_GPG_SubKey::USAGE_*
620 * constants.
621 *
622 * @return string the key usage string.
623 */
624 protected function getUsage($usage)
625 {
626 $map = array(
627 Crypt_GPG_SubKey::USAGE_ENCRYPT => 'encrypt',
628 Crypt_GPG_SubKey::USAGE_SIGN => 'sign',
629 Crypt_GPG_SubKey::USAGE_CERTIFY => 'cert',
630 Crypt_GPG_SubKey::USAGE_AUTHENTICATION => 'auth',
631 );
632
633 // cert is always used for primary keys and does not need to be
634 // specified
635 $usage &= ~Crypt_GPG_SubKey::USAGE_CERTIFY;
636
637 $usageArray = array();
638
639 foreach ($map as $key => $value) {
640 if (($usage & $key) === $key) {
641 $usageArray[] = $value;
642 }
643 }
644
645 return implode(',', $usageArray);
646 }
647
648 // }}}
649 // {{{ getUserId()
650
651 /**
652 * Gets a user id object from parameters
653 *
654 * @param string|Crypt_GPG_UserId $name either a {@link Crypt_GPG_UserId}
655 * object, or a string containing
656 * the name of the user id.
657 * @param string $email optional. If <i>$name</i> is
658 * specified as a string, this is
659 * the email address of the user id.
660 * @param string $comment optional. If <i>$name</i> is
661 * specified as a string, this is
662 * the comment of the user id.
663 *
664 * @return Crypt_GPG_UserId a user id object for the specified parameters.
665 */
666 protected function getUserId($name, $email = '', $comment = '')
667 {
668 if ($name instanceof Crypt_GPG_UserId) {
669 $userId = $name;
670 } else {
671 $userId = new Crypt_GPG_UserId();
672 $userId->setName($name)->setEmail($email)->setComment($comment);
673 }
674
675 return $userId;
676 }
677
678 // }}}
679 }
680
681 // }}}
682
683 ?>