pgg-gpg.el 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. ;;; pgg-gpg.el --- GnuPG support for PGG.
  2. ;; Copyright (C) 1999-2000, 2002-2012 Free Software Foundation, Inc.
  3. ;; Author: Daiki Ueno <ueno@unixuser.org>
  4. ;; Symmetric encryption and gpg-agent support added by:
  5. ;; Sascha Wilde <wilde@sha-bang.de>
  6. ;; Created: 1999/10/28
  7. ;; Keywords: PGP, OpenPGP, GnuPG
  8. ;; Package: pgg
  9. ;; Obsolete-since: 24.1
  10. ;; This file is part of GNU Emacs.
  11. ;; GNU Emacs is free software: you can redistribute it and/or modify
  12. ;; it under the terms of the GNU General Public License as published by
  13. ;; the Free Software Foundation, either version 3 of the License, or
  14. ;; (at your option) any later version.
  15. ;; GNU Emacs is distributed in the hope that it will be useful,
  16. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. ;; GNU General Public License for more details.
  19. ;; You should have received a copy of the GNU General Public License
  20. ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
  21. ;;; Code:
  22. (eval-when-compile
  23. (require 'cl) ; for gpg macros
  24. (require 'pgg))
  25. (defgroup pgg-gpg ()
  26. "GnuPG interface."
  27. :group 'pgg)
  28. (defcustom pgg-gpg-program "gpg"
  29. "The GnuPG executable."
  30. :group 'pgg-gpg
  31. :type 'string)
  32. (defcustom pgg-gpg-extra-args nil
  33. "Extra arguments for every GnuPG invocation."
  34. :group 'pgg-gpg
  35. :type '(repeat (string :tag "Argument")))
  36. (defcustom pgg-gpg-recipient-argument "--recipient"
  37. "GnuPG option to specify recipient."
  38. :group 'pgg-gpg
  39. :type '(choice (const :tag "New `--recipient' option" "--recipient")
  40. (const :tag "Old `--remote-user' option" "--remote-user")))
  41. (defcustom pgg-gpg-use-agent t
  42. "Whether to use gnupg agent for key caching."
  43. :group 'pgg-gpg
  44. :type 'boolean)
  45. (defvar pgg-gpg-user-id nil
  46. "GnuPG ID of your default identity.")
  47. (defun pgg-gpg-process-region (start end passphrase program args)
  48. (let* ((use-agent (and (null passphrase) (pgg-gpg-use-agent-p)))
  49. (output-file-name (pgg-make-temp-file "pgg-output"))
  50. (args
  51. `("--status-fd" "2"
  52. ,@(if use-agent '("--use-agent")
  53. (if passphrase '("--passphrase-fd" "0")))
  54. "--yes" ; overwrite
  55. "--output" ,output-file-name
  56. ,@pgg-gpg-extra-args ,@args))
  57. (output-buffer pgg-output-buffer)
  58. (errors-buffer pgg-errors-buffer)
  59. (orig-mode (default-file-modes))
  60. (process-connection-type nil)
  61. (inhibit-redisplay t)
  62. process status exit-status
  63. passphrase-with-newline
  64. encoded-passphrase-with-new-line)
  65. (with-current-buffer (get-buffer-create errors-buffer)
  66. (buffer-disable-undo)
  67. (erase-buffer))
  68. (unwind-protect
  69. (progn
  70. (set-default-file-modes 448)
  71. (let ((coding-system-for-write 'binary))
  72. (setq process
  73. (apply #'start-process "*GnuPG*" errors-buffer
  74. program args)))
  75. (set-process-sentinel process #'ignore)
  76. (when passphrase
  77. (setq passphrase-with-newline (concat passphrase "\n"))
  78. (if pgg-passphrase-coding-system
  79. (progn
  80. (setq encoded-passphrase-with-new-line
  81. (encode-coding-string
  82. passphrase-with-newline
  83. (coding-system-change-eol-conversion
  84. pgg-passphrase-coding-system 'unix)))
  85. (pgg-clear-string passphrase-with-newline))
  86. (setq encoded-passphrase-with-new-line passphrase-with-newline
  87. passphrase-with-newline nil))
  88. (process-send-string process encoded-passphrase-with-new-line))
  89. (process-send-region process start end)
  90. (process-send-eof process)
  91. (while (eq 'run (process-status process))
  92. (accept-process-output process 5))
  93. ;; Accept any remaining pending output coming after the
  94. ;; status change.
  95. (accept-process-output process 5)
  96. (setq status (process-status process)
  97. exit-status (process-exit-status process))
  98. (delete-process process)
  99. (with-current-buffer (get-buffer-create output-buffer)
  100. (buffer-disable-undo)
  101. (erase-buffer)
  102. (if (file-exists-p output-file-name)
  103. (let ((coding-system-for-read (if pgg-text-mode
  104. 'raw-text
  105. 'binary)))
  106. (insert-file-contents output-file-name)))
  107. (set-buffer errors-buffer)
  108. (if (memq status '(stop signal))
  109. (error "%s exited abnormally: '%s'" program exit-status))
  110. (if (= 127 exit-status)
  111. (error "%s could not be found" program))))
  112. (if passphrase-with-newline
  113. (pgg-clear-string passphrase-with-newline))
  114. (if encoded-passphrase-with-new-line
  115. (pgg-clear-string encoded-passphrase-with-new-line))
  116. (if (and process (eq 'run (process-status process)))
  117. (interrupt-process process))
  118. (if (file-exists-p output-file-name)
  119. (delete-file output-file-name))
  120. (set-default-file-modes orig-mode))))
  121. (defun pgg-gpg-possibly-cache-passphrase (passphrase &optional key notruncate)
  122. (if (and passphrase
  123. pgg-cache-passphrase
  124. (progn
  125. (goto-char (point-min))
  126. (re-search-forward "^\\[GNUPG:] \\(GOOD_PASSPHRASE\\>\\)\\|\\(SIG_CREATED\\)" nil t)))
  127. (pgg-add-passphrase-to-cache
  128. (or key
  129. (progn
  130. (goto-char (point-min))
  131. (if (re-search-forward
  132. "^\\[GNUPG:] NEED_PASSPHRASE\\(_PIN\\)? \\w+ ?\\w*" nil t)
  133. (substring (match-string 0) -8))))
  134. passphrase
  135. notruncate)))
  136. (defvar pgg-gpg-all-secret-keys 'unknown)
  137. (defun pgg-gpg-lookup-all-secret-keys ()
  138. "Return all secret keys present in secret key ring."
  139. (when (eq pgg-gpg-all-secret-keys 'unknown)
  140. (setq pgg-gpg-all-secret-keys '())
  141. (let ((args (list "--with-colons" "--no-greeting" "--batch"
  142. "--list-secret-keys")))
  143. (with-temp-buffer
  144. (apply #'call-process pgg-gpg-program nil t nil args)
  145. (goto-char (point-min))
  146. (while (re-search-forward
  147. "^\\(sec\\|pub\\):[^:]*:[^:]*:[^:]*:\\([^:]*\\)" nil t)
  148. (push (substring (match-string 2) 8)
  149. pgg-gpg-all-secret-keys)))))
  150. pgg-gpg-all-secret-keys)
  151. (defun pgg-gpg-lookup-key (string &optional type)
  152. "Search keys associated with STRING."
  153. (let ((args (list "--with-colons" "--no-greeting" "--batch"
  154. (if type "--list-secret-keys" "--list-keys")
  155. string)))
  156. (with-temp-buffer
  157. (apply #'call-process pgg-gpg-program nil t nil args)
  158. (goto-char (point-min))
  159. (if (re-search-forward "^\\(sec\\|pub\\):[^:]*:[^:]*:[^:]*:\\([^:]*\\)"
  160. nil t)
  161. (substring (match-string 2) 8)))))
  162. (defun pgg-gpg-lookup-key-owner (string &optional all)
  163. "Search keys associated with STRING and return owner of identified key.
  164. The value may be just the bare key id, or it may be a combination of the
  165. user name associated with the key and the key id, with the key id enclosed
  166. in \"<...>\" angle brackets.
  167. Optional ALL non-nil means search all keys, including secret keys."
  168. (let ((args (list "--with-colons" "--no-greeting" "--batch"
  169. (if all "--list-secret-keys" "--list-keys")
  170. string))
  171. (key-regexp (concat "^\\(sec\\|pub\\|uid\\)"
  172. ":[^:]*:[^:]*:[^:]*:\\([^:]*\\):[^:]*"
  173. ":[^:]*:[^:]*:[^:]*:\\([^:]+\\):")))
  174. (with-temp-buffer
  175. (apply #'call-process pgg-gpg-program nil t nil args)
  176. (goto-char (point-min))
  177. (if (re-search-forward key-regexp
  178. nil t)
  179. (match-string 3)))))
  180. (defun pgg-gpg-key-id-from-key-owner (key-owner)
  181. (cond ((not key-owner) nil)
  182. ;; Extract bare key id from outermost paired angle brackets, if any:
  183. ((string-match "[^<]*<\\(.+\\)>[^>]*" key-owner)
  184. (substring key-owner (match-beginning 1)(match-end 1)))
  185. (key-owner)))
  186. (defun pgg-gpg-encrypt-region (start end recipients &optional sign passphrase)
  187. "Encrypt the current region between START and END.
  188. If optional argument SIGN is non-nil, do a combined sign and encrypt.
  189. If optional PASSPHRASE is not specified, it will be obtained from the
  190. passphrase cache or user."
  191. (let* ((pgg-gpg-user-id (or pgg-gpg-user-id pgg-default-user-id))
  192. (passphrase (or passphrase
  193. (when (and sign (not (pgg-gpg-use-agent-p)))
  194. (pgg-read-passphrase
  195. (format "GnuPG passphrase for %s: "
  196. pgg-gpg-user-id)
  197. pgg-gpg-user-id))))
  198. (args
  199. (append
  200. (list "--batch" "--armor" "--always-trust" "--encrypt")
  201. (if pgg-text-mode (list "--textmode"))
  202. (if sign (list "--sign" "--local-user" pgg-gpg-user-id))
  203. (if (or recipients pgg-encrypt-for-me)
  204. (apply #'nconc
  205. (mapcar (lambda (rcpt)
  206. (list pgg-gpg-recipient-argument rcpt))
  207. (append recipients
  208. (if pgg-encrypt-for-me
  209. (list pgg-gpg-user-id)))))))))
  210. (pgg-gpg-process-region start end passphrase pgg-gpg-program args)
  211. (when sign
  212. (with-current-buffer pgg-errors-buffer
  213. ;; Possibly cache passphrase under, e.g. "jas", for future sign.
  214. (pgg-gpg-possibly-cache-passphrase passphrase pgg-gpg-user-id)
  215. ;; Possibly cache passphrase under, e.g. B565716F, for future decrypt.
  216. (pgg-gpg-possibly-cache-passphrase passphrase)))
  217. (pgg-process-when-success)))
  218. (defun pgg-gpg-encrypt-symmetric-region (start end &optional passphrase)
  219. "Encrypt the current region between START and END with symmetric cipher.
  220. If optional PASSPHRASE is not specified, it will be obtained from the
  221. passphrase cache or user."
  222. (let* ((passphrase (or passphrase
  223. (when (not (pgg-gpg-use-agent-p))
  224. (pgg-read-passphrase
  225. "GnuPG passphrase for symmetric encryption: "))))
  226. (args
  227. (append (list "--batch" "--armor" "--symmetric" )
  228. (if pgg-text-mode (list "--textmode")))))
  229. (pgg-gpg-process-region start end passphrase pgg-gpg-program args)
  230. (pgg-process-when-success)))
  231. (defun pgg-gpg-decrypt-region (start end &optional passphrase)
  232. "Decrypt the current region between START and END.
  233. If optional PASSPHRASE is not specified, it will be obtained from the
  234. passphrase cache or user."
  235. (let* ((current-buffer (current-buffer))
  236. (message-keys (with-temp-buffer
  237. (insert-buffer-substring current-buffer)
  238. (pgg-decode-armor-region (point-min) (point-max))))
  239. (secret-keys (pgg-gpg-lookup-all-secret-keys))
  240. ;; XXX the user is stuck if they need to use the passphrase for
  241. ;; any but the first secret key for which the message is
  242. ;; encrypted. ideally, we would incrementally give them a
  243. ;; chance with subsequent keys each time they fail with one.
  244. (key (pgg-gpg-select-matching-key message-keys secret-keys))
  245. (key-owner (and key (pgg-gpg-lookup-key-owner key t)))
  246. (key-id (pgg-gpg-key-id-from-key-owner key-owner))
  247. (pgg-gpg-user-id (or key-id key
  248. pgg-gpg-user-id pgg-default-user-id))
  249. (passphrase (or passphrase
  250. (when (not (pgg-gpg-use-agent-p))
  251. (pgg-read-passphrase
  252. (format (if (pgg-gpg-symmetric-key-p message-keys)
  253. "Passphrase for symmetric decryption: "
  254. "GnuPG passphrase for %s: ")
  255. (or key-owner "??"))
  256. pgg-gpg-user-id))))
  257. (args '("--batch" "--decrypt")))
  258. (pgg-gpg-process-region start end passphrase pgg-gpg-program args)
  259. (with-current-buffer pgg-errors-buffer
  260. (pgg-gpg-possibly-cache-passphrase passphrase pgg-gpg-user-id)
  261. (goto-char (point-min))
  262. (re-search-forward "^\\[GNUPG:] DECRYPTION_OKAY\\>" nil t))))
  263. ;;;###autoload
  264. (defun pgg-gpg-symmetric-key-p (message-keys)
  265. "True if decoded armor MESSAGE-KEYS has symmetric encryption indicator."
  266. (let (result)
  267. (dolist (key message-keys result)
  268. (when (and (eq (car key) 3)
  269. (member '(symmetric-key-algorithm) key))
  270. (setq result key)))))
  271. (defun pgg-gpg-select-matching-key (message-keys secret-keys)
  272. "Choose a key from MESSAGE-KEYS that matches one of the keys in SECRET-KEYS."
  273. (loop for message-key in message-keys
  274. for message-key-id = (and (equal (car message-key) 1)
  275. (cdr (assq 'key-identifier
  276. (cdr message-key))))
  277. for key = (and message-key-id (pgg-lookup-key message-key-id 'encrypt))
  278. when (and key (member key secret-keys)) return key))
  279. (defun pgg-gpg-sign-region (start end &optional cleartext passphrase)
  280. "Make detached signature from text between START and END."
  281. (let* ((pgg-gpg-user-id (or pgg-gpg-user-id pgg-default-user-id))
  282. (passphrase (or passphrase
  283. (when (not (pgg-gpg-use-agent-p))
  284. (pgg-read-passphrase
  285. (format "GnuPG passphrase for %s: "
  286. pgg-gpg-user-id)
  287. pgg-gpg-user-id))))
  288. (args
  289. (append (list (if cleartext "--clearsign" "--detach-sign")
  290. "--armor" "--batch" "--verbose"
  291. "--local-user" pgg-gpg-user-id)
  292. (if pgg-text-mode (list "--textmode"))))
  293. (inhibit-read-only t)
  294. buffer-read-only)
  295. (pgg-gpg-process-region start end passphrase pgg-gpg-program args)
  296. (with-current-buffer pgg-errors-buffer
  297. ;; Possibly cache passphrase under, e.g. "jas", for future sign.
  298. (pgg-gpg-possibly-cache-passphrase passphrase pgg-gpg-user-id)
  299. ;; Possibly cache passphrase under, e.g. B565716F, for future decrypt.
  300. (pgg-gpg-possibly-cache-passphrase passphrase))
  301. (pgg-process-when-success)))
  302. (defun pgg-gpg-verify-region (start end &optional signature)
  303. "Verify region between START and END as the detached signature SIGNATURE."
  304. (let ((args '("--batch" "--verify")))
  305. (when (stringp signature)
  306. (setq args (append args (list signature))))
  307. (setq args (append args '("-")))
  308. (pgg-gpg-process-region start end nil pgg-gpg-program args)
  309. (with-current-buffer pgg-errors-buffer
  310. (goto-char (point-min))
  311. (while (re-search-forward "^gpg: \\(.*\\)\n" nil t)
  312. (with-current-buffer pgg-output-buffer
  313. (insert-buffer-substring pgg-errors-buffer
  314. (match-beginning 1) (match-end 0)))
  315. (delete-region (match-beginning 0) (match-end 0)))
  316. (goto-char (point-min))
  317. (re-search-forward "^\\[GNUPG:] GOODSIG\\>" nil t))))
  318. (defun pgg-gpg-insert-key ()
  319. "Insert public key at point."
  320. (let* ((pgg-gpg-user-id (or pgg-gpg-user-id pgg-default-user-id))
  321. (args (list "--batch" "--export" "--armor"
  322. pgg-gpg-user-id)))
  323. (pgg-gpg-process-region (point)(point) nil pgg-gpg-program args)
  324. (insert-buffer-substring pgg-output-buffer)))
  325. (defun pgg-gpg-snarf-keys-region (start end)
  326. "Add all public keys in region between START and END to the keyring."
  327. (let ((args '("--import" "--batch" "-")) status)
  328. (pgg-gpg-process-region start end nil pgg-gpg-program args)
  329. (set-buffer pgg-errors-buffer)
  330. (goto-char (point-min))
  331. (when (re-search-forward "^\\[GNUPG:] IMPORT_RES\\>" nil t)
  332. (setq status (buffer-substring (match-end 0)
  333. (progn (end-of-line)(point)))
  334. status (vconcat (mapcar #'string-to-number (split-string status))))
  335. (erase-buffer)
  336. (insert (format "Imported %d key(s).
  337. \tArmor contains %d key(s) [%d bad, %d old].\n"
  338. (+ (aref status 2)
  339. (aref status 10))
  340. (aref status 0)
  341. (aref status 1)
  342. (+ (aref status 4)
  343. (aref status 11)))
  344. (if (zerop (aref status 9))
  345. ""
  346. "\tSecret keys are imported.\n")))
  347. (append-to-buffer pgg-output-buffer (point-min)(point-max))
  348. (pgg-process-when-success)))
  349. (defun pgg-gpg-update-agent ()
  350. "Try to connect to gpg-agent and send UPDATESTARTUPTTY."
  351. (if (fboundp 'make-network-process)
  352. (let* ((agent-info (getenv "GPG_AGENT_INFO"))
  353. (socket (and agent-info
  354. (string-match "^\\([^:]*\\)" agent-info)
  355. (match-string 1 agent-info)))
  356. (conn (and socket
  357. (make-network-process :name "gpg-agent-process"
  358. :host 'local :family 'local
  359. :service socket))))
  360. (when (and conn (eq (process-status conn) 'open))
  361. (process-send-string conn "UPDATESTARTUPTTY\n")
  362. (delete-process conn)
  363. t))
  364. ;; We can't check, so assume gpg-agent is up.
  365. t))
  366. (defun pgg-gpg-use-agent-p ()
  367. "Return t if `pgg-gpg-use-agent' is t and gpg-agent is available."
  368. (and pgg-gpg-use-agent (pgg-gpg-update-agent)))
  369. (provide 'pgg-gpg)
  370. ;;; pgg-gpg.el ends here