/**
  * Handles a status line.
  *
  * @param string $line the status line to handle.
  */
 public function handle($line)
 {
     $tokens = explode(' ', $line);
     switch ($tokens[0]) {
         case 'ENC_TO':
             // Now we know the message is encrypted. Set flag to check if
             // decryption succeeded.
             $this->decryptionOkay = false;
             // this is the new key message
             $this->currentSubKeyId = $tokens[1];
             break;
         case 'NEED_PASSPHRASE':
             // send passphrase to the GPG engine
             $subKeyId = $tokens[1];
             if (array_key_exists($subKeyId, $this->keys)) {
                 $passphrase = $this->keys[$subKeyId]['passphrase'];
                 $this->engine->sendCommand($passphrase);
             } else {
                 $this->engine->sendCommand('');
             }
             break;
         case 'USERID_HINT':
             // remember the user id for pretty exception messages
             $this->badPassphrases[$tokens[1]] = implode(' ', array_splice($tokens, 2));
             break;
         case 'GOOD_PASSPHRASE':
             // if we got a good passphrase, remove the key from the list of
             // bad passphrases.
             unset($this->badPassphrases[$this->currentSubKeyId]);
             break;
         case 'MISSING_PASSPHRASE':
             $this->missingPassphrases[$this->currentSubKeyId] = $this->currentSubKeyId;
             break;
         case 'NO_SECKEY':
             // note: this message is also received if there are multiple
             // recipients and a previous key had a correct passphrase.
             $this->missingKeys[$tokens[1]] = $tokens[1];
             break;
         case 'NODATA':
             $this->noData = true;
             break;
         case 'DECRYPTION_OKAY':
             // If the message is encrypted, this is the all-clear signal.
             $this->decryptionOkay = true;
             break;
     }
 }
