From 3e98f8be718578644bb15ee6a992a875f6468e8f Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Fri, 27 Dec 2013 13:14:40 +0100 Subject: Add some code for S/MIME signatures verification, update Crypt_GPG package --- program/lib/Crypt/GPGAbstract.php | 508 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 508 insertions(+) create mode 100644 program/lib/Crypt/GPGAbstract.php (limited to 'program/lib/Crypt/GPGAbstract.php') diff --git a/program/lib/Crypt/GPGAbstract.php b/program/lib/Crypt/GPGAbstract.php new file mode 100644 index 000000000..214133936 --- /dev/null +++ b/program/lib/Crypt/GPGAbstract.php @@ -0,0 +1,508 @@ + + * @author Michael Gauthier + * @copyright 2005-2013 silverorange + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: GPG.php 305428 2010-11-17 02:47:56Z gauthierm $ + * @link http://pear.php.net/package/Crypt_GPG + * @link http://pear.php.net/manual/en/package.encryption.crypt-gpg.php + * @link http://www.gnupg.org/ + */ + +/** + * GPG key class + */ +require_once 'Crypt/GPG/Key.php'; + +/** + * GPG sub-key class + */ +require_once 'Crypt/GPG/SubKey.php'; + +/** + * GPG user id class + */ +require_once 'Crypt/GPG/UserId.php'; + +/** + * GPG process and I/O engine class + */ +require_once 'Crypt/GPG/Engine.php'; + +/** + * GPG exception classes + */ +require_once 'Crypt/GPG/Exceptions.php'; + +// {{{ class Crypt_GPGAbstract + +/** + * Base class for implementing a user of {@link Crypt_GPG_Engine} + * + * @category Encryption + * @package Crypt_GPG + * @author Nathan Fredrickson + * @author Michael Gauthier + * @copyright 2005-2013 silverorange + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @link http://pear.php.net/package/Crypt_GPG + * @link http://www.gnupg.org/ + */ +abstract class Crypt_GPGAbstract +{ + // {{{ class error constants + + /** + * Error code returned when there is no error. + */ + const ERROR_NONE = 0; + + /** + * Error code returned when an unknown or unhandled error occurs. + */ + const ERROR_UNKNOWN = 1; + + /** + * Error code returned when a bad passphrase is used. + */ + const ERROR_BAD_PASSPHRASE = 2; + + /** + * Error code returned when a required passphrase is missing. + */ + const ERROR_MISSING_PASSPHRASE = 3; + + /** + * Error code returned when a key that is already in the keyring is + * imported. + */ + const ERROR_DUPLICATE_KEY = 4; + + /** + * Error code returned the required data is missing for an operation. + * + * This could be missing key data, missing encrypted data or missing + * signature data. + */ + const ERROR_NO_DATA = 5; + + /** + * Error code returned when an unsigned key is used. + */ + const ERROR_UNSIGNED_KEY = 6; + + /** + * Error code returned when a key that is not self-signed is used. + */ + const ERROR_NOT_SELF_SIGNED = 7; + + /** + * Error code returned when a public or private key that is not in the + * keyring is used. + */ + const ERROR_KEY_NOT_FOUND = 8; + + /** + * Error code returned when an attempt to delete public key having a + * private key is made. + */ + const ERROR_DELETE_PRIVATE_KEY = 9; + + /** + * Error code returned when one or more bad signatures are detected. + */ + const ERROR_BAD_SIGNATURE = 10; + + /** + * Error code returned when there is a problem reading GnuPG data files. + */ + const ERROR_FILE_PERMISSIONS = 11; + + /** + * Error code returned when a key could not be created. + */ + const ERROR_KEY_NOT_CREATED = 12; + + /** + * Error code returned when bad key parameters are used during key + * generation. + */ + const ERROR_BAD_KEY_PARAMS = 13; + + // }}} + // {{{ other class constants + + /** + * URI at which package bugs may be reported. + */ + const BUG_URI = 'http://pear.php.net/bugs/report.php?package=Crypt_GPG'; + + // }}} + // {{{ protected class properties + + /** + * Engine used to control the GPG subprocess + * + * @var Crypt_GPG_Engine + * + * @see Crypt_GPGAbstract::setEngine() + */ + protected $engine = null; + + // }}} + // {{{ __construct() + + /** + * Creates a new GPG object + * + * Available options are: + * + * - string homedir - the directory where the GPG + * keyring files are stored. If not + * specified, Crypt_GPG uses the + * default of ~/.gnupg. + * - string publicKeyring - the file path of the public + * keyring. Use this if the public + * keyring is not in the homedir, or + * if the keyring is in a directory + * not writable by the process + * invoking GPG (like Apache). Then + * you can specify the path to the + * keyring with this option + * (/foo/bar/pubring.gpg), and specify + * a writable directory (like /tmp) + * using the homedir option. + * - string privateKeyring - the file path of the private + * keyring. Use this if the private + * keyring is not in the homedir, or + * if the keyring is in a directory + * not writable by the process + * invoking GPG (like Apache). Then + * you can specify the path to the + * keyring with this option + * (/foo/bar/secring.gpg), and specify + * a writable directory (like /tmp) + * using the homedir option. + * - string trustDb - the file path of the web-of-trust + * database. Use this if the trust + * database is not in the homedir, or + * if the database is in a directory + * not writable by the process + * invoking GPG (like Apache). Then + * you can specify the path to the + * trust database with this option + * (/foo/bar/trustdb.gpg), and specify + * a writable directory (like /tmp) + * using the homedir option. + * - string binary - the location of the GPG binary. If + * not specified, the driver attempts + * to auto-detect the GPG binary + * location using a list of known + * default locations for the current + * operating system. The option + * gpgBinary is a + * deprecated alias for this option. + * - string agent - the location of the GnuPG agent + * binary. The gpg-agent is only + * used for GnuPG 2.x. If not + * specified, the engine attempts + * to auto-detect the gpg-agent + * binary location using a list of + * know default locations for the + * current operating system. + * - boolean debug - whether or not to use debug mode. + * When debug mode is on, all + * communication to and from the GPG + * subprocess is logged. This can be + * + * @param array $options optional. An array of options used to create the + * GPG object. All options are optional and are + * represented as key-value pairs. + * + * @throws Crypt_GPG_FileException if the homedir does not exist + * and cannot be created. This can happen if homedir is + * not specified, Crypt_GPG is run as the web user, and the web + * user has no home directory. This exception is also thrown if any + * of the options publicKeyring, + * privateKeyring or trustDb options are + * specified but the files do not exist or are are not readable. + * This can happen if the user running the Crypt_GPG process (for + * example, the Apache user) does not have permission to read the + * files. + * + * @throws PEAR_Exception if the provided binary is invalid, or + * if no binary is provided and no suitable binary could + * be found. + * + * @throws PEAR_Exception if the provided agent is invalid, or + * if no agent is provided and no suitable gpg-agent + * cound be found. + */ + public function __construct(array $options = array()) + { + $this->setEngine(new Crypt_GPG_Engine($options)); + } + + // }}} + // {{{ setEngine() + + /** + * Sets the I/O engine to use for GnuPG operations + * + * Normally this method does not need to be used. It provides a means for + * dependency injection. + * + * @param Crypt_GPG_Engine $engine the engine to use. + * + * @return Crypt_GPGAbstract the current object, for fluent interface. + */ + public function setEngine(Crypt_GPG_Engine $engine) + { + $this->engine = $engine; + return $this; + } + + // }}} + // {{{ _getKeys() + + /** + * Gets the available keys in the keyring + * + * Calls GPG with the --list-keys command and grabs keys. See + * the first section of doc/DETAILS in the + * {@link http://www.gnupg.org/download/ GPG package} for a detailed + * description of how the GPG command output is parsed. + * + * @param string $keyId optional. Only keys with that match the specified + * pattern are returned. The pattern may be part of + * a user id, a key id or a key fingerprint. If not + * specified, all keys are returned. + * + * @return array an array of {@link Crypt_GPG_Key} objects. If no keys + * match the specified $keyId an empty array is + * returned. + * + * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs. + * Use the debug option and file a bug report if these + * exceptions occur. + * + * @see Crypt_GPG_Key + */ + protected function _getKeys($keyId = '') + { + // get private key fingerprints + if ($keyId == '') { + $operation = '--list-secret-keys'; + } else { + $operation = '--list-secret-keys ' . escapeshellarg($keyId); + } + + // According to The file 'doc/DETAILS' in the GnuPG distribution, using + // double '--with-fingerprint' also prints the fingerprint for subkeys. + $arguments = array( + '--with-colons', + '--with-fingerprint', + '--with-fingerprint', + '--fixed-list-mode' + ); + + $output = ''; + + $this->engine->reset(); + $this->engine->setOutput($output); + $this->engine->setOperation($operation, $arguments); + $this->engine->run(); + + $code = $this->engine->getErrorCode(); + + switch ($code) { + case self::ERROR_NONE: + case self::ERROR_KEY_NOT_FOUND: + // ignore not found key errors + break; + case self::ERROR_FILE_PERMISSIONS: + $filename = $this->engine->getErrorFilename(); + if ($filename) { + throw new Crypt_GPG_FileException( + sprintf( + 'Error reading GnuPG data file \'%s\'. Check to make ' . + 'sure it is readable by the current user.', + $filename + ), + $code, + $filename + ); + } + throw new Crypt_GPG_FileException( + 'Error reading GnuPG data file. Check to make GnuPG data ' . + 'files are readable by the current user.', + $code + ); + default: + throw new Crypt_GPG_Exception( + 'Unknown error getting keys. Please use the \'debug\' option ' . + 'when creating the Crypt_GPG object, and file a bug report ' . + 'at ' . self::BUG_URI, + $code + ); + } + + $privateKeyFingerprints = array(); + + $lines = explode(PHP_EOL, $output); + foreach ($lines as $line) { + $lineExp = explode(':', $line); + if ($lineExp[0] == 'fpr') { + $privateKeyFingerprints[] = $lineExp[9]; + } + } + + // get public keys + if ($keyId == '') { + $operation = '--list-public-keys'; + } else { + $operation = '--list-public-keys ' . escapeshellarg($keyId); + } + + $output = ''; + + $this->engine->reset(); + $this->engine->setOutput($output); + $this->engine->setOperation($operation, $arguments); + $this->engine->run(); + + $code = $this->engine->getErrorCode(); + + switch ($code) { + case self::ERROR_NONE: + case self::ERROR_KEY_NOT_FOUND: + // ignore not found key errors + break; + case self::ERROR_FILE_PERMISSIONS: + $filename = $this->engine->getErrorFilename(); + if ($filename) { + throw new Crypt_GPG_FileException( + sprintf( + 'Error reading GnuPG data file \'%s\'. Check to make ' . + 'sure it is readable by the current user.', + $filename + ), + $code, + $filename + ); + } + throw new Crypt_GPG_FileException( + 'Error reading GnuPG data file. Check to make GnuPG data ' . + 'files are readable by the current user.', + $code + ); + default: + throw new Crypt_GPG_Exception( + 'Unknown error getting keys. Please use the \'debug\' option ' . + 'when creating the Crypt_GPG object, and file a bug report ' . + 'at ' . self::BUG_URI, + $code + ); + } + + $keys = array(); + + $key = null; // current key + $subKey = null; // current sub-key + + $lines = explode(PHP_EOL, $output); + foreach ($lines as $line) { + $lineExp = explode(':', $line); + + if ($lineExp[0] == 'pub') { + + // new primary key means last key should be added to the array + if ($key !== null) { + $keys[] = $key; + } + + $key = new Crypt_GPG_Key(); + + $subKey = Crypt_GPG_SubKey::parse($line); + $key->addSubKey($subKey); + + } elseif ($lineExp[0] == 'sub') { + + $subKey = Crypt_GPG_SubKey::parse($line); + $key->addSubKey($subKey); + + } elseif ($lineExp[0] == 'fpr') { + + $fingerprint = $lineExp[9]; + + // set current sub-key fingerprint + $subKey->setFingerprint($fingerprint); + + // if private key exists, set has private to true + if (in_array($fingerprint, $privateKeyFingerprints)) { + $subKey->setHasPrivate(true); + } + + } elseif ($lineExp[0] == 'uid') { + + $string = stripcslashes($lineExp[9]); // as per documentation + $userId = new Crypt_GPG_UserId($string); + + if ($lineExp[1] == 'r') { + $userId->setRevoked(true); + } + + $key->addUserId($userId); + + } + } + + // add last key + if ($key !== null) { + $keys[] = $key; + } + + return $keys; + } + + // }}} +} + +// }}} + +?> -- cgit v1.2.3