/** * Verifies data * * @param string $data the signed data to be verified. * @param boolean $isFile whether or not the data is a filename. * @param string $signature if verifying a file signed using a detached * signature, this must be the detached signature * data. Otherwise, specify ''. * * @return array an array of {@link Crypt_GPG_Signature} objects for the * signed data. * * @throws Crypt_GPG_NoDataException if the provided data is not signed * data. * * @throws Crypt_GPG_FileException 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 _verify($data, $isFile, $signature) { if ($signature == '') { $operation = '--verify'; $arguments = array(); } else { // Signed data goes in FD_MESSAGE, detached signature data goes in // FD_INPUT. $operation = '--verify - "-&' . Crypt_GPG_Engine::FD_MESSAGE . '"'; $arguments = array('--enable-special-filenames'); } $handler = new Crypt_GPG_VerifyStatusHandler(); if ($isFile) { $input = @fopen($data, 'rb'); if ($input === false) { throw new Crypt_GPG_FileException('Could not open input file "' . $data . '" for verifying.', 0, $data); } } else { $input = strval($data); if ($input == '') { throw new Crypt_GPG_NoDataException('No valid signature data found.', self::ERROR_NO_DATA); } } $this->engine->reset(); $this->engine->addStatusHandler(array($handler, 'handle')); if ($signature == '') { // signed or clearsigned data $this->engine->setInput($input); } else { // detached signature $this->engine->setInput($signature); $this->engine->setMessage($input); } $this->engine->setOperation($operation, $arguments); $this->engine->run(); if ($isFile) { fclose($input); } $code = $this->engine->getErrorCode(); switch ($code) { case self::ERROR_NONE: case self::ERROR_BAD_SIGNATURE: break; case self::ERROR_NO_DATA: throw new Crypt_GPG_NoDataException('No valid signature data found.', $code); case self::ERROR_KEY_NOT_FOUND: throw new Crypt_GPG_KeyNotFoundException('Public key required for data verification not in keyring.', $code, $this->engine->getErrorKeyId()); default: throw new Crypt_GPG_Exception('Unknown error validating signature details. Please use the ' . '\'debug\' option when creating the Crypt_GPG object, and ' . 'file a bug report at ' . self::BUG_URI, $code); } return $handler->getSignatures(); }
/** * 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; }