~ubuntu-branches/ubuntu/vivid/php-crypt-gpg/vivid

« back to all changes in this revision

Viewing changes to Crypt_GPG/Crypt/GPG.php

  • Committer: Bazaar Package Importer
  • Author(s): Joey Schulze
  • Date: 2010-05-15 20:54:46 UTC
  • mfrom: (1.1.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20100515205446-ubzkt4hr9y5uiokw
Tags: 1.1.0-1
* Change section from web to php to accomplish an override disparity
* New upstream version

Show diffs side-by-side

added added

removed removed

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