Exemple #2
0
 /**
  * Handles error values in the status output from GPG
  *
  * This method is responsible for setting the
  * {@link self::$errorCode}. See <b>doc/DETAILS</b> in the
  * {@link http://www.gnupg.org/download/ GPG distribution} for detailed
  * information on GPG's status output.
  *
  * @param string $line the status line to handle.
  *
  * @return void
  */
 public function handleStatus($line)
 {
     $tokens = explode(' ', $line);
     switch ($tokens[0]) {
         case 'NODATA':
             $this->errorCode = Crypt_GPG::ERROR_NO_DATA;
             break;
         case 'DECRYPTION_OKAY':
             // If the message is encrypted, this is the all-clear signal.
             $this->data['DecryptionOkay'] = true;
             $this->errorCode = Crypt_GPG::ERROR_NONE;
             break;
         case 'DELETE_PROBLEM':
             if ($tokens[1] == '1') {
                 $this->errorCode = Crypt_GPG::ERROR_KEY_NOT_FOUND;
                 break;
             } elseif ($tokens[1] == '2') {
                 $this->errorCode = Crypt_GPG::ERROR_DELETE_PRIVATE_KEY;
                 break;
             }
             break;
         case 'IMPORT_OK':
             $this->data['Import']['fingerprint'] = $tokens[2];
             if (empty($this->data['Import']['fingerprints'])) {
                 $this->data['Import']['fingerprints'] = array($tokens[2]);
             } else {
                 if (!in_array($tokens[2], $this->data['Import']['fingerprints'])) {
                     $this->data['Import']['fingerprints'][] = $tokens[2];
                 }
             }
             break;
         case 'IMPORT_RES':
             $this->data['Import']['public_imported'] = intval($tokens[3]);
             $this->data['Import']['public_unchanged'] = intval($tokens[5]);
             $this->data['Import']['private_imported'] = intval($tokens[11]);
             $this->data['Import']['private_unchanged'] = intval($tokens[12]);
             break;
         case 'NO_PUBKEY':
         case 'NO_SECKEY':
             $this->data['ErrorKeyId'] = $tokens[1];
             if ($this->errorCode != Crypt_GPG::ERROR_MISSING_PASSPHRASE && $this->errorCode != Crypt_GPG::ERROR_BAD_PASSPHRASE) {
                 $this->errorCode = Crypt_GPG::ERROR_KEY_NOT_FOUND;
             }
             // note: this message is also received if there are multiple
             // recipients and a previous key had a correct passphrase.
             $this->data['MissingKeys'][$tokens[1]] = $tokens[1];
             // @FIXME: remove missing passphrase registered in ENC_TO handler
             //         This is for GnuPG 2.1
             unset($this->data['MissingPassphrases'][$tokens[1]]);
             break;
         case 'KEY_CONSIDERED':
             // In GnuPG 2.1.x exporting/importing a secret key requires passphrase
             // However, no NEED_PASSPRASE is returned, https://bugs.gnupg.org/gnupg/issue2667
             // So, handling KEY_CONSIDERED and GET_HIDDEN is needed.
             if (!array_key_exists('KeyConsidered', $this->data)) {
                 $this->data['KeyConsidered'] = $tokens[1];
             }
             break;
         case 'USERID_HINT':
             // remember the user id for pretty exception messages
             // GnuPG 2.1.15 gives me: "USERID_HINT 0000000000000000 [?]"
             $keyId = $tokens[1];
             if (strcspn($keyId, '0')) {
                 $username = implode(' ', array_splice($tokens, 2));
                 $this->data['BadPassphrases'][$keyId] = $username;
             }
             break;
         case 'ENC_TO':
             // Now we know the message is encrypted. Set flag to check if
             // decryption succeeded.
             $this->data['DecryptionOkay'] = false;
             // this is the new key message
             $this->data['CurrentSubKeyId'] = $keyId = $tokens[1];
             // For some reason in GnuPG 2.1.11 I get only ENC_TO and no
             // NEED_PASSPHRASE/MISSING_PASSPHRASE/USERID_HINT
             // This is not needed for GnuPG 2.1.15
             if (!empty($_ENV['PINENTRY_USER_DATA'])) {
                 $passphrases = json_decode($_ENV['PINENTRY_USER_DATA'], true);
             } else {
                 $passphrases = array();
             }
             // @TODO: Get user name/email
             $this->data['BadPassphrases'][$keyId] = $keyId;
             if (empty($passphrases) || empty($passphrases[$keyId])) {
                 $this->data['MissingPassphrases'][$keyId] = $keyId;
             }
             break;
         case 'GOOD_PASSPHRASE':
             // if we got a good passphrase, remove the key from the list of
             // bad passphrases.
             if (isset($this->data['CurrentSubKeyId'])) {
                 unset($this->data['BadPassphrases'][$this->data['CurrentSubKeyId']]);
                 unset($this->data['MissingPassphrases'][$this->data['CurrentSubKeyId']]);
             }
             $this->needPassphrase--;
             break;
         case 'BAD_PASSPHRASE':
             $this->errorCode = Crypt_GPG::ERROR_BAD_PASSPHRASE;
             break;
         case 'MISSING_PASSPHRASE':
             if (isset($this->data['CurrentSubKeyId'])) {
                 $this->data['MissingPassphrases'][$this->data['CurrentSubKeyId']] = $this->data['CurrentSubKeyId'];
             }
             $this->errorCode = Crypt_GPG::ERROR_MISSING_PASSPHRASE;
             break;
         case 'GET_HIDDEN':
             if ($tokens[1] == 'passphrase.enter' && isset($this->data['KeyConsidered'])) {
                 $tokens[1] = $this->data['KeyConsidered'];
             } else {
                 break;
             }
             // no break
         // no break
         case 'NEED_PASSPHRASE':
             $passphrase = $this->getPin($tokens[1]);
             $this->engine->sendCommand($passphrase);
             if ($passphrase === '') {
                 $this->needPassphrase++;
             }
             break;
         case 'SIG_CREATED':
             $this->data['SigCreated'] = $line;
             break;
         case 'SIG_ID':
             // note: signature id comes before new signature line and may not
             // exist for some signature types
             $this->data['SignatureId'] = $tokens[1];
             break;
         case 'EXPSIG':
         case 'EXPKEYSIG':
         case 'REVKEYSIG':
         case 'BADSIG':
         case 'ERRSIG':
             $this->errorCode = Crypt_GPG::ERROR_BAD_SIGNATURE;
             // no break
         // no break
         case 'GOODSIG':
             $signature = new Crypt_GPG_Signature();
             // if there was a signature id, set it on the new signature
             if (!empty($this->data['SignatureId'])) {
                 $signature->setId($this->data['SignatureId']);
                 $this->data['SignatureId'] = '';
             }
             // Detect whether fingerprint or key id was returned and set
             // signature values appropriately. Key ids are strings of either
             // 16 or 8 hexadecimal characters. Fingerprints are strings of 40
             // hexadecimal characters. The key id is the last 16 characters of
             // the key fingerprint.
             if (mb_strlen($tokens[1], '8bit') > 16) {
                 $signature->setKeyFingerprint($tokens[1]);
                 $signature->setKeyId(mb_substr($tokens[1], -16, null, '8bit'));
             } else {
                 $signature->setKeyId($tokens[1]);
             }
             // get user id string
             if ($tokens[0] != 'ERRSIG') {
                 $string = implode(' ', array_splice($tokens, 2));
                 $string = rawurldecode($string);
                 $signature->setUserId(Crypt_GPG_UserId::parse($string));
             }
             $this->data['Signatures'][] = $signature;
             break;
         case 'VALIDSIG':
             if (empty($this->data['Signatures'])) {
                 break;
             }
             $signature = end($this->data['Signatures']);
             $signature->setValid(true);
             $signature->setKeyFingerprint($tokens[1]);
             if (strpos($tokens[3], 'T') === false) {
                 $signature->setCreationDate($tokens[3]);
             } else {
                 $signature->setCreationDate(strtotime($tokens[3]));
             }
             if (array_key_exists(4, $tokens)) {
                 if (strpos($tokens[4], 'T') === false) {
                     $signature->setExpirationDate($tokens[4]);
                 } else {
                     $signature->setExpirationDate(strtotime($tokens[4]));
                 }
             }
             break;
         case 'KEY_CREATED':
             if (isset($this->data['Handle']) && $tokens[3] == $this->data['Handle']) {
                 $this->data['KeyCreated'] = $tokens[2];
             }
             break;
         case 'KEY_NOT_CREATED':
             if (isset($this->data['Handle']) && $tokens[1] == $this->data['Handle']) {
                 $this->errorCode = Crypt_GPG::ERROR_KEY_NOT_CREATED;
             }
             break;
         case 'PROGRESS':
             // todo: at some point, support reporting status async
             break;
             // GnuPG 2.1 uses FAILURE and ERROR responses
         // GnuPG 2.1 uses FAILURE and ERROR responses
         case 'FAILURE':
         case 'ERROR':
             $errnum = (int) $tokens[2];
             $source = $errnum >> 24;
             $errcode = $errnum & 0xffffff;
             switch ($errcode) {
                 case 11:
                     // bad passphrase
                 // bad passphrase
                 case 87:
                     // bad PIN
                     $this->errorCode = Crypt_GPG::ERROR_BAD_PASSPHRASE;
                     break;
                 case 177:
                     // no passphrase
                 // no passphrase
                 case 178:
                     // no PIN
                     $this->errorCode = Crypt_GPG::ERROR_MISSING_PASSPHRASE;
                     break;
                 case 58:
                     $this->errorCode = Crypt_GPG::ERROR_NO_DATA;
                     break;
             }
             break;
     }
 }
