0
|
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 package provides an object oriented interface to GNU Privacy
|
|
9 * Guard (GPG). It requires the GPG executable to be on the system.
|
|
10 *
|
|
11 * Though GPG can support symmetric-key cryptography, this package is intended
|
|
12 * only to facilitate public-key cryptography.
|
|
13 *
|
|
14 * This file contains an abstract implementation of a user of the
|
|
15 * {@link Crypt_GPG_Engine} class.
|
|
16 *
|
|
17 * PHP version 5
|
|
18 *
|
|
19 * LICENSE:
|
|
20 *
|
|
21 * This library is free software; you can redistribute it and/or modify
|
|
22 * it under the terms of the GNU Lesser General Public License as
|
|
23 * published by the Free Software Foundation; either version 2.1 of the
|
|
24 * License, or (at your option) any later version.
|
|
25 *
|
|
26 * This library is distributed in the hope that it will be useful,
|
|
27 * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
29 * Lesser General Public License for more details.
|
|
30 *
|
|
31 * You should have received a copy of the GNU Lesser General Public
|
|
32 * License along with this library; if not, see
|
|
33 * <http://www.gnu.org/licenses/>
|
|
34 *
|
|
35 * @category Encryption
|
|
36 * @package Crypt_GPG
|
|
37 * @author Nathan Fredrickson <nathan@silverorange.com>
|
|
38 * @author Michael Gauthier <mike@silverorange.com>
|
|
39 * @copyright 2005-2013 silverorange
|
|
40 * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
|
41 * @link http://pear.php.net/package/Crypt_GPG
|
|
42 * @link http://pear.php.net/manual/en/package.encryption.crypt-gpg.php
|
|
43 * @link http://www.gnupg.org/
|
|
44 */
|
|
45
|
|
46 /**
|
|
47 * GPG key class
|
|
48 */
|
|
49 require_once 'Crypt/GPG/Key.php';
|
|
50
|
|
51 /**
|
|
52 * GPG sub-key class
|
|
53 */
|
|
54 require_once 'Crypt/GPG/SubKey.php';
|
|
55
|
|
56 /**
|
|
57 * GPG user id class
|
|
58 */
|
|
59 require_once 'Crypt/GPG/UserId.php';
|
|
60
|
|
61 /**
|
|
62 * GPG process and I/O engine class
|
|
63 */
|
|
64 require_once 'Crypt/GPG/Engine.php';
|
|
65
|
|
66 // {{{ class Crypt_GPGAbstract
|
|
67
|
|
68 /**
|
|
69 * Base class for implementing a user of {@link Crypt_GPG_Engine}
|
|
70 *
|
|
71 * @category Encryption
|
|
72 * @package Crypt_GPG
|
|
73 * @author Nathan Fredrickson <nathan@silverorange.com>
|
|
74 * @author Michael Gauthier <mike@silverorange.com>
|
|
75 * @copyright 2005-2013 silverorange
|
|
76 * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
|
77 * @link http://pear.php.net/package/Crypt_GPG
|
|
78 * @link http://www.gnupg.org/
|
|
79 */
|
|
80 abstract class Crypt_GPGAbstract
|
|
81 {
|
|
82 // {{{ class error constants
|
|
83
|
|
84 /**
|
|
85 * Error code returned when there is no error.
|
|
86 */
|
|
87 const ERROR_NONE = 0;
|
|
88
|
|
89 /**
|
|
90 * Error code returned when an unknown or unhandled error occurs.
|
|
91 */
|
|
92 const ERROR_UNKNOWN = 1;
|
|
93
|
|
94 /**
|
|
95 * Error code returned when a bad passphrase is used.
|
|
96 */
|
|
97 const ERROR_BAD_PASSPHRASE = 2;
|
|
98
|
|
99 /**
|
|
100 * Error code returned when a required passphrase is missing.
|
|
101 */
|
|
102 const ERROR_MISSING_PASSPHRASE = 3;
|
|
103
|
|
104 /**
|
|
105 * Error code returned when a key that is already in the keyring is
|
|
106 * imported.
|
|
107 */
|
|
108 const ERROR_DUPLICATE_KEY = 4;
|
|
109
|
|
110 /**
|
|
111 * Error code returned the required data is missing for an operation.
|
|
112 *
|
|
113 * This could be missing key data, missing encrypted data or missing
|
|
114 * signature data.
|
|
115 */
|
|
116 const ERROR_NO_DATA = 5;
|
|
117
|
|
118 /**
|
|
119 * Error code returned when an unsigned key is used.
|
|
120 */
|
|
121 const ERROR_UNSIGNED_KEY = 6;
|
|
122
|
|
123 /**
|
|
124 * Error code returned when a key that is not self-signed is used.
|
|
125 */
|
|
126 const ERROR_NOT_SELF_SIGNED = 7;
|
|
127
|
|
128 /**
|
|
129 * Error code returned when a public or private key that is not in the
|
|
130 * keyring is used.
|
|
131 */
|
|
132 const ERROR_KEY_NOT_FOUND = 8;
|
|
133
|
|
134 /**
|
|
135 * Error code returned when an attempt to delete public key having a
|
|
136 * private key is made.
|
|
137 */
|
|
138 const ERROR_DELETE_PRIVATE_KEY = 9;
|
|
139
|
|
140 /**
|
|
141 * Error code returned when one or more bad signatures are detected.
|
|
142 */
|
|
143 const ERROR_BAD_SIGNATURE = 10;
|
|
144
|
|
145 /**
|
|
146 * Error code returned when there is a problem reading GnuPG data files.
|
|
147 */
|
|
148 const ERROR_FILE_PERMISSIONS = 11;
|
|
149
|
|
150 /**
|
|
151 * Error code returned when a key could not be created.
|
|
152 */
|
|
153 const ERROR_KEY_NOT_CREATED = 12;
|
|
154
|
|
155 /**
|
|
156 * Error code returned when bad key parameters are used during key
|
|
157 * generation.
|
|
158 */
|
|
159 const ERROR_BAD_KEY_PARAMS = 13;
|
|
160
|
|
161 // }}}
|
|
162 // {{{ other class constants
|
|
163
|
|
164 /**
|
|
165 * URI at which package bugs may be reported.
|
|
166 */
|
|
167 const BUG_URI = 'http://pear.php.net/bugs/report.php?package=Crypt_GPG';
|
|
168
|
|
169 // }}}
|
|
170 // {{{ protected class properties
|
|
171
|
|
172 /**
|
|
173 * Engine used to control the GPG subprocess
|
|
174 *
|
|
175 * @var Crypt_GPG_Engine
|
|
176 *
|
|
177 * @see Crypt_GPGAbstract::setEngine()
|
|
178 */
|
|
179 protected $engine = null;
|
|
180
|
|
181 // }}}
|
|
182 // {{{ __construct()
|
|
183
|
|
184 /**
|
|
185 * Creates a new GPG object
|
|
186 *
|
|
187 * Available options are:
|
|
188 *
|
|
189 * - <kbd>string homedir</kbd> - the directory where the GPG
|
|
190 * keyring files are stored. If not
|
|
191 * specified, Crypt_GPG uses the
|
|
192 * default of <kbd>~/.gnupg</kbd>.
|
|
193 * - <kbd>string publicKeyring</kbd> - the file path of the public
|
|
194 * keyring. Use this if the public
|
|
195 * keyring is not in the homedir, or
|
|
196 * if the keyring is in a directory
|
|
197 * not writable by the process
|
|
198 * invoking GPG (like Apache). Then
|
|
199 * you can specify the path to the
|
|
200 * keyring with this option
|
|
201 * (/foo/bar/pubring.gpg), and specify
|
|
202 * a writable directory (like /tmp)
|
|
203 * using the <i>homedir</i> option.
|
|
204 * - <kbd>string privateKeyring</kbd> - the file path of the private
|
|
205 * keyring. Use this if the private
|
|
206 * keyring is not in the homedir, or
|
|
207 * if the keyring is in a directory
|
|
208 * not writable by the process
|
|
209 * invoking GPG (like Apache). Then
|
|
210 * you can specify the path to the
|
|
211 * keyring with this option
|
|
212 * (/foo/bar/secring.gpg), and specify
|
|
213 * a writable directory (like /tmp)
|
|
214 * using the <i>homedir</i> option.
|
|
215 * - <kbd>string trustDb</kbd> - the file path of the web-of-trust
|
|
216 * database. Use this if the trust
|
|
217 * database is not in the homedir, or
|
|
218 * if the database is in a directory
|
|
219 * not writable by the process
|
|
220 * invoking GPG (like Apache). Then
|
|
221 * you can specify the path to the
|
|
222 * trust database with this option
|
|
223 * (/foo/bar/trustdb.gpg), and specify
|
|
224 * a writable directory (like /tmp)
|
|
225 * using the <i>homedir</i> option.
|
|
226 * - <kbd>string binary</kbd> - the location of the GPG binary. If
|
|
227 * not specified, the driver attempts
|
|
228 * to auto-detect the GPG binary
|
|
229 * location using a list of known
|
|
230 * default locations for the current
|
|
231 * operating system. The option
|
|
232 * <kbd>gpgBinary</kbd> is a
|
|
233 * deprecated alias for this option.
|
|
234 * - <kbd>string agent</kbd> - the location of the GnuPG agent
|
|
235 * binary. The gpg-agent is only
|
|
236 * used for GnuPG 2.x. If not
|
|
237 * specified, the engine attempts
|
|
238 * to auto-detect the gpg-agent
|
|
239 * binary location using a list of
|
|
240 * know default locations for the
|
|
241 * current operating system.
|
|
242 * - <kbd>string|false gpgconf</kbd> - the location of the GnuPG conf
|
|
243 * binary. The gpgconf is only
|
|
244 * used for GnuPG >= 2.1. If not
|
|
245 * specified, the engine attempts
|
|
246 * to auto-detect the location using
|
|
247 * a list of know default locations.
|
|
248 * When set to FALSE `gpgconf --kill`
|
|
249 * will not be executed via destructor.
|
|
250 * - <kbd>string digest-algo</kbd> - Sets the message digest algorithm.
|
|
251 * - <kbd>string cipher-algo</kbd> - Sets the symmetric cipher.
|
|
252 * - <kbd>boolean strict</kbd> - In strict mode clock problems on
|
|
253 * subkeys and signatures are not ignored
|
|
254 * (--ignore-time-conflict
|
|
255 * and --ignore-valid-from options)
|
|
256 * - <kbd>mixed debug</kbd> - whether or not to use debug mode.
|
|
257 * When debug mode is on, all
|
|
258 * communication to and from the GPG
|
|
259 * subprocess is logged. This can be
|
|
260 * useful to diagnose errors when
|
|
261 * using Crypt_GPG.
|
|
262 *
|
|
263 * @param array $options optional. An array of options used to create the
|
|
264 * GPG object. All options are optional and are
|
|
265 * represented as key-value pairs.
|
|
266 *
|
|
267 * @throws Crypt_GPG_FileException if the <kbd>homedir</kbd> does not exist
|
|
268 * and cannot be created. This can happen if <kbd>homedir</kbd> is
|
|
269 * not specified, Crypt_GPG is run as the web user, and the web
|
|
270 * user has no home directory. This exception is also thrown if any
|
|
271 * of the options <kbd>publicKeyring</kbd>,
|
|
272 * <kbd>privateKeyring</kbd> or <kbd>trustDb</kbd> options are
|
|
273 * specified but the files do not exist or are are not readable.
|
|
274 * This can happen if the user running the Crypt_GPG process (for
|
|
275 * example, the Apache user) does not have permission to read the
|
|
276 * files.
|
|
277 *
|
|
278 * @throws PEAR_Exception if the provided <kbd>binary</kbd> is invalid, or
|
|
279 * if no <kbd>binary</kbd> is provided and no suitable binary could
|
|
280 * be found.
|
|
281 *
|
|
282 * @throws PEAR_Exception if the provided <kbd>agent</kbd> is invalid, or
|
|
283 * if no <kbd>agent</kbd> is provided and no suitable gpg-agent
|
|
284 * cound be found.
|
|
285 */
|
|
286 public function __construct(array $options = array())
|
|
287 {
|
|
288 $this->setEngine(new Crypt_GPG_Engine($options));
|
|
289 }
|
|
290
|
|
291 // }}}
|
|
292 // {{{ setEngine()
|
|
293
|
|
294 /**
|
|
295 * Sets the I/O engine to use for GnuPG operations
|
|
296 *
|
|
297 * Normally this method does not need to be used. It provides a means for
|
|
298 * dependency injection.
|
|
299 *
|
|
300 * @param Crypt_GPG_Engine $engine the engine to use.
|
|
301 *
|
|
302 * @return Crypt_GPGAbstract the current object, for fluent interface.
|
|
303 */
|
|
304 public function setEngine(Crypt_GPG_Engine $engine)
|
|
305 {
|
|
306 $this->engine = $engine;
|
|
307 return $this;
|
|
308 }
|
|
309
|
|
310 // }}}
|
|
311 // {{{ getVersion()
|
|
312
|
|
313 /**
|
|
314 * Returns version of the engine (GnuPG) used for operation.
|
|
315 *
|
|
316 * @return string GnuPG version.
|
|
317 *
|
|
318 * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs.
|
|
319 * Use the <kbd>debug</kbd> option and file a bug report if these
|
|
320 * exceptions occur.
|
|
321 */
|
|
322 public function getVersion()
|
|
323 {
|
|
324 return $this->engine->getVersion();
|
|
325 }
|
|
326
|
|
327 // }}}
|
|
328 // {{{ _getKeys()
|
|
329
|
|
330 /**
|
|
331 * Gets the available keys in the keyring
|
|
332 *
|
|
333 * Calls GPG with the <kbd>--list-keys</kbd> command and grabs keys. See
|
|
334 * the first section of <b>doc/DETAILS</b> in the
|
|
335 * {@link http://www.gnupg.org/download/ GPG package} for a detailed
|
|
336 * description of how the GPG command output is parsed.
|
|
337 *
|
|
338 * @param string $keyId optional. Only keys with that match the specified
|
|
339 * pattern are returned. The pattern may be part of
|
|
340 * a user id, a key id or a key fingerprint. If not
|
|
341 * specified, all keys are returned.
|
|
342 *
|
|
343 * @return array an array of {@link Crypt_GPG_Key} objects. If no keys
|
|
344 * match the specified <kbd>$keyId</kbd> an empty array is
|
|
345 * returned.
|
|
346 *
|
|
347 * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs.
|
|
348 * Use the <kbd>debug</kbd> option and file a bug report if these
|
|
349 * exceptions occur.
|
|
350 *
|
|
351 * @see Crypt_GPG_Key
|
|
352 */
|
|
353 protected function _getKeys($keyId = '')
|
|
354 {
|
|
355 // get private key fingerprints
|
|
356 if ($keyId == '') {
|
|
357 $operation = '--list-secret-keys';
|
|
358 } else {
|
|
359 $operation = '--utf8-strings --list-secret-keys ' . escapeshellarg($keyId);
|
|
360 }
|
|
361
|
|
362 // According to The file 'doc/DETAILS' in the GnuPG distribution, using
|
|
363 // double '--with-fingerprint' also prints the fingerprint for subkeys.
|
|
364 $arguments = array(
|
|
365 '--with-colons',
|
|
366 '--with-fingerprint',
|
|
367 '--with-fingerprint',
|
|
368 '--fixed-list-mode'
|
|
369 );
|
|
370
|
|
371 $output = '';
|
|
372
|
|
373 $this->engine->reset();
|
|
374 $this->engine->setOutput($output);
|
|
375 $this->engine->setOperation($operation, $arguments);
|
|
376 $this->engine->run();
|
|
377
|
|
378 $privateKeyFingerprints = array();
|
|
379
|
|
380 foreach (explode(PHP_EOL, $output) as $line) {
|
|
381 $lineExp = explode(':', $line);
|
|
382 if ($lineExp[0] == 'fpr') {
|
|
383 $privateKeyFingerprints[] = $lineExp[9];
|
|
384 }
|
|
385 }
|
|
386
|
|
387 // get public keys
|
|
388 if ($keyId == '') {
|
|
389 $operation = '--list-public-keys';
|
|
390 } else {
|
|
391 $operation = '--utf8-strings --list-public-keys ' . escapeshellarg($keyId);
|
|
392 }
|
|
393
|
|
394 $output = '';
|
|
395
|
|
396 $this->engine->reset();
|
|
397 $this->engine->setOutput($output);
|
|
398 $this->engine->setOperation($operation, $arguments);
|
|
399 $this->engine->run();
|
|
400
|
|
401 $keys = array();
|
|
402 $key = null; // current key
|
|
403 $subKey = null; // current sub-key
|
|
404
|
|
405 foreach (explode(PHP_EOL, $output) as $line) {
|
|
406 $lineExp = explode(':', $line);
|
|
407
|
|
408 if ($lineExp[0] == 'pub') {
|
|
409
|
|
410 // new primary key means last key should be added to the array
|
|
411 if ($key !== null) {
|
|
412 $keys[] = $key;
|
|
413 }
|
|
414
|
|
415 $key = new Crypt_GPG_Key();
|
|
416
|
|
417 $subKey = Crypt_GPG_SubKey::parse($line);
|
|
418 $key->addSubKey($subKey);
|
|
419
|
|
420 } elseif ($lineExp[0] == 'sub') {
|
|
421
|
|
422 $subKey = Crypt_GPG_SubKey::parse($line);
|
|
423 $key->addSubKey($subKey);
|
|
424
|
|
425 } elseif ($lineExp[0] == 'fpr') {
|
|
426
|
|
427 $fingerprint = $lineExp[9];
|
|
428
|
|
429 // set current sub-key fingerprint
|
|
430 $subKey->setFingerprint($fingerprint);
|
|
431
|
|
432 // if private key exists, set has private to true
|
|
433 if (in_array($fingerprint, $privateKeyFingerprints)) {
|
|
434 $subKey->setHasPrivate(true);
|
|
435 }
|
|
436
|
|
437 } elseif ($lineExp[0] == 'uid') {
|
|
438
|
|
439 $string = stripcslashes($lineExp[9]); // as per documentation
|
|
440 $userId = new Crypt_GPG_UserId($string);
|
|
441
|
|
442 if ($lineExp[1] == 'r') {
|
|
443 $userId->setRevoked(true);
|
|
444 }
|
|
445
|
|
446 $key->addUserId($userId);
|
|
447
|
|
448 }
|
|
449 }
|
|
450
|
|
451 // add last key
|
|
452 if ($key !== null) {
|
|
453 $keys[] = $key;
|
|
454 }
|
|
455
|
|
456 return $keys;
|
|
457 }
|
|
458
|
|
459 // }}}
|
|
460 }
|
|
461
|
|
462 // }}}
|
|
463
|
|
464 ?>
|