PageRenderTime 6ms CodeModel.GetById 110ms app.highlight 37ms RepoModel.GetById 1ms app.codeStats 1ms

/mail/plugins/enigma/lib/Crypt/GPG.php

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