]> git.donarmstrong.com Git - roundcube.git/blob - plugins/enigma/lib/Crypt/GPG.php
Imported Upstream version 0.6+dfsg
[roundcube.git] / plugins / enigma / lib / Crypt / GPG.php
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 the main GPG class. The class in this file lets you
15  * encrypt, decrypt, sign and verify data; import and delete keys; and perform
16  * other useful GPG tasks.
17  *
18  * Example usage:
19  * <code>
20  * <?php
21  * // encrypt some data
22  * $gpg = new Crypt_GPG();
23  * $gpg->addEncryptKey($mySecretKeyId);
24  * $encryptedData = $gpg->encrypt($data);
25  * ?>
26  * </code>
27  *
28  * PHP version 5
29  *
30  * LICENSE:
31  *
32  * This library is free software; you can redistribute it and/or modify
33  * it under the terms of the GNU Lesser General Public License as
34  * published by the Free Software Foundation; either version 2.1 of the
35  * License, or (at your option) any later version.
36  *
37  * This library is distributed in the hope that it will be useful,
38  * but WITHOUT ANY WARRANTY; without even the implied warranty of
39  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
40  * Lesser General Public License for more details.
41  *
42  * You should have received a copy of the GNU Lesser General Public
43  * License along with this library; if not, write to the Free Software
44  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
45  *
46  * @category  Encryption
47  * @package   Crypt_GPG
48  * @author    Nathan Fredrickson <nathan@silverorange.com>
49  * @author    Michael Gauthier <mike@silverorange.com>
50  * @copyright 2005-2010 silverorange
51  * @license   http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
52  * @version   CVS: $Id: GPG.php 302814 2010-08-26 15:43:07Z gauthierm $
53  * @link      http://pear.php.net/package/Crypt_GPG
54  * @link      http://pear.php.net/manual/en/package.encryption.crypt-gpg.php
55  * @link      http://www.gnupg.org/
56  */
57
58 /**
59  * Signature handler class
60  */
61 require_once 'Crypt/GPG/VerifyStatusHandler.php';
62
63 /**
64  * Decryption handler class
65  */
66 require_once 'Crypt/GPG/DecryptStatusHandler.php';
67
68 /**
69  * GPG key class
70  */
71 require_once 'Crypt/GPG/Key.php';
72
73 /**
74  * GPG sub-key class
75  */
76 require_once 'Crypt/GPG/SubKey.php';
77
78 /**
79  * GPG user id class
80  */
81 require_once 'Crypt/GPG/UserId.php';
82
83 /**
84  * GPG process and I/O engine class
85  */
86 require_once 'Crypt/GPG/Engine.php';
87
88 /**
89  * GPG exception classes
90  */
91 require_once 'Crypt/GPG/Exceptions.php';
92
93 // {{{ class Crypt_GPG
94
95 /**
96  * A class to use GPG from PHP
97  *
98  * This class provides an object oriented interface to GNU Privacy Guard (GPG).
99  *
100  * Though GPG can support symmetric-key cryptography, this class is intended
101  * only to facilitate public-key cryptography.
102  *
103  * @category  Encryption
104  * @package   Crypt_GPG
105  * @author    Nathan Fredrickson <nathan@silverorange.com>
106  * @author    Michael Gauthier <mike@silverorange.com>
107  * @copyright 2005-2010 silverorange
108  * @license   http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
109  * @link      http://pear.php.net/package/Crypt_GPG
110  * @link      http://www.gnupg.org/
111  */
112 class Crypt_GPG
113 {
114     // {{{ class error constants
115
116     /**
117      * Error code returned when there is no error.
118      */
119     const ERROR_NONE = 0;
120
121     /**
122      * Error code returned when an unknown or unhandled error occurs.
123      */
124     const ERROR_UNKNOWN = 1;
125
126     /**
127      * Error code returned when a bad passphrase is used.
128      */
129     const ERROR_BAD_PASSPHRASE = 2;
130
131     /**
132      * Error code returned when a required passphrase is missing.
133      */
134     const ERROR_MISSING_PASSPHRASE = 3;
135
136     /**
137      * Error code returned when a key that is already in the keyring is
138      * imported.
139      */
140     const ERROR_DUPLICATE_KEY = 4;
141
142     /**
143      * Error code returned the required data is missing for an operation.
144      *
145      * This could be missing key data, missing encrypted data or missing
146      * signature data.
147      */
148     const ERROR_NO_DATA = 5;
149
150     /**
151      * Error code returned when an unsigned key is used.
152      */
153     const ERROR_UNSIGNED_KEY = 6;
154
155     /**
156      * Error code returned when a key that is not self-signed is used.
157      */
158     const ERROR_NOT_SELF_SIGNED = 7;
159
160     /**
161      * Error code returned when a public or private key that is not in the
162      * keyring is used.
163      */
164     const ERROR_KEY_NOT_FOUND = 8;
165
166     /**
167      * Error code returned when an attempt to delete public key having a
168      * private key is made.
169      */
170     const ERROR_DELETE_PRIVATE_KEY = 9;
171
172     /**
173      * Error code returned when one or more bad signatures are detected.
174      */
175     const ERROR_BAD_SIGNATURE = 10;
176
177     /**
178      * Error code returned when there is a problem reading GnuPG data files.
179      */
180     const ERROR_FILE_PERMISSIONS = 11;
181
182     // }}}
183     // {{{ class constants for data signing modes
184
185     /**
186      * Signing mode for normal signing of data. The signed message will not
187      * be readable without special software.
188      *
189      * This is the default signing mode.
190      *
191      * @see Crypt_GPG::sign()
192      * @see Crypt_GPG::signFile()
193      */
194     const SIGN_MODE_NORMAL = 1;
195
196     /**
197      * Signing mode for clearsigning data. Clearsigned signatures are ASCII
198      * armored data and are readable without special software. If the signed
199      * message is unencrypted, the message will still be readable. The message
200      * text will be in the original encoding.
201      *
202      * @see Crypt_GPG::sign()
203      * @see Crypt_GPG::signFile()
204      */
205     const SIGN_MODE_CLEAR = 2;
206
207     /**
208      * Signing mode for creating a detached signature. When using detached
209      * signatures, only the signature data is returned. The original message
210      * text may be distributed separately from the signature data. This is
211      * useful for miltipart/signed email messages as per
212      * {@link http://www.ietf.org/rfc/rfc3156.txt RFC 3156}.
213      *
214      * @see Crypt_GPG::sign()
215      * @see Crypt_GPG::signFile()
216      */
217     const SIGN_MODE_DETACHED = 3;
218
219     // }}}
220     // {{{ class constants for fingerprint formats
221
222     /**
223      * No formatting is performed.
224      *
225      * Example: C3BC615AD9C766E5A85C1F2716D27458B1BBA1C4
226      *
227      * @see Crypt_GPG::getFingerprint()
228      */
229     const FORMAT_NONE = 1;
230
231     /**
232      * Fingerprint is formatted in the format used by the GnuPG gpg command's
233      * default output.
234      *
235      * Example: C3BC 615A D9C7 66E5 A85C  1F27 16D2 7458 B1BB A1C4
236      *
237      * @see Crypt_GPG::getFingerprint()
238      */
239     const FORMAT_CANONICAL = 2;
240
241     /**
242      * Fingerprint is formatted in the format used when displaying X.509
243      * certificates
244      *
245      * Example: C3:BC:61:5A:D9:C7:66:E5:A8:5C:1F:27:16:D2:74:58:B1:BB:A1:C4
246      *
247      * @see Crypt_GPG::getFingerprint()
248      */
249     const FORMAT_X509 = 3;
250
251     // }}}
252     // {{{ other class constants
253
254     /**
255      * URI at which package bugs may be reported.
256      */
257     const BUG_URI = 'http://pear.php.net/bugs/report.php?package=Crypt_GPG';
258
259     // }}}
260     // {{{ protected class properties
261
262     /**
263      * Engine used to control the GPG subprocess
264      *
265      * @var Crypt_GPG_Engine
266      *
267      * @see Crypt_GPG::setEngine()
268      */
269     protected $engine = null;
270
271     /**
272      * Keys used to encrypt
273      *
274      * The array is of the form:
275      * <code>
276      * array(
277      *   $key_id => array(
278      *     'fingerprint' => $fingerprint,
279      *     'passphrase'  => null
280      *   )
281      * );
282      * </code>
283      *
284      * @var array
285      * @see Crypt_GPG::addEncryptKey()
286      * @see Crypt_GPG::clearEncryptKeys()
287      */
288     protected $encryptKeys = array();
289
290     /**
291      * Keys used to decrypt
292      *
293      * The array is of the form:
294      * <code>
295      * array(
296      *   $key_id => array(
297      *     'fingerprint' => $fingerprint,
298      *     'passphrase'  => $passphrase
299      *   )
300      * );
301      * </code>
302      *
303      * @var array
304      * @see Crypt_GPG::addSignKey()
305      * @see Crypt_GPG::clearSignKeys()
306      */
307     protected $signKeys = array();
308
309     /**
310      * Keys used to sign
311      *
312      * The array is of the form:
313      * <code>
314      * array(
315      *   $key_id => array(
316      *     'fingerprint' => $fingerprint,
317      *     'passphrase'  => $passphrase
318      *   )
319      * );
320      * </code>
321      *
322      * @var array
323      * @see Crypt_GPG::addDecryptKey()
324      * @see Crypt_GPG::clearDecryptKeys()
325      */
326     protected $decryptKeys = array();
327
328     // }}}
329     // {{{ __construct()
330
331     /**
332      * Creates a new GPG object
333      *
334      * Available options are:
335      *
336      * - <kbd>string  homedir</kbd>        - the directory where the GPG
337      *                                       keyring files are stored. If not
338      *                                       specified, Crypt_GPG uses the
339      *                                       default of <kbd>~/.gnupg</kbd>.
340      * - <kbd>string  publicKeyring</kbd>  - the file path of the public
341      *                                       keyring. Use this if the public
342      *                                       keyring is not in the homedir, or
343      *                                       if the keyring is in a directory
344      *                                       not writable by the process
345      *                                       invoking GPG (like Apache). Then
346      *                                       you can specify the path to the
347      *                                       keyring with this option
348      *                                       (/foo/bar/pubring.gpg), and specify
349      *                                       a writable directory (like /tmp)
350      *                                       using the <i>homedir</i> option.
351      * - <kbd>string  privateKeyring</kbd> - the file path of the private
352      *                                       keyring. Use this if the private
353      *                                       keyring is not in the homedir, or
354      *                                       if the keyring is in a directory
355      *                                       not writable by the process
356      *                                       invoking GPG (like Apache). Then
357      *                                       you can specify the path to the
358      *                                       keyring with this option
359      *                                       (/foo/bar/secring.gpg), and specify
360      *                                       a writable directory (like /tmp)
361      *                                       using the <i>homedir</i> option.
362      * - <kbd>string  trustDb</kbd>        - the file path of the web-of-trust
363      *                                       database. Use this if the trust
364      *                                       database is not in the homedir, or
365      *                                       if the database is in a directory
366      *                                       not writable by the process
367      *                                       invoking GPG (like Apache). Then
368      *                                       you can specify the path to the
369      *                                       trust database with this option
370      *                                       (/foo/bar/trustdb.gpg), and specify
371      *                                       a writable directory (like /tmp)
372      *                                       using the <i>homedir</i> option.
373      * - <kbd>string  binary</kbd>         - the location of the GPG binary. If
374      *                                       not specified, the driver attempts
375      *                                       to auto-detect the GPG binary
376      *                                       location using a list of known
377      *                                       default locations for the current
378      *                                       operating system. The option
379      *                                       <kbd>gpgBinary</kbd> is a
380      *                                       deprecated alias for this option.
381      * - <kbd>boolean debug</kbd>          - whether or not to use debug mode.
382      *                                       When debug mode is on, all
383      *                                       communication to and from the GPG
384      *                                       subprocess is logged. This can be
385      *
386      * @param array $options optional. An array of options used to create the
387      *                       GPG object. All options are optional and are
388      *                       represented as key-value pairs.
389      *
390      * @throws Crypt_GPG_FileException if the <kbd>homedir</kbd> does not exist
391      *         and cannot be created. This can happen if <kbd>homedir</kbd> is
392      *         not specified, Crypt_GPG is run as the web user, and the web
393      *         user has no home directory. This exception is also thrown if any
394      *         of the options <kbd>publicKeyring</kbd>,
395      *         <kbd>privateKeyring</kbd> or <kbd>trustDb</kbd> options are
396      *         specified but the files do not exist or are are not readable.
397      *         This can happen if the user running the Crypt_GPG process (for
398      *         example, the Apache user) does not have permission to read the
399      *         files.
400      *
401      * @throws PEAR_Exception if the provided <kbd>binary</kbd> is invalid, or
402      *         if no <kbd>binary</kbd> is provided and no suitable binary could
403      *         be found.
404      */
405     public function __construct(array $options = array())
406     {
407         $this->setEngine(new Crypt_GPG_Engine($options));
408     }
409
410     // }}}
411     // {{{ importKey()
412
413     /**
414      * Imports a public or private key into the keyring
415      *
416      * Keys may be removed from the keyring using
417      * {@link Crypt_GPG::deletePublicKey()} or
418      * {@link Crypt_GPG::deletePrivateKey()}.
419      *
420      * @param string $data the key data to be imported.
421      *
422      * @return array an associative array containing the following elements:
423      *               - <kbd>fingerprint</kbd>       - the fingerprint of the
424      *                                                imported key,
425      *               - <kbd>public_imported</kbd>   - the number of public
426      *                                                keys imported,
427      *               - <kbd>public_unchanged</kbd>  - the number of unchanged
428      *                                                public keys,
429      *               - <kbd>private_imported</kbd>  - the number of private
430      *                                                keys imported,
431      *               - <kbd>private_unchanged</kbd> - the number of unchanged
432      *                                                private keys.
433      *
434      * @throws Crypt_GPG_NoDataException if the key data is missing or if the
435      *         data is is not valid key data.
436      *
437      * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs.
438      *         Use the <kbd>debug</kbd> option and file a bug report if these
439      *         exceptions occur.
440      */
441     public function importKey($data)
442     {
443         return $this->_importKey($data, false);
444     }
445
446     // }}}
447     // {{{ importKeyFile()
448
449     /**
450      * Imports a public or private key file into the keyring
451      *
452      * Keys may be removed from the keyring using
453      * {@link Crypt_GPG::deletePublicKey()} or
454      * {@link Crypt_GPG::deletePrivateKey()}.
455      *
456      * @param string $filename the key file to be imported.
457      *
458      * @return array an associative array containing the following elements:
459      *               - <kbd>fingerprint</kbd>       - the fingerprint of the
460      *                                                imported key,
461      *               - <kbd>public_imported</kbd>   - the number of public
462      *                                                keys imported,
463      *               - <kbd>public_unchanged</kbd>  - the number of unchanged
464      *                                                public keys,
465      *               - <kbd>private_imported</kbd>  - the number of private
466      *                                                keys imported,
467      *               - <kbd>private_unchanged</kbd> - the number of unchanged
468      *                                                private keys.
469      *                                                  private keys.
470      *
471      * @throws Crypt_GPG_NoDataException if the key data is missing or if the
472      *         data is is not valid key data.
473      *
474      * @throws Crypt_GPG_FileException if the key file is not readable.
475      *
476      * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs.
477      *         Use the <kbd>debug</kbd> option and file a bug report if these
478      *         exceptions occur.
479      */
480     public function importKeyFile($filename)
481     {
482         return $this->_importKey($filename, true);
483     }
484
485     // }}}
486     // {{{ exportPublicKey()
487
488     /**
489      * Exports a public key from the keyring
490      *
491      * The exported key remains on the keyring. To delete the public key, use
492      * {@link Crypt_GPG::deletePublicKey()}.
493      *
494      * If more than one key fingerprint is available for the specified
495      * <kbd>$keyId</kbd> (for example, if you use a non-unique uid) only the
496      * first public key is exported.
497      *
498      * @param string  $keyId either the full uid of the public key, the email
499      *                       part of the uid of the public key or the key id of
500      *                       the public key. For example,
501      *                       "Test User (example) <test@example.com>",
502      *                       "test@example.com" or a hexadecimal string.
503      * @param boolean $armor optional. If true, ASCII armored data is returned;
504      *                       otherwise, binary data is returned. Defaults to
505      *                       true.
506      *
507      * @return string the public key data.
508      *
509      * @throws Crypt_GPG_KeyNotFoundException if a public key with the given
510      *         <kbd>$keyId</kbd> is not found.
511      *
512      * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs.
513      *         Use the <kbd>debug</kbd> option and file a bug report if these
514      *         exceptions occur.
515      */
516     public function exportPublicKey($keyId, $armor = true)
517     {
518         $fingerprint = $this->getFingerprint($keyId);
519
520         if ($fingerprint === null) {
521             throw new Crypt_GPG_KeyNotFoundException(
522                 'Public key not found: ' . $keyId,
523                 Crypt_GPG::ERROR_KEY_NOT_FOUND, $keyId);
524         }
525
526         $keyData   = '';
527         $operation = '--export ' . escapeshellarg($fingerprint);
528         $arguments = ($armor) ? array('--armor') : array();
529
530         $this->engine->reset();
531         $this->engine->setOutput($keyData);
532         $this->engine->setOperation($operation, $arguments);
533         $this->engine->run();
534
535         $code = $this->engine->getErrorCode();
536
537         if ($code !== Crypt_GPG::ERROR_NONE) {
538             throw new Crypt_GPG_Exception(
539                 'Unknown error exporting public key. Please use the ' .
540                 '\'debug\' option when creating the Crypt_GPG object, and ' .
541                 'file a bug report at ' . self::BUG_URI, $code);
542         }
543
544         return $keyData;
545     }
546
547     // }}}
548     // {{{ deletePublicKey()
549
550     /**
551      * Deletes a public key from the keyring
552      *
553      * If more than one key fingerprint is available for the specified
554      * <kbd>$keyId</kbd> (for example, if you use a non-unique uid) only the
555      * first public key is deleted.
556      *
557      * The private key must be deleted first or an exception will be thrown.
558      * See {@link Crypt_GPG::deletePrivateKey()}.
559      *
560      * @param string $keyId either the full uid of the public key, the email
561      *                      part of the uid of the public key or the key id of
562      *                      the public key. For example,
563      *                      "Test User (example) <test@example.com>",
564      *                      "test@example.com" or a hexadecimal string.
565      *
566      * @return void
567      *
568      * @throws Crypt_GPG_KeyNotFoundException if a public key with the given
569      *         <kbd>$keyId</kbd> is not found.
570      *
571      * @throws Crypt_GPG_DeletePrivateKeyException if the specified public key
572      *         has an associated private key on the keyring. The private key
573      *         must be deleted first.
574      *
575      * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs.
576      *         Use the <kbd>debug</kbd> option and file a bug report if these
577      *         exceptions occur.
578      */
579     public function deletePublicKey($keyId)
580     {
581         $fingerprint = $this->getFingerprint($keyId);
582
583         if ($fingerprint === null) {
584             throw new Crypt_GPG_KeyNotFoundException(
585                 'Public key not found: ' . $keyId,
586                 Crypt_GPG::ERROR_KEY_NOT_FOUND, $keyId);
587         }
588
589         $operation = '--delete-key ' . escapeshellarg($fingerprint);
590         $arguments = array(
591             '--batch',
592             '--yes'
593         );
594
595         $this->engine->reset();
596         $this->engine->setOperation($operation, $arguments);
597         $this->engine->run();
598
599         $code = $this->engine->getErrorCode();
600
601         switch ($code) {
602         case Crypt_GPG::ERROR_NONE:
603             break;
604         case Crypt_GPG::ERROR_DELETE_PRIVATE_KEY:
605             throw new Crypt_GPG_DeletePrivateKeyException(
606                 'Private key must be deleted before public key can be ' .
607                 'deleted.', $code, $keyId);
608         default:
609             throw new Crypt_GPG_Exception(
610                 'Unknown error deleting public key. Please use the ' .
611                 '\'debug\' option when creating the Crypt_GPG object, and ' .
612                 'file a bug report at ' . self::BUG_URI, $code);
613         }
614     }
615
616     // }}}
617     // {{{ deletePrivateKey()
618
619     /**
620      * Deletes a private key from the keyring
621      *
622      * If more than one key fingerprint is available for the specified
623      * <kbd>$keyId</kbd> (for example, if you use a non-unique uid) only the
624      * first private key is deleted.
625      *
626      * Calls GPG with the <kbd>--delete-secret-key</kbd> command.
627      *
628      * @param string $keyId either the full uid of the private key, the email
629      *                      part of the uid of the private key or the key id of
630      *                      the private key. For example,
631      *                      "Test User (example) <test@example.com>",
632      *                      "test@example.com" or a hexadecimal string.
633      *
634      * @return void
635      *
636      * @throws Crypt_GPG_KeyNotFoundException if a private key with the given
637      *         <kbd>$keyId</kbd> is not found.
638      *
639      * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs.
640      *         Use the <kbd>debug</kbd> option and file a bug report if these
641      *         exceptions occur.
642      */
643     public function deletePrivateKey($keyId)
644     {
645         $fingerprint = $this->getFingerprint($keyId);
646
647         if ($fingerprint === null) {
648             throw new Crypt_GPG_KeyNotFoundException(
649                 'Private key not found: ' . $keyId,
650                 Crypt_GPG::ERROR_KEY_NOT_FOUND, $keyId);
651         }
652
653         $operation = '--delete-secret-key ' . escapeshellarg($fingerprint);
654         $arguments = array(
655             '--batch',
656             '--yes'
657         );
658
659         $this->engine->reset();
660         $this->engine->setOperation($operation, $arguments);
661         $this->engine->run();
662
663         $code = $this->engine->getErrorCode();
664
665         switch ($code) {
666         case Crypt_GPG::ERROR_NONE:
667             break;
668         case Crypt_GPG::ERROR_KEY_NOT_FOUND:
669             throw new Crypt_GPG_KeyNotFoundException(
670                 'Private key not found: ' . $keyId,
671                 $code, $keyId);
672         default:
673             throw new Crypt_GPG_Exception(
674                 'Unknown error deleting private key. Please use the ' .
675                 '\'debug\' option when creating the Crypt_GPG object, and ' .
676                 'file a bug report at ' . self::BUG_URI, $code);
677         }
678     }
679
680     // }}}
681     // {{{ getKeys()
682
683     /**
684      * Gets the available keys in the keyring
685      *
686      * Calls GPG with the <kbd>--list-keys</kbd> command and grabs keys. See
687      * the first section of <b>doc/DETAILS</b> in the
688      * {@link http://www.gnupg.org/download/ GPG package} for a detailed
689      * description of how the GPG command output is parsed.
690      *
691      * @param string $keyId optional. Only keys with that match the specified
692      *                      pattern are returned. The pattern may be part of
693      *                      a user id, a key id or a key fingerprint. If not
694      *                      specified, all keys are returned.
695      *
696      * @return array an array of {@link Crypt_GPG_Key} objects. If no keys
697      *               match the specified <kbd>$keyId</kbd> an empty array is
698      *               returned.
699      *
700      * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs.
701      *         Use the <kbd>debug</kbd> option and file a bug report if these
702      *         exceptions occur.
703      *
704      * @see Crypt_GPG_Key
705      */
706     public function getKeys($keyId = '')
707     {
708         // get private key fingerprints
709         if ($keyId == '') {
710             $operation = '--list-secret-keys';
711         } else {
712             $operation = '--list-secret-keys ' . escapeshellarg($keyId);
713         }
714
715         // According to The file 'doc/DETAILS' in the GnuPG distribution, using
716         // double '--with-fingerprint' also prints the fingerprint for subkeys.
717         $arguments = array(
718             '--with-colons',
719             '--with-fingerprint',
720             '--with-fingerprint',
721             '--fixed-list-mode'
722         );
723
724         $output = '';
725
726         $this->engine->reset();
727         $this->engine->setOutput($output);
728         $this->engine->setOperation($operation, $arguments);
729         $this->engine->run();
730
731         $code = $this->engine->getErrorCode();
732
733         switch ($code) {
734         case Crypt_GPG::ERROR_NONE:
735         case Crypt_GPG::ERROR_KEY_NOT_FOUND:
736             // ignore not found key errors
737             break;
738         case Crypt_GPG::ERROR_FILE_PERMISSIONS:
739             $filename = $this->engine->getErrorFilename();
740             if ($filename) {
741                 throw new Crypt_GPG_FileException(sprintf(
742                     'Error reading GnuPG data file \'%s\'. Check to make ' .
743                     'sure it is readable by the current user.', $filename),
744                     $code, $filename);
745             }
746             throw new Crypt_GPG_FileException(
747                 'Error reading GnuPG data file. Check to make GnuPG data ' .
748                 'files are readable by the current user.', $code);
749         default:
750             throw new Crypt_GPG_Exception(
751                 'Unknown error getting keys. Please use the \'debug\' option ' .
752                 'when creating the Crypt_GPG object, and file a bug report ' .
753                 'at ' . self::BUG_URI, $code);
754         }
755
756         $privateKeyFingerprints = array();
757
758         $lines = explode(PHP_EOL, $output);
759         foreach ($lines as $line) {
760             $lineExp = explode(':', $line);
761             if ($lineExp[0] == 'fpr') {
762                 $privateKeyFingerprints[] = $lineExp[9];
763             }
764         }
765
766         // get public keys
767         if ($keyId == '') {
768             $operation = '--list-public-keys';
769         } else {
770             $operation = '--list-public-keys ' . escapeshellarg($keyId);
771         }
772
773         $output = '';
774
775         $this->engine->reset();
776         $this->engine->setOutput($output);
777         $this->engine->setOperation($operation, $arguments);
778         $this->engine->run();
779
780         $code = $this->engine->getErrorCode();
781
782         switch ($code) {
783         case Crypt_GPG::ERROR_NONE:
784         case Crypt_GPG::ERROR_KEY_NOT_FOUND:
785             // ignore not found key errors
786             break;
787         case Crypt_GPG::ERROR_FILE_PERMISSIONS:
788             $filename = $this->engine->getErrorFilename();
789             if ($filename) {
790                 throw new Crypt_GPG_FileException(sprintf(
791                     'Error reading GnuPG data file \'%s\'. Check to make ' .
792                     'sure it is readable by the current user.', $filename),
793                     $code, $filename);
794             }
795             throw new Crypt_GPG_FileException(
796                 'Error reading GnuPG data file. Check to make GnuPG data ' .
797                 'files are readable by the current user.', $code);
798         default:
799             throw new Crypt_GPG_Exception(
800                 'Unknown error getting keys. Please use the \'debug\' option ' .
801                 'when creating the Crypt_GPG object, and file a bug report ' .
802                 'at ' . self::BUG_URI, $code);
803         }
804
805         $keys = array();
806
807         $key    = null; // current key
808         $subKey = null; // current sub-key
809
810         $lines = explode(PHP_EOL, $output);
811         foreach ($lines as $line) {
812             $lineExp = explode(':', $line);
813
814             if ($lineExp[0] == 'pub') {
815
816                 // new primary key means last key should be added to the array
817                 if ($key !== null) {
818                     $keys[] = $key;
819                 }
820
821                 $key = new Crypt_GPG_Key();
822
823                 $subKey = Crypt_GPG_SubKey::parse($line);
824                 $key->addSubKey($subKey);
825
826             } elseif ($lineExp[0] == 'sub') {
827
828                 $subKey = Crypt_GPG_SubKey::parse($line);
829                 $key->addSubKey($subKey);
830
831             } elseif ($lineExp[0] == 'fpr') {
832
833                 $fingerprint = $lineExp[9];
834
835                 // set current sub-key fingerprint
836                 $subKey->setFingerprint($fingerprint);
837
838                 // if private key exists, set has private to true
839                 if (in_array($fingerprint, $privateKeyFingerprints)) {
840                     $subKey->setHasPrivate(true);
841                 }
842
843             } elseif ($lineExp[0] == 'uid') {
844
845                 $string = stripcslashes($lineExp[9]); // as per documentation
846                 $userId = new Crypt_GPG_UserId($string);
847
848                 if ($lineExp[1] == 'r') {
849                     $userId->setRevoked(true);
850                 }
851
852                 $key->addUserId($userId);
853
854             }
855         }
856
857         // add last key
858         if ($key !== null) {
859             $keys[] = $key;
860         }
861
862         return $keys;
863     }
864
865     // }}}
866     // {{{ getFingerprint()
867
868     /**
869      * Gets a key fingerprint from the keyring
870      *
871      * If more than one key fingerprint is available (for example, if you use
872      * a non-unique user id) only the first key fingerprint is returned.
873      *
874      * Calls the GPG <kbd>--list-keys</kbd> command with the
875      * <kbd>--with-fingerprint</kbd> option to retrieve a public key
876      * fingerprint.
877      *
878      * @param string  $keyId  either the full user id of the key, the email
879      *                        part of the user id of the key, or the key id of
880      *                        the key. For example,
881      *                        "Test User (example) <test@example.com>",
882      *                        "test@example.com" or a hexadecimal string.
883      * @param integer $format optional. How the fingerprint should be formatted.
884      *                        Use {@link Crypt_GPG::FORMAT_X509} for X.509
885      *                        certificate format,
886      *                        {@link Crypt_GPG::FORMAT_CANONICAL} for the format
887      *                        used by GnuPG output and
888      *                        {@link Crypt_GPG::FORMAT_NONE} for no formatting.
889      *                        Defaults to <code>Crypt_GPG::FORMAT_NONE</code>.
890      *
891      * @return string the fingerprint of the key, or null if no fingerprint
892      *                is found for the given <kbd>$keyId</kbd>.
893      *
894      * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs.
895      *         Use the <kbd>debug</kbd> option and file a bug report if these
896      *         exceptions occur.
897      */
898     public function getFingerprint($keyId, $format = Crypt_GPG::FORMAT_NONE)
899     {
900         $output    = '';
901         $operation = '--list-keys ' . escapeshellarg($keyId);
902         $arguments = array(
903             '--with-colons',
904             '--with-fingerprint'
905         );
906
907         $this->engine->reset();
908         $this->engine->setOutput($output);
909         $this->engine->setOperation($operation, $arguments);
910         $this->engine->run();
911
912         $code = $this->engine->getErrorCode();
913
914         switch ($code) {
915         case Crypt_GPG::ERROR_NONE:
916         case Crypt_GPG::ERROR_KEY_NOT_FOUND:
917             // ignore not found key errors
918             break;
919         default:
920             throw new Crypt_GPG_Exception(
921                 'Unknown error getting key fingerprint. Please use the ' .
922                 '\'debug\' option when creating the Crypt_GPG object, and ' .
923                 'file a bug report at ' . self::BUG_URI, $code);
924         }
925
926         $fingerprint = null;
927
928         $lines = explode(PHP_EOL, $output);
929         foreach ($lines as $line) {
930             if (substr($line, 0, 3) == 'fpr') {
931                 $lineExp     = explode(':', $line);
932                 $fingerprint = $lineExp[9];
933
934                 switch ($format) {
935                 case Crypt_GPG::FORMAT_CANONICAL:
936                     $fingerprintExp = str_split($fingerprint, 4);
937                     $format         = '%s %s %s %s %s  %s %s %s %s %s';
938                     $fingerprint    = vsprintf($format, $fingerprintExp);
939                     break;
940
941                 case Crypt_GPG::FORMAT_X509:
942                     $fingerprintExp = str_split($fingerprint, 2);
943                     $fingerprint    = implode(':', $fingerprintExp);
944                     break;
945                 }
946
947                 break;
948             }
949         }
950
951         return $fingerprint;
952     }
953
954     // }}}
955     // {{{ encrypt()
956
957     /**
958      * Encrypts string data
959      *
960      * Data is ASCII armored by default but may optionally be returned as
961      * binary.
962      *
963      * @param string  $data  the data to be encrypted.
964      * @param boolean $armor optional. If true, ASCII armored data is returned;
965      *                       otherwise, binary data is returned. Defaults to
966      *                       true.
967      *
968      * @return string the encrypted data.
969      *
970      * @throws Crypt_GPG_KeyNotFoundException if no encryption key is specified.
971      *         See {@link Crypt_GPG::addEncryptKey()}.
972      *
973      * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs.
974      *         Use the <kbd>debug</kbd> option and file a bug report if these
975      *         exceptions occur.
976      *
977      * @sensitive $data
978      */
979     public function encrypt($data, $armor = true)
980     {
981         return $this->_encrypt($data, false, null, $armor);
982     }
983
984     // }}}
985     // {{{ encryptFile()
986
987     /**
988      * Encrypts a file
989      *
990      * Encrypted data is ASCII armored by default but may optionally be saved
991      * as binary.
992      *
993      * @param string  $filename      the filename of the file to encrypt.
994      * @param string  $encryptedFile optional. The filename of the file in
995      *                               which to store the encrypted data. If null
996      *                               or unspecified, the encrypted data is
997      *                               returned as a string.
998      * @param boolean $armor         optional. If true, ASCII armored data is
999      *                               returned; otherwise, binary data is
1000      *                               returned. Defaults to true.
1001      *
1002      * @return void|string if the <kbd>$encryptedFile</kbd> parameter is null,
1003      *                     a string containing the encrypted data is returned.
1004      *
1005      * @throws Crypt_GPG_KeyNotFoundException if no encryption key is specified.
1006      *         See {@link Crypt_GPG::addEncryptKey()}.
1007      *
1008      * @throws Crypt_GPG_FileException if the output file is not writeable or
1009      *         if the input file is not readable.
1010      *
1011      * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs.
1012      *         Use the <kbd>debug</kbd> option and file a bug report if these
1013      *         exceptions occur.
1014      */
1015     public function encryptFile($filename, $encryptedFile = null, $armor = true)
1016     {
1017         return $this->_encrypt($filename, true, $encryptedFile, $armor);
1018     }
1019
1020     // }}}
1021     // {{{ encryptAndSign()
1022
1023     /**
1024      * Encrypts and signs data
1025      *
1026      * Data is encrypted and signed in a single pass.
1027      *
1028      * NOTE: Until GnuPG version 1.4.10, it was not possible to verify
1029      * encrypted-signed data without decrypting it at the same time. If you try
1030      * to use {@link Crypt_GPG::verify()} method on encrypted-signed data with
1031      * earlier GnuPG versions, you will get an error. Please use
1032      * {@link Crypt_GPG::decryptAndVerify()} to verify encrypted-signed data.
1033      *
1034      * @param string  $data  the data to be encrypted and signed.
1035      * @param boolean $armor optional. If true, ASCII armored data is returned;
1036      *                       otherwise, binary data is returned. Defaults to
1037      *                       true.
1038      *
1039      * @return string the encrypted signed data.
1040      *
1041      * @throws Crypt_GPG_KeyNotFoundException if no encryption key is specified
1042      *         or if no signing key is specified. See
1043      *         {@link Crypt_GPG::addEncryptKey()} and
1044      *         {@link Crypt_GPG::addSignKey()}.
1045      *
1046      * @throws Crypt_GPG_BadPassphraseException if a specified passphrase is
1047      *         incorrect or if a required passphrase is not specified.
1048      *
1049      * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs.
1050      *         Use the <kbd>debug</kbd> option and file a bug report if these
1051      *         exceptions occur.
1052      *
1053      * @see Crypt_GPG::decryptAndVerify()
1054      */
1055     public function encryptAndSign($data, $armor = true)
1056     {
1057         return $this->_encryptAndSign($data, false, null, $armor);
1058     }
1059
1060     // }}}
1061     // {{{ encryptAndSignFile()
1062
1063     /**
1064      * Encrypts and signs a file
1065      *
1066      * The file is encrypted and signed in a single pass.
1067      *
1068      * NOTE: Until GnuPG version 1.4.10, it was not possible to verify
1069      * encrypted-signed files without decrypting them at the same time. If you
1070      * try to use {@link Crypt_GPG::verify()} method on encrypted-signed files
1071      * with earlier GnuPG versions, you will get an error. Please use
1072      * {@link Crypt_GPG::decryptAndVerifyFile()} to verify encrypted-signed
1073      * files.
1074      *
1075      * @param string  $filename   the name of the file containing the data to
1076      *                            be encrypted and signed.
1077      * @param string  $signedFile optional. The name of the file in which the
1078      *                            encrypted, signed data should be stored. If
1079      *                            null or unspecified, the encrypted, signed
1080      *                            data is returned as a string.
1081      * @param boolean $armor      optional. If true, ASCII armored data is
1082      *                            returned; otherwise, binary data is returned.
1083      *                            Defaults to true.
1084      *
1085      * @return void|string if the <kbd>$signedFile</kbd> parameter is null, a
1086      *                     string containing the encrypted, signed data is
1087      *                     returned.
1088      *
1089      * @throws Crypt_GPG_KeyNotFoundException if no encryption key is specified
1090      *         or if no signing key is specified. See
1091      *         {@link Crypt_GPG::addEncryptKey()} and
1092      *         {@link Crypt_GPG::addSignKey()}.
1093      *
1094      * @throws Crypt_GPG_BadPassphraseException if a specified passphrase is
1095      *         incorrect or if a required passphrase is not specified.
1096      *
1097      * @throws Crypt_GPG_FileException if the output file is not writeable or
1098      *         if the input file is not readable.
1099      *
1100      * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs.
1101      *         Use the <kbd>debug</kbd> option and file a bug report if these
1102      *         exceptions occur.
1103      *
1104      * @see Crypt_GPG::decryptAndVerifyFile()
1105      */
1106     public function encryptAndSignFile($filename, $signedFile = null,
1107         $armor = true
1108     ) {
1109         return $this->_encryptAndSign($filename, true, $signedFile, $armor);
1110     }
1111
1112     // }}}
1113     // {{{ decrypt()
1114
1115     /**
1116      * Decrypts string data
1117      *
1118      * This method assumes the required private key is available in the keyring
1119      * and throws an exception if the private key is not available. To add a
1120      * private key to the keyring, use the {@link Crypt_GPG::importKey()} or
1121      * {@link Crypt_GPG::importKeyFile()} methods.
1122      *
1123      * @param string $encryptedData the data to be decrypted.
1124      *
1125      * @return string the decrypted data.
1126      *
1127      * @throws Crypt_GPG_KeyNotFoundException if the private key needed to
1128      *         decrypt the data is not in the user's keyring.
1129      *
1130      * @throws Crypt_GPG_NoDataException if specified data does not contain
1131      *         GPG encrypted data.
1132      *
1133      * @throws Crypt_GPG_BadPassphraseException if a required passphrase is
1134      *         incorrect or if a required passphrase is not specified. See
1135      *         {@link Crypt_GPG::addDecryptKey()}.
1136      *
1137      * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs.
1138      *         Use the <kbd>debug</kbd> option and file a bug report if these
1139      *         exceptions occur.
1140      */
1141     public function decrypt($encryptedData)
1142     {
1143         return $this->_decrypt($encryptedData, false, null);
1144     }
1145
1146     // }}}
1147     // {{{ decryptFile()
1148
1149     /**
1150      * Decrypts a file
1151      *
1152      * This method assumes the required private key is available in the keyring
1153      * and throws an exception if the private key is not available. To add a
1154      * private key to the keyring, use the {@link Crypt_GPG::importKey()} or
1155      * {@link Crypt_GPG::importKeyFile()} methods.
1156      *
1157      * @param string $encryptedFile the name of the encrypted file data to
1158      *                              decrypt.
1159      * @param string $decryptedFile optional. The name of the file to which the
1160      *                              decrypted data should be written. If null
1161      *                              or unspecified, the decrypted data is
1162      *                              returned as a string.
1163      *
1164      * @return void|string if the <kbd>$decryptedFile</kbd> parameter is null,
1165      *                     a string containing the decrypted data is returned.
1166      *
1167      * @throws Crypt_GPG_KeyNotFoundException if the private key needed to
1168      *         decrypt the data is not in the user's keyring.
1169      *
1170      * @throws Crypt_GPG_NoDataException if specified data does not contain
1171      *         GPG encrypted data.
1172      *
1173      * @throws Crypt_GPG_BadPassphraseException if a required passphrase is
1174      *         incorrect or if a required passphrase is not specified. See
1175      *         {@link Crypt_GPG::addDecryptKey()}.
1176      *
1177      * @throws Crypt_GPG_FileException if the output file is not writeable or
1178      *         if the input file is not readable.
1179      *
1180      * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs.
1181      *         Use the <kbd>debug</kbd> option and file a bug report if these
1182      *         exceptions occur.
1183      */
1184     public function decryptFile($encryptedFile, $decryptedFile = null)
1185     {
1186         return $this->_decrypt($encryptedFile, true, $decryptedFile);
1187     }
1188
1189     // }}}
1190     // {{{ decryptAndVerify()
1191
1192     /**
1193      * Decrypts and verifies string data
1194      *
1195      * This method assumes the required private key is available in the keyring
1196      * and throws an exception if the private key is not available. To add a
1197      * private key to the keyring, use the {@link Crypt_GPG::importKey()} or
1198      * {@link Crypt_GPG::importKeyFile()} methods.
1199      *
1200      * @param string $encryptedData the encrypted, signed data to be decrypted
1201      *                              and verified.
1202      *
1203      * @return array two element array. The array has an element 'data'
1204      *               containing the decrypted data and an element
1205      *               'signatures' containing an array of
1206      *               {@link Crypt_GPG_Signature} objects for the signed data.
1207      *
1208      * @throws Crypt_GPG_KeyNotFoundException if the private key needed to
1209      *         decrypt the data is not in the user's keyring.
1210      *
1211      * @throws Crypt_GPG_NoDataException if specified data does not contain
1212      *         GPG encrypted data.
1213      *
1214      * @throws Crypt_GPG_BadPassphraseException if a required passphrase is
1215      *         incorrect or if a required passphrase is not specified. See
1216      *         {@link Crypt_GPG::addDecryptKey()}.
1217      *
1218      * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs.
1219      *         Use the <kbd>debug</kbd> option and file a bug report if these
1220      *         exceptions occur.
1221      */
1222     public function decryptAndVerify($encryptedData)
1223     {
1224         return $this->_decryptAndVerify($encryptedData, false, null);
1225     }
1226
1227     // }}}
1228     // {{{ decryptAndVerifyFile()
1229
1230     /**
1231      * Decrypts and verifies a signed, encrypted file
1232      *
1233      * This method assumes the required private key is available in the keyring
1234      * and throws an exception if the private key is not available. To add a
1235      * private key to the keyring, use the {@link Crypt_GPG::importKey()} or
1236      * {@link Crypt_GPG::importKeyFile()} methods.
1237      *
1238      * @param string $encryptedFile the name of the signed, encrypted file to
1239      *                              to decrypt and verify.
1240      * @param string $decryptedFile optional. The name of the file to which the
1241      *                              decrypted data should be written. If null
1242      *                              or unspecified, the decrypted data is
1243      *                              returned in the results array.
1244      *
1245      * @return array two element array. The array has an element 'data'
1246      *               containing the decrypted data and an element
1247      *               'signatures' containing an array of
1248      *               {@link Crypt_GPG_Signature} objects for the signed data.
1249      *               If the decrypted data is written to a file, the 'data'
1250      *               element is null.
1251      *
1252      * @throws Crypt_GPG_KeyNotFoundException if the private key needed to
1253      *         decrypt the data is not in the user's keyring.
1254      *
1255      * @throws Crypt_GPG_NoDataException if specified data does not contain
1256      *         GPG encrypted data.
1257      *
1258      * @throws Crypt_GPG_BadPassphraseException if a required passphrase is
1259      *         incorrect or if a required passphrase is not specified. See
1260      *         {@link Crypt_GPG::addDecryptKey()}.
1261      *
1262      * @throws Crypt_GPG_FileException if the output file is not writeable or
1263      *         if the input file is not readable.
1264      *
1265      * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs.
1266      *         Use the <kbd>debug</kbd> option and file a bug report if these
1267      *         exceptions occur.
1268      */
1269     public function decryptAndVerifyFile($encryptedFile, $decryptedFile = null)
1270     {
1271         return $this->_decryptAndVerify($encryptedFile, true, $decryptedFile);
1272     }
1273
1274     // }}}
1275     // {{{ sign()
1276
1277     /**
1278      * Signs data
1279      *
1280      * Data may be signed using any one of the three available signing modes:
1281      * - {@link Crypt_GPG::SIGN_MODE_NORMAL}
1282      * - {@link Crypt_GPG::SIGN_MODE_CLEAR}
1283      * - {@link Crypt_GPG::SIGN_MODE_DETACHED}
1284      *
1285      * @param string  $data     the data to be signed.
1286      * @param boolean $mode     optional. The data signing mode to use. Should
1287      *                          be one of {@link Crypt_GPG::SIGN_MODE_NORMAL},
1288      *                          {@link Crypt_GPG::SIGN_MODE_CLEAR} or
1289      *                          {@link Crypt_GPG::SIGN_MODE_DETACHED}. If not
1290      *                          specified, defaults to
1291      *                          <kbd>Crypt_GPG::SIGN_MODE_NORMAL</kbd>.
1292      * @param boolean $armor    optional. If true, ASCII armored data is
1293      *                          returned; otherwise, binary data is returned.
1294      *                          Defaults to true. This has no effect if the
1295      *                          mode <kbd>Crypt_GPG::SIGN_MODE_CLEAR</kbd> is
1296      *                          used.
1297      * @param boolean $textmode optional. If true, line-breaks in signed data
1298      *                          are normalized. Use this option when signing
1299      *                          e-mail, or for greater compatibility between
1300      *                          systems with different line-break formats.
1301      *                          Defaults to false. This has no effect if the
1302      *                          mode <kbd>Crypt_GPG::SIGN_MODE_CLEAR</kbd> is
1303      *                          used as clear-signing always uses textmode.
1304      *
1305      * @return string the signed data, or the signature data if a detached
1306      *                signature is requested.
1307      *
1308      * @throws Crypt_GPG_KeyNotFoundException if no signing key is specified.
1309      *         See {@link Crypt_GPG::addSignKey()}.
1310      *
1311      * @throws Crypt_GPG_BadPassphraseException if a specified passphrase is
1312      *         incorrect or if a required passphrase is not specified.
1313      *
1314      * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs.
1315      *         Use the <kbd>debug</kbd> option and file a bug report if these
1316      *         exceptions occur.
1317      */
1318     public function sign($data, $mode = Crypt_GPG::SIGN_MODE_NORMAL,
1319         $armor = true, $textmode = false
1320     ) {
1321         return $this->_sign($data, false, null, $mode, $armor, $textmode);
1322     }
1323
1324     // }}}
1325     // {{{ signFile()
1326
1327     /**
1328      * Signs a file
1329      *
1330      * The file may be signed using any one of the three available signing
1331      * modes:
1332      * - {@link Crypt_GPG::SIGN_MODE_NORMAL}
1333      * - {@link Crypt_GPG::SIGN_MODE_CLEAR}
1334      * - {@link Crypt_GPG::SIGN_MODE_DETACHED}
1335      *
1336      * @param string  $filename   the name of the file containing the data to
1337      *                            be signed.
1338      * @param string  $signedFile optional. The name of the file in which the
1339      *                            signed data should be stored. If null or
1340      *                            unspecified, the signed data is returned as a
1341      *                            string.
1342      * @param boolean $mode       optional. The data signing mode to use. Should
1343      *                            be one of {@link Crypt_GPG::SIGN_MODE_NORMAL},
1344      *                            {@link Crypt_GPG::SIGN_MODE_CLEAR} or
1345      *                            {@link Crypt_GPG::SIGN_MODE_DETACHED}. If not
1346      *                            specified, defaults to
1347      *                            <kbd>Crypt_GPG::SIGN_MODE_NORMAL</kbd>.
1348      * @param boolean $armor      optional. If true, ASCII armored data is
1349      *                            returned; otherwise, binary data is returned.
1350      *                            Defaults to true. This has no effect if the
1351      *                            mode <kbd>Crypt_GPG::SIGN_MODE_CLEAR</kbd> is
1352      *                            used.
1353      * @param boolean $textmode   optional. If true, line-breaks in signed data
1354      *                            are normalized. Use this option when signing
1355      *                            e-mail, or for greater compatibility between
1356      *                            systems with different line-break formats.
1357      *                            Defaults to false. This has no effect if the
1358      *                            mode <kbd>Crypt_GPG::SIGN_MODE_CLEAR</kbd> is
1359      *                            used as clear-signing always uses textmode.
1360      *
1361      * @return void|string if the <kbd>$signedFile</kbd> parameter is null, a
1362      *                     string containing the signed data (or the signature
1363      *                     data if a detached signature is requested) is
1364      *                     returned.
1365      *
1366      * @throws Crypt_GPG_KeyNotFoundException if no signing key is specified.
1367      *         See {@link Crypt_GPG::addSignKey()}.
1368      *
1369      * @throws Crypt_GPG_BadPassphraseException if a specified passphrase is
1370      *         incorrect or if a required passphrase is not specified.
1371      *
1372      * @throws Crypt_GPG_FileException if the output file is not writeable or
1373      *         if the input file is not readable.
1374      *
1375      * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs.
1376      *         Use the <kbd>debug</kbd> option and file a bug report if these
1377      *         exceptions occur.
1378      */
1379     public function signFile($filename, $signedFile = null,
1380         $mode = Crypt_GPG::SIGN_MODE_NORMAL, $armor = true, $textmode = false
1381     ) {
1382         return $this->_sign(
1383             $filename,
1384             true,
1385             $signedFile,
1386             $mode,
1387             $armor,
1388             $textmode
1389         );
1390     }
1391
1392     // }}}
1393     // {{{ verify()
1394
1395     /**
1396      * Verifies signed data
1397      *
1398      * The {@link Crypt_GPG::decrypt()} method may be used to get the original
1399      * message if the signed data is not clearsigned and does not use a
1400      * detached signature.
1401      *
1402      * @param string $signedData the signed data to be verified.
1403      * @param string $signature  optional. If verifying data signed using a
1404      *                           detached signature, this must be the detached
1405      *                           signature data. The data that was signed is
1406      *                           specified in <kbd>$signedData</kbd>.
1407      *
1408      * @return array an array of {@link Crypt_GPG_Signature} objects for the
1409      *               signed data. For each signature that is valid, the
1410      *               {@link Crypt_GPG_Signature::isValid()} will return true.
1411      *
1412      * @throws Crypt_GPG_NoDataException if the provided data is not signed
1413      *         data.
1414      *
1415      * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs.
1416      *         Use the <kbd>debug</kbd> option and file a bug report if these
1417      *         exceptions occur.
1418      *
1419      * @see Crypt_GPG_Signature
1420      */
1421     public function verify($signedData, $signature = '')
1422     {
1423         return $this->_verify($signedData, false, $signature);
1424     }
1425
1426     // }}}
1427     // {{{ verifyFile()
1428
1429     /**
1430      * Verifies a signed file
1431      *
1432      * The {@link Crypt_GPG::decryptFile()} method may be used to get the
1433      * original message if the signed data is not clearsigned and does not use
1434      * a detached signature.
1435      *
1436      * @param string $filename  the signed file to be verified.
1437      * @param string $signature optional. If verifying a file signed using a
1438      *                          detached signature, this must be the detached
1439      *                          signature data. The file that was signed is
1440      *                          specified in <kbd>$filename</kbd>.
1441      *
1442      * @return array an array of {@link Crypt_GPG_Signature} objects for the
1443      *               signed data. For each signature that is valid, the
1444      *               {@link Crypt_GPG_Signature::isValid()} will return true.
1445      *
1446      * @throws Crypt_GPG_NoDataException if the provided data is not signed
1447      *         data.
1448      *
1449      * @throws Crypt_GPG_FileException if the input file is not readable.
1450      *
1451      * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs.
1452      *         Use the <kbd>debug</kbd> option and file a bug report if these
1453      *         exceptions occur.
1454      *
1455      * @see Crypt_GPG_Signature
1456      */
1457     public function verifyFile($filename, $signature = '')
1458     {
1459         return $this->_verify($filename, true, $signature);
1460     }
1461
1462     // }}}
1463     // {{{ addDecryptKey()
1464
1465     /**
1466      * Adds a key to use for decryption
1467      *
1468      * @param mixed  $key        the key to use. This may be a key identifier,
1469      *                           user id, fingerprint, {@link Crypt_GPG_Key} or
1470      *                           {@link Crypt_GPG_SubKey}. The key must be able
1471      *                           to encrypt.
1472      * @param string $passphrase optional. The passphrase of the key required
1473      *                           for decryption.
1474      *
1475      * @return void
1476      *
1477      * @see Crypt_GPG::decrypt()
1478      * @see Crypt_GPG::decryptFile()
1479      * @see Crypt_GPG::clearDecryptKeys()
1480      * @see Crypt_GPG::_addKey()
1481      * @see Crypt_GPG_DecryptStatusHandler
1482      *
1483      * @sensitive $passphrase
1484      */
1485     public function addDecryptKey($key, $passphrase = null)
1486     {
1487         $this->_addKey($this->decryptKeys, true, false, $key, $passphrase);
1488     }
1489
1490     // }}}
1491     // {{{ addEncryptKey()
1492
1493     /**
1494      * Adds a key to use for encryption
1495      *
1496      * @param mixed $key the key to use. This may be a key identifier, user id
1497      *                   user id, fingerprint, {@link Crypt_GPG_Key} or
1498      *                   {@link Crypt_GPG_SubKey}. The key must be able to
1499      *                   encrypt.
1500      *
1501      * @return void
1502      *
1503      * @see Crypt_GPG::encrypt()
1504      * @see Crypt_GPG::encryptFile()
1505      * @see Crypt_GPG::clearEncryptKeys()
1506      * @see Crypt_GPG::_addKey()
1507      */
1508     public function addEncryptKey($key)
1509     {
1510         $this->_addKey($this->encryptKeys, true, false, $key);
1511     }
1512
1513     // }}}
1514     // {{{ addSignKey()
1515
1516     /**
1517      * Adds a key to use for signing
1518      *
1519      * @param mixed  $key        the key to use. This may be a key identifier,
1520      *                           user id, fingerprint, {@link Crypt_GPG_Key} or
1521      *                           {@link Crypt_GPG_SubKey}. The key must be able
1522      *                           to sign.
1523      * @param string $passphrase optional. The passphrase of the key required
1524      *                           for signing.
1525      *
1526      * @return void
1527      *
1528      * @see Crypt_GPG::sign()
1529      * @see Crypt_GPG::signFile()
1530      * @see Crypt_GPG::clearSignKeys()
1531      * @see Crypt_GPG::handleSignStatus()
1532      * @see Crypt_GPG::_addKey()
1533      *
1534      * @sensitive $passphrase
1535      */
1536     public function addSignKey($key, $passphrase = null)
1537     {
1538         $this->_addKey($this->signKeys, false, true, $key, $passphrase);
1539     }
1540
1541     // }}}
1542     // {{{ clearDecryptKeys()
1543
1544     /**
1545      * Clears all decryption keys
1546      *
1547      * @return void
1548      *
1549      * @see Crypt_GPG::decrypt()
1550      * @see Crypt_GPG::addDecryptKey()
1551      */
1552     public function clearDecryptKeys()
1553     {
1554         $this->decryptKeys = array();
1555     }
1556
1557     // }}}
1558     // {{{ clearEncryptKeys()
1559
1560     /**
1561      * Clears all encryption keys
1562      *
1563      * @return void
1564      *
1565      * @see Crypt_GPG::encrypt()
1566      * @see Crypt_GPG::addEncryptKey()
1567      */
1568     public function clearEncryptKeys()
1569     {
1570         $this->encryptKeys = array();
1571     }
1572
1573     // }}}
1574     // {{{ clearSignKeys()
1575
1576     /**
1577      * Clears all signing keys
1578      *
1579      * @return void
1580      *
1581      * @see Crypt_GPG::sign()
1582      * @see Crypt_GPG::addSignKey()
1583      */
1584     public function clearSignKeys()
1585     {
1586         $this->signKeys = array();
1587     }
1588
1589     // }}}
1590     // {{{ handleSignStatus()
1591
1592     /**
1593      * Handles the status output from GPG for the sign operation
1594      *
1595      * This method is responsible for sending the passphrase commands when
1596      * required by the {@link Crypt_GPG::sign()} method. See <b>doc/DETAILS</b>
1597      * in the {@link http://www.gnupg.org/download/ GPG distribution} for
1598      * detailed information on GPG's status output.
1599      *
1600      * @param string $line the status line to handle.
1601      *
1602      * @return void
1603      *
1604      * @see Crypt_GPG::sign()
1605      */
1606     public function handleSignStatus($line)
1607     {
1608         $tokens = explode(' ', $line);
1609         switch ($tokens[0]) {
1610         case 'NEED_PASSPHRASE':
1611             $subKeyId = $tokens[1];
1612             if (array_key_exists($subKeyId, $this->signKeys)) {
1613                 $passphrase = $this->signKeys[$subKeyId]['passphrase'];
1614                 $this->engine->sendCommand($passphrase);
1615             } else {
1616                 $this->engine->sendCommand('');
1617             }
1618             break;
1619         }
1620     }
1621
1622     // }}}
1623     // {{{ handleImportKeyStatus()
1624
1625     /**
1626      * Handles the status output from GPG for the import operation
1627      *
1628      * This method is responsible for building the result array that is
1629      * returned from the {@link Crypt_GPG::importKey()} method. See
1630      * <b>doc/DETAILS</b> in the
1631      * {@link http://www.gnupg.org/download/ GPG distribution} for detailed
1632      * information on GPG's status output.
1633      *
1634      * @param string $line    the status line to handle.
1635      * @param array  &$result the current result array being processed.
1636      *
1637      * @return void
1638      *
1639      * @see Crypt_GPG::importKey()
1640      * @see Crypt_GPG::importKeyFile()
1641      * @see Crypt_GPG_Engine::addStatusHandler()
1642      */
1643     public function handleImportKeyStatus($line, array &$result)
1644     {
1645         $tokens = explode(' ', $line);
1646         switch ($tokens[0]) {
1647         case 'IMPORT_OK':
1648             $result['fingerprint'] = $tokens[2];
1649             break;
1650
1651         case 'IMPORT_RES':
1652             $result['public_imported']   = intval($tokens[3]);
1653             $result['public_unchanged']  = intval($tokens[5]);
1654             $result['private_imported']  = intval($tokens[11]);
1655             $result['private_unchanged'] = intval($tokens[12]);
1656             break;
1657         }
1658     }
1659
1660     // }}}
1661     // {{{ setEngine()
1662
1663     /**
1664      * Sets the I/O engine to use for GnuPG operations
1665      *
1666      * Normally this method does not need to be used. It provides a means for
1667      * dependency injection.
1668      *
1669      * @param Crypt_GPG_Engine $engine the engine to use.
1670      *
1671      * @return void
1672      */
1673     public function setEngine(Crypt_GPG_Engine $engine)
1674     {
1675         $this->engine = $engine;
1676     }
1677
1678     // }}}
1679     // {{{ _addKey()
1680
1681     /**
1682      * Adds a key to one of the internal key arrays
1683      *
1684      * This handles resolving full key objects from the provided
1685      * <kbd>$key</kbd> value.
1686      *
1687      * @param array   &$array     the array to which the key should be added.
1688      * @param boolean $encrypt    whether or not the key must be able to
1689      *                            encrypt.
1690      * @param boolean $sign       whether or not the key must be able to sign.
1691      * @param mixed   $key        the key to add. This may be a key identifier,
1692      *                            user id, fingerprint, {@link Crypt_GPG_Key} or
1693      *                            {@link Crypt_GPG_SubKey}.
1694      * @param string  $passphrase optional. The passphrase associated with the
1695      *                            key.
1696      *
1697      * @return void
1698      *
1699      * @sensitive $passphrase
1700      */
1701     private function _addKey(array &$array, $encrypt, $sign, $key,
1702         $passphrase = null
1703     ) {
1704         $subKeys = array();
1705
1706         if (is_scalar($key)) {
1707             $keys = $this->getKeys($key);
1708             if (count($keys) == 0) {
1709                 throw new Crypt_GPG_KeyNotFoundException(
1710                     'Key "' . $key . '" not found.', 0, $key);
1711             }
1712             $key = $keys[0];
1713         }
1714
1715         if ($key instanceof Crypt_GPG_Key) {
1716             if ($encrypt && !$key->canEncrypt()) {
1717                 throw new InvalidArgumentException(
1718                     'Key "' . $key . '" cannot encrypt.');
1719             }
1720
1721             if ($sign && !$key->canSign()) {
1722                 throw new InvalidArgumentException(
1723                     'Key "' . $key . '" cannot sign.');
1724             }
1725
1726             foreach ($key->getSubKeys() as $subKey) {
1727                 $canEncrypt = $subKey->canEncrypt();
1728                 $canSign    = $subKey->canSign();
1729                 if (   ($encrypt && $sign && $canEncrypt && $canSign)
1730                     || ($encrypt && !$sign && $canEncrypt)
1731                     || (!$encrypt && $sign && $canSign)
1732                 ) {
1733                     // We add all subkeys that meet the requirements because we
1734                     // were not told which subkey is required.
1735                     $subKeys[] = $subKey;
1736                 }
1737             }
1738         } elseif ($key instanceof Crypt_GPG_SubKey) {
1739             $subKeys[] = $key;
1740         }
1741
1742         if (count($subKeys) === 0) {
1743             throw new InvalidArgumentException(
1744                 'Key "' . $key . '" is not in a recognized format.');
1745         }
1746
1747         foreach ($subKeys as $subKey) {
1748             if ($encrypt && !$subKey->canEncrypt()) {
1749                 throw new InvalidArgumentException(
1750                     'Key "' . $key . '" cannot encrypt.');
1751             }
1752
1753             if ($sign && !$subKey->canSign()) {
1754                 throw new InvalidArgumentException(
1755                     'Key "' . $key . '" cannot sign.');
1756             }
1757
1758             $array[$subKey->getId()] = array(
1759                 'fingerprint' => $subKey->getFingerprint(),
1760                 'passphrase'  => $passphrase
1761             );
1762         }
1763     }
1764
1765     // }}}
1766     // {{{ _importKey()
1767
1768     /**
1769      * Imports a public or private key into the keyring
1770      *
1771      * @param string  $key    the key to be imported.
1772      * @param boolean $isFile whether or not the input is a filename.
1773      *
1774      * @return array an associative array containing the following elements:
1775      *               - <kbd>fingerprint</kbd>       - the fingerprint of the
1776      *                                                imported key,
1777      *               - <kbd>public_imported</kbd>   - the number of public
1778      *                                                keys imported,
1779      *               - <kbd>public_unchanged</kbd>  - the number of unchanged
1780      *                                                public keys,
1781      *               - <kbd>private_imported</kbd>  - the number of private
1782      *                                                keys imported,
1783      *               - <kbd>private_unchanged</kbd> - the number of unchanged
1784      *                                                private keys.
1785      *
1786      * @throws Crypt_GPG_NoDataException if the key data is missing or if the
1787      *         data is is not valid key data.
1788      *
1789      * @throws Crypt_GPG_FileException if the key file is not readable.
1790      *
1791      * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs.
1792      *         Use the <kbd>debug</kbd> option and file a bug report if these
1793      *         exceptions occur.
1794      */
1795     private function _importKey($key, $isFile)
1796     {
1797         $result = array();
1798
1799         if ($isFile) {
1800             $input = @fopen($key, 'rb');
1801             if ($input === false) {
1802                 throw new Crypt_GPG_FileException('Could not open key file "' .
1803                     $key . '" for importing.', 0, $key);
1804             }
1805         } else {
1806             $input = strval($key);
1807             if ($input == '') {
1808                 throw new Crypt_GPG_NoDataException(
1809                     'No valid GPG key data found.', Crypt_GPG::ERROR_NO_DATA);
1810             }
1811         }
1812
1813         $arguments = array();
1814         $version   = $this->engine->getVersion();
1815
1816         if (   version_compare($version, '1.0.5', 'ge')
1817             && version_compare($version, '1.0.7', 'lt')
1818         ) {
1819             $arguments[] = '--allow-secret-key-import';
1820         }
1821
1822         $this->engine->reset();
1823         $this->engine->addStatusHandler(
1824             array($this, 'handleImportKeyStatus'),
1825             array(&$result)
1826         );
1827
1828         $this->engine->setOperation('--import', $arguments);
1829         $this->engine->setInput($input);
1830         $this->engine->run();
1831
1832         if ($isFile) {
1833             fclose($input);
1834         }
1835
1836         $code = $this->engine->getErrorCode();
1837
1838         switch ($code) {
1839         case Crypt_GPG::ERROR_DUPLICATE_KEY:
1840         case Crypt_GPG::ERROR_NONE:
1841             // ignore duplicate key import errors
1842             break;
1843         case Crypt_GPG::ERROR_NO_DATA:
1844             throw new Crypt_GPG_NoDataException(
1845                 'No valid GPG key data found.', $code);
1846         default:
1847             throw new Crypt_GPG_Exception(
1848                 'Unknown error importing GPG key. Please use the \'debug\' ' .
1849                 'option when creating the Crypt_GPG object, and file a bug ' .
1850                 'report at ' . self::BUG_URI, $code);
1851         }
1852
1853         return $result;
1854     }
1855
1856     // }}}
1857     // {{{ _encrypt()
1858
1859     /**
1860      * Encrypts data
1861      *
1862      * @param string  $data       the data to encrypt.
1863      * @param boolean $isFile     whether or not the data is a filename.
1864      * @param string  $outputFile the filename of the file in which to store
1865      *                            the encrypted data. If null, the encrypted
1866      *                            data is returned as a string.
1867      * @param boolean $armor      if true, ASCII armored data is returned;
1868      *                            otherwise, binary data is returned.
1869      *
1870      * @return void|string if the <kbd>$outputFile</kbd> parameter is null, a
1871      *                     string containing the encrypted data is returned.
1872      *
1873      * @throws Crypt_GPG_KeyNotFoundException if no encryption key is specified.
1874      *         See {@link Crypt_GPG::addEncryptKey()}.
1875      *
1876      * @throws Crypt_GPG_FileException if the output file is not writeable or
1877      *         if the input file is not readable.
1878      *
1879      * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs.
1880      *         Use the <kbd>debug</kbd> option and file a bug report if these
1881      *         exceptions occur.
1882      */
1883     private function _encrypt($data, $isFile, $outputFile, $armor)
1884     {
1885         if (count($this->encryptKeys) === 0) {
1886             throw new Crypt_GPG_KeyNotFoundException(
1887                 'No encryption keys specified.');
1888         }
1889
1890         if ($isFile) {
1891             $input = @fopen($data, 'rb');
1892             if ($input === false) {
1893                 throw new Crypt_GPG_FileException('Could not open input file "' .
1894                     $data . '" for encryption.', 0, $data);
1895             }
1896         } else {
1897             $input = strval($data);
1898         }
1899
1900         if ($outputFile === null) {
1901             $output = '';
1902         } else {
1903             $output = @fopen($outputFile, 'wb');
1904             if ($output === false) {
1905                 if ($isFile) {
1906                     fclose($input);
1907                 }
1908                 throw new Crypt_GPG_FileException('Could not open output ' .
1909                     'file "' . $outputFile . '" for storing encrypted data.',
1910                     0, $outputFile);
1911             }
1912         }
1913
1914         $arguments = ($armor) ? array('--armor') : array();
1915         foreach ($this->encryptKeys as $key) {
1916             $arguments[] = '--recipient ' . escapeshellarg($key['fingerprint']);
1917         }
1918
1919         $this->engine->reset();
1920         $this->engine->setInput($input);
1921         $this->engine->setOutput($output);
1922         $this->engine->setOperation('--encrypt', $arguments);
1923         $this->engine->run();
1924
1925         if ($isFile) {
1926             fclose($input);
1927         }
1928
1929         if ($outputFile !== null) {
1930             fclose($output);
1931         }
1932
1933         $code = $this->engine->getErrorCode();
1934
1935         if ($code !== Crypt_GPG::ERROR_NONE) {
1936             throw new Crypt_GPG_Exception(
1937                 'Unknown error encrypting data. Please use the \'debug\' ' .
1938                 'option when creating the Crypt_GPG object, and file a bug ' .
1939                 'report at ' . self::BUG_URI, $code);
1940         }
1941
1942         if ($outputFile === null) {
1943             return $output;
1944         }
1945     }
1946
1947     // }}}
1948     // {{{ _decrypt()
1949
1950     /**
1951      * Decrypts data
1952      *
1953      * @param string  $data       the data to be decrypted.
1954      * @param boolean $isFile     whether or not the data is a filename.
1955      * @param string  $outputFile the name of the file to which the decrypted
1956      *                            data should be written. If null, the decrypted
1957      *                            data is returned as a string.
1958      *
1959      * @return void|string if the <kbd>$outputFile</kbd> parameter is null, a
1960      *                     string containing the decrypted data is returned.
1961      *
1962      * @throws Crypt_GPG_KeyNotFoundException if the private key needed to
1963      *         decrypt the data is not in the user's keyring.
1964      *
1965      * @throws Crypt_GPG_NoDataException if specified data does not contain
1966      *         GPG encrypted data.
1967      *
1968      * @throws Crypt_GPG_BadPassphraseException if a required passphrase is
1969      *         incorrect or if a required passphrase is not specified. See
1970      *         {@link Crypt_GPG::addDecryptKey()}.
1971      *
1972      * @throws Crypt_GPG_FileException if the output file is not writeable or
1973      *         if the input file is not readable.
1974      *
1975      * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs.
1976      *         Use the <kbd>debug</kbd> option and file a bug report if these
1977      *         exceptions occur.
1978      */
1979     private function _decrypt($data, $isFile, $outputFile)
1980     {
1981         if ($isFile) {
1982             $input = @fopen($data, 'rb');
1983             if ($input === false) {
1984                 throw new Crypt_GPG_FileException('Could not open input file "' .
1985                     $data . '" for decryption.', 0, $data);
1986             }
1987         } else {
1988             $input = strval($data);
1989             if ($input == '') {
1990                 throw new Crypt_GPG_NoDataException(
1991                     'Cannot decrypt data. No PGP encrypted data was found in '.
1992                     'the provided data.', Crypt_GPG::ERROR_NO_DATA);
1993             }
1994         }
1995
1996         if ($outputFile === null) {
1997             $output = '';
1998         } else {
1999             $output = @fopen($outputFile, 'wb');
2000             if ($output === false) {
2001                 if ($isFile) {
2002                     fclose($input);
2003                 }
2004                 throw new Crypt_GPG_FileException('Could not open output ' .
2005                     'file "' . $outputFile . '" for storing decrypted data.',
2006                     0, $outputFile);
2007             }
2008         }
2009
2010         $handler = new Crypt_GPG_DecryptStatusHandler($this->engine,
2011             $this->decryptKeys);
2012
2013         $this->engine->reset();
2014         $this->engine->addStatusHandler(array($handler, 'handle'));
2015         $this->engine->setOperation('--decrypt');
2016         $this->engine->setInput($input);
2017         $this->engine->setOutput($output);
2018         $this->engine->run();
2019
2020         if ($isFile) {
2021             fclose($input);
2022         }
2023
2024         if ($outputFile !== null) {
2025             fclose($output);
2026         }
2027
2028         // if there was any problem decrypting the data, the handler will
2029         // deal with it here.
2030         $handler->throwException();
2031
2032         if ($outputFile === null) {
2033             return $output;
2034         }
2035     }
2036
2037     // }}}
2038     // {{{ _sign()
2039
2040     /**
2041      * Signs data
2042      *
2043      * @param string  $data       the data to be signed.
2044      * @param boolean $isFile     whether or not the data is a filename.
2045      * @param string  $outputFile the name of the file in which the signed data
2046      *                            should be stored. If null, the signed data is
2047      *                            returned as a string.
2048      * @param boolean $mode       the data signing mode to use. Should be one of
2049      *                            {@link Crypt_GPG::SIGN_MODE_NORMAL},
2050      *                            {@link Crypt_GPG::SIGN_MODE_CLEAR} or
2051      *                            {@link Crypt_GPG::SIGN_MODE_DETACHED}.
2052      * @param boolean $armor      if true, ASCII armored data is returned;
2053      *                            otherwise, binary data is returned. This has
2054      *                            no effect if the mode
2055      *                            <kbd>Crypt_GPG::SIGN_MODE_CLEAR</kbd> is
2056      *                            used.
2057      * @param boolean $textmode   if true, line-breaks in signed data be
2058      *                            normalized. Use this option when signing
2059      *                            e-mail, or for greater compatibility between
2060      *                            systems with different line-break formats.
2061      *                            Defaults to false. This has no effect if the
2062      *                            mode <kbd>Crypt_GPG::SIGN_MODE_CLEAR</kbd> is
2063      *                            used as clear-signing always uses textmode.
2064      *
2065      * @return void|string if the <kbd>$outputFile</kbd> parameter is null, a
2066      *                     string containing the signed data (or the signature
2067      *                     data if a detached signature is requested) is
2068      *                     returned.
2069      *
2070      * @throws Crypt_GPG_KeyNotFoundException if no signing key is specified.
2071      *         See {@link Crypt_GPG::addSignKey()}.
2072      *
2073      * @throws Crypt_GPG_BadPassphraseException if a specified passphrase is
2074      *         incorrect or if a required passphrase is not specified.
2075      *
2076      * @throws Crypt_GPG_FileException if the output file is not writeable or
2077      *         if the input file is not readable.
2078      *
2079      * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs.
2080      *         Use the <kbd>debug</kbd> option and file a bug report if these
2081      *         exceptions occur.
2082      */
2083     private function _sign($data, $isFile, $outputFile, $mode, $armor,
2084         $textmode
2085     ) {
2086         if (count($this->signKeys) === 0) {
2087             throw new Crypt_GPG_KeyNotFoundException(
2088                 'No signing keys specified.');
2089         }
2090
2091         if ($isFile) {
2092             $input = @fopen($data, 'rb');
2093             if ($input === false) {
2094                 throw new Crypt_GPG_FileException('Could not open input ' .
2095                     'file "' . $data . '" for signing.', 0, $data);
2096             }
2097         } else {
2098             $input = strval($data);
2099         }
2100
2101         if ($outputFile === null) {
2102             $output = '';
2103         } else {
2104             $output = @fopen($outputFile, 'wb');
2105             if ($output === false) {
2106                 if ($isFile) {
2107                     fclose($input);
2108                 }
2109                 throw new Crypt_GPG_FileException('Could not open output ' .
2110                     'file "' . $outputFile . '" for storing signed ' .
2111                     'data.', 0, $outputFile);
2112             }
2113         }
2114
2115         switch ($mode) {
2116         case Crypt_GPG::SIGN_MODE_DETACHED:
2117             $operation = '--detach-sign';
2118             break;
2119         case Crypt_GPG::SIGN_MODE_CLEAR:
2120             $operation = '--clearsign';
2121             break;
2122         case Crypt_GPG::SIGN_MODE_NORMAL:
2123         default:
2124             $operation = '--sign';
2125             break;
2126         }
2127
2128         $arguments  = array();
2129
2130         if ($armor) {
2131             $arguments[] = '--armor';
2132         }
2133         if ($textmode) {
2134             $arguments[] = '--textmode';
2135         }
2136
2137         foreach ($this->signKeys as $key) {
2138             $arguments[] = '--local-user ' .
2139                 escapeshellarg($key['fingerprint']);
2140         }
2141
2142         $this->engine->reset();
2143         $this->engine->addStatusHandler(array($this, 'handleSignStatus'));
2144         $this->engine->setInput($input);
2145         $this->engine->setOutput($output);
2146         $this->engine->setOperation($operation, $arguments);
2147         $this->engine->run();
2148
2149         if ($isFile) {
2150             fclose($input);
2151         }
2152
2153         if ($outputFile !== null) {
2154             fclose($output);
2155         }
2156
2157         $code = $this->engine->getErrorCode();
2158
2159         switch ($code) {
2160         case Crypt_GPG::ERROR_NONE:
2161             break;
2162         case Crypt_GPG::ERROR_KEY_NOT_FOUND:
2163             throw new Crypt_GPG_KeyNotFoundException(
2164                 'Cannot sign data. Private key not found. Import the '.
2165                 'private key before trying to sign data.', $code,
2166                 $this->engine->getErrorKeyId());
2167         case Crypt_GPG::ERROR_BAD_PASSPHRASE:
2168             throw new Crypt_GPG_BadPassphraseException(
2169                 'Cannot sign data. Incorrect passphrase provided.', $code);
2170         case Crypt_GPG::ERROR_MISSING_PASSPHRASE:
2171             throw new Crypt_GPG_BadPassphraseException(
2172                 'Cannot sign data. No passphrase provided.', $code);
2173         default:
2174             throw new Crypt_GPG_Exception(
2175                 'Unknown error signing data. Please use the \'debug\' option ' .
2176                 'when creating the Crypt_GPG object, and file a bug report ' .
2177                 'at ' . self::BUG_URI, $code);
2178         }
2179
2180         if ($outputFile === null) {
2181             return $output;
2182         }
2183     }
2184
2185     // }}}
2186     // {{{ _encryptAndSign()
2187
2188     /**
2189      * Encrypts and signs data
2190      *
2191      * @param string  $data       the data to be encrypted and signed.
2192      * @param boolean $isFile     whether or not the data is a filename.
2193      * @param string  $outputFile the name of the file in which the encrypted,
2194      *                            signed data should be stored. If null, the
2195      *                            encrypted, signed data is returned as a
2196      *                            string.
2197      * @param boolean $armor      if true, ASCII armored data is returned;
2198      *                            otherwise, binary data is returned.
2199      *
2200      * @return void|string if the <kbd>$outputFile</kbd> parameter is null, a
2201      *                     string containing the encrypted, signed data is
2202      *                     returned.
2203      *
2204      * @throws Crypt_GPG_KeyNotFoundException if no encryption key is specified
2205      *         or if no signing key is specified. See
2206      *         {@link Crypt_GPG::addEncryptKey()} and
2207      *         {@link Crypt_GPG::addSignKey()}.
2208      *
2209      * @throws Crypt_GPG_BadPassphraseException if a specified passphrase is
2210      *         incorrect or if a required passphrase is not specified.
2211      *
2212      * @throws Crypt_GPG_FileException if the output file is not writeable or
2213      *         if the input file is not readable.
2214      *
2215      * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs.
2216      *         Use the <kbd>debug</kbd> option and file a bug report if these
2217      *         exceptions occur.
2218      */
2219     private function _encryptAndSign($data, $isFile, $outputFile, $armor)
2220     {
2221         if (count($this->signKeys) === 0) {
2222             throw new Crypt_GPG_KeyNotFoundException(
2223                 'No signing keys specified.');
2224         }
2225
2226         if (count($this->encryptKeys) === 0) {
2227             throw new Crypt_GPG_KeyNotFoundException(
2228                 'No encryption keys specified.');
2229         }
2230
2231
2232         if ($isFile) {
2233             $input = @fopen($data, 'rb');
2234             if ($input === false) {
2235                 throw new Crypt_GPG_FileException('Could not open input ' .
2236                     'file "' . $data . '" for encrypting and signing.', 0,
2237                     $data);
2238             }
2239         } else {
2240             $input = strval($data);
2241         }
2242
2243         if ($outputFile === null) {
2244             $output = '';
2245         } else {
2246             $output = @fopen($outputFile, 'wb');
2247             if ($output === false) {
2248                 if ($isFile) {
2249                     fclose($input);
2250                 }
2251                 throw new Crypt_GPG_FileException('Could not open output ' .
2252                     'file "' . $outputFile . '" for storing encrypted, ' .
2253                     'signed data.', 0, $outputFile);
2254             }
2255         }
2256
2257         $arguments  = ($armor) ? array('--armor') : array();
2258
2259         foreach ($this->signKeys as $key) {
2260             $arguments[] = '--local-user ' .
2261                 escapeshellarg($key['fingerprint']);
2262         }
2263
2264         foreach ($this->encryptKeys as $key) {
2265             $arguments[] = '--recipient ' . escapeshellarg($key['fingerprint']);
2266         }
2267
2268         $this->engine->reset();
2269         $this->engine->addStatusHandler(array($this, 'handleSignStatus'));
2270         $this->engine->setInput($input);
2271         $this->engine->setOutput($output);
2272         $this->engine->setOperation('--encrypt --sign', $arguments);
2273         $this->engine->run();
2274
2275         if ($isFile) {
2276             fclose($input);
2277         }
2278
2279         if ($outputFile !== null) {
2280             fclose($output);
2281         }
2282
2283         $code = $this->engine->getErrorCode();
2284
2285         switch ($code) {
2286         case Crypt_GPG::ERROR_NONE:
2287             break;
2288         case Crypt_GPG::ERROR_KEY_NOT_FOUND:
2289             throw new Crypt_GPG_KeyNotFoundException(
2290                 'Cannot sign encrypted data. Private key not found. Import '.
2291                 'the private key before trying to sign the encrypted data.',
2292                 $code, $this->engine->getErrorKeyId());
2293         case Crypt_GPG::ERROR_BAD_PASSPHRASE:
2294             throw new Crypt_GPG_BadPassphraseException(
2295                 'Cannot sign encrypted data. Incorrect passphrase provided.',
2296                 $code);
2297         case Crypt_GPG::ERROR_MISSING_PASSPHRASE:
2298             throw new Crypt_GPG_BadPassphraseException(
2299                 'Cannot sign encrypted data. No passphrase provided.', $code);
2300         default:
2301             throw new Crypt_GPG_Exception(
2302                 'Unknown error encrypting and signing data. Please use the ' .
2303                 '\'debug\' option when creating the Crypt_GPG object, and ' .
2304                 'file a bug report at ' . self::BUG_URI, $code);
2305         }
2306
2307         if ($outputFile === null) {
2308             return $output;
2309         }
2310     }
2311
2312     // }}}
2313     // {{{ _verify()
2314
2315     /**
2316      * Verifies data
2317      *
2318      * @param string  $data      the signed data to be verified.
2319      * @param boolean $isFile    whether or not the data is a filename.
2320      * @param string  $signature if verifying a file signed using a detached
2321      *                           signature, this must be the detached signature
2322      *                           data. Otherwise, specify ''.
2323      *
2324      * @return array an array of {@link Crypt_GPG_Signature} objects for the
2325      *               signed data.
2326      *
2327      * @throws Crypt_GPG_NoDataException if the provided data is not signed
2328      *         data.
2329      *
2330      * @throws Crypt_GPG_FileException if the input file is not readable.
2331      *
2332      * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs.
2333      *         Use the <kbd>debug</kbd> option and file a bug report if these
2334      *         exceptions occur.
2335      *
2336      * @see Crypt_GPG_Signature
2337      */
2338     private function _verify($data, $isFile, $signature)
2339     {
2340         if ($signature == '') {
2341             $operation = '--verify';
2342             $arguments = array();
2343         } else {
2344             // Signed data goes in FD_MESSAGE, detached signature data goes in
2345             // FD_INPUT.
2346             $operation = '--verify - "-&' . Crypt_GPG_Engine::FD_MESSAGE. '"';
2347             $arguments = array('--enable-special-filenames');
2348         }
2349
2350         $handler = new Crypt_GPG_VerifyStatusHandler();
2351
2352         if ($isFile) {
2353             $input = @fopen($data, 'rb');
2354             if ($input === false) {
2355                 throw new Crypt_GPG_FileException('Could not open input ' .
2356                     'file "' . $data . '" for verifying.', 0, $data);
2357             }
2358         } else {
2359             $input = strval($data);
2360             if ($input == '') {
2361                 throw new Crypt_GPG_NoDataException(
2362                     'No valid signature data found.', Crypt_GPG::ERROR_NO_DATA);
2363             }
2364         }
2365
2366         $this->engine->reset();
2367         $this->engine->addStatusHandler(array($handler, 'handle'));
2368
2369         if ($signature == '') {
2370             // signed or clearsigned data
2371             $this->engine->setInput($input);
2372         } else {
2373             // detached signature
2374             $this->engine->setInput($signature);
2375             $this->engine->setMessage($input);
2376         }
2377
2378         $this->engine->setOperation($operation, $arguments);
2379         $this->engine->run();
2380
2381         if ($isFile) {
2382             fclose($input);
2383         }
2384
2385         $code = $this->engine->getErrorCode();
2386
2387         switch ($code) {
2388         case Crypt_GPG::ERROR_NONE:
2389         case Crypt_GPG::ERROR_BAD_SIGNATURE:
2390             break;
2391         case Crypt_GPG::ERROR_NO_DATA:
2392             throw new Crypt_GPG_NoDataException(
2393                 'No valid signature data found.', $code);
2394         case Crypt_GPG::ERROR_KEY_NOT_FOUND:
2395             throw new Crypt_GPG_KeyNotFoundException(
2396                 'Public key required for data verification not in keyring.',
2397                 $code, $this->engine->getErrorKeyId());
2398         default:
2399             throw new Crypt_GPG_Exception(
2400                 'Unknown error validating signature details. Please use the ' .
2401                 '\'debug\' option when creating the Crypt_GPG object, and ' .
2402                 'file a bug report at ' . self::BUG_URI, $code);
2403         }
2404
2405         return $handler->getSignatures();
2406     }
2407
2408     // }}}
2409     // {{{ _decryptAndVerify()
2410
2411     /**
2412      * Decrypts and verifies encrypted, signed data
2413      *
2414      * @param string  $data       the encrypted signed data to be decrypted and
2415      *                            verified.
2416      * @param boolean $isFile     whether or not the data is a filename.
2417      * @param string  $outputFile the name of the file to which the decrypted
2418      *                            data should be written. If null, the decrypted
2419      *                            data is returned in the results array.
2420      *
2421      * @return array two element array. The array has an element 'data'
2422      *               containing the decrypted data and an element
2423      *               'signatures' containing an array of
2424      *               {@link Crypt_GPG_Signature} objects for the signed data.
2425      *               If the decrypted data is written to a file, the 'data'
2426      *               element is null.
2427      *
2428      * @throws Crypt_GPG_KeyNotFoundException if the private key needed to
2429      *         decrypt the data is not in the user's keyring or it the public
2430      *         key needed for verification is not in the user's keyring.
2431      *
2432      * @throws Crypt_GPG_NoDataException if specified data does not contain
2433      *         GPG signed, encrypted data.
2434      *
2435      * @throws Crypt_GPG_BadPassphraseException if a required passphrase is
2436      *         incorrect or if a required passphrase is not specified. See
2437      *         {@link Crypt_GPG::addDecryptKey()}.
2438      *
2439      * @throws Crypt_GPG_FileException if the output file is not writeable or
2440      *         if the input file is not readable.
2441      *
2442      * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs.
2443      *         Use the <kbd>debug</kbd> option and file a bug report if these
2444      *         exceptions occur.
2445      *
2446      * @see Crypt_GPG_Signature
2447      */
2448     private function _decryptAndVerify($data, $isFile, $outputFile)
2449     {
2450         if ($isFile) {
2451             $input = @fopen($data, 'rb');
2452             if ($input === false) {
2453                 throw new Crypt_GPG_FileException('Could not open input ' .
2454                     'file "' . $data . '" for decrypting and verifying.', 0,
2455                     $data);
2456             }
2457         } else {
2458             $input = strval($data);
2459             if ($input == '') {
2460                 throw new Crypt_GPG_NoDataException(
2461                     'No valid encrypted signed data found.',
2462                     Crypt_GPG::ERROR_NO_DATA);
2463             }
2464         }
2465
2466         if ($outputFile === null) {
2467             $output = '';
2468         } else {
2469             $output = @fopen($outputFile, 'wb');
2470             if ($output === false) {
2471                 if ($isFile) {
2472                     fclose($input);
2473                 }
2474                 throw new Crypt_GPG_FileException('Could not open output ' .
2475                     'file "' . $outputFile . '" for storing decrypted data.',
2476                     0, $outputFile);
2477             }
2478         }
2479
2480         $verifyHandler = new Crypt_GPG_VerifyStatusHandler();
2481
2482         $decryptHandler = new Crypt_GPG_DecryptStatusHandler($this->engine,
2483             $this->decryptKeys);
2484
2485         $this->engine->reset();
2486         $this->engine->addStatusHandler(array($verifyHandler, 'handle'));
2487         $this->engine->addStatusHandler(array($decryptHandler, 'handle'));
2488         $this->engine->setInput($input);
2489         $this->engine->setOutput($output);
2490         $this->engine->setOperation('--decrypt');
2491         $this->engine->run();
2492
2493         if ($isFile) {
2494             fclose($input);
2495         }
2496
2497         if ($outputFile !== null) {
2498             fclose($output);
2499         }
2500
2501         $return = array(
2502             'data'       => null,
2503             'signatures' => $verifyHandler->getSignatures()
2504         );
2505
2506         // if there was any problem decrypting the data, the handler will
2507         // deal with it here.
2508         try {
2509             $decryptHandler->throwException();
2510         } catch (Exception $e) {
2511             if ($e instanceof Crypt_GPG_KeyNotFoundException) {
2512                 throw new Crypt_GPG_KeyNotFoundException(
2513                     'Public key required for data verification not in ',
2514                     'the keyring. Either no suitable private decryption key ' .
2515                     'is in the keyring or the public key required for data ' .
2516                     'verification is not in the keyring. Import a suitable ' .
2517                     'key before trying to decrypt and verify this data.',
2518                     self::ERROR_KEY_NOT_FOUND, $this->engine->getErrorKeyId());
2519             }
2520
2521             if ($e instanceof Crypt_GPG_NoDataException) {
2522                 throw new Crypt_GPG_NoDataException(
2523                     'Cannot decrypt and verify data. No PGP encrypted data ' .
2524                     'was found in the provided data.', self::ERROR_NO_DATA);
2525             }
2526
2527             throw $e;
2528         }
2529
2530         if ($outputFile === null) {
2531             $return['data'] = $output;
2532         }
2533
2534         return $return;
2535     }
2536
2537     // }}}
2538 }
2539
2540 // }}}
2541
2542 ?>