Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

GPGAbstract.php 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508
  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  3. /**
  4. * Crypt_GPG is a package to use GPG from PHP
  5. *
  6. * This package provides an object oriented interface to GNU Privacy
  7. * Guard (GPG). It requires the GPG executable to be on the system.
  8. *
  9. * Though GPG can support symmetric-key cryptography, this package is intended
  10. * only to facilitate public-key cryptography.
  11. *
  12. * This file contains an abstract implementation of a user of the
  13. * {@link Crypt_GPG_Engine} class.
  14. *
  15. * PHP version 5
  16. *
  17. * LICENSE:
  18. *
  19. * This library is free software; you can redistribute it and/or modify
  20. * it under the terms of the GNU Lesser General Public License as
  21. * published by the Free Software Foundation; either version 2.1 of the
  22. * License, or (at your option) any later version.
  23. *
  24. * This library is distributed in the hope that it will be useful,
  25. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  26. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  27. * Lesser General Public License for more details.
  28. *
  29. * You should have received a copy of the GNU Lesser General Public
  30. * License along with this library; if not, see
  31. * <http://www.gnu.org/licenses/>
  32. *
  33. * @category Encryption
  34. * @package Crypt_GPG
  35. * @author Nathan Fredrickson <nathan@silverorange.com>
  36. * @author Michael Gauthier <mike@silverorange.com>
  37. * @copyright 2005-2013 silverorange
  38. * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
  39. * @version CVS: $Id: GPG.php 305428 2010-11-17 02:47:56Z gauthierm $
  40. * @link http://pear.php.net/package/Crypt_GPG
  41. * @link http://pear.php.net/manual/en/package.encryption.crypt-gpg.php
  42. * @link http://www.gnupg.org/
  43. */
  44. /**
  45. * GPG key class
  46. */
  47. require_once 'Crypt/GPG/Key.php';
  48. /**
  49. * GPG sub-key class
  50. */
  51. require_once 'Crypt/GPG/SubKey.php';
  52. /**
  53. * GPG user id class
  54. */
  55. require_once 'Crypt/GPG/UserId.php';
  56. /**
  57. * GPG process and I/O engine class
  58. */
  59. require_once 'Crypt/GPG/Engine.php';
  60. /**
  61. * GPG exception classes
  62. */
  63. require_once 'Crypt/GPG/Exceptions.php';
  64. // {{{ class Crypt_GPGAbstract
  65. /**
  66. * Base class for implementing a user of {@link Crypt_GPG_Engine}
  67. *
  68. * @category Encryption
  69. * @package Crypt_GPG
  70. * @author Nathan Fredrickson <nathan@silverorange.com>
  71. * @author Michael Gauthier <mike@silverorange.com>
  72. * @copyright 2005-2013 silverorange
  73. * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
  74. * @link http://pear.php.net/package/Crypt_GPG
  75. * @link http://www.gnupg.org/
  76. */
  77. abstract class Crypt_GPGAbstract
  78. {
  79. // {{{ class error constants
  80. /**
  81. * Error code returned when there is no error.
  82. */
  83. const ERROR_NONE = 0;
  84. /**
  85. * Error code returned when an unknown or unhandled error occurs.
  86. */
  87. const ERROR_UNKNOWN = 1;
  88. /**
  89. * Error code returned when a bad passphrase is used.
  90. */
  91. const ERROR_BAD_PASSPHRASE = 2;
  92. /**
  93. * Error code returned when a required passphrase is missing.
  94. */
  95. const ERROR_MISSING_PASSPHRASE = 3;
  96. /**
  97. * Error code returned when a key that is already in the keyring is
  98. * imported.
  99. */
  100. const ERROR_DUPLICATE_KEY = 4;
  101. /**
  102. * Error code returned the required data is missing for an operation.
  103. *
  104. * This could be missing key data, missing encrypted data or missing
  105. * signature data.
  106. */
  107. const ERROR_NO_DATA = 5;
  108. /**
  109. * Error code returned when an unsigned key is used.
  110. */
  111. const ERROR_UNSIGNED_KEY = 6;
  112. /**
  113. * Error code returned when a key that is not self-signed is used.
  114. */
  115. const ERROR_NOT_SELF_SIGNED = 7;
  116. /**
  117. * Error code returned when a public or private key that is not in the
  118. * keyring is used.
  119. */
  120. const ERROR_KEY_NOT_FOUND = 8;
  121. /**
  122. * Error code returned when an attempt to delete public key having a
  123. * private key is made.
  124. */
  125. const ERROR_DELETE_PRIVATE_KEY = 9;
  126. /**
  127. * Error code returned when one or more bad signatures are detected.
  128. */
  129. const ERROR_BAD_SIGNATURE = 10;
  130. /**
  131. * Error code returned when there is a problem reading GnuPG data files.
  132. */
  133. const ERROR_FILE_PERMISSIONS = 11;
  134. /**
  135. * Error code returned when a key could not be created.
  136. */
  137. const ERROR_KEY_NOT_CREATED = 12;
  138. /**
  139. * Error code returned when bad key parameters are used during key
  140. * generation.
  141. */
  142. const ERROR_BAD_KEY_PARAMS = 13;
  143. // }}}
  144. // {{{ other class constants
  145. /**
  146. * URI at which package bugs may be reported.
  147. */
  148. const BUG_URI = 'http://pear.php.net/bugs/report.php?package=Crypt_GPG';
  149. // }}}
  150. // {{{ protected class properties
  151. /**
  152. * Engine used to control the GPG subprocess
  153. *
  154. * @var Crypt_GPG_Engine
  155. *
  156. * @see Crypt_GPGAbstract::setEngine()
  157. */
  158. protected $engine = null;
  159. // }}}
  160. // {{{ __construct()
  161. /**
  162. * Creates a new GPG object
  163. *
  164. * Available options are:
  165. *
  166. * - <kbd>string homedir</kbd> - the directory where the GPG
  167. * keyring files are stored. If not
  168. * specified, Crypt_GPG uses the
  169. * default of <kbd>~/.gnupg</kbd>.
  170. * - <kbd>string publicKeyring</kbd> - the file path of the public
  171. * keyring. Use this if the public
  172. * keyring is not in the homedir, or
  173. * if the keyring is in a directory
  174. * not writable by the process
  175. * invoking GPG (like Apache). Then
  176. * you can specify the path to the
  177. * keyring with this option
  178. * (/foo/bar/pubring.gpg), and specify
  179. * a writable directory (like /tmp)
  180. * using the <i>homedir</i> option.
  181. * - <kbd>string privateKeyring</kbd> - the file path of the private
  182. * keyring. Use this if the private
  183. * keyring is not in the homedir, or
  184. * if the keyring is in a directory
  185. * not writable by the process
  186. * invoking GPG (like Apache). Then
  187. * you can specify the path to the
  188. * keyring with this option
  189. * (/foo/bar/secring.gpg), and specify
  190. * a writable directory (like /tmp)
  191. * using the <i>homedir</i> option.
  192. * - <kbd>string trustDb</kbd> - the file path of the web-of-trust
  193. * database. Use this if the trust
  194. * database is not in the homedir, or
  195. * if the database is in a directory
  196. * not writable by the process
  197. * invoking GPG (like Apache). Then
  198. * you can specify the path to the
  199. * trust database with this option
  200. * (/foo/bar/trustdb.gpg), and specify
  201. * a writable directory (like /tmp)
  202. * using the <i>homedir</i> option.
  203. * - <kbd>string binary</kbd> - the location of the GPG binary. If
  204. * not specified, the driver attempts
  205. * to auto-detect the GPG binary
  206. * location using a list of known
  207. * default locations for the current
  208. * operating system. The option
  209. * <kbd>gpgBinary</kbd> is a
  210. * deprecated alias for this option.
  211. * - <kbd>string agent</kbd> - the location of the GnuPG agent
  212. * binary. The gpg-agent is only
  213. * used for GnuPG 2.x. If not
  214. * specified, the engine attempts
  215. * to auto-detect the gpg-agent
  216. * binary location using a list of
  217. * know default locations for the
  218. * current operating system.
  219. * - <kbd>boolean debug</kbd> - whether or not to use debug mode.
  220. * When debug mode is on, all
  221. * communication to and from the GPG
  222. * subprocess is logged. This can be
  223. *
  224. * @param array $options optional. An array of options used to create the
  225. * GPG object. All options are optional and are
  226. * represented as key-value pairs.
  227. *
  228. * @throws Crypt_GPG_FileException if the <kbd>homedir</kbd> does not exist
  229. * and cannot be created. This can happen if <kbd>homedir</kbd> is
  230. * not specified, Crypt_GPG is run as the web user, and the web
  231. * user has no home directory. This exception is also thrown if any
  232. * of the options <kbd>publicKeyring</kbd>,
  233. * <kbd>privateKeyring</kbd> or <kbd>trustDb</kbd> options are
  234. * specified but the files do not exist or are are not readable.
  235. * This can happen if the user running the Crypt_GPG process (for
  236. * example, the Apache user) does not have permission to read the
  237. * files.
  238. *
  239. * @throws PEAR_Exception if the provided <kbd>binary</kbd> is invalid, or
  240. * if no <kbd>binary</kbd> is provided and no suitable binary could
  241. * be found.
  242. *
  243. * @throws PEAR_Exception if the provided <kbd>agent</kbd> is invalid, or
  244. * if no <kbd>agent</kbd> is provided and no suitable gpg-agent
  245. * cound be found.
  246. */
  247. public function __construct(array $options = array())
  248. {
  249. $this->setEngine(new Crypt_GPG_Engine($options));
  250. }
  251. // }}}
  252. // {{{ setEngine()
  253. /**
  254. * Sets the I/O engine to use for GnuPG operations
  255. *
  256. * Normally this method does not need to be used. It provides a means for
  257. * dependency injection.
  258. *
  259. * @param Crypt_GPG_Engine $engine the engine to use.
  260. *
  261. * @return Crypt_GPGAbstract the current object, for fluent interface.
  262. */
  263. public function setEngine(Crypt_GPG_Engine $engine)
  264. {
  265. $this->engine = $engine;
  266. return $this;
  267. }
  268. // }}}
  269. // {{{ _getKeys()
  270. /**
  271. * Gets the available keys in the keyring
  272. *
  273. * Calls GPG with the <kbd>--list-keys</kbd> command and grabs keys. See
  274. * the first section of <b>doc/DETAILS</b> in the
  275. * {@link http://www.gnupg.org/download/ GPG package} for a detailed
  276. * description of how the GPG command output is parsed.
  277. *
  278. * @param string $keyId optional. Only keys with that match the specified
  279. * pattern are returned. The pattern may be part of
  280. * a user id, a key id or a key fingerprint. If not
  281. * specified, all keys are returned.
  282. *
  283. * @return array an array of {@link Crypt_GPG_Key} objects. If no keys
  284. * match the specified <kbd>$keyId</kbd> an empty array is
  285. * returned.
  286. *
  287. * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs.
  288. * Use the <kbd>debug</kbd> option and file a bug report if these
  289. * exceptions occur.
  290. *
  291. * @see Crypt_GPG_Key
  292. */
  293. protected function _getKeys($keyId = '')
  294. {
  295. // get private key fingerprints
  296. if ($keyId == '') {
  297. $operation = '--list-secret-keys';
  298. } else {
  299. $operation = '--list-secret-keys ' . escapeshellarg($keyId);
  300. }
  301. // According to The file 'doc/DETAILS' in the GnuPG distribution, using
  302. // double '--with-fingerprint' also prints the fingerprint for subkeys.
  303. $arguments = array(
  304. '--with-colons',
  305. '--with-fingerprint',
  306. '--with-fingerprint',
  307. '--fixed-list-mode'
  308. );
  309. $output = '';
  310. $this->engine->reset();
  311. $this->engine->setOutput($output);
  312. $this->engine->setOperation($operation, $arguments);
  313. $this->engine->run();
  314. $code = $this->engine->getErrorCode();
  315. switch ($code) {
  316. case self::ERROR_NONE:
  317. case self::ERROR_KEY_NOT_FOUND:
  318. // ignore not found key errors
  319. break;
  320. case self::ERROR_FILE_PERMISSIONS:
  321. $filename = $this->engine->getErrorFilename();
  322. if ($filename) {
  323. throw new Crypt_GPG_FileException(
  324. sprintf(
  325. 'Error reading GnuPG data file \'%s\'. Check to make ' .
  326. 'sure it is readable by the current user.',
  327. $filename
  328. ),
  329. $code,
  330. $filename
  331. );
  332. }
  333. throw new Crypt_GPG_FileException(
  334. 'Error reading GnuPG data file. Check to make GnuPG data ' .
  335. 'files are readable by the current user.',
  336. $code
  337. );
  338. default:
  339. throw new Crypt_GPG_Exception(
  340. 'Unknown error getting keys. Please use the \'debug\' option ' .
  341. 'when creating the Crypt_GPG object, and file a bug report ' .
  342. 'at ' . self::BUG_URI,
  343. $code
  344. );
  345. }
  346. $privateKeyFingerprints = array();
  347. $lines = explode(PHP_EOL, $output);
  348. foreach ($lines as $line) {
  349. $lineExp = explode(':', $line);
  350. if ($lineExp[0] == 'fpr') {
  351. $privateKeyFingerprints[] = $lineExp[9];
  352. }
  353. }
  354. // get public keys
  355. if ($keyId == '') {
  356. $operation = '--list-public-keys';
  357. } else {
  358. $operation = '--list-public-keys ' . escapeshellarg($keyId);
  359. }
  360. $output = '';
  361. $this->engine->reset();
  362. $this->engine->setOutput($output);
  363. $this->engine->setOperation($operation, $arguments);
  364. $this->engine->run();
  365. $code = $this->engine->getErrorCode();
  366. switch ($code) {
  367. case self::ERROR_NONE:
  368. case self::ERROR_KEY_NOT_FOUND:
  369. // ignore not found key errors
  370. break;
  371. case self::ERROR_FILE_PERMISSIONS:
  372. $filename = $this->engine->getErrorFilename();
  373. if ($filename) {
  374. throw new Crypt_GPG_FileException(
  375. sprintf(
  376. 'Error reading GnuPG data file \'%s\'. Check to make ' .
  377. 'sure it is readable by the current user.',
  378. $filename
  379. ),
  380. $code,
  381. $filename
  382. );
  383. }
  384. throw new Crypt_GPG_FileException(
  385. 'Error reading GnuPG data file. Check to make GnuPG data ' .
  386. 'files are readable by the current user.',
  387. $code
  388. );
  389. default:
  390. throw new Crypt_GPG_Exception(
  391. 'Unknown error getting keys. Please use the \'debug\' option ' .
  392. 'when creating the Crypt_GPG object, and file a bug report ' .
  393. 'at ' . self::BUG_URI,
  394. $code
  395. );
  396. }
  397. $keys = array();
  398. $key = null; // current key
  399. $subKey = null; // current sub-key
  400. $lines = explode(PHP_EOL, $output);
  401. foreach ($lines as $line) {
  402. $lineExp = explode(':', $line);
  403. if ($lineExp[0] == 'pub') {
  404. // new primary key means last key should be added to the array
  405. if ($key !== null) {
  406. $keys[] = $key;
  407. }
  408. $key = new Crypt_GPG_Key();
  409. $subKey = Crypt_GPG_SubKey::parse($line);
  410. $key->addSubKey($subKey);
  411. } elseif ($lineExp[0] == 'sub') {
  412. $subKey = Crypt_GPG_SubKey::parse($line);
  413. $key->addSubKey($subKey);
  414. } elseif ($lineExp[0] == 'fpr') {
  415. $fingerprint = $lineExp[9];
  416. // set current sub-key fingerprint
  417. $subKey->setFingerprint($fingerprint);
  418. // if private key exists, set has private to true
  419. if (in_array($fingerprint, $privateKeyFingerprints)) {
  420. $subKey->setHasPrivate(true);
  421. }
  422. } elseif ($lineExp[0] == 'uid') {
  423. $string = stripcslashes($lineExp[9]); // as per documentation
  424. $userId = new Crypt_GPG_UserId($string);
  425. if ($lineExp[1] == 'r') {
  426. $userId->setRevoked(true);
  427. }
  428. $key->addUserId($userId);
  429. }
  430. }
  431. // add last key
  432. if ($key !== null) {
  433. $keys[] = $key;
  434. }
  435. return $keys;
  436. }
  437. // }}}
  438. }
  439. // }}}
  440. ?>