Exemple #3
0
 /**
  * Decrypts and verifies encrypted, signed data
  *
  * @param string  $data       the encrypted signed data to be decrypted and
  *                            verified.
  * @param boolean $isFile     whether or not the data is a filename.
  * @param string  $outputFile the name of the file to which the decrypted
  *                            data should be written. If null, the decrypted
  *                            data is returned in the results array.
  *
  * @return array two element array. The array has an element 'data'
  *               containing the decrypted data and an element
  *               'signatures' containing an array of
  *               {@link Crypt_GPG_Signature} objects for the signed data.
  *               If the decrypted data is written to a file, the 'data'
  *               element is null.
  *
  * @throws Crypt_GPG_KeyNotFoundException if the private key needed to
  *         decrypt the data is not in the user's keyring or it the public
  *         key needed for verification is not in the user's keyring.
  *
  * @throws Crypt_GPG_NoDataException if specified data does not contain
  *         GPG signed, encrypted data.
  *
  * @throws Crypt_GPG_BadPassphraseException if a required passphrase is
  *         incorrect or if a required passphrase is not specified. See
  *         {@link Crypt_GPG::addDecryptKey()}.
  *
  * @throws Crypt_GPG_FileException if the output file is not writeable or
  *         if the input file is not readable.
  *
  * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs.
  *         Use the <kbd>debug</kbd> option and file a bug report if these
  *         exceptions occur.
  *
  * @see Crypt_GPG_Signature
  */
 protected function _decryptAndVerify($data, $isFile, $outputFile)
 {
     if ($isFile) {
         $input = @fopen($data, 'rb');
         if ($input === false) {
             throw new Crypt_GPG_FileException('Could not open input file "' . $data . '" for decrypting and verifying.', 0, $data);
         }
     } else {
         $input = strval($data);
         if ($input == '') {
             throw new Crypt_GPG_NoDataException('No valid encrypted signed data found.', self::ERROR_NO_DATA);
         }
     }
     if ($outputFile === null) {
         $output = '';
     } else {
         $output = @fopen($outputFile, 'wb');
         if ($output === false) {
             if ($isFile) {
                 fclose($input);
             }
             throw new Crypt_GPG_FileException('Could not open output file "' . $outputFile . '" for storing decrypted data.', 0, $outputFile);
         }
     }
     $verifyHandler = new Crypt_GPG_VerifyStatusHandler();
     $decryptHandler = new Crypt_GPG_DecryptStatusHandler($this->engine, $this->decryptKeys);
     // If using gpg-agent, set the decrypt pins used by the pinentry
     $this->_setPinEntryEnv($this->decryptKeys);
     $this->engine->reset();
     $this->engine->addStatusHandler(array($verifyHandler, 'handle'));
     $this->engine->addStatusHandler(array($decryptHandler, 'handle'));
     $this->engine->setInput($input);
     $this->engine->setOutput($output);
     $this->engine->setOperation('--decrypt');
     $this->engine->run();
     if ($isFile) {
         fclose($input);
     }
     if ($outputFile !== null) {
         fclose($output);
     }
     $return = array('data' => null, 'signatures' => $verifyHandler->getSignatures());
     // if there was any problem decrypting the data, the handler will
     // deal with it here.
     try {
         $decryptHandler->throwException();
     } catch (Exception $e) {
         if ($e instanceof Crypt_GPG_KeyNotFoundException) {
             throw new Crypt_GPG_KeyNotFoundException('Public key required for data verification not in ', 'the keyring. Either no suitable private decryption key ' . 'is in the keyring or the public key required for data ' . 'verification is not in the keyring. Import a suitable ' . 'key before trying to decrypt and verify this data.', self::ERROR_KEY_NOT_FOUND, $this->engine->getErrorKeyId());
         }
         if ($e instanceof Crypt_GPG_NoDataException) {
             throw new Crypt_GPG_NoDataException('Cannot decrypt and verify data. No PGP encrypted data ' . 'was found in the provided data.', self::ERROR_NO_DATA);
         }
         throw $e;
     }
     if ($outputFile === null) {
         $return['data'] = $output;
     }
     return $return;
 }
Exemple #4
0
 /**
  * Creates a new GPG engine
  *
  * Available options are:
  *
  * - <kbd>string  homedir</kbd>        - the directory where the GPG
  *                                       keyring files are stored. If not
  *                                       specified, Crypt_GPG uses the
  *                                       default of <kbd>~/.gnupg</kbd>.
  * - <kbd>string  publicKeyring</kbd>  - 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 <i>homedir</i> option.
  * - <kbd>string  privateKeyring</kbd> - 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 <i>homedir</i> option.
  * - <kbd>string  trustDb</kbd>        - 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 <i>homedir</i> option.
  * - <kbd>string  binary</kbd>         - 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
  *                                       <kbd>gpgBinary</kbd> is a
  *                                       deprecated alias for this option.
  * - <kbd>boolean debug</kbd>          - 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
  *                                       useful to diagnose errors when
  *                                       using Crypt_GPG.
  *
  * @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 <kbd>homedir</kbd> does not exist
  *         and cannot be created. This can happen if <kbd>homedir</kbd> 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 <kbd>publicKeyring</kbd>,
  *         <kbd>privateKeyring</kbd> or <kbd>trustDb</kbd> 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 <kbd>binary</kbd> is invalid, or
  *         if no <kbd>binary</kbd> is provided and no suitable binary could
  *         be found.
  */
 public function __construct(array $options = array())
 {
     $this->_isDarwin = strncmp(strtoupper(PHP_OS), 'DARWIN', 6) === 0;
     // populate mbstring overloading cache if not set
     if (self::$_mbStringOverload === null) {
         self::$_mbStringOverload = extension_loaded('mbstring') && (ini_get('mbstring.func_overload') & 0x2) === 0x2;
     }
     // get homedir
     if (array_key_exists('homedir', $options)) {
         $this->_homedir = (string) $options['homedir'];
     } else {
         // note: this requires the package OS dep exclude 'windows'
         $info = posix_getpwuid(posix_getuid());
         $this->_homedir = $info['dir'] . '/.gnupg';
     }
     // attempt to create homedir if it does not exist
     if (!is_dir($this->_homedir)) {
         if (@mkdir($this->_homedir, 0777, true)) {
             // Set permissions on homedir. Parent directories are created
             // with 0777, homedir is set to 0700.
             chmod($this->_homedir, 0700);
         } else {
             throw new Crypt_GPG_FileException('The \'homedir\' "' . $this->_homedir . '" is not readable or does not exist ' . 'and cannot be created. This can happen if \'homedir\' ' . 'is not specified in the Crypt_GPG options, Crypt_GPG is ' . 'run as the web user, and the web user has no home ' . 'directory.', 0, $this->_homedir);
         }
     }
     // get binary
     if (array_key_exists('binary', $options)) {
         $this->_binary = (string) $options['binary'];
     } elseif (array_key_exists('gpgBinary', $options)) {
         // deprecated alias
         $this->_binary = (string) $options['gpgBinary'];
     } else {
         $this->_binary = $this->_getBinary();
     }
     if ($this->_binary == '' || !is_executable($this->_binary)) {
         throw new PEAR_Exception('GPG binary not found. If you are sure ' . 'the GPG binary is installed, please specify the location of ' . 'the GPG binary using the \'binary\' driver option.');
     }
     /*
      * Note:
      *
      * Normally, GnuPG expects keyrings to be in the homedir and expects
      * to be able to write temporary files in the homedir. Sometimes,
      * keyrings are not in the homedir, or location of the keyrings does
      * not allow writing temporary files. In this case, the <i>homedir</i>
      * option by itself is not enough to specify the keyrings because GnuPG
      * can not write required temporary files. Additional options are
      * provided so you can specify the location of the keyrings separately
      * from the homedir.
      */
     // get public keyring
     if (array_key_exists('publicKeyring', $options)) {
         $this->_publicKeyring = (string) $options['publicKeyring'];
         if (!is_readable($this->_publicKeyring)) {
             throw new Crypt_GPG_FileException('The \'publicKeyring\' "' . $this->_publicKeyring . '" does not exist or is ' . 'not readable. Check the location and ensure the file ' . 'permissions are correct.', 0, $this->_publicKeyring);
         }
     }
     // get private keyring
     if (array_key_exists('privateKeyring', $options)) {
         $this->_privateKeyring = (string) $options['privateKeyring'];
         if (!is_readable($this->_privateKeyring)) {
             throw new Crypt_GPG_FileException('The \'privateKeyring\' "' . $this->_privateKeyring . '" does not exist or is ' . 'not readable. Check the location and ensure the file ' . 'permissions are correct.', 0, $this->_privateKeyring);
         }
     }
     // get trust database
     if (array_key_exists('trustDb', $options)) {
         $this->_trustDb = (string) $options['trustDb'];
         if (!is_readable($this->_trustDb)) {
             throw new Crypt_GPG_FileException('The \'trustDb\' "' . $this->_trustDb . '" does not exist or is not readable. ' . 'Check the location and ensure the file permissions are ' . 'correct.', 0, $this->_trustDb);
         }
     }
     if (array_key_exists('debug', $options)) {
         $this->_debug = (bool) $options['debug'];
     }
 }
Exemple #5
0
 /**
  * Gets the available keys in the keyring.
  *
  * Calls GPG with the <kbd>--list-keys</kbd> command and grabs keys. See
  * the first section of <b>doc/DETAILS</b> 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 <kbd>$keyId</kbd> an empty array is
  *               returned.
  *
  * @throws Exception if an unknown or unexpected error occurs.
  *                   Use the <kbd>debug</kbd> 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 \Exception(sprintf('Error reading GnuPG data file \'%s\'. Check to make ' . 'sure it is readable by the current user.', $filename), $code);
             }
             throw new \Exception('Error reading GnuPG data file. Check to make GnuPG data ' . 'files are readable by the current user.', $code);
         default:
             throw new \Exception('Unknown error getting keys. Please use the \'debug\' option ', $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 \Exception(sprintf('Error reading GnuPG data file \'%s\'. Check to make ' . 'sure it is readable by the current user.', $filename), $code);
             }
             throw new \Exception('Error reading GnuPG data file. Check to make GnuPG data ' . 'files are readable by the current user.', $code);
         default:
             throw new \Exception('Unknown error getting keys. Please use the \'debug\' option ', $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 Key();
             $subKey = self::parseSubKeyLine($line);
             $key->addSubKey($subKey);
         } elseif ($lineExp[0] == 'sub') {
             $subKey = self::parseSubKeyLine($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 = self::parseUserIdLine($string);
             // isRevoked
             if (strpos($lineExp[1], 'r') !== false) {
                 $userId->setIsRevoked(true);
             }
             // isValid
             if (strpos($lineExp[1], 'i') !== false) {
                 $userId->setIsValid(false);
             }
             // isValid
             if (strpos($lineExp[1], 'd') !== false) {
                 $userId->setDisabled(true);
             }
             // validity
             if (strpos($lineExp[1], 'n') !== false) {
                 //NEVER
             } elseif (strpos($lineExp[1], 'm') !== false) {
                 //MARGINAL
             } elseif (strpos($lineExp[1], 'f') !== false) {
                 //FULL
             } elseif (strpos($lineExp[1], 'u') !== false) {
                 //ULTIMATE
             }
             $key->addUserId($userId);
         }
     }
     // add last key
     if ($key !== null) {
         $keys[] = $key;
     }
     return $keys;
 }
Exemple #6
0
 /**
  * Gets the available keys in the keyring
  *
  * Calls GPG with the <kbd>--list-keys</kbd> command and grabs keys. See
  * the first section of <b>doc/DETAILS</b> 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 <kbd>$keyId</kbd> an empty array is
  *               returned.
  *
  * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs.
  *         Use the <kbd>debug</kbd> 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 = '--utf8-strings --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();
     $privateKeyFingerprints = array();
     foreach (explode(PHP_EOL, $output) as $line) {
         $lineExp = explode(':', $line);
         if ($lineExp[0] == 'fpr') {
             $privateKeyFingerprints[] = $lineExp[9];
         }
     }
     // get public keys
     if ($keyId == '') {
         $operation = '--list-public-keys';
     } else {
         $operation = '--utf8-strings --list-public-keys ' . escapeshellarg($keyId);
     }
     $output = '';
     $this->engine->reset();
     $this->engine->setOutput($output);
     $this->engine->setOperation($operation, $arguments);
     $this->engine->run();
     $keys = array();
     $key = null;
     // current key
     $subKey = null;
     // current sub-key
     foreach (explode(PHP_EOL, $output) 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;
